summary refs log tree commit diff
path: root/drivers/isdn/hisax
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/hisax
downloadlinux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
Diffstat (limited to 'drivers/isdn/hisax')
-rw-r--r--drivers/isdn/hisax/Kconfig442
-rw-r--r--drivers/isdn/hisax/Makefile64
-rw-r--r--drivers/isdn/hisax/amd7930_fn.c796
-rw-r--r--drivers/isdn/hisax/amd7930_fn.h37
-rw-r--r--drivers/isdn/hisax/arcofi.c134
-rw-r--r--drivers/isdn/hisax/arcofi.h27
-rw-r--r--drivers/isdn/hisax/asuscom.c427
-rw-r--r--drivers/isdn/hisax/avm_a1.c317
-rw-r--r--drivers/isdn/hisax/avm_a1p.c268
-rw-r--r--drivers/isdn/hisax/avm_pci.c865
-rw-r--r--drivers/isdn/hisax/avma1_cs.c527
-rw-r--r--drivers/isdn/hisax/bkm_a4t.c344
-rw-r--r--drivers/isdn/hisax/bkm_a8.c451
-rw-r--r--drivers/isdn/hisax/bkm_ax.h119
-rw-r--r--drivers/isdn/hisax/callc.c1793
-rw-r--r--drivers/isdn/hisax/config.c1958
-rw-r--r--drivers/isdn/hisax/diva.c1183
-rw-r--r--drivers/isdn/hisax/elsa.c1190
-rw-r--r--drivers/isdn/hisax/elsa_cs.c532
-rw-r--r--drivers/isdn/hisax/elsa_ser.c657
-rw-r--r--drivers/isdn/hisax/enternow.h51
-rw-r--r--drivers/isdn/hisax/enternow_pci.c399
-rw-r--r--drivers/isdn/hisax/fsm.c163
-rw-r--r--drivers/isdn/hisax/fsm.h61
-rw-r--r--drivers/isdn/hisax/gazel.c684
-rw-r--r--drivers/isdn/hisax/hfc4s8s_l1.c1714
-rw-r--r--drivers/isdn/hisax/hfc4s8s_l1.h88
-rw-r--r--drivers/isdn/hisax/hfc_2bds0.c1082
-rw-r--r--drivers/isdn/hisax/hfc_2bds0.h128
-rw-r--r--drivers/isdn/hisax/hfc_2bs0.c593
-rw-r--r--drivers/isdn/hisax/hfc_2bs0.h60
-rw-r--r--drivers/isdn/hisax/hfc_pci.c1747
-rw-r--r--drivers/isdn/hisax/hfc_pci.h236
-rw-r--r--drivers/isdn/hisax/hfc_sx.c1521
-rw-r--r--drivers/isdn/hisax/hfc_sx.h197
-rw-r--r--drivers/isdn/hisax/hfc_usb.c1828
-rw-r--r--drivers/isdn/hisax/hfc_usb.h228
-rw-r--r--drivers/isdn/hisax/hfcscard.c266
-rw-r--r--drivers/isdn/hisax/hisax.h1341
-rw-r--r--drivers/isdn/hisax/hisax_cfg.h64
-rw-r--r--drivers/isdn/hisax/hisax_debug.h81
-rw-r--r--drivers/isdn/hisax/hisax_fcpcipnp.c1028
-rw-r--r--drivers/isdn/hisax/hisax_fcpcipnp.h58
-rw-r--r--drivers/isdn/hisax/hisax_if.h66
-rw-r--r--drivers/isdn/hisax/hisax_isac.c897
-rw-r--r--drivers/isdn/hisax/hisax_isac.h45
-rw-r--r--drivers/isdn/hisax/hscx.c280
-rw-r--r--drivers/isdn/hisax/hscx.h41
-rw-r--r--drivers/isdn/hisax/hscx_irq.c292
-rw-r--r--drivers/isdn/hisax/icc.c685
-rw-r--r--drivers/isdn/hisax/icc.h72
-rw-r--r--drivers/isdn/hisax/ipac.h29
-rw-r--r--drivers/isdn/hisax/ipacx.c1004
-rw-r--r--drivers/isdn/hisax/ipacx.h162
-rw-r--r--drivers/isdn/hisax/isac.c684
-rw-r--r--drivers/isdn/hisax/isac.h70
-rw-r--r--drivers/isdn/hisax/isar.c1909
-rw-r--r--drivers/isdn/hisax/isar.h222
-rw-r--r--drivers/isdn/hisax/isdnhdlc.c628
-rw-r--r--drivers/isdn/hisax/isdnhdlc.h72
-rw-r--r--drivers/isdn/hisax/isdnl1.c932
-rw-r--r--drivers/isdn/hisax/isdnl1.h32
-rw-r--r--drivers/isdn/hisax/isdnl2.c1860
-rw-r--r--drivers/isdn/hisax/isdnl2.h26
-rw-r--r--drivers/isdn/hisax/isdnl3.c610
-rw-r--r--drivers/isdn/hisax/isdnl3.h37
-rw-r--r--drivers/isdn/hisax/isurf.c306
-rw-r--r--drivers/isdn/hisax/ix1_micro.c318
-rw-r--r--drivers/isdn/hisax/jade.c318
-rw-r--r--drivers/isdn/hisax/jade.h135
-rw-r--r--drivers/isdn/hisax/jade_irq.c236
-rw-r--r--drivers/isdn/hisax/l3_1tr6.c955
-rw-r--r--drivers/isdn/hisax/l3_1tr6.h164
-rw-r--r--drivers/isdn/hisax/l3dss1.c3238
-rw-r--r--drivers/isdn/hisax/l3dss1.h124
-rw-r--r--drivers/isdn/hisax/l3ni1.c3189
-rw-r--r--drivers/isdn/hisax/l3ni1.h136
-rw-r--r--drivers/isdn/hisax/lmgr.c50
-rw-r--r--drivers/isdn/hisax/mic.c239
-rw-r--r--drivers/isdn/hisax/netjet.c996
-rw-r--r--drivers/isdn/hisax/netjet.h72
-rw-r--r--drivers/isdn/hisax/niccy.c389
-rw-r--r--drivers/isdn/hisax/nj_s.c278
-rw-r--r--drivers/isdn/hisax/nj_u.c244
-rw-r--r--drivers/isdn/hisax/q931.c1522
-rw-r--r--drivers/isdn/hisax/s0box.c266
-rw-r--r--drivers/isdn/hisax/saphir.c300
-rw-r--r--drivers/isdn/hisax/sedlbauer.c833
-rw-r--r--drivers/isdn/hisax/sedlbauer_cs.c640
-rw-r--r--drivers/isdn/hisax/sportster.c270
-rw-r--r--drivers/isdn/hisax/st5481.h535
-rw-r--r--drivers/isdn/hisax/st5481_b.c374
-rw-r--r--drivers/isdn/hisax/st5481_d.c776
-rw-r--r--drivers/isdn/hisax/st5481_hdlc.c580
-rw-r--r--drivers/isdn/hisax/st5481_hdlc.h62
-rw-r--r--drivers/isdn/hisax/st5481_init.c224
-rw-r--r--drivers/isdn/hisax/st5481_usb.c650
-rw-r--r--drivers/isdn/hisax/tei.c466
-rw-r--r--drivers/isdn/hisax/teleint.c339
-rw-r--r--drivers/isdn/hisax/teles0.c367
-rw-r--r--drivers/isdn/hisax/teles3.c499
-rw-r--r--drivers/isdn/hisax/teles_cs.c513
-rw-r--r--drivers/isdn/hisax/telespci.c359
-rw-r--r--drivers/isdn/hisax/w6692.c1096
-rw-r--r--drivers/isdn/hisax/w6692.h184
105 files changed, 58646 insertions, 0 deletions
diff --git a/drivers/isdn/hisax/Kconfig b/drivers/isdn/hisax/Kconfig
new file mode 100644
index 000000000000..6c7b8bffc6fd
--- /dev/null
+++ b/drivers/isdn/hisax/Kconfig
@@ -0,0 +1,442 @@
+
+menu "Passive cards"
+	depends on ISDN_I4L
+
+config ISDN_DRV_HISAX
+	tristate "HiSax SiemensChipSet driver support"
+	select CRC_CCITT
+	---help---
+	  This is a driver supporting the Siemens chipset on various
+	  ISDN-cards (like AVM A1, Elsa ISDN cards, Teles S0-16.0, Teles
+	  S0-16.3, Teles S0-8, Teles/Creatix PnP, ITK micro ix1 and many
+	  compatibles).
+
+	  HiSax is just the name of this driver, not the name of any hardware.
+
+	  If you have a card with such a chipset, you should say Y here and
+	  also to the configuration option of the driver for your particular
+	  card, below.
+
+if ISDN_DRV_HISAX!=n
+
+comment "D-channel protocol features"
+
+config HISAX_EURO
+	bool "HiSax Support for EURO/DSS1"
+	help
+	  Say Y or N according to the D-channel protocol which your local
+	  telephone service company provides.
+
+	  The call control protocol E-DSS1 is used in most European countries.
+	  If unsure, say Y.
+
+config DE_AOC
+	bool "Support for german chargeinfo"
+	depends on HISAX_EURO
+	help
+	  If you want that the HiSax hardware driver sends messages to the
+	  upper level of the isdn code on each AOCD (Advice Of Charge, During
+	  the call -- transmission of the fee information during a call) and
+	  on each AOCE (Advice Of Charge, at the End of the call --
+	  transmission of fee information at the end of the call), say Y here.
+	  This works only in Germany.
+
+config HISAX_NO_SENDCOMPLETE
+	bool "Disable sending complete"
+	depends on HISAX_EURO
+	help
+	  If you have trouble with some ugly exchanges or you live in
+	  Australia select this option.
+
+config HISAX_NO_LLC
+	bool "Disable sending low layer compatibility"
+	depends on HISAX_EURO
+	help
+	  If you have trouble with some ugly exchanges try to select this
+	  option.
+
+config HISAX_NO_KEYPAD
+	bool "Disable keypad protocol option"
+	depends on HISAX_EURO
+	help
+	  If you like to send special dial strings including * or # without
+	  using the keypad protocol, select this option.
+
+config HISAX_1TR6
+	bool "HiSax Support for german 1TR6"
+	help
+	  Say Y or N according to the D-channel protocol which your local
+	  telephone service company provides.
+
+	  1TR6 is an old call control protocol which was used in Germany
+	  before E-DSS1 was established. Nowadays, all new lines in Germany
+	  use E-DSS1.
+
+config HISAX_NI1
+	bool "HiSax Support for US NI1"
+	help
+	  Enable this if you like to use ISDN in US on a NI1 basic rate
+	  interface.
+
+config HISAX_MAX_CARDS
+	int "Maximum number of cards supported by HiSax"
+	default "8"
+	help
+	  This option allows you to specify the maximum number of cards which
+	  the HiSax driver will be able to handle.  
+
+comment "HiSax supported cards"
+
+config HISAX_16_0
+	bool "Teles 16.0/8.0"
+	depends on ISA
+	help
+	  This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8
+	  and many compatibles.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port/shmem settings.
+
+config HISAX_16_3
+	bool "Teles 16.3 or PNP or PCMCIA"
+	help
+	  This enables HiSax support for the Teles ISDN-cards S0-16.3 the
+	  Teles/Creatix PnP and the Teles PCMCIA.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_TELESPCI
+	bool "Teles PCI"
+	depends on PCI && (BROKEN || !(SPARC64 || PPC))
+	help
+	  This enables HiSax support for the Teles PCI.
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it.
+
+config HISAX_S0BOX
+	bool "Teles S0Box"
+	help
+	  This enables HiSax support for the Teles/Creatix parallel port
+	  S0BOX.  See <file:Documentation/isdn/README.HiSax> on how to
+	  configure it.
+
+config HISAX_AVM_A1
+	bool "AVM A1 (Fritz)"
+	depends on ISA
+	help
+	  This enables HiSax support for the AVM A1 (aka "Fritz").
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_FRITZPCI
+	bool "AVM PnP/PCI (Fritz!PnP/PCI)"
+	help
+	  This enables HiSax support for the AVM "Fritz!PnP" and "Fritz!PCI".
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it.
+
+config HISAX_AVM_A1_PCMCIA
+	bool "AVM A1 PCMCIA (Fritz)"
+	help
+	  This enables HiSax support for the AVM A1 "Fritz!PCMCIA").
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it.
+
+config HISAX_ELSA
+	bool "Elsa cards"
+	help
+	  This enables HiSax support for the Elsa Mircolink ISA cards, for the
+	  Elsa Quickstep series cards and Elsa PCMCIA.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_IX1MICROR2
+	bool "ITK ix1-micro Revision 2"
+	depends on ISA
+	help
+	  This enables HiSax support for the ITK ix1-micro Revision 2 card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_DIEHLDIVA
+	bool "Eicon.Diehl Diva cards"
+	help
+	  This enables HiSax support for the Eicon.Diehl Diva none PRO
+	  versions passive ISDN cards.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_ASUSCOM
+	bool "ASUSCOM ISA cards"
+	depends on ISA
+	help
+	  This enables HiSax support for the AsusCom and their OEM versions
+	  passive ISDN ISA cards.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_TELEINT
+	bool "TELEINT cards"
+	depends on ISA
+	help
+	  This enables HiSax support for the TELEINT SA1 semiactiv ISDN card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_HFCS
+	bool "HFC-S based cards"
+	depends on ISA
+	help
+	  This enables HiSax support for the HFC-S 2BDS0 based cards, like
+	  teles 16.3c.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_SEDLBAUER
+	bool "Sedlbauer cards"
+	help
+	  This enables HiSax support for the Sedlbauer passive ISDN cards.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_SPORTSTER
+	bool "USR Sportster internal TA"
+	depends on ISA
+	help
+	  This enables HiSax support for the USR Sportster internal TA card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_MIC
+	bool "MIC card"
+	depends on ISA
+	help
+	  This enables HiSax support for the ITH MIC card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_NETJET
+	bool "NETjet card"
+	depends on PCI && (BROKEN || !(SPARC64 || PPC))
+	help
+	  This enables HiSax support for the NetJet from Traverse
+	  Technologies.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_NETJET_U
+	bool "NETspider U card"
+	depends on PCI && (BROKEN || !(SPARC64 || PPC))
+	help
+	  This enables HiSax support for the Netspider U interface ISDN card
+	  from Traverse Technologies.
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_NICCY
+	bool "Niccy PnP/PCI card"
+	help
+	  This enables HiSax support for the Dr. Neuhaus Niccy PnP or PCI.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_ISURF
+	bool "Siemens I-Surf card"
+	depends on ISA
+	help
+	  This enables HiSax support for the Siemens I-Talk/I-Surf card with
+	  ISAR chip.
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_HSTSAPHIR
+	bool "HST Saphir card"
+	depends on ISA
+	help
+	  This enables HiSax support for the HST Saphir card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_BKM_A4T
+	bool "Telekom A4T card"
+	depends on PCI
+	help
+	  This enables HiSax support for the Telekom A4T card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_SCT_QUADRO
+	bool "Scitel Quadro card"
+	depends on PCI
+	help
+	  This enables HiSax support for the Scitel Quadro card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_GAZEL
+	bool "Gazel cards"
+	help
+	  This enables HiSax support for the Gazel cards.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_HFC_PCI
+	bool "HFC PCI-Bus cards"
+	depends on PCI && (BROKEN || !(SPARC64 || PPC))
+	help
+	  This enables HiSax support for the HFC-S PCI 2BDS0 based cards.
+
+	  For more informations see under
+	  <file:Documentation/isdn/README.hfc-pci>.
+
+config HISAX_W6692
+	bool "Winbond W6692 based cards"
+	depends on PCI
+	help
+	  This enables HiSax support for Winbond W6692 based PCI ISDN cards.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_HFC_SX
+	bool "HFC-S+, HFC-SP, HFC-PCMCIA cards"
+	help
+	  This enables HiSax support for the HFC-S+, HFC-SP and HFC-PCMCIA
+	  cards. This code is not finished yet.
+
+#      bool '  TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU
+
+config HISAX_ENTERNOW_PCI
+	bool "Formula-n enter:now PCI card"
+	depends on PCI && (BROKEN || !(SPARC64 || PPC))
+	help
+	  This enables HiSax support for the Formula-n enter:now PCI
+	  ISDN card.
+
+config HISAX_AMD7930
+	bool "Am7930 (EXPERIMENTAL)"
+	depends on EXPERIMENTAL && (SPARC32 || SPARC64)
+	help
+	  This enables HiSax support for the AMD7930 chips on some SPARCs.
+	  This code is not finished yet.
+
+endif
+
+if ISDN_DRV_HISAX
+
+config HISAX_DEBUG
+	bool "HiSax debugging"
+	help
+	  This enables debugging code in the new-style HiSax drivers, i.e.
+	  the ST5481 USB driver currently. 
+	  If in doubt, say yes.
+
+comment "HiSax PCMCIA card service modules"
+
+config HISAX_SEDLBAUER_CS
+	tristate "Sedlbauer PCMCIA cards"
+	depends on PCMCIA && HISAX_SEDLBAUER
+	help
+	  This enables the PCMCIA client driver for the Sedlbauer Speed Star
+	  and Speed Star II cards.
+
+config HISAX_ELSA_CS
+	tristate "ELSA PCMCIA MicroLink cards"
+	depends on PCMCIA && HISAX_ELSA
+	help
+	  This enables the PCMCIA client driver for the Elsa PCMCIA MicroLink
+	  card.
+
+config HISAX_AVM_A1_CS
+	tristate "AVM A1 PCMCIA cards"
+	depends on PCMCIA && ISDN_DRV_HISAX
+	help
+	  This enables the PCMCIA client driver for the AVM A1 / Fritz!Card
+	  PCMCIA cards.
+
+config HISAX_TELES_CS
+	tristate "TELES PCMCIA cards"
+	depends on PCMCIA && HISAX_16_3
+	help
+	  This enables the PCMCIA client driver for the Teles PCMCIA cards.
+
+comment "HiSax sub driver modules"
+
+config HISAX_ST5481
+	tristate "ST5481 USB ISDN modem (EXPERIMENTAL)"
+	depends on USB && EXPERIMENTAL
+	select CRC_CCITT
+	help
+	  This enables the driver for ST5481 based USB ISDN adapters,
+	  e.g. the BeWan Gazel 128 USB
+
+config HISAX_HFCUSB
+	tristate "HFC USB based ISDN modems (EXPERIMENTAL)"
+	depends on USB && EXPERIMENTAL
+	help
+	  This enables the driver for HFC USB based ISDN modems.
+
+config HISAX_HFC4S8S
+	tristate "HFC-4S/8S based ISDN cards (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	help
+	  This enables the driver for HFC-4S/8S based ISDN cards.
+
+config HISAX_FRITZ_PCIPNP
+	tristate "AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)"
+	depends on PCI && EXPERIMENTAL
+	help
+	  This enables the driver for the AVM Fritz!Card PCI,
+	  Fritz!Card PCI v2 and Fritz!Card PnP.
+	  (the latter also needs you to select "ISA Plug and Play support"
+	  from the menu "Plug and Play configuration")
+
+config HISAX_HDLC
+	bool
+	depends on HISAX_ST5481
+	default y
+
+config HISAX_AVM_A1_PCMCIA
+	bool
+	depends on HISAX_AVM_A1_CS
+	default y
+
+endif
+
+endmenu
+
diff --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile
new file mode 100644
index 000000000000..8d6bb56754b8
--- /dev/null
+++ b/drivers/isdn/hisax/Makefile
@@ -0,0 +1,64 @@
+# Makefile for the hisax ISDN device driver
+
+# The target object and module list name.
+
+# Define maximum number of cards
+
+EXTRA_CFLAGS      += -DHISAX_MAX_CARDS=$(CONFIG_HISAX_MAX_CARDS)
+
+obj-$(CONFIG_ISDN_DRV_HISAX)		+= hisax.o
+obj-$(CONFIG_HISAX_SEDLBAUER_CS)	+= sedlbauer_cs.o
+obj-$(CONFIG_HISAX_ELSA_CS)		+= elsa_cs.o
+obj-$(CONFIG_HISAX_AVM_A1_CS)		+= avma1_cs.o
+obj-$(CONFIG_HISAX_TELES_CS)		+= teles_cs.o
+obj-$(CONFIG_HISAX_ST5481)		+= hisax_st5481.o
+obj-$(CONFIG_HISAX_HFCUSB)		+= hfc_usb.o
+obj-$(CONFIG_HISAX_HFC4S8S)		+= hfc4s8s_l1.o
+obj-$(CONFIG_HISAX_FRITZ_PCIPNP)        += hisax_isac.o hisax_fcpcipnp.o
+
+ifdef CONFIG_HISAX_HDLC
+obj-$(CONFIG_ISDN_DRV_HISAX)		+= isdnhdlc.o
+endif
+
+# Multipart objects.
+
+hisax_st5481-y 				:= st5481_init.o st5481_usb.o st5481_d.o \
+					   st5481_b.o st5481_hdlc.o
+
+hisax-y	  				:= config.o isdnl1.o tei.o isdnl2.o isdnl3.o \
+		     			   lmgr.o q931.o callc.o fsm.o
+hisax-$(CONFIG_HISAX_EURO)		+= l3dss1.o
+hisax-$(CONFIG_HISAX_NI1)		+= l3ni1.o
+hisax-$(CONFIG_HISAX_1TR6)		+= l3_1tr6.o
+
+hisax-$(CONFIG_HISAX_16_0)		+= teles0.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_16_3)		+= teles3.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_TELESPCI)		+= telespci.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_S0BOX)		+= s0box.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_AVM_A1)		+= avm_a1.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_AVM_A1_PCMCIA)	+= avm_a1p.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_FRITZPCI)		+= avm_pci.o isac.o arcofi.o
+hisax-$(CONFIG_HISAX_ELSA)		+= elsa.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_IX1MICROR2)	+= ix1_micro.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_DIEHLDIVA)		+= diva.o isac.o arcofi.o hscx.o ipacx.o 
+hisax-$(CONFIG_HISAX_ASUSCOM)		+= asuscom.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_TELEINT)		+= teleint.o isac.o arcofi.o hfc_2bs0.o
+hisax-$(CONFIG_HISAX_SEDLBAUER)		+= sedlbauer.o isac.o arcofi.o hscx.o \
+					   isar.o
+hisax-$(CONFIG_HISAX_SPORTSTER)		+= sportster.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_MIC)		+= mic.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_NETJET)		+= nj_s.o netjet.o isac.o arcofi.o
+hisax-$(CONFIG_HISAX_NETJET_U)		+= nj_u.o netjet.o icc.o
+hisax-$(CONFIG_HISAX_HFCS)		+= hfcscard.o hfc_2bds0.o
+hisax-$(CONFIG_HISAX_HFC_PCI)		+= hfc_pci.o
+hisax-$(CONFIG_HISAX_HFC_SX)		+= hfc_sx.o
+hisax-$(CONFIG_HISAX_NICCY)		+= niccy.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_ISURF)		+= isurf.o isac.o arcofi.o isar.o
+hisax-$(CONFIG_HISAX_HSTSAPHIR)		+= saphir.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_BKM_A4T)		+= bkm_a4t.o isac.o arcofi.o jade.o
+hisax-$(CONFIG_HISAX_SCT_QUADRO)	+= bkm_a8.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_GAZEL)		+= gazel.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_W6692)		+= w6692.o
+hisax-$(CONFIG_HISAX_ENTERNOW_PCI)	+= enternow_pci.o amd7930_fn.o
+#hisax-$(CONFIG_HISAX_TESTEMU)		+= testemu.o
+
diff --git a/drivers/isdn/hisax/amd7930_fn.c b/drivers/isdn/hisax/amd7930_fn.c
new file mode 100644
index 000000000000..c4f861a5db25
--- /dev/null
+++ b/drivers/isdn/hisax/amd7930_fn.c
@@ -0,0 +1,796 @@
+/* gerdes_amd7930.c,v 0.99 2001/10/02
+ *
+ * gerdes_amd7930.c     Amd 79C30A and 79C32A specific routines
+ *                      (based on HiSax driver by Karsten Keil)
+ *
+ * Author               Christoph Ersfeld <info@formula-n.de>
+ *                      Formula-n Europe AG (www.formula-n.com)
+ *                      previously Gerdes AG
+ *
+ *
+ *                      This file is (c) under GNU PUBLIC LICENSE
+ *
+ *
+ * Notes:
+ * Version 0.99 is the first release of this driver and there are
+ * certainly a few bugs.
+ *
+ * Please don't report any malfunction to me without sending
+ * (compressed) debug-logs.
+ * It would be nearly impossible to retrace it.
+ *
+ * Log D-channel-processing as follows:
+ *
+ * 1. Load hisax with card-specific parameters, this example ist for
+ *    Formula-n enter:now ISDN PCI and compatible
+ *    (f.e. Gerdes Power ISDN PCI)
+ *
+ *    modprobe hisax type=41 protocol=2 id=gerdes
+ *
+ *    if you chose an other value for id, you need to modify the
+ *    code below, too.
+ *
+ * 2. set debug-level
+ *
+ *    hisaxctrl gerdes 1 0x3ff
+ *    hisaxctrl gerdes 11 0x4f
+ *    cat /dev/isdnctrl >> ~/log &
+ *
+ * Please take also a look into /var/log/messages if there is
+ * anything importand concerning HISAX.
+ *
+ *
+ * Credits:
+ * Programming the driver for Formula-n enter:now ISDN PCI and
+ * necessary this driver for the used Amd 7930 D-channel-controller
+ * was spnsored by Formula-n Europe AG.
+ * Thanks to Karsten Keil and Petr Novak, who gave me support in
+ * Hisax-specific questions.
+ * I want so say special thanks to Carl-Friedrich Braun, who had to
+ * answer a lot of questions about generally ISDN and about handling
+ * of the Amd-Chip.
+ *
+ */
+
+
+#include "hisax.h"
+#include "isdnl1.h"
+#include "isac.h"
+#include "amd7930_fn.h"
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+static void Amd7930_new_ph(struct IsdnCardState *cs);
+
+static WORD initAMD[] = {
+	0x0100,
+
+	0x00A5, 3, 0x01, 0x40, 0x58,				// LPR, LMR1, LMR2
+	0x0086, 1, 0x0B,					// DMR1 (D-Buffer TH-Interrupts on)
+	0x0087, 1, 0xFF,					// DMR2
+	0x0092, 1, 0x03,					// EFCR (extended mode d-channel-fifo on)
+	0x0090, 4, 0xFE, 0xFF, 0x02, 0x0F,			// FRAR4, SRAR4, DMR3, DMR4 (address recognition )
+	0x0084, 2, 0x80, 0x00,					// DRLR
+	0x00C0, 1, 0x47,					// PPCR1
+	0x00C8, 1, 0x01,					// PPCR2
+
+	0x0102,
+	0x0107,
+	0x01A1, 1,
+	0x0121, 1,
+	0x0189, 2,
+
+	0x0045, 4, 0x61, 0x72, 0x00, 0x00,			// MCR1, MCR2, MCR3, MCR4
+	0x0063, 2, 0x08, 0x08,					// GX
+	0x0064, 2, 0x08, 0x08,					// GR
+	0x0065, 2, 0x99, 0x00,					// GER
+	0x0066, 2, 0x7C, 0x8B,					// STG
+	0x0067, 2, 0x00, 0x00,					// FTGR1, FTGR2
+	0x0068, 2, 0x20, 0x20,					// ATGR1, ATGR2
+	0x0069, 1, 0x4F,					// MMR1
+	0x006A, 1, 0x00,					// MMR2
+	0x006C, 1, 0x40,					// MMR3
+	0x0021, 1, 0x02,					// INIT
+	0x00A3, 1, 0x40,					// LMR1
+
+	0xFFFF
+};
+
+
+void /* macro wWordAMD */
+WriteWordAmd7930(struct IsdnCardState *cs, BYTE reg, WORD val)
+{
+        wByteAMD(cs, 0x00, reg);
+        wByteAMD(cs, 0x01, LOBYTE(val));
+        wByteAMD(cs, 0x01, HIBYTE(val));
+}
+
+WORD /* macro rWordAMD */
+ReadWordAmd7930(struct IsdnCardState *cs, BYTE reg)
+{
+        WORD res;
+        /* direct access register */
+        if(reg < 8) {
+        	res = rByteAMD(cs, reg);
+                res += 256*rByteAMD(cs, reg);
+        }
+        /* indirect access register */
+        else {
+                wByteAMD(cs, 0x00, reg);
+	        res = rByteAMD(cs, 0x01);
+                res += 256*rByteAMD(cs, 0x01);
+        }
+	return (res);
+}
+
+
+static void
+Amd7930_ph_command(struct IsdnCardState *cs, u_char command, char *s)
+{
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "AMD7930: %s: ph_command 0x%02X", s, command);
+
+        cs->dc.amd7930.lmr1 = command;
+        wByteAMD(cs, 0xA3, command);
+}
+
+
+
+static BYTE i430States[] = {
+// to   reset  F3    F4    F5    F6    F7    F8    AR     from
+        0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00,   // init
+        0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00,   // reset
+        0x01, 0x02, 0x00, 0x00, 0x00, 0x09, 0x05, 0x04,   // F3
+        0x01, 0x02, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,   // F4
+        0x01, 0x02, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,   // F5
+        0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x05, 0x00,   // F6
+        0x11, 0x13, 0x00, 0x00, 0x1B, 0x00, 0x15, 0x00,   // F7
+        0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,   // F8
+        0x01, 0x03, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0A};  // AR
+
+
+/*                    Row     init    -   reset  F3    F4    F5    F6    F7    F8    AR */
+static BYTE stateHelper[] = { 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+
+
+
+static void
+Amd7930_get_state(struct IsdnCardState *cs) {
+        BYTE lsr = rByteAMD(cs, 0xA1);
+        cs->dc.amd7930.ph_state = (lsr & 0x7) + 2;
+        Amd7930_new_ph(cs);
+}
+
+
+
+static void
+Amd7930_new_ph(struct IsdnCardState *cs)
+{
+        u_char index = stateHelper[cs->dc.amd7930.old_state]*8 + stateHelper[cs->dc.amd7930.ph_state]-1;
+        u_char message = i430States[index];
+
+        if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "AMD7930: new_ph %d, old_ph %d, message %d, index %d",
+                        cs->dc.amd7930.ph_state, cs->dc.amd7930.old_state, message & 0x0f, index);
+
+        cs->dc.amd7930.old_state = cs->dc.amd7930.ph_state;
+
+        /* abort transmit if nessesary */
+        if ((message & 0xf0) && (cs->tx_skb)) {
+                wByteAMD(cs, 0x21, 0xC2);
+                wByteAMD(cs, 0x21, 0x02);
+        }
+
+	switch (message & 0x0f) {
+
+                case (1):
+                        l1_msg(cs, HW_RESET | INDICATION, NULL);
+                        Amd7930_get_state(cs);
+                        break;
+                case (2): /* init, Card starts in F3 */
+                        l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+                        break;
+                case (3):
+                        l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+                        break;
+                case (4):
+                        l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+                        Amd7930_ph_command(cs, 0x50, "HW_ENABLE REQUEST");
+                        break;
+                case (5):
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+                        break;
+                case (6):
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+                        break;
+                case (7): /* init, Card starts in F7 */
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+                        break;
+                case (8):
+                        l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+                        /* fall through */
+                case (9):
+                        Amd7930_ph_command(cs, 0x40, "HW_ENABLE REQ cleared if set");
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+                        break;
+                case (10):
+                        Amd7930_ph_command(cs, 0x40, "T3 expired, HW_ENABLE REQ cleared");
+                        cs->dc.amd7930.old_state = 3;
+                        break;
+                case (11):
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+                        break;
+		default:
+			break;
+	}
+}
+
+
+
+static void
+Amd7930_bh(struct IsdnCardState *cs)
+{
+
+        struct PStack *stptr;
+
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+                if (cs->debug)
+			debugl1(cs, "Amd7930: bh, D-Channel Busy cleared");
+		stptr = cs->stlist;
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			stptr = stptr->next;
+		}
+	}
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+	        if (cs->debug & L1_DEB_ISAC)
+		        debugl1(cs, "AMD7930: bh, D_L1STATECHANGE");
+                Amd7930_new_ph(cs);
+        }
+
+        if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) {
+	        if (cs->debug & L1_DEB_ISAC)
+		        debugl1(cs, "AMD7930: bh, D_RCVBUFREADY");
+                DChannel_proc_rcv(cs);
+        }
+
+        if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) {
+	        if (cs->debug & L1_DEB_ISAC)
+		        debugl1(cs, "AMD7930: bh, D_XMTBUFREADY");
+                DChannel_proc_xmt(cs);
+        }
+}
+
+static void
+Amd7930_empty_Dfifo(struct IsdnCardState *cs, int flag)
+{
+
+        BYTE stat, der;
+	BYTE *ptr;
+	struct sk_buff *skb;
+
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "Amd7930: empty_Dfifo");
+
+
+	ptr = cs->rcvbuf + cs->rcvidx;
+
+	/* AMD interrupts off */
+	AmdIrqOff(cs);
+
+	/* read D-Channel-Fifo*/
+	stat = rByteAMD(cs, 0x07); // DSR2
+
+		/* while Data in Fifo ... */
+		while ( (stat & 2) && ((ptr-cs->rcvbuf) < MAX_DFRAME_LEN_L1) ) {
+			*ptr = rByteAMD(cs, 0x04); // DCRB
+			ptr++;
+	                stat = rByteAMD(cs, 0x07); // DSR2
+			cs->rcvidx = ptr - cs->rcvbuf;
+
+                        /* Paket ready? */
+			if (stat & 1) {
+
+                                der = rWordAMD(cs, 0x03);
+
+                                /* no errors, packet ok */
+                                if(!der && !flag) {
+					rWordAMD(cs, 0x89); // clear DRCR
+
+                                        if ((cs->rcvidx) > 0) {
+                                                if (!(skb = alloc_skb(cs->rcvidx, GFP_ATOMIC)))
+							printk(KERN_WARNING "HiSax: Amd7930: empty_Dfifo, D receive out of memory!\n");
+						else {
+                                                        /* Debugging */
+                                                        if (cs->debug & L1_DEB_ISAC_FIFO) {
+								char *t = cs->dlog;
+
+								t += sprintf(t, "Amd7930: empty_Dfifo cnt: %d |", cs->rcvidx);
+								QuickHex(t, cs->rcvbuf, cs->rcvidx);
+								debugl1(cs, cs->dlog);
+							}
+                                                        /* moves received data in sk-buffer */
+							memcpy(skb_put(skb, cs->rcvidx), cs->rcvbuf, cs->rcvidx);
+							skb_queue_tail(&cs->rq, skb);
+						}
+					}
+
+				}
+                                /* throw damaged packets away, reset receive-buffer, indicate RX */
+				ptr = cs->rcvbuf;
+				cs->rcvidx = 0;
+				schedule_event(cs, D_RCVBUFREADY);
+			}
+                }
+		/* Packet to long, overflow */
+		if(cs->rcvidx >= MAX_DFRAME_LEN_L1) {
+			if (cs->debug & L1_DEB_WARN)
+			        debugl1(cs, "AMD7930: empty_Dfifo L2-Framelength overrun");
+			cs->rcvidx = 0;
+			return;
+		}
+	/* AMD interrupts on */
+	AmdIrqOn(cs);
+}
+
+
+static void
+Amd7930_fill_Dfifo(struct IsdnCardState *cs)
+{
+
+        WORD dtcrr, dtcrw, len, count;
+        BYTE txstat, dmr3;
+        BYTE *ptr, *deb_ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "Amd7930: fill_Dfifo");
+
+	if ((!cs->tx_skb) || (cs->tx_skb->len <= 0))
+		return;
+
+        dtcrw = 0;
+        if(!cs->dc.amd7930.tx_xmtlen)
+                /* new Frame */
+                len = dtcrw = cs->tx_skb->len;
+        /* continue frame */
+        else len = cs->dc.amd7930.tx_xmtlen;
+
+
+	/* AMD interrupts off */
+	AmdIrqOff(cs);
+
+        deb_ptr = ptr = cs->tx_skb->data;
+
+        /* while free place in tx-fifo available and data in sk-buffer */
+        txstat = 0x10;
+        while((txstat & 0x10) && (cs->tx_cnt < len)) {
+                wByteAMD(cs, 0x04, *ptr);
+                ptr++;
+                cs->tx_cnt++;
+                txstat= rByteAMD(cs, 0x07);
+        }
+        count = ptr - cs->tx_skb->data;
+	skb_pull(cs->tx_skb, count);
+
+
+        dtcrr = rWordAMD(cs, 0x85); // DTCR
+        dmr3  = rByteAMD(cs, 0x8E);
+
+	if (cs->debug & L1_DEB_ISAC) {
+		debugl1(cs, "Amd7930: fill_Dfifo, DMR3: 0x%02X, DTCR read: 0x%04X write: 0x%02X 0x%02X", dmr3, dtcrr, LOBYTE(dtcrw), HIBYTE(dtcrw));
+        }
+
+        /* writeing of dtcrw starts transmit */
+        if(!cs->dc.amd7930.tx_xmtlen) {
+                wWordAMD(cs, 0x85, dtcrw);
+                cs->dc.amd7930.tx_xmtlen = dtcrw;
+        }
+
+	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		debugl1(cs, "Amd7930: fill_Dfifo dbusytimer running");
+		del_timer(&cs->dbusytimer);
+	}
+	init_timer(&cs->dbusytimer);
+	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000);
+	add_timer(&cs->dbusytimer);
+
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "Amd7930: fill_Dfifo cnt: %d |", count);
+		QuickHex(t, deb_ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+	/* AMD interrupts on */
+        AmdIrqOn(cs);
+}
+
+
+void Amd7930_interrupt(struct IsdnCardState *cs, BYTE irflags)
+{
+	BYTE dsr1, dsr2, lsr;
+        WORD der;
+
+ while (irflags)
+ {
+
+        dsr1 = rByteAMD(cs, 0x02);
+        der  = rWordAMD(cs, 0x03);
+        dsr2 = rByteAMD(cs, 0x07);
+        lsr  = rByteAMD(cs, 0xA1);
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "Amd7930: interrupt: flags: 0x%02X, DSR1: 0x%02X, DSR2: 0x%02X, LSR: 0x%02X, DER=0x%04X", irflags, dsr1, dsr2, lsr, der);
+
+        /* D error -> read DER and DSR2 bit 2 */
+	if (der || (dsr2 & 4)) {
+
+                if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "Amd7930: interrupt: D error DER=0x%04X", der);
+
+                /* RX, TX abort if collision detected */
+                if (der & 2) {
+                        wByteAMD(cs, 0x21, 0xC2);
+                        wByteAMD(cs, 0x21, 0x02);
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+                        /* restart frame */
+                        if (cs->tx_skb) {
+				skb_push(cs->tx_skb, cs->tx_cnt);
+				cs->tx_cnt = 0;
+                                cs->dc.amd7930.tx_xmtlen = 0;
+				Amd7930_fill_Dfifo(cs);
+			} else {
+				printk(KERN_WARNING "HiSax: Amd7930 D-Collision, no skb\n");
+				debugl1(cs, "Amd7930: interrupt: D-Collision, no skb");
+			}
+                }
+                /* remove damaged data from fifo */
+		Amd7930_empty_Dfifo(cs, 1);
+
+		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+                /* restart TX-Frame */
+                if (cs->tx_skb) {
+			skb_push(cs->tx_skb, cs->tx_cnt);
+			cs->tx_cnt = 0;
+                        cs->dc.amd7930.tx_xmtlen = 0;
+			Amd7930_fill_Dfifo(cs);
+		}
+	}
+
+        /* D TX FIFO empty -> fill */
+	if (irflags & 1) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Amd7930: interrupt: clear Timer and fill D-TX-FIFO if data");
+
+		/* AMD interrupts off */
+                AmdIrqOff(cs);
+
+                if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+		if (cs->tx_skb) {
+			if (cs->tx_skb->len)
+				Amd7930_fill_Dfifo(cs);
+		}
+		/* AMD interrupts on */
+                AmdIrqOn(cs);
+	}
+
+
+        /* D RX FIFO full or tiny packet in Fifo -> empty */
+	if ((irflags & 2) || (dsr1 & 2)) {
+                if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Amd7930: interrupt: empty D-FIFO");
+                Amd7930_empty_Dfifo(cs, 0);
+	}
+
+
+        /* D-Frame transmit complete */
+	if (dsr1 & 64) {
+		if (cs->debug & L1_DEB_ISAC) {
+			debugl1(cs, "Amd7930: interrupt: transmit packet ready");
+        	}
+		/* AMD interrupts off */
+                AmdIrqOff(cs);
+
+                if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+
+                if (cs->tx_skb) {
+        		if (cs->debug & L1_DEB_ISAC)
+	        		debugl1(cs, "Amd7930: interrupt: TX-Packet ready, freeing skb");
+                        dev_kfree_skb_irq(cs->tx_skb);
+			cs->tx_cnt = 0;
+                        cs->dc.amd7930.tx_xmtlen=0;
+			cs->tx_skb = NULL;
+                }
+                if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+        		if (cs->debug & L1_DEB_ISAC)
+	        		debugl1(cs, "Amd7930: interrupt: TX-Packet ready, next packet dequeued");
+	        	cs->tx_cnt = 0;
+                        cs->dc.amd7930.tx_xmtlen=0;
+			Amd7930_fill_Dfifo(cs);
+		}
+                else
+			schedule_event(cs, D_XMTBUFREADY);
+		/* AMD interrupts on */
+                AmdIrqOn(cs);
+        }
+
+	/* LIU status interrupt -> read LSR, check statechanges */
+	if (lsr & 0x38) {
+                /* AMD interrupts off */
+                AmdIrqOff(cs);
+
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Amd: interrupt: LSR=0x%02X, LIU is in state %d", lsr, ((lsr & 0x7) +2));
+
+		cs->dc.amd7930.ph_state = (lsr & 0x7) + 2;
+
+		schedule_event(cs, D_L1STATECHANGE);
+		/* AMD interrupts on */
+                AmdIrqOn(cs);
+	}
+
+        /* reads Interrupt-Register again. If there is a new interrupt-flag: restart handler */
+        irflags = rByteAMD(cs, 0x00);
+ }
+
+}
+
+static void
+Amd7930_l1hw(struct PStack *st, int pr, void *arg)
+{
+        struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+        if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "Amd7930: l1hw called, pr: 0x%04X", pr);
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+                        if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+                                cs->dc.amd7930.tx_xmtlen=0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA", 0);
+#endif
+				Amd7930_fill_Dfifo(cs);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "Amd7930: l1hw: l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+                        cs->dc.amd7930.tx_xmtlen=0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA_PULLED", 0);
+#endif
+			Amd7930_fill_Dfifo(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "Amd7930: l1hw: -> PH_REQUEST_PULL, skb: %s", (cs->tx_skb)? "yes":"no");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			if ((cs->dc.amd7930.ph_state == 8)) {
+				/* b-channels off, PH-AR cleared
+				 * change to F3 */
+				Amd7930_ph_command(cs, 0x20, "HW_RESET REQEST"); //LMR1 bit 5
+				spin_unlock_irqrestore(&cs->lock, flags);
+			} else {
+				Amd7930_ph_command(cs, 0x40, "HW_RESET REQUEST");
+				cs->dc.amd7930.ph_state = 2;
+				spin_unlock_irqrestore(&cs->lock, flags);
+				Amd7930_new_ph(cs);
+			}
+			break;
+		case (HW_ENABLE | REQUEST):
+                        cs->dc.amd7930.ph_state = 9;
+                        Amd7930_new_ph(cs);
+			break;
+		case (HW_INFO3 | REQUEST):
+			// automatic
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			/* not implemented yet */
+			break;
+		case (HW_DEACTIVATE | RESPONSE):
+                        skb_queue_purge(&cs->rq);
+			skb_queue_purge(&cs->sq);
+			if (cs->tx_skb) {
+				dev_kfree_skb(cs->tx_skb);
+				cs->tx_skb = NULL;
+			}
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "Amd7930: l1hw: unknown %04x", pr);
+			break;
+	}
+}
+
+void
+setstack_Amd7930(struct PStack *st, struct IsdnCardState *cs)
+{
+
+        if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "Amd7930: setstack called");
+
+        st->l1.l1hw = Amd7930_l1hw;
+}
+
+
+void
+DC_Close_Amd7930(struct IsdnCardState *cs) {
+        if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "Amd7930: DC_Close called");
+}
+
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+	u_long flags;
+	struct PStack *stptr;
+        WORD dtcr, der;
+        BYTE dsr1, dsr2;
+
+
+        if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "Amd7930: dbusy_timer expired!");
+
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		spin_lock_irqsave(&cs->lock, flags);
+                /* D Transmit Byte Count Register:
+                 * Counts down packet's number of Bytes, 0 if packet ready */
+                dtcr = rWordAMD(cs, 0x85);
+                dsr1 = rByteAMD(cs, 0x02);
+                dsr2 = rByteAMD(cs, 0x07);
+                der  = rWordAMD(cs, 0x03);
+
+	        if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Amd7930: dbusy_timer_handler: DSR1=0x%02X, DSR2=0x%02X, DER=0x%04X, cs->tx_skb->len=%u, tx_stat=%u, dtcr=%u, cs->tx_cnt=%u", dsr1, dsr2, der, cs->tx_skb->len, cs->dc.amd7930.tx_xmtlen, dtcr, cs->tx_cnt);
+
+		if ((cs->dc.amd7930.tx_xmtlen - dtcr) < cs->tx_cnt) {	/* D-Channel Busy */
+			test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+			stptr = cs->stlist;
+			spin_unlock_irqrestore(&cs->lock, flags);
+			while (stptr != NULL) {
+				stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+				stptr = stptr->next;
+			}
+
+		} else {
+			/* discard frame; reset transceiver */
+			test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+                                cs->dc.amd7930.tx_xmtlen = 0;
+			} else {
+				printk(KERN_WARNING "HiSax: Amd7930: D-Channel Busy no skb\n");
+				debugl1(cs, "Amd7930: D-Channel Busy no skb");
+
+			}
+			/* Transmitter reset, abort transmit */
+			wByteAMD(cs, 0x21, 0x82);
+			wByteAMD(cs, 0x21, 0x02);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			cs->irq_func(cs->irq, cs, NULL);
+
+                        if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "Amd7930: dbusy_timer_handler: Transmitter reset");
+		}
+	}
+}
+
+
+
+void __devinit
+Amd7930_init(struct IsdnCardState *cs)
+{
+    WORD *ptr;
+    BYTE cmd, cnt;
+
+        if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "Amd7930: initamd called");
+
+        cs->dc.amd7930.tx_xmtlen = 0;
+        cs->dc.amd7930.old_state = 0;
+        cs->dc.amd7930.lmr1 = 0x40;
+        cs->dc.amd7930.ph_command = Amd7930_ph_command;
+	cs->setstack_d = setstack_Amd7930;
+	cs->DC_Close = DC_Close_Amd7930;
+
+	/* AMD Initialisation */
+	for (ptr = initAMD; *ptr != 0xFFFF; ) {
+		cmd = LOBYTE(*ptr);
+
+                /* read */
+                if (*ptr++ >= 0x100) {
+			if (cmd < 8)
+                                /* setzt Register zurück */
+                                rByteAMD(cs, cmd);
+			else {
+				wByteAMD(cs, 0x00, cmd);
+				for (cnt = *ptr++; cnt > 0; cnt--)
+					rByteAMD(cs, 0x01);
+			}
+		}
+                /* write */
+                else if (cmd < 8)
+			wByteAMD(cs, cmd, LOBYTE(*ptr++));
+
+		else {
+			wByteAMD(cs, 0x00, cmd);
+			for (cnt = *ptr++; cnt > 0; cnt--)
+				wByteAMD(cs, 0x01, LOBYTE(*ptr++));
+		}
+	}
+}
+
+void __devinit
+setup_Amd7930(struct IsdnCardState *cs)
+{
+        INIT_WORK(&cs->tqueue, (void *)(void *) Amd7930_bh, cs);
+	cs->dbusytimer.function = (void *) dbusy_timer_handler;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+}
diff --git a/drivers/isdn/hisax/amd7930_fn.h b/drivers/isdn/hisax/amd7930_fn.h
new file mode 100644
index 000000000000..e039c3a0f2a2
--- /dev/null
+++ b/drivers/isdn/hisax/amd7930_fn.h
@@ -0,0 +1,37 @@
+/* 2001/10/02
+ *
+ * gerdes_amd7930.h     Header-file included by
+ *                      gerdes_amd7930.c
+ *
+ * Author               Christoph Ersfeld <info@formula-n.de>
+ *                      Formula-n Europe AG (www.formula-n.com)
+ *                      previously Gerdes AG
+ *
+ *
+ *                      This file is (c) under GNU PUBLIC LICENSE
+ */
+
+
+
+
+#define BYTE							unsigned char
+#define WORD							unsigned int
+#define rByteAMD(cs, reg)					cs->readisac(cs, reg)
+#define wByteAMD(cs, reg, val)					cs->writeisac(cs, reg, val)
+#define rWordAMD(cs, reg)					ReadWordAmd7930(cs, reg)
+#define wWordAMD(cs, reg, val)					WriteWordAmd7930(cs, reg, val)
+#define HIBYTE(w)						((unsigned char)((w & 0xff00) / 256))
+#define LOBYTE(w)						((unsigned char)(w & 0x00ff))
+
+#define AmdIrqOff(cs)						cs->dc.amd7930.setIrqMask(cs, 0)
+#define AmdIrqOn(cs)						cs->dc.amd7930.setIrqMask(cs, 1)
+
+#define AMD_CR		0x00
+#define AMD_DR		0x01
+
+
+#define DBUSY_TIMER_VALUE 80
+
+extern void Amd7930_interrupt(struct IsdnCardState *, unsigned char);
+extern void Amd7930_init(struct IsdnCardState *);
+extern void setup_Amd7930(struct IsdnCardState *);
diff --git a/drivers/isdn/hisax/arcofi.c b/drivers/isdn/hisax/arcofi.c
new file mode 100644
index 000000000000..d30ce5b978c2
--- /dev/null
+++ b/drivers/isdn/hisax/arcofi.c
@@ -0,0 +1,134 @@
+/* $Id: arcofi.c,v 1.14.2.3 2004/01/13 14:31:24 keil Exp $
+ *
+ * Ansteuerung ARCOFI 2165
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+ 
+#include "hisax.h"
+#include "isdnl1.h"
+#include "isac.h"
+#include "arcofi.h"
+
+#define ARCOFI_TIMER_VALUE	20
+
+static void
+add_arcofi_timer(struct IsdnCardState *cs) {
+	if (test_and_set_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
+		del_timer(&cs->dc.isac.arcofitimer);
+	}	
+	init_timer(&cs->dc.isac.arcofitimer);
+	cs->dc.isac.arcofitimer.expires = jiffies + ((ARCOFI_TIMER_VALUE * HZ)/1000);
+	add_timer(&cs->dc.isac.arcofitimer);
+}
+
+static void
+send_arcofi(struct IsdnCardState *cs) {
+	u_char val;
+	
+	add_arcofi_timer(cs);
+	cs->dc.isac.mon_txp = 0;
+	cs->dc.isac.mon_txc = cs->dc.isac.arcofi_list->len;
+	memcpy(cs->dc.isac.mon_tx, cs->dc.isac.arcofi_list->msg, cs->dc.isac.mon_txc);
+	switch(cs->dc.isac.arcofi_bc) {
+		case 0: break;
+		case 1: cs->dc.isac.mon_tx[1] |= 0x40;
+			break;
+		default: break;
+	}
+	cs->dc.isac.mocr &= 0x0f;
+	cs->dc.isac.mocr |= 0xa0;
+	cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+	val = cs->readisac(cs, ISAC_MOSR);
+	cs->writeisac(cs, ISAC_MOX1, cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]);
+	cs->dc.isac.mocr |= 0x10;
+	cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+}
+
+int
+arcofi_fsm(struct IsdnCardState *cs, int event, void *data) {
+	if (cs->debug & L1_DEB_MONITOR) {
+		debugl1(cs, "arcofi state %d event %d", cs->dc.isac.arcofi_state, event);
+	}
+	if (event == ARCOFI_TIMEOUT) {
+		cs->dc.isac.arcofi_state = ARCOFI_NOP;
+		test_and_set_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags);
+		wake_up(&cs->dc.isac.arcofi_wait);
+ 		return(1);
+	}
+	switch (cs->dc.isac.arcofi_state) {
+		case ARCOFI_NOP:
+			if (event == ARCOFI_START) {
+				cs->dc.isac.arcofi_list = data;
+				cs->dc.isac.arcofi_state = ARCOFI_TRANSMIT;
+				send_arcofi(cs);
+			}
+			break;
+		case ARCOFI_TRANSMIT:
+			if (event == ARCOFI_TX_END) {
+				if (cs->dc.isac.arcofi_list->receive) {
+					add_arcofi_timer(cs);
+					cs->dc.isac.arcofi_state = ARCOFI_RECEIVE;
+				} else {
+					if (cs->dc.isac.arcofi_list->next) {
+						cs->dc.isac.arcofi_list =
+							cs->dc.isac.arcofi_list->next;
+						send_arcofi(cs);
+					} else {
+						if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
+							del_timer(&cs->dc.isac.arcofitimer);
+						}
+						cs->dc.isac.arcofi_state = ARCOFI_NOP;
+						wake_up(&cs->dc.isac.arcofi_wait);
+					}
+				}
+			}
+			break;
+		case ARCOFI_RECEIVE:
+			if (event == ARCOFI_RX_END) {
+				if (cs->dc.isac.arcofi_list->next) {
+					cs->dc.isac.arcofi_list =
+						cs->dc.isac.arcofi_list->next;
+					cs->dc.isac.arcofi_state = ARCOFI_TRANSMIT;
+					send_arcofi(cs);
+				} else {
+					if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
+						del_timer(&cs->dc.isac.arcofitimer);
+					}
+					cs->dc.isac.arcofi_state = ARCOFI_NOP;
+					wake_up(&cs->dc.isac.arcofi_wait);
+				}
+			}
+			break;
+		default:
+			debugl1(cs, "Arcofi unknown state %x", cs->dc.isac.arcofi_state);
+			return(2);
+	}
+	return(0);
+}
+
+static void
+arcofi_timer(struct IsdnCardState *cs) {
+	arcofi_fsm(cs, ARCOFI_TIMEOUT, NULL);
+}
+
+void
+clear_arcofi(struct IsdnCardState *cs) {
+	if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
+		del_timer(&cs->dc.isac.arcofitimer);
+	}
+}
+
+void
+init_arcofi(struct IsdnCardState *cs) {
+	cs->dc.isac.arcofitimer.function = (void *) arcofi_timer;
+	cs->dc.isac.arcofitimer.data = (long) cs;
+	init_timer(&cs->dc.isac.arcofitimer);
+	init_waitqueue_head(&cs->dc.isac.arcofi_wait);
+	test_and_set_bit(HW_ARCOFI, &cs->HW_Flags);
+}
diff --git a/drivers/isdn/hisax/arcofi.h b/drivers/isdn/hisax/arcofi.h
new file mode 100644
index 000000000000..00c44d3ce972
--- /dev/null
+++ b/drivers/isdn/hisax/arcofi.h
@@ -0,0 +1,27 @@
+/* $Id: arcofi.h,v 1.6.6.2 2001/09/23 22:24:46 kai Exp $
+ *
+ * Ansteuerung ARCOFI 2165
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+ 
+#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
+
+extern int arcofi_fsm(struct IsdnCardState *cs, int event, void *data);
+extern void init_arcofi(struct IsdnCardState *cs);
+extern void clear_arcofi(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/asuscom.c b/drivers/isdn/hisax/asuscom.c
new file mode 100644
index 000000000000..7546e2e4a94e
--- /dev/null
+++ b/drivers/isdn/hisax/asuscom.c
@@ -0,0 +1,427 @@
+/* $Id: asuscom.c,v 1.14.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for ASUSCOM NETWORK INC. ISDNLink cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to  ASUSCOM NETWORK INC. Taiwan and  Dynalink NL for information
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include "hisax.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *Asuscom_revision = "$Revision: 1.14.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ASUS_ISAC	0
+#define ASUS_HSCX	1
+#define ASUS_ADR	2
+#define ASUS_CTRL_U7	3
+#define ASUS_CTRL_POTS	5
+
+#define ASUS_IPAC_ALE	0
+#define ASUS_IPAC_DATA	1
+
+#define ASUS_ISACHSCX	1
+#define ASUS_IPAC	2
+
+/* CARD_ADR (Write) */
+#define ASUS_RESET      0x80	/* Bit 7 Reset-Leitung */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.asus.adr,
+			cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.asus.adr,
+		 cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0x0);
+	writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+asuscom_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista, val, icnt = 5;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	ista = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA | 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "ASUS IRQ LOOP\n");
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_asuscom(struct IsdnCardState *cs)
+{
+	int bytecnt = 8;
+
+	if (cs->hw.asus.cfg_reg)
+		release_region(cs->hw.asus.cfg_reg, bytecnt);
+}
+
+static void
+reset_asuscom(struct IsdnCardState *cs)
+{
+	if (cs->subtyp == ASUS_IPAC)
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x20);
+	else
+		byteout(cs->hw.asus.adr, ASUS_RESET);	/* Reset On */
+	mdelay(10);
+	if (cs->subtyp == ASUS_IPAC)
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x0);
+	else
+		byteout(cs->hw.asus.adr, 0);	/* Reset Off */
+	mdelay(10);
+	if (cs->subtyp == ASUS_IPAC) {
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ACFG, 0xff);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_AOE, 0x0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xc0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_PCFG, 0x12);
+	}
+}
+
+static int
+Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_asuscom(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_asuscom(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->debug |= L1_DEB_IPAC;
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id asus_ids[] __initdata = {
+	{ ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1688),
+	  ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1688), 
+	  (unsigned long) "Asus1688 PnP" },
+	{ ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1690),
+	  ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1690), 
+	  (unsigned long) "Asus1690 PnP" },
+	{ ISAPNP_VENDOR('S', 'I', 'E'), ISAPNP_FUNCTION(0x0020),
+	  ISAPNP_VENDOR('S', 'I', 'E'), ISAPNP_FUNCTION(0x0020), 
+	  (unsigned long) "Isurf2 PnP" },
+	{ ISAPNP_VENDOR('E', 'L', 'F'), ISAPNP_FUNCTION(0x0000),
+	  ISAPNP_VENDOR('E', 'L', 'F'), ISAPNP_FUNCTION(0x0000), 
+	  (unsigned long) "Iscas TE320" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __initdata = &asus_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __init
+setup_asuscom(struct IsdnCard *card)
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	u_char val;
+	char tmp[64];
+
+	strcpy(tmp, Asuscom_revision);
+	printk(KERN_INFO "HiSax: Asuscom ISDNLink driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_ASUSCOM)
+		return (0);
+#ifdef __ISAPNP__
+	if (!card->para[1] && isapnp_present()) {
+		struct pnp_dev *pnp_d;
+		while(ipid->card_vendor) {
+			if ((pnp_c = pnp_find_card(ipid->card_vendor,
+				ipid->card_device, pnp_c))) {
+				pnp_d = NULL;
+				if ((pnp_d = pnp_find_dev(pnp_c,
+					ipid->vendor, ipid->function, pnp_d))) {
+					int err;
+
+					printk(KERN_INFO "HiSax: %s detected\n",
+						(char *)ipid->driver_data);
+					pnp_disable_dev(pnp_d);
+					err = pnp_activate_dev(pnp_d);
+					if (err<0) {
+						printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+							__FUNCTION__, err);
+						return(0);
+					}
+					card->para[1] = pnp_port_start(pnp_d, 0);
+					card->para[0] = pnp_irq(pnp_d, 0);
+					if (!card->para[0] || !card->para[1]) {
+						printk(KERN_ERR "AsusPnP:some resources are missing %ld/%lx\n",
+							card->para[0], card->para[1]);
+						pnp_disable_dev(pnp_d);
+						return(0);
+					}
+					break;
+				} else {
+					printk(KERN_ERR "AsusPnP: PnP error card found, no device\n");
+				}
+			}
+			ipid++;
+			pnp_c = NULL;
+		} 
+		if (!ipid->card_vendor) {
+			printk(KERN_INFO "AsusPnP: no ISAPnP card found\n");
+			return(0);
+		}
+	}
+#endif
+	bytecnt = 8;
+	cs->hw.asus.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	if (!request_region(cs->hw.asus.cfg_reg, bytecnt, "asuscom isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.asus.cfg_reg,
+		       cs->hw.asus.cfg_reg + bytecnt);
+		return (0);
+	}
+	printk(KERN_INFO "ISDNLink: defined at 0x%x IRQ %d\n",
+		cs->hw.asus.cfg_reg, cs->irq);
+	setup_isac(cs);
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Asus_card_msg;
+	val = readreg(cs->hw.asus.cfg_reg + ASUS_IPAC_ALE, 
+		cs->hw.asus.cfg_reg + ASUS_IPAC_DATA, IPAC_ID);
+	if ((val == 1) || (val == 2)) {
+		cs->subtyp = ASUS_IPAC;
+		cs->hw.asus.adr  = cs->hw.asus.cfg_reg + ASUS_IPAC_ALE;
+		cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
+		cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
+		test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		cs->readisac = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		cs->irq_func = &asuscom_interrupt_ipac;
+		printk(KERN_INFO "Asus: IPAC version %x\n", val);
+	} else {
+		cs->subtyp = ASUS_ISACHSCX;
+		cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR;
+		cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC;
+		cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX;
+		cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7;
+		cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS;
+		cs->readisac = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		cs->irq_func = &asuscom_interrupt;
+		ISACVersion(cs, "ISDNLink:");
+		if (HscxVersion(cs, "ISDNLink:")) {
+			printk(KERN_WARNING
+		     	"ISDNLink: wrong HSCX versions check IO address\n");
+			release_io_asuscom(cs);
+			return (0);
+		}
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/avm_a1.c b/drivers/isdn/hisax/avm_a1.c
new file mode 100644
index 000000000000..8f028d42fd2f
--- /dev/null
+++ b/drivers/isdn/hisax/avm_a1.c
@@ -0,0 +1,317 @@
+/* $Id: avm_a1.c,v 2.15.2.4 2004/01/13 21:46:03 keil Exp $
+ *
+ * low level stuff for AVM A1 (Fritz) isdn cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+static const char *avm_revision = "$Revision: 2.15.2.4 $";
+
+#define	 AVM_A1_STAT_ISAC	0x01
+#define	 AVM_A1_STAT_HSCX	0x02
+#define	 AVM_A1_STAT_TIMER	0x04
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int adr, u_char off)
+{
+	return (bytein(adr + off));
+}
+
+static inline void
+writereg(unsigned int adr, u_char off, u_char data)
+{
+	byteout(adr + off, data);
+}
+
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+	insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.avm.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.avm.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo(cs->hw.avm.isacfifo, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo(cs->hw.avm.isacfifo, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.avm.hscx[hscx], offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.avm.hscx[hscx], offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.avm.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.avm.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	while (((sval = bytein(cs->hw.avm.cfg_reg)) & 0xf) != 0x7) {
+		if (!(sval & AVM_A1_STAT_TIMER)) {
+			byteout(cs->hw.avm.cfg_reg, 0x1E);
+			sval = bytein(cs->hw.avm.cfg_reg);
+		} else if (cs->debug & L1_DEB_INTSTAT)
+			debugl1(cs, "avm IntStatus %x", sval);
+		if (!(sval & AVM_A1_STAT_HSCX)) {
+			val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA);
+			if (val)
+				hscx_int_main(cs, val);
+		}
+		if (!(sval & AVM_A1_STAT_ISAC)) {
+			val = readreg(cs->hw.avm.isac, ISAC_ISTA);
+			if (val)
+				isac_interrupt(cs, val);
+		}
+	}
+	writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF);
+	writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF);
+	writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.avm.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0);
+	writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+inline static void
+release_ioregs(struct IsdnCardState *cs, int mask)
+{
+	release_region(cs->hw.avm.cfg_reg, 8);
+	if (mask & 1)
+		release_region(cs->hw.avm.isac + 32, 32);
+	if (mask & 2)
+		release_region(cs->hw.avm.isacfifo, 1);
+	if (mask & 4)
+		release_region(cs->hw.avm.hscx[0] + 32, 32);
+	if (mask & 8)
+		release_region(cs->hw.avm.hscxfifo[0], 1);
+	if (mask & 0x10)
+		release_region(cs->hw.avm.hscx[1] + 32, 32);
+	if (mask & 0x20)
+		release_region(cs->hw.avm.hscxfifo[1], 1);
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			release_ioregs(cs, 0x3f);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 1);
+			byteout(cs->hw.avm.cfg_reg, 0x16);
+			byteout(cs->hw.avm.cfg_reg, 0x1E);
+			inithscxisac(cs, 2);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+int __init
+setup_avm_a1(struct IsdnCard *card)
+{
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, avm_revision);
+	printk(KERN_INFO "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_A1)
+		return (0);
+
+	cs->hw.avm.cfg_reg = card->para[1] + 0x1800;
+	cs->hw.avm.isac = card->para[1] + 0x1400 - 0x20;
+	cs->hw.avm.hscx[0] = card->para[1] + 0x400 - 0x20;
+	cs->hw.avm.hscx[1] = card->para[1] + 0xc00 - 0x20;
+	cs->hw.avm.isacfifo = card->para[1] + 0x1000;
+	cs->hw.avm.hscxfifo[0] = card->para[1];
+	cs->hw.avm.hscxfifo[1] = card->para[1] + 0x800;
+	cs->irq = card->para[0];
+	if (!request_region(cs->hw.avm.cfg_reg, 8, "avm cfg")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.avm.cfg_reg,
+		       cs->hw.avm.cfg_reg + 8);
+		return (0);
+	}
+	if (!request_region(cs->hw.avm.isac + 32, 32, "HiSax isac")) {
+		printk(KERN_WARNING
+		       "HiSax: %s isac ports %x-%x already in use\n",
+		       CardType[cs->typ],
+		       cs->hw.avm.isac + 32,
+		       cs->hw.avm.isac + 64);
+		release_ioregs(cs, 0);
+		return (0);
+	}
+	if (!request_region(cs->hw.avm.isacfifo, 1, "HiSax isac fifo")) {
+		printk(KERN_WARNING
+		       "HiSax: %s isac fifo port %x already in use\n",
+		       CardType[cs->typ],
+		       cs->hw.avm.isacfifo);
+		release_ioregs(cs, 1);
+		return (0);
+	}
+	if (!request_region(cs->hw.avm.hscx[0] + 32, 32, "HiSax hscx A")) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx A ports %x-%x already in use\n",
+		       CardType[cs->typ],
+		       cs->hw.avm.hscx[0] + 32,
+		       cs->hw.avm.hscx[0] + 64);
+		release_ioregs(cs, 3);
+		return (0);
+	}
+	if (!request_region(cs->hw.avm.hscxfifo[0], 1, "HiSax hscx A fifo")) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx A fifo port %x already in use\n",
+		       CardType[cs->typ],
+		       cs->hw.avm.hscxfifo[0]);
+		release_ioregs(cs, 7);
+		return (0);
+	}
+	if (!request_region(cs->hw.avm.hscx[1] + 32, 32, "HiSax hscx B")) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx B ports %x-%x already in use\n",
+		       CardType[cs->typ],
+		       cs->hw.avm.hscx[1] + 32,
+		       cs->hw.avm.hscx[1] + 64);
+		release_ioregs(cs, 0xf);
+		return (0);
+	}
+	if (!request_region(cs->hw.avm.hscxfifo[1], 1, "HiSax hscx B fifo")) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx B fifo port %x already in use\n",
+		       CardType[cs->typ],
+		       cs->hw.avm.hscxfifo[1]);
+		release_ioregs(cs, 0x1f);
+		return (0);
+	}
+	byteout(cs->hw.avm.cfg_reg, 0x0);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg, 0x1);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg, 0x0);
+	HZDELAY(HZ / 5 + 1);
+	val = cs->irq;
+	if (val == 9)
+		val = 2;
+	byteout(cs->hw.avm.cfg_reg + 1, val);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg, 0x0);
+	HZDELAY(HZ / 5 + 1);
+
+	val = bytein(cs->hw.avm.cfg_reg);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       cs->hw.avm.cfg_reg, val);
+	val = bytein(cs->hw.avm.cfg_reg + 3);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       cs->hw.avm.cfg_reg + 3, val);
+	val = bytein(cs->hw.avm.cfg_reg + 2);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       cs->hw.avm.cfg_reg + 2, val);
+	val = bytein(cs->hw.avm.cfg_reg);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       cs->hw.avm.cfg_reg, val);
+
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.avm.cfg_reg);
+	printk(KERN_INFO
+	       "HiSax: isac:0x%X/0x%X\n",
+	       cs->hw.avm.isac + 32, cs->hw.avm.isacfifo);
+	printk(KERN_INFO
+	       "HiSax: hscx A:0x%X/0x%X  hscx B:0x%X/0x%X\n",
+	       cs->hw.avm.hscx[0] + 32, cs->hw.avm.hscxfifo[0],
+	       cs->hw.avm.hscx[1] + 32, cs->hw.avm.hscxfifo[1]);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	setup_isac(cs);
+	cs->cardmsg = &AVM_card_msg;
+	cs->irq_func = &avm_a1_interrupt;
+	ISACVersion(cs, "AVM A1:");
+	if (HscxVersion(cs, "AVM A1:")) {
+		printk(KERN_WARNING
+		       "AVM A1: wrong HSCX versions check IO address\n");
+		release_ioregs(cs, 0x3f);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/avm_a1p.c b/drivers/isdn/hisax/avm_a1p.c
new file mode 100644
index 000000000000..d643bb32ad09
--- /dev/null
+++ b/drivers/isdn/hisax/avm_a1p.c
@@ -0,0 +1,268 @@
+/* $Id: avm_a1p.c,v 2.9.2.5 2004/01/24 20:47:19 keil Exp $
+ *
+ * low level stuff for the following AVM cards:
+ * A1 PCMCIA
+ * FRITZ!Card PCMCIA
+ * FRITZ!Card PCMCIA 2.0
+ *
+ * Author       Carsten Paeth
+ * Copyright    by Carsten Paeth     <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+/* register offsets */
+#define ADDRREG_OFFSET		0x02
+#define DATAREG_OFFSET		0x03
+#define ASL0_OFFSET		0x04
+#define ASL1_OFFSET		0x05
+#define MODREG_OFFSET		0x06
+#define VERREG_OFFSET		0x07
+
+/* address offsets */
+#define ISAC_FIFO_OFFSET	0x00
+#define ISAC_REG_OFFSET		0x20
+#define HSCX_CH_DIFF		0x40
+#define HSCX_FIFO_OFFSET	0x80
+#define HSCX_REG_OFFSET		0xa0
+
+/* read bits ASL0 */
+#define	 ASL0_R_TIMER		0x10 /* active low */
+#define	 ASL0_R_ISAC		0x20 /* active low */
+#define	 ASL0_R_HSCX		0x40 /* active low */
+#define	 ASL0_R_TESTBIT		0x80
+#define  ASL0_R_IRQPENDING	(ASL0_R_ISAC|ASL0_R_HSCX|ASL0_R_TIMER)
+
+/* write bits ASL0 */
+#define	 ASL0_W_RESET		0x01
+#define	 ASL0_W_TDISABLE	0x02
+#define	 ASL0_W_TRESET		0x04
+#define	 ASL0_W_IRQENABLE	0x08
+#define	 ASL0_W_TESTBIT		0x80
+
+/* write bits ASL1 */
+#define	 ASL1_W_LED0		0x10
+#define	 ASL1_W_LED1		0x20
+#define	 ASL1_W_ENABLE_S0	0xC0
+ 
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static const char *avm_revision = "$Revision: 2.9.2.5 $";
+
+static inline u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+        u_char ret;
+
+        offset -= 0x20;
+        byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+	ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+	return ret;
+}
+
+static inline void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+        offset -= 0x20;
+        byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+	byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+}
+
+static inline void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+	insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+}
+
+static inline void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+	outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+}
+
+static inline u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	u_char ret;
+
+        offset -= 0x20;
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+	ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+	return ret;
+}
+
+static inline void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+        offset -= 0x20;
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+	byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+}
+
+static inline void
+ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+	insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+}
+
+static inline void
+WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+	outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt) 
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+avm_a1p_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	while ((sval = (~bytein(cs->hw.avm.cfg_reg+ASL0_OFFSET) & ASL0_R_IRQPENDING))) {
+		if (cs->debug & L1_DEB_INTSTAT)
+			debugl1(cs, "avm IntStatus %x", sval);
+		if (sval & ASL0_R_HSCX) {
+                        val = ReadHSCX(cs, 1, HSCX_ISTA);
+			if (val)
+				hscx_int_main(cs, val);
+		}
+		if (sval & ASL0_R_ISAC) {
+			val = ReadISAC(cs, ISAC_ISTA);
+			if (val)
+				isac_interrupt(cs, val);
+		}
+	}
+	WriteHSCX(cs, 0, HSCX_MASK, 0xff);
+	WriteHSCX(cs, 1, HSCX_MASK, 0xff);
+	WriteISAC(cs, ISAC_MASK, 0xff);
+	WriteISAC(cs, ISAC_MASK, 0x00);
+	WriteHSCX(cs, 0, HSCX_MASK, 0x00);
+	WriteHSCX(cs, 1, HSCX_MASK, 0x00);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+			HZDELAY(HZ / 5 + 1);
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+			HZDELAY(HZ / 5 + 1);
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return 0;
+
+		case CARD_RELEASE:
+			/* free_irq is done in HiSax_closecard(). */
+		        /* free_irq(cs->irq, cs); */
+			return 0;
+
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_TDISABLE|ASL0_W_TRESET|ASL0_W_IRQENABLE);
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			inithscxisac(cs, 1);
+			inithscxisac(cs, 2);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return 0;
+
+		case CARD_TEST:
+			/* we really don't need it for the PCMCIA Version */
+			return 0;
+
+		default:
+			/* all card drivers ignore others, so we do the same */
+			return 0;
+	}
+	return 0;
+}
+
+int
+setup_avm_a1_pcmcia(struct IsdnCard *card)
+{
+	u_char model, vers;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+
+	strcpy(tmp, avm_revision);
+	printk(KERN_INFO "HiSax: AVM A1 PCMCIA driver Rev. %s\n",
+						 HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_A1_PCMCIA)
+		return (0);
+
+	cs->hw.avm.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+
+
+	byteout(cs->hw.avm.cfg_reg+ASL1_OFFSET, ASL1_W_ENABLE_S0);
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET, ASL0_W_TDISABLE|ASL0_W_TRESET);
+
+	model = bytein(cs->hw.avm.cfg_reg+MODREG_OFFSET);
+	vers = bytein(cs->hw.avm.cfg_reg+VERREG_OFFSET);
+
+	printk(KERN_INFO "AVM A1 PCMCIA: io 0x%x irq %d model %d version %d\n",
+				cs->hw.avm.cfg_reg, cs->irq, model, vers);
+
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &AVM_card_msg;
+	cs->irq_flags = SA_SHIRQ;
+	cs->irq_func = &avm_a1p_interrupt;
+
+	ISACVersion(cs, "AVM A1 PCMCIA:");
+	if (HscxVersion(cs, "AVM A1 PCMCIA:")) {
+		printk(KERN_WARNING
+		       "AVM A1 PCMCIA: wrong HSCX versions check IO address\n");
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c
new file mode 100644
index 000000000000..6fcb2cf7b0b6
--- /dev/null
+++ b/drivers/isdn/hisax/avm_pci.c
@@ -0,0 +1,865 @@
+/* $Id: avm_pci.c,v 1.29.2.4 2004/02/11 13:21:32 keil Exp $
+ *
+ * low level stuff for AVM Fritz!PCI and ISA PnP isdn cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to AVM, Berlin for information
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/interrupt.h>
+
+extern const char *CardType[];
+static const char *avm_pci_rev = "$Revision: 1.29.2.4 $";
+
+#define  AVM_FRITZ_PCI		1
+#define  AVM_FRITZ_PNP		2
+
+#define  HDLC_FIFO		0x0
+#define  HDLC_STATUS		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
+
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+	register u_char val;
+
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	val = inb(cs->hw.avm.isac + (offset & 0xf));
+	return (val);
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	outb(value, cs->hw.avm.isac + (offset & 0xf));
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+	insb(cs->hw.avm.isac, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+	outsb(cs->hw.avm.isac, data, size);
+}
+
+static inline u_int
+ReadHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset)
+{
+	register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+	register u_int val;
+
+	outl(idx, cs->hw.avm.cfg_reg + 4);
+	val = inl(cs->hw.avm.isac + offset);
+	return (val);
+}
+
+static inline void
+WriteHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset, u_int value)
+{
+	register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+
+	outl(idx, cs->hw.avm.cfg_reg + 4);
+	outl(value, cs->hw.avm.isac + offset);
+}
+
+static inline u_char
+ReadHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset)
+{
+	register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+	register u_char val;
+
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	val = inb(cs->hw.avm.isac + offset);
+	return (val);
+}
+
+static inline void
+WriteHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+	register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	outb(value, cs->hw.avm.isac + offset);
+}
+
+static u_char
+ReadHDLC_s(struct IsdnCardState *cs, int chan, u_char offset)
+{
+	return(0xff & ReadHDLCPCI(cs, chan, offset));
+}
+
+static void
+WriteHDLC_s(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+	WriteHDLCPCI(cs, chan, offset, value);
+}
+
+static inline
+struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+	if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+		return(&cs->bcs[0]);
+	else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+		return(&cs->bcs[1]);
+	else
+		return(NULL);
+}
+
+void
+write_ctrl(struct BCState *bcs, int which) {
+
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs, "hdlc %c wr%x ctrl %x",
+			'A' + bcs->channel, which, bcs->hw.hdlc.ctrl.ctrl);
+	if (bcs->cs->subtyp == AVM_FRITZ_PCI) {
+		WriteHDLCPCI(bcs->cs, bcs->channel, HDLC_STATUS, bcs->hw.hdlc.ctrl.ctrl);
+	} else {
+		if (which & 4)
+			WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 2,
+				bcs->hw.hdlc.ctrl.sr.mode);
+		if (which & 2)
+			WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 1,
+				bcs->hw.hdlc.ctrl.sr.xml);
+		if (which & 1)
+			WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS,
+				bcs->hw.hdlc.ctrl.sr.cmd);
+	}
+}
+
+void
+modehdlc(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int hdlc = bcs->channel;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hdlc %c mode %d --> %d ichan %d --> %d",
+			'A' + hdlc, bcs->mode, mode, hdlc, bc);
+	bcs->hw.hdlc.ctrl.ctrl = 0;
+	switch (mode) {
+		case (-1): /* used for init */
+			bcs->mode = 1;
+			bcs->channel = bc;
+			bc = 0;
+		case (L1_MODE_NULL):
+			if (bcs->mode == L1_MODE_NULL)
+				return;
+			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+			write_ctrl(bcs, 5);
+			bcs->mode = L1_MODE_NULL;
+			bcs->channel = bc;
+			break;
+		case (L1_MODE_TRANS):
+			bcs->mode = mode;
+			bcs->channel = bc;
+			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+			write_ctrl(bcs, 5);
+			bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.ctrl.sr.cmd = 0;
+			schedule_event(bcs, B_XMTBUFREADY);
+			break;
+		case (L1_MODE_HDLC):
+			bcs->mode = mode;
+			bcs->channel = bc;
+			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+			write_ctrl(bcs, 5);
+			bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.ctrl.sr.cmd = 0;
+			schedule_event(bcs, B_XMTBUFREADY);
+			break;
+	}
+}
+
+static inline void
+hdlc_empty_fifo(struct BCState *bcs, int count)
+{
+	register u_int *ptr;
+	u_char *p;
+	u_char idx = bcs->channel ? AVM_HDLC_2 : AVM_HDLC_1;
+	int cnt=0;
+	struct IsdnCardState *cs = bcs->cs;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hdlc_empty_fifo %d", count);
+	if (bcs->hw.hdlc.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hdlc_empty_fifo: incoming packet too large");
+		return;
+	}
+	p = bcs->hw.hdlc.rcvbuf + bcs->hw.hdlc.rcvidx;
+	ptr = (u_int *)p;
+	bcs->hw.hdlc.rcvidx += count;
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		outl(idx, cs->hw.avm.cfg_reg + 4);
+		while (cnt < count) {
+#ifdef __powerpc__
+#ifdef CONFIG_APUS
+			*ptr++ = in_le32((unsigned *)(cs->hw.avm.isac +_IO_BASE));
+#else
+			*ptr++ = in_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE));
+#endif /* CONFIG_APUS */
+#else
+			*ptr++ = inl(cs->hw.avm.isac);
+#endif /* __powerpc__ */
+			cnt += 4;
+		}
+	} else {
+		outb(idx, cs->hw.avm.cfg_reg + 4);
+		while (cnt < count) {
+			*p++ = inb(cs->hw.avm.isac);
+			cnt++;
+		}
+	}
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		if (cs->subtyp == AVM_FRITZ_PNP)
+			p = (u_char *) ptr;
+		t += sprintf(t, "hdlc_empty_fifo %c cnt %d",
+			     bcs->channel ? 'B' : 'A', count);
+		QuickHex(t, p, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static inline void
+hdlc_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int count, cnt =0;
+	int fifo_size = 32;
+	u_char *p;
+	u_int *ptr;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hdlc_fill_fifo");
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XME;
+	if (bcs->tx_skb->len > fifo_size) {
+		count = fifo_size;
+	} else {
+		count = bcs->tx_skb->len;
+		if (bcs->mode != L1_MODE_TRANS)
+			bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XME;
+	}
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hdlc_fill_fifo %d/%ld", count, bcs->tx_skb->len);
+	p = bcs->tx_skb->data;
+	ptr = (u_int *)p;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hdlc.count += count;
+	bcs->hw.hdlc.ctrl.sr.xml = ((count == fifo_size) ? 0 : count);
+	write_ctrl(bcs, 3);  /* sets the correct index too */
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		while (cnt<count) {
+#ifdef __powerpc__
+#ifdef CONFIG_APUS
+			out_le32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++);
+#else
+			out_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++);
+#endif /* CONFIG_APUS */
+#else
+			outl(*ptr++, cs->hw.avm.isac);
+#endif /* __powerpc__ */
+			cnt += 4;
+		}
+	} else {
+		while (cnt<count) {
+			outb(*p++, cs->hw.avm.isac);
+			cnt++;
+		}
+	}
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		if (cs->subtyp == AVM_FRITZ_PNP)
+			p = (u_char *) ptr;
+		t += sprintf(t, "hdlc_fill_fifo %c cnt %d",
+			     bcs->channel ? 'B' : 'A', count);
+		QuickHex(t, p, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static inline void
+HDLC_irq(struct BCState *bcs, u_int stat) {
+	int len;
+	struct sk_buff *skb;
+
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+	if (stat & HDLC_INT_RPR) {
+		if (stat & HDLC_STAT_RDO) {
+			if (bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(bcs->cs, "RDO");
+			else
+				debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+			bcs->hw.hdlc.ctrl.sr.xml = 0;
+			bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_RRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.rcvidx = 0;
+		} else {
+			if (!(len = (stat & HDLC_STAT_RML_MASK)>>8))
+				len = 32;
+			hdlc_empty_fifo(bcs, len);
+			if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) {
+				if (((stat & HDLC_STAT_CRCVFRRAB)==HDLC_STAT_CRCVFR) ||
+					(bcs->mode == L1_MODE_TRANS)) {
+					if (!(skb = dev_alloc_skb(bcs->hw.hdlc.rcvidx)))
+						printk(KERN_WARNING "HDLC: receive out of memory\n");
+					else {
+						memcpy(skb_put(skb, bcs->hw.hdlc.rcvidx),
+							bcs->hw.hdlc.rcvbuf, bcs->hw.hdlc.rcvidx);
+						skb_queue_tail(&bcs->rqueue, skb);
+					}
+					bcs->hw.hdlc.rcvidx = 0;
+					schedule_event(bcs, B_RCVBUFREADY);
+				} else {
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs, "invalid frame");
+					else
+						debugl1(bcs->cs, "ch%d invalid frame %#x", bcs->channel, stat);
+					bcs->hw.hdlc.rcvidx = 0;
+				}
+			}
+		}
+	}
+	if (stat & HDLC_INT_XDU) {
+		/* Here we lost an TX interrupt, so
+		 * restart transmitting the whole frame.
+		 */
+		if (bcs->tx_skb) {
+			skb_push(bcs->tx_skb, bcs->hw.hdlc.count);
+			bcs->tx_cnt += bcs->hw.hdlc.count;
+			bcs->hw.hdlc.count = 0;
+			if (bcs->cs->debug & L1_DEB_WARN)
+				debugl1(bcs->cs, "ch%d XDU", bcs->channel);
+		} else if (bcs->cs->debug & L1_DEB_WARN)
+			debugl1(bcs->cs, "ch%d XDU without skb", bcs->channel);
+		bcs->hw.hdlc.ctrl.sr.xml = 0;
+		bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XRS;
+		write_ctrl(bcs, 1);
+		bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+		write_ctrl(bcs, 1);
+		hdlc_fill_fifo(bcs);
+	} else if (stat & HDLC_INT_XPR) {
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				hdlc_fill_fifo(bcs);
+				return;
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->hw.hdlc.count;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+				dev_kfree_skb_irq(bcs->tx_skb);
+				bcs->hw.hdlc.count = 0;
+				bcs->tx_skb = NULL;
+			}
+		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hdlc.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			hdlc_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+inline void
+HDLC_irq_main(struct IsdnCardState *cs)
+{
+	u_int stat;
+	struct BCState *bcs;
+
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		stat = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+	} else {
+		stat = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+		if (stat & HDLC_INT_RPR)
+			stat |= (ReadHDLCPnP(cs, 0, HDLC_STATUS+1))<<8;
+	}
+	if (stat & HDLC_INT_MASK) {
+		if (!(bcs = Sel_BCS(cs, 0))) {
+			if (cs->debug)
+				debugl1(cs, "hdlc spurious channel 0 IRQ");
+		} else
+			HDLC_irq(bcs, stat);
+	}
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		stat = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+	} else {
+		stat = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+		if (stat & HDLC_INT_RPR)
+			stat |= (ReadHDLCPnP(cs, 1, HDLC_STATUS+1))<<8;
+	}
+	if (stat & HDLC_INT_MASK) {
+		if (!(bcs = Sel_BCS(cs, 1))) {
+			if (cs->debug)
+				debugl1(cs, "hdlc spurious channel 1 IRQ");
+		} else
+			HDLC_irq(bcs, stat);
+	}
+}
+
+void
+hdlc_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->hw.hdlc.count = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "hdlc_l2l1: this shouldn't happen\n");
+			} else {
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->tx_skb = skb;
+				bcs->hw.hdlc.count = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			modehdlc(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			modehdlc(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_hdlcstate(struct BCState *bcs)
+{
+	modehdlc(bcs, 0, 0);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hdlc.rcvbuf) {
+			kfree(bcs->hw.hdlc.rcvbuf);
+			bcs->hw.hdlc.rcvbuf = NULL;
+		}
+		if (bcs->blog) {
+			kfree(bcs->blog);
+			bcs->blog = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+int
+open_hdlcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.hdlc.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for hdlc.rcvbuf\n");
+			return (1);
+		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax: No memory for bcs->blog\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.hdlc.rcvbuf);
+			bcs->hw.hdlc.rcvbuf = NULL;
+			return (2);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.hdlc.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_hdlc(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hdlcstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hdlc_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void __init
+clear_pending_hdlc_ints(struct IsdnCardState *cs)
+{
+	u_int val;
+
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		val = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+		debugl1(cs, "HDLC 1 STA %x", val);
+		val = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+		debugl1(cs, "HDLC 2 STA %x", val);
+	} else {
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+		debugl1(cs, "HDLC 1 STA %x", val);
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 1);
+		debugl1(cs, "HDLC 1 RML %x", val);
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 2);
+		debugl1(cs, "HDLC 1 MODE %x", val);
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 3);
+		debugl1(cs, "HDLC 1 VIN %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+		debugl1(cs, "HDLC 2 STA %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 1);
+		debugl1(cs, "HDLC 2 RML %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 2);
+		debugl1(cs, "HDLC 2 MODE %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 3);
+		debugl1(cs, "HDLC 2 VIN %x", val);
+	}
+}
+
+void __init
+inithdlc(struct IsdnCardState *cs)
+{
+	cs->bcs[0].BC_SetStack = setstack_hdlc;
+	cs->bcs[1].BC_SetStack = setstack_hdlc;
+	cs->bcs[0].BC_Close = close_hdlcstate;
+	cs->bcs[1].BC_Close = close_hdlcstate;
+	modehdlc(cs->bcs, -1, 0);
+	modehdlc(cs->bcs + 1, -1, 1);
+}
+
+static irqreturn_t
+avm_pcipnp_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_long flags;
+	u_char val;
+	u_char sval;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	sval = inb(cs->hw.avm.cfg_reg + 2);
+	if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) {
+		/* possible a shared  IRQ reqest */
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+	if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
+		val = ReadISAC(cs, ISAC_ISTA);
+		isac_interrupt(cs, val);
+	}
+	if (!(sval & AVM_STATUS0_IRQ_HDLC)) {
+		HDLC_irq_main(cs);
+	}
+	WriteISAC(cs, ISAC_MASK, 0xFF);
+	WriteISAC(cs, ISAC_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+reset_avmpcipnp(struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "AVM PCI/PnP: reset\n");
+	outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2);
+	mdelay(10);
+	outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+	outb(AVM_STATUS1_ENA_IOM | cs->irq, cs->hw.avm.cfg_reg + 3);
+	mdelay(10);
+	printk(KERN_INFO "AVM PCI/PnP: S1 %x\n", inb(cs->hw.avm.cfg_reg + 3));
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_avmpcipnp(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			outb(0, cs->hw.avm.cfg_reg + 2);
+			release_region(cs->hw.avm.cfg_reg, 32);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_avmpcipnp(cs);
+			clear_pending_isac_ints(cs);
+			initisac(cs);
+			inithdlc(cs);
+			outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER,
+				cs->hw.avm.cfg_reg + 2);
+			WriteISAC(cs, ISAC_MASK, 0);
+			outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER |
+				AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+			/* RESET Receiver and Transmitter */
+			WriteISAC(cs, ISAC_CMDR, 0x41);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+#ifdef CONFIG_PCI
+static struct pci_dev *dev_avm __initdata = NULL;
+#endif
+#ifdef __ISAPNP__
+static struct pnp_card *pnp_avm_c __initdata = NULL;
+#endif
+
+int __init
+setup_avm_pcipnp(struct IsdnCard *card)
+{
+	u_int val, ver;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, avm_pci_rev);
+	printk(KERN_INFO "HiSax: AVM PCI driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_FRITZPCI)
+		return (0);
+	if (card->para[1]) {
+		/* old manual method */
+		cs->hw.avm.cfg_reg = card->para[1];
+		cs->irq = card->para[0];
+		cs->subtyp = AVM_FRITZ_PNP;
+		goto ready;
+	}
+#ifdef __ISAPNP__
+	if (isapnp_present()) {
+		struct pnp_dev *pnp_avm_d = NULL;
+		if ((pnp_avm_c = pnp_find_card(
+			ISAPNP_VENDOR('A', 'V', 'M'),
+			ISAPNP_FUNCTION(0x0900), pnp_avm_c))) {
+			if ((pnp_avm_d = pnp_find_dev(pnp_avm_c,
+				ISAPNP_VENDOR('A', 'V', 'M'),
+				ISAPNP_FUNCTION(0x0900), pnp_avm_d))) {
+				int err;
+
+				pnp_disable_dev(pnp_avm_d);
+				err = pnp_activate_dev(pnp_avm_d);
+				if (err<0) {
+					printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+						__FUNCTION__, err);
+					return(0);
+				}
+				cs->hw.avm.cfg_reg =
+					pnp_port_start(pnp_avm_d, 0);
+				cs->irq = pnp_irq(pnp_avm_d, 0);
+				if (!cs->irq) {
+					printk(KERN_ERR "FritzPnP:No IRQ\n");
+					return(0);
+				}
+				if (!cs->hw.avm.cfg_reg) {
+					printk(KERN_ERR "FritzPnP:No IO address\n");
+					return(0);
+				}
+				cs->subtyp = AVM_FRITZ_PNP;
+				goto ready;
+			}
+		}
+	} else {
+		printk(KERN_INFO "FritzPnP: no ISA PnP present\n");
+	}
+#endif
+#ifdef CONFIG_PCI
+	if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM,
+		PCI_DEVICE_ID_AVM_A1,  dev_avm))) {
+		if (pci_enable_device(dev_avm))
+			return(0);
+		cs->irq = dev_avm->irq;
+		if (!cs->irq) {
+			printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n");
+			return(0);
+		}
+		cs->hw.avm.cfg_reg = pci_resource_start(dev_avm, 1);
+		if (!cs->hw.avm.cfg_reg) {
+			printk(KERN_ERR "FritzPCI: No IO-Adr for PCI card found\n");
+			return(0);
+		}
+		cs->subtyp = AVM_FRITZ_PCI;
+	} else {
+		printk(KERN_WARNING "FritzPCI: No PCI card found\n");
+		return(0);
+	}
+	cs->irq_flags |= SA_SHIRQ;
+#else
+	printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n");
+	return (0);
+#endif /* CONFIG_PCI */
+ready:
+	cs->hw.avm.isac = cs->hw.avm.cfg_reg + 0x10;
+	if (!request_region(cs->hw.avm.cfg_reg, 32,
+		(cs->subtyp == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.avm.cfg_reg,
+		       cs->hw.avm.cfg_reg + 31);
+		return (0);
+	}
+	switch (cs->subtyp) {
+	  case AVM_FRITZ_PCI:
+		val = inl(cs->hw.avm.cfg_reg);
+		printk(KERN_INFO "AVM PCI: stat %#x\n", val);
+		printk(KERN_INFO "AVM PCI: Class %X Rev %d\n",
+			val & 0xff, (val>>8) & 0xff);
+		cs->BC_Read_Reg = &ReadHDLC_s;
+		cs->BC_Write_Reg = &WriteHDLC_s;
+		break;
+	  case AVM_FRITZ_PNP:
+		val = inb(cs->hw.avm.cfg_reg);
+		ver = inb(cs->hw.avm.cfg_reg + 1);
+		printk(KERN_INFO "AVM PnP: Class %X Rev %d\n", val, ver);
+		cs->BC_Read_Reg = &ReadHDLCPnP;
+		cs->BC_Write_Reg = &WriteHDLCPnP;
+		break;
+	  default:
+	  	printk(KERN_WARNING "AVM unknown subtype %d\n", cs->subtyp);
+	  	return(0);
+	}
+	printk(KERN_INFO "HiSax: %s config irq:%d base:0x%X\n",
+		(cs->subtyp == AVM_FRITZ_PCI) ? "AVM Fritz!PCI" : "AVM Fritz!PnP",
+		cs->irq, cs->hw.avm.cfg_reg);
+
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Send_Data = &hdlc_fill_fifo;
+	cs->cardmsg = &AVM_card_msg;
+	cs->irq_func = &avm_pcipnp_interrupt;
+	cs->writeisac(cs, ISAC_MASK, 0xFF);
+	ISACVersion(cs, (cs->subtyp == AVM_FRITZ_PCI) ? "AVM PCI:" : "AVM PnP:");
+	return (1);
+}
diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c
new file mode 100644
index 000000000000..663a0bf703b7
--- /dev/null
+++ b/drivers/isdn/hisax/avma1_cs.c
@@ -0,0 +1,527 @@
+/*
+ * PCMCIA client driver for AVM A1 / Fritz!PCMCIA
+ *
+ * Author       Carsten Paeth
+ * Copyright    1998-2001 by Carsten Paeth <calle@calle.in-berlin.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include "hisax_cfg.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for AVM A1/Fritz!PCMCIA cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/*
+   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
+   you do not define PCMCIA_DEBUG at all, all the debug code will be
+   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
+   be present but disabled -- but it can then be enabled for specific
+   modules at load time with a 'pc_debug=#' option to insmod.
+*/
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
+static char *version =
+"avma1_cs.c 1.00 1998/01/23 10:00:00 (Carsten Paeth)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int isdnprot = 2;
+
+module_param(isdnprot, int, 0);
+
+/*====================================================================*/
+
+/*
+   The event() function is this driver's Card Services event handler.
+   It will be called by Card Services when an appropriate card status
+   event is received.  The config() and release() entry points are
+   used to configure or release a socket, in response to card insertion
+   and ejection events.  They are invoked from the skeleton event
+   handler.
+*/
+
+static void avma1cs_config(dev_link_t *link);
+static void avma1cs_release(dev_link_t *link);
+static int avma1cs_event(event_t event, int priority,
+			  event_callback_args_t *args);
+
+/*
+   The attach() and detach() entry points are used to create and destroy
+   "instances" of the driver, where each instance represents everything
+   needed to manage one actual PCMCIA card.
+*/
+
+static dev_link_t *avma1cs_attach(void);
+static void avma1cs_detach(dev_link_t *);
+
+/*
+   The dev_info variable is the "key" that is used to match up this
+   device driver with appropriate cards, through the card configuration
+   database.
+*/
+
+static dev_info_t dev_info = "avma1_cs";
+
+/*
+   A linked list of "instances" of the skeleton device.  Each actual
+   PCMCIA card corresponds to one device instance, and is described
+   by one dev_link_t structure (defined in ds.h).
+
+   You may not want to use a linked list for this -- for example, the
+   memory card driver uses an array of dev_link_t pointers, where minor
+   device numbers are used to derive the corresponding array index.
+*/
+
+static dev_link_t *dev_list = NULL;
+
+/*
+   A dev_link_t structure has fields for most things that are needed
+   to keep track of a socket, but there will usually be some device
+   specific information that also needs to be kept track of.  The
+   'priv' pointer in a dev_link_t structure can be used to point to
+   a device-specific private data structure, like this.
+
+   A driver needs to provide a dev_node_t structure for each device
+   on a card.  In some cases, there is only one device per card (for
+   example, ethernet cards, modems).  In other cases, there may be
+   many actual or logical devices (SCSI adapters, memory cards with
+   multiple partitions).  The dev_node_t structures need to be kept
+   in a linked list starting at the 'dev' field of a dev_link_t
+   structure.  We allocate them in the card's private data structure,
+   because they generally can't be allocated dynamically.
+*/
+   
+typedef struct local_info_t {
+    dev_node_t	node;
+} local_info_t;
+
+/*======================================================================
+
+    avma1cs_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+    The dev_link structure is initialized, but we don't actually
+    configure the card at this point -- we wait until we receive a
+    card insertion event.
+    
+======================================================================*/
+
+static dev_link_t *avma1cs_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    local_info_t *local;
+    int ret;
+    
+    DEBUG(0, "avma1cs_attach()\n");
+
+    /* Initialize the dev_link_t structure */
+    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+    if (!link)
+	return NULL;
+    memset(link, 0, sizeof(struct dev_link_t));
+
+    /* Allocate space for private device-specific data */
+    local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+    if (!local) {
+	kfree(link);
+	return NULL;
+    }
+    memset(local, 0, sizeof(local_info_t));
+    link->priv = local;
+
+    /* The io structure describes IO port mapping */
+    link->io.NumPorts1 = 16;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+    link->io.NumPorts2 = 16;
+    link->io.Attributes2 = IO_DATA_PATH_WIDTH_16;
+    link->io.IOAddrLines = 5;
+
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+
+    /* General socket configuration */
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.ConfigIndex = 1;
+    link->conf.Present = PRESENT_OPTION;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &avma1cs_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != 0) {
+	cs_error(link->handle, RegisterClient, ret);
+	avma1cs_detach(link);
+	return NULL;
+    }
+
+    return link;
+} /* avma1cs_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void avma1cs_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+
+    DEBUG(0, "avma1cs_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    /*
+       If the device is currently configured and active, we won't
+       actually delete it yet.  Instead, it is marked so that when
+       the release() function is called, that will trigger a proper
+       detach().
+    */
+    if (link->state & DEV_CONFIG) {
+#ifdef PCMCIA_DEBUG
+	printk(KERN_DEBUG "avma1_cs: detach postponed, '%s' "
+	       "still locked\n", link->dev->dev_name);
+#endif
+	link->state |= DEV_STALE_LINK;
+	return;
+    }
+
+    /* Break the link with Card Services */
+    if (link->handle)
+    	pcmcia_deregister_client(link->handle);
+    
+    /* Unlink device structure, free pieces */
+    *linkp = link->next;
+    if (link->priv) {
+	kfree(link->priv);
+    }
+    kfree(link);
+    
+} /* avma1cs_detach */
+
+/*======================================================================
+
+    avma1cs_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    ethernet device available to the system.
+    
+======================================================================*/
+
+static int get_tuple(client_handle_t handle, tuple_t *tuple,
+		     cisparse_t *parse)
+{
+    int i = pcmcia_get_tuple_data(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return pcmcia_parse_tuple(handle, tuple, parse);
+}
+
+static int first_tuple(client_handle_t handle, tuple_t *tuple,
+		     cisparse_t *parse)
+{
+    int i = pcmcia_get_first_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static int next_tuple(client_handle_t handle, tuple_t *tuple,
+		     cisparse_t *parse)
+{
+    int i = pcmcia_get_next_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static void avma1cs_config(dev_link_t *link)
+{
+    client_handle_t handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+    local_info_t *dev;
+    int i;
+    u_char buf[64];
+    char devname[128];
+    IsdnCard_t	icard;
+    int busy = 0;
+    
+    handle = link->handle;
+    dev = link->priv;
+
+    DEBUG(0, "avma1cs_config(0x%p)\n", link);
+
+    /*
+       This reads the card's CONFIG tuple to find its configuration
+       registers.
+    */
+    do {
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	i = pcmcia_get_first_tuple(handle, &tuple);
+	if (i != CS_SUCCESS) break;
+	tuple.TupleData = buf;
+	tuple.TupleDataMax = 64;
+	tuple.TupleOffset = 0;
+	i = pcmcia_get_tuple_data(handle, &tuple);
+	if (i != CS_SUCCESS) break;
+	i = pcmcia_parse_tuple(handle, &tuple, &parse);
+	if (i != CS_SUCCESS) break;
+	link->conf.ConfigBase = parse.config.base;
+    } while (0);
+    if (i != CS_SUCCESS) {
+	cs_error(link->handle, ParseTuple, i);
+	link->state &= ~DEV_CONFIG_PENDING;
+	return;
+    }
+    
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    do {
+
+	tuple.Attributes = 0;
+	tuple.TupleData = buf;
+	tuple.TupleDataMax = 254;
+	tuple.TupleOffset = 0;
+	tuple.DesiredTuple = CISTPL_VERS_1;
+
+	devname[0] = 0;
+	if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) {
+	    strlcpy(devname,parse.version_1.str + parse.version_1.ofs[1], 
+			sizeof(devname));
+	}
+	/*
+         * find IO port
+         */
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+	tuple.Attributes = 0;
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	i = first_tuple(handle, &tuple, &parse);
+	while (i == CS_SUCCESS) {
+	    if (cf->io.nwin > 0) {
+		link->conf.ConfigIndex = cf->index;
+		link->io.BasePort1 = cf->io.win[0].base;
+		link->io.NumPorts1 = cf->io.win[0].len;
+		link->io.NumPorts2 = 0;
+		printk(KERN_INFO "avma1_cs: testing i/o %#x-%#x\n",
+			link->io.BasePort1,
+			link->io.BasePort1+link->io.NumPorts1 - 1);
+		i = pcmcia_request_io(link->handle, &link->io);
+		if (i == CS_SUCCESS) goto found_port;
+	    }
+	    i = next_tuple(handle, &tuple, &parse);
+	}
+
+found_port:
+	if (i != CS_SUCCESS) {
+	    cs_error(link->handle, RequestIO, i);
+	    break;
+	}
+	
+	/*
+	 * allocate an interrupt line
+	 */
+	i = pcmcia_request_irq(link->handle, &link->irq);
+	if (i != CS_SUCCESS) {
+	    cs_error(link->handle, RequestIRQ, i);
+	    pcmcia_release_io(link->handle, &link->io);
+	    break;
+	}
+	
+	/*
+	 * configure the PCMCIA socket
+	 */
+	i = pcmcia_request_configuration(link->handle, &link->conf);
+	if (i != CS_SUCCESS) {
+	    cs_error(link->handle, RequestConfiguration, i);
+	    pcmcia_release_io(link->handle, &link->io);
+	    pcmcia_release_irq(link->handle, &link->irq);
+	    break;
+	}
+
+    } while (0);
+
+    /* At this point, the dev_node_t structure(s) should be
+       initialized and arranged in a linked list at link->dev. */
+
+    strcpy(dev->node.dev_name, "A1");
+    dev->node.major = 45;
+    dev->node.minor = 0;
+    link->dev = &dev->node;
+    
+    link->state &= ~DEV_CONFIG_PENDING;
+    /* If any step failed, release any partially configured state */
+    if (i != 0) {
+	avma1cs_release(link);
+	return;
+    }
+
+    printk(KERN_NOTICE "avma1_cs: checking at i/o %#x, irq %d\n",
+				link->io.BasePort1, link->irq.AssignedIRQ);
+
+    icard.para[0] = link->irq.AssignedIRQ;
+    icard.para[1] = link->io.BasePort1;
+    icard.protocol = isdnprot;
+    icard.typ = ISDN_CTYPE_A1_PCMCIA;
+    
+    i = hisax_init_pcmcia(link, &busy, &icard);
+    if (i < 0) {
+    	printk(KERN_ERR "avma1_cs: failed to initialize AVM A1 PCMCIA %d at i/o %#x\n", i, link->io.BasePort1);
+	avma1cs_release(link);
+	return;
+    }
+    dev->node.minor = i;
+
+} /* avma1cs_config */
+
+/*======================================================================
+
+    After a card is removed, avma1cs_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+    
+======================================================================*/
+
+static void avma1cs_release(dev_link_t *link)
+{
+    local_info_t *local = link->priv;
+
+    DEBUG(0, "avma1cs_release(0x%p)\n", link);
+
+    /* no unregister function with hisax */
+    HiSax_closecard(local->node.minor);
+
+    /* Unlink the device chain */
+    link->dev = NULL;
+    
+    /* Don't bother checking to see if these succeed or not */
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+    link->state &= ~DEV_CONFIG;
+    
+    if (link->state & DEV_STALE_LINK)
+	avma1cs_detach(link);
+} /* avma1cs_release */
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+    When a CARD_REMOVAL event is received, we immediately set a flag
+    to block future accesses to this device.  All the functions that
+    actually access the device should check this flag to make sure
+    the card is still present.
+    
+======================================================================*/
+
+static int avma1cs_event(event_t event, int priority,
+			  event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+
+    DEBUG(1, "avma1cs_event(0x%06x)\n", event);
+    
+    switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+	    if (link->state & DEV_CONFIG)
+		avma1cs_release(link);
+	    break;
+	case CS_EVENT_CARD_INSERTION:
+	    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	    avma1cs_config(link);
+	    break;
+	case CS_EVENT_PM_SUSPEND:
+	    link->state |= DEV_SUSPEND;
+	    /* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+	    if (link->state & DEV_CONFIG)
+		pcmcia_release_configuration(link->handle);
+	    break;
+	case CS_EVENT_PM_RESUME:
+	    link->state &= ~DEV_SUSPEND;
+	    /* Fall through... */
+	case CS_EVENT_CARD_RESET:
+ 	    if (link->state & DEV_CONFIG)
+		pcmcia_request_configuration(link->handle, &link->conf);
+	    break;
+    }
+    return 0;
+} /* avma1cs_event */
+
+static struct pcmcia_driver avma1cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "avma1_cs",
+	},
+	.attach		= avma1cs_attach,
+	.detach		= avma1cs_detach,
+};
+ 
+/*====================================================================*/
+
+static int __init init_avma1_cs(void)
+{
+	return(pcmcia_register_driver(&avma1cs_driver));
+}
+
+static void __exit exit_avma1_cs(void)
+{
+	pcmcia_unregister_driver(&avma1cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_avma1_cs);
+module_exit(exit_avma1_cs);
diff --git a/drivers/isdn/hisax/bkm_a4t.c b/drivers/isdn/hisax/bkm_a4t.c
new file mode 100644
index 000000000000..f410f628a3e2
--- /dev/null
+++ b/drivers/isdn/hisax/bkm_a4t.c
@@ -0,0 +1,344 @@
+/* $Id: bkm_a4t.c,v 1.22.2.4 2004/01/14 16:04:48 keil Exp $
+ *
+ * low level stuff for T-Berkom A4T
+ *
+ * Author       Roland Klabunde
+ * Copyright    by Roland Klabunde   <R.Klabunde@Berkom.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/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "jade.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include "bkm_ax.h"
+
+extern const char *CardType[];
+
+const char *bkm_a4t_revision = "$Revision: 1.22.2.4 $";
+
+
+static inline u_char
+readreg(unsigned int ale, unsigned long adr, u_char off)
+{
+	register u_int ret;
+	unsigned int *po = (unsigned int *) adr;	/* Postoffice */
+
+	*po = (GCS_2 | PO_WRITE | off);
+	__WAITI20__(po);
+	*po = (ale | PO_READ);
+	__WAITI20__(po);
+	ret = *po;
+	return ((unsigned char) ret);
+}
+
+
+static inline void
+readfifo(unsigned int ale, unsigned long adr, u_char off, u_char * data, int size)
+{
+	int i;
+	for (i = 0; i < size; i++)
+		*data++ = readreg(ale, adr, off);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned long adr, u_char off, u_char data)
+{
+	unsigned int *po = (unsigned int *) adr;	/* Postoffice */
+	*po = (GCS_2 | PO_WRITE | off);
+	__WAITI20__(po);
+	*po = (ale | PO_WRITE | data);
+	__WAITI20__(po);
+}
+
+
+static inline void
+writefifo(unsigned int ale, unsigned long adr, u_char off, u_char * data, int size)
+{
+	int i;
+
+	for (i = 0; i < size; i++)
+		writereg(ale, adr, off, *data++);
+}
+
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size);
+}
+
+static u_char
+ReadJADE(struct IsdnCardState *cs, int jade, u_char offset)
+{
+	return (readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80))));
+}
+
+static void
+WriteJADE(struct IsdnCardState *cs, int jade, u_char offset, u_char value)
+{
+	writereg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80)), value);
+}
+
+/*
+ * fast interrupt JADE stuff goes here
+ */
+
+#define READJADE(cs, nr, reg) readreg(cs->hw.ax.jade_ale,\
+ 		cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)))
+#define WRITEJADE(cs, nr, reg, data) writereg(cs->hw.ax.jade_ale,\
+ 		cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), data)
+
+#define READJADEFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.jade_ale,\
+		cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt)
+#define WRITEJADEFIFO(cs, nr, ptr, cnt) writefifo( cs->hw.ax.jade_ale,\
+		cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt)
+
+#include "jade_irq.c"
+
+static irqreturn_t
+bkm_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val = 0;
+	u_long flags;
+	I20_REGISTER_FILE *pI20_Regs;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
+
+	/* ISDN interrupt pending? */
+	if (pI20_Regs->i20IntStatus & intISDN) {
+		/* Reset the ISDN interrupt     */
+		pI20_Regs->i20IntStatus = intISDN;
+		/* Disable ISDN interrupt */
+		pI20_Regs->i20IntCtrl &= ~intISDN;
+		/* Channel A first */
+		val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0x80);
+		if (val) {
+			jade_int_main(cs, val, 0);
+		}
+		/* Channel B  */
+		val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0xC0);
+		if (val) {
+			jade_int_main(cs, val, 1);
+		}
+		/* D-Channel */
+		val = readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, ISAC_ISTA);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+		/* Reenable ISDN interrupt */
+		pI20_Regs->i20IntCtrl |= intISDN;
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_HANDLED;
+	} else {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+}
+
+void
+release_io_bkm(struct IsdnCardState *cs)
+{
+	if (cs->hw.ax.base) {
+		iounmap((void *) cs->hw.ax.base);
+		cs->hw.ax.base = 0;
+	}
+}
+
+static void
+enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable)
+{
+	if (cs->typ == ISDN_CTYPE_BKM_A4T) {
+		I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
+		if (bEnable)
+			pI20_Regs->i20IntCtrl |= (intISDN | intPCI);
+		else
+			/* CAUTION: This disables the video capture driver too */
+			pI20_Regs->i20IntCtrl &= ~(intISDN | intPCI);
+	}
+}
+
+static void
+reset_bkm(struct IsdnCardState *cs)
+{
+	if (cs->typ == ISDN_CTYPE_BKM_A4T) {
+		I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
+		/* Issue the I20 soft reset     */
+		pI20_Regs->i20SysControl = 0xFF;	/* all in */
+		mdelay(10);
+		/* Remove the soft reset */
+		pI20_Regs->i20SysControl = sysRESET | 0xFF;
+		mdelay(10);
+		/* Set our configuration */
+		pI20_Regs->i20SysControl = sysRESET | sysCFG;
+		/* Issue ISDN reset     */
+		pI20_Regs->i20GuestControl = guestWAIT_CFG |
+		    g_A4T_JADE_RES |
+		    g_A4T_ISAR_RES |
+		    g_A4T_ISAC_RES |
+		    g_A4T_JADE_BOOTR |
+		    g_A4T_ISAR_BOOTR;
+		mdelay(10);
+
+		/* Remove RESET state from ISDN */
+		pI20_Regs->i20GuestControl &= ~(g_A4T_ISAC_RES |
+						g_A4T_JADE_RES |
+						g_A4T_ISAR_RES);
+		mdelay(10);
+	}
+}
+
+static int
+BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			/* Disable ints */
+			spin_lock_irqsave(&cs->lock, flags);
+			enable_bkm_int(cs, 0);
+			reset_bkm(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_RELEASE:
+			/* Sanity */
+			spin_lock_irqsave(&cs->lock, flags);
+			enable_bkm_int(cs, 0);
+			reset_bkm(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			release_io_bkm(cs);
+			return (0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			clear_pending_isac_ints(cs);
+			clear_pending_jade_ints(cs);
+			initisac(cs);
+			initjade(cs);
+			/* Enable ints */
+			enable_bkm_int(cs, 1);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_TEST:
+			return (0);
+	}
+	return (0);
+}
+
+static struct pci_dev *dev_a4t __initdata = NULL;
+
+int __init
+setup_bkm_a4t(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	u_int pci_memaddr = 0, found = 0;
+	I20_REGISTER_FILE *pI20_Regs;
+#ifdef CONFIG_PCI
+#endif
+
+	strcpy(tmp, bkm_a4t_revision);
+	printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ == ISDN_CTYPE_BKM_A4T) {
+		cs->subtyp = BKM_A4T;
+	} else
+		return (0);
+
+#ifdef CONFIG_PCI
+	while ((dev_a4t = pci_find_device(PCI_VENDOR_ID_ZORAN,
+		PCI_DEVICE_ID_ZORAN_36120, dev_a4t))) {
+		u16 sub_sys;
+		u16 sub_vendor;
+
+		sub_vendor = dev_a4t->subsystem_vendor;
+		sub_sys = dev_a4t->subsystem_device;
+		if ((sub_sys == PCI_DEVICE_ID_BERKOM_A4T) && (sub_vendor == PCI_VENDOR_ID_BERKOM)) {
+			if (pci_enable_device(dev_a4t))
+				return(0);
+			found = 1;
+			pci_memaddr = pci_resource_start(dev_a4t, 0);
+			cs->irq = dev_a4t->irq;
+			break;
+		}
+	}
+	if (!found) {
+		printk(KERN_WARNING "HiSax: %s: Card not found\n", CardType[card->typ]);
+		return (0);
+	}
+	if (!cs->irq) {		/* IRQ range check ?? */
+		printk(KERN_WARNING "HiSax: %s: No IRQ\n", CardType[card->typ]);
+		return (0);
+	}
+	if (!pci_memaddr) {
+		printk(KERN_WARNING "HiSax: %s: No Memory base address\n", CardType[card->typ]);
+		return (0);
+	}
+	cs->hw.ax.base = (long) ioremap(pci_memaddr, 4096);
+	/* Check suspecious address */
+	pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
+	if ((pI20_Regs->i20IntStatus & 0x8EFFFFFF) != 0) {
+		printk(KERN_WARNING "HiSax: %s address %lx-%lx suspecious\n",
+		       CardType[card->typ], cs->hw.ax.base, cs->hw.ax.base + 4096);
+		iounmap((void *) cs->hw.ax.base);
+		cs->hw.ax.base = 0;
+		return (0);
+	}
+	cs->hw.ax.isac_adr = cs->hw.ax.base + PO_OFFSET;
+	cs->hw.ax.jade_adr = cs->hw.ax.base + PO_OFFSET;
+	cs->hw.ax.isac_ale = GCS_1;
+	cs->hw.ax.jade_ale = GCS_3;
+#else
+	printk(KERN_WARNING "HiSax: %s: NO_PCI_BIOS\n", CardType[card->typ]);
+	printk(KERN_WARNING "HiSax: %s: unable to configure\n", CardType[card->typ]);
+	return (0);
+#endif				/* CONFIG_PCI */
+	printk(KERN_INFO "HiSax: %s: Card configured at 0x%lX IRQ %d\n",
+	       CardType[card->typ], cs->hw.ax.base, cs->irq);
+
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadJADE;
+	cs->BC_Write_Reg = &WriteJADE;
+	cs->BC_Send_Data = &jade_fill_fifo;
+	cs->cardmsg = &BKM_card_msg;
+	cs->irq_func = &bkm_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+	ISACVersion(cs, "Telekom A4T:");
+	/* Jade version */
+	JadeVersion(cs, "Telekom A4T:");
+	return (1);
+}
diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c
new file mode 100644
index 000000000000..94bb83ce7fd8
--- /dev/null
+++ b/drivers/isdn/hisax/bkm_a8.c
@@ -0,0 +1,451 @@
+/* $Id: bkm_a8.c,v 1.22.2.4 2004/01/15 14:02:34 keil Exp $
+ *
+ * low level stuff for Scitel Quadro (4*S0, passive)
+ *
+ * Author       Roland Klabunde
+ * Copyright    by Roland Klabunde   <R.Klabunde@Berkom.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/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include "bkm_ax.h"
+
+#ifdef CONFIG_PCI
+
+#define	ATTEMPT_PCI_REMAPPING	/* Required for PLX rev 1 */
+
+extern const char *CardType[];
+
+const char sct_quadro_revision[] = "$Revision: 1.22.2.4 $";
+
+static const char *sct_quadro_subtypes[] =
+{
+	"",
+	"#1",
+	"#2",
+	"#3",
+	"#4"
+};
+
+
+#define wordout(addr,val) outw(val,addr)
+#define wordin(addr) inw(addr)
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	wordout(ale, off);
+	ret = wordin(adr) & 0xFF;
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	int i;
+	wordout(ale, off);
+	for (i = 0; i < size; i++)
+		data[i] = wordin(adr) & 0xFF;
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	wordout(ale, off);
+	wordout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	int i;
+	wordout(ale, off);
+	for (i = 0; i < size; i++)
+		wordout(adr, data[i]);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size);
+}
+
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0), value);
+}
+
+/* Set the specific ipac to active */
+static void
+set_ipac_active(struct IsdnCardState *cs, u_int active)
+{
+	/* set irq mask */
+	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK,
+		active ? 0xc0 : 0xff);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.ax.base, \
+	cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ax.base, \
+	cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0), data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.base, \
+	cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ax.base, \
+	cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+bkm_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista, val, icnt = 5;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA);
+	if (!(ista & 0x3f)) { /* not this IPAC */
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+      Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val) {
+			hscx_int_main(cs, val);
+		}
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.ax.base, cs->hw.ax.data_adr, ISAC_ISTA | 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "HiSax: %s (%s) IRQ LOOP\n",
+		       CardType[cs->typ],
+		       sct_quadro_subtypes[cs->subtyp]);
+	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xFF);
+	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_sct_quadro(struct IsdnCardState *cs)
+{
+	release_region(cs->hw.ax.base & 0xffffffc0, 128);
+	if (cs->subtyp == SCT_1)
+		release_region(cs->hw.ax.plx_adr, 64);
+}
+
+static void
+enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable)
+{
+	if (cs->typ == ISDN_CTYPE_SCT_QUADRO) {
+		if (bEnable)
+			wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) | 0x41));
+		else
+			wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) & ~0x41));
+	}
+}
+
+static void
+reset_bkm(struct IsdnCardState *cs)
+{
+	if (cs->subtyp == SCT_1) {
+		wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) & ~4));
+		mdelay(10);
+		/* Remove the soft reset */
+		wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) | 4));
+		mdelay(10);
+	}
+}
+
+static int
+BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			/* Disable ints */
+			set_ipac_active(cs, 0);
+			enable_bkm_int(cs, 0);
+			reset_bkm(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_RELEASE:
+			/* Sanity */
+			spin_lock_irqsave(&cs->lock, flags);
+			set_ipac_active(cs, 0);
+			enable_bkm_int(cs, 0);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			release_io_sct_quadro(cs);
+			return (0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->debug |= L1_DEB_IPAC;
+			set_ipac_active(cs, 1);
+			inithscxisac(cs, 3);
+			/* Enable ints */
+			enable_bkm_int(cs, 1);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_TEST:
+			return (0);
+	}
+	return (0);
+}
+
+int __init
+sct_alloc_io(u_int adr, u_int len)
+{
+	if (!request_region(adr, len, "scitel")) {
+		printk(KERN_WARNING
+			"HiSax: Scitel port %#x-%#x already in use\n",
+			adr, adr + len);
+		return (1);
+	}
+	return(0);
+}
+
+static struct pci_dev *dev_a8 __initdata = NULL;
+static u16  sub_vendor_id __initdata = 0;
+static u16  sub_sys_id __initdata = 0;
+static u_char pci_bus __initdata = 0;
+static u_char pci_device_fn __initdata = 0;
+static u_char pci_irq __initdata = 0;
+
+#endif /* CONFIG_PCI */
+
+int __init
+setup_sct_quadro(struct IsdnCard *card)
+{
+#ifdef CONFIG_PCI
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	u_char pci_rev_id;
+	u_int found = 0;
+	u_int pci_ioaddr1, pci_ioaddr2, pci_ioaddr3, pci_ioaddr4, pci_ioaddr5;
+
+	strcpy(tmp, sct_quadro_revision);
+	printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ == ISDN_CTYPE_SCT_QUADRO) {
+		cs->subtyp = SCT_1;	/* Preset */
+	} else
+		return (0);
+
+	/* Identify subtype by para[0] */
+	if (card->para[0] >= SCT_1 && card->para[0] <= SCT_4)
+		cs->subtyp = card->para[0];
+	else {
+		printk(KERN_WARNING "HiSax: %s: Invalid subcontroller in configuration, default to 1\n",
+			CardType[card->typ]);
+		return (0);
+	}
+	if ((cs->subtyp != SCT_1) && ((sub_sys_id != PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO) ||
+		(sub_vendor_id != PCI_VENDOR_ID_BERKOM)))
+		return (0);
+	if (cs->subtyp == SCT_1) {
+		while ((dev_a8 = pci_find_device(PCI_VENDOR_ID_PLX,
+			PCI_DEVICE_ID_PLX_9050, dev_a8))) {
+			
+			sub_vendor_id = dev_a8->subsystem_vendor;
+			sub_sys_id = dev_a8->subsystem_device;
+			if ((sub_sys_id == PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO) &&
+				(sub_vendor_id == PCI_VENDOR_ID_BERKOM)) {
+				if (pci_enable_device(dev_a8))
+					return(0);
+				pci_ioaddr1 = pci_resource_start(dev_a8, 1);
+				pci_irq = dev_a8->irq;
+				pci_bus = dev_a8->bus->number;
+				pci_device_fn = dev_a8->devfn;
+				found = 1;
+				break;
+			}
+		}
+		if (!found) {
+			printk(KERN_WARNING "HiSax: %s (%s): Card not found\n",
+				CardType[card->typ],
+				sct_quadro_subtypes[cs->subtyp]);
+			return (0);
+		}
+#ifdef ATTEMPT_PCI_REMAPPING
+/* HACK: PLX revision 1 bug: PLX address bit 7 must not be set */
+		pci_read_config_byte(dev_a8, PCI_REVISION_ID, &pci_rev_id);
+		if ((pci_ioaddr1 & 0x80) && (pci_rev_id == 1)) {
+			printk(KERN_WARNING "HiSax: %s (%s): PLX rev 1, remapping required!\n",
+				CardType[card->typ],
+				sct_quadro_subtypes[cs->subtyp]);
+			/* Restart PCI negotiation */
+			pci_write_config_dword(dev_a8, PCI_BASE_ADDRESS_1, (u_int) - 1);
+			/* Move up by 0x80 byte */
+			pci_ioaddr1 += 0x80;
+			pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK;
+			pci_write_config_dword(dev_a8, PCI_BASE_ADDRESS_1, pci_ioaddr1);
+			dev_a8->resource[ 1].start = pci_ioaddr1;
+		}
+#endif /* End HACK */
+	}
+	if (!pci_irq) {		/* IRQ range check ?? */
+		printk(KERN_WARNING "HiSax: %s (%s): No IRQ\n",
+		       CardType[card->typ],
+		       sct_quadro_subtypes[cs->subtyp]);
+		return (0);
+	}
+	pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_1, &pci_ioaddr1);
+	pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_2, &pci_ioaddr2);
+	pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_3, &pci_ioaddr3);
+	pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_4, &pci_ioaddr4);
+	pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_5, &pci_ioaddr5);
+	if (!pci_ioaddr1 || !pci_ioaddr2 || !pci_ioaddr3 || !pci_ioaddr4 || !pci_ioaddr5) {
+		printk(KERN_WARNING "HiSax: %s (%s): No IO base address(es)\n",
+		       CardType[card->typ],
+		       sct_quadro_subtypes[cs->subtyp]);
+		return (0);
+	}
+	pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK;
+	pci_ioaddr2 &= PCI_BASE_ADDRESS_IO_MASK;
+	pci_ioaddr3 &= PCI_BASE_ADDRESS_IO_MASK;
+	pci_ioaddr4 &= PCI_BASE_ADDRESS_IO_MASK;
+	pci_ioaddr5 &= PCI_BASE_ADDRESS_IO_MASK;
+	/* Take over */
+	cs->irq = pci_irq;
+	cs->irq_flags |= SA_SHIRQ;
+	/* pci_ioaddr1 is unique to all subdevices */
+	/* pci_ioaddr2 is for the fourth subdevice only */
+	/* pci_ioaddr3 is for the third subdevice only */
+	/* pci_ioaddr4 is for the second subdevice only */
+	/* pci_ioaddr5 is for the first subdevice only */
+	cs->hw.ax.plx_adr = pci_ioaddr1;
+	/* Enter all ipac_base addresses */
+	switch(cs->subtyp) {
+		case 1:
+			cs->hw.ax.base = pci_ioaddr5 + 0x00;
+			if (sct_alloc_io(pci_ioaddr1, 128))
+				return(0);
+			if (sct_alloc_io(pci_ioaddr5, 64))
+				return(0);
+			/* disable all IPAC */
+			writereg(pci_ioaddr5, pci_ioaddr5 + 4,
+				IPAC_MASK, 0xFF);
+			writereg(pci_ioaddr4 + 0x08, pci_ioaddr4 + 0x0c,
+				IPAC_MASK, 0xFF);
+			writereg(pci_ioaddr3 + 0x10, pci_ioaddr3 + 0x14,
+				IPAC_MASK, 0xFF);
+			writereg(pci_ioaddr2 + 0x20, pci_ioaddr2 + 0x24,
+				IPAC_MASK, 0xFF);
+			break;
+		case 2:
+			cs->hw.ax.base = pci_ioaddr4 + 0x08;
+			if (sct_alloc_io(pci_ioaddr4, 64))
+				return(0);
+			break;
+		case 3:
+			cs->hw.ax.base = pci_ioaddr3 + 0x10;
+			if (sct_alloc_io(pci_ioaddr3, 64))
+				return(0);
+			break;
+		case 4:
+			cs->hw.ax.base = pci_ioaddr2 + 0x20;
+			if (sct_alloc_io(pci_ioaddr2, 64))
+				return(0);
+			break;
+	}	
+	/* For isac and hscx data path */
+	cs->hw.ax.data_adr = cs->hw.ax.base + 4;
+
+	printk(KERN_INFO "HiSax: %s (%s) configured at 0x%.4lX, 0x%.4lX, 0x%.4lX and IRQ %d\n",
+	       CardType[card->typ],
+	       sct_quadro_subtypes[cs->subtyp],
+	       cs->hw.ax.plx_adr,
+	       cs->hw.ax.base,
+	       cs->hw.ax.data_adr,
+	       cs->irq);
+
+	test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &BKM_card_msg;
+	cs->irq_func = &bkm_interrupt_ipac;
+
+	printk(KERN_INFO "HiSax: %s (%s): IPAC Version %d\n",
+		CardType[card->typ],
+		sct_quadro_subtypes[cs->subtyp],
+		readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ID));
+	return (1);
+#else
+	printk(KERN_ERR "HiSax: bkm_a8 only supported on PCI Systems\n");
+#endif /* CONFIG_PCI */
+}
diff --git a/drivers/isdn/hisax/bkm_ax.h b/drivers/isdn/hisax/bkm_ax.h
new file mode 100644
index 000000000000..029e0a277661
--- /dev/null
+++ b/drivers/isdn/hisax/bkm_ax.h
@@ -0,0 +1,119 @@
+/* $Id: bkm_ax.h,v 1.5.6.3 2001/09/23 22:24:46 kai Exp $
+ *
+ * low level decls for T-Berkom cards A4T and Scitel Quadro (4*S0, passive)
+ *
+ * Author       Roland Klabunde
+ * Copyright    by Roland Klabunde   <R.Klabunde@Berkom.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef	__BKM_AX_H__
+#define	__BKM_AX_H__
+
+/* Supported boards	(subtypes) */
+#define SCT_1		1
+#define	SCT_2		2
+#define	SCT_3		3
+#define	SCT_4		4
+#define BKM_A4T		5
+
+#define	PLX_ADDR_PLX		0x14	/* Addr PLX configuration */
+#define	PLX_ADDR_ISAC		0x18	/* Addr ISAC */
+#define	PLX_ADDR_HSCX		0x1C	/* Addr HSCX */
+#define	PLX_ADDR_ALE		0x20	/* Addr ALE */
+#define	PLX_ADDR_ALEPLUS	0x24	/* Next Addr behind ALE */
+
+#define	PLX_SUBVEN		0x2C	/* Offset SubVendor */
+#define	PLX_SUBSYS		0x2E	/* Offset SubSystem */
+
+
+/* Application specific registers I20 (Siemens SZB6120H) */
+typedef	struct {
+		/* Video front end horizontal configuration register */
+	volatile u_int	i20VFEHorzCfg;	/* Offset 00 */
+		/* Video front end vertical configuration register */
+	volatile u_int	i20VFEVertCfg;	/* Offset 04 */	
+		/* Video front end scaler and pixel format register */
+	volatile u_int	i20VFEScaler;	/* Offset 08 */	
+		/* Video display top register */
+	volatile u_int	i20VDispTop;   	/* Offset 0C */	
+		/* Video display bottom register */
+	volatile u_int	i20VDispBottom;	/* Offset 10 */	
+		/* Video stride, status and frame grab register */
+	volatile u_int	i20VidFrameGrab;/* Offset 14 */	
+		/* Video display configuration register */
+	volatile u_int	i20VDispCfg;	/* Offset 18 */	
+		/* Video masking map top */
+	volatile u_int	i20VMaskTop;	/* Offset 1C */	
+		/* Video masking map bottom */
+	volatile u_int	i20VMaskBottom;	/* Offset 20 */	
+		/* Overlay control register */
+	volatile u_int 	i20OvlyControl;	/* Offset 24 */	
+		/* System, PCI and general purpose pins control register */
+ 	volatile u_int	i20SysControl; 	/* Offset 28 */	
+#define	sysRESET		0x01000000	/* bit 24:Softreset (Low)		*/
+			/* GPIO 4...0: Output fixed for our cfg! */
+#define	sysCFG			0x000000E0	/* GPIO 7,6,5: Input */
+	/* General purpose pins and guest bus control register */
+ 	volatile u_int	i20GuestControl;/* Offset 2C */	
+#define	guestWAIT_CFG	0x00005555	/* 4 PCI waits for all */
+#define	guestISDN_INT_E	0x01000000	/* ISDN Int en (low) */
+#define	guestVID_INT_E 	0x02000000	/* Video interrupt en (low) */
+#define	guestADI1_INT_R	0x04000000	/* ADI #1 int req (low) */
+#define	guestADI2_INT_R	0x08000000	/* ADI #2 int req (low) */
+#define	guestISDN_RES	0x10000000	/* ISDN reset bit (high) */
+#define	guestADI1_INT_S	0x20000000	/* ADI #1 int pending (low) */
+#define	guestADI2_INT_S	0x40000000	/* ADI #2 int pending (low) */
+#define	guestISDN_INT_S	0x80000000	/* ISAC int pending (low) */
+
+#define	g_A4T_JADE_RES	0x01000000	/* JADE Reset (High) */
+#define	g_A4T_ISAR_RES	0x02000000	/* ISAR Reset (High) */
+#define	g_A4T_ISAC_RES	0x04000000	/* ISAC Reset (High) */
+#define	g_A4T_JADE_BOOTR 0x08000000	/* JADE enable boot SRAM (Low) NOT USED */
+#define	g_A4T_ISAR_BOOTR 0x10000000	/* ISAR enable boot SRAM (Low) NOT USED */
+#define	g_A4T_JADE_INT_S 0x20000000	/* JADE interrupt pnd (Low) */
+#define	g_A4T_ISAR_INT_S 0x40000000	/* ISAR interrupt pnd (Low) */
+#define	g_A4T_ISAC_INT_S 0x80000000	/* ISAC interrupt pnd (Low) */
+
+ 	volatile u_int	i20CodeSource;	/* Offset 30 */	
+ 	volatile u_int	i20CodeXferCtrl;/* Offset 34 */	
+ 	volatile u_int	i20CodeMemPtr;	/* Offset 38 */	
+
+  	volatile u_int	i20IntStatus;	/* Offset 3C */	
+ 	volatile u_int	i20IntCtrl;	/* Offset 40 */	
+#define	intISDN		0x40000000	/* GIRQ1En (ISAC/ADI) (High) */
+#define	intVID		0x20000000	/* GIRQ0En (VSYNC)    (High) */
+#define	intCOD		0x10000000	/* CodRepIrqEn        (High) */
+#define	intPCI 		0x01000000	/* PCI IntA enable    (High) */
+
+ 	volatile u_int	i20I2CCtrl;	/* Offset 44					*/	
+} I20_REGISTER_FILE, *PI20_REGISTER_FILE;
+
+/*
+ * Postoffice structure for A4T
+ *
+ */
+#define	PO_OFFSET	0x00000200	/* Postoffice offset from base */
+
+#define	GCS_0		0x00000000 	/* Guest bus chip selects */
+#define	GCS_1		0x00100000
+#define	GCS_2		0x00200000
+#define	GCS_3		0x00300000
+
+#define	PO_READ		0x00000000	/* R/W from/to guest bus */
+#define	PO_WRITE	0x00800000
+
+#define	PO_PEND		0x02000000
+
+#define POSTOFFICE(postoffice) *(volatile unsigned int*)(postoffice)
+
+/* Wait unlimited (don't worry)										*/ 
+#define	__WAITI20__(postoffice)										\
+do {		   	 		   											\
+  	while ((POSTOFFICE(postoffice) & PO_PEND)) ;					\
+} while (0)
+
+#endif	/* __BKM_AX_H__ */
diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c
new file mode 100644
index 000000000000..04065ab2610f
--- /dev/null
+++ b/drivers/isdn/hisax/callc.c
@@ -0,0 +1,1793 @@
+/* $Id: callc.c,v 2.59.2.4 2004/02/11 13:21:32 keil Exp $
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include <linux/isdn/capicmd.h>
+
+const char *lli_revision = "$Revision: 2.59.2.4 $";
+
+extern struct IsdnCard cards[];
+extern int nrcards;
+
+static int init_b_st(struct Channel *chanp, int incoming);
+static void release_b_st(struct Channel *chanp);
+
+static struct Fsm callcfsm;
+static int chancount;
+
+/* experimental REJECT after ALERTING for CALLBACK to beat the 4s delay */
+#define ALERT_REJECT 0
+
+/* Value to delay the sending of the first B-channel paket after CONNECT
+ * here is no value given by ITU, but experience shows that 300 ms will
+ * work on many networks, if you or your other side is behind local exchanges
+ * a greater value may be recommented. If the delay is to short the first paket
+ * will be lost and autodetect on many comercial routers goes wrong !
+ * You can adjust this value on runtime with
+ * hisaxctrl <id> 2 <value>
+ * value is in milliseconds
+ */
+#define DEFAULT_B_DELAY	300
+
+/* Flags for remembering action done in lli */
+
+#define  FLG_START_B	0
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState *
+hisax_findcard(int driverid)
+{
+	int i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].cs)
+			if (cards[i].cs->myid == driverid)
+				return (cards[i].cs);
+	return (struct IsdnCardState *) 0;
+}
+
+static void
+link_debug(struct Channel *chanp, int direction, char *fmt, ...)
+{
+	va_list args;
+	char tmp[16];
+
+	va_start(args, fmt);
+	sprintf(tmp, "Ch%d %s ", chanp->chan,
+		direction ? "LL->HL" : "HL->LL");
+	VHiSax_putstatus(chanp->cs, tmp, fmt, args);
+	va_end(args);
+}
+
+enum {
+	ST_NULL,		/*  0 inactive */
+	ST_OUT_DIAL,		/*  1 outgoing, SETUP send; awaiting confirm */
+	ST_IN_WAIT_LL,		/*  2 incoming call received; wait for LL confirm */
+	ST_IN_ALERT_SENT,	/*  3 incoming call received; ALERT send */
+	ST_IN_WAIT_CONN_ACK,	/*  4 incoming CONNECT send; awaiting CONN_ACK */
+	ST_WAIT_BCONN,		/*  5 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */
+	ST_ACTIVE,		/*  6 active, b channel prot. established */
+	ST_WAIT_BRELEASE,	/*  7 call clear. (initiator), awaiting b channel prot. rel. */
+	ST_WAIT_BREL_DISC,	/*  8 call clear. (receiver), DISCONNECT req. received */
+	ST_WAIT_DCOMMAND,	/*  9 call clear. (receiver), awaiting DCHANNEL message */
+	ST_WAIT_DRELEASE,	/* 10 DISCONNECT sent, awaiting RELEASE */
+	ST_WAIT_D_REL_CNF,	/* 11 RELEASE sent, awaiting RELEASE confirm */
+	ST_IN_PROCEED_SEND,	/* 12 incoming call, proceeding send */ 
+};
+  
+
+#define STATE_COUNT (ST_IN_PROCEED_SEND + 1)
+
+static char *strState[] =
+{
+	"ST_NULL",
+	"ST_OUT_DIAL",
+	"ST_IN_WAIT_LL",
+	"ST_IN_ALERT_SENT",
+	"ST_IN_WAIT_CONN_ACK",
+	"ST_WAIT_BCONN",
+	"ST_ACTIVE",
+	"ST_WAIT_BRELEASE",
+	"ST_WAIT_BREL_DISC",
+	"ST_WAIT_DCOMMAND",
+	"ST_WAIT_DRELEASE",
+	"ST_WAIT_D_REL_CNF",
+	"ST_IN_PROCEED_SEND",
+};
+
+enum {
+	EV_DIAL,		/*  0 */
+	EV_SETUP_CNF,		/*  1 */
+	EV_ACCEPTB,		/*  2 */
+	EV_DISCONNECT_IND,	/*  3 */
+	EV_RELEASE, 		/*  4 */
+	EV_LEASED,		/*  5 */
+	EV_LEASED_REL,		/*  6 */
+	EV_SETUP_IND,		/*  7 */
+	EV_ACCEPTD,		/*  8 */
+	EV_SETUP_CMPL_IND,	/*  9 */
+	EV_BC_EST,		/* 10 */
+	EV_WRITEBUF,		/* 11 */
+	EV_HANGUP,		/* 12 */
+	EV_BC_REL,		/* 13 */
+	EV_CINF,		/* 14 */
+	EV_SUSPEND,		/* 15 */
+	EV_RESUME,		/* 16 */
+	EV_NOSETUP_RSP,		/* 17 */
+	EV_SETUP_ERR,		/* 18 */
+	EV_CONNECT_ERR,		/* 19 */
+	EV_PROCEED,		/* 20 */
+	EV_ALERT,		/* 21 */ 
+	EV_REDIR,		/* 22 */ 
+};
+
+#define EVENT_COUNT (EV_REDIR + 1)
+
+static char *strEvent[] =
+{
+	"EV_DIAL",
+	"EV_SETUP_CNF",
+	"EV_ACCEPTB",
+	"EV_DISCONNECT_IND",
+	"EV_RELEASE",
+	"EV_LEASED",
+	"EV_LEASED_REL",
+	"EV_SETUP_IND",
+	"EV_ACCEPTD",
+	"EV_SETUP_CMPL_IND",
+	"EV_BC_EST",
+	"EV_WRITEBUF",
+	"EV_HANGUP",
+	"EV_BC_REL",
+	"EV_CINF",
+	"EV_SUSPEND",
+	"EV_RESUME",
+	"EV_NOSETUP_RSP",
+	"EV_SETUP_ERR",
+	"EV_CONNECT_ERR",
+	"EV_PROCEED",
+	"EV_ALERT",
+	"EV_REDIR",
+};
+
+
+static inline void
+HL_LL(struct Channel *chanp, int command)
+{
+	isdn_ctrl ic;
+
+	ic.driver = chanp->cs->myid;
+	ic.command = command;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
+}
+
+static inline void
+lli_deliver_cause(struct Channel *chanp)
+{
+	isdn_ctrl ic;
+
+	if (!chanp->proc)
+		return;
+	if (chanp->proc->para.cause == NO_CAUSE)
+		return;
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_CAUSE;
+	ic.arg = chanp->chan;
+	if (chanp->cs->protocol == ISDN_PTYPE_EURO)
+		sprintf(ic.parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f,
+			chanp->proc->para.cause & 0x7f);
+	else
+		sprintf(ic.parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f,
+			chanp->proc->para.cause & 0x7f);
+	chanp->cs->iif.statcallb(&ic);
+}
+
+static inline void
+lli_close(struct FsmInst *fi)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_NULL);
+	chanp->Flags = 0;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+}
+
+static void
+lli_leased_in(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+	int ret;
+
+	if (!chanp->leased)
+		return;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+	FsmChangeState(fi, ST_IN_WAIT_LL);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_ICALL_LEASED");
+	ic.driver = chanp->cs->myid;
+	ic.command = ((chanp->chan < 2) ? ISDN_STAT_ICALL : ISDN_STAT_ICALLW);
+	ic.arg = chanp->chan;
+	ic.parm.setup.si1 = 7;
+	ic.parm.setup.si2 = 0;
+	ic.parm.setup.plan = 0;
+	ic.parm.setup.screen = 0;
+	sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1);
+	sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid);
+	ret = chanp->cs->iif.statcallb(&ic);
+	if (chanp->debug & 1)
+		link_debug(chanp, 1, "statcallb ret=%d", ret);
+	if (!ret) {
+		chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+		FsmChangeState(fi, ST_NULL);
+	}
+}
+
+
+/*
+ * Dial out
+ */
+static void
+lli_init_bchan_out(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_WAIT_BCONN);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DCONN");
+	HL_LL(chanp, ISDN_STAT_DCONN);
+	init_b_st(chanp, 0);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+lli_prep_dialout(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmDelTimer(&chanp->drel_timer, 60);
+	FsmDelTimer(&chanp->dial_timer, 73);
+	chanp->l2_active_protocol = chanp->l2_protocol;
+	chanp->incoming = 0;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+	if (chanp->leased) {
+		lli_init_bchan_out(fi, event, arg);
+	} else {
+		FsmChangeState(fi, ST_OUT_DIAL);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | REQUEST, chanp);
+	}
+}
+
+static void
+lli_resume(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmDelTimer(&chanp->drel_timer, 60);
+	FsmDelTimer(&chanp->dial_timer, 73);
+	chanp->l2_active_protocol = chanp->l2_protocol;
+	chanp->incoming = 0;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+	if (chanp->leased) {
+		lli_init_bchan_out(fi, event, arg);
+	} else {
+		FsmChangeState(fi, ST_OUT_DIAL);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_RESUME | REQUEST, chanp);
+	}
+}
+
+static void
+lli_go_active(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+
+	FsmChangeState(fi, ST_ACTIVE);
+	chanp->data_open = !0;
+	if (chanp->bcs->conmsg)
+		strcpy(ic.parm.num, chanp->bcs->conmsg);
+	else
+		ic.parm.num[0] = 0;
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BCONN %s", ic.parm.num);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_BCONN;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) (long)chanp->chan);
+}
+
+
+/*
+ * RESUME
+ */
+
+/* incoming call */
+
+static void
+lli_deliver_call(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+	int ret;
+
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+	/*
+	 * Report incoming calls only once to linklevel, use CallFlags
+	 * which is set to 3 with each broadcast message in isdnl1.c
+	 * and resetted if a interface  answered the STAT_ICALL.
+	 */
+	if (1) { /* for only one TEI */
+		FsmChangeState(fi, ST_IN_WAIT_LL);
+		if (chanp->debug & 1)
+			link_debug(chanp, 0, (chanp->chan < 2) ? "STAT_ICALL" : "STAT_ICALLW");
+		ic.driver = chanp->cs->myid;
+		ic.command = ((chanp->chan < 2) ? ISDN_STAT_ICALL : ISDN_STAT_ICALLW);
+
+		ic.arg = chanp->chan;
+		/*
+		 * No need to return "unknown" for calls without OAD,
+		 * cause that's handled in linklevel now (replaced by '0')
+		 */
+		memcpy(&ic.parm.setup, &chanp->proc->para.setup, sizeof(setup_parm));
+		ret = chanp->cs->iif.statcallb(&ic);
+		if (chanp->debug & 1)
+			link_debug(chanp, 1, "statcallb ret=%d", ret);
+
+		switch (ret) {
+			case 1:	/* OK, someone likes this call */
+				FsmDelTimer(&chanp->drel_timer, 61);
+				FsmChangeState(fi, ST_IN_ALERT_SENT);
+				chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+				break;
+			case 5: /* direct redirect */
+			case 4: /* Proceeding desired */
+				FsmDelTimer(&chanp->drel_timer, 61);
+				FsmChangeState(fi, ST_IN_PROCEED_SEND);
+				chanp->d_st->lli.l4l3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc);
+				if (ret == 5) {
+					memcpy(&chanp->setup, &ic.parm.setup, sizeof(setup_parm));
+					chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
+				}
+				break;
+			case 2:	/* Rejecting Call */
+				break;
+			case 3:	/* incomplete number */
+				FsmDelTimer(&chanp->drel_timer, 61);
+				chanp->d_st->lli.l4l3(chanp->d_st, CC_MORE_INFO | REQUEST, chanp->proc);
+				break;
+			case 0:	/* OK, nobody likes this call */
+			default:	/* statcallb problems */
+				chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+				chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+				FsmChangeState(fi, ST_NULL);
+				break;
+		}
+	} else {
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+		chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+	}
+}
+
+static void
+lli_send_dconnect(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
+}
+
+static void
+lli_send_alert(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_IN_ALERT_SENT);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+}
+
+static void
+lli_send_redir(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
+}
+
+static void
+lli_init_bchan_in(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_WAIT_BCONN);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DCONN");
+	HL_LL(chanp, ISDN_STAT_DCONN);
+	chanp->l2_active_protocol = chanp->l2_protocol;
+	chanp->incoming = !0;
+	init_b_st(chanp, !0);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+lli_setup_rsp(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_init_bchan_in(fi, event, arg);
+	} else {
+		FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+#ifdef WANT_ALERT
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+#endif
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
+	}
+}
+
+/* Call suspend */
+
+static void
+lli_suspend(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc);
+}
+
+/* Call clearing */
+
+static void
+lli_leased_hup(struct FsmInst *fi, struct Channel *chanp)
+{
+	isdn_ctrl ic;
+
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_CAUSE;
+	ic.arg = chanp->chan;
+	sprintf(ic.parm.num, "L0010");
+	chanp->cs->iif.statcallb(&ic);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	HL_LL(chanp, ISDN_STAT_DHUP);
+	lli_close(fi);
+}
+
+static void
+lli_disconnect_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		FsmChangeState(fi, ST_WAIT_DRELEASE);
+		if (chanp->proc)
+			chanp->proc->para.cause = 0x10;	/* Normal Call Clearing */
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
+			chanp->proc);
+	}
+}
+
+static void
+lli_disconnect_reject(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		FsmChangeState(fi, ST_WAIT_DRELEASE);
+		if (chanp->proc)
+			chanp->proc->para.cause = 0x15;	/* Call Rejected */
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
+			chanp->proc);
+	}
+}
+
+static void
+lli_dhup_close(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		if (chanp->debug & 1)
+			link_debug(chanp, 0, "STAT_DHUP");
+		lli_deliver_cause(chanp);
+		HL_LL(chanp, ISDN_STAT_DHUP);
+		lli_close(fi);
+	}
+}
+
+static void
+lli_reject_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+		return;
+	}
+#ifndef ALERT_REJECT
+	if (chanp->proc)
+		chanp->proc->para.cause = 0x15;	/* Call Rejected */
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc);
+	lli_dhup_close(fi, event, arg);
+#else
+	FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63);
+	FsmChangeState(fi, ST_IN_ALERT_SENT);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+#endif
+}
+
+static void
+lli_disconn_bchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_BRELEASE);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+}
+
+static void
+lli_start_disc(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		lli_disconnect_req(fi, event, arg);
+	}
+}
+
+static void
+lli_rel_b_disc(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	release_b_st(chanp);
+	lli_start_disc(fi, event, arg);
+}
+
+static void
+lli_bhup_disc(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+ 
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	lli_rel_b_disc(fi, event, arg);
+}
+
+static void
+lli_bhup_rel_b(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_WAIT_DCOMMAND);
+	chanp->data_open = 0;
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	release_b_st(chanp);
+}
+
+static void
+lli_release_bchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_BREL_DISC);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+}
+
+
+static void
+lli_rel_b_dhup(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	release_b_st(chanp);
+	lli_dhup_close(fi, event, arg);
+}
+
+static void
+lli_bhup_dhup(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	lli_rel_b_dhup(fi, event, arg);
+}
+
+static void
+lli_abort(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+	lli_bhup_dhup(fi, event, arg);
+}
+ 
+static void
+lli_release_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		FsmChangeState(fi, ST_WAIT_D_REL_CNF);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST,
+			chanp->proc);
+	}
+}
+
+static void
+lli_rel_b_release_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	release_b_st(chanp);
+	lli_release_req(fi, event, arg);
+}
+
+static void
+lli_bhup_release_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+ 
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	lli_rel_b_release_req(fi, event, arg);
+}
+
+
+/* processing charge info */
+static void
+lli_charge_info(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_CINF;
+	ic.arg = chanp->chan;
+	sprintf(ic.parm.num, "%d", chanp->proc->para.chargeinfo);
+	chanp->cs->iif.statcallb(&ic);
+}
+
+/* error procedures */
+
+static void
+lli_dchan_not_ready(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	HL_LL(chanp, ISDN_STAT_DHUP); 
+}
+
+static void
+lli_no_setup_rsp(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	HL_LL(chanp, ISDN_STAT_DHUP);
+	lli_close(fi); 
+}
+
+static void
+lli_error(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+}
+
+static void
+lli_failure_l(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_NULL);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_CAUSE;
+	ic.arg = chanp->chan;
+	sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f);
+	chanp->cs->iif.statcallb(&ic);
+	HL_LL(chanp, ISDN_STAT_DHUP);
+	chanp->Flags = 0;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+}
+
+static void
+lli_rel_b_fail(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	release_b_st(chanp);
+	lli_failure_l(fi, event, arg);
+}
+
+static void
+lli_bhup_fail(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	lli_rel_b_fail(fi, event, arg);
+}
+
+static void
+lli_failure_a(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+	lli_bhup_fail(fi, event, arg);
+}
+
+/* *INDENT-OFF* */
+static struct FsmNode fnlist[] __initdata =
+{
+        {ST_NULL,               EV_DIAL,                lli_prep_dialout},
+        {ST_NULL,               EV_RESUME,              lli_resume},
+        {ST_NULL,               EV_SETUP_IND,           lli_deliver_call},
+        {ST_NULL,               EV_LEASED,              lli_leased_in},
+        {ST_OUT_DIAL,           EV_SETUP_CNF,           lli_init_bchan_out},
+        {ST_OUT_DIAL,           EV_HANGUP,              lli_disconnect_req},
+        {ST_OUT_DIAL,           EV_DISCONNECT_IND,      lli_release_req},
+        {ST_OUT_DIAL,           EV_RELEASE,             lli_dhup_close},
+        {ST_OUT_DIAL,           EV_NOSETUP_RSP,         lli_no_setup_rsp},
+        {ST_OUT_DIAL,           EV_SETUP_ERR,           lli_error},
+        {ST_IN_WAIT_LL,         EV_LEASED_REL,          lli_failure_l},
+        {ST_IN_WAIT_LL,         EV_ACCEPTD,             lli_setup_rsp},
+        {ST_IN_WAIT_LL,         EV_HANGUP,              lli_reject_req},
+        {ST_IN_WAIT_LL,         EV_DISCONNECT_IND,      lli_release_req},
+        {ST_IN_WAIT_LL,         EV_RELEASE,             lli_dhup_close},
+        {ST_IN_WAIT_LL,         EV_SETUP_IND,           lli_deliver_call},
+        {ST_IN_WAIT_LL,         EV_SETUP_ERR,           lli_error},
+        {ST_IN_ALERT_SENT,      EV_SETUP_CMPL_IND,      lli_init_bchan_in},
+        {ST_IN_ALERT_SENT,      EV_ACCEPTD,             lli_send_dconnect},
+        {ST_IN_ALERT_SENT,      EV_HANGUP,              lli_disconnect_reject},
+        {ST_IN_ALERT_SENT,      EV_DISCONNECT_IND,      lli_release_req},
+        {ST_IN_ALERT_SENT,      EV_RELEASE,             lli_dhup_close},
+	{ST_IN_ALERT_SENT,	EV_REDIR,		lli_send_redir},
+	{ST_IN_PROCEED_SEND,	EV_REDIR,		lli_send_redir},
+	{ST_IN_PROCEED_SEND,	EV_ALERT,		lli_send_alert},
+	{ST_IN_PROCEED_SEND,	EV_ACCEPTD,		lli_send_dconnect},
+	{ST_IN_PROCEED_SEND,	EV_HANGUP,		lli_disconnect_reject},
+	{ST_IN_PROCEED_SEND,	EV_DISCONNECT_IND,	lli_dhup_close},
+        {ST_IN_ALERT_SENT,      EV_RELEASE,             lli_dhup_close},
+        {ST_IN_WAIT_CONN_ACK,   EV_SETUP_CMPL_IND,      lli_init_bchan_in},
+        {ST_IN_WAIT_CONN_ACK,   EV_HANGUP,              lli_disconnect_req},
+        {ST_IN_WAIT_CONN_ACK,   EV_DISCONNECT_IND,      lli_release_req},
+        {ST_IN_WAIT_CONN_ACK,   EV_RELEASE,             lli_dhup_close},
+        {ST_IN_WAIT_CONN_ACK,   EV_CONNECT_ERR,         lli_error},
+        {ST_WAIT_BCONN,         EV_BC_EST,              lli_go_active},
+        {ST_WAIT_BCONN,         EV_BC_REL,              lli_rel_b_disc},
+        {ST_WAIT_BCONN,         EV_HANGUP,              lli_rel_b_disc},
+        {ST_WAIT_BCONN,         EV_DISCONNECT_IND,      lli_rel_b_release_req},
+        {ST_WAIT_BCONN,         EV_RELEASE,             lli_rel_b_dhup},
+        {ST_WAIT_BCONN,         EV_LEASED_REL,          lli_rel_b_fail},
+        {ST_WAIT_BCONN,         EV_CINF,                lli_charge_info},
+        {ST_ACTIVE,             EV_CINF,                lli_charge_info},
+        {ST_ACTIVE,             EV_BC_REL,              lli_bhup_rel_b},
+        {ST_ACTIVE,             EV_SUSPEND,             lli_suspend},
+        {ST_ACTIVE,             EV_HANGUP,              lli_disconn_bchan},
+        {ST_ACTIVE,             EV_DISCONNECT_IND,      lli_release_bchan},
+        {ST_ACTIVE,             EV_RELEASE,             lli_abort},
+        {ST_ACTIVE,             EV_LEASED_REL,          lli_failure_a},
+        {ST_WAIT_BRELEASE,      EV_BC_REL,              lli_bhup_disc},
+        {ST_WAIT_BRELEASE,      EV_DISCONNECT_IND,      lli_bhup_release_req},
+        {ST_WAIT_BRELEASE,      EV_RELEASE,             lli_bhup_dhup},
+        {ST_WAIT_BRELEASE,      EV_LEASED_REL,          lli_bhup_fail},
+        {ST_WAIT_BREL_DISC,     EV_BC_REL,              lli_bhup_release_req},
+        {ST_WAIT_BREL_DISC,     EV_RELEASE,             lli_bhup_dhup},
+        {ST_WAIT_DCOMMAND,      EV_HANGUP,              lli_start_disc},
+        {ST_WAIT_DCOMMAND,      EV_DISCONNECT_IND,      lli_release_req},
+        {ST_WAIT_DCOMMAND,      EV_RELEASE,             lli_dhup_close},
+        {ST_WAIT_DCOMMAND,      EV_LEASED_REL,          lli_failure_l},
+        {ST_WAIT_DRELEASE,      EV_RELEASE,             lli_dhup_close},
+        {ST_WAIT_DRELEASE,      EV_DIAL,                lli_dchan_not_ready},
+  /* ETS 300-104 16.1 */
+        {ST_WAIT_D_REL_CNF,     EV_RELEASE,             lli_dhup_close},
+        {ST_WAIT_D_REL_CNF,     EV_DIAL,                lli_dchan_not_ready},
+};
+/* *INDENT-ON* */
+
+#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
+
+int __init
+CallcNew(void)
+{
+	callcfsm.state_count = STATE_COUNT;
+	callcfsm.event_count = EVENT_COUNT;
+	callcfsm.strEvent = strEvent;
+	callcfsm.strState = strState;
+	return FsmNew(&callcfsm, fnlist, FNCOUNT);
+}
+
+void
+CallcFree(void)
+{
+	FsmFree(&callcfsm);
+}
+
+static void
+release_b_st(struct Channel *chanp)
+{
+	struct PStack *st = chanp->b_st;
+
+	if(test_and_clear_bit(FLG_START_B, &chanp->Flags)) {
+		chanp->bcs->BC_Close(chanp->bcs);
+		switch (chanp->l2_active_protocol) {
+			case (ISDN_PROTO_L2_X75I):
+				releasestack_isdnl2(st);
+				break;
+			case (ISDN_PROTO_L2_HDLC):
+			case (ISDN_PROTO_L2_HDLC_56K):
+			case (ISDN_PROTO_L2_TRANS):
+			case (ISDN_PROTO_L2_MODEM):
+			case (ISDN_PROTO_L2_FAX):
+				releasestack_transl2(st);
+				break;
+		}
+	} 
+}
+
+struct Channel
+*selectfreechannel(struct PStack *st, int bch)
+{
+	struct IsdnCardState *cs = st->l1.hardware;
+	struct Channel *chanp = st->lli.userdata;
+	int i;
+
+	if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+		i=1;
+	else
+		i=0;
+
+	if (!bch) {
+		i = 2; /* virtual channel */
+		chanp += 2;
+	}
+
+	while (i < ((bch) ? cs->chanlimit : (2 + MAX_WAITING_CALLS))) {
+		if (chanp->fi.state == ST_NULL)
+			return (chanp);
+		chanp++;
+		i++;
+	}
+
+	if (bch) /* number of channels is limited */ {
+		i = 2; /* virtual channel */
+		chanp = st->lli.userdata;
+		chanp += i;
+		while (i < (2 + MAX_WAITING_CALLS)) {
+			if (chanp->fi.state == ST_NULL)
+				return (chanp);
+			chanp++;
+			i++;
+		}
+	}
+	return (NULL);
+}
+
+static void stat_redir_result(struct IsdnCardState *cs, int chan, ulong result)
+{	isdn_ctrl ic;
+  
+	ic.driver = cs->myid;
+	ic.command = ISDN_STAT_REDIR;
+	ic.arg = chan; 
+	ic.parm.num[0] = result;
+	cs->iif.statcallb(&ic);
+} /* stat_redir_result */
+
+static void
+dchan_l3l4(struct PStack *st, int pr, void *arg)
+{
+	struct l3_process *pc = arg;
+	struct IsdnCardState *cs = st->l1.hardware;
+	struct Channel *chanp;
+
+	if(!pc)
+		return;
+
+	if (pr == (CC_SETUP | INDICATION)) {
+		if (!(chanp = selectfreechannel(pc->st, pc->para.bchannel))) {
+			pc->para.cause = 0x11;	/* User busy */
+			pc->st->lli.l4l3(pc->st, CC_REJECT | REQUEST, pc);
+		} else {
+			chanp->proc = pc;
+			pc->chan = chanp;
+			FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+		}
+		return;
+	}
+	if (!(chanp = pc->chan))
+		return;
+
+	switch (pr) {
+		case (CC_MORE_INFO | INDICATION):
+			FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+			break;
+		case (CC_DISCONNECT | INDICATION):
+			FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
+			break;
+		case (CC_RELEASE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+			break;
+		case (CC_SUSPEND | CONFIRM):
+			FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+			break;
+		case (CC_RESUME | CONFIRM):
+			FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+			break;
+		case (CC_RESUME_ERR):
+			FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+			break;
+		case (CC_RELEASE | INDICATION):
+			FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+			break;
+		case (CC_SETUP_COMPL | INDICATION):
+			FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
+			break;
+		case (CC_SETUP | CONFIRM):
+			FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+			break;
+		case (CC_CHARGE | INDICATION):
+			FsmEvent(&chanp->fi, EV_CINF, NULL);
+			break;
+		case (CC_NOSETUP_RSP):
+			FsmEvent(&chanp->fi, EV_NOSETUP_RSP, NULL);
+			break;
+		case (CC_SETUP_ERR):
+			FsmEvent(&chanp->fi, EV_SETUP_ERR, NULL);
+			break;
+		case (CC_CONNECT_ERR):
+			FsmEvent(&chanp->fi, EV_CONNECT_ERR, NULL);
+			break;
+		case (CC_RELEASE_ERR):
+			FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+			break;
+		case (CC_PROCEED_SEND | INDICATION):
+		case (CC_PROCEEDING | INDICATION):
+		case (CC_ALERTING | INDICATION):
+		case (CC_PROGRESS | INDICATION):
+		case (CC_NOTIFY | INDICATION):
+			break;
+		case (CC_REDIR | INDICATION):
+			stat_redir_result(cs, chanp->chan, pc->redir_result); 
+			break;
+			default:
+			if (chanp->debug & 0x800) {
+				HiSax_putstatus(chanp->cs, "Ch",
+					"%d L3->L4 unknown primitiv %#x",
+					chanp->chan, pr);
+			}
+	}
+}
+
+static void
+dummy_pstack(struct PStack *st, int pr, void *arg) {
+	printk(KERN_WARNING"call to dummy_pstack pr=%04x arg %lx\n", pr, (long)arg);
+}
+
+static int
+init_PStack(struct PStack **stp) {
+	*stp = kmalloc(sizeof(struct PStack), GFP_ATOMIC);
+	if (!*stp)
+		return -ENOMEM;
+	(*stp)->next = NULL;
+	(*stp)->l1.l1l2 = dummy_pstack;
+	(*stp)->l1.l1hw = dummy_pstack;
+	(*stp)->l1.l1tei = dummy_pstack;
+	(*stp)->l2.l2tei = dummy_pstack;
+	(*stp)->l2.l2l1 = dummy_pstack;
+	(*stp)->l2.l2l3 = dummy_pstack;
+	(*stp)->l3.l3l2 = dummy_pstack;
+	(*stp)->l3.l3ml3 = dummy_pstack;
+	(*stp)->l3.l3l4 = dummy_pstack;
+	(*stp)->lli.l4l3 = dummy_pstack;
+	(*stp)->ma.layer = dummy_pstack;
+	return 0;
+}
+
+static int
+init_d_st(struct Channel *chanp)
+{
+	struct PStack *st;
+	struct IsdnCardState *cs = chanp->cs;
+	char tmp[16];
+	int err;
+
+	err = init_PStack(&chanp->d_st);
+	if (err)
+		return err;
+	st = chanp->d_st;
+	st->next = NULL;
+	HiSax_addlist(cs, st);
+	setstack_HiSax(st, cs);
+	st->l2.sap = 0;
+	st->l2.tei = -1;
+	st->l2.flag = 0;
+	test_and_set_bit(FLG_MOD128, &st->l2.flag);
+	test_and_set_bit(FLG_LAPD, &st->l2.flag);
+	test_and_set_bit(FLG_ORIG, &st->l2.flag);
+	st->l2.maxlen = MAX_DFRAME_LEN;
+	st->l2.window = 1;
+	st->l2.T200 = 1000;	/* 1000 milliseconds  */
+	st->l2.N200 = 3;	/* try 3 times        */
+	st->l2.T203 = 10000;	/* 10000 milliseconds */
+	if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+		sprintf(tmp, "DCh%d Q.921 ", chanp->chan);
+	else
+		sprintf(tmp, "DCh Q.921 ");
+	setstack_isdnl2(st, tmp);
+	setstack_l3dc(st, chanp);
+	st->lli.userdata = chanp;
+	st->l3.l3l4 = dchan_l3l4;
+
+	return 0;
+}
+
+static void
+callc_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	struct Channel *chanp = fi->userdata;
+	char tmp[16];
+
+	va_start(args, fmt);
+	sprintf(tmp, "Ch%d callc ", chanp->chan);
+	VHiSax_putstatus(chanp->cs, tmp, fmt, args);
+	va_end(args);
+}
+
+static int
+init_chan(int chan, struct IsdnCardState *csta)
+{
+	struct Channel *chanp = csta->channel + chan;
+	int err;
+
+	chanp->cs = csta;
+	chanp->bcs = csta->bcs + chan;
+	chanp->chan = chan;
+	chanp->incoming = 0;
+	chanp->debug = 0;
+	chanp->Flags = 0;
+	chanp->leased = 0;
+	err = init_PStack(&chanp->b_st);
+	if (err)
+		return err;
+	chanp->b_st->l1.delay = DEFAULT_B_DELAY;
+	chanp->fi.fsm = &callcfsm;
+	chanp->fi.state = ST_NULL;
+	chanp->fi.debug = 0;
+	chanp->fi.userdata = chanp;
+	chanp->fi.printdebug = callc_debug;
+	FsmInitTimer(&chanp->fi, &chanp->dial_timer);
+	FsmInitTimer(&chanp->fi, &chanp->drel_timer);
+	if (!chan || (test_bit(FLG_TWO_DCHAN, &csta->HW_Flags) && chan < 2)) {
+		err = init_d_st(chanp);
+		if (err)
+			return err;
+	} else {
+		chanp->d_st = csta->channel->d_st;
+	}
+	chanp->data_open = 0;
+	return 0;
+}
+
+int
+CallcNewChan(struct IsdnCardState *csta) {
+	int i, err;
+
+	chancount += 2;
+	err = init_chan(0, csta);
+	if (err)
+		return err;
+	err = init_chan(1, csta);
+	if (err)
+		return err;
+	printk(KERN_INFO "HiSax: 2 channels added\n");
+
+	for (i = 0; i < MAX_WAITING_CALLS; i++) { 
+		err = init_chan(i+2,csta);
+		if (err)
+			return err;
+	}
+	printk(KERN_INFO "HiSax: MAX_WAITING_CALLS added\n");
+	if (test_bit(FLG_PTP, &csta->channel->d_st->l2.flag)) {
+		printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
+		csta->channel->d_st->lli.l4l3(csta->channel->d_st,
+			DL_ESTABLISH | REQUEST, NULL);
+	}
+	return (0);
+}
+
+static void
+release_d_st(struct Channel *chanp)
+{
+	struct PStack *st = chanp->d_st;
+
+	if (!st)
+		return;
+	releasestack_isdnl2(st);
+	releasestack_isdnl3(st);
+	HiSax_rmlist(st->l1.hardware, st);
+	kfree(st);
+	chanp->d_st = NULL;
+}
+
+void
+CallcFreeChan(struct IsdnCardState *csta)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		FsmDelTimer(&csta->channel[i].drel_timer, 74);
+		FsmDelTimer(&csta->channel[i].dial_timer, 75);
+		if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags))
+			release_d_st(csta->channel + i);
+		if (csta->channel[i].b_st) {
+			release_b_st(csta->channel + i);
+			kfree(csta->channel[i].b_st);
+			csta->channel[i].b_st = NULL;
+		} else
+			printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i);
+		if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+			release_d_st(csta->channel + i);
+		} else
+			csta->channel[i].d_st = NULL;
+	}
+}
+
+static void
+lldata_handler(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (DL_DATA  | INDICATION):
+			if (chanp->data_open) {
+				if (chanp->debug & 0x800)
+					link_debug(chanp, 0, "lldata: %d", skb->len);
+				chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
+			} else {
+				link_debug(chanp, 0, "lldata: channel not open");
+				dev_kfree_skb(skb);
+			}
+			break;
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_ESTABLISH | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+			break;
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+			break;
+		default:
+			printk(KERN_WARNING "lldata_handler unknown primitive %#x\n",
+				pr);
+			break;
+	}
+}
+
+static void
+lltrans_handler(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (PH_DATA | INDICATION):
+			if (chanp->data_open) {
+				if (chanp->debug & 0x800)
+					link_debug(chanp, 0, "lltrans: %d", skb->len);
+				chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
+			} else {
+				link_debug(chanp, 0, "lltrans: channel not open");
+				dev_kfree_skb(skb);
+			}
+			break;
+		case (PH_ACTIVATE | INDICATION):
+		case (PH_ACTIVATE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+			break;
+		case (PH_DEACTIVATE | INDICATION):
+		case (PH_DEACTIVATE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+			break;
+		default:
+			printk(KERN_WARNING "lltrans_handler unknown primitive %#x\n",
+				pr);
+			break;
+	}
+}
+
+void
+lli_writewakeup(struct PStack *st, int len)
+{
+	struct Channel *chanp = st->lli.userdata;
+	isdn_ctrl ic;
+
+	if (chanp->debug & 0x800)
+		link_debug(chanp, 0, "llwakeup: %d", len);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_BSENT;
+	ic.arg = chanp->chan;
+	ic.parm.length = len;
+	chanp->cs->iif.statcallb(&ic);
+}
+
+static int
+init_b_st(struct Channel *chanp, int incoming)
+{
+	struct PStack *st = chanp->b_st;
+	struct IsdnCardState *cs = chanp->cs;
+	char tmp[16];
+
+	st->l1.hardware = cs;
+	if (chanp->leased)
+		st->l1.bc = chanp->chan & 1;
+	else
+		st->l1.bc = chanp->proc->para.bchannel - 1;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+		case (ISDN_PROTO_L2_HDLC):
+			st->l1.mode = L1_MODE_HDLC;
+			break;
+		case (ISDN_PROTO_L2_HDLC_56K):
+			st->l1.mode = L1_MODE_HDLC_56K;
+			break;
+		case (ISDN_PROTO_L2_TRANS):
+			st->l1.mode = L1_MODE_TRANS;
+			break;
+		case (ISDN_PROTO_L2_MODEM):
+			st->l1.mode = L1_MODE_V32;
+			break;
+		case (ISDN_PROTO_L2_FAX):
+			st->l1.mode = L1_MODE_FAX;
+			break;
+	}
+	chanp->bcs->conmsg = NULL;
+	if (chanp->bcs->BC_SetStack(st, chanp->bcs))
+		return (-1);
+	st->l2.flag = 0;
+	test_and_set_bit(FLG_LAPB, &st->l2.flag);
+	st->l2.maxlen = MAX_DATA_SIZE;
+	if (!incoming)
+		test_and_set_bit(FLG_ORIG, &st->l2.flag);
+	st->l2.T200 = 1000;	/* 1000 milliseconds */
+	st->l2.window = 7;
+	st->l2.N200 = 4;	/* try 4 times       */
+	st->l2.T203 = 5000;	/* 5000 milliseconds */
+	st->l3.debug = 0;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			sprintf(tmp, "Ch%d X.75", chanp->chan);
+			setstack_isdnl2(st, tmp);
+			setstack_l3bc(st, chanp);
+			st->l2.l2l3 = lldata_handler;
+			st->lli.userdata = chanp;
+			test_and_clear_bit(FLG_LLI_L1WAKEUP, &st->lli.flag);
+			test_and_set_bit(FLG_LLI_L2WAKEUP, &st->lli.flag);
+			st->l2.l2m.debug = chanp->debug & 16;
+			st->l2.debug = chanp->debug & 64;
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+		case (ISDN_PROTO_L2_HDLC_56K):
+		case (ISDN_PROTO_L2_TRANS):
+		case (ISDN_PROTO_L2_MODEM):
+		case (ISDN_PROTO_L2_FAX):
+			st->l1.l1l2 = lltrans_handler;
+			st->lli.userdata = chanp;
+			test_and_set_bit(FLG_LLI_L1WAKEUP, &st->lli.flag);
+			test_and_clear_bit(FLG_LLI_L2WAKEUP, &st->lli.flag);
+			setstack_transl2(st);
+			setstack_l3bc(st, chanp);
+			break;
+	}
+	test_and_set_bit(FLG_START_B, &chanp->Flags);
+	return (0);
+}
+
+static void
+leased_l4l3(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+			link_debug(chanp, 0, "leased line d-channel DATA");
+			dev_kfree_skb(skb);
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+			break;
+		case (DL_RELEASE | REQUEST):
+			break;
+		default:
+			printk(KERN_WARNING "transd_l4l3 unknown primitive %#x\n",
+				pr);
+			break;
+	}
+}
+
+static void
+leased_l1l2(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
+	struct sk_buff *skb = arg;
+	int i,event = EV_LEASED_REL;
+
+	switch (pr) {
+		case (PH_DATA | INDICATION):
+			link_debug(chanp, 0, "leased line d-channel DATA");
+			dev_kfree_skb(skb);
+			break;
+		case (PH_ACTIVATE | INDICATION):
+		case (PH_ACTIVATE | CONFIRM):
+			event = EV_LEASED;
+		case (PH_DEACTIVATE | INDICATION):
+		case (PH_DEACTIVATE | CONFIRM):
+			if (test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags))
+				i = 1;
+			else
+				i = 0;
+			while (i < 2) {
+				FsmEvent(&chanp->fi, event, NULL);
+				chanp++;
+				i++;
+			}
+			break;
+		default:
+			printk(KERN_WARNING
+				"transd_l1l2 unknown primitive %#x\n", pr);
+			break;
+	}
+}
+
+static void
+distr_debug(struct IsdnCardState *csta, int debugflags)
+{
+	int i;
+	struct Channel *chanp = csta->channel;
+
+	for (i = 0; i < (2 + MAX_WAITING_CALLS) ; i++) {
+		chanp[i].debug = debugflags;
+		chanp[i].fi.debug = debugflags & 2;
+		chanp[i].d_st->l2.l2m.debug = debugflags & 8;
+		chanp[i].b_st->l2.l2m.debug = debugflags & 0x10;
+		chanp[i].d_st->l2.debug = debugflags & 0x20;
+		chanp[i].b_st->l2.debug = debugflags & 0x40;
+		chanp[i].d_st->l3.l3m.debug = debugflags & 0x80;
+		chanp[i].b_st->l3.l3m.debug = debugflags & 0x100;
+		chanp[i].b_st->ma.tei_m.debug = debugflags & 0x200;
+		chanp[i].b_st->ma.debug = debugflags & 0x200;
+		chanp[i].d_st->l1.l1m.debug = debugflags & 0x1000;
+		chanp[i].b_st->l1.l1m.debug = debugflags & 0x2000;
+	}
+	if (debugflags & 4)
+		csta->debug |= DEB_DLOG_HEX;
+	else
+		csta->debug &= ~DEB_DLOG_HEX;
+}
+
+static char tmpbuf[256];
+
+static void
+capi_debug(struct Channel *chanp, capi_msg *cm)
+{
+	char *t = tmpbuf;
+
+	t += QuickHex(t, (u_char *)cm, (cm->Length>50)? 50: cm->Length);
+	t--;
+	*t= 0;
+	HiSax_putstatus(chanp->cs, "Ch", "%d CAPIMSG %s", chanp->chan, tmpbuf);
+}
+
+void
+lli_got_fac_req(struct Channel *chanp, capi_msg *cm) {
+	if ((cm->para[0] != 3) || (cm->para[1] != 0))
+		return;
+	if (cm->para[2]<3)
+		return;
+	if (cm->para[4] != 0)
+		return;
+	switch(cm->para[3]) {
+		case 4: /* Suspend */
+			strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1);
+			FsmEvent(&chanp->fi, EV_SUSPEND, cm);
+			break;
+		case 5: /* Resume */
+			strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1);
+			if (chanp->fi.state == ST_NULL) {
+				FsmEvent(&chanp->fi, EV_RESUME, cm);
+			} else {
+				FsmDelTimer(&chanp->dial_timer, 72);
+				FsmAddTimer(&chanp->dial_timer, 80, EV_RESUME, cm, 73);
+			}
+			break;
+	}
+}
+
+void
+lli_got_manufacturer(struct Channel *chanp, struct IsdnCardState *cs, capi_msg *cm) {
+	if ((cs->typ == ISDN_CTYPE_ELSA) || (cs->typ == ISDN_CTYPE_ELSA_PNP) ||
+		(cs->typ == ISDN_CTYPE_ELSA_PCI)) {
+		if (cs->hw.elsa.MFlag) {
+			cs->cardmsg(cs, CARD_AUX_IND, cm->para);
+		}
+	}
+}
+
+
+/***************************************************************/
+/* Limit the available number of channels for the current card */
+/***************************************************************/
+static int 
+set_channel_limit(struct IsdnCardState *cs, int chanmax)
+{
+	isdn_ctrl ic;
+	int i, ii;
+
+	if ((chanmax < 0) || (chanmax > 2))
+		return(-EINVAL);
+	cs->chanlimit = 0;
+	for (ii = 0; ii < 2; ii++) {
+		ic.driver = cs->myid;
+		ic.command = ISDN_STAT_DISCH;
+		ic.arg = ii;
+		if (ii >= chanmax)
+			ic.parm.num[0] = 0; /* disabled */
+		else
+			ic.parm.num[0] = 1; /* enabled */
+		i = cs->iif.statcallb(&ic); 
+		if (i) return(-EINVAL);
+		if (ii < chanmax) 
+			cs->chanlimit++;
+	}
+	return(0);
+} /* set_channel_limit */
+
+int
+HiSax_command(isdn_ctrl * ic)
+{
+	struct IsdnCardState *csta = hisax_findcard(ic->driver);
+	struct PStack *st;
+	struct Channel *chanp;
+	int i;
+	u_int num;
+
+	if (!csta) {
+		printk(KERN_ERR
+		"HiSax: if_command %d called with invalid driverId %d!\n",
+			ic->command, ic->driver);
+		return -ENODEV;
+	}
+	switch (ic->command) {
+		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,
+					ic->parm.setup.si1, ic->parm.setup.si2);
+			memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm));
+			if (!strcmp(chanp->setup.eazmsn, "0"))
+				chanp->setup.eazmsn[0] = '\0';
+			/* this solution is dirty and may be change, if
+			 * we make a callreference based callmanager */
+			if (chanp->fi.state == ST_NULL) {
+				FsmEvent(&chanp->fi, EV_DIAL, NULL);
+			} else {
+				FsmDelTimer(&chanp->dial_timer, 70);
+				FsmAddTimer(&chanp->dial_timer, 50, EV_DIAL, NULL, 71);
+			}
+			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)
+				break;
+			switch(ic->parm.cmsg.Command) {
+				case CAPI_FACILITY:
+					if (ic->parm.cmsg.Subcommand == CAPI_REQ)
+						lli_got_fac_req(chanp, &ic->parm.cmsg);
+					break;
+				case CAPI_MANUFACTURER:
+					if (ic->parm.cmsg.Subcommand == CAPI_REQ)
+						lli_got_manufacturer(chanp, csta, &ic->parm.cmsg);
+					break;
+				default:
+					break;
+			}
+			break;
+		case (ISDN_CMD_IOCTL):
+			switch (ic->arg) {
+				case (0):
+					num = *(unsigned int *) ic->parm.num;
+					HiSax_reportcard(csta->cardnr, num);
+					break;
+				case (1):
+					num = *(unsigned int *) ic->parm.num;
+					distr_debug(csta, num);
+					printk(KERN_DEBUG "HiSax: debugging flags card %d set to %x\n",
+						csta->cardnr + 1, num);
+					HiSax_putstatus(csta, "debugging flags ",
+						"card %d set to %x", csta->cardnr + 1, num);
+					break;
+				case (2):
+					num = *(unsigned int *) ic->parm.num;
+					csta->channel[0].b_st->l1.delay = num;
+					csta->channel[1].b_st->l1.delay = num;
+					HiSax_putstatus(csta, "delay ", "card %d set to %d ms",
+						csta->cardnr + 1, num);
+					printk(KERN_DEBUG "HiSax: delay card %d set to %d ms\n",
+						csta->cardnr + 1, num);
+					break;
+				case (5):	/* set card in leased mode */
+					num = *(unsigned int *) ic->parm.num;
+					if ((num <1) || (num > 2)) {
+						HiSax_putstatus(csta, "Set LEASED ",
+							"wrong channel %d", num);
+						printk(KERN_WARNING "HiSax: Set LEASED wrong channel %d\n",
+							num);
+					} else {
+						num--;
+						chanp = csta->channel +num;
+						chanp->leased = 1;
+						HiSax_putstatus(csta, "Card",
+							"%d channel %d set leased mode\n",
+							csta->cardnr + 1, num + 1);
+						chanp->d_st->l1.l1l2 = leased_l1l2;
+						chanp->d_st->lli.l4l3 = leased_l4l3;
+						chanp->d_st->lli.l4l3(chanp->d_st,
+							DL_ESTABLISH | REQUEST, NULL);
+					}
+					break;
+				case (6):	/* set B-channel test loop */
+					num = *(unsigned int *) ic->parm.num;
+					if (csta->stlist)
+						csta->stlist->l2.l2l1(csta->stlist,
+							PH_TESTLOOP | REQUEST, (void *) (long)num);
+					break;
+				case (7):	/* set card in PTP mode */
+					num = *(unsigned int *) ic->parm.num;
+					if (test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+						printk(KERN_ERR "HiSax PTP mode only with one TEI possible\n");
+					} else if (num) {
+						test_and_set_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
+						test_and_set_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
+						csta->channel[0].d_st->l2.tei = 0;
+						HiSax_putstatus(csta, "set card ", "in PTP mode");
+						printk(KERN_DEBUG "HiSax: set card in PTP mode\n");
+						printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
+						csta->channel[0].d_st->lli.l4l3(csta->channel[0].d_st,
+							DL_ESTABLISH | REQUEST, NULL);
+					} else {
+						test_and_clear_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
+						test_and_clear_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
+						HiSax_putstatus(csta, "set card ", "in PTMP mode");
+						printk(KERN_DEBUG "HiSax: set card in PTMP mode\n");
+					}
+					break;
+				case (8):	/* set card in FIXED TEI mode */
+					num = *(unsigned int *) ic->parm.num;
+					chanp = csta->channel + (num & 1);
+					num = num >>1;
+					if (num == 127) {
+						test_and_clear_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag);
+						chanp->d_st->l2.tei = -1;
+						HiSax_putstatus(csta, "set card ", "in VAR TEI mode");
+						printk(KERN_DEBUG "HiSax: set card in VAR TEI mode\n");
+					} else {
+						test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag);
+						chanp->d_st->l2.tei = num;
+						HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num);
+						printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n",
+							num);
+					}
+					chanp->d_st->lli.l4l3(chanp->d_st,
+						DL_ESTABLISH | REQUEST, NULL);
+					break;
+				case (11):
+					num = csta->debug & DEB_DLOG_HEX;
+					csta->debug = *(unsigned int *) ic->parm.num;
+					csta->debug |= num;
+					HiSax_putstatus(cards[0].cs, "l1 debugging ",
+						"flags card %d set to %x",
+						csta->cardnr + 1, csta->debug);
+					printk(KERN_DEBUG "HiSax: l1 debugging flags card %d set to %x\n",
+						csta->cardnr + 1, csta->debug);
+					break;
+				case (13):
+					csta->channel[0].d_st->l3.debug = *(unsigned int *) ic->parm.num;
+					csta->channel[1].d_st->l3.debug = *(unsigned int *) ic->parm.num;
+					HiSax_putstatus(cards[0].cs, "l3 debugging ",
+						"flags card %d set to %x\n", csta->cardnr + 1,
+						*(unsigned int *) ic->parm.num);
+					printk(KERN_DEBUG "HiSax: l3 debugging flags card %d set to %x\n",
+						csta->cardnr + 1, *(unsigned int *) ic->parm.num);
+					break;
+				case (10):
+					i = *(unsigned int *) ic->parm.num;
+					return(set_channel_limit(csta, i));
+				default:
+					if (csta->auxcmd)
+						return(csta->auxcmd(csta, ic));
+					printk(KERN_DEBUG "HiSax: invalid ioclt %d\n",
+						(int) ic->arg);
+					return (-EINVAL);
+			}
+			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));
+			FsmEvent(&chanp->fi, EV_REDIR, NULL);
+			break;
+
+		/* protocol specific io commands */
+		case (ISDN_CMD_PROT_IO):
+			for (st = csta->stlist; st; st = st->next)
+				if (st->protocol == (ic->arg & 0xFF))
+					return(st->lli.l4l3_proto(st, ic));
+			return(-EINVAL);
+			break;
+		default:
+			if (csta->auxcmd)
+				return(csta->auxcmd(csta, ic));
+			return(-EINVAL);
+	}
+	return (0);
+}
+
+int
+HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb)
+{
+	struct IsdnCardState *csta = hisax_findcard(id);
+	struct Channel *chanp;
+	struct PStack *st;
+	int len = skb->len;
+	struct sk_buff *nskb;
+
+	if (!csta) {
+		printk(KERN_ERR
+			"HiSax: if_sendbuf called with invalid driverId!\n");
+		return -ENODEV;
+	}
+	chanp = csta->channel + chan;
+	st = chanp->b_st;
+	if (!chanp->data_open) {
+		link_debug(chanp, 1, "writebuf: channel not open");
+		return -EIO;
+	}
+	if (len > MAX_DATA_SIZE) {
+		link_debug(chanp, 1, "writebuf: packet too large (%d bytes)", len);
+		printk(KERN_WARNING "HiSax_writebuf: packet too large (%d bytes) !\n",
+			len);
+		return -EINVAL;
+	}
+	if (len) {
+		if ((len + chanp->bcs->tx_cnt) > MAX_DATA_MEM) {
+			/* Must return 0 here, since this is not an error
+			 * but a temporary lack of resources.
+			 */
+			if (chanp->debug & 0x800)
+				link_debug(chanp, 1, "writebuf: no buffers for %d bytes", len);
+			return 0;
+		} else if (chanp->debug & 0x800)
+			link_debug(chanp, 1, "writebuf %d/%d/%d", len, chanp->bcs->tx_cnt,MAX_DATA_MEM);
+		nskb = skb_clone(skb, GFP_ATOMIC);
+		if (nskb) {
+			nskb->truesize = nskb->len;
+			if (!ack)
+				nskb->pkt_type = PACKET_NOACK;
+			if (chanp->l2_active_protocol == ISDN_PROTO_L2_X75I)
+				st->l3.l3l2(st, DL_DATA | REQUEST, nskb);
+			else {
+				chanp->bcs->tx_cnt += len;
+				st->l2.l2l1(st, PH_DATA | REQUEST, nskb);
+			}
+			dev_kfree_skb(skb);
+		} else
+			len = 0;
+	}
+	return (len);
+}
diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c
new file mode 100644
index 000000000000..1663ee69d41d
--- /dev/null
+++ b/drivers/isdn/hisax/config.c
@@ -0,0 +1,1958 @@
+/* $Id: config.c,v 2.84.2.5 2004/02/11 13:21:33 keil Exp $
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *              by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * based on the teles driver from Jan den Ouden
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include <linux/module.h>
+#include <linux/kernel_stat.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#define HISAX_STATUS_BUFSIZE 4096
+#define INCLUDE_INLINE_FUNCS
+
+/*
+ * This structure array contains one entry per card. An entry looks
+ * like this:
+ *
+ * { type, protocol, p0, p1, p2, NULL }
+ *
+ * type
+ *    1 Teles 16.0       p0=irq p1=membase p2=iobase
+ *    2 Teles  8.0       p0=irq p1=membase
+ *    3 Teles 16.3       p0=irq p1=iobase
+ *    4 Creatix PNP      p0=irq p1=IO0 (ISAC)  p2=IO1 (HSCX)
+ *    5 AVM A1 (Fritz)   p0=irq p1=iobase
+ *    6 ELSA PC          [p0=iobase] or nothing (autodetect)
+ *    7 ELSA Quickstep   p0=irq p1=iobase
+ *    8 Teles PCMCIA     p0=irq p1=iobase
+ *    9 ITK ix1-micro    p0=irq p1=iobase
+ *   10 ELSA PCMCIA      p0=irq p1=iobase
+ *   11 Eicon.Diehl Diva p0=irq p1=iobase
+ *   12 Asuscom ISDNLink p0=irq p1=iobase
+ *   13 Teleint          p0=irq p1=iobase
+ *   14 Teles 16.3c      p0=irq p1=iobase
+ *   15 Sedlbauer speed  p0=irq p1=iobase
+ *   15 Sedlbauer PC/104	p0=irq p1=iobase
+ *   15 Sedlbauer speed pci	no parameter
+ *   16 USR Sportster internal  p0=irq  p1=iobase
+ *   17 MIC card                p0=irq  p1=iobase
+ *   18 ELSA Quickstep 1000PCI  no parameter
+ *   19 Compaq ISDN S0 ISA card p0=irq  p1=IO0 (HSCX)  p2=IO1 (ISAC) p3=IO2
+ *   20 Travers Technologies NETjet-S PCI card
+ *   21 TELES PCI               no parameter
+ *   22 Sedlbauer Speed Star    p0=irq p1=iobase
+ *   23 reserved
+ *   24 Dr Neuhaus Niccy PnP/PCI card p0=irq p1=IO0 p2=IO1 (PnP only)
+ *   25 Teles S0Box             p0=irq p1=iobase (from isapnp setup)
+ *   26 AVM A1 PCMCIA (Fritz)   p0=irq p1=iobase
+ *   27 AVM PnP/PCI 		p0=irq p1=iobase (PCI no parameter)
+ *   28 Sedlbauer Speed Fax+ 	p0=irq p1=iobase (from isapnp setup)
+ *   29 Siemens I-Surf          p0=irq p1=iobase p2=memory (from isapnp setup)
+ *   30 ACER P10                p0=irq p1=iobase (from isapnp setup)
+ *   31 HST Saphir              p0=irq  p1=iobase
+ *   32 Telekom A4T             none
+ *   33 Scitel Quadro		p0=subcontroller (4*S0, subctrl 1...4)
+ *   34	Gazel ISDN cards
+ *   35 HFC 2BDS0 PCI           none
+ *   36 Winbond 6692 PCI        none
+ *   37 HFC 2BDS0 S+/SP         p0=irq p1=iobase
+ *   38 Travers Technologies NETspider-U PCI card
+ *   39 HFC 2BDS0-SP PCMCIA     p0=irq p1=iobase
+ *   40 hotplug interface
+ *   41 Formula-n enter:now ISDN PCI a/b   none
+ *
+ * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1
+ *
+ *
+ */
+
+const char *CardType[] = {
+	"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3",
+	"Creatix/Teles PnP", "AVM A1", "Elsa ML", "Elsa Quickstep",
+	"Teles PCMCIA",	"ITK ix1-micro Rev.2", "Elsa PCMCIA",
+	"Eicon.Diehl Diva", "ISDNLink",	"TeleInt", "Teles 16.3c",
+	"Sedlbauer Speed Card", "USR Sportster", "ith mic Linux",
+	"Elsa PCI", "Compaq ISA", "NETjet-S", "Teles PCI", 
+	"Sedlbauer Speed Star (PCMCIA)", "AMD 7930", "NICCY", "S0Box",
+	"AVM A1 (PCMCIA)", "AVM Fritz PnP/PCI", "Sedlbauer Speed Fax +",
+	"Siemens I-Surf", "Acer P10", "HST Saphir", "Telekom A4T",
+	"Scitel Quadro", "Gazel", "HFC 2BDS0 PCI", "Winbond 6692",
+	"HFC 2BDS0 SX", "NETspider-U", "HFC-2BDS0-SP PCMCIA",
+	"Hotplug", "Formula-n enter:now PCI a/b", 
+};
+
+#ifdef CONFIG_HISAX_ELSA
+#define DEFAULT_CARD ISDN_CTYPE_ELSA
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_AVM_A1
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_A1
+#define DEFAULT_CFG {10,0x340,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_A1_PCMCIA
+#define DEFAULT_CFG {11,0x170,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_FRITZPCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_FRITZPCI
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_16_3
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_16_3
+#define DEFAULT_CFG {15,0x180,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_S0BOX
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_S0BOX
+#define DEFAULT_CFG {7,0x378,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_16_0
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_16_0
+#define DEFAULT_CFG {15,0xd0000,0xd80,0}
+#endif
+
+#ifdef CONFIG_HISAX_TELESPCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELESPCI
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_IX1MICROR2
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_IX1MICROR2
+#define DEFAULT_CFG {5,0x390,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_DIEHLDIVA
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_DIEHLDIVA
+#define DEFAULT_CFG {0,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_ASUSCOM
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_ASUSCOM
+#define DEFAULT_CFG {5,0x200,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_TELEINT
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELEINT
+#define DEFAULT_CFG {5,0x300,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_SEDLBAUER
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_SEDLBAUER
+#define DEFAULT_CFG {11,0x270,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_SPORTSTER
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_SPORTSTER
+#define DEFAULT_CFG {7,0x268,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_MIC
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_MIC
+#define DEFAULT_CFG {12,0x3e0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_NETJET
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NETJET_S
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_HFCS
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELES3C
+#define DEFAULT_CFG {5,0x500,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_HFC_PCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_HFC_PCI
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_HFC_SX
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_HFC_SX
+#define DEFAULT_CFG {5,0x2E0,0,0}
+#endif
+
+
+#ifdef CONFIG_HISAX_AMD7930
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_AMD7930
+#define DEFAULT_CFG {12,0x3e0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_NICCY
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NICCY
+#define DEFAULT_CFG {0,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_ISURF
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_ISURF
+#define DEFAULT_CFG {5,0x100,0xc8000,0}
+#endif
+
+#ifdef CONFIG_HISAX_HSTSAPHIR
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_HSTSAPHIR
+#define DEFAULT_CFG {5,0x250,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_BKM_A4T
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_BKM_A4T
+#define DEFAULT_CFG {0,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_SCT_QUADRO
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_SCT_QUADRO
+#define DEFAULT_CFG {1,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_GAZEL
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_GAZEL
+#define DEFAULT_CFG {15,0x180,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_W6692
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_W6692
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_NETJET_U
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NETJET_U
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_1TR6
+#define DEFAULT_PROTO ISDN_PTYPE_1TR6
+#define DEFAULT_PROTO_NAME "1TR6"
+#endif
+#ifdef CONFIG_HISAX_NI1
+#undef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_NI1
+#undef DEFAULT_PROTO_NAME
+#define DEFAULT_PROTO_NAME "NI1"
+#endif
+#ifdef CONFIG_HISAX_EURO
+#undef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_EURO
+#undef DEFAULT_PROTO_NAME
+#define DEFAULT_PROTO_NAME "EURO"
+#endif
+#ifndef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_UNKNOWN
+#define DEFAULT_PROTO_NAME "UNKNOWN"
+#endif
+#ifndef DEFAULT_CARD
+#define DEFAULT_CARD 0
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#define FIRST_CARD { \
+	DEFAULT_CARD, \
+	DEFAULT_PROTO, \
+	DEFAULT_CFG, \
+	NULL, \
+}
+
+struct IsdnCard cards[HISAX_MAX_CARDS] = {
+	FIRST_CARD,
+};
+
+#define HISAX_IDSIZE (HISAX_MAX_CARDS*8)
+static char HiSaxID[HISAX_IDSIZE] = { 0, };
+
+char *HiSax_id = HiSaxID;
+#ifdef MODULE
+/* Variables for insmod */
+static int type[HISAX_MAX_CARDS] = { 0, };
+static int protocol[HISAX_MAX_CARDS] = { 0, };
+static int io[HISAX_MAX_CARDS] = { 0, };
+#undef IO0_IO1
+#ifdef CONFIG_HISAX_16_3
+#define IO0_IO1
+#endif
+#ifdef CONFIG_HISAX_NICCY
+#undef IO0_IO1
+#define IO0_IO1
+#endif
+#ifdef IO0_IO1
+static int io0[HISAX_MAX_CARDS] __devinitdata = { 0, };
+static int io1[HISAX_MAX_CARDS] __devinitdata = { 0, };
+#endif
+static int irq[HISAX_MAX_CARDS] __devinitdata = { 0, };
+static int mem[HISAX_MAX_CARDS] __devinitdata = { 0, };
+static char *id = HiSaxID;
+
+MODULE_DESCRIPTION("ISDN4Linux: Driver for passive ISDN cards");
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL");
+module_param_array(type, int, NULL, 0);
+module_param_array(protocol, int, NULL, 0);
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+module_param(id, charp, 0);
+#ifdef IO0_IO1
+module_param_array(io0, int, NULL, 0);
+module_param_array(io1, int, NULL, 0);
+#endif
+#endif /* MODULE */
+
+int nrcards;
+
+extern char *l1_revision;
+extern char *l2_revision;
+extern char *l3_revision;
+extern char *lli_revision;
+extern char *tei_revision;
+
+char *HiSax_getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "???";
+	return rev;
+}
+
+void __init HiSaxVersion(void)
+{
+	char tmp[64];
+
+	printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n");
+#ifdef MODULE
+	printk(KERN_INFO "HiSax: Version 3.5 (module)\n");
+#else
+	printk(KERN_INFO "HiSax: Version 3.5 (kernel)\n");
+#endif
+	strcpy(tmp, l1_revision);
+	printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp));
+	strcpy(tmp, l2_revision);
+	printk(KERN_INFO "HiSax: Layer2 Revision %s\n", HiSax_getrev(tmp));
+	strcpy(tmp, tei_revision);
+	printk(KERN_INFO "HiSax: TeiMgr Revision %s\n", HiSax_getrev(tmp));
+	strcpy(tmp, l3_revision);
+	printk(KERN_INFO "HiSax: Layer3 Revision %s\n", HiSax_getrev(tmp));
+	strcpy(tmp, lli_revision);
+	printk(KERN_INFO "HiSax: LinkLayer Revision %s\n",
+	       HiSax_getrev(tmp));
+}
+
+#ifndef MODULE
+#define MAX_ARG	(HISAX_MAX_CARDS*5)
+static int __init HiSax_setup(char *line)
+{
+	int i, j, argc;
+	int ints[MAX_ARG + 1];
+	char *str;
+
+	str = get_options(line, MAX_ARG, ints);
+	argc = ints[0];
+	printk(KERN_DEBUG "HiSax_setup: argc(%d) str(%s)\n", argc, str);
+	i = 0;
+	j = 1;
+	while (argc && (i < HISAX_MAX_CARDS)) {
+		cards[i].protocol = DEFAULT_PROTO;
+		if (argc) {
+			cards[i].typ = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].protocol = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].para[0] = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].para[1] = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].para[2] = ints[j];
+			j++;
+			argc--;
+		}
+		i++;
+	}
+  	if (str && *str) {
+		if (strlen(str) < HISAX_IDSIZE)
+			strcpy(HiSaxID, str);
+		else
+			printk(KERN_WARNING "HiSax: ID too long!");
+	} else
+		strcpy(HiSaxID, "HiSax");
+
+	HiSax_id = HiSaxID;
+	return 1;
+}
+
+__setup("hisax=", HiSax_setup);
+#endif /* MODULES */
+
+#if CARD_TELES0
+extern int setup_teles0(struct IsdnCard *card);
+#endif
+
+#if CARD_TELES3
+extern int setup_teles3(struct IsdnCard *card);
+#endif
+
+#if CARD_S0BOX
+extern int setup_s0box(struct IsdnCard *card);
+#endif
+
+#if CARD_TELESPCI
+extern int setup_telespci(struct IsdnCard *card);
+#endif
+
+#if CARD_AVM_A1
+extern int setup_avm_a1(struct IsdnCard *card);
+#endif
+
+#if CARD_AVM_A1_PCMCIA
+extern int setup_avm_a1_pcmcia(struct IsdnCard *card);
+#endif
+
+#if CARD_FRITZPCI
+extern int setup_avm_pcipnp(struct IsdnCard *card);
+#endif
+
+#if CARD_ELSA
+extern int setup_elsa(struct IsdnCard *card);
+#endif
+
+#if CARD_IX1MICROR2
+extern int setup_ix1micro(struct IsdnCard *card);
+#endif
+
+#if CARD_DIEHLDIVA
+extern int setup_diva(struct IsdnCard *card);
+#endif
+
+#if CARD_ASUSCOM
+extern int setup_asuscom(struct IsdnCard *card);
+#endif
+
+#if CARD_TELEINT
+extern int setup_TeleInt(struct IsdnCard *card);
+#endif
+
+#if CARD_SEDLBAUER
+extern int setup_sedlbauer(struct IsdnCard *card);
+#endif
+
+#if CARD_SPORTSTER
+extern int setup_sportster(struct IsdnCard *card);
+#endif
+
+#if CARD_MIC
+extern int setup_mic(struct IsdnCard *card);
+#endif
+
+#if CARD_NETJET_S
+extern int setup_netjet_s(struct IsdnCard *card);
+#endif
+
+#if CARD_HFCS
+extern int setup_hfcs(struct IsdnCard *card);
+#endif
+
+#if CARD_HFC_PCI
+extern int setup_hfcpci(struct IsdnCard *card);
+#endif
+
+#if CARD_HFC_SX
+extern int setup_hfcsx(struct IsdnCard *card);
+#endif
+
+#if CARD_AMD7930
+extern int setup_amd7930(struct IsdnCard *card);
+#endif
+
+#if CARD_NICCY
+extern int setup_niccy(struct IsdnCard *card);
+#endif
+
+#if CARD_ISURF
+extern int setup_isurf(struct IsdnCard *card);
+#endif
+
+#if CARD_HSTSAPHIR
+extern int setup_saphir(struct IsdnCard *card);
+#endif
+
+#if CARD_TESTEMU
+extern int setup_testemu(struct IsdnCard *card);
+#endif
+
+#if CARD_BKM_A4T
+extern int setup_bkm_a4t(struct IsdnCard *card);
+#endif
+
+#if CARD_SCT_QUADRO
+extern int setup_sct_quadro(struct IsdnCard *card);
+#endif
+
+#if CARD_GAZEL
+extern int setup_gazel(struct IsdnCard *card);
+#endif
+
+#if CARD_W6692
+extern int setup_w6692(struct IsdnCard *card);
+#endif
+
+#if CARD_NETJET_U
+extern int setup_netjet_u(struct IsdnCard *card);
+#endif
+
+#if CARD_FN_ENTERNOW_PCI
+extern int setup_enternow_pci(struct IsdnCard *card);
+#endif
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState *hisax_findcard(int driverid)
+{
+	int i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].cs)
+			if (cards[i].cs->myid == driverid)
+				return cards[i].cs;
+	return NULL;
+}
+
+/*
+ * Find card with given card number
+ */
+struct IsdnCardState *hisax_get_card(int cardnr)
+{
+	if ((cardnr <= nrcards) && (cardnr > 0))
+		if (cards[cardnr - 1].cs)
+			return cards[cardnr - 1].cs;
+	return NULL;
+}
+
+int HiSax_readstatus(u_char __user *buf, int len, int id, int channel)
+{
+	int count, cnt;
+	u_char __user *p = buf;
+	struct IsdnCardState *cs = hisax_findcard(id);
+
+	if (cs) {
+		if (len > HISAX_STATUS_BUFSIZE) {
+			printk(KERN_WARNING
+			       "HiSax: status overflow readstat %d/%d\n",
+			       len, HISAX_STATUS_BUFSIZE);
+		}
+		count = cs->status_end - cs->status_read + 1;
+		if (count >= len)
+			count = len;
+		copy_to_user(p, cs->status_read, count);
+		cs->status_read += count;
+		if (cs->status_read > cs->status_end)
+			cs->status_read = cs->status_buf;
+		p += count;
+		count = len - count;
+		while (count) {
+			if (count > HISAX_STATUS_BUFSIZE)
+				cnt = HISAX_STATUS_BUFSIZE;
+			else
+				cnt = count;
+			copy_to_user(p, cs->status_read, cnt);
+			p += cnt;
+			cs->status_read += cnt % HISAX_STATUS_BUFSIZE;
+			count -= cnt;
+		}
+		return len;
+	} else {
+		printk(KERN_ERR
+		       "HiSax: if_readstatus called with invalid driverId!\n");
+		return -ENODEV;
+	}
+}
+
+int jiftime(char *s, long mark)
+{
+	s += 8;
+
+	*s-- = '\0';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = '.';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 6 + '0';
+	mark /= 6;
+	*s-- = ':';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 10 + '0';
+	return 8;
+}
+
+static u_char tmpbuf[HISAX_STATUS_BUFSIZE];
+
+void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt,
+		      va_list args)
+{
+	/* if head == NULL the fmt contains the full info */
+
+	u_long		flags;
+	int		count, i;
+	u_char		*p;
+	isdn_ctrl	ic;
+	int		len;
+
+	if (!cs) {
+		printk(KERN_WARNING "HiSax: No CardStatus for message");
+		return;
+	}
+	spin_lock_irqsave(&cs->statlock, flags);
+	p = tmpbuf;
+	if (head) {
+		p += jiftime(p, jiffies);
+		p += sprintf(p, " %s", head);
+		p += vsprintf(p, fmt, args);
+		*p++ = '\n';
+		*p = 0;
+		len = p - tmpbuf;
+		p = tmpbuf;
+	} else {
+		p = fmt;
+		len = strlen(fmt);
+	}
+	if (len > HISAX_STATUS_BUFSIZE) {
+		spin_unlock_irqrestore(&cs->statlock, flags);
+		printk(KERN_WARNING "HiSax: status overflow %d/%d\n",
+		       len, HISAX_STATUS_BUFSIZE);
+		return;
+	}
+	count = len;
+	i = cs->status_end - cs->status_write + 1;
+	if (i >= len)
+		i = len;
+	len -= i;
+	memcpy(cs->status_write, p, i);
+	cs->status_write += i;
+	if (cs->status_write > cs->status_end)
+		cs->status_write = cs->status_buf;
+	p += i;
+	if (len) {
+		memcpy(cs->status_write, p, len);
+		cs->status_write += len;
+	}
+#ifdef KERNELSTACK_DEBUG
+	i = (ulong) & len - current->kernel_stack_page;
+	sprintf(tmpbuf, "kstack %s %lx use %ld\n", current->comm,
+		current->kernel_stack_page, i);
+	len = strlen(tmpbuf);
+	for (p = tmpbuf, i = len; i > 0; i--, p++) {
+		*cs->status_write++ = *p;
+		if (cs->status_write > cs->status_end)
+			cs->status_write = cs->status_buf;
+		count++;
+	}
+#endif
+	spin_unlock_irqrestore(&cs->statlock, flags);
+	if (count) {
+		ic.command = ISDN_STAT_STAVAIL;
+		ic.driver = cs->myid;
+		ic.arg = count;
+		cs->iif.statcallb(&ic);
+	}
+}
+
+void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	VHiSax_putstatus(cs, head, fmt, args);
+	va_end(args);
+}
+
+int ll_run(struct IsdnCardState *cs, int addfeatures)
+{
+	isdn_ctrl ic;
+
+	ic.driver = cs->myid;
+	ic.command = ISDN_STAT_RUN;
+	cs->iif.features |= addfeatures;
+	cs->iif.statcallb(&ic);
+	return 0;
+}
+
+void ll_stop(struct IsdnCardState *cs)
+{
+	isdn_ctrl ic;
+
+	ic.command = ISDN_STAT_STOP;
+	ic.driver = cs->myid;
+	cs->iif.statcallb(&ic);
+	//      CallcFreeChan(cs);
+}
+
+static void ll_unload(struct IsdnCardState *cs)
+{
+	isdn_ctrl ic;
+
+	ic.command = ISDN_STAT_UNLOAD;
+	ic.driver = cs->myid;
+	cs->iif.statcallb(&ic);
+	if (cs->status_buf)
+		kfree(cs->status_buf);
+	cs->status_read = NULL;
+	cs->status_write = NULL;
+	cs->status_end = NULL;
+	kfree(cs->dlog);
+	cs->dlog = NULL;
+}
+
+static void closecard(int cardnr)
+{
+	struct IsdnCardState *csta = cards[cardnr].cs;
+
+	if (csta->bcs->BC_Close != NULL) {
+		csta->bcs->BC_Close(csta->bcs + 1);
+		csta->bcs->BC_Close(csta->bcs);
+	}
+
+	skb_queue_purge(&csta->rq);
+	skb_queue_purge(&csta->sq);
+	if (csta->rcvbuf) {
+		kfree(csta->rcvbuf);
+		csta->rcvbuf = NULL;
+	}
+	if (csta->tx_skb) {
+		dev_kfree_skb(csta->tx_skb);
+		csta->tx_skb = NULL;
+	}
+	if (csta->DC_Close != NULL) {
+		csta->DC_Close(csta);
+	}
+	if (csta->cardmsg)
+		csta->cardmsg(csta, CARD_RELEASE, NULL);
+	if (csta->dbusytimer.function != NULL) // FIXME?
+		del_timer(&csta->dbusytimer);
+	ll_unload(csta);
+}
+
+static int init_card(struct IsdnCardState *cs)
+{
+	int 	irq_cnt, cnt = 3, ret;
+
+	if (!cs->irq) {
+		ret = cs->cardmsg(cs, CARD_INIT, NULL);
+		return(ret);
+	}
+	irq_cnt = kstat_irqs(cs->irq);
+	printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ],
+	       cs->irq, irq_cnt);
+	if (request_irq(cs->irq, cs->irq_func, cs->irq_flags, "HiSax", cs)) {
+		printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
+		       cs->irq);
+		return 1;
+	}
+	while (cnt) {
+		cs->cardmsg(cs, CARD_INIT, NULL);
+		/* Timeout 10ms */
+		msleep(10);
+		printk(KERN_INFO "%s: IRQ %d count %d\n",
+		       CardType[cs->typ], cs->irq, kstat_irqs(cs->irq));
+		if (kstat_irqs(cs->irq) == irq_cnt) {
+			printk(KERN_WARNING
+			       "%s: IRQ(%d) getting no interrupts during init %d\n",
+			       CardType[cs->typ], cs->irq, 4 - cnt);
+			if (cnt == 1) {
+				free_irq(cs->irq, cs);
+				return 2;
+			} else {
+				cs->cardmsg(cs, CARD_RESET, NULL);
+				cnt--;
+			}
+		} else {
+			cs->cardmsg(cs, CARD_TEST, NULL);
+			return 0;
+		}
+	}
+	return 3;
+}
+
+static int checkcard(int cardnr, char *id, int *busy_flag, struct module *lockowner)
+{
+	int ret = 0;
+	struct IsdnCard *card = cards + cardnr;
+	struct IsdnCardState *cs;
+
+	cs = kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC);
+	if (!cs) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for IsdnCardState(card %d)\n",
+		       cardnr + 1);
+		goto out;
+	}
+	memset(cs, 0, sizeof(struct IsdnCardState));
+	card->cs = cs;
+	spin_lock_init(&cs->statlock);
+	spin_lock_init(&cs->lock);
+	cs->chanlimit = 2;	/* maximum B-channel number */
+	cs->logecho = 0;	/* No echo logging */
+	cs->cardnr = cardnr;
+	cs->debug = L1_DEB_WARN;
+	cs->HW_Flags = 0;
+	cs->busy_flag = busy_flag;
+	cs->irq_flags = I4L_IRQ_FLAG;
+#if TEI_PER_CARD
+	if (card->protocol == ISDN_PTYPE_NI1)
+		test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
+#else
+	test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
+#endif
+	cs->protocol = card->protocol;
+
+	if (card->typ <= 0 || card->typ > ISDN_CTYPE_COUNT) {
+		printk(KERN_WARNING
+		       "HiSax: Card Type %d out of range\n", card->typ);
+		goto outf_cs;
+	}
+	if (!(cs->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for dlog(card %d)\n", cardnr + 1);
+		goto outf_cs;
+	}
+	if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for status_buf(card %d)\n",
+		       cardnr + 1);
+		goto outf_dlog;
+	}
+	cs->stlist = NULL;
+	cs->status_read = cs->status_buf;
+	cs->status_write = cs->status_buf;
+	cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1;
+	cs->typ = card->typ;
+#ifdef MODULE
+	cs->iif.owner = lockowner;
+#endif
+	strcpy(cs->iif.id, id);
+	cs->iif.channels = 2;
+	cs->iif.maxbufsize = MAX_DATA_SIZE;
+	cs->iif.hl_hdrlen = MAX_HEADER_LEN;
+	cs->iif.features =
+		ISDN_FEATURE_L2_X75I |
+		ISDN_FEATURE_L2_HDLC |
+		ISDN_FEATURE_L2_HDLC_56K |
+		ISDN_FEATURE_L2_TRANS |
+		ISDN_FEATURE_L3_TRANS |
+#ifdef	CONFIG_HISAX_1TR6
+		ISDN_FEATURE_P_1TR6 |
+#endif
+#ifdef	CONFIG_HISAX_EURO
+		ISDN_FEATURE_P_EURO |
+#endif
+#ifdef	CONFIG_HISAX_NI1
+		ISDN_FEATURE_P_NI1 |
+#endif
+		0;
+
+	cs->iif.command = HiSax_command;
+	cs->iif.writecmd = NULL;
+	cs->iif.writebuf_skb = HiSax_writebuf_skb;
+	cs->iif.readstat = HiSax_readstatus;
+	register_isdn(&cs->iif);
+	cs->myid = cs->iif.channels;
+	printk(KERN_INFO
+	       "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1,
+	       (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" :
+	       (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" :
+	       (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" :
+	       (card->protocol == ISDN_PTYPE_NI1) ? "NI1" :
+	       "NONE", cs->iif.id, cs->myid);
+	switch (card->typ) {
+#if CARD_TELES0
+	case ISDN_CTYPE_16_0:
+	case ISDN_CTYPE_8_0:
+		ret = setup_teles0(card);
+		break;
+#endif
+#if CARD_TELES3
+	case ISDN_CTYPE_16_3:
+	case ISDN_CTYPE_PNP:
+	case ISDN_CTYPE_TELESPCMCIA:
+	case ISDN_CTYPE_COMPAQ_ISA:
+		ret = setup_teles3(card);
+		break;
+#endif
+#if CARD_S0BOX
+	case ISDN_CTYPE_S0BOX:
+		ret = setup_s0box(card);
+		break;
+#endif
+#if CARD_TELESPCI
+	case ISDN_CTYPE_TELESPCI:
+		ret = setup_telespci(card);
+		break;
+#endif
+#if CARD_AVM_A1
+	case ISDN_CTYPE_A1:
+		ret = setup_avm_a1(card);
+		break;
+#endif
+#if CARD_AVM_A1_PCMCIA
+	case ISDN_CTYPE_A1_PCMCIA:
+		ret = setup_avm_a1_pcmcia(card);
+		break;
+#endif
+#if CARD_FRITZPCI
+	case ISDN_CTYPE_FRITZPCI:
+		ret = setup_avm_pcipnp(card);
+		break;
+#endif
+#if CARD_ELSA
+	case ISDN_CTYPE_ELSA:
+	case ISDN_CTYPE_ELSA_PNP:
+	case ISDN_CTYPE_ELSA_PCMCIA:
+	case ISDN_CTYPE_ELSA_PCI:
+		ret = setup_elsa(card);
+		break;
+#endif
+#if CARD_IX1MICROR2
+	case ISDN_CTYPE_IX1MICROR2:
+		ret = setup_ix1micro(card);
+		break;
+#endif
+#if CARD_DIEHLDIVA
+	case ISDN_CTYPE_DIEHLDIVA:
+		ret = setup_diva(card);
+		break;
+#endif
+#if CARD_ASUSCOM
+	case ISDN_CTYPE_ASUSCOM:
+		ret = setup_asuscom(card);
+		break;
+#endif
+#if CARD_TELEINT
+	case ISDN_CTYPE_TELEINT:
+		ret = setup_TeleInt(card);
+		break;
+#endif
+#if CARD_SEDLBAUER
+	case ISDN_CTYPE_SEDLBAUER:
+	case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+	case ISDN_CTYPE_SEDLBAUER_FAX:
+		ret = setup_sedlbauer(card);
+		break;
+#endif
+#if CARD_SPORTSTER
+	case ISDN_CTYPE_SPORTSTER:
+		ret = setup_sportster(card);
+		break;
+#endif
+#if CARD_MIC
+	case ISDN_CTYPE_MIC:
+		ret = setup_mic(card);
+		break;
+#endif
+#if CARD_NETJET_S
+	case ISDN_CTYPE_NETJET_S:
+		ret = setup_netjet_s(card);
+		break;
+#endif
+#if CARD_HFCS
+	case ISDN_CTYPE_TELES3C:
+	case ISDN_CTYPE_ACERP10:
+		ret = setup_hfcs(card);
+		break;
+#endif
+#if CARD_HFC_PCI
+	case ISDN_CTYPE_HFC_PCI:
+		ret = setup_hfcpci(card);
+		break;
+#endif
+#if CARD_HFC_SX
+	case ISDN_CTYPE_HFC_SX:
+		ret = setup_hfcsx(card);
+		break;
+#endif
+#if CARD_NICCY
+	case ISDN_CTYPE_NICCY:
+		ret = setup_niccy(card);
+		break;
+#endif
+#if CARD_AMD7930
+	case ISDN_CTYPE_AMD7930:
+		ret = setup_amd7930(card);
+		break;
+#endif
+#if CARD_ISURF
+	case ISDN_CTYPE_ISURF:
+		ret = setup_isurf(card);
+		break;
+#endif
+#if CARD_HSTSAPHIR
+	case ISDN_CTYPE_HSTSAPHIR:
+		ret = setup_saphir(card);
+		break;
+#endif
+#if CARD_TESTEMU
+	case ISDN_CTYPE_TESTEMU:
+		ret = setup_testemu(card);
+		break;
+#endif
+#if	CARD_BKM_A4T
+	case ISDN_CTYPE_BKM_A4T:
+		ret = setup_bkm_a4t(card);
+		break;
+#endif
+#if	CARD_SCT_QUADRO
+	case ISDN_CTYPE_SCT_QUADRO:
+		ret = setup_sct_quadro(card);
+		break;
+#endif
+#if CARD_GAZEL
+	case ISDN_CTYPE_GAZEL:
+		ret = setup_gazel(card);
+		break;
+#endif
+#if CARD_W6692
+	case ISDN_CTYPE_W6692:
+		ret = setup_w6692(card);
+		break;
+#endif
+#if CARD_NETJET_U
+	case ISDN_CTYPE_NETJET_U:
+		ret = setup_netjet_u(card);
+		break;
+#endif
+#if CARD_FN_ENTERNOW_PCI
+	case ISDN_CTYPE_ENTERNOW:
+		ret = setup_enternow_pci(card);
+		break;
+#endif
+	case ISDN_CTYPE_DYNAMIC:
+		ret = 2;
+		break;
+	default:
+		printk(KERN_WARNING
+		       "HiSax: Support for %s Card not selected\n",
+		       CardType[card->typ]);
+		ll_unload(cs);
+		goto outf_cs;
+	}
+	if (!ret) {
+		ll_unload(cs);
+		goto outf_cs;
+	}
+	if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) {
+		printk(KERN_WARNING "HiSax: No memory for isac rcvbuf\n");
+		ll_unload(cs);
+		goto outf_cs;
+	}
+	cs->rcvidx = 0;
+	cs->tx_skb = NULL;
+	cs->tx_cnt = 0;
+	cs->event = 0;
+	cs->tqueue.data = cs;
+
+	skb_queue_head_init(&cs->rq);
+	skb_queue_head_init(&cs->sq);
+
+	init_bcstate(cs, 0);
+	init_bcstate(cs, 1);
+
+	/* init_card only handles interrupts which are not */
+	/* used here for the loadable driver */
+	switch (card->typ) {
+		case ISDN_CTYPE_DYNAMIC:
+			ret = 0;
+			break;
+		default:
+			ret = init_card(cs);
+			break;
+	}
+	if (ret) {
+		closecard(cardnr);
+		ret = 0;
+		goto outf_cs;
+	}
+	init_tei(cs, cs->protocol);
+	ret = CallcNewChan(cs);
+	if (ret) {
+		closecard(cardnr);
+		ret = 0;
+		goto outf_cs;
+	}
+	/* ISAR needs firmware download first */
+	if (!test_bit(HW_ISAR, &cs->HW_Flags))
+		ll_run(cs, 0);
+
+	ret = 1;
+	goto out;
+
+ outf_dlog:
+	kfree(cs->dlog);
+ outf_cs:
+	kfree(cs);
+	card->cs = NULL;
+ out:
+	return ret;
+}
+
+void HiSax_shiftcards(int idx)
+{
+	int i;
+
+	for (i = idx; i < (HISAX_MAX_CARDS - 1); i++)
+		memcpy(&cards[i], &cards[i + 1], sizeof(cards[i]));
+}
+
+int HiSax_inithardware(int *busy_flag)
+{
+	int foundcards = 0;
+	int i = 0;
+	int t = ',';
+	int flg = 0;
+	char *id;
+	char *next_id = HiSax_id;
+	char ids[20];
+
+	if (strchr(HiSax_id, ','))
+		t = ',';
+	else if (strchr(HiSax_id, '%'))
+		t = '%';
+
+	while (i < nrcards) {
+		if (cards[i].typ < 1)
+			break;
+		id = next_id;
+		if ((next_id = strchr(id, t))) {
+			*next_id++ = 0;
+			strcpy(ids, id);
+			flg = i + 1;
+		} else {
+			next_id = id;
+			if (flg >= i)
+				strcpy(ids, id);
+			else
+				sprintf(ids, "%s%d", id, i);
+		}
+		if (checkcard(i, ids, busy_flag, THIS_MODULE)) {
+			foundcards++;
+			i++;
+		} else {
+			/* make sure we don't oops the module */
+			if (cards[i].typ > 0 && cards[i].typ <= ISDN_CTYPE_COUNT) {
+				printk(KERN_WARNING
+			       		"HiSax: Card %s not installed !\n",
+			       		CardType[cards[i].typ]);
+			}
+			HiSax_shiftcards(i);
+			nrcards--;
+		}
+	}
+	return foundcards;
+}
+
+void HiSax_closecard(int cardnr)
+{
+	int i, last = nrcards - 1;
+
+	if (cardnr > last || cardnr < 0)
+		return;
+	if (cards[cardnr].cs) {
+		ll_stop(cards[cardnr].cs);
+		release_tei(cards[cardnr].cs);
+		CallcFreeChan(cards[cardnr].cs);
+
+		closecard(cardnr);
+		if (cards[cardnr].cs->irq)
+			free_irq(cards[cardnr].cs->irq, cards[cardnr].cs);
+		kfree((void *) cards[cardnr].cs);
+		cards[cardnr].cs = NULL;
+	}
+	i = cardnr;
+	while (i <= last) {
+		cards[i] = cards[i + 1];
+		i++;
+	}
+	nrcards--;
+}
+
+void HiSax_reportcard(int cardnr, int sel)
+{
+	struct IsdnCardState *cs = cards[cardnr].cs;
+
+	printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1);
+	printk(KERN_DEBUG "HiSax: Type %s\n", CardType[cs->typ]);
+	printk(KERN_DEBUG "HiSax: debuglevel %x\n", cs->debug);
+	printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n",
+	       (ulong) & HiSax_reportcard);
+	printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs);
+	printk(KERN_DEBUG "HiSax: HW_Flags %lx bc0 flg %lx bc1 flg %lx\n",
+	       cs->HW_Flags, cs->bcs[0].Flag, cs->bcs[1].Flag);
+	printk(KERN_DEBUG "HiSax: bcs 0 mode %d ch%d\n",
+	       cs->bcs[0].mode, cs->bcs[0].channel);
+	printk(KERN_DEBUG "HiSax: bcs 1 mode %d ch%d\n",
+	       cs->bcs[1].mode, cs->bcs[1].channel);
+#ifdef ERROR_STATISTIC
+	printk(KERN_DEBUG "HiSax: dc errors(rx,crc,tx) %d,%d,%d\n",
+	       cs->err_rx, cs->err_crc, cs->err_tx);
+	printk(KERN_DEBUG
+	       "HiSax: bc0 errors(inv,rdo,crc,tx) %d,%d,%d,%d\n",
+	       cs->bcs[0].err_inv, cs->bcs[0].err_rdo, cs->bcs[0].err_crc,
+	       cs->bcs[0].err_tx);
+	printk(KERN_DEBUG
+	       "HiSax: bc1 errors(inv,rdo,crc,tx) %d,%d,%d,%d\n",
+	       cs->bcs[1].err_inv, cs->bcs[1].err_rdo, cs->bcs[1].err_crc,
+	       cs->bcs[1].err_tx);
+	if (sel == 99) {
+		cs->err_rx  = 0;
+		cs->err_crc = 0;
+		cs->err_tx  = 0;
+		cs->bcs[0].err_inv = 0;
+		cs->bcs[0].err_rdo = 0;
+		cs->bcs[0].err_crc = 0;
+		cs->bcs[0].err_tx  = 0;
+		cs->bcs[1].err_inv = 0;
+		cs->bcs[1].err_rdo = 0;
+		cs->bcs[1].err_crc = 0;
+		cs->bcs[1].err_tx  = 0;
+	}
+#endif
+}
+
+static int __init HiSax_init(void)
+{
+	int i, retval;
+#ifdef MODULE
+	int j;
+	int nzproto = 0;
+#endif
+
+	HiSaxVersion();
+	retval = CallcNew();
+	if (retval)
+		goto out;
+	retval = Isdnl3New();
+	if (retval)
+		goto out_callc;
+	retval = Isdnl2New();
+	if (retval)
+		goto out_isdnl3;
+	retval = TeiNew();
+	if (retval)
+		goto out_isdnl2;
+	retval = Isdnl1New();
+	if (retval)
+		goto out_tei;
+
+#ifdef MODULE
+	if (!type[0]) {
+		/* We 'll register drivers later, but init basic functions */
+		for (i = 0; i < HISAX_MAX_CARDS; i++)
+			cards[i].typ = 0;
+		return 0;
+	}
+#ifdef CONFIG_HISAX_ELSA
+	if (type[0] == ISDN_CTYPE_ELSA_PCMCIA) {
+		/* we have exported  and return in this case */
+		return 0;
+	}
+#endif
+#ifdef CONFIG_HISAX_SEDLBAUER
+	if (type[0] == ISDN_CTYPE_SEDLBAUER_PCMCIA) {
+		/* we have to export  and return in this case */
+		return 0;
+	}
+#endif
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+	if (type[0] == ISDN_CTYPE_A1_PCMCIA) {
+		/* we have to export  and return in this case */
+		return 0;
+	}
+#endif
+#ifdef CONFIG_HISAX_HFC_SX
+	if (type[0] == ISDN_CTYPE_HFC_SP_PCMCIA) {
+		/* we have to export  and return in this case */
+		return 0;
+	}
+#endif
+#endif
+	nrcards = 0;
+#ifdef MODULE
+	if (id)			/* If id= string used */
+		HiSax_id = id;
+	for (i = j = 0; j < HISAX_MAX_CARDS; i++) {
+		cards[j].typ = type[i];
+		if (protocol[i]) {
+			cards[j].protocol = protocol[i];
+			nzproto++;
+		} else {
+			cards[j].protocol = DEFAULT_PROTO;
+		}
+		switch (type[i]) {
+		case ISDN_CTYPE_16_0:
+			cards[j].para[0] = irq[i];
+			cards[j].para[1] = mem[i];
+			cards[j].para[2] = io[i];
+			break;
+
+		case ISDN_CTYPE_8_0:
+			cards[j].para[0] = irq[i];
+			cards[j].para[1] = mem[i];
+			break;
+
+#ifdef IO0_IO1
+		case ISDN_CTYPE_PNP:
+		case ISDN_CTYPE_NICCY:
+			cards[j].para[0] = irq[i];
+			cards[j].para[1] = io0[i];
+			cards[j].para[2] = io1[i];
+			break;
+		case ISDN_CTYPE_COMPAQ_ISA:
+			cards[j].para[0] = irq[i];
+			cards[j].para[1] = io0[i];
+			cards[j].para[2] = io1[i];
+			cards[j].para[3] = io[i];
+			break;
+#endif
+		case ISDN_CTYPE_ELSA:
+		case ISDN_CTYPE_HFC_PCI:
+			cards[j].para[0] = io[i];
+			break;
+		case ISDN_CTYPE_16_3:
+		case ISDN_CTYPE_TELESPCMCIA:
+		case ISDN_CTYPE_A1:
+		case ISDN_CTYPE_A1_PCMCIA:
+		case ISDN_CTYPE_ELSA_PNP:
+		case ISDN_CTYPE_ELSA_PCMCIA:
+		case ISDN_CTYPE_IX1MICROR2:
+		case ISDN_CTYPE_DIEHLDIVA:
+		case ISDN_CTYPE_ASUSCOM:
+		case ISDN_CTYPE_TELEINT:
+		case ISDN_CTYPE_SEDLBAUER:
+		case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+		case ISDN_CTYPE_SEDLBAUER_FAX:
+		case ISDN_CTYPE_SPORTSTER:
+		case ISDN_CTYPE_MIC:
+		case ISDN_CTYPE_TELES3C:
+		case ISDN_CTYPE_ACERP10:
+		case ISDN_CTYPE_S0BOX:
+		case ISDN_CTYPE_FRITZPCI:
+		case ISDN_CTYPE_HSTSAPHIR:
+		case ISDN_CTYPE_GAZEL:
+		case ISDN_CTYPE_HFC_SX:
+		case ISDN_CTYPE_HFC_SP_PCMCIA:
+			cards[j].para[0] = irq[i];
+			cards[j].para[1] = io[i];
+			break;
+		case ISDN_CTYPE_ISURF:
+			cards[j].para[0] = irq[i];
+			cards[j].para[1] = io[i];
+			cards[j].para[2] = mem[i];
+			break;
+		case ISDN_CTYPE_ELSA_PCI:
+		case ISDN_CTYPE_NETJET_S:
+		case ISDN_CTYPE_AMD7930:
+		case ISDN_CTYPE_TELESPCI:
+		case ISDN_CTYPE_W6692:
+		case ISDN_CTYPE_NETJET_U:
+			break;
+		case ISDN_CTYPE_BKM_A4T:
+			break;
+		case ISDN_CTYPE_SCT_QUADRO:
+			if (irq[i]) {
+				cards[j].para[0] = irq[i];
+			} else {
+				/* QUADRO is a 4 BRI card */
+				cards[j++].para[0] = 1;
+				/* we need to check if further cards can be added */
+				if (j < HISAX_MAX_CARDS) {
+					cards[j].typ = ISDN_CTYPE_SCT_QUADRO;
+					cards[j].protocol = protocol[i];
+					cards[j++].para[0] = 2;
+				}
+				if (j < HISAX_MAX_CARDS) {
+					cards[j].typ = ISDN_CTYPE_SCT_QUADRO;
+					cards[j].protocol = protocol[i];
+					cards[j++].para[0] = 3;
+				}
+				if (j < HISAX_MAX_CARDS) {
+					cards[j].typ = ISDN_CTYPE_SCT_QUADRO;
+					cards[j].protocol = protocol[i];
+					cards[j].para[0] = 4;
+				}
+			}
+			break;
+		}
+		j++;
+	}
+	if (!nzproto) {
+		printk(KERN_WARNING
+		       "HiSax: Warning - no protocol specified\n");
+		printk(KERN_WARNING "HiSax: using protocol %s\n",
+		       DEFAULT_PROTO_NAME);
+	}
+#endif
+	if (!HiSax_id)
+		HiSax_id = HiSaxID;
+	if (!HiSaxID[0])
+		strcpy(HiSaxID, "HiSax");
+	for (i = 0; i < HISAX_MAX_CARDS; i++)
+		if (cards[i].typ > 0)
+			nrcards++;
+	printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
+	       nrcards, (nrcards > 1) ? "s" : "");
+
+	/* Install only, if at least one card found */
+	if (!HiSax_inithardware(NULL))
+		return -ENODEV;
+	return 0;
+
+ out_tei:
+	TeiFree();
+ out_isdnl2:
+	Isdnl2Free();
+ out_isdnl3:
+	Isdnl3Free();
+ out_callc:
+	CallcFree();
+ out:
+	return retval;
+}
+
+static void __exit HiSax_exit(void)
+{
+	int cardnr = nrcards - 1;
+
+	while (cardnr >= 0)
+		HiSax_closecard(cardnr--);
+	Isdnl1Free();
+	TeiFree();
+	Isdnl2Free();
+	Isdnl3Free();
+	CallcFree();
+	printk(KERN_INFO "HiSax module removed\n");
+}
+
+int hisax_init_pcmcia(void *pcm_iob, int *busy_flag, struct IsdnCard *card)
+{
+	u_char ids[16];
+	int ret = -1;
+
+	cards[nrcards] = *card;
+	if (nrcards)
+		sprintf(ids, "HiSax%d", nrcards);
+	else
+		sprintf(ids, "HiSax");
+	if (!checkcard(nrcards, ids, busy_flag, THIS_MODULE))
+		goto error;
+
+	ret = nrcards;
+	nrcards++;
+error:
+	return ret;
+}
+
+EXPORT_SYMBOL(hisax_init_pcmcia);
+EXPORT_SYMBOL(HiSax_closecard);
+
+#include "hisax_if.h"
+
+EXPORT_SYMBOL(hisax_register);
+EXPORT_SYMBOL(hisax_unregister);
+
+static void hisax_d_l1l2(struct hisax_if *ifc, int pr, void *arg);
+static void hisax_b_l1l2(struct hisax_if *ifc, int pr, void *arg);
+static void hisax_d_l2l1(struct PStack *st, int pr, void *arg);
+static void hisax_b_l2l1(struct PStack *st, int pr, void *arg);
+static int hisax_cardmsg(struct IsdnCardState *cs, int mt, void *arg);
+static int hisax_bc_setstack(struct PStack *st, struct BCState *bcs);
+static void hisax_bc_close(struct BCState *bcs);
+static void hisax_bh(struct IsdnCardState *cs);
+static void EChannel_proc_rcv(struct hisax_d_if *d_if);
+
+int hisax_register(struct hisax_d_if *hisax_d_if, struct hisax_b_if *b_if[],
+		   char *name, int protocol)
+{
+	int i, retval;
+	char id[20];
+	struct IsdnCardState *cs;
+
+	for (i = 0; i < HISAX_MAX_CARDS; i++) {
+		if (!cards[i].typ)
+			break;
+	}
+
+	if (i >= HISAX_MAX_CARDS)
+		return -EBUSY;
+
+	cards[i].typ = ISDN_CTYPE_DYNAMIC;
+	cards[i].protocol = protocol;
+	sprintf(id, "%s%d", name, i);
+	nrcards++;
+	retval = checkcard(i, id, NULL, hisax_d_if->owner);
+	if (retval == 0) { // yuck
+		cards[i].typ = 0;
+		nrcards--;
+		return retval;
+	}
+	cs = cards[i].cs;
+	hisax_d_if->cs = cs;
+	cs->hw.hisax_d_if = hisax_d_if;
+	cs->cardmsg = hisax_cardmsg;
+	INIT_WORK(&cs->tqueue, (void *)(void *)hisax_bh, cs);
+	cs->channel[0].d_st->l2.l2l1 = hisax_d_l2l1;
+	for (i = 0; i < 2; i++) {
+		cs->bcs[i].BC_SetStack = hisax_bc_setstack;
+		cs->bcs[i].BC_Close = hisax_bc_close;
+
+		b_if[i]->ifc.l1l2 = hisax_b_l1l2;
+
+		hisax_d_if->b_if[i] = b_if[i];
+	}
+	hisax_d_if->ifc.l1l2 = hisax_d_l1l2;
+	skb_queue_head_init(&hisax_d_if->erq);
+	clear_bit(0, &hisax_d_if->ph_state);
+	
+	return 0;
+}
+
+void hisax_unregister(struct hisax_d_if *hisax_d_if)
+{
+	cards[hisax_d_if->cs->cardnr].typ = 0;
+	HiSax_closecard(hisax_d_if->cs->cardnr);
+	skb_queue_purge(&hisax_d_if->erq);
+}
+
+#include "isdnl1.h"
+
+static void hisax_sched_event(struct IsdnCardState *cs, int event)
+{
+	test_and_set_bit(event, &cs->event);
+	schedule_work(&cs->tqueue);
+}
+
+static void hisax_bh(struct IsdnCardState *cs)
+{
+	struct PStack *st;
+	int pr;
+
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(E_RCVBUFREADY, &cs->event))
+		EChannel_proc_rcv(cs->hw.hisax_d_if);
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+		if (test_bit(0, &cs->hw.hisax_d_if->ph_state))
+			pr = PH_ACTIVATE | INDICATION;
+		else
+			pr = PH_DEACTIVATE | INDICATION;
+		for (st = cs->stlist; st; st = st->next)
+			st->l1.l1l2(st, pr, NULL);
+		
+	}
+}
+
+static void hisax_b_sched_event(struct BCState *bcs, int event)
+{
+	test_and_set_bit(event, &bcs->event);
+	schedule_work(&bcs->tqueue);
+}
+
+static inline void D_L2L1(struct hisax_d_if *d_if, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) d_if;
+	ifc->l2l1(ifc, pr, arg);
+}
+
+static inline void B_L2L1(struct hisax_b_if *b_if, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) b_if;
+	ifc->l2l1(ifc, pr, arg);
+}
+
+static void hisax_d_l1l2(struct hisax_if *ifc, int pr, void *arg)
+{
+	struct hisax_d_if *d_if = (struct hisax_d_if *) ifc;
+	struct IsdnCardState *cs = d_if->cs;
+	struct PStack *st;
+	struct sk_buff *skb;
+
+	switch (pr) {
+	case PH_ACTIVATE | INDICATION:
+		set_bit(0, &d_if->ph_state);
+		hisax_sched_event(cs, D_L1STATECHANGE);
+		break;
+	case PH_DEACTIVATE | INDICATION:
+		clear_bit(0, &d_if->ph_state);
+		hisax_sched_event(cs, D_L1STATECHANGE);
+		break;
+	case PH_DATA | INDICATION:
+		skb_queue_tail(&cs->rq, arg);
+		hisax_sched_event(cs, D_RCVBUFREADY);
+		break;
+	case PH_DATA | CONFIRM:
+		skb = skb_dequeue(&cs->sq);
+		if (skb) {
+			D_L2L1(d_if, PH_DATA | REQUEST, skb);
+			break;
+		}
+		clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+		for (st = cs->stlist; st; st = st->next) {
+			if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) {
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+				break;
+			}
+		}
+		break;
+	case PH_DATA_E | INDICATION:
+		skb_queue_tail(&d_if->erq, arg);
+		hisax_sched_event(cs, E_RCVBUFREADY);
+		break;
+	default:
+		printk("pr %#x\n", pr);
+		break;
+	}
+}
+
+static void hisax_b_l1l2(struct hisax_if *ifc, int pr, void *arg)
+{
+	struct hisax_b_if *b_if = (struct hisax_b_if *) ifc;
+	struct BCState *bcs = b_if->bcs;
+	struct PStack *st = bcs->st;
+	struct sk_buff *skb;
+
+	// FIXME use isdnl1?
+	switch (pr) {
+	case PH_ACTIVATE | INDICATION:
+		st->l1.l1l2(st, pr, NULL);
+		break;
+	case PH_DEACTIVATE | INDICATION:
+		st->l1.l1l2(st, pr, NULL);
+		clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		skb_queue_purge(&bcs->squeue);
+		bcs->hw.b_if = NULL;
+		break;
+	case PH_DATA | INDICATION:
+		skb_queue_tail(&bcs->rqueue, arg);
+		hisax_b_sched_event(bcs, B_RCVBUFREADY);
+		break;
+	case PH_DATA | CONFIRM:
+		bcs->tx_cnt -= (int) arg;
+		if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag)) {
+			u_long	flags;
+			spin_lock_irqsave(&bcs->aclock, flags);
+			bcs->ackcnt += (int) arg;
+			spin_unlock_irqrestore(&bcs->aclock, flags);
+			schedule_event(bcs, B_ACKPENDING);
+		}
+		skb = skb_dequeue(&bcs->squeue);
+		if (skb) {
+			B_L2L1(b_if, PH_DATA | REQUEST, skb);
+			break;
+		}
+		clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) {
+			st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+		}
+		break;
+	default:
+		printk("hisax_b_l1l2 pr %#x\n", pr);
+		break;
+	}
+}
+
+static void hisax_d_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = st->l1.hardware;
+	struct hisax_d_if *hisax_d_if = cs->hw.hisax_d_if;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+	case PH_DATA | REQUEST:
+	case PH_PULL | INDICATION:
+		if (cs->debug & DEB_DLOG_HEX)
+			LogFrame(cs, skb->data, skb->len);
+		if (cs->debug & DEB_DLOG_VERBOSE)
+			dlogframe(cs, skb, 0);
+		Logl2Frame(cs, skb, "PH_DATA_REQ", 0);
+		// FIXME lock?
+		if (!test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			D_L2L1(hisax_d_if, PH_DATA | REQUEST, skb);
+		else
+			skb_queue_tail(&cs->sq, skb);
+		break;
+	case PH_PULL | REQUEST:
+		if (!test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+		else
+			set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+		break;
+	default:
+		D_L2L1(hisax_d_if, pr, arg);
+		break;
+	}
+}
+
+static int hisax_cardmsg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	return 0;
+}
+
+static void hisax_b_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct hisax_b_if *b_if = bcs->hw.b_if;
+
+	switch (pr) {
+	case PH_ACTIVATE | REQUEST:
+		B_L2L1(b_if, pr, (void *) st->l1.mode);
+		break;
+	case PH_DATA | REQUEST:
+	case PH_PULL | INDICATION:
+		// FIXME lock?
+		if (!test_and_set_bit(BC_FLG_BUSY, &bcs->Flag)) {
+			B_L2L1(b_if, PH_DATA | REQUEST, arg);
+		} else {
+			skb_queue_tail(&bcs->squeue, arg);
+		}
+		break;
+	case PH_PULL | REQUEST:
+		if (!test_bit(BC_FLG_BUSY, &bcs->Flag))
+			st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+		else
+			set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+		break;
+	case PH_DEACTIVATE | REQUEST:
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		skb_queue_purge(&bcs->squeue);
+	default:
+		B_L2L1(b_if, pr, arg);
+		break;
+	}
+}
+
+static int hisax_bc_setstack(struct PStack *st, struct BCState *bcs)
+{
+	struct IsdnCardState *cs = st->l1.hardware;
+	struct hisax_d_if *hisax_d_if = cs->hw.hisax_d_if;
+
+	bcs->channel = st->l1.bc;
+
+	bcs->hw.b_if = hisax_d_if->b_if[st->l1.bc];
+	hisax_d_if->b_if[st->l1.bc]->bcs = bcs;
+
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hisax_b_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	skb_queue_head_init(&bcs->rqueue);
+	skb_queue_head_init(&bcs->squeue);
+	return 0;
+}
+
+static void hisax_bc_close(struct BCState *bcs)
+{
+	struct hisax_b_if *b_if = bcs->hw.b_if;
+
+	if (b_if)
+		B_L2L1(b_if, PH_DEACTIVATE | REQUEST, NULL);
+}
+
+static void EChannel_proc_rcv(struct hisax_d_if *d_if)
+{
+	struct IsdnCardState *cs = d_if->cs;
+	u_char *ptr;
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&d_if->erq)) != NULL) {
+		if (cs->debug & DEB_DLOG_HEX) {
+			ptr = cs->dlog;
+			if ((skb->len) < MAX_DLOG_SPACE / 3 - 10) {
+				*ptr++ = 'E';
+				*ptr++ = 'C';
+				*ptr++ = 'H';
+				*ptr++ = 'O';
+				*ptr++ = ':';
+				ptr += QuickHex(ptr, skb->data, skb->len);
+				ptr--;
+				*ptr++ = '\n';
+				*ptr = 0;
+				HiSax_putstatus(cs, NULL, cs->dlog);
+			} else
+				HiSax_putstatus(cs, "LogEcho: ",
+						"warning Frame too big (%d)",
+						skb->len);
+		}
+		dev_kfree_skb_any(skb);
+	}
+}
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+
+static struct pci_device_id hisax_pci_tbl[] __initdata = {
+#ifdef CONFIG_HISAX_FRITZPCI
+	{PCI_VENDOR_ID_AVM,      PCI_DEVICE_ID_AVM_A1,           PCI_ANY_ID, PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_DIEHLDIVA
+	{PCI_VENDOR_ID_EICON,    PCI_DEVICE_ID_EICON_DIVA20,     PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_EICON,    PCI_DEVICE_ID_EICON_DIVA20_U,   PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_EICON,    PCI_DEVICE_ID_EICON_DIVA201,    PCI_ANY_ID, PCI_ANY_ID},
+//#########################################################################################	
+	{PCI_VENDOR_ID_EICON,    PCI_DEVICE_ID_EICON_DIVA202,    PCI_ANY_ID, PCI_ANY_ID},
+//#########################################################################################	
+#endif
+#ifdef CONFIG_HISAX_ELSA
+	{PCI_VENDOR_ID_ELSA,     PCI_DEVICE_ID_ELSA_MICROLINK,   PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_ELSA,     PCI_DEVICE_ID_ELSA_QS3000,      PCI_ANY_ID, PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_GAZEL
+	{PCI_VENDOR_ID_PLX,      PCI_DEVICE_ID_PLX_R685,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_PLX,      PCI_DEVICE_ID_PLX_R753,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_PLX,      PCI_DEVICE_ID_PLX_DJINN_ITOO,   PCI_ANY_ID, PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_QUADRO
+	{PCI_VENDOR_ID_PLX,      PCI_DEVICE_ID_PLX_9050,         PCI_ANY_ID, PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_NICCY
+	{PCI_VENDOR_ID_SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY,   PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_SEDLBAUER
+	{PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,     PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#if defined(CONFIG_HISAX_NETJET) || defined(CONFIG_HISAX_NETJET_U)
+	{PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300,     PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#if defined(CONFIG_HISAX_TELESPCI) || defined(CONFIG_HISAX_SCT_QUADRO)
+	{PCI_VENDOR_ID_ZORAN,    PCI_DEVICE_ID_ZORAN_36120,      PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_W6692
+	{PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH,  PCI_ANY_ID,PCI_ANY_ID},
+	{PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,    PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_HFC_PCI
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_2BD0,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B000,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B006,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B007,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B008,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B009,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B00A,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B00B,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B00C,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B100,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_ABOCOM,   PCI_DEVICE_ID_ABOCOM_2BD1,      PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_ASUSTEK,  PCI_DEVICE_ID_ASUSTEK_0675,     PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_BERKOM,   PCI_DEVICE_ID_BERKOM_T_CONCEPT, PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_BERKOM,   PCI_DEVICE_ID_BERKOM_A1T,       PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_ANIGMA,   PCI_DEVICE_ID_ANIGMA_MC145575,  PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_ZOLTRIX,  PCI_DEVICE_ID_ZOLTRIX_2BD0,     PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_DIGI,     PCI_DEVICE_ID_DIGI_DF_M_IOM2_E, PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_DIGI,     PCI_DEVICE_ID_DIGI_DF_M_E,      PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_DIGI,     PCI_DEVICE_ID_DIGI_DF_M_IOM2_A, PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_DIGI,     PCI_DEVICE_ID_DIGI_DF_M_A,      PCI_ANY_ID, PCI_ANY_ID},
+#endif
+	{ }				/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, hisax_pci_tbl);
+#endif /* CONFIG_PCI */
+
+module_init(HiSax_init);
+module_exit(HiSax_exit);
+
+EXPORT_SYMBOL(FsmNew);
+EXPORT_SYMBOL(FsmFree);
+EXPORT_SYMBOL(FsmEvent);
+EXPORT_SYMBOL(FsmChangeState);
+EXPORT_SYMBOL(FsmInitTimer);
+EXPORT_SYMBOL(FsmDelTimer);
+EXPORT_SYMBOL(FsmRestartTimer);
diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c
new file mode 100644
index 000000000000..394d481e093f
--- /dev/null
+++ b/drivers/isdn/hisax/diva.c
@@ -0,0 +1,1183 @@
+/* $Id: diva.c,v 1.33.2.6 2004/02/11 13:21:33 keil Exp $
+ *
+ * low level stuff for Eicon.Diehl Diva Family ISDN cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to Eicon Technology for documents and information
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "ipac.h"
+#include "ipacx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+
+extern const char *CardType[];
+
+const char *Diva_revision = "$Revision: 1.33.2.6 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define DIVA_HSCX_DATA		0
+#define DIVA_HSCX_ADR		4
+#define DIVA_ISA_ISAC_DATA	2
+#define DIVA_ISA_ISAC_ADR	6
+#define DIVA_ISA_CTRL		7
+#define DIVA_IPAC_ADR		0
+#define DIVA_IPAC_DATA		1
+
+#define DIVA_PCI_ISAC_DATA	8
+#define DIVA_PCI_ISAC_ADR	0xc
+#define DIVA_PCI_CTRL		0x10
+
+/* SUB Types */
+#define DIVA_ISA	1
+#define DIVA_PCI	2
+#define DIVA_IPAC_ISA	3
+#define DIVA_IPAC_PCI	4
+#define DIVA_IPACX_PCI	5
+
+/* CTRL (Read) */
+#define DIVA_IRQ_STAT	0x01
+#define DIVA_EEPROM_SDA	0x02
+
+/* CTRL (Write) */
+#define DIVA_IRQ_REQ	0x01
+#define DIVA_RESET	0x08
+#define DIVA_EEPROM_CLK	0x40
+#define DIVA_PCI_LED_A	0x10
+#define DIVA_PCI_LED_B	0x20
+#define DIVA_ISA_LED_A	0x20
+#define DIVA_ISA_LED_B	0x40
+#define DIVA_IRQ_CLR	0x80
+
+/* Siemens PITA */
+#define PITA_MISC_REG		0x1c
+#ifdef __BIG_ENDIAN
+#define PITA_PARA_SOFTRESET	0x00000001
+#define PITA_SER_SOFTRESET	0x00000002
+#define PITA_PARA_MPX_MODE	0x00000004
+#define PITA_INT0_ENABLE	0x00000200
+#else
+#define PITA_PARA_SOFTRESET	0x01000000
+#define PITA_SER_SOFTRESET	0x02000000
+#define PITA_PARA_MPX_MODE	0x04000000
+#define PITA_INT0_ENABLE	0x00020000
+#endif
+#define PITA_INT0_STATUS	0x02
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+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);
+}
+
+static inline u_char
+memreadreg(unsigned long adr, u_char off)
+{
+	return(*((unsigned char *)
+		(((unsigned int *)adr) + off)));
+}
+
+static inline void
+memwritereg(unsigned long adr, u_char off, u_char data)
+{
+	register u_char *p;
+	
+	p = (unsigned char *)(((unsigned int *)adr) + off);
+	*p = data;
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return(readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset+0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return(readreg(cs->hw.diva.hscx_adr,
+		cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.diva.hscx_adr,
+		cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+static u_char
+MemReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (memreadreg(cs->hw.diva.cfg_reg, offset+0x80));
+}
+
+static void
+MemWriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	memwritereg(cs->hw.diva.cfg_reg, offset|0x80, value);
+}
+
+static void
+MemReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	while(size--)
+		*data++ = memreadreg(cs->hw.diva.cfg_reg, 0x80);
+}
+
+static void
+MemWriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	while(size--)
+		memwritereg(cs->hw.diva.cfg_reg, 0x80, *data++);
+}
+
+static u_char
+MemReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return(memreadreg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+MemWriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	memwritereg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0), value);
+}
+
+/* IO-Functions for IPACX type cards */
+static u_char
+MemReadISAC_IPACX(struct IsdnCardState *cs, u_char offset)
+{
+	return (memreadreg(cs->hw.diva.cfg_reg, offset));
+}
+
+static void
+MemWriteISAC_IPACX(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	memwritereg(cs->hw.diva.cfg_reg, offset, value);
+}
+
+static void
+MemReadISACfifo_IPACX(struct IsdnCardState *cs, u_char * data, int size)
+{
+	while(size--)
+		*data++ = memreadreg(cs->hw.diva.cfg_reg, 0);
+}
+
+static void
+MemWriteISACfifo_IPACX(struct IsdnCardState *cs, u_char * data, int size)
+{
+	while(size--)
+		memwritereg(cs->hw.diva.cfg_reg, 0, *data++);
+}
+
+static u_char
+MemReadHSCX_IPACX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return(memreadreg(cs->hw.diva.cfg_reg, offset + 
+                    (hscx ? IPACX_OFF_B2 : IPACX_OFF_B1)));
+}
+
+static void
+MemWriteHSCX_IPACX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	memwritereg(cs->hw.diva.cfg_reg, offset + 
+              (hscx ? IPACX_OFF_B2 : IPACX_OFF_B1), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.diva.hscx_adr, \
+		cs->hw.diva.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.diva.hscx_adr, \
+                cs->hw.diva.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.diva.hscx_adr, \
+		cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.diva.hscx_adr, \
+		cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+diva_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval;
+	u_long flags;
+	int cnt=5;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	while (((sval = bytein(cs->hw.diva.ctrl)) & DIVA_IRQ_REQ) && cnt) {
+		val = readreg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_ISTA + 0x40);
+		if (val)
+			hscx_int_main(cs, val);
+		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA);
+		if (val)
+			isac_interrupt(cs, val);
+		cnt--;
+	}
+	if (!cnt)
+		printk(KERN_WARNING "Diva: IRQ LOOP\n");
+	writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0x0);
+	writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+diva_irq_ipac_isa(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista,val;
+	u_long flags;
+	int icnt=5;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA);
+Start_IPACISA:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA + 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPACISA;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "DIVA IPAC IRQ LOOP\n");
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static inline void
+MemwaitforCEC(struct IsdnCardState *cs, int hscx)
+{
+	int to = 50;
+
+	while ((MemReadHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: waitforCEC timeout\n");
+}
+
+
+static inline void
+MemwaitforXFW(struct IsdnCardState *cs, int hscx)
+{
+	int to = 50;
+
+	while ((!(MemReadHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: waitforXFW timeout\n");
+}
+
+static inline void
+MemWriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data)
+{
+	MemwaitforCEC(cs, hscx);
+	MemWriteHSCX(cs, hscx, HSCX_CMDR, data);
+}
+
+static void
+Memhscx_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *cs = bcs->cs;
+	int cnt;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hscx_empty_fifo");
+
+	if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hscx_empty_fifo: incoming packet too large");
+		MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+		bcs->hw.hscx.rcvidx = 0;
+		return;
+	}
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	cnt = count;
+	while (cnt--)
+		*ptr++ = memreadreg(cs->hw.diva.cfg_reg, bcs->hw.hscx.hscx ? 0x40 : 0);
+	MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	bcs->hw.hscx.rcvidx += count;
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static void
+Memhscx_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int more, count, cnt;
+	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+	u_char *ptr,*p;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hscx_fill_fifo");
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+	if (bcs->tx_skb->len > fifo_size) {
+		more = !0;
+		count = fifo_size;
+	} else
+		count = bcs->tx_skb->len;
+	cnt = count;
+	MemwaitforXFW(cs, bcs->hw.hscx.hscx);
+	p = ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hscx.count += count;
+	while(cnt--)
+		memwritereg(cs->hw.diva.cfg_reg, bcs->hw.hscx.hscx ? 0x40 : 0,
+			*p++);
+	MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static inline void
+Memhscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx)
+{
+	u_char r;
+	struct BCState *bcs = cs->bcs + hscx;
+	struct sk_buff *skb;
+	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+	int count;
+
+	if (!test_bit(BC_FLG_INIT, &bcs->Flag))
+		return;
+
+	if (val & 0x80) {	/* RME */
+		r = MemReadHSCX(cs, hscx, HSCX_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!(r & 0x80))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX invalid frame");
+			if ((r & 0x40) && bcs->mode)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX RDO mode=%d",
+						bcs->mode);
+			if (!(r & 0x20))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX CRC error");
+			MemWriteHSCXCMDR(cs, hscx, 0x80);
+		} else {
+			count = MemReadHSCX(cs, hscx, HSCX_RBCL) & (
+				test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f);
+			if (count == 0)
+				count = fifo_size;
+			Memhscx_empty_fifo(bcs, count);
+			if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+				if (cs->debug & L1_DEB_HSCX_FIFO)
+					debugl1(cs, "HX Frame %d", count);
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "HSCX: receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+					skb_queue_tail(&bcs->rqueue, skb);
+				}
+			}
+		}
+		bcs->hw.hscx.rcvidx = 0;
+		schedule_event(bcs, B_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		Memhscx_empty_fifo(bcs, fifo_size);
+		if (bcs->mode == L1_MODE_TRANS) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(fifo_size)))
+				printk(KERN_WARNING "HiSax: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size);
+				skb_queue_tail(&bcs->rqueue, skb);
+			}
+			bcs->hw.hscx.rcvidx = 0;
+			schedule_event(bcs, B_RCVBUFREADY);
+		}
+	}
+	if (val & 0x10) {	/* XPR */
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				Memhscx_fill_fifo(bcs);
+				return;
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->hw.hscx.count;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+				dev_kfree_skb_irq(bcs->tx_skb);
+				bcs->hw.hscx.count = 0; 
+				bcs->tx_skb = NULL;
+			}
+		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hscx.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			Memhscx_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+static inline void
+Memhscx_int_main(struct IsdnCardState *cs, u_char val)
+{
+
+	u_char exval;
+	struct BCState *bcs;
+
+	if (val & 0x01) { // EXB
+		bcs = cs->bcs + 1;
+		exval = MemReadHSCX(cs, 1, HSCX_EXIR);
+		if (exval & 0x40) {
+			if (bcs->mode == 1)
+				Memhscx_fill_fifo(bcs);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (bcs->tx_skb) {
+					skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+					bcs->tx_cnt += bcs->hw.hscx.count;
+					bcs->hw.hscx.count = 0;
+				}
+				MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX B EXIR %x Lost TX", exval);
+			}
+		} else if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX B EXIR %x", exval);
+	}
+	if (val & 0xf8) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX B interrupt %x", val);
+		Memhscx_interrupt(cs, val, 1);
+	}
+	if (val & 0x02) {	// EXA
+		bcs = cs->bcs;
+		exval = MemReadHSCX(cs, 0, HSCX_EXIR);
+		if (exval & 0x40) {
+			if (bcs->mode == L1_MODE_TRANS)
+				Memhscx_fill_fifo(bcs);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (bcs->tx_skb) {
+					skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+					bcs->tx_cnt += bcs->hw.hscx.count;
+					bcs->hw.hscx.count = 0;
+				}
+				MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX A EXIR %x Lost TX", exval);
+			}
+		} else if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX A EXIR %x", exval);
+	}
+	if (val & 0x04) {	// ICA
+		exval = MemReadHSCX(cs, 0, HSCX_ISTA);
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX A interrupt %x", exval);
+		Memhscx_interrupt(cs, exval, 0);
+	}
+}
+
+static irqreturn_t
+diva_irq_ipac_pci(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista,val;
+	int icnt=5;
+	u_char *cfg;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	cfg = (u_char *) cs->hw.diva.pci_cfg;
+	val = *cfg;
+	if (!(val & PITA_INT0_STATUS)) {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE; /* other shared IRQ */
+	}
+	*cfg = PITA_INT0_STATUS; /* Reset pending INT0 */
+	ista = memreadreg(cs->hw.diva.cfg_reg, IPAC_ISTA);
+Start_IPACPCI:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = memreadreg(cs->hw.diva.cfg_reg, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			Memhscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & memreadreg(cs->hw.diva.cfg_reg, ISAC_ISTA + 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = memreadreg(cs->hw.diva.cfg_reg, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPACPCI;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "DIVA IPAC PCI IRQ LOOP\n");
+	memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xFF);
+	memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+diva_irq_ipacx_pci(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_char *cfg;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	cfg = (u_char *) cs->hw.diva.pci_cfg;
+	val = *cfg;
+	if (!(val &PITA_INT0_STATUS)) {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE; // other shared IRQ
+	}
+ 	interrupt_ipacx(cs);      // handler for chip
+	*cfg = PITA_INT0_STATUS;  // Reset PLX interrupt
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_diva(struct IsdnCardState *cs)
+{
+	int bytecnt;
+
+	if ((cs->subtyp == DIVA_IPAC_PCI) || 
+	    (cs->subtyp == DIVA_IPACX_PCI)   ) {
+		u_int *cfg = (unsigned int *)cs->hw.diva.pci_cfg;
+
+		*cfg = 0; /* disable INT0/1 */ 
+		*cfg = 2; /* reset pending INT0 */
+		iounmap((void *)cs->hw.diva.cfg_reg);
+		iounmap((void *)cs->hw.diva.pci_cfg);
+		return;
+	} else if (cs->subtyp != DIVA_IPAC_ISA) {
+		del_timer(&cs->hw.diva.tl);
+		if (cs->hw.diva.cfg_reg)
+			byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */
+	}
+	if ((cs->subtyp == DIVA_ISA) || (cs->subtyp == DIVA_IPAC_ISA))
+		bytecnt = 8;
+	else
+		bytecnt = 32;
+	if (cs->hw.diva.cfg_reg) {
+		release_region(cs->hw.diva.cfg_reg, bytecnt);
+	}
+}
+
+static void
+reset_diva(struct IsdnCardState *cs)
+{
+	if (cs->subtyp == DIVA_IPAC_ISA) {
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20);
+		mdelay(10);
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00);
+		mdelay(10);
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0);
+	} else if (cs->subtyp == DIVA_IPAC_PCI) {
+		unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg +
+					PITA_MISC_REG);
+		*ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE;
+		mdelay(10);
+		*ireg = PITA_PARA_MPX_MODE;
+		mdelay(10);
+		memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xc0);
+	} else if (cs->subtyp == DIVA_IPACX_PCI) {
+		unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg +
+					PITA_MISC_REG);
+		*ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE;
+		mdelay(10);
+		*ireg = PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET;
+		mdelay(10);
+		MemWriteISAC_IPACX(cs, IPACX_MASK, 0xff); // Interrupts off
+	} else { /* DIVA 2.0 */
+		cs->hw.diva.ctrl_reg = 0;        /* Reset On */
+		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+		mdelay(10);
+		cs->hw.diva.ctrl_reg |= DIVA_RESET;  /* Reset Off */
+		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+		mdelay(10);
+		if (cs->subtyp == DIVA_ISA)
+			cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A;
+		else {
+			/* Workaround PCI9060 */
+			byteout(cs->hw.diva.pci_cfg + 0x69, 9);
+			cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A;
+		}
+		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+	}
+}
+
+#define DIVA_ASSIGN 1
+
+static void
+diva_led_handler(struct IsdnCardState *cs)
+{
+	int blink = 0;
+
+	if ((cs->subtyp == DIVA_IPAC_ISA) ||
+	    (cs->subtyp == DIVA_IPAC_PCI) ||
+	    (cs->subtyp == DIVA_IPACX_PCI)   )
+		return;
+	del_timer(&cs->hw.diva.tl);
+	if (cs->hw.diva.status & DIVA_ASSIGN)
+		cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_A : DIVA_PCI_LED_A;
+	else {
+		cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_A : DIVA_PCI_LED_A;
+		blink = 250;
+	}
+	if (cs->hw.diva.status & 0xf000)
+		cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_B : DIVA_PCI_LED_B;
+	else if (cs->hw.diva.status & 0x0f00) {
+		cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_B : DIVA_PCI_LED_B;
+		blink = 500;
+	} else
+		cs->hw.diva.ctrl_reg &= ~((DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_B : DIVA_PCI_LED_B);
+
+	byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+	if (blink) {
+		init_timer(&cs->hw.diva.tl);
+		cs->hw.diva.tl.expires = jiffies + ((blink * HZ) / 1000);
+		add_timer(&cs->hw.diva.tl);
+	}
+}
+
+static int
+Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_int *ireg;
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_diva(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_diva(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_diva(cs);
+			if (cs->subtyp == DIVA_IPACX_PCI) {
+				ireg = (unsigned int *)cs->hw.diva.pci_cfg;
+				*ireg = PITA_INT0_ENABLE;
+				init_ipacx(cs, 3); // init chip and enable interrupts
+				spin_unlock_irqrestore(&cs->lock, flags);
+				return (0);
+			}
+			if (cs->subtyp == DIVA_IPAC_PCI) {
+				ireg = (unsigned int *)cs->hw.diva.pci_cfg;
+				*ireg = PITA_INT0_ENABLE;
+			}
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+		case (MDL_REMOVE | REQUEST):
+			cs->hw.diva.status = 0;
+			break;
+		case (MDL_ASSIGN | REQUEST):
+			cs->hw.diva.status |= DIVA_ASSIGN;
+			break;
+		case MDL_INFO_SETUP:
+			if ((long)arg)
+				cs->hw.diva.status |=  0x0200;
+			else
+				cs->hw.diva.status |=  0x0100;
+			break;
+		case MDL_INFO_CONN:
+			if ((long)arg)
+				cs->hw.diva.status |=  0x2000;
+			else
+				cs->hw.diva.status |=  0x1000;
+			break;
+		case MDL_INFO_REL:
+			if ((long)arg) {
+				cs->hw.diva.status &=  ~0x2000;
+				cs->hw.diva.status &=  ~0x0200;
+			} else {
+				cs->hw.diva.status &=  ~0x1000;
+				cs->hw.diva.status &=  ~0x0100;
+			}
+			break;
+	}
+	if ((cs->subtyp != DIVA_IPAC_ISA) && 
+	    (cs->subtyp != DIVA_IPAC_PCI) &&
+	    (cs->subtyp != DIVA_IPACX_PCI)) {
+	    	spin_lock_irqsave(&cs->lock, flags);
+		diva_led_handler(cs);
+		spin_unlock_irqrestore(&cs->lock, flags);
+	}
+	return(0);
+}
+
+static struct pci_dev *dev_diva __initdata = NULL;
+static struct pci_dev *dev_diva_u __initdata = NULL;
+static struct pci_dev *dev_diva201 __initdata = NULL;
+static struct pci_dev *dev_diva202 __initdata = NULL;
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id diva_ids[] __initdata = {
+	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51),
+	  ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51), 
+	  (unsigned long) "Diva picola" },
+	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51),
+	  ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0x51), 
+	  (unsigned long) "Diva picola" },
+	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71),
+	  ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71), 
+	  (unsigned long) "Diva 2.0" },
+	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71),
+	  ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0x71), 
+	  (unsigned long) "Diva 2.0" },
+	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1),
+	  ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1), 
+	  (unsigned long) "Diva 2.01" },
+	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1),
+	  ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0xA1), 
+	  (unsigned long) "Diva 2.01" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __initdata = &diva_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+
+int __init
+setup_diva(struct IsdnCard *card)
+{
+	int bytecnt = 8;
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, Diva_revision);
+	printk(KERN_INFO "HiSax: Eicon.Diehl Diva driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_DIEHLDIVA)
+		return(0);
+	cs->hw.diva.status = 0;
+	if (card->para[1]) {
+		cs->hw.diva.ctrl_reg = 0;
+		cs->hw.diva.cfg_reg = card->para[1];
+		val = readreg(cs->hw.diva.cfg_reg + DIVA_IPAC_ADR,
+			cs->hw.diva.cfg_reg + DIVA_IPAC_DATA, IPAC_ID);
+		printk(KERN_INFO "Diva: IPAC version %x\n", val);
+		if ((val == 1) || (val==2)) {
+			cs->subtyp = DIVA_IPAC_ISA;
+			cs->hw.diva.ctrl = 0;
+			cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA;
+			cs->hw.diva.hscx = card->para[1] + DIVA_IPAC_DATA;
+			cs->hw.diva.isac_adr = card->para[1] + DIVA_IPAC_ADR;
+			cs->hw.diva.hscx_adr = card->para[1] + DIVA_IPAC_ADR;
+			test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		} else {
+			cs->subtyp = DIVA_ISA;
+			cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL;
+			cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA;
+			cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA;
+			cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR;
+			cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR;
+		}
+		cs->irq = card->para[0];
+	} else {
+#ifdef __ISAPNP__
+		if (isapnp_present()) {
+			struct pnp_dev *pnp_d;
+			while(ipid->card_vendor) {
+				if ((pnp_c = pnp_find_card(ipid->card_vendor,
+					ipid->card_device, pnp_c))) {
+					pnp_d = NULL;
+					if ((pnp_d = pnp_find_dev(pnp_c,
+						ipid->vendor, ipid->function, pnp_d))) {
+						int err;
+
+						printk(KERN_INFO "HiSax: %s detected\n",
+							(char *)ipid->driver_data);
+						pnp_disable_dev(pnp_d);
+						err = pnp_activate_dev(pnp_d);
+						if (err<0) {
+							printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+								__FUNCTION__, err);
+							return(0);
+						}
+						card->para[1] = pnp_port_start(pnp_d, 0);
+						card->para[0] = pnp_irq(pnp_d, 0);
+						if (!card->para[0] || !card->para[1]) {
+							printk(KERN_ERR "Diva PnP:some resources are missing %ld/%lx\n",
+								card->para[0], card->para[1]);
+							pnp_disable_dev(pnp_d); 
+							return(0);
+						}
+						cs->hw.diva.cfg_reg  = card->para[1];
+						cs->irq = card->para[0];
+						if (ipid->function == ISAPNP_FUNCTION(0xA1)) {
+							cs->subtyp = DIVA_IPAC_ISA;
+							cs->hw.diva.ctrl = 0;
+							cs->hw.diva.isac =
+								card->para[1] + DIVA_IPAC_DATA;
+							cs->hw.diva.hscx =
+								card->para[1] + DIVA_IPAC_DATA;
+							cs->hw.diva.isac_adr =
+								card->para[1] + DIVA_IPAC_ADR;
+							cs->hw.diva.hscx_adr =
+								card->para[1] + DIVA_IPAC_ADR;
+							test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+						} else {
+							cs->subtyp = DIVA_ISA;
+							cs->hw.diva.ctrl =
+								card->para[1] + DIVA_ISA_CTRL;
+							cs->hw.diva.isac =
+								card->para[1] + DIVA_ISA_ISAC_DATA;
+							cs->hw.diva.hscx =
+								card->para[1] + DIVA_HSCX_DATA;
+							cs->hw.diva.isac_adr =
+								card->para[1] + DIVA_ISA_ISAC_ADR;
+							cs->hw.diva.hscx_adr =
+								card->para[1] + DIVA_HSCX_ADR;
+						}
+						goto ready;
+					} else {
+						printk(KERN_ERR "Diva PnP: PnP error card found, no device\n");
+						return(0);
+					}
+				}
+				ipid++;
+				pnp_c=NULL;
+			} 
+			if (!ipid->card_vendor) {
+				printk(KERN_INFO "Diva PnP: no ISAPnP card found\n");
+			}
+		}
+#endif
+#ifdef CONFIG_PCI
+		cs->subtyp = 0;
+		if ((dev_diva = pci_find_device(PCI_VENDOR_ID_EICON,
+			PCI_DEVICE_ID_EICON_DIVA20, dev_diva))) {
+			if (pci_enable_device(dev_diva))
+				return(0);
+			cs->subtyp = DIVA_PCI;
+			cs->irq = dev_diva->irq;
+			cs->hw.diva.cfg_reg = pci_resource_start(dev_diva, 2);
+		} else if ((dev_diva_u = pci_find_device(PCI_VENDOR_ID_EICON,
+			PCI_DEVICE_ID_EICON_DIVA20_U, dev_diva_u))) {
+			if (pci_enable_device(dev_diva_u))
+				return(0);
+			cs->subtyp = DIVA_PCI;
+			cs->irq = dev_diva_u->irq;
+			cs->hw.diva.cfg_reg = pci_resource_start(dev_diva_u, 2);
+		} else if ((dev_diva201 = pci_find_device(PCI_VENDOR_ID_EICON,
+			PCI_DEVICE_ID_EICON_DIVA201, dev_diva201))) {
+			if (pci_enable_device(dev_diva201))
+				return(0);
+			cs->subtyp = DIVA_IPAC_PCI;
+			cs->irq = dev_diva201->irq;
+			cs->hw.diva.pci_cfg =
+				(ulong) ioremap(pci_resource_start(dev_diva201, 0), 4096);
+			cs->hw.diva.cfg_reg =
+				(ulong) ioremap(pci_resource_start(dev_diva201, 1), 4096);
+		} else if ((dev_diva202 = pci_find_device(PCI_VENDOR_ID_EICON,
+			PCI_DEVICE_ID_EICON_DIVA202, dev_diva202))) {
+			if (pci_enable_device(dev_diva202))
+				return(0);
+			cs->subtyp = DIVA_IPACX_PCI;
+			cs->irq = dev_diva202->irq;
+			cs->hw.diva.pci_cfg =
+				(ulong) ioremap(pci_resource_start(dev_diva202, 0), 4096);
+			cs->hw.diva.cfg_reg =
+				(ulong) ioremap(pci_resource_start(dev_diva202, 1), 4096);
+		} else {
+			printk(KERN_WARNING "Diva: No PCI card found\n");
+			return(0);
+		}
+
+		if (!cs->irq) {
+			printk(KERN_WARNING "Diva: No IRQ for PCI card found\n");
+			return(0);
+		}
+
+		if (!cs->hw.diva.cfg_reg) {
+			printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n");
+			return(0);
+		}
+		cs->irq_flags |= SA_SHIRQ;
+#else
+		printk(KERN_WARNING "Diva: cfgreg 0 and NO_PCI_BIOS\n");
+		printk(KERN_WARNING "Diva: unable to config DIVA PCI\n");
+		return (0);
+#endif /* CONFIG_PCI */
+		if ((cs->subtyp == DIVA_IPAC_PCI) ||
+		    (cs->subtyp == DIVA_IPACX_PCI)   ) {
+			cs->hw.diva.ctrl = 0;
+			cs->hw.diva.isac = 0;
+			cs->hw.diva.hscx = 0;
+			cs->hw.diva.isac_adr = 0;
+			cs->hw.diva.hscx_adr = 0;
+			test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+			bytecnt = 0;
+		} else {
+			cs->hw.diva.ctrl = cs->hw.diva.cfg_reg + DIVA_PCI_CTRL;
+			cs->hw.diva.isac = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_DATA;
+			cs->hw.diva.hscx = cs->hw.diva.cfg_reg + DIVA_HSCX_DATA;
+			cs->hw.diva.isac_adr = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_ADR;
+			cs->hw.diva.hscx_adr = cs->hw.diva.cfg_reg + DIVA_HSCX_ADR;
+			bytecnt = 32;
+		}
+	}
+ready:
+	printk(KERN_INFO
+		"Diva: %s card configured at %#lx IRQ %d\n",
+		(cs->subtyp == DIVA_PCI) ? "PCI" :
+		(cs->subtyp == DIVA_ISA) ? "ISA" : 
+		(cs->subtyp == DIVA_IPAC_ISA) ? "IPAC ISA" :
+		(cs->subtyp == DIVA_IPAC_PCI) ? "IPAC PCI" : "IPACX PCI",
+		cs->hw.diva.cfg_reg, cs->irq);
+	if ((cs->subtyp == DIVA_IPAC_PCI)  || 
+	    (cs->subtyp == DIVA_IPACX_PCI) || 
+	    (cs->subtyp == DIVA_PCI)         )
+		printk(KERN_INFO "Diva: %s space at %#lx\n",
+			(cs->subtyp == DIVA_PCI) ? "PCI" :
+			(cs->subtyp == DIVA_IPAC_PCI) ? "IPAC PCI" : "IPACX PCI",
+			cs->hw.diva.pci_cfg);
+	if ((cs->subtyp != DIVA_IPAC_PCI) &&
+	    (cs->subtyp != DIVA_IPACX_PCI)   ) {
+		if (!request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn")) {
+			printk(KERN_WARNING
+			       "HiSax: %s config port %lx-%lx already in use\n",
+			       CardType[card->typ],
+			       cs->hw.diva.cfg_reg,
+			       cs->hw.diva.cfg_reg + bytecnt);
+			return (0);
+		}
+	}
+	cs->BC_Read_Reg  = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Diva_card_msg;
+	setup_isac(cs);
+	if (cs->subtyp == DIVA_IPAC_ISA) {
+		cs->readisac  = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo  = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		cs->irq_func = &diva_irq_ipac_isa;
+		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ID);
+		printk(KERN_INFO "Diva: IPAC version %x\n", val);
+	} else if (cs->subtyp == DIVA_IPAC_PCI) {
+		cs->readisac  = &MemReadISAC_IPAC;
+		cs->writeisac = &MemWriteISAC_IPAC;
+		cs->readisacfifo  = &MemReadISACfifo_IPAC;
+		cs->writeisacfifo = &MemWriteISACfifo_IPAC;
+		cs->BC_Read_Reg  = &MemReadHSCX;
+		cs->BC_Write_Reg = &MemWriteHSCX;
+		cs->BC_Send_Data = &Memhscx_fill_fifo;
+		cs->irq_func = &diva_irq_ipac_pci;
+		val = memreadreg(cs->hw.diva.cfg_reg, IPAC_ID);
+		printk(KERN_INFO "Diva: IPAC version %x\n", val);
+	} else if (cs->subtyp == DIVA_IPACX_PCI) {
+		cs->readisac  = &MemReadISAC_IPACX;
+		cs->writeisac = &MemWriteISAC_IPACX;
+		cs->readisacfifo  = &MemReadISACfifo_IPACX;
+		cs->writeisacfifo = &MemWriteISACfifo_IPACX;
+		cs->BC_Read_Reg  = &MemReadHSCX_IPACX;
+		cs->BC_Write_Reg = &MemWriteHSCX_IPACX;
+		cs->BC_Send_Data = NULL; // function located in ipacx module
+		cs->irq_func = &diva_irq_ipacx_pci;
+		printk(KERN_INFO "Diva: IPACX Design Id: %x\n", 
+			MemReadISAC_IPACX(cs, IPACX_ID) &0x3F);
+	} else { /* DIVA 2.0 */
+		cs->hw.diva.tl.function = (void *) diva_led_handler;
+		cs->hw.diva.tl.data = (long) cs;
+		init_timer(&cs->hw.diva.tl);
+		cs->readisac  = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo  = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		cs->irq_func = &diva_interrupt;
+		ISACVersion(cs, "Diva:");
+		if (HscxVersion(cs, "Diva:")) {
+			printk(KERN_WARNING
+		       "Diva: wrong HSCX versions check IO address\n");
+			release_io_diva(cs);
+			return (0);
+		}
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c
new file mode 100644
index 000000000000..4d7a0250d7e2
--- /dev/null
+++ b/drivers/isdn/hisax/elsa.c
@@ -0,0 +1,1190 @@
+/* $Id: elsa.c,v 2.32.2.4 2004/01/24 20:47:21 keil Exp $
+ *
+ * low level stuff for Elsa isdn cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to    Elsa GmbH for documents and information
+ *
+ *              Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE)
+ *              for ELSA PCMCIA support
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include "hisax.h"
+#include "arcofi.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+
+extern const char *CardType[];
+
+const char *Elsa_revision = "$Revision: 2.32.2.4 $";
+const char *Elsa_Types[] =
+{"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro",
+ "PCMCIA", "QS 1000", "QS 3000", "Microlink PCI", "QS 3000 PCI", 
+ "PCMCIA-IPAC" };
+
+const char *ITACVer[] =
+{"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2",
+ "B1", "A1"};
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ELSA_ISAC	0
+#define ELSA_ISAC_PCM	1
+#define ELSA_ITAC	1
+#define ELSA_HSCX	2
+#define ELSA_ALE	3
+#define ELSA_ALE_PCM	4
+#define ELSA_CONTROL	4
+#define ELSA_CONFIG	5
+#define ELSA_START_TIMER 6
+#define ELSA_TRIG_IRQ	7
+
+#define ELSA_PC      1
+#define ELSA_PCC8    2
+#define ELSA_PCC16   3
+#define ELSA_PCF     4
+#define ELSA_PCFPRO  5
+#define ELSA_PCMCIA  6
+#define ELSA_QS1000  7
+#define ELSA_QS3000  8
+#define ELSA_QS1000PCI 9
+#define ELSA_QS3000PCI 10
+#define ELSA_PCMCIA_IPAC 11
+
+/* PCI stuff */
+#define ELSA_PCI_IRQ_MASK	0x04
+
+/* ITAC Registeradressen (only Microlink PC) */
+#define ITAC_SYS	0x34
+#define ITAC_ISEN	0x48
+#define ITAC_RFIE	0x4A
+#define ITAC_XFIE	0x4C
+#define ITAC_SCIE	0x4E
+#define ITAC_STIE	0x46
+
+/***                                                                    ***
+ ***   Makros als Befehle fuer die Kartenregister                       ***
+ ***   (mehrere Befehle werden durch Bit-Oderung kombiniert)            ***
+ ***                                                                    ***/
+
+/* Config-Register (Read) */
+#define ELSA_TIMER_RUN       0x02	/* Bit 1 des Config-Reg     */
+#define ELSA_TIMER_RUN_PCC8  0x01	/* Bit 0 des Config-Reg  bei PCC */
+#define ELSA_IRQ_IDX       0x38	/* Bit 3,4,5 des Config-Reg */
+#define ELSA_IRQ_IDX_PCC8  0x30	/* Bit 4,5 des Config-Reg */
+#define ELSA_IRQ_IDX_PC    0x0c	/* Bit 2,3 des Config-Reg */
+
+/* Control-Register (Write) */
+#define ELSA_LINE_LED        0x02	/* Bit 1 Gelbe LED */
+#define ELSA_STAT_LED        0x08	/* Bit 3 Gruene LED */
+#define ELSA_ISDN_RESET      0x20	/* Bit 5 Reset-Leitung */
+#define ELSA_ENA_TIMER_INT   0x80	/* Bit 7 Freigabe Timer Interrupt */
+
+/* ALE-Register (Read) */
+#define ELSA_HW_RELEASE      0x07	/* Bit 0-2 Hardwarerkennung */
+#define ELSA_S0_POWER_BAD    0x08	/* Bit 3 S0-Bus Spannung fehlt */
+
+/* Status Flags */
+#define ELSA_TIMER_AKTIV 1
+#define ELSA_BAD_PWR     2
+#define ELSA_ASSIGN      4
+
+#define RS_ISR_PASS_LIMIT 256
+#define _INLINE_ inline
+#define FLG_MODEM_ACTIVE 1
+/* IPAC AUX */
+#define ELSA_IPAC_LINE_LED	0x40	/* Bit 6 Gelbe LED */
+#define ELSA_IPAC_STAT_LED	0x80	/* Bit 7 Gruene LED */
+
+#if ARCOFI_USE
+static struct arcofi_msg ARCOFI_XOP_F =
+	{NULL,0,2,{0xa1,0x3f,0,0,0,0,0,0,0,0}}; /* Normal OP */
+static struct arcofi_msg ARCOFI_XOP_1 =
+	{&ARCOFI_XOP_F,0,2,{0xa1,0x31,0,0,0,0,0,0,0,0}}; /* PWR UP */
+static struct arcofi_msg ARCOFI_SOP_F = 
+	{&ARCOFI_XOP_1,0,10,{0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12}};
+static struct arcofi_msg ARCOFI_COP_9 =
+	{&ARCOFI_SOP_F,0,10,{0xa1,0x29,0x80,0xcb,0xe9,0x88,0x00,0xc8,0xd8,0x80}}; /* RX */
+static struct arcofi_msg ARCOFI_COP_8 =
+	{&ARCOFI_COP_9,0,10,{0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}}; /* TX */
+static struct arcofi_msg ARCOFI_COP_7 =
+	{&ARCOFI_COP_8,0,4,{0xa1,0x27,0x80,0x80,0,0,0,0,0,0}}; /* GZ */
+static struct arcofi_msg ARCOFI_COP_6 =
+	{&ARCOFI_COP_7,0,6,{0xa1,0x26,0,0,0x82,0x7c,0,0,0,0}}; /* GRL GRH */
+static struct arcofi_msg ARCOFI_COP_5 =
+	{&ARCOFI_COP_6,0,4,{0xa1,0x25,0xbb,0x4a,0,0,0,0,0,0}}; /* GTX */
+static struct arcofi_msg ARCOFI_VERSION =
+	{NULL,1,2,{0xa0,0,0,0,0,0,0,0,0,0}};
+static struct arcofi_msg ARCOFI_XOP_0 =
+	{NULL,0,2,{0xa1,0x30,0,0,0,0,0,0,0,0}}; /* PWR Down */
+
+static void set_arcofi(struct IsdnCardState *cs, int bc);
+
+#include "elsa_ser.c"
+#endif /* ARCOFI_USE */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset+0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.elsa.ale,
+			cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.elsa.ale,
+		 cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+static inline u_char
+readitac(struct IsdnCardState *cs, u_char off)
+{
+	register u_char ret;
+
+	byteout(cs->hw.elsa.ale, off);
+	ret = bytein(cs->hw.elsa.itac);
+	return (ret);
+}
+
+static inline void
+writeitac(struct IsdnCardState *cs, u_char off, u_char data)
+{
+	byteout(cs->hw.elsa.ale, off);
+	byteout(cs->hw.elsa.itac, data);
+}
+
+static inline int
+TimerRun(struct IsdnCardState *cs)
+{
+	register u_char v;
+
+	v = bytein(cs->hw.elsa.cfg);
+	if ((cs->subtyp == ELSA_QS1000) || (cs->subtyp == ELSA_QS3000))
+		return (0 == (v & ELSA_TIMER_RUN));
+	else if (cs->subtyp == ELSA_PCC8)
+		return (v & ELSA_TIMER_RUN_PCC8);
+	return (v & ELSA_TIMER_RUN);
+}
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_long flags;
+	u_char val;
+	int icnt=5;
+
+	if ((cs->typ == ISDN_CTYPE_ELSA_PCMCIA) && (*cs->busy_flag == 1)) {
+	/* The card tends to generate interrupts while being removed
+	   causing us to just crash the kernel. bad. */
+		printk(KERN_WARNING "Elsa: card not available!\n");
+		return IRQ_NONE;
+	}
+	spin_lock_irqsave(&cs->lock, flags);
+#if ARCOFI_USE
+	if (cs->hw.elsa.MFlag) {
+		val = serial_inp(cs, UART_IIR);
+		if (!(val & UART_IIR_NO_INT)) {
+			debugl1(cs,"IIR %02x", val);
+			rs_interrupt_elsa(intno, cs);
+		}
+	}
+#endif
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(cs, val);
+	}
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+	}
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
+	if (val && icnt) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		icnt--;
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA);
+	if (val && icnt) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		icnt--;
+		goto Start_ISAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING"ELSA IRQ LOOP\n");
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0xFF);
+	if (cs->hw.elsa.status & ELSA_TIMER_AKTIV) {
+		if (!TimerRun(cs)) {
+			/* Timer Restart */
+			byteout(cs->hw.elsa.timer, 0);
+			cs->hw.elsa.counter++;
+		}
+	}
+#if ARCOFI_USE
+	if (cs->hw.elsa.MFlag) {
+		val = serial_inp(cs, UART_MCR);
+		val ^= 0x8;
+		serial_outp(cs, UART_MCR, val);
+		val = serial_inp(cs, UART_MCR);
+		val ^= 0x8;
+		serial_outp(cs, UART_MCR, val);
+	}
+#endif
+	if (cs->hw.elsa.trig)
+		byteout(cs->hw.elsa.trig, 0x00);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0x0);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0x0);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+elsa_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_long flags;
+	u_char ista,val;
+	int icnt=5;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if (cs->subtyp == ELSA_QS1000PCI || cs->subtyp == ELSA_QS3000PCI) {
+		val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */
+		if (!(val & ELSA_PCI_IRQ_MASK)) {
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_NONE;
+		}
+	}
+#if ARCOFI_USE
+	if (cs->hw.elsa.MFlag) {
+		val = serial_inp(cs, UART_IIR);
+		if (!(val & UART_IIR_NO_INT)) {
+			debugl1(cs,"IIR %02x", val);
+			rs_interrupt_elsa(intno, cs);
+		}
+	}
+#endif
+	ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA + 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "ELSA IRQ LOOP\n");
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_elsa(struct IsdnCardState *cs)
+{
+	int bytecnt = 8;
+
+	del_timer(&cs->hw.elsa.tl);
+#if ARCOFI_USE
+	clear_arcofi(cs);
+#endif
+	if (cs->hw.elsa.ctrl)
+		byteout(cs->hw.elsa.ctrl, 0);	/* LEDs Out */
+	if (cs->subtyp == ELSA_QS1000PCI) {
+		byteout(cs->hw.elsa.cfg + 0x4c, 0x01);  /* disable IRQ */
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+		bytecnt = 2;
+		release_region(cs->hw.elsa.cfg, 0x80);
+	}
+	if (cs->subtyp == ELSA_QS3000PCI) {
+		byteout(cs->hw.elsa.cfg + 0x4c, 0x03); /* disable ELSA PCI IRQ */
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+		release_region(cs->hw.elsa.cfg, 0x80);
+	}
+ 	if (cs->subtyp == ELSA_PCMCIA_IPAC) {
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+ 	}
+	if ((cs->subtyp == ELSA_PCFPRO) ||
+		(cs->subtyp == ELSA_QS3000) ||
+		(cs->subtyp == ELSA_PCF) ||
+		(cs->subtyp == ELSA_QS3000PCI)) {
+		bytecnt = 16;
+#if ARCOFI_USE
+		release_modem(cs);
+#endif
+	}
+	if (cs->hw.elsa.base)
+		release_region(cs->hw.elsa.base, bytecnt);
+}
+
+static void
+reset_elsa(struct IsdnCardState *cs)
+{
+	if (cs->hw.elsa.timer) {
+		/* Wait 1 Timer */
+		byteout(cs->hw.elsa.timer, 0);
+		while (TimerRun(cs));
+		cs->hw.elsa.ctrl_reg |= 0x50;
+		cs->hw.elsa.ctrl_reg &= ~ELSA_ISDN_RESET;	/* Reset On */
+		byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+		/* Wait 1 Timer */
+		byteout(cs->hw.elsa.timer, 0);
+		while (TimerRun(cs));
+		cs->hw.elsa.ctrl_reg |= ELSA_ISDN_RESET;	/* Reset Off */
+		byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+		/* Wait 1 Timer */
+		byteout(cs->hw.elsa.timer, 0);
+		while (TimerRun(cs));
+		if (cs->hw.elsa.trig)
+			byteout(cs->hw.elsa.trig, 0xff);
+	}
+	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI) || (cs->subtyp == ELSA_PCMCIA_IPAC)) {
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20);
+		mdelay(10);
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00);
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0);
+		mdelay(10);
+		if (cs->subtyp != ELSA_PCMCIA_IPAC) {
+			writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x0);
+			writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0x3c);
+		} else {
+			writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_PCFG, 0x10);
+			writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x4);
+			writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0xf8);
+		}
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+		if (cs->subtyp == ELSA_QS1000PCI)
+			byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */
+		else if (cs->subtyp == ELSA_QS3000PCI)
+			byteout(cs->hw.elsa.cfg + 0x4c, 0x43); /* enable ELSA PCI IRQ */
+	}
+}
+
+#if ARCOFI_USE
+
+static void
+set_arcofi(struct IsdnCardState *cs, int bc) {
+	cs->dc.isac.arcofi_bc = bc;
+	arcofi_fsm(cs, ARCOFI_START, &ARCOFI_COP_5);
+	interruptible_sleep_on(&cs->dc.isac.arcofi_wait);
+}
+
+static int
+check_arcofi(struct IsdnCardState *cs)
+{
+	int arcofi_present = 0;
+	char tmp[40];
+	char *t;
+	u_char *p;
+
+	if (!cs->dc.isac.mon_tx)
+		if (!(cs->dc.isac.mon_tx=kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "ISAC MON TX out of buffers!");
+			return(0);
+		}
+	cs->dc.isac.arcofi_bc = 0;
+	arcofi_fsm(cs, ARCOFI_START, &ARCOFI_VERSION);
+	interruptible_sleep_on(&cs->dc.isac.arcofi_wait);
+	if (!test_and_clear_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags)) {
+			debugl1(cs, "Arcofi response received %d bytes", cs->dc.isac.mon_rxp);
+			p = cs->dc.isac.mon_rx;
+			t = tmp;
+			t += sprintf(tmp, "Arcofi data");
+			QuickHex(t, p, cs->dc.isac.mon_rxp);
+			debugl1(cs, tmp);
+			if ((cs->dc.isac.mon_rxp == 2) && (cs->dc.isac.mon_rx[0] == 0xa0)) {
+				switch(cs->dc.isac.mon_rx[1]) {
+					case 0x80:
+						debugl1(cs, "Arcofi 2160 detected");
+						arcofi_present = 1;
+						break;
+					case 0x82:
+						debugl1(cs, "Arcofi 2165 detected");
+						arcofi_present = 2;
+						break;
+					case 0x84:
+						debugl1(cs, "Arcofi 2163 detected");
+						arcofi_present = 3;
+						break;
+					default:
+						debugl1(cs, "unknown Arcofi response");
+						break;
+				}
+			} else
+				debugl1(cs, "undefined Monitor response");
+			cs->dc.isac.mon_rxp = 0;
+	} else if (cs->dc.isac.mon_tx) {
+		debugl1(cs, "Arcofi not detected");
+	}
+	if (arcofi_present) {
+		if (cs->subtyp==ELSA_QS1000) {
+			cs->subtyp = ELSA_QS3000;
+			printk(KERN_INFO
+				"Elsa: %s detected modem at 0x%lx\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base+8);
+			release_region(cs->hw.elsa.base, 8);
+			if (!request_region(cs->hw.elsa.base, 16, "elsa isdn modem")) {
+				printk(KERN_WARNING
+					"HiSax: %s config port %lx-%lx already in use\n",
+					Elsa_Types[cs->subtyp],
+					cs->hw.elsa.base + 8,
+					cs->hw.elsa.base + 16);
+			}
+		} else if (cs->subtyp==ELSA_PCC16) {
+			cs->subtyp = ELSA_PCF;
+			printk(KERN_INFO
+				"Elsa: %s detected modem at 0x%lx\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base+8);
+			release_region(cs->hw.elsa.base, 8);
+			if (!request_region(cs->hw.elsa.base, 16, "elsa isdn modem")) {
+				printk(KERN_WARNING
+					"HiSax: %s config port %lx-%lx already in use\n",
+					Elsa_Types[cs->subtyp],
+					cs->hw.elsa.base + 8,
+					cs->hw.elsa.base + 16);
+			}
+		} else
+			printk(KERN_INFO
+				"Elsa: %s detected modem at 0x%lx\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base+8);
+		arcofi_fsm(cs, ARCOFI_START, &ARCOFI_XOP_0);
+		interruptible_sleep_on(&cs->dc.isac.arcofi_wait);
+		return(1);
+	}
+	return(0);
+}
+#endif /* ARCOFI_USE */
+
+static void
+elsa_led_handler(struct IsdnCardState *cs)
+{
+	int blink = 0;
+
+	if (cs->subtyp == ELSA_PCMCIA || cs->subtyp == ELSA_PCMCIA_IPAC)
+		return;
+	del_timer(&cs->hw.elsa.tl);
+	if (cs->hw.elsa.status & ELSA_ASSIGN)
+		cs->hw.elsa.ctrl_reg |= ELSA_STAT_LED;
+	else if (cs->hw.elsa.status & ELSA_BAD_PWR)
+		cs->hw.elsa.ctrl_reg &= ~ELSA_STAT_LED;
+	else {
+		cs->hw.elsa.ctrl_reg ^= ELSA_STAT_LED;
+		blink = 250;
+	}
+	if (cs->hw.elsa.status & 0xf000)
+		cs->hw.elsa.ctrl_reg |= ELSA_LINE_LED;
+	else if (cs->hw.elsa.status & 0x0f00) {
+		cs->hw.elsa.ctrl_reg ^= ELSA_LINE_LED;
+		blink = 500;
+	} else
+		cs->hw.elsa.ctrl_reg &= ~ELSA_LINE_LED;
+
+	if ((cs->subtyp == ELSA_QS1000PCI) ||
+		(cs->subtyp == ELSA_QS3000PCI)) {
+		u_char led = 0xff;
+		if (cs->hw.elsa.ctrl_reg & ELSA_LINE_LED)
+			led ^= ELSA_IPAC_LINE_LED;
+		if (cs->hw.elsa.ctrl_reg & ELSA_STAT_LED)
+			led ^= ELSA_IPAC_STAT_LED;
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, led);
+	} else
+		byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+	if (blink) {
+		init_timer(&cs->hw.elsa.tl);
+		cs->hw.elsa.tl.expires = jiffies + ((blink * HZ) / 1000);
+		add_timer(&cs->hw.elsa.tl);
+	}
+}
+
+static int
+Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	int ret = 0;
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_elsa(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_elsa(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->debug |= L1_DEB_IPAC;
+			reset_elsa(cs);
+			inithscxisac(cs, 1);
+			if ((cs->subtyp == ELSA_QS1000) ||
+			    (cs->subtyp == ELSA_QS3000))
+			{
+				byteout(cs->hw.elsa.timer, 0);
+			}
+			if (cs->hw.elsa.trig)
+				byteout(cs->hw.elsa.trig, 0xff);
+			inithscxisac(cs, 2);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			if ((cs->subtyp == ELSA_PCMCIA) ||
+				(cs->subtyp == ELSA_PCMCIA_IPAC) ||
+				(cs->subtyp == ELSA_QS1000PCI)) {
+				return(0);
+			} else if (cs->subtyp == ELSA_QS3000PCI) {
+				ret = 0;
+			} else {
+				spin_lock_irqsave(&cs->lock, flags);
+				cs->hw.elsa.counter = 0;
+				cs->hw.elsa.ctrl_reg |= ELSA_ENA_TIMER_INT;
+				cs->hw.elsa.status |= ELSA_TIMER_AKTIV;
+				byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+				byteout(cs->hw.elsa.timer, 0);
+				spin_unlock_irqrestore(&cs->lock, flags);
+				msleep(110);
+				spin_lock_irqsave(&cs->lock, flags);
+				cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT;
+				byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+				cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV;
+				spin_unlock_irqrestore(&cs->lock, flags);
+				printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n",
+				       cs->hw.elsa.counter);
+				if ((cs->hw.elsa.counter > 10) &&
+					(cs->hw.elsa.counter < 16)) {
+					printk(KERN_INFO "Elsa: timer and irq OK\n");
+					ret = 0;
+				} else {
+					printk(KERN_WARNING
+					       "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n",
+					       cs->hw.elsa.counter, cs->irq);
+					ret = 1;
+				}
+			}
+#if ARCOFI_USE
+			if (check_arcofi(cs)) {
+				init_modem(cs);
+			}
+#endif
+			elsa_led_handler(cs);
+			return(ret);
+		case (MDL_REMOVE | REQUEST):
+			cs->hw.elsa.status &= 0;
+			break;
+		case (MDL_ASSIGN | REQUEST):
+			cs->hw.elsa.status |= ELSA_ASSIGN;
+			break;
+		case MDL_INFO_SETUP:
+			if ((long) arg)
+				cs->hw.elsa.status |= 0x0200;
+			else
+				cs->hw.elsa.status |= 0x0100;
+			break;
+		case MDL_INFO_CONN:
+			if ((long) arg)
+				cs->hw.elsa.status |= 0x2000;
+			else
+				cs->hw.elsa.status |= 0x1000;
+			break;
+		case MDL_INFO_REL:
+			if ((long) arg) {
+				cs->hw.elsa.status &= ~0x2000;
+				cs->hw.elsa.status &= ~0x0200;
+			} else {
+				cs->hw.elsa.status &= ~0x1000;
+				cs->hw.elsa.status &= ~0x0100;
+			}
+			break;
+#if ARCOFI_USE
+		case CARD_AUX_IND:
+			if (cs->hw.elsa.MFlag) {
+				int len;
+				u_char *msg;
+
+				if (!arg)
+					return(0);
+				msg = arg;
+				len = *msg;
+				msg++;
+				modem_write_cmd(cs, msg, len);
+			}
+			break;
+#endif
+	}
+	if (cs->typ == ISDN_CTYPE_ELSA) {
+		int pwr = bytein(cs->hw.elsa.ale);
+		if (pwr & 0x08)
+			cs->hw.elsa.status |= ELSA_BAD_PWR;
+		else
+			cs->hw.elsa.status &= ~ELSA_BAD_PWR;
+	}
+	elsa_led_handler(cs);
+	return(ret);
+}
+
+static unsigned char
+probe_elsa_adr(unsigned int adr, int typ)
+{
+	int i, in1, in2, p16_1 = 0, p16_2 = 0, p8_1 = 0, p8_2 = 0, pc_1 = 0,
+	 pc_2 = 0, pfp_1 = 0, pfp_2 = 0;
+
+	/* In case of the elsa pcmcia card, this region is in use,
+	   reserved for us by the card manager. So we do not check it
+	   here, it would fail. */
+	if (typ != ISDN_CTYPE_ELSA_PCMCIA) {
+		if (request_region(adr, 8, "elsa card")) {
+			release_region(adr, 8);
+		} else {
+			printk(KERN_WARNING
+			       "Elsa: Probing Port 0x%x: already in use\n", adr);
+			return (0);
+		}
+	}
+	for (i = 0; i < 16; i++) {
+		in1 = inb(adr + ELSA_CONFIG);	/* 'toggelt' bei */
+		in2 = inb(adr + ELSA_CONFIG);	/* jedem Zugriff */
+		p16_1 += 0x04 & in1;
+		p16_2 += 0x04 & in2;
+		p8_1 += 0x02 & in1;
+		p8_2 += 0x02 & in2;
+		pc_1 += 0x01 & in1;
+		pc_2 += 0x01 & in2;
+		pfp_1 += 0x40 & in1;
+		pfp_2 += 0x40 & in2;
+	}
+	printk(KERN_INFO "Elsa: Probing IO 0x%x", adr);
+	if (65 == ++p16_1 * ++p16_2) {
+		printk(" PCC-16/PCF found\n");
+		return (ELSA_PCC16);
+	} else if (1025 == ++pfp_1 * ++pfp_2) {
+		printk(" PCF-Pro found\n");
+		return (ELSA_PCFPRO);
+	} else if (33 == ++p8_1 * ++p8_2) {
+		printk(" PCC8 found\n");
+		return (ELSA_PCC8);
+	} else if (17 == ++pc_1 * ++pc_2) {
+		printk(" PC found\n");
+		return (ELSA_PC);
+	} else {
+		printk(" failed\n");
+		return (0);
+	}
+}
+
+static unsigned int
+probe_elsa(struct IsdnCardState *cs)
+{
+	int i;
+	unsigned int CARD_portlist[] =
+	{0x160, 0x170, 0x260, 0x360, 0};
+
+	for (i = 0; CARD_portlist[i]; i++) {
+		if ((cs->subtyp = probe_elsa_adr(CARD_portlist[i], cs->typ)))
+			break;
+	}
+	return (CARD_portlist[i]);
+}
+
+static 	struct pci_dev *dev_qs1000 __devinitdata = NULL;
+static 	struct pci_dev *dev_qs3000 __devinitdata = NULL;
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id elsa_ids[] __devinitdata = {
+	{ ISAPNP_VENDOR('E', 'L', 'S'), ISAPNP_FUNCTION(0x0133),
+	  ISAPNP_VENDOR('E', 'L', 'S'), ISAPNP_FUNCTION(0x0133), 
+	  (unsigned long) "Elsa QS1000" },
+	{ ISAPNP_VENDOR('E', 'L', 'S'), ISAPNP_FUNCTION(0x0134),
+	  ISAPNP_VENDOR('E', 'L', 'S'), ISAPNP_FUNCTION(0x0134), 
+	  (unsigned long) "Elsa QS3000" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &elsa_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit
+setup_elsa(struct IsdnCard *card)
+{
+	int bytecnt;
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, Elsa_revision);
+	printk(KERN_INFO "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp));
+	cs->hw.elsa.ctrl_reg = 0;
+	cs->hw.elsa.status = 0;
+	cs->hw.elsa.MFlag = 0;
+	cs->subtyp = 0;
+	if (cs->typ == ISDN_CTYPE_ELSA) {
+		cs->hw.elsa.base = card->para[0];
+		printk(KERN_INFO "Elsa: Microlink IO probing\n");
+		if (cs->hw.elsa.base) {
+			if (!(cs->subtyp = probe_elsa_adr(cs->hw.elsa.base,
+							  cs->typ))) {
+				printk(KERN_WARNING
+				       "Elsa: no Elsa Microlink at %#lx\n",
+				       cs->hw.elsa.base);
+				return (0);
+			}
+		} else
+			cs->hw.elsa.base = probe_elsa(cs);
+		if (cs->hw.elsa.base) {
+			cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG;
+			cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL;
+			cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE;
+			cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC;
+			cs->hw.elsa.itac = cs->hw.elsa.base + ELSA_ITAC;
+			cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+			cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ;
+			cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER;
+			val = bytein(cs->hw.elsa.cfg);
+			if (cs->subtyp == ELSA_PC) {
+				const u_char CARD_IrqTab[8] =
+				{7, 3, 5, 9, 0, 0, 0, 0};
+				cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PC) >> 2];
+			} else if (cs->subtyp == ELSA_PCC8) {
+				const u_char CARD_IrqTab[8] =
+				{7, 3, 5, 9, 0, 0, 0, 0};
+				cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PCC8) >> 4];
+			} else {
+				const u_char CARD_IrqTab[8] =
+				{15, 10, 15, 3, 11, 5, 11, 9};
+				cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX) >> 3];
+			}
+			val = bytein(cs->hw.elsa.ale) & ELSA_HW_RELEASE;
+			if (val < 3)
+				val |= 8;
+			val += 'A' - 3;
+			if (val == 'B' || val == 'C')
+				val ^= 1;
+			if ((cs->subtyp == ELSA_PCFPRO) && (val = 'G'))
+				val = 'C';
+			printk(KERN_INFO
+			       "Elsa: %s found at %#lx Rev.:%c IRQ %d\n",
+			       Elsa_Types[cs->subtyp],
+			       cs->hw.elsa.base,
+			       val, cs->irq);
+			val = bytein(cs->hw.elsa.ale) & ELSA_S0_POWER_BAD;
+			if (val) {
+				printk(KERN_WARNING
+				   "Elsa: Microlink S0 bus power bad\n");
+				cs->hw.elsa.status |= ELSA_BAD_PWR;
+			}
+		} else {
+			printk(KERN_WARNING
+			       "No Elsa Microlink found\n");
+			return (0);
+		}
+	} else if (cs->typ == ISDN_CTYPE_ELSA_PNP) {
+#ifdef __ISAPNP__
+		if (!card->para[1] && isapnp_present()) {
+			struct pnp_dev *pnp_d;
+			while(ipid->card_vendor) {
+				if ((pnp_c = pnp_find_card(ipid->card_vendor,
+					ipid->card_device, pnp_c))) {
+					pnp_d = NULL;
+					if ((pnp_d = pnp_find_dev(pnp_c,
+						ipid->vendor, ipid->function, pnp_d))) {
+						int err;
+
+						printk(KERN_INFO "HiSax: %s detected\n",
+							(char *)ipid->driver_data);
+						pnp_disable_dev(pnp_d);
+						err = pnp_activate_dev(pnp_d);
+						if (err<0) {
+							printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+								__FUNCTION__, err);
+							return(0);
+						}
+						card->para[1] = pnp_port_start(pnp_d, 0);
+						card->para[0] = pnp_irq(pnp_d, 0);
+
+						if (!card->para[0] || !card->para[1]) {
+							printk(KERN_ERR "Elsa PnP:some resources are missing %ld/%lx\n",
+								card->para[0], card->para[1]);
+							pnp_disable_dev(pnp_d);
+							return(0);
+						}
+						if (ipid->function == ISAPNP_FUNCTION(0x133))
+							cs->subtyp = ELSA_QS1000;
+						else
+							cs->subtyp = ELSA_QS3000;
+						break;
+					} else {
+						printk(KERN_ERR "Elsa PnP: PnP error card found, no device\n");
+						return(0);
+					}
+				}
+				ipid++;
+				pnp_c=NULL;
+			} 
+			if (!ipid->card_vendor) {
+				printk(KERN_INFO "Elsa PnP: no ISAPnP card found\n");
+				return(0);
+			}
+		}
+#endif
+		if (card->para[1] && card->para[0]) { 
+			cs->hw.elsa.base = card->para[1];
+			cs->irq = card->para[0];
+			if (!cs->subtyp)
+				cs->subtyp = ELSA_QS1000;
+		} else {
+			printk(KERN_ERR "Elsa PnP: no parameter\n");
+		}
+		cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG;
+		cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE;
+		cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC;
+		cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+		cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ;
+		cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER;
+		cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL;
+		printk(KERN_INFO
+		       "Elsa: %s defined at %#lx IRQ %d\n",
+		       Elsa_Types[cs->subtyp],
+		       cs->hw.elsa.base,
+		       cs->irq);
+	} else if (cs->typ == ISDN_CTYPE_ELSA_PCMCIA) {
+		cs->hw.elsa.base = card->para[1];
+		cs->irq = card->para[0];
+		val = readreg(cs->hw.elsa.base + 0, cs->hw.elsa.base + 2, IPAC_ID);
+		if ((val == 1) || (val == 2)) { /* IPAC version 1.1/1.2 */
+			cs->subtyp = ELSA_PCMCIA_IPAC;
+			cs->hw.elsa.ale = cs->hw.elsa.base + 0;
+			cs->hw.elsa.isac = cs->hw.elsa.base + 2;
+			cs->hw.elsa.hscx = cs->hw.elsa.base + 2;
+			test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		} else {
+			cs->subtyp = ELSA_PCMCIA;
+			cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM;
+			cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM;
+			cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+		}
+		cs->hw.elsa.timer = 0;
+		cs->hw.elsa.trig = 0;
+		cs->hw.elsa.ctrl = 0;
+		cs->irq_flags |= SA_SHIRQ;
+		printk(KERN_INFO
+		       "Elsa: %s defined at %#lx IRQ %d\n",
+		       Elsa_Types[cs->subtyp],
+		       cs->hw.elsa.base,
+		       cs->irq);
+	} else if (cs->typ == ISDN_CTYPE_ELSA_PCI) {
+#ifdef CONFIG_PCI
+		cs->subtyp = 0;
+		if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ID_ELSA,
+			PCI_DEVICE_ID_ELSA_MICROLINK, dev_qs1000))) {
+			if (pci_enable_device(dev_qs1000))
+				return(0);
+			cs->subtyp = ELSA_QS1000PCI;
+			cs->irq = dev_qs1000->irq;
+			cs->hw.elsa.cfg = pci_resource_start(dev_qs1000, 1);
+			cs->hw.elsa.base = pci_resource_start(dev_qs1000, 3);
+		} else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ID_ELSA,
+			PCI_DEVICE_ID_ELSA_QS3000, dev_qs3000))) {
+			if (pci_enable_device(dev_qs3000))
+				return(0);
+			cs->subtyp = ELSA_QS3000PCI;
+			cs->irq = dev_qs3000->irq;
+			cs->hw.elsa.cfg = pci_resource_start(dev_qs3000, 1);
+			cs->hw.elsa.base = pci_resource_start(dev_qs3000, 3);
+		} else {
+			printk(KERN_WARNING "Elsa: No PCI card found\n");
+			return(0);
+		}
+		if (!cs->irq) {
+			printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n");
+			return(0);
+		}
+
+		if (!(cs->hw.elsa.base && cs->hw.elsa.cfg)) {
+			printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n");
+			return(0);
+		}
+		if ((cs->hw.elsa.cfg & 0xff) || (cs->hw.elsa.base & 0xf)) {
+			printk(KERN_WARNING "Elsa: You may have a wrong PCI bios\n");
+			printk(KERN_WARNING "Elsa: If your system hangs now, read\n");
+			printk(KERN_WARNING "Elsa: Documentation/isdn/README.HiSax\n");
+		}
+		cs->hw.elsa.ale  = cs->hw.elsa.base;
+		cs->hw.elsa.isac = cs->hw.elsa.base +1;
+		cs->hw.elsa.hscx = cs->hw.elsa.base +1; 
+		test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		cs->hw.elsa.timer = 0;
+		cs->hw.elsa.trig  = 0;
+		cs->irq_flags |= SA_SHIRQ;
+		printk(KERN_INFO
+		       "Elsa: %s defined at %#lx/0x%x IRQ %d\n",
+		       Elsa_Types[cs->subtyp],
+		       cs->hw.elsa.base,
+		       cs->hw.elsa.cfg,
+		       cs->irq);
+#else
+		printk(KERN_WARNING "Elsa: Elsa PCI and NO_PCI_BIOS\n");
+		printk(KERN_WARNING "Elsa: unable to config Elsa PCI\n");
+		return (0);
+#endif /* CONFIG_PCI */
+	} else 
+		return (0);
+
+	switch (cs->subtyp) {
+		case ELSA_PC:
+		case ELSA_PCC8:
+		case ELSA_PCC16:
+		case ELSA_QS1000:
+		case ELSA_PCMCIA:
+		case ELSA_PCMCIA_IPAC:
+			bytecnt = 8;
+			break;
+		case ELSA_PCFPRO:
+		case ELSA_PCF:
+		case ELSA_QS3000:
+		case ELSA_QS3000PCI:
+			bytecnt = 16;
+			break;
+		case ELSA_QS1000PCI:
+			bytecnt = 2;
+			break;
+		default:
+			printk(KERN_WARNING
+			       "Unknown ELSA subtype %d\n", cs->subtyp);
+			return (0);
+	}
+	/* In case of the elsa pcmcia card, this region is in use,
+	   reserved for us by the card manager. So we do not check it
+	   here, it would fail. */
+	if (cs->typ != ISDN_CTYPE_ELSA_PCMCIA && !request_region(cs->hw.elsa.base, bytecnt, "elsa isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %#lx-%#lx already in use\n",
+		       CardType[card->typ],
+		       cs->hw.elsa.base,
+		       cs->hw.elsa.base + bytecnt);
+		return (0);
+	}
+	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
+		if (!request_region(cs->hw.elsa.cfg, 0x80, "elsa isdn pci")) {
+			printk(KERN_WARNING
+			       "HiSax: %s pci port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.elsa.cfg,
+				cs->hw.elsa.cfg + 0x80);
+			release_region(cs->hw.elsa.base, bytecnt);
+			return (0);
+		}
+	}
+#if ARCOFI_USE
+	init_arcofi(cs);
+#endif
+	setup_isac(cs);
+	cs->hw.elsa.tl.function = (void *) elsa_led_handler;
+	cs->hw.elsa.tl.data = (long) cs;
+	init_timer(&cs->hw.elsa.tl);
+	/* Teste Timer */
+	if (cs->hw.elsa.timer) {
+		byteout(cs->hw.elsa.trig, 0xff);
+		byteout(cs->hw.elsa.timer, 0);
+		if (!TimerRun(cs)) {
+			byteout(cs->hw.elsa.timer, 0);	/* 2. Versuch */
+			if (!TimerRun(cs)) {
+				printk(KERN_WARNING
+				       "Elsa: timer do not start\n");
+				release_io_elsa(cs);
+				return (0);
+			}
+		}
+		HZDELAY((HZ/100) + 1);	/* wait >=10 ms */
+		if (TimerRun(cs)) {
+			printk(KERN_WARNING "Elsa: timer do not run down\n");
+			release_io_elsa(cs);
+			return (0);
+		}
+		printk(KERN_INFO "Elsa: timer OK; resetting card\n");
+	}
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Elsa_card_msg;
+	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI) || (cs->subtyp == ELSA_PCMCIA_IPAC)) {
+		cs->readisac = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		cs->irq_func = &elsa_interrupt_ipac;
+		val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ID);
+		printk(KERN_INFO "Elsa: IPAC version %x\n", val);
+	} else {
+		cs->readisac = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		cs->irq_func = &elsa_interrupt;
+		ISACVersion(cs, "Elsa:");
+		if (HscxVersion(cs, "Elsa:")) {
+			printk(KERN_WARNING
+				"Elsa: wrong HSCX versions check IO address\n");
+			release_io_elsa(cs);
+			return (0);
+		}
+	}
+	if (cs->subtyp == ELSA_PC) {
+		val = readitac(cs, ITAC_SYS);
+		printk(KERN_INFO "Elsa: ITAC version %s\n", ITACVer[val & 7]);
+		writeitac(cs, ITAC_ISEN, 0);
+		writeitac(cs, ITAC_RFIE, 0);
+		writeitac(cs, ITAC_XFIE, 0);
+		writeitac(cs, ITAC_SCIE, 0);
+		writeitac(cs, ITAC_STIE, 0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c
new file mode 100644
index 000000000000..bfc013225f46
--- /dev/null
+++ b/drivers/isdn/hisax/elsa_cs.c
@@ -0,0 +1,532 @@
+/*======================================================================
+
+    An elsa_cs PCMCIA client driver
+
+    This driver is for the Elsa PCM ISDN Cards, i.e. the MicroLink
+
+
+    The contents of this file are subject to the Mozilla Public
+    License Version 1.1 (the "License"); you may not use this file
+    except in compliance with the License. You may obtain a copy of
+    the License at http://www.mozilla.org/MPL/
+
+    Software distributed under the License is distributed on an "AS
+    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+    implied. See the License for the specific language governing
+    rights and limitations under the License.
+
+    The initial developer of the original code is David A. Hinds
+    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Modifications from dummy_cs.c are Copyright (C) 1999-2001 Klaus
+    Lichtenwalder <Lichtenwalder@ACM.org>. All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU General Public License version 2 (the "GPL"), in
+    which case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the MPL, indicate your decision
+    by deleting the provisions above and replace them with the notice
+    and other provisions required by the GPL.  If you do not delete
+    the provisions above, a recipient may use your version of this
+    file under either the MPL or the GPL.
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include "hisax_cfg.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Elsa PCM cards");
+MODULE_AUTHOR("Klaus Lichtenwalder");
+MODULE_LICENSE("Dual MPL/GPL");
+
+/*
+   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
+   you do not define PCMCIA_DEBUG at all, all the debug code will be
+   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
+   be present but disabled -- but it can then be enabled for specific
+   modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
+static char *version =
+"elsa_cs.c $Revision: 1.2.2.4 $ $Date: 2004/01/25 15:07:06 $ (K.Lichtenwalder)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int protocol = 2;        /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+
+/*====================================================================*/
+
+/*
+   The event() function is this driver's Card Services event handler.
+   It will be called by Card Services when an appropriate card status
+   event is received.  The config() and release() entry points are
+   used to configure or release a socket, in response to card insertion
+   and ejection events.  They are invoked from the elsa_cs event
+   handler.
+*/
+
+static void elsa_cs_config(dev_link_t *link);
+static void elsa_cs_release(dev_link_t *link);
+static int elsa_cs_event(event_t event, int priority,
+                          event_callback_args_t *args);
+
+/*
+   The attach() and detach() entry points are used to create and destroy
+   "instances" of the driver, where each instance represents everything
+   needed to manage one actual PCMCIA card.
+*/
+
+static dev_link_t *elsa_cs_attach(void);
+static void elsa_cs_detach(dev_link_t *);
+
+/*
+   The dev_info variable is the "key" that is used to match up this
+   device driver with appropriate cards, through the card configuration
+   database.
+*/
+
+static dev_info_t dev_info = "elsa_cs";
+
+/*
+   A linked list of "instances" of the elsa_cs device.  Each actual
+   PCMCIA card corresponds to one device instance, and is described
+   by one dev_link_t structure (defined in ds.h).
+
+   You may not want to use a linked list for this -- for example, the
+   memory card driver uses an array of dev_link_t pointers, where minor
+   device numbers are used to derive the corresponding array index.
+*/
+
+static dev_link_t *dev_list = NULL;
+
+/*
+   A dev_link_t structure has fields for most things that are needed
+   to keep track of a socket, but there will usually be some device
+   specific information that also needs to be kept track of.  The
+   'priv' pointer in a dev_link_t structure can be used to point to
+   a device-specific private data structure, like this.
+
+   To simplify the data structure handling, we actually include the
+   dev_link_t structure in the device's private data structure.
+
+   A driver needs to provide a dev_node_t structure for each device
+   on a card.  In some cases, there is only one device per card (for
+   example, ethernet cards, modems).  In other cases, there may be
+   many actual or logical devices (SCSI adapters, memory cards with
+   multiple partitions).  The dev_node_t structures need to be kept
+   in a linked list starting at the 'dev' field of a dev_link_t
+   structure.  We allocate them in the card's private data structure,
+   because they generally shouldn't be allocated dynamically.
+   In this case, we also provide a flag to indicate if a device is
+   "stopped" due to a power management event, or card ejection.  The
+   device IO routines can use a flag like this to throttle IO to a
+   card that is not ready to accept it.
+*/
+
+typedef struct local_info_t {
+    dev_link_t          link;
+    dev_node_t          node;
+    int                 busy;
+    int			cardnr;
+} local_info_t;
+
+/*======================================================================
+
+    elsa_cs_attach() creates an "instance" of the driver, allocatingx
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+    The dev_link structure is initialized, but we don't actually
+    configure the card at this point -- we wait until we receive a
+    card insertion event.
+
+======================================================================*/
+
+static dev_link_t *elsa_cs_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    local_info_t *local;
+    int ret;
+
+    DEBUG(0, "elsa_cs_attach()\n");
+
+    /* Allocate space for private device-specific data */
+    local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+    if (!local) return NULL;
+    memset(local, 0, sizeof(local_info_t));
+    local->cardnr = -1;
+    link = &local->link; link->priv = local;
+
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID|IRQ_SHARE_ID;
+    link->irq.Handler = NULL;
+
+    /*
+      General socket configuration defaults can go here.  In this
+      client, we assume very little, and rely on the CIS for almost
+      everything.  In most clients, many details (i.e., number, sizes,
+      and attributes of IO windows) are fixed by the nature of the
+      device, and can be hard-wired here.
+    */
+    link->io.NumPorts1 = 8;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 3;
+
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+        CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+        CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+        CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &elsa_cs_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != CS_SUCCESS) {
+        cs_error(link->handle, RegisterClient, ret);
+        elsa_cs_detach(link);
+        return NULL;
+    }
+
+    return link;
+} /* elsa_cs_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void elsa_cs_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+    local_info_t *info = link->priv;
+    int ret;
+
+    DEBUG(0, "elsa_cs_detach(0x%p)\n", link);
+
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+        if (*linkp == link) break;
+    if (*linkp == NULL)
+        return;
+
+    if (link->state & DEV_CONFIG)
+        elsa_cs_release(link);
+
+    /* Break the link with Card Services */
+    if (link->handle) {
+        ret = pcmcia_deregister_client(link->handle);
+	if (ret != CS_SUCCESS)
+	    cs_error(link->handle, DeregisterClient, ret);
+    }
+
+    /* Unlink device structure and free it */
+    *linkp = link->next;
+    kfree(info);
+
+} /* elsa_cs_detach */
+
+/*======================================================================
+
+    elsa_cs_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    device available to the system.
+
+======================================================================*/
+static int get_tuple(client_handle_t handle, tuple_t *tuple,
+                     cisparse_t *parse)
+{
+    int i = pcmcia_get_tuple_data(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return pcmcia_parse_tuple(handle, tuple, parse);
+}
+
+static int first_tuple(client_handle_t handle, tuple_t *tuple,
+                     cisparse_t *parse)
+{
+    int i = pcmcia_get_first_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static int next_tuple(client_handle_t handle, tuple_t *tuple,
+                     cisparse_t *parse)
+{
+    int i = pcmcia_get_next_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static void elsa_cs_config(dev_link_t *link)
+{
+    client_handle_t handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    local_info_t *dev;
+    int i, j, last_fn;
+    u_short buf[128];
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+    IsdnCard_t icard;
+
+    DEBUG(0, "elsa_config(0x%p)\n", link);
+    handle = link->handle;
+    dev = link->priv;
+
+    /*
+       This reads the card's CONFIG tuple to find its configuration
+       registers.
+    */
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleDataMax = 255;
+    tuple.TupleOffset = 0;
+    tuple.Attributes = 0;
+    i = first_tuple(handle, &tuple, &parse);
+    if (i != CS_SUCCESS) {
+        last_fn = ParseTuple;
+	goto cs_failed;
+    }
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present = parse.config.rmask[0];
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+    tuple.Attributes = 0;
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    i = first_tuple(handle, &tuple, &parse);
+    while (i == CS_SUCCESS) {
+        if ( (cf->io.nwin > 0) && cf->io.win[0].base) {
+            printk(KERN_INFO "(elsa_cs: looks like the 96 model)\n");
+            link->conf.ConfigIndex = cf->index;
+            link->io.BasePort1 = cf->io.win[0].base;
+            i = pcmcia_request_io(link->handle, &link->io);
+            if (i == CS_SUCCESS) break;
+        } else {
+          printk(KERN_INFO "(elsa_cs: looks like the 97 model)\n");
+          link->conf.ConfigIndex = cf->index;
+          for (i = 0, j = 0x2f0; j > 0x100; j -= 0x10) {
+            link->io.BasePort1 = j;
+            i = pcmcia_request_io(link->handle, &link->io);
+            if (i == CS_SUCCESS) break;
+          }
+          break;
+        }
+        i = next_tuple(handle, &tuple, &parse);
+    }
+
+    if (i != CS_SUCCESS) {
+	last_fn = RequestIO;
+	goto cs_failed;
+    }
+
+    i = pcmcia_request_irq(link->handle, &link->irq);
+    if (i != CS_SUCCESS) {
+        link->irq.AssignedIRQ = 0;
+	last_fn = RequestIRQ;
+        goto cs_failed;
+    }
+
+    i = pcmcia_request_configuration(link->handle, &link->conf);
+    if (i != CS_SUCCESS) {
+      last_fn = RequestConfiguration;
+      goto cs_failed;
+    }
+
+    /* At this point, the dev_node_t structure(s) should be
+       initialized and arranged in a linked list at link->dev. *//*  */
+    sprintf(dev->node.dev_name, "elsa");
+    dev->node.major = dev->node.minor = 0x0;
+
+    link->dev = &dev->node;
+
+    /* Finally, report what we've done */
+    printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
+           dev->node.dev_name, link->conf.ConfigIndex,
+           link->conf.Vcc/10, link->conf.Vcc%10);
+    if (link->conf.Vpp1)
+        printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+    if (link->conf.Attributes & CONF_ENABLE_IRQ)
+        printk(", irq %d", link->irq.AssignedIRQ);
+    if (link->io.NumPorts1)
+        printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+               link->io.BasePort1+link->io.NumPorts1-1);
+    if (link->io.NumPorts2)
+        printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+               link->io.BasePort2+link->io.NumPorts2-1);
+    printk("\n");
+
+    link->state &= ~DEV_CONFIG_PENDING;
+
+    icard.para[0] = link->irq.AssignedIRQ;
+    icard.para[1] = link->io.BasePort1;
+    icard.protocol = protocol;
+    icard.typ = ISDN_CTYPE_ELSA_PCMCIA;
+    
+    i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
+    if (i < 0) {
+    	printk(KERN_ERR "elsa_cs: failed to initialize Elsa PCMCIA %d at i/o %#x\n",
+    		i, link->io.BasePort1);
+    	elsa_cs_release(link);
+    } else
+    	((local_info_t*)link->priv)->cardnr = i;
+
+    return;
+cs_failed:
+    cs_error(link->handle, last_fn, i);
+    elsa_cs_release(link);
+} /* elsa_cs_config */
+
+/*======================================================================
+
+    After a card is removed, elsa_cs_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void elsa_cs_release(dev_link_t *link)
+{
+    local_info_t *local = link->priv;
+
+    DEBUG(0, "elsa_cs_release(0x%p)\n", link);
+
+    if (local) {
+    	if (local->cardnr >= 0) {
+    	    /* no unregister function with hisax */
+	    HiSax_closecard(local->cardnr);
+	}
+    }
+    /* Unlink the device chain */
+    link->dev = NULL;
+
+    /* Don't bother checking to see if these succeed or not */
+    if (link->win)
+        pcmcia_release_window(link->win);
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+    link->state &= ~DEV_CONFIG;
+} /* elsa_cs_release */
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+    When a CARD_REMOVAL event is received, we immediately set a flag
+    to block future accesses to this device.  All the functions that
+    actually access the device should check this flag to make sure
+    the card is still present.
+
+======================================================================*/
+
+static int elsa_cs_event(event_t event, int priority,
+                          event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    local_info_t *dev = link->priv;
+
+    DEBUG(1, "elsa_cs_event(%d)\n", event);
+
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+        link->state &= ~DEV_PRESENT;
+        if (link->state & DEV_CONFIG) {
+            ((local_info_t*)link->priv)->busy = 1;
+	    elsa_cs_release(link);
+        }
+        break;
+    case CS_EVENT_CARD_INSERTION:
+        link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+        elsa_cs_config(link);
+        break;
+    case CS_EVENT_PM_SUSPEND:
+        link->state |= DEV_SUSPEND;
+        /* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+        /* Mark the device as stopped, to block IO until later */
+        dev->busy = 1;
+        if (link->state & DEV_CONFIG)
+            pcmcia_release_configuration(link->handle);
+        break;
+    case CS_EVENT_PM_RESUME:
+        link->state &= ~DEV_SUSPEND;
+        /* Fall through... */
+    case CS_EVENT_CARD_RESET:
+        if (link->state & DEV_CONFIG)
+            pcmcia_request_configuration(link->handle, &link->conf);
+        dev->busy = 0;
+        break;
+    }
+    return 0;
+} /* elsa_cs_event */
+
+static struct pcmcia_driver elsa_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "elsa_cs",
+	},
+	.attach		= elsa_cs_attach,
+	.detach		= elsa_cs_detach,
+};
+
+static int __init init_elsa_cs(void)
+{
+	return pcmcia_register_driver(&elsa_cs_driver);
+}
+
+static void __exit exit_elsa_cs(void)
+{
+	pcmcia_unregister_driver(&elsa_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_elsa_cs);
+module_exit(exit_elsa_cs);
diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c
new file mode 100644
index 000000000000..689c83395693
--- /dev/null
+++ b/drivers/isdn/hisax/elsa_ser.c
@@ -0,0 +1,657 @@
+/* $Id: elsa_ser.c,v 2.14.2.3 2004/02/11 13:21:33 keil Exp $
+ *
+ * stuff for the serial modem on ELSA cards
+ *
+ * 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/serial.h>
+#include <linux/serial_reg.h>
+
+#define MAX_MODEM_BUF	256
+#define WAKEUP_CHARS	(MAX_MODEM_BUF/2)
+#define RS_ISR_PASS_LIMIT 256
+#define BASE_BAUD ( 1843200 / 16 )
+
+//#define SERIAL_DEBUG_OPEN 1
+//#define SERIAL_DEBUG_INTR 1
+//#define SERIAL_DEBUG_FLOW 1
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_REG
+//#define SERIAL_DEBUG_REG 1
+
+#ifdef SERIAL_DEBUG_REG
+static u_char deb[32];
+const char *ModemIn[] = {"RBR","IER","IIR","LCR","MCR","LSR","MSR","SCR"};
+const char *ModemOut[] = {"THR","IER","FCR","LCR","MCR","LSR","MSR","SCR"};
+#endif
+
+static char *MInit_1 = "AT&F&C1E0&D2\r\0";
+static char *MInit_2 = "ATL2M1S64=13\r\0";
+static char *MInit_3 = "AT+FCLASS=0\r\0";
+static char *MInit_4 = "ATV1S2=128X1\r\0";
+static char *MInit_5 = "AT\\V8\\N3\r\0";
+static char *MInit_6 = "ATL0M0&G0%E1\r\0";
+static char *MInit_7 = "AT%L1%M0%C3\r\0";
+
+static char *MInit_speed28800 = "AT%G0%B28800\r\0";
+
+static char *MInit_dialout = "ATs7=60 x1 d\r\0";
+static char *MInit_dialin = "ATs7=60 x1 a\r\0";
+
+
+static inline unsigned int serial_in(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+	u_int val = inb(cs->hw.elsa.base + 8 + offset);
+	debugl1(cs,"in   %s %02x",ModemIn[offset], val);
+	return(val);
+#else
+	return inb(cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+static inline unsigned int serial_inp(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	u_int val = inb(cs->hw.elsa.base + 8 + offset);
+	debugl1(cs,"inp  %s %02x",ModemIn[offset], val);
+#else
+	u_int val = inb_p(cs->hw.elsa.base + 8 + offset);
+	debugl1(cs,"inP  %s %02x",ModemIn[offset], val);
+#endif
+	return(val);
+#else
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	return inb(cs->hw.elsa.base + 8 + offset);
+#else
+	return inb_p(cs->hw.elsa.base + 8 + offset);
+#endif
+#endif
+}
+
+static inline void serial_out(struct IsdnCardState *cs, int offset, int value)
+{
+#ifdef SERIAL_DEBUG_REG
+	debugl1(cs,"out  %s %02x",ModemOut[offset], value);
+#endif
+	outb(value, cs->hw.elsa.base + 8 + offset);
+}
+
+static inline void serial_outp(struct IsdnCardState *cs, int offset,
+			       int value)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	debugl1(cs,"outp %s %02x",ModemOut[offset], value);
+#else
+	debugl1(cs,"outP %s %02x",ModemOut[offset], value);
+#endif
+#endif
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	outb(value, cs->hw.elsa.base + 8 + offset);
+#else
+    	outb_p(value, cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct IsdnCardState *cs, int baud)
+{
+	int	quot = 0, baud_base;
+	unsigned cval, fcr = 0;
+	int	bits;
+
+
+	/* byte size and parity */
+	cval = 0x03; bits = 10;
+	/* Determine divisor based on baud rate */
+	baud_base = BASE_BAUD;
+	quot = baud_base / baud;
+	/* If the quotient is ever zero, default to 9600 bps */
+	if (!quot)
+		quot = baud_base / 9600;
+
+	/* Set up FIFO's */
+	if ((baud_base / quot) < 2400)
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+	else
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+	serial_outp(cs, UART_FCR, fcr);
+	/* CTS flow control flag and modem status interrupts */
+	cs->hw.elsa.IER &= ~UART_IER_MSI;
+	cs->hw.elsa.IER |= UART_IER_MSI;
+	serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+
+	debugl1(cs,"modem quot=0x%x", quot);
+	serial_outp(cs, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+	serial_outp(cs, UART_DLL, quot & 0xff);		/* LS of divisor */
+	serial_outp(cs, UART_DLM, quot >> 8);		/* MS of divisor */
+	serial_outp(cs, UART_LCR, cval);		/* reset DLAB */
+	serial_inp(cs, UART_RX);
+}
+
+static int mstartup(struct IsdnCardState *cs)
+{
+	int	retval=0;
+
+	/*
+	 * Clear the FIFO buffers and disable them
+	 * (they will be reenabled in change_speed())
+	 */
+	serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+
+	/*
+	 * At this point there's no way the LSR could still be 0xFF;
+	 * if it is, then bail out, because there's likely no UART
+	 * here.
+	 */
+	if (serial_inp(cs, UART_LSR) == 0xff) {
+		retval = -ENODEV;
+		goto errout;
+	}
+	
+	/*
+	 * Clear the interrupt registers.
+	 */
+	(void) serial_inp(cs, UART_RX);
+	(void) serial_inp(cs, UART_IIR);
+	(void) serial_inp(cs, UART_MSR);
+
+	/*
+	 * Now, initialize the UART 
+	 */
+	serial_outp(cs, UART_LCR, UART_LCR_WLEN8);	/* reset DLAB */
+
+	cs->hw.elsa.MCR = 0;
+	cs->hw.elsa.MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+	serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+	
+	/*
+	 * Finally, enable interrupts
+	 */
+	cs->hw.elsa.IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+	serial_outp(cs, UART_IER, cs->hw.elsa.IER);	/* enable interrupts */
+	
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	(void)serial_inp(cs, UART_LSR);
+	(void)serial_inp(cs, UART_RX);
+	(void)serial_inp(cs, UART_IIR);
+	(void)serial_inp(cs, UART_MSR);
+
+	cs->hw.elsa.transcnt = cs->hw.elsa.transp = 0;
+	cs->hw.elsa.rcvcnt = cs->hw.elsa.rcvp =0;
+
+	/*
+	 * and set the speed of the serial port
+	 */
+	change_speed(cs, BASE_BAUD);
+	cs->hw.elsa.MFlag = 1;
+errout:
+	return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void mshutdown(struct IsdnCardState *cs)
+{
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk(KERN_DEBUG"Shutting down serial ....");
+#endif
+	
+	/*
+	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+	 * here so the queue might never be waken up
+	 */
+
+	cs->hw.elsa.IER = 0;
+	serial_outp(cs, UART_IER, 0x00);	/* disable all intrs */
+	cs->hw.elsa.MCR &= ~UART_MCR_OUT2;
+	
+	/* disable break condition */
+	serial_outp(cs, UART_LCR, serial_inp(cs, UART_LCR) & ~UART_LCR_SBC);
+	
+	cs->hw.elsa.MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
+	serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+
+	/* disable FIFO's */	
+	serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+	serial_inp(cs, UART_RX);    /* read data port to reset things */
+	
+#ifdef SERIAL_DEBUG_OPEN
+	printk(" done\n");
+#endif
+}
+
+inline int
+write_modem(struct BCState *bcs) {
+	int ret=0;
+	struct IsdnCardState *cs = bcs->cs;
+	int count, len, fp;
+	
+	if (!bcs->tx_skb)
+		return 0;
+	if (bcs->tx_skb->len <= 0)
+		return 0;
+	len = bcs->tx_skb->len;
+	if (len > MAX_MODEM_BUF - cs->hw.elsa.transcnt)
+		len = MAX_MODEM_BUF - cs->hw.elsa.transcnt;
+	fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+	fp &= (MAX_MODEM_BUF -1);
+	count = len;
+	if (count > MAX_MODEM_BUF - fp) {
+		count = MAX_MODEM_BUF - fp;
+		memcpy(cs->hw.elsa.transbuf + fp, bcs->tx_skb->data, count);
+		skb_pull(bcs->tx_skb, count);
+		cs->hw.elsa.transcnt += count;
+		ret = count;
+		count = len - count;
+		fp = 0;
+	}
+	memcpy((cs->hw.elsa.transbuf + fp), bcs->tx_skb->data, count);
+	skb_pull(bcs->tx_skb, count);
+	cs->hw.elsa.transcnt += count;
+	ret += count;
+	
+	if (cs->hw.elsa.transcnt && 
+	    !(cs->hw.elsa.IER & UART_IER_THRI)) {
+			cs->hw.elsa.IER |= UART_IER_THRI;
+		serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+	}
+	return(ret);
+}
+
+inline void
+modem_fill(struct BCState *bcs) {
+		
+	if (bcs->tx_skb) {
+		if (bcs->tx_skb->len) {
+			write_modem(bcs);
+			return;
+		} else {
+			if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+				(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+				u_long	flags;
+				spin_lock_irqsave(&bcs->aclock, flags);
+				bcs->ackcnt += bcs->hw.hscx.count;
+				spin_unlock_irqrestore(&bcs->aclock, flags);
+				schedule_event(bcs, B_ACKPENDING);
+			}
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+		}
+	}
+	if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+		bcs->hw.hscx.count = 0;
+		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+		write_modem(bcs);
+	} else {
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		schedule_event(bcs, B_XMTBUFREADY);
+	}
+}
+
+static inline void receive_chars(struct IsdnCardState *cs,
+				 int *status)
+{
+	unsigned char ch;
+	struct sk_buff *skb;
+
+	do {
+		ch = serial_in(cs, UART_RX);
+		if (cs->hw.elsa.rcvcnt >= MAX_MODEM_BUF)
+			break;
+		cs->hw.elsa.rcvbuf[cs->hw.elsa.rcvcnt++] = ch;
+#ifdef SERIAL_DEBUG_INTR
+		printk("DR%02x:%02x...", ch, *status);
+#endif
+		if (*status & (UART_LSR_BI | UART_LSR_PE |
+			       UART_LSR_FE | UART_LSR_OE)) {
+					
+#ifdef SERIAL_DEBUG_INTR
+			printk("handling exept....");
+#endif
+		}
+		*status = serial_inp(cs, UART_LSR);
+	} while (*status & UART_LSR_DR);
+	if (cs->hw.elsa.MFlag == 2) {
+		if (!(skb = dev_alloc_skb(cs->hw.elsa.rcvcnt)))
+			printk(KERN_WARNING "ElsaSER: receive out of memory\n");
+		else {
+			memcpy(skb_put(skb, cs->hw.elsa.rcvcnt), cs->hw.elsa.rcvbuf, 
+				cs->hw.elsa.rcvcnt);
+			skb_queue_tail(& cs->hw.elsa.bcs->rqueue, skb);
+		}
+		schedule_event(cs->hw.elsa.bcs, B_RCVBUFREADY);
+	} else {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "modem read cnt %d", cs->hw.elsa.rcvcnt);
+		QuickHex(t, cs->hw.elsa.rcvbuf, cs->hw.elsa.rcvcnt);
+		debugl1(cs, tmp);
+	}
+	cs->hw.elsa.rcvcnt = 0;
+}
+
+static inline void transmit_chars(struct IsdnCardState *cs, int *intr_done)
+{
+	int count;
+	
+	debugl1(cs, "transmit_chars: p(%x) cnt(%x)", cs->hw.elsa.transp, 
+		cs->hw.elsa.transcnt);
+	
+	if (cs->hw.elsa.transcnt <= 0) {
+		cs->hw.elsa.IER &= ~UART_IER_THRI;
+		serial_out(cs, UART_IER, cs->hw.elsa.IER);
+		return;
+	}
+	count = 16;
+	do {
+		serial_outp(cs, UART_TX, cs->hw.elsa.transbuf[cs->hw.elsa.transp++]);
+		if (cs->hw.elsa.transp >= MAX_MODEM_BUF)
+			cs->hw.elsa.transp=0;
+		if (--cs->hw.elsa.transcnt <= 0)
+			break;
+	} while (--count > 0);
+	if ((cs->hw.elsa.transcnt < WAKEUP_CHARS) && (cs->hw.elsa.MFlag==2))
+		modem_fill(cs->hw.elsa.bcs);
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("THRE...");
+#endif
+	if (intr_done)
+		*intr_done = 0;
+	if (cs->hw.elsa.transcnt <= 0) {
+		cs->hw.elsa.IER &= ~UART_IER_THRI;
+		serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+	}
+}
+
+
+static void rs_interrupt_elsa(int irq, struct IsdnCardState *cs)
+{
+	int status, iir, msr;
+	int pass_counter = 0;
+	
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt_single(%d)...", irq);
+#endif
+
+	do {
+		status = serial_inp(cs, UART_LSR);
+		debugl1(cs,"rs LSR %02x", status);
+#ifdef SERIAL_DEBUG_INTR
+		printk("status = %x...", status);
+#endif
+		if (status & UART_LSR_DR)
+			receive_chars(cs, &status);
+		if (status & UART_LSR_THRE)
+			transmit_chars(cs, NULL);
+		if (pass_counter++ > RS_ISR_PASS_LIMIT) {
+			printk("rs_single loop break.\n");
+			break;
+		}
+		iir = serial_inp(cs, UART_IIR);
+		debugl1(cs,"rs IIR %02x", iir);
+		if ((iir & 0xf) == 0) {
+			msr = serial_inp(cs, UART_MSR);
+			debugl1(cs,"rs MSR %02x", msr);
+		}
+	} while (!(iir & UART_IIR_NO_INT));
+#ifdef SERIAL_DEBUG_INTR
+	printk("end.\n");
+#endif
+}
+
+extern int open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs);
+extern void modehscx(struct BCState *bcs, int mode, int bc);
+extern void hscx_l2l1(struct PStack *st, int pr, void *arg);
+
+void
+close_elsastate(struct BCState *bcs)
+{
+	modehscx(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hscx.rcvbuf) {
+			if (bcs->mode != L1_MODE_MODEM)
+				kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+void
+modem_write_cmd(struct IsdnCardState *cs, u_char *buf, int len) {
+	int count, fp;
+	u_char *msg = buf;
+	
+	if (!len)
+		return;
+	if (len > (MAX_MODEM_BUF - cs->hw.elsa.transcnt)) {
+		return;
+	}
+	fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+	fp &= (MAX_MODEM_BUF -1);
+	count = len;
+	if (count > MAX_MODEM_BUF - fp) {
+		count = MAX_MODEM_BUF - fp;
+		memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+		cs->hw.elsa.transcnt += count;
+		msg += count;
+		count = len - count;
+		fp = 0;
+	}
+	memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+	cs->hw.elsa.transcnt += count;
+	if (cs->hw.elsa.transcnt && 
+	    !(cs->hw.elsa.IER & UART_IER_THRI)) {
+		cs->hw.elsa.IER |= UART_IER_THRI;
+		serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+	}
+}
+
+void
+modem_set_init(struct IsdnCardState *cs) {
+	int timeout;
+
+#define RCV_DELAY 20000	
+	modem_write_cmd(cs, MInit_1, strlen(MInit_1));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_2, strlen(MInit_2));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_3, strlen(MInit_3));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_4, strlen(MInit_4));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY );
+	modem_write_cmd(cs, MInit_5, strlen(MInit_5));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_6, strlen(MInit_6));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_7, strlen(MInit_7));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+}
+
+void
+modem_set_dial(struct IsdnCardState *cs, int outgoing) {
+	int timeout;
+#define RCV_DELAY 20000	
+
+	modem_write_cmd(cs, MInit_speed28800, strlen(MInit_speed28800));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	if (outgoing)
+		modem_write_cmd(cs, MInit_dialout, strlen(MInit_dialout));
+	else
+		modem_write_cmd(cs, MInit_dialin, strlen(MInit_dialin));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+}
+
+void
+modem_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	if (pr == (PH_DATA | REQUEST)) {
+		spin_lock_irqsave(&bcs->cs->lock, flags);
+		if (bcs->tx_skb) {
+			skb_queue_tail(&bcs->squeue, skb);
+		} else {
+			bcs->tx_skb = skb;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->hw.hscx.count = 0;
+			write_modem(bcs);
+		}
+		spin_unlock_irqrestore(&bcs->cs->lock, flags);
+	} else if (pr == (PH_ACTIVATE | REQUEST)) {
+		test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+		st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+		set_arcofi(bcs->cs, st->l1.bc);
+		mstartup(bcs->cs);
+		modem_set_dial(bcs->cs, test_bit(FLG_ORIG, &st->l2.flag));
+		bcs->cs->hw.elsa.MFlag=2;
+	} else if (pr == (PH_DEACTIVATE | REQUEST)) {
+		test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+		bcs->cs->dc.isac.arcofi_bc = st->l1.bc;
+		arcofi_fsm(bcs->cs, ARCOFI_START, &ARCOFI_XOP_0);
+		interruptible_sleep_on(&bcs->cs->dc.isac.arcofi_wait);
+		bcs->cs->hw.elsa.MFlag=1;
+	} else {
+		printk(KERN_WARNING"ElsaSer: unknown pr %x\n", pr);
+	}
+}
+
+int
+setstack_elsa(struct PStack *st, struct BCState *bcs)
+{
+
+	bcs->channel = st->l1.bc;
+	switch (st->l1.mode) {
+		case L1_MODE_HDLC:
+		case L1_MODE_TRANS:
+			if (open_hscxstate(st->l1.hardware, bcs))
+				return (-1);
+			st->l2.l2l1 = hscx_l2l1;
+			break;
+		case L1_MODE_MODEM:
+			bcs->mode = L1_MODE_MODEM;
+			if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+				bcs->hw.hscx.rcvbuf = bcs->cs->hw.elsa.rcvbuf;
+				skb_queue_head_init(&bcs->rqueue);
+				skb_queue_head_init(&bcs->squeue);
+			}
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->event = 0;
+			bcs->hw.hscx.rcvidx = 0;
+			bcs->tx_cnt = 0;
+			bcs->cs->hw.elsa.bcs = bcs;
+			st->l2.l2l1 = modem_l2l1;
+			break;
+	}
+	st->l1.bcs = bcs;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void
+init_modem(struct IsdnCardState *cs) {
+
+	cs->bcs[0].BC_SetStack = setstack_elsa;
+	cs->bcs[1].BC_SetStack = setstack_elsa;
+	cs->bcs[0].BC_Close = close_elsastate;
+	cs->bcs[1].BC_Close = close_elsastate;
+	if (!(cs->hw.elsa.rcvbuf = kmalloc(MAX_MODEM_BUF,
+		GFP_ATOMIC))) {
+		printk(KERN_WARNING
+			"Elsa: No modem mem hw.elsa.rcvbuf\n");
+		return;
+	}
+	if (!(cs->hw.elsa.transbuf = kmalloc(MAX_MODEM_BUF,
+		GFP_ATOMIC))) {
+		printk(KERN_WARNING
+			"Elsa: No modem mem hw.elsa.transbuf\n");
+		kfree(cs->hw.elsa.rcvbuf);
+		cs->hw.elsa.rcvbuf = NULL;
+		return;
+	}
+	if (mstartup(cs)) {
+		printk(KERN_WARNING "Elsa: problem startup modem\n");
+	}
+	modem_set_init(cs);
+}
+
+void
+release_modem(struct IsdnCardState *cs) {
+
+	cs->hw.elsa.MFlag = 0;
+	if (cs->hw.elsa.transbuf) {
+		if (cs->hw.elsa.rcvbuf) {
+			mshutdown(cs);
+			kfree(cs->hw.elsa.rcvbuf);
+			cs->hw.elsa.rcvbuf = NULL;
+		}
+		kfree(cs->hw.elsa.transbuf);
+		cs->hw.elsa.transbuf = NULL;
+	}
+}
diff --git a/drivers/isdn/hisax/enternow.h b/drivers/isdn/hisax/enternow.h
new file mode 100644
index 000000000000..ed2eec5874c5
--- /dev/null
+++ b/drivers/isdn/hisax/enternow.h
@@ -0,0 +1,51 @@
+/* 2001/10/02
+ *
+ * enternow.h   Header-file included by
+ *              enternow_pci.c
+ *
+ * Author       Christoph Ersfeld <info@formula-n.de>
+ *              Formula-n Europe AG (www.formula-n.com)
+ *              previously Gerdes AG
+ *
+ *
+ *              This file is (c) under GNU PUBLIC LICENSE
+ */
+
+
+/* ***************************************************************************************** *
+ * ****************************** datatypes and macros ************************************* *
+ * ***************************************************************************************** */
+
+#define BYTE							unsigned char
+#define WORD							unsigned int
+#define HIBYTE(w)						((unsigned char)((w & 0xff00) / 256))
+#define LOBYTE(w)						((unsigned char)(w & 0x00ff))
+#define InByte(addr)						inb(addr)
+#define OutByte(addr,val)					outb(val,addr)
+
+
+
+/* ***************************************************************************************** *
+ * *********************************** card-specific *************************************** *
+ * ***************************************************************************************** */
+
+/* für PowerISDN PCI */
+#define TJ_AMD_IRQ 						0x20
+#define TJ_LED1 						0x40
+#define TJ_LED2 						0x80
+
+
+/* Das Fenster zum AMD...
+ * Ab Adresse hw.njet.base + TJ_AMD_PORT werden vom AMD jeweils 8 Bit in
+ * den TigerJet i/o-Raum gemappt
+ * -> 0x01 des AMD bei hw.njet.base + 0C4 */
+#define TJ_AMD_PORT						0xC0
+
+
+
+/* ***************************************************************************************** *
+ * *************************************** Prototypen ************************************** *
+ * ***************************************************************************************** */
+
+BYTE ReadByteAmd7930(struct IsdnCardState *cs, BYTE offset);
+void WriteByteAmd7930(struct IsdnCardState *cs, BYTE offset, BYTE value);
diff --git a/drivers/isdn/hisax/enternow_pci.c b/drivers/isdn/hisax/enternow_pci.c
new file mode 100644
index 000000000000..1cc4d11e007a
--- /dev/null
+++ b/drivers/isdn/hisax/enternow_pci.c
@@ -0,0 +1,399 @@
+/* enternow_pci.c,v 0.99 2001/10/02
+ *
+ * enternow_pci.c       Card-specific routines for
+ *                      Formula-n enter:now ISDN PCI ab
+ *                      Gerdes AG Power ISDN PCI
+ *                      Woerltronic SA 16 PCI
+ *                      (based on HiSax driver by Karsten Keil)
+ *
+ * Author               Christoph Ersfeld <info@formula-n.de>
+ *                      Formula-n Europe AG (www.formula-n.com)
+ *                      previously Gerdes AG
+ *
+ *
+ *                      This file is (c) under GNU PUBLIC LICENSE
+ *
+ * Notes:
+ * This driver interfaces to netjet.c which performs B-channel
+ * processing.
+ *
+ * Version 0.99 is the first release of this driver and there are
+ * certainly a few bugs.
+ * It isn't testet on linux 2.4 yet, so consider this code to be
+ * beta.
+ *
+ * Please don't report me any malfunction without sending
+ * (compressed) debug-logs.
+ * It would be nearly impossible to retrace it.
+ *
+ * Log D-channel-processing as follows:
+ *
+ * 1. Load hisax with card-specific parameters, this example ist for
+ *    Formula-n enter:now ISDN PCI and compatible
+ *    (f.e. Gerdes Power ISDN PCI)
+ *
+ *    modprobe hisax type=41 protocol=2 id=gerdes
+ *
+ *    if you chose an other value for id, you need to modify the
+ *    code below, too.
+ *
+ * 2. set debug-level
+ *
+ *    hisaxctrl gerdes 1 0x3ff
+ *    hisaxctrl gerdes 11 0x4f
+ *    cat /dev/isdnctrl >> ~/log &
+ *
+ * Please take also a look into /var/log/messages if there is
+ * anything importand concerning HISAX.
+ *
+ *
+ * Credits:
+ * Programming the driver for Formula-n enter:now ISDN PCI and
+ * necessary the driver for the used Amd 7930 D-channel-controller
+ * was spnsored by Formula-n Europe AG.
+ * Thanks to Karsten Keil and Petr Novak, who gave me support in
+ * Hisax-specific questions.
+ * I want so say special thanks to Carl-Friedrich Braun, who had to
+ * answer a lot of questions about generally ISDN and about handling
+ * of the Amd-Chip.
+ *
+ */
+
+
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include "amd7930_fn.h"
+#include "enternow.h"
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include "netjet.h"
+
+
+
+const char *enternow_pci_rev = "$Revision: 1.1.4.5 $";
+
+
+/* *************************** I/O-Interface functions ************************************* */
+
+
+/* cs->readisac, macro rByteAMD */
+BYTE
+ReadByteAmd7930(struct IsdnCardState *cs, BYTE offset)
+{
+	/* direktes Register */
+	if(offset < 8)
+		return (InByte(cs->hw.njet.isac + 4*offset));
+
+	/* indirektes Register */
+	else {
+		OutByte(cs->hw.njet.isac + 4*AMD_CR, offset);
+		return(InByte(cs->hw.njet.isac + 4*AMD_DR));
+	}
+}
+
+/* cs->writeisac, macro wByteAMD */
+void
+WriteByteAmd7930(struct IsdnCardState *cs, BYTE offset, BYTE value)
+{
+	/* direktes Register */
+	if(offset < 8)
+		OutByte(cs->hw.njet.isac + 4*offset, value);
+
+	/* indirektes Register */
+	else {
+		OutByte(cs->hw.njet.isac + 4*AMD_CR, offset);
+		OutByte(cs->hw.njet.isac + 4*AMD_DR, value);
+	}
+}
+
+
+void
+enpci_setIrqMask(struct IsdnCardState *cs, BYTE val) {
+        if (!val)
+	        OutByte(cs->hw.njet.base+NETJET_IRQMASK1, 0x00);
+        else
+	        OutByte(cs->hw.njet.base+NETJET_IRQMASK1, TJ_AMD_IRQ);
+}
+
+
+static BYTE dummyrr(struct IsdnCardState *cs, int chan, BYTE off)
+{
+        return(5);
+}
+
+static void dummywr(struct IsdnCardState *cs, int chan, BYTE off, BYTE value)
+{
+
+}
+
+
+/* ******************************************************************************** */
+
+
+static void
+reset_enpci(struct IsdnCardState *cs)
+{
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "enter:now PCI: reset");
+
+	/* Reset on, (also for AMD) */
+	cs->hw.njet.ctrl_reg = 0x07;
+	OutByte(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	mdelay(20);
+	/* Reset off */
+	cs->hw.njet.ctrl_reg = 0x30;
+	OutByte(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	/* 20ms delay */
+	mdelay(20);
+	cs->hw.njet.auxd = 0;  // LED-status
+	cs->hw.njet.dmactrl = 0;
+	OutByte(cs->hw.njet.base + NETJET_AUXCTRL, ~TJ_AMD_IRQ);
+	OutByte(cs->hw.njet.base + NETJET_IRQMASK1, TJ_AMD_IRQ);
+	OutByte(cs->hw.njet.auxa, cs->hw.njet.auxd); // LED off
+}
+
+
+static int
+enpci_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+        BYTE *chan;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "enter:now PCI: card_msg: 0x%04X", mt);
+
+        switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_enpci(cs);
+                        Amd7930_init(cs);
+                        spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case CARD_RELEASE:
+			release_io_netjet(cs);
+			break;
+		case CARD_INIT:
+			reset_enpci(cs);
+			inittiger(cs);
+			/* irq must be on here */
+			Amd7930_init(cs);
+			break;
+		case CARD_TEST:
+			break;
+                case MDL_ASSIGN:
+                        /* TEI assigned, LED1 on */
+                        cs->hw.njet.auxd = TJ_AMD_IRQ << 1;
+                        OutByte(cs->hw.njet.base + NETJET_AUXDATA, cs->hw.njet.auxd);
+                        break;
+                case MDL_REMOVE:
+                        /* TEI removed, LEDs off */
+	                cs->hw.njet.auxd = 0;
+                        OutByte(cs->hw.njet.base + NETJET_AUXDATA, 0x00);
+                        break;
+                case MDL_BC_ASSIGN:
+                        /* activate B-channel */
+                        chan = (BYTE *)arg;
+
+                        if (cs->debug & L1_DEB_ISAC)
+		                debugl1(cs, "enter:now PCI: assign phys. BC %d in AMD LMR1", *chan);
+
+                        cs->dc.amd7930.ph_command(cs, (cs->dc.amd7930.lmr1 | (*chan + 1)), "MDL_BC_ASSIGN");
+                        /* at least one b-channel in use, LED 2 on */
+                        cs->hw.njet.auxd |= TJ_AMD_IRQ << 2;
+                        OutByte(cs->hw.njet.base + NETJET_AUXDATA, cs->hw.njet.auxd);
+                        break;
+                case MDL_BC_RELEASE:
+                        /* deactivate B-channel */
+                        chan = (BYTE *)arg;
+
+                        if (cs->debug & L1_DEB_ISAC)
+		                debugl1(cs, "enter:now PCI: release phys. BC %d in Amd LMR1", *chan);
+
+                        cs->dc.amd7930.ph_command(cs, (cs->dc.amd7930.lmr1 & ~(*chan + 1)), "MDL_BC_RELEASE");
+                        /* no b-channel active -> LED2 off */
+                        if (!(cs->dc.amd7930.lmr1 & 3)) {
+                                cs->hw.njet.auxd &= ~(TJ_AMD_IRQ << 2);
+                                OutByte(cs->hw.njet.base + NETJET_AUXDATA, cs->hw.njet.auxd);
+                        }
+                        break;
+                default:
+                        break;
+
+	}
+	return(0);
+}
+
+static irqreturn_t
+enpci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	BYTE s0val, s1val, ir;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	s1val = InByte(cs->hw.njet.base + NETJET_IRQSTAT1);
+
+        /* AMD threw an interrupt */
+	if (!(s1val & TJ_AMD_IRQ)) {
+                /* read and clear interrupt-register */
+		ir = ReadByteAmd7930(cs, 0x00);
+		Amd7930_interrupt(cs, ir);
+		s1val = 1;
+	} else
+		s1val = 0;
+	s0val = InByte(cs->hw.njet.base + NETJET_IRQSTAT0);
+	if ((s0val | s1val)==0) { // shared IRQ
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	} 
+	if (s0val)
+		OutByte(cs->hw.njet.base + NETJET_IRQSTAT0, s0val);
+
+	/* DMA-Interrupt: B-channel-stuff */
+	/* set bits in sval to indicate which page is free */
+	if (inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR) <
+		inl(cs->hw.njet.base + NETJET_DMA_WRITE_IRQ))
+		/* the 2nd write page is free */
+		s0val = 0x08;
+	else	/* the 1st write page is free */
+		s0val = 0x04;
+	if (inl(cs->hw.njet.base + NETJET_DMA_READ_ADR) <
+		inl(cs->hw.njet.base + NETJET_DMA_READ_IRQ))
+		/* the 2nd read page is free */
+		s0val = s0val | 0x02;
+	else	/* the 1st read page is free */
+		s0val = s0val | 0x01;
+	if (s0val != cs->hw.njet.last_is0) /* we have a DMA interrupt */
+	{
+		if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_HANDLED;
+		}
+		cs->hw.njet.irqstat0 = s0val;
+		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) !=
+			(cs->hw.njet.last_is0 & NETJET_IRQM0_READ))
+			/* we have a read dma int */
+			read_tiger(cs);
+		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) !=
+			(cs->hw.njet.last_is0 & NETJET_IRQM0_WRITE))
+			/* we have a write dma int */
+			write_tiger(cs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+
+static struct pci_dev *dev_netjet __initdata = NULL;
+
+/* called by config.c */
+int __init
+setup_enternow_pci(struct IsdnCard *card)
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+#ifdef CONFIG_PCI
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+        strcpy(tmp, enternow_pci_rev);
+	printk(KERN_INFO "HiSax: Formula-n Europe AG enter:now ISDN PCI driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_ENTERNOW)
+		return(0);
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+
+	for ( ;; )
+	{
+		if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+			PCI_DEVICE_ID_TIGERJET_300,  dev_netjet))) {
+			if (pci_enable_device(dev_netjet))
+				return(0);
+			cs->irq = dev_netjet->irq;
+			if (!cs->irq) {
+				printk(KERN_WARNING "enter:now PCI: No IRQ for PCI card found\n");
+				return(0);
+			}
+			cs->hw.njet.base = pci_resource_start(dev_netjet, 0);
+			if (!cs->hw.njet.base) {
+				printk(KERN_WARNING "enter:now PCI: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+                        /* checks Sub-Vendor ID because system crashes with Traverse-Card */
+			if ((dev_netjet->subsystem_vendor != 0x55) ||
+				(dev_netjet->subsystem_device != 0x02)) {
+				printk(KERN_WARNING "enter:now: You tried to load this driver with an incompatible TigerJet-card\n");
+                                printk(KERN_WARNING "Use type=20 for Traverse NetJet PCI Card.\n");
+                                return(0);
+                        }
+		} else {
+                        printk(KERN_WARNING "enter:now PCI: No PCI card found\n");
+			return(0);
+		}
+
+		cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
+		cs->hw.njet.isac = cs->hw.njet.base + 0xC0; // Fenster zum AMD
+
+		/* Reset an */
+		cs->hw.njet.ctrl_reg = 0x07;  // geändert von 0xff
+		OutByte(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+		/* 20 ms Pause */
+		mdelay(20);
+
+		cs->hw.njet.ctrl_reg = 0x30;  /* Reset Off and status read clear */
+		OutByte(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+		mdelay(10);
+
+		cs->hw.njet.auxd = 0x00; // war 0xc0
+		cs->hw.njet.dmactrl = 0;
+
+		OutByte(cs->hw.njet.base + NETJET_AUXCTRL, ~TJ_AMD_IRQ);
+		OutByte(cs->hw.njet.base + NETJET_IRQMASK1, TJ_AMD_IRQ);
+		OutByte(cs->hw.njet.auxa, cs->hw.njet.auxd);
+
+		break;
+	}
+#else
+
+	printk(KERN_WARNING "enter:now PCI: NO_PCI_BIOS\n");
+	printk(KERN_WARNING "enter:now PCI: unable to config Formula-n enter:now ISDN PCI ab\n");
+	return (0);
+
+#endif /* CONFIG_PCI */
+
+	bytecnt = 256;
+
+	printk(KERN_INFO
+		"enter:now PCI: PCI card configured at 0x%lx IRQ %d\n",
+		cs->hw.njet.base, cs->irq);
+	if (!request_region(cs->hw.njet.base, bytecnt, "Fn_ISDN")) {
+		printk(KERN_WARNING
+			   "HiSax: %s config port %lx-%lx already in use\n",
+			   CardType[card->typ],
+			   cs->hw.njet.base,
+			   cs->hw.njet.base + bytecnt);
+		return (0);
+	}
+	setup_Amd7930(cs);
+	cs->hw.njet.last_is0 = 0;
+        /* macro rByteAMD */
+        cs->readisac = &ReadByteAmd7930;
+        /* macro wByteAMD */
+        cs->writeisac = &WriteByteAmd7930;
+        cs->dc.amd7930.setIrqMask = &enpci_setIrqMask;
+
+        cs->BC_Read_Reg  = &dummyrr;
+	cs->BC_Write_Reg = &dummywr;
+	cs->BC_Send_Data = &netjet_fill_dma;
+	cs->cardmsg = &enpci_card_msg;
+	cs->irq_func = &enpci_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+
+        return (1);
+}
diff --git a/drivers/isdn/hisax/fsm.c b/drivers/isdn/hisax/fsm.c
new file mode 100644
index 000000000000..0d44a3f480ac
--- /dev/null
+++ b/drivers/isdn/hisax/fsm.c
@@ -0,0 +1,163 @@
+/* $Id: fsm.c,v 1.14.6.4 2001/09/23 22:24:47 kai Exp $
+ *
+ * Finite state machine
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *              by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include "hisax.h"
+
+#define FSM_TIMER_DEBUG 0
+
+int
+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);
+	if (!fsm->jumpmatrix)
+		return -ENOMEM;
+
+	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 "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;
+	return 0;
+}
+
+void
+FsmFree(struct Fsm *fsm)
+{
+	kfree((void *) fsm->jumpmatrix);
+}
+
+int
+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 "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 routine",
+				fi->fsm->strState[fi->state],
+				fi->fsm->strEvent[event]);
+		return (!0);
+	}
+}
+
+void
+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
+	FsmEvent(ft->fi, ft->event, ft->arg);
+}
+
+void
+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, "FsmInitTimer %lx", (long) ft);
+#endif
+	init_timer(&ft->tl);
+}
+
+void
+FsmDelTimer(struct FsmTimer *ft, int where)
+{
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmDelTimer %lx %d", (long) ft, where);
+#endif
+	del_timer(&ft->tl);
+}
+
+int
+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, "FsmAddTimer %lx %d %d",
+			(long) ft, millisec, where);
+#endif
+
+	if (timer_pending(&ft->tl)) {
+		printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
+		ft->fi->printdebug(ft->fi, "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
+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, "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);
+}
diff --git a/drivers/isdn/hisax/fsm.h b/drivers/isdn/hisax/fsm.h
new file mode 100644
index 000000000000..f02f7da1688d
--- /dev/null
+++ b/drivers/isdn/hisax/fsm.h
@@ -0,0 +1,61 @@
+/* $Id: fsm.h,v 1.3.2.2 2001/09/23 22:24:47 kai Exp $
+ *
+ * Finite state machine
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *              by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __FSM_H__
+#define __FSM_H__
+
+#include <linux/timer.h>
+
+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;
+};
+
+int FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount);
+void FsmFree(struct Fsm *fsm);
+int FsmEvent(struct FsmInst *fi, int event, void *arg);
+void FsmChangeState(struct FsmInst *fi, int newstate);
+void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
+int FsmAddTimer(struct FsmTimer *ft, int millisec, int event,
+		void *arg, int where);
+void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event,
+		     void *arg, int where);
+void FsmDelTimer(struct FsmTimer *ft, int where);
+
+#endif
diff --git a/drivers/isdn/hisax/gazel.c b/drivers/isdn/hisax/gazel.c
new file mode 100644
index 000000000000..24a05a43f33e
--- /dev/null
+++ b/drivers/isdn/hisax/gazel.c
@@ -0,0 +1,684 @@
+/* $Id: gazel.c,v 2.19.2.4 2004/01/14 16:04:48 keil Exp $
+ *
+ * low level stuff for Gazel isdn cards
+ *
+ * Author       BeWan Systems
+ *              based on source code from Karsten Keil
+ * Copyright    by BeWan Systems
+ * 
+ * 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/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include "ipac.h"
+#include <linux/pci.h>
+
+extern const char *CardType[];
+const char *gazel_revision = "$Revision: 2.19.2.4 $";
+
+#define R647      1
+#define R685      2
+#define R753      3
+#define R742      4
+
+#define PLX_CNTRL    0x50	/* registre de controle PLX */
+#define RESET_GAZEL  0x4
+#define RESET_9050   0x40000000
+#define PLX_INCSR    0x4C	/* registre d'IT du 9050 */
+#define INT_ISAC_EN  0x8	/* 1 = enable IT isac */
+#define INT_ISAC     0x20	/* 1 = IT isac en cours */
+#define INT_HSCX_EN  0x1	/* 1 = enable IT hscx */
+#define INT_HSCX     0x4	/* 1 = IT hscx en cours */
+#define INT_PCI_EN   0x40	/* 1 = enable IT PCI */
+#define INT_IPAC_EN  0x3	/* enable IT ipac */
+
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int adr, u_short off)
+{
+	return bytein(adr + off);
+}
+
+static inline void
+writereg(unsigned int adr, u_short off, u_char data)
+{
+	byteout(adr + off, data);
+}
+
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+	insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+	outsb(adr, data, size);
+}
+
+static inline u_char
+readreg_ipac(unsigned int adr, u_short off)
+{
+	register u_char ret;
+
+	byteout(adr, off);
+	ret = bytein(adr + 4);
+	return ret;
+}
+
+static inline void
+writereg_ipac(unsigned int adr, u_short off, u_char data)
+{
+	byteout(adr, off);
+	byteout(adr + 4, data);
+}
+
+
+static inline void
+read_fifo_ipac(unsigned int adr, u_short off, u_char * data, int size)
+{
+	byteout(adr, off);
+	insb(adr + 4, data, size);
+}
+
+static void
+write_fifo_ipac(unsigned int adr, u_short off, u_char * data, int size)
+{
+	byteout(adr, off);
+	outsb(adr + 4, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	u_short off2 = offset;
+
+	switch (cs->subtyp) {
+		case R647:
+			off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
+		case R685:
+			return (readreg(cs->hw.gazel.isac, off2));
+		case R753:
+		case R742:
+			return (readreg_ipac(cs->hw.gazel.ipac, 0x80 + off2));
+	}
+	return 0;
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	u_short off2 = offset;
+
+	switch (cs->subtyp) {
+		case R647:
+			off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
+		case R685:
+			writereg(cs->hw.gazel.isac, off2, value);
+			break;
+		case R753:
+		case R742:
+			writereg_ipac(cs->hw.gazel.ipac, 0x80 + off2, value);
+			break;
+	}
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	switch (cs->subtyp) {
+		case R647:
+		case R685:
+			read_fifo(cs->hw.gazel.isacfifo, data, size);
+			break;
+		case R753:
+		case R742:
+			read_fifo_ipac(cs->hw.gazel.ipac, 0x80, data, size);
+			break;
+	}
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	switch (cs->subtyp) {
+		case R647:
+		case R685:
+			write_fifo(cs->hw.gazel.isacfifo, data, size);
+			break;
+		case R753:
+		case R742:
+			write_fifo_ipac(cs->hw.gazel.ipac, 0x80, data, size);
+			break;
+	}
+}
+
+static void
+ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+	switch (cs->subtyp) {
+		case R647:
+		case R685:
+			read_fifo(cs->hw.gazel.hscxfifo[hscx], data, size);
+			break;
+		case R753:
+		case R742:
+			read_fifo_ipac(cs->hw.gazel.ipac, hscx * 0x40, data, size);
+			break;
+	}
+}
+
+static void
+WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+	switch (cs->subtyp) {
+		case R647:
+		case R685:
+			write_fifo(cs->hw.gazel.hscxfifo[hscx], data, size);
+			break;
+		case R753:
+		case R742:
+			write_fifo_ipac(cs->hw.gazel.ipac, hscx * 0x40, data, size);
+			break;
+	}
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	u_short off2 = offset;
+
+	switch (cs->subtyp) {
+		case R647:
+			off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
+		case R685:
+			return (readreg(cs->hw.gazel.hscx[hscx], off2));
+		case R753:
+		case R742:
+			return (readreg_ipac(cs->hw.gazel.ipac, hscx * 0x40 + off2));
+	}
+	return 0;
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	u_short off2 = offset;
+
+	switch (cs->subtyp) {
+		case R647:
+			off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
+		case R685:
+			writereg(cs->hw.gazel.hscx[hscx], off2, value);
+			break;
+		case R753:
+		case R742:
+			writereg_ipac(cs->hw.gazel.ipac, hscx * 0x40 + off2, value);
+			break;
+	}
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+gazel_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 5
+	struct IsdnCardState *cs = dev_id;
+	u_char valisac, valhscx;
+	int count = 0;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	do {
+		valhscx = ReadHSCX(cs, 1, HSCX_ISTA);
+		if (valhscx)
+			hscx_int_main(cs, valhscx);
+		valisac = ReadISAC(cs, ISAC_ISTA);
+		if (valisac)
+			isac_interrupt(cs, valisac);
+		count++;
+	} while ((valhscx || valisac) && (count < MAXCOUNT));
+
+	WriteHSCX(cs, 0, HSCX_MASK, 0xFF);
+	WriteHSCX(cs, 1, HSCX_MASK, 0xFF);
+	WriteISAC(cs, ISAC_MASK, 0xFF);
+	WriteISAC(cs, ISAC_MASK, 0x0);
+	WriteHSCX(cs, 0, HSCX_MASK, 0x0);
+	WriteHSCX(cs, 1, HSCX_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+
+static irqreturn_t
+gazel_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista, val;
+	int count = 0;
+	u_long flags;
+	
+	spin_lock_irqsave(&cs->lock, flags);
+	ista = ReadISAC(cs, IPAC_ISTA - 0x80);
+	do {
+		if (ista & 0x0f) {
+			val = ReadHSCX(cs, 1, HSCX_ISTA);
+			if (ista & 0x01)
+				val |= 0x01;
+			if (ista & 0x04)
+				val |= 0x02;
+			if (ista & 0x08)
+				val |= 0x04;
+			if (val) {
+				hscx_int_main(cs, val);
+			}
+		}
+		if (ista & 0x20) {
+			val = 0xfe & ReadISAC(cs, ISAC_ISTA);
+			if (val) {
+				isac_interrupt(cs, val);
+			}
+		}
+		if (ista & 0x10) {
+			val = 0x01;
+			isac_interrupt(cs, val);
+		}
+		ista = ReadISAC(cs, IPAC_ISTA - 0x80);
+		count++;
+	}
+	while ((ista & 0x3f) && (count < MAXCOUNT));
+
+	WriteISAC(cs, IPAC_MASK - 0x80, 0xFF);
+	WriteISAC(cs, IPAC_MASK - 0x80, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+void
+release_io_gazel(struct IsdnCardState *cs)
+{
+	unsigned int i;
+
+	switch (cs->subtyp) {
+		case R647:
+			for (i = 0x0000; i < 0xC000; i += 0x1000)
+				release_region(i + cs->hw.gazel.hscx[0], 16);
+			release_region(0xC000 + cs->hw.gazel.hscx[0], 1);
+			break;
+
+		case R685:
+			release_region(cs->hw.gazel.hscx[0], 0x100);
+			release_region(cs->hw.gazel.cfg_reg, 0x80);
+			break;
+
+		case R753:
+			release_region(cs->hw.gazel.ipac, 0x8);
+			release_region(cs->hw.gazel.cfg_reg, 0x80);
+			break;
+
+		case R742:
+			release_region(cs->hw.gazel.ipac, 8);
+			break;
+	}
+}
+
+static int
+reset_gazel(struct IsdnCardState *cs)
+{
+	unsigned long plxcntrl, addr = cs->hw.gazel.cfg_reg;
+
+	switch (cs->subtyp) {
+		case R647:
+			writereg(addr, 0, 0);
+			HZDELAY(10);
+			writereg(addr, 0, 1);
+			HZDELAY(2);
+			break;
+		case R685:
+			plxcntrl = inl(addr + PLX_CNTRL);
+			plxcntrl |= (RESET_9050 + RESET_GAZEL);
+			outl(plxcntrl, addr + PLX_CNTRL);
+			plxcntrl &= ~(RESET_9050 + RESET_GAZEL);
+			HZDELAY(4);
+			outl(plxcntrl, addr + PLX_CNTRL);
+			HZDELAY(10);
+			outb(INT_ISAC_EN + INT_HSCX_EN + INT_PCI_EN, addr + PLX_INCSR);
+			break;
+		case R753:
+			plxcntrl = inl(addr + PLX_CNTRL);
+			plxcntrl |= (RESET_9050 + RESET_GAZEL);
+			outl(plxcntrl, addr + PLX_CNTRL);
+			plxcntrl &= ~(RESET_9050 + RESET_GAZEL);
+			WriteISAC(cs, IPAC_POTA2 - 0x80, 0x20);
+			HZDELAY(4);
+			outl(plxcntrl, addr + PLX_CNTRL);
+			HZDELAY(10);
+			WriteISAC(cs, IPAC_POTA2 - 0x80, 0x00);
+			WriteISAC(cs, IPAC_ACFG - 0x80, 0xff);
+			WriteISAC(cs, IPAC_AOE - 0x80, 0x0);
+			WriteISAC(cs, IPAC_MASK - 0x80, 0xff);
+			WriteISAC(cs, IPAC_CONF - 0x80, 0x1);
+			outb(INT_IPAC_EN + INT_PCI_EN, addr + PLX_INCSR);
+			WriteISAC(cs, IPAC_MASK - 0x80, 0xc0);
+			break;
+		case R742:
+			WriteISAC(cs, IPAC_POTA2 - 0x80, 0x20);
+			HZDELAY(4);
+			WriteISAC(cs, IPAC_POTA2 - 0x80, 0x00);
+			WriteISAC(cs, IPAC_ACFG - 0x80, 0xff);
+			WriteISAC(cs, IPAC_AOE - 0x80, 0x0);
+			WriteISAC(cs, IPAC_MASK - 0x80, 0xff);
+			WriteISAC(cs, IPAC_CONF - 0x80, 0x1);
+			WriteISAC(cs, IPAC_MASK - 0x80, 0xc0);
+			break;
+	}
+	return (0);
+}
+
+static int
+Gazel_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_gazel(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_RELEASE:
+			release_io_gazel(cs);
+			return (0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 1);
+			if ((cs->subtyp==R647)||(cs->subtyp==R685)) {
+				int i;
+				for (i=0;i<(2+MAX_WAITING_CALLS);i++) {
+					cs->bcs[i].hw.hscx.tsaxr0 = 0x1f;
+					cs->bcs[i].hw.hscx.tsaxr1 = 0x23;
+				}
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_TEST:
+			return (0);
+	}
+	return (0);
+}
+
+static int
+reserve_regions(struct IsdnCard *card, struct IsdnCardState *cs)
+{
+	unsigned int i, j, base = 0, adr = 0, len = 0;
+
+	switch (cs->subtyp) {
+		case R647:
+			base = cs->hw.gazel.hscx[0];
+			if (!request_region(adr = (0xC000 + base), len = 1, "gazel"))
+				goto error;
+			for (i = 0x0000; i < 0xC000; i += 0x1000) {
+				if (!request_region(adr = (i + base), len = 16, "gazel"))
+					goto error;
+			}
+			if (i != 0xC000) {
+				for (j = 0; j < i; j+= 0x1000)
+					release_region(j + base, 16);
+				release_region(0xC000 + base, 1);
+				goto error;
+			}
+			break;
+
+		case R685:
+			if (!request_region(adr = cs->hw.gazel.hscx[0], len = 0x100, "gazel"))
+				goto error;
+			if (!request_region(adr = cs->hw.gazel.cfg_reg, len = 0x80, "gazel")) {
+				release_region(cs->hw.gazel.hscx[0],0x100);
+				goto error;
+			}
+			break;
+
+		case R753:
+			if (!request_region(adr = cs->hw.gazel.ipac, len = 0x8, "gazel"))
+				goto error;
+			if (!request_region(adr = cs->hw.gazel.cfg_reg, len = 0x80, "gazel")) {
+				release_region(cs->hw.gazel.ipac, 8);
+				goto error;
+			}
+			break;
+
+		case R742:
+			if (!request_region(adr = cs->hw.gazel.ipac, len = 0x8, "gazel"))
+				goto error;
+			break;
+	}
+
+	return 0;
+
+      error:
+	printk(KERN_WARNING "Gazel: %s io ports 0x%x-0x%x already in use\n",
+	       CardType[cs->typ], adr, adr + len);
+	return 1;
+}
+
+static int __init
+setup_gazelisa(struct IsdnCard *card, struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "Gazel: ISA PnP card automatic recognition\n");
+	// we got an irq parameter, assume it is an ISA card
+	// R742 decodes address even in not started...
+	// R647 returns FF if not present or not started
+	// eventually needs improvment
+	if (readreg_ipac(card->para[1], IPAC_ID) == 1)
+		cs->subtyp = R742;
+	else
+		cs->subtyp = R647;
+
+	setup_isac(cs);
+	cs->hw.gazel.cfg_reg = card->para[1] + 0xC000;
+	cs->hw.gazel.ipac = card->para[1];
+	cs->hw.gazel.isac = card->para[1] + 0x8000;
+	cs->hw.gazel.hscx[0] = card->para[1];
+	cs->hw.gazel.hscx[1] = card->para[1] + 0x4000;
+	cs->irq = card->para[0];
+	cs->hw.gazel.isacfifo = cs->hw.gazel.isac;
+	cs->hw.gazel.hscxfifo[0] = cs->hw.gazel.hscx[0];
+	cs->hw.gazel.hscxfifo[1] = cs->hw.gazel.hscx[1];
+
+	switch (cs->subtyp) {
+		case R647:
+			printk(KERN_INFO "Gazel: Card ISA R647/R648 found\n");
+			cs->dc.isac.adf2 = 0x87;
+			printk(KERN_INFO
+				"Gazel: config irq:%d isac:0x%X  cfg:0x%X\n",
+				cs->irq, cs->hw.gazel.isac, cs->hw.gazel.cfg_reg);
+			printk(KERN_INFO
+				"Gazel: hscx A:0x%X  hscx B:0x%X\n",
+				cs->hw.gazel.hscx[0], cs->hw.gazel.hscx[1]);
+
+			break;
+		case R742:
+			printk(KERN_INFO "Gazel: Card ISA R742 found\n");
+			test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+			printk(KERN_INFO
+			       "Gazel: config irq:%d ipac:0x%X\n",
+			       cs->irq, cs->hw.gazel.ipac);
+			break;
+	}
+
+	return (0);
+}
+
+static struct pci_dev *dev_tel __initdata = NULL;
+
+static int __init
+setup_gazelpci(struct IsdnCardState *cs)
+{
+	u_int pci_ioaddr0 = 0, pci_ioaddr1 = 0;
+	u_char pci_irq = 0, found;
+	u_int nbseek, seekcard;
+
+	printk(KERN_WARNING "Gazel: PCI card automatic recognition\n");
+
+	found = 0;
+	seekcard = PCI_DEVICE_ID_PLX_R685;
+	for (nbseek = 0; nbseek < 3; nbseek++) {
+		if ((dev_tel = pci_find_device(PCI_VENDOR_ID_PLX, seekcard, dev_tel))) {
+			if (pci_enable_device(dev_tel))
+				return 1;
+			pci_irq = dev_tel->irq;
+			pci_ioaddr0 = pci_resource_start(dev_tel, 1);
+			pci_ioaddr1 = pci_resource_start(dev_tel, 2);
+			found = 1;
+		}
+		if (found)
+			break;
+		else {
+			switch (seekcard) {
+				case PCI_DEVICE_ID_PLX_R685:
+					seekcard = PCI_DEVICE_ID_PLX_R753;
+					break;
+				case PCI_DEVICE_ID_PLX_R753:
+					seekcard = PCI_DEVICE_ID_PLX_DJINN_ITOO;
+					break;
+			}
+		}
+	}
+	if (!found) {
+		printk(KERN_WARNING "Gazel: No PCI card found\n");
+		return (1);
+	}
+	if (!pci_irq) {
+		printk(KERN_WARNING "Gazel: No IRQ for PCI card found\n");
+		return 1;
+	}
+	cs->hw.gazel.pciaddr[0] = pci_ioaddr0;
+	cs->hw.gazel.pciaddr[1] = pci_ioaddr1;
+	setup_isac(cs);
+	pci_ioaddr1 &= 0xfffe;
+	cs->hw.gazel.cfg_reg = pci_ioaddr0 & 0xfffe;
+	cs->hw.gazel.ipac = pci_ioaddr1;
+	cs->hw.gazel.isac = pci_ioaddr1 + 0x80;
+	cs->hw.gazel.hscx[0] = pci_ioaddr1;
+	cs->hw.gazel.hscx[1] = pci_ioaddr1 + 0x40;
+	cs->hw.gazel.isacfifo = cs->hw.gazel.isac;
+	cs->hw.gazel.hscxfifo[0] = cs->hw.gazel.hscx[0];
+	cs->hw.gazel.hscxfifo[1] = cs->hw.gazel.hscx[1];
+	cs->irq = pci_irq;
+	cs->irq_flags |= SA_SHIRQ;
+
+	switch (seekcard) {
+		case PCI_DEVICE_ID_PLX_R685:
+			printk(KERN_INFO "Gazel: Card PCI R685 found\n");
+			cs->subtyp = R685;
+			cs->dc.isac.adf2 = 0x87;
+			printk(KERN_INFO
+			    "Gazel: config irq:%d isac:0x%X  cfg:0x%X\n",
+			cs->irq, cs->hw.gazel.isac, cs->hw.gazel.cfg_reg);
+			printk(KERN_INFO
+			       "Gazel: hscx A:0x%X  hscx B:0x%X\n",
+			     cs->hw.gazel.hscx[0], cs->hw.gazel.hscx[1]);
+			break;
+		case PCI_DEVICE_ID_PLX_R753:
+		case PCI_DEVICE_ID_PLX_DJINN_ITOO:
+			printk(KERN_INFO "Gazel: Card PCI R753 found\n");
+			cs->subtyp = R753;
+			test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+			printk(KERN_INFO
+			    "Gazel: config irq:%d ipac:0x%X  cfg:0x%X\n",
+			cs->irq, cs->hw.gazel.ipac, cs->hw.gazel.cfg_reg);
+			break;
+	}
+
+	return (0);
+}
+
+int __init
+setup_gazel(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	u_char val;
+
+	strcpy(tmp, gazel_revision);
+	printk(KERN_INFO "Gazel: Driver Revision %s\n", HiSax_getrev(tmp));
+
+	if (cs->typ != ISDN_CTYPE_GAZEL)
+		return (0);
+
+	if (card->para[0]) {
+		if (setup_gazelisa(card, cs))
+			return (0);
+	} else {
+
+#ifdef CONFIG_PCI
+		if (setup_gazelpci(cs))
+			return (0);
+#else
+		printk(KERN_WARNING "Gazel: Card PCI requested and NO_PCI_BIOS, unable to config\n");
+		return (0);
+#endif				/* CONFIG_PCI */
+	}
+
+	if (reserve_regions(card, cs)) {
+		return (0);
+	}
+	if (reset_gazel(cs)) {
+		printk(KERN_WARNING "Gazel: wrong IRQ\n");
+		release_io_gazel(cs);
+		return (0);
+	}
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Gazel_card_msg;
+
+	switch (cs->subtyp) {
+		case R647:
+		case R685:
+			cs->irq_func = &gazel_interrupt;
+			ISACVersion(cs, "Gazel:");
+			if (HscxVersion(cs, "Gazel:")) {
+				printk(KERN_WARNING
+				       "Gazel: wrong HSCX versions check IO address\n");
+				release_io_gazel(cs);
+				return (0);
+			}
+			break;
+		case R742:
+		case R753:
+			cs->irq_func = &gazel_interrupt_ipac;
+			val = ReadISAC(cs, IPAC_ID - 0x80);
+			printk(KERN_INFO "Gazel: IPAC version %x\n", val);
+			break;
+	}
+
+	return (1);
+}
diff --git a/drivers/isdn/hisax/hfc4s8s_l1.c b/drivers/isdn/hisax/hfc4s8s_l1.c
new file mode 100644
index 000000000000..1ac46c26b936
--- /dev/null
+++ b/drivers/isdn/hisax/hfc4s8s_l1.c
@@ -0,0 +1,1714 @@
+/*************************************************************************/
+/* $Id: hfc4s8s_l1.c,v 1.10 2005/02/09 16:31:09 martinb1 Exp $           */
+/* HFC-4S/8S low layer interface for Cologne Chip HFC-4S/8S isdn chips   */
+/* The low layer (L1) is implemented as a loadable module for usage with */
+/* the HiSax isdn driver for passive cards.                              */
+/*                                                                       */
+/* Author: Werner Cornelius                                              */
+/* (C) 2003 Cornelius Consult (werner@cornelius-consult.de)              */
+/*                                                                       */
+/* Driver maintained by Cologne Chip                                     */
+/*   - Martin Bachem, support@colognechip.com                            */
+/*                                                                       */
+/* This driver only works with chip revisions >= 1, older revision 0     */
+/* engineering samples (only first manufacturer sample cards) will not   */
+/* work and are rejected by the driver.                                  */
+/*                                                                       */
+/* This file distributed under the GNU GPL.                              */
+/*                                                                       */
+/* See Version History at the end of this file                           */
+/*                                                                       */
+/*************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+#include "hisax_if.h"
+#include "hfc4s8s_l1.h"
+
+static const char hfc4s8s_rev[] = "Revision: 1.10";
+
+/***************************************************************/
+/* adjustable transparent mode fifo threshold                  */
+/* The value defines the used fifo threshold with the equation */
+/*                                                             */
+/* notify number of bytes = 2 * 2 ^ TRANS_FIFO_THRES           */
+/*                                                             */
+/* The default value is 5 which results in a buffer size of 64 */
+/* and an interrupt rate of 8ms.                               */
+/* The maximum value is 7 due to fifo size restrictions.       */
+/* Values below 3-4 are not recommended due to high interrupt  */
+/* load of the processor. For non critical applications the    */
+/* value should be raised to 7 to reduce any interrupt overhead*/
+/***************************************************************/
+#define TRANS_FIFO_THRES 5
+
+/*************/
+/* constants */
+/*************/
+#define CLOCKMODE_0     0	/* ext. 24.576 MhZ clk freq, int. single clock mode */
+#define CLOCKMODE_1     1	/* ext. 49.576 MhZ clk freq, int. single clock mode */
+#define CHIP_ID_SHIFT   4
+#define HFC_MAX_ST 8
+#define MAX_D_FRAME_SIZE 270
+#define MAX_B_FRAME_SIZE 1536
+#define TRANS_TIMER_MODE (TRANS_FIFO_THRES & 0xf)
+#define TRANS_FIFO_BYTES (2 << TRANS_FIFO_THRES)
+#define MAX_F_CNT 0x0f
+
+#define CLKDEL_NT 0x6c
+#define CLKDEL_TE 0xf
+#define CTRL0_NT  4
+#define CTRL0_TE  0
+
+#define L1_TIMER_T4 2		/* minimum in jiffies */
+#define L1_TIMER_T3 (7 * HZ)	/* activation timeout */
+#define L1_TIMER_T1 ((120 * HZ) / 1000)	/* NT mode deactivation timeout */
+
+
+/******************/
+/* types and vars */
+/******************/
+static int card_cnt;
+
+/* private driver_data */
+typedef struct {
+	int chip_id;
+	int clock_mode;
+	int max_st_ports;
+	char *device_name;
+} hfc4s8s_param;
+
+static struct pci_device_id hfc4s8s_ids[] = {
+	{.vendor = PCI_VENDOR_ID_CCD,
+	 .device = PCI_DEVICE_ID_4S,
+	 .subvendor = 0x1397,
+	 .subdevice = 0x08b4,
+	 .driver_data =
+	 (unsigned long) &((hfc4s8s_param) {CHIP_ID_4S, CLOCKMODE_0, 4,
+					    "HFC-4S Evaluation Board"}),
+	 },
+	{.vendor = PCI_VENDOR_ID_CCD,
+	 .device = PCI_DEVICE_ID_8S,
+	 .subvendor = 0x1397,
+	 .subdevice = 0x16b8,
+	 .driver_data =
+	 (unsigned long) &((hfc4s8s_param) {CHIP_ID_8S, CLOCKMODE_0, 8,
+					    "HFC-8S Evaluation Board"}),
+	 },
+	{.vendor = PCI_VENDOR_ID_CCD,
+	 .device = PCI_DEVICE_ID_4S,
+	 .subvendor = 0x1397,
+	 .subdevice = 0xb520,
+	 .driver_data =
+	 (unsigned long) &((hfc4s8s_param) {CHIP_ID_4S, CLOCKMODE_1, 4,
+					    "IOB4ST"}),
+	 },
+	{.vendor = PCI_VENDOR_ID_CCD,
+	 .device = PCI_DEVICE_ID_8S,
+	 .subvendor = 0x1397,
+	 .subdevice = 0xb522,
+	 .driver_data =
+	 (unsigned long) &((hfc4s8s_param) {CHIP_ID_8S, CLOCKMODE_1, 8,
+					    "IOB8ST"}),
+	 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(pci, hfc4s8s_ids);
+
+MODULE_AUTHOR("Werner Cornelius, werner@cornelius-consult.de");
+MODULE_DESCRIPTION("ISDN layer 1 for Cologne Chip HFC-4S/8S chips");
+MODULE_LICENSE("GPL");
+
+/***********/
+/* layer 1 */
+/***********/
+struct hfc4s8s_btype {
+	spinlock_t lock;
+	struct hisax_b_if b_if;
+	struct hfc4s8s_l1 *l1p;
+	struct sk_buff_head tx_queue;
+	struct sk_buff *tx_skb;
+	struct sk_buff *rx_skb;
+	__u8 *rx_ptr;
+	int tx_cnt;
+	int bchan;
+	int mode;
+};
+
+struct _hfc4s8s_hw;
+
+struct hfc4s8s_l1 {
+	spinlock_t lock;
+	struct _hfc4s8s_hw *hw;	/* pointer to hardware area */
+	int l1_state;		/* actual l1 state */
+	struct timer_list l1_timer;	/* layer 1 timer structure */
+	int nt_mode;		/* set to nt mode */
+	int st_num;		/* own index */
+	int enabled;		/* interface is enabled */
+	struct sk_buff_head d_tx_queue;	/* send queue */
+	int tx_cnt;		/* bytes to send */
+	struct hisax_d_if d_if;	/* D-channel interface */
+	struct hfc4s8s_btype b_ch[2];	/* B-channel data */
+	struct hisax_b_if *b_table[2];
+};
+
+/**********************/
+/* hardware structure */
+/**********************/
+typedef struct _hfc4s8s_hw {
+	spinlock_t lock;
+
+	int cardnum;
+	int ifnum;
+	int iobase;
+	int nt_mode;
+	u_char *membase;
+	u_char *hw_membase;
+	void *pdev;
+	int max_fifo;
+	hfc4s8s_param driver_data;
+	int irq;
+	int fifo_sched_cnt;
+	struct work_struct tqueue;
+	struct hfc4s8s_l1 l1[HFC_MAX_ST];
+	char card_name[60];
+	struct {
+		u_char r_irq_ctrl;
+		u_char r_ctrl0;
+		volatile u_char r_irq_statech;	/* active isdn l1 status */
+		u_char r_irqmsk_statchg;	/* enabled isdn status ints */
+		u_char r_irq_fifo_blx[8];	/* fifo status registers */
+		u_char fifo_rx_trans_enables[8];	/* mask for enabled transparent rx fifos */
+		u_char fifo_slow_timer_service[8];	/* mask for fifos needing slower timer service */
+		volatile u_char r_irq_oview;	/* contents of overview register */
+		volatile u_char timer_irq;
+		int timer_usg_cnt;	/* number of channels using timer */
+	} mr;
+} hfc4s8s_hw;
+
+
+
+/***************************/
+/* inline function defines */
+/***************************/
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM	/* inline functions mempry mapped */
+
+/* memory write and dummy IO read to avoid PCI byte merge problems */
+#define Write_hfc8(a,b,c) {(*((volatile u_char *)(a->membase+b)) = c); inb(a->iobase+4);}
+/* memory write without dummy IO access for fifo data access */
+#define fWrite_hfc8(a,b,c) (*((volatile u_char *)(a->membase+b)) = c)
+#define Read_hfc8(a,b) (*((volatile u_char *)(a->membase+b)))
+#define Write_hfc16(a,b,c) (*((volatile unsigned short *)(a->membase+b)) = c)
+#define Read_hfc16(a,b) (*((volatile unsigned short *)(a->membase+b)))
+#define Write_hfc32(a,b,c) (*((volatile unsigned long *)(a->membase+b)) = c)
+#define Read_hfc32(a,b) (*((volatile unsigned long *)(a->membase+b)))
+#define wait_busy(a) {while ((Read_hfc8(a, R_STATUS) & M_BUSY));}
+#define PCI_ENA_MEMIO	0x03
+
+#else
+
+/* inline functions io mapped */
+static inline void
+SetRegAddr(hfc4s8s_hw * a, u_char b)
+{
+	outb(b, (a->iobase) + 4);
+}
+
+static inline u_char
+GetRegAddr(hfc4s8s_hw * a)
+{
+	return (inb((volatile u_int) (a->iobase + 4)));
+}
+
+
+static inline void
+Write_hfc8(hfc4s8s_hw * a, u_char b, u_char c)
+{
+	SetRegAddr(a, b);
+	outb(c, a->iobase);
+}
+
+static inline void
+fWrite_hfc8(hfc4s8s_hw * a, u_char c)
+{
+	outb(c, a->iobase);
+}
+
+static inline void
+Write_hfc16(hfc4s8s_hw * a, u_char b, u_short c)
+{
+	SetRegAddr(a, b);
+	outw(c, a->iobase);
+}
+
+static inline void
+Write_hfc32(hfc4s8s_hw * a, u_char b, u_long c)
+{
+	SetRegAddr(a, b);
+	outl(c, a->iobase);
+}
+
+static inline void
+fWrite_hfc32(hfc4s8s_hw * a, u_long c)
+{
+	outl(c, a->iobase);
+}
+
+static inline u_char
+Read_hfc8(hfc4s8s_hw * a, u_char b)
+{
+	SetRegAddr(a, b);
+	return (inb((volatile u_int) a->iobase));
+}
+
+static inline u_char
+fRead_hfc8(hfc4s8s_hw * a)
+{
+	return (inb((volatile u_int) a->iobase));
+}
+
+
+static inline u_short
+Read_hfc16(hfc4s8s_hw * a, u_char b)
+{
+	SetRegAddr(a, b);
+	return (inw((volatile u_int) a->iobase));
+}
+
+static inline u_long
+Read_hfc32(hfc4s8s_hw * a, u_char b)
+{
+	SetRegAddr(a, b);
+	return (inl((volatile u_int) a->iobase));
+}
+
+static inline u_long
+fRead_hfc32(hfc4s8s_hw * a)
+{
+	return (inl((volatile u_int) a->iobase));
+}
+
+static inline void
+wait_busy(hfc4s8s_hw * a)
+{
+	SetRegAddr(a, R_STATUS);
+	while (inb((volatile u_int) a->iobase) & M_BUSY);
+}
+
+#define PCI_ENA_REGIO	0x01
+
+#endif				/* CONFIG_HISAX_HFC4S8S_PCIMEM */
+
+/******************************************************/
+/* function to read critical counter registers that   */
+/* may be udpated by the chip during read             */
+/******************************************************/
+static volatile u_char
+Read_hfc8_stable(hfc4s8s_hw * hw, int reg)
+{
+	u_char ref8;
+	u_char in8;
+	ref8 = Read_hfc8(hw, reg);
+	while (((in8 = Read_hfc8(hw, reg)) != ref8)) {
+		ref8 = in8;
+	}
+	return in8;
+}
+
+static volatile int
+Read_hfc16_stable(hfc4s8s_hw * hw, int reg)
+{
+	int ref16;
+	int in16;
+
+	ref16 = Read_hfc16(hw, reg);
+	while (((in16 = Read_hfc16(hw, reg)) != ref16)) {
+		ref16 = in16;
+	}
+	return in16;
+}
+
+/*****************************/
+/* D-channel call from HiSax */
+/*****************************/
+static void
+dch_l2l1(struct hisax_d_if *iface, int pr, void *arg)
+{
+	struct hfc4s8s_l1 *l1 = iface->ifc.priv;
+	struct sk_buff *skb = (struct sk_buff *) arg;
+	u_long flags;
+
+	switch (pr) {
+
+		case (PH_DATA | REQUEST):
+			if (!l1->enabled) {
+				dev_kfree_skb(skb);
+				break;
+			}
+			spin_lock_irqsave(&l1->lock, flags);
+			skb_queue_tail(&l1->d_tx_queue, skb);
+			if ((skb_queue_len(&l1->d_tx_queue) == 1) &&
+			    (l1->tx_cnt <= 0)) {
+				l1->hw->mr.r_irq_fifo_blx[l1->st_num] |=
+				    0x10;
+				spin_unlock_irqrestore(&l1->lock, flags);
+				schedule_work(&l1->hw->tqueue);
+			} else
+				spin_unlock_irqrestore(&l1->lock, flags);
+			break;
+
+		case (PH_ACTIVATE | REQUEST):
+			if (!l1->enabled)
+				break;
+			if (!l1->nt_mode) {
+				if (l1->l1_state < 6) {
+					spin_lock_irqsave(&l1->lock,
+							  flags);
+
+					Write_hfc8(l1->hw, R_ST_SEL,
+						   l1->st_num);
+					Write_hfc8(l1->hw, A_ST_WR_STA,
+						   0x60);
+					mod_timer(&l1->l1_timer,
+						  jiffies + L1_TIMER_T3);
+					spin_unlock_irqrestore(&l1->lock,
+							       flags);
+				} else if (l1->l1_state == 7)
+					l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+							  PH_ACTIVATE |
+							  INDICATION,
+							  NULL);
+			} else {
+				if (l1->l1_state != 3) {
+					spin_lock_irqsave(&l1->lock,
+							  flags);
+					Write_hfc8(l1->hw, R_ST_SEL,
+						   l1->st_num);
+					Write_hfc8(l1->hw, A_ST_WR_STA,
+						   0x60);
+					spin_unlock_irqrestore(&l1->lock,
+							       flags);
+				} else if (l1->l1_state == 3)
+					l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+							  PH_ACTIVATE |
+							  INDICATION,
+							  NULL);
+			}
+			break;
+
+		default:
+			printk(KERN_INFO
+			       "HFC-4S/8S: Unknown D-chan cmd 0x%x received, ignored\n",
+			       pr);
+			break;
+	}
+	if (!l1->enabled)
+		l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+				  PH_DEACTIVATE | INDICATION, NULL);
+}				/* dch_l2l1 */
+
+/*****************************/
+/* B-channel call from HiSax */
+/*****************************/
+static void
+bch_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+	struct hfc4s8s_btype *bch = ifc->priv;
+	struct hfc4s8s_l1 *l1 = bch->l1p;
+	struct sk_buff *skb = (struct sk_buff *) arg;
+	int mode = (int) arg;
+	u_long flags;
+
+	switch (pr) {
+
+		case (PH_DATA | REQUEST):
+			if (!l1->enabled || (bch->mode == L1_MODE_NULL)) {
+				dev_kfree_skb(skb);
+				break;
+			}
+			spin_lock_irqsave(&l1->lock, flags);
+			skb_queue_tail(&bch->tx_queue, skb);
+			if (!bch->tx_skb && (bch->tx_cnt <= 0)) {
+				l1->hw->mr.r_irq_fifo_blx[l1->st_num] |=
+				    ((bch->bchan == 1) ? 1 : 4);
+				spin_unlock_irqrestore(&l1->lock, flags);
+				schedule_work(&l1->hw->tqueue);
+			} else
+				spin_unlock_irqrestore(&l1->lock, flags);
+			break;
+
+		case (PH_ACTIVATE | REQUEST):
+		case (PH_DEACTIVATE | REQUEST):
+			if (!l1->enabled)
+				break;
+			if (pr == (PH_DEACTIVATE | REQUEST))
+				mode = L1_MODE_NULL;
+
+			switch (mode) {
+				case L1_MODE_HDLC:
+					spin_lock_irqsave(&l1->lock,
+							  flags);
+					l1->hw->mr.timer_usg_cnt++;
+					l1->hw->mr.
+					    fifo_slow_timer_service[l1->
+								    st_num]
+					    |=
+					    ((bch->bchan ==
+					      1) ? 0x2 : 0x8);
+					Write_hfc8(l1->hw, R_FIFO,
+						   (l1->st_num * 8 +
+						    ((bch->bchan ==
+						      1) ? 0 : 2)));
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, A_CON_HDLC, 0xc);	/* HDLC mode, flag fill, connect ST */
+					Write_hfc8(l1->hw, A_SUBCH_CFG, 0);	/* 8 bits */
+					Write_hfc8(l1->hw, A_IRQ_MSK, 1);	/* enable TX interrupts for hdlc */
+					Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+					wait_busy(l1->hw);
+
+					Write_hfc8(l1->hw, R_FIFO,
+						   (l1->st_num * 8 +
+						    ((bch->bchan ==
+						      1) ? 1 : 3)));
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, A_CON_HDLC, 0xc);	/* HDLC mode, flag fill, connect ST */
+					Write_hfc8(l1->hw, A_SUBCH_CFG, 0);	/* 8 bits */
+					Write_hfc8(l1->hw, A_IRQ_MSK, 1);	/* enable RX interrupts for hdlc */
+					Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+
+					Write_hfc8(l1->hw, R_ST_SEL,
+						   l1->st_num);
+					l1->hw->mr.r_ctrl0 |=
+					    (bch->bchan & 3);
+					Write_hfc8(l1->hw, A_ST_CTRL0,
+						   l1->hw->mr.r_ctrl0);
+					bch->mode = L1_MODE_HDLC;
+					spin_unlock_irqrestore(&l1->lock,
+							       flags);
+
+					bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+							   PH_ACTIVATE |
+							   INDICATION,
+							   NULL);
+					break;
+
+				case L1_MODE_TRANS:
+					spin_lock_irqsave(&l1->lock,
+							  flags);
+					l1->hw->mr.
+					    fifo_rx_trans_enables[l1->
+								  st_num]
+					    |=
+					    ((bch->bchan ==
+					      1) ? 0x2 : 0x8);
+					l1->hw->mr.timer_usg_cnt++;
+					Write_hfc8(l1->hw, R_FIFO,
+						   (l1->st_num * 8 +
+						    ((bch->bchan ==
+						      1) ? 0 : 2)));
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, A_CON_HDLC, 0xf);	/* Transparent mode, 1 fill, connect ST */
+					Write_hfc8(l1->hw, A_SUBCH_CFG, 0);	/* 8 bits */
+					Write_hfc8(l1->hw, A_IRQ_MSK, 0);	/* disable TX interrupts */
+					Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+					wait_busy(l1->hw);
+
+					Write_hfc8(l1->hw, R_FIFO,
+						   (l1->st_num * 8 +
+						    ((bch->bchan ==
+						      1) ? 1 : 3)));
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, A_CON_HDLC, 0xf);	/* Transparent mode, 1 fill, connect ST */
+					Write_hfc8(l1->hw, A_SUBCH_CFG, 0);	/* 8 bits */
+					Write_hfc8(l1->hw, A_IRQ_MSK, 0);	/* disable RX interrupts */
+					Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+
+					Write_hfc8(l1->hw, R_ST_SEL,
+						   l1->st_num);
+					l1->hw->mr.r_ctrl0 |=
+					    (bch->bchan & 3);
+					Write_hfc8(l1->hw, A_ST_CTRL0,
+						   l1->hw->mr.r_ctrl0);
+					bch->mode = L1_MODE_TRANS;
+					spin_unlock_irqrestore(&l1->lock,
+							       flags);
+
+					bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+							   PH_ACTIVATE |
+							   INDICATION,
+							   NULL);
+					break;
+
+				default:
+					if (bch->mode == L1_MODE_NULL)
+						break;
+					spin_lock_irqsave(&l1->lock,
+							  flags);
+					l1->hw->mr.
+					    fifo_slow_timer_service[l1->
+								    st_num]
+					    &=
+					    ~((bch->bchan ==
+					       1) ? 0x3 : 0xc);
+					l1->hw->mr.
+					    fifo_rx_trans_enables[l1->
+								  st_num]
+					    &=
+					    ~((bch->bchan ==
+					       1) ? 0x3 : 0xc);
+					l1->hw->mr.timer_usg_cnt--;
+					Write_hfc8(l1->hw, R_FIFO,
+						   (l1->st_num * 8 +
+						    ((bch->bchan ==
+						      1) ? 0 : 2)));
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, A_IRQ_MSK, 0);	/* disable TX interrupts */
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, R_FIFO,
+						   (l1->st_num * 8 +
+						    ((bch->bchan ==
+						      1) ? 1 : 3)));
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, A_IRQ_MSK, 0);	/* disable RX interrupts */
+					Write_hfc8(l1->hw, R_ST_SEL,
+						   l1->st_num);
+					l1->hw->mr.r_ctrl0 &=
+					    ~(bch->bchan & 3);
+					Write_hfc8(l1->hw, A_ST_CTRL0,
+						   l1->hw->mr.r_ctrl0);
+					spin_unlock_irqrestore(&l1->lock,
+							       flags);
+
+					bch->mode = L1_MODE_NULL;
+					bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+							   PH_DEACTIVATE |
+							   INDICATION,
+							   NULL);
+					if (bch->tx_skb) {
+						dev_kfree_skb(bch->tx_skb);
+						bch->tx_skb = NULL;
+					}
+					if (bch->rx_skb) {
+						dev_kfree_skb(bch->rx_skb);
+						bch->rx_skb = NULL;
+					}
+					skb_queue_purge(&bch->tx_queue);
+					bch->tx_cnt = 0;
+					bch->rx_ptr = NULL;
+					break;
+			}
+
+			/* timer is only used when at least one b channel */
+			/* is set up to transparent mode */
+			if (l1->hw->mr.timer_usg_cnt) {
+				Write_hfc8(l1->hw, R_IRQMSK_MISC,
+					   M_TI_IRQMSK);
+			} else {
+				Write_hfc8(l1->hw, R_IRQMSK_MISC, 0);
+			}
+
+			break;
+
+		default:
+			printk(KERN_INFO
+			       "HFC-4S/8S: Unknown B-chan cmd 0x%x received, ignored\n",
+			       pr);
+			break;
+	}
+	if (!l1->enabled)
+		bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+				   PH_DEACTIVATE | INDICATION, NULL);
+}				/* bch_l2l1 */
+
+/**************************/
+/* layer 1 timer function */
+/**************************/
+static void
+hfc_l1_timer(struct hfc4s8s_l1 *l1)
+{
+	u_long flags;
+
+	if (!l1->enabled)
+		return;
+
+	spin_lock_irqsave(&l1->lock, flags);
+	if (l1->nt_mode) {
+		l1->l1_state = 1;
+		Write_hfc8(l1->hw, R_ST_SEL, l1->st_num);
+		Write_hfc8(l1->hw, A_ST_WR_STA, 0x11);
+		spin_unlock_irqrestore(&l1->lock, flags);
+		l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+				  PH_DEACTIVATE | INDICATION, NULL);
+		spin_lock_irqsave(&l1->lock, flags);
+		l1->l1_state = 1;
+		Write_hfc8(l1->hw, A_ST_WR_STA, 0x1);
+		spin_unlock_irqrestore(&l1->lock, flags);
+	} else {
+		/* activation timed out */
+		Write_hfc8(l1->hw, R_ST_SEL, l1->st_num);
+		Write_hfc8(l1->hw, A_ST_WR_STA, 0x13);
+		spin_unlock_irqrestore(&l1->lock, flags);
+		l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+				  PH_DEACTIVATE | INDICATION, NULL);
+		spin_lock_irqsave(&l1->lock, flags);
+		Write_hfc8(l1->hw, R_ST_SEL, l1->st_num);
+		Write_hfc8(l1->hw, A_ST_WR_STA, 0x3);
+		spin_unlock_irqrestore(&l1->lock, flags);
+	}
+}				/* hfc_l1_timer */
+
+/****************************************/
+/* a complete D-frame has been received */
+/****************************************/
+static void
+rx_d_frame(struct hfc4s8s_l1 *l1p, int ech)
+{
+	int z1, z2;
+	u_char f1, f2, df;
+	struct sk_buff *skb;
+	u_char *cp;
+
+
+	if (!l1p->enabled)
+		return;
+	do {
+		/* E/D RX fifo */
+		Write_hfc8(l1p->hw, R_FIFO,
+			   (l1p->st_num * 8 + ((ech) ? 7 : 5)));
+		wait_busy(l1p->hw);
+
+		f1 = Read_hfc8_stable(l1p->hw, A_F1);
+		f2 = Read_hfc8(l1p->hw, A_F2);
+		df = f1 - f2;
+		if ((f1 - f2) < 0)
+			df = f1 - f2 + MAX_F_CNT + 1;
+
+
+		if (!df) {
+			return;	/* no complete frame in fifo */
+		}
+
+		z1 = Read_hfc16_stable(l1p->hw, A_Z1);
+		z2 = Read_hfc16(l1p->hw, A_Z2);
+
+		z1 = z1 - z2 + 1;
+		if (z1 < 0)
+			z1 += 384;
+
+		if (!(skb = dev_alloc_skb(MAX_D_FRAME_SIZE))) {
+			printk(KERN_INFO
+			       "HFC-4S/8S: Could not allocate D/E "
+			       "channel receive buffer");
+			Write_hfc8(l1p->hw, A_INC_RES_FIFO, 2);
+			wait_busy(l1p->hw);
+			return;
+		}
+
+		if (((z1 < 4) || (z1 > MAX_D_FRAME_SIZE))) {
+			if (skb)
+				dev_kfree_skb(skb);
+			/* remove errornous D frame */
+			if (df == 1) {
+				/* reset fifo */
+				Write_hfc8(l1p->hw, A_INC_RES_FIFO, 2);
+				wait_busy(l1p->hw);
+				return;
+			} else {
+				/* read errornous D frame */
+
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+				SetRegAddr(l1p->hw, A_FIFO_DATA0);
+#endif
+
+				while (z1 >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+					Read_hfc32(l1p->hw, A_FIFO_DATA0);
+#else
+					fRead_hfc32(l1p->hw);
+#endif
+					z1 -= 4;
+				}
+
+				while (z1--)
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+					Read_hfc8(l1p->hw, A_FIFO_DATA0);
+#else
+					fRead_hfc8(l1p->hw);
+#endif
+
+				Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1);
+				wait_busy(l1p->hw);
+				return;
+			}
+		}
+
+		cp = skb->data;
+
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+		SetRegAddr(l1p->hw, A_FIFO_DATA0);
+#endif
+
+		while (z1 >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			*((unsigned long *) cp) =
+			    Read_hfc32(l1p->hw, A_FIFO_DATA0);
+#else
+			*((unsigned long *) cp) = fRead_hfc32(l1p->hw);
+#endif
+			cp += 4;
+			z1 -= 4;
+		}
+
+		while (z1--)
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			*cp++ = Read_hfc8(l1p->hw, A_FIFO_DATA0);
+#else
+			*cp++ = fRead_hfc8(l1p->hw);
+#endif
+
+		Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1);	/* increment f counter */
+		wait_busy(l1p->hw);
+
+		if (*(--cp)) {
+			dev_kfree_skb(skb);
+		} else {
+			skb->len = (cp - skb->data) - 2;
+			if (ech)
+				l1p->d_if.ifc.l1l2(&l1p->d_if.ifc,
+						   PH_DATA_E | INDICATION,
+						   skb);
+			else
+				l1p->d_if.ifc.l1l2(&l1p->d_if.ifc,
+						   PH_DATA | INDICATION,
+						   skb);
+		}
+	} while (1);
+}				/* rx_d_frame */
+
+/*************************************************************/
+/* a B-frame has been received (perhaps not fully completed) */
+/*************************************************************/
+static void
+rx_b_frame(struct hfc4s8s_btype *bch)
+{
+	int z1, z2, hdlc_complete;
+	u_char f1, f2;
+	struct hfc4s8s_l1 *l1 = bch->l1p;
+	struct sk_buff *skb;
+
+	if (!l1->enabled || (bch->mode == L1_MODE_NULL))
+		return;
+
+	do {
+		/* RX Fifo */
+		Write_hfc8(l1->hw, R_FIFO,
+			   (l1->st_num * 8 + ((bch->bchan == 1) ? 1 : 3)));
+		wait_busy(l1->hw);
+
+		if (bch->mode == L1_MODE_HDLC) {
+			f1 = Read_hfc8_stable(l1->hw, A_F1);
+			f2 = Read_hfc8(l1->hw, A_F2);
+			hdlc_complete = ((f1 ^ f2) & MAX_F_CNT);
+		} else
+			hdlc_complete = 0;
+		z1 = Read_hfc16_stable(l1->hw, A_Z1);
+		z2 = Read_hfc16(l1->hw, A_Z2);
+		z1 = (z1 - z2);
+		if (hdlc_complete)
+			z1++;
+		if (z1 < 0)
+			z1 += 384;
+
+		if (!z1)
+			break;
+
+		if (!(skb = bch->rx_skb)) {
+			if (!
+			    (skb =
+			     dev_alloc_skb((bch->mode ==
+					    L1_MODE_TRANS) ? z1
+					   : (MAX_B_FRAME_SIZE + 3)))) {
+				printk(KERN_ERR
+				       "HFC-4S/8S: Could not allocate B "
+				       "channel receive buffer");
+				return;
+			}
+			bch->rx_ptr = skb->data;
+			bch->rx_skb = skb;
+		}
+
+		skb->len = (bch->rx_ptr - skb->data) + z1;
+
+		/* HDLC length check */
+		if ((bch->mode == L1_MODE_HDLC) &&
+		    ((hdlc_complete && (skb->len < 4)) ||
+		     (skb->len > (MAX_B_FRAME_SIZE + 3)))) {
+
+			skb->len = 0;
+			bch->rx_ptr = skb->data;
+			Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+			wait_busy(l1->hw);
+			return;
+		}
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+		SetRegAddr(l1->hw, A_FIFO_DATA0);
+#endif
+
+		while (z1 >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			*((unsigned long *) bch->rx_ptr) =
+			    Read_hfc32(l1->hw, A_FIFO_DATA0);
+#else
+			*((unsigned long *) bch->rx_ptr) =
+			    fRead_hfc32(l1->hw);
+#endif
+			bch->rx_ptr += 4;
+			z1 -= 4;
+		}
+
+		while (z1--)
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			*(bch->rx_ptr++) = Read_hfc8(l1->hw, A_FIFO_DATA0);
+#else
+			*(bch->rx_ptr++) = fRead_hfc8(l1->hw);
+#endif
+
+		if (hdlc_complete) {
+			/* increment f counter */
+			Write_hfc8(l1->hw, A_INC_RES_FIFO, 1);
+			wait_busy(l1->hw);
+
+			/* hdlc crc check */
+			bch->rx_ptr--;
+			if (*bch->rx_ptr) {
+				skb->len = 0;
+				bch->rx_ptr = skb->data;
+				continue;
+			}
+			skb->len -= 3;
+		}
+		if (hdlc_complete || (bch->mode == L1_MODE_TRANS)) {
+			bch->rx_skb = NULL;
+			bch->rx_ptr = NULL;
+			bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+					   PH_DATA | INDICATION, skb);
+		}
+
+	} while (1);
+}				/* rx_b_frame */
+
+/********************************************/
+/* a D-frame has been/should be transmitted */
+/********************************************/
+static void
+tx_d_frame(struct hfc4s8s_l1 *l1p)
+{
+	struct sk_buff *skb;
+	u_char f1, f2;
+	u_char *cp;
+	int cnt;
+
+	if (l1p->l1_state != 7)
+		return;
+
+	/* TX fifo */
+	Write_hfc8(l1p->hw, R_FIFO, (l1p->st_num * 8 + 4));
+	wait_busy(l1p->hw);
+
+	f1 = Read_hfc8(l1p->hw, A_F1);
+	f2 = Read_hfc8_stable(l1p->hw, A_F2);
+
+	if ((f1 ^ f2) & MAX_F_CNT)
+		return;		/* fifo is still filled */
+
+	if (l1p->tx_cnt > 0) {
+		cnt = l1p->tx_cnt;
+		l1p->tx_cnt = 0;
+		l1p->d_if.ifc.l1l2(&l1p->d_if.ifc, PH_DATA | CONFIRM,
+				   (void *) cnt);
+	}
+
+	if ((skb = skb_dequeue(&l1p->d_tx_queue))) {
+		cp = skb->data;
+		cnt = skb->len;
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+		SetRegAddr(l1p->hw, A_FIFO_DATA0);
+#endif
+
+		while (cnt >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			fWrite_hfc32(l1p->hw, A_FIFO_DATA0,
+				     *(unsigned long *) cp);
+#else
+			SetRegAddr(l1p->hw, A_FIFO_DATA0);
+			fWrite_hfc32(l1p->hw, *(unsigned long *) cp);
+#endif
+			cp += 4;
+			cnt -= 4;
+		}
+
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+		while (cnt--)
+			fWrite_hfc8(l1p->hw, A_FIFO_DATA0, *cp++);
+#else
+		while (cnt--)
+			fWrite_hfc8(l1p->hw, *cp++);
+#endif
+
+		l1p->tx_cnt = skb->truesize;
+		Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1);	/* increment f counter */
+		wait_busy(l1p->hw);
+
+		dev_kfree_skb(skb);
+	}
+}				/* tx_d_frame */
+
+/******************************************************/
+/* a B-frame may be transmitted (or is not completed) */
+/******************************************************/
+static void
+tx_b_frame(struct hfc4s8s_btype *bch)
+{
+	struct sk_buff *skb;
+	struct hfc4s8s_l1 *l1 = bch->l1p;
+	u_char *cp;
+	int cnt, max, hdlc_num, ack_len = 0;
+
+	if (!l1->enabled || (bch->mode == L1_MODE_NULL))
+		return;
+
+	/* TX fifo */
+	Write_hfc8(l1->hw, R_FIFO,
+		   (l1->st_num * 8 + ((bch->bchan == 1) ? 0 : 2)));
+	wait_busy(l1->hw);
+	do {
+
+		if (bch->mode == L1_MODE_HDLC) {
+			hdlc_num = Read_hfc8(l1->hw, A_F1) & MAX_F_CNT;
+			hdlc_num -=
+			    (Read_hfc8_stable(l1->hw, A_F2) & MAX_F_CNT);
+			if (hdlc_num < 0)
+				hdlc_num += 16;
+			if (hdlc_num >= 15)
+				break;	/* fifo still filled up with hdlc frames */
+		} else
+			hdlc_num = 0;
+
+		if (!(skb = bch->tx_skb)) {
+			if (!(skb = skb_dequeue(&bch->tx_queue))) {
+				l1->hw->mr.fifo_slow_timer_service[l1->
+								   st_num]
+				    &= ~((bch->bchan == 1) ? 1 : 4);
+				break;	/* list empty */
+			}
+			bch->tx_skb = skb;
+			bch->tx_cnt = 0;
+		}
+
+		if (!hdlc_num)
+			l1->hw->mr.fifo_slow_timer_service[l1->st_num] |=
+			    ((bch->bchan == 1) ? 1 : 4);
+		else
+			l1->hw->mr.fifo_slow_timer_service[l1->st_num] &=
+			    ~((bch->bchan == 1) ? 1 : 4);
+
+		max = Read_hfc16_stable(l1->hw, A_Z2);
+		max -= Read_hfc16(l1->hw, A_Z1);
+		if (max <= 0)
+			max += 384;
+		max--;
+
+		if (max < 16)
+			break;	/* don't write to small amounts of bytes */
+
+		cnt = skb->len - bch->tx_cnt;
+		if (cnt > max)
+			cnt = max;
+		cp = skb->data + bch->tx_cnt;
+		bch->tx_cnt += cnt;
+
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+		SetRegAddr(l1->hw, A_FIFO_DATA0);
+#endif
+		while (cnt >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			fWrite_hfc32(l1->hw, A_FIFO_DATA0,
+				     *(unsigned long *) cp);
+#else
+			fWrite_hfc32(l1->hw, *(unsigned long *) cp);
+#endif
+			cp += 4;
+			cnt -= 4;
+		}
+
+		while (cnt--)
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			fWrite_hfc8(l1->hw, A_FIFO_DATA0, *cp++);
+#else
+			fWrite_hfc8(l1->hw, *cp++);
+#endif
+
+		if (bch->tx_cnt >= skb->len) {
+			if (bch->mode == L1_MODE_HDLC) {
+				/* increment f counter */
+				Write_hfc8(l1->hw, A_INC_RES_FIFO, 1);
+			}
+			ack_len += skb->truesize;
+			bch->tx_skb = 0;
+			bch->tx_cnt = 0;
+			dev_kfree_skb(skb);
+		} else
+			/* Re-Select */
+			Write_hfc8(l1->hw, R_FIFO,
+				   (l1->st_num * 8 +
+				    ((bch->bchan == 1) ? 0 : 2)));
+		wait_busy(l1->hw);
+	} while (1);
+
+	if (ack_len)
+		bch->b_if.ifc.l1l2((struct hisax_if *) &bch->b_if,
+				   PH_DATA | CONFIRM, (void *) ack_len);
+}				/* tx_b_frame */
+
+/*************************************/
+/* bottom half handler for interrupt */
+/*************************************/
+static void
+hfc4s8s_bh(hfc4s8s_hw * hw)
+{
+	u_char b;
+	struct hfc4s8s_l1 *l1p;
+	volatile u_char *fifo_stat;
+	int idx;
+
+	/* handle layer 1 state changes */
+	b = 1;
+	l1p = hw->l1;
+	while (b) {
+		if ((b & hw->mr.r_irq_statech)) {
+			/* reset l1 event */
+			hw->mr.r_irq_statech &= ~b;
+			if (l1p->enabled) {
+				if (l1p->nt_mode) {
+					u_char oldstate = l1p->l1_state;
+
+					Write_hfc8(l1p->hw, R_ST_SEL,
+						   l1p->st_num);
+					l1p->l1_state =
+					    Read_hfc8(l1p->hw,
+						      A_ST_RD_STA) & 0xf;
+
+					if ((oldstate == 3)
+					    && (l1p->l1_state != 3))
+						l1p->d_if.ifc.l1l2(&l1p->
+								   d_if.
+								   ifc,
+								   PH_DEACTIVATE
+								   |
+								   INDICATION,
+								   NULL);
+
+					if (l1p->l1_state != 2) {
+						del_timer(&l1p->l1_timer);
+						if (l1p->l1_state == 3) {
+							l1p->d_if.ifc.
+							    l1l2(&l1p->
+								 d_if.ifc,
+								 PH_ACTIVATE
+								 |
+								 INDICATION,
+								 NULL);
+						}
+					} else {
+						/* allow transition */
+						Write_hfc8(hw, A_ST_WR_STA,
+							   M_SET_G2_G3);
+						mod_timer(&l1p->l1_timer,
+							  jiffies +
+							  L1_TIMER_T1);
+					}
+					printk(KERN_INFO
+					       "HFC-4S/8S: NT ch %d l1 state %d -> %d\n",
+					       l1p->st_num, oldstate,
+					       l1p->l1_state);
+				} else {
+					u_char oldstate = l1p->l1_state;
+
+					Write_hfc8(l1p->hw, R_ST_SEL,
+						   l1p->st_num);
+					l1p->l1_state =
+					    Read_hfc8(l1p->hw,
+						      A_ST_RD_STA) & 0xf;
+
+					if (((l1p->l1_state == 3) &&
+					     ((oldstate == 7) ||
+					      (oldstate == 8))) ||
+					    ((timer_pending
+					      (&l1p->l1_timer))
+					     && (l1p->l1_state == 8))) {
+						mod_timer(&l1p->l1_timer,
+							  L1_TIMER_T4 +
+							  jiffies);
+					} else {
+						if (l1p->l1_state == 7) {
+							del_timer(&l1p->
+								  l1_timer);
+							l1p->d_if.ifc.
+							    l1l2(&l1p->
+								 d_if.ifc,
+								 PH_ACTIVATE
+								 |
+								 INDICATION,
+								 NULL);
+							tx_d_frame(l1p);
+						}
+						if (l1p->l1_state == 3) {
+							if (oldstate != 3)
+								l1p->d_if.
+								    ifc.
+								    l1l2
+								    (&l1p->
+								     d_if.
+								     ifc,
+								     PH_DEACTIVATE
+								     |
+								     INDICATION,
+								     NULL);
+						}
+					}
+					printk(KERN_INFO
+					       "HFC-4S/8S: TE %d ch %d l1 state %d -> %d\n",
+					       l1p->hw->cardnum,
+					       l1p->st_num, oldstate,
+					       l1p->l1_state);
+				}
+			}
+		}
+		b <<= 1;
+		l1p++;
+	}
+
+	/* now handle the fifos */
+	idx = 0;
+	fifo_stat = hw->mr.r_irq_fifo_blx;
+	l1p = hw->l1;
+	while (idx < hw->driver_data.max_st_ports) {
+
+		if (hw->mr.timer_irq) {
+			*fifo_stat |= hw->mr.fifo_rx_trans_enables[idx];
+			if (hw->fifo_sched_cnt <= 0) {
+				*fifo_stat |=
+				    hw->mr.fifo_slow_timer_service[l1p->
+								   st_num];
+			}
+		}
+		/* ignore fifo 6 (TX E fifo) */
+		*fifo_stat &= 0xff - 0x40;
+
+		while (*fifo_stat) {
+
+			if (!l1p->nt_mode) {
+				/* RX Fifo has data to read */
+				if ((*fifo_stat & 0x20)) {
+					*fifo_stat &= ~0x20;
+					rx_d_frame(l1p, 0);
+				}
+				/* E Fifo has data to read */
+				if ((*fifo_stat & 0x80)) {
+					*fifo_stat &= ~0x80;
+					rx_d_frame(l1p, 1);
+				}
+				/* TX Fifo completed send */
+				if ((*fifo_stat & 0x10)) {
+					*fifo_stat &= ~0x10;
+					tx_d_frame(l1p);
+				}
+			}
+			/* B1 RX Fifo has data to read */
+			if ((*fifo_stat & 0x2)) {
+				*fifo_stat &= ~0x2;
+				rx_b_frame(l1p->b_ch);
+			}
+			/* B1 TX Fifo has send completed */
+			if ((*fifo_stat & 0x1)) {
+				*fifo_stat &= ~0x1;
+				tx_b_frame(l1p->b_ch);
+			}
+			/* B2 RX Fifo has data to read */
+			if ((*fifo_stat & 0x8)) {
+				*fifo_stat &= ~0x8;
+				rx_b_frame(l1p->b_ch + 1);
+			}
+			/* B2 TX Fifo has send completed */
+			if ((*fifo_stat & 0x4)) {
+				*fifo_stat &= ~0x4;
+				tx_b_frame(l1p->b_ch + 1);
+			}
+		}
+		fifo_stat++;
+		l1p++;
+		idx++;
+	}
+
+	if (hw->fifo_sched_cnt <= 0)
+		hw->fifo_sched_cnt += (1 << (7 - TRANS_TIMER_MODE));
+	hw->mr.timer_irq = 0;	/* clear requested timer irq */
+}				/* hfc4s8s_bh */
+
+/*********************/
+/* interrupt handler */
+/*********************/
+static irqreturn_t
+hfc4s8s_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	hfc4s8s_hw *hw = dev_id;
+	u_char b, ovr;
+	volatile u_char *ovp;
+	int idx;
+	u_char old_ioreg;
+
+	if (!hw || !(hw->mr.r_irq_ctrl & M_GLOB_IRQ_EN))
+		return IRQ_NONE;
+
+#ifndef	CONFIG_HISAX_HFC4S8S_PCIMEM
+	/* read current selected regsister */
+	old_ioreg = GetRegAddr(hw);
+#endif
+
+	/* Layer 1 State change */
+	hw->mr.r_irq_statech |=
+	    (Read_hfc8(hw, R_SCI) & hw->mr.r_irqmsk_statchg);
+	if (!
+	    (b = (Read_hfc8(hw, R_STATUS) & (M_MISC_IRQSTA | M_FR_IRQSTA)))
+&& !hw->mr.r_irq_statech) {
+#ifndef	CONFIG_HISAX_HFC4S8S_PCIMEM
+		SetRegAddr(hw, old_ioreg);
+#endif
+		return IRQ_NONE;
+	}
+
+	/* timer event */
+	if (Read_hfc8(hw, R_IRQ_MISC) & M_TI_IRQ) {
+		hw->mr.timer_irq = 1;
+		hw->fifo_sched_cnt--;
+	}
+
+	/* FIFO event */
+	if ((ovr = Read_hfc8(hw, R_IRQ_OVIEW))) {
+		hw->mr.r_irq_oview |= ovr;
+		idx = R_IRQ_FIFO_BL0;
+		ovp = hw->mr.r_irq_fifo_blx;
+		while (ovr) {
+			if ((ovr & 1)) {
+				*ovp |= Read_hfc8(hw, idx);
+			}
+			ovp++;
+			idx++;
+			ovr >>= 1;
+		}
+	}
+
+	/* queue the request to allow other cards to interrupt */
+	schedule_work(&hw->tqueue);
+
+#ifndef	CONFIG_HISAX_HFC4S8S_PCIMEM
+	SetRegAddr(hw, old_ioreg);
+#endif
+	return IRQ_HANDLED;
+}				/* hfc4s8s_interrupt */
+
+/***********************************************************************/
+/* reset the complete chip, don't release the chips irq but disable it */
+/***********************************************************************/
+static void
+chipreset(hfc4s8s_hw * hw)
+{
+	u_long flags;
+
+	spin_lock_irqsave(&hw->lock, flags);
+	Write_hfc8(hw, R_CTRL, 0);	/* use internal RAM */
+	Write_hfc8(hw, R_RAM_MISC, 0);	/* 32k*8 RAM */
+	Write_hfc8(hw, R_FIFO_MD, 0);	/* fifo mode 386 byte/fifo simple mode */
+	Write_hfc8(hw, R_CIRM, M_SRES);	/* reset chip */
+	hw->mr.r_irq_ctrl = 0;	/* interrupt is inactive */
+	spin_unlock_irqrestore(&hw->lock, flags);
+
+	udelay(3);
+	Write_hfc8(hw, R_CIRM, 0);	/* disable reset */
+	wait_busy(hw);
+
+	Write_hfc8(hw, R_PCM_MD0, M_PCM_MD);	/* master mode */
+	Write_hfc8(hw, R_RAM_MISC, M_FZ_MD);	/* transmit fifo option */
+	if (hw->driver_data.clock_mode == 1)
+		Write_hfc8(hw, R_BRG_PCM_CFG, M_PCM_CLK);	/* PCM clk / 2 */
+	Write_hfc8(hw, R_TI_WD, TRANS_TIMER_MODE);	/* timer interval */
+
+	memset(&hw->mr, 0, sizeof(hw->mr));
+}				/* chipreset */
+
+/********************************************/
+/* disable/enable hardware in nt or te mode */
+/********************************************/
+void
+hfc_hardware_enable(hfc4s8s_hw * hw, int enable, int nt_mode)
+{
+	u_long flags;
+	char if_name[40];
+	int i;
+
+	if (enable) {
+		/* save system vars */
+		hw->nt_mode = nt_mode;
+
+		/* enable fifo and state irqs, but not global irq enable */
+		hw->mr.r_irq_ctrl = M_FIFO_IRQ;
+		Write_hfc8(hw, R_IRQ_CTRL, hw->mr.r_irq_ctrl);
+		hw->mr.r_irqmsk_statchg = 0;
+		Write_hfc8(hw, R_SCI_MSK, hw->mr.r_irqmsk_statchg);
+		Write_hfc8(hw, R_PWM_MD, 0x80);
+		Write_hfc8(hw, R_PWM1, 26);
+		if (!nt_mode)
+			Write_hfc8(hw, R_ST_SYNC, M_AUTO_SYNC);
+
+		/* enable the line interfaces and fifos */
+		for (i = 0; i < hw->driver_data.max_st_ports; i++) {
+			hw->mr.r_irqmsk_statchg |= (1 << i);
+			Write_hfc8(hw, R_SCI_MSK, hw->mr.r_irqmsk_statchg);
+			Write_hfc8(hw, R_ST_SEL, i);
+			Write_hfc8(hw, A_ST_CLK_DLY,
+				   ((nt_mode) ? CLKDEL_NT : CLKDEL_TE));
+			hw->mr.r_ctrl0 = ((nt_mode) ? CTRL0_NT : CTRL0_TE);
+			Write_hfc8(hw, A_ST_CTRL0, hw->mr.r_ctrl0);
+			Write_hfc8(hw, A_ST_CTRL2, 3);
+			Write_hfc8(hw, A_ST_WR_STA, 0);	/* enable state machine */
+
+			hw->l1[i].enabled = 1;
+			hw->l1[i].nt_mode = nt_mode;
+
+			if (!nt_mode) {
+				/* setup E-fifo */
+				Write_hfc8(hw, R_FIFO, i * 8 + 7);	/* E fifo */
+				wait_busy(hw);
+				Write_hfc8(hw, A_CON_HDLC, 0x11);	/* HDLC mode, 1 fill, connect ST */
+				Write_hfc8(hw, A_SUBCH_CFG, 2);	/* only 2 bits */
+				Write_hfc8(hw, A_IRQ_MSK, 1);	/* enable interrupt */
+				Write_hfc8(hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+				wait_busy(hw);
+
+				/* setup D RX-fifo */
+				Write_hfc8(hw, R_FIFO, i * 8 + 5);	/* RX fifo */
+				wait_busy(hw);
+				Write_hfc8(hw, A_CON_HDLC, 0x11);	/* HDLC mode, 1 fill, connect ST */
+				Write_hfc8(hw, A_SUBCH_CFG, 2);	/* only 2 bits */
+				Write_hfc8(hw, A_IRQ_MSK, 1);	/* enable interrupt */
+				Write_hfc8(hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+				wait_busy(hw);
+
+				/* setup D TX-fifo */
+				Write_hfc8(hw, R_FIFO, i * 8 + 4);	/* TX fifo */
+				wait_busy(hw);
+				Write_hfc8(hw, A_CON_HDLC, 0x11);	/* HDLC mode, 1 fill, connect ST */
+				Write_hfc8(hw, A_SUBCH_CFG, 2);	/* only 2 bits */
+				Write_hfc8(hw, A_IRQ_MSK, 1);	/* enable interrupt */
+				Write_hfc8(hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+				wait_busy(hw);
+			}
+
+			sprintf(if_name, "hfc4s8s_%d%d_", hw->cardnum, i);
+
+			if (hisax_register
+			    (&hw->l1[i].d_if, hw->l1[i].b_table, if_name,
+			     ((nt_mode) ? 3 : 2))) {
+
+				hw->l1[i].enabled = 0;
+				hw->mr.r_irqmsk_statchg &= ~(1 << i);
+				Write_hfc8(hw, R_SCI_MSK,
+					   hw->mr.r_irqmsk_statchg);
+				printk(KERN_INFO
+				       "HFC-4S/8S: Unable to register S/T device %s, break\n",
+				       if_name);
+				break;
+			}
+		}
+		spin_lock_irqsave(&hw->lock, flags);
+		hw->mr.r_irq_ctrl |= M_GLOB_IRQ_EN;
+		Write_hfc8(hw, R_IRQ_CTRL, hw->mr.r_irq_ctrl);
+		spin_unlock_irqrestore(&hw->lock, flags);
+	} else {
+		/* disable hardware */
+		spin_lock_irqsave(&hw->lock, flags);
+		hw->mr.r_irq_ctrl &= ~M_GLOB_IRQ_EN;
+		Write_hfc8(hw, R_IRQ_CTRL, hw->mr.r_irq_ctrl);
+		spin_unlock_irqrestore(&hw->lock, flags);
+
+		for (i = hw->driver_data.max_st_ports - 1; i >= 0; i--) {
+			hw->l1[i].enabled = 0;
+			hisax_unregister(&hw->l1[i].d_if);
+			del_timer(&hw->l1[i].l1_timer);
+			skb_queue_purge(&hw->l1[i].d_tx_queue);
+			skb_queue_purge(&hw->l1[i].b_ch[0].tx_queue);
+			skb_queue_purge(&hw->l1[i].b_ch[1].tx_queue);
+		}
+		chipreset(hw);
+	}
+}				/* hfc_hardware_enable */
+
+/******************************************/
+/* disable memory mapped ports / io ports */
+/******************************************/
+void
+release_pci_ports(hfc4s8s_hw * hw)
+{
+	pci_write_config_word(hw->pdev, PCI_COMMAND, 0);
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+	if (hw->membase)
+		iounmap((void *) hw->membase);
+#else
+	if (hw->iobase)
+		release_region(hw->iobase, 8);
+#endif
+}
+
+/*****************************************/
+/* enable memory mapped ports / io ports */
+/*****************************************/
+void
+enable_pci_ports(hfc4s8s_hw * hw)
+{
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+	pci_write_config_word(hw->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
+#else
+	pci_write_config_word(hw->pdev, PCI_COMMAND, PCI_ENA_REGIO);
+#endif
+}
+
+/*************************************/
+/* initialise the HFC-4s/8s hardware */
+/* return 0 on success.              */
+/*************************************/
+static int __devinit
+setup_instance(hfc4s8s_hw * hw)
+{
+	int err = -EIO;
+	int i;
+
+	for (i = 0; i < HFC_MAX_ST; i++) {
+		struct hfc4s8s_l1 *l1p;
+
+		l1p = hw->l1 + i;
+		spin_lock_init(&l1p->lock);
+		l1p->hw = hw;
+		l1p->l1_timer.function = (void *) hfc_l1_timer;
+		l1p->l1_timer.data = (long) (l1p);
+		init_timer(&l1p->l1_timer);
+		l1p->st_num = i;
+		skb_queue_head_init(&l1p->d_tx_queue);
+		l1p->d_if.ifc.priv = hw->l1 + i;
+		l1p->d_if.ifc.l2l1 = (void *) dch_l2l1;
+
+		spin_lock_init(&l1p->b_ch[0].lock);
+		l1p->b_ch[0].b_if.ifc.l2l1 = (void *) bch_l2l1;
+		l1p->b_ch[0].b_if.ifc.priv = (void *) &l1p->b_ch[0];
+		l1p->b_ch[0].l1p = hw->l1 + i;
+		l1p->b_ch[0].bchan = 1;
+		l1p->b_table[0] = &l1p->b_ch[0].b_if;
+		skb_queue_head_init(&l1p->b_ch[0].tx_queue);
+
+		spin_lock_init(&l1p->b_ch[1].lock);
+		l1p->b_ch[1].b_if.ifc.l2l1 = (void *) bch_l2l1;
+		l1p->b_ch[1].b_if.ifc.priv = (void *) &l1p->b_ch[1];
+		l1p->b_ch[1].l1p = hw->l1 + i;
+		l1p->b_ch[1].bchan = 2;
+		l1p->b_table[1] = &l1p->b_ch[1].b_if;
+		skb_queue_head_init(&l1p->b_ch[1].tx_queue);
+	}
+
+	enable_pci_ports(hw);
+	chipreset(hw);
+
+	i = Read_hfc8(hw, R_CHIP_ID) >> CHIP_ID_SHIFT;
+	if (i != hw->driver_data.chip_id) {
+		printk(KERN_INFO
+		       "HFC-4S/8S: invalid chip id 0x%x instead of 0x%x, card ignored\n",
+		       i, hw->driver_data.chip_id);
+		goto out;
+	}
+
+	i = Read_hfc8(hw, R_CHIP_RV) & 0xf;
+	if (!i) {
+		printk(KERN_INFO
+		       "HFC-4S/8S: chip revision 0 not supported, card ignored\n");
+		goto out;
+	}
+
+	INIT_WORK(&hw->tqueue, (void *) (void *) hfc4s8s_bh, hw);
+
+	if (request_irq
+	    (hw->irq, hfc4s8s_interrupt, SA_SHIRQ, hw->card_name, hw)) {
+		printk(KERN_INFO
+		       "HFC-4S/8S: unable to alloc irq %d, card ignored\n",
+		       hw->irq);
+		goto out;
+	}
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+	printk(KERN_INFO
+	       "HFC-4S/8S: found PCI card at membase 0x%p, irq %d\n",
+	       hw->hw_membase, hw->irq);
+#else
+	printk(KERN_INFO
+	       "HFC-4S/8S: found PCI card at iobase 0x%x, irq %d\n",
+	       hw->iobase, hw->irq);
+#endif
+
+	hfc_hardware_enable(hw, 1, 0);
+
+	return (0);
+
+      out:
+	hw->irq = 0;
+	release_pci_ports(hw);
+	kfree(hw);
+	return (err);
+}
+
+/*****************************************/
+/* PCI hotplug interface: probe new card */
+/*****************************************/
+static int __devinit
+hfc4s8s_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int err = -ENOMEM;
+	hfc4s8s_param *driver_data = (hfc4s8s_param *) ent->driver_data;
+	hfc4s8s_hw *hw;
+
+	if (!(hw = kmalloc(sizeof(hfc4s8s_hw), GFP_ATOMIC))) {
+		printk(KERN_ERR "No kmem for HFC-4S/8S card\n");
+		return (err);
+	}
+	memset(hw, 0, sizeof(hfc4s8s_hw));
+
+	hw->pdev = pdev;
+	err = pci_enable_device(pdev);
+
+	if (err)
+		goto out;
+
+	hw->cardnum = card_cnt;
+	sprintf(hw->card_name, "hfc4s8s_%d", hw->cardnum);
+	printk(KERN_INFO "HFC-4S/8S: found adapter %s (%s) at %s\n",
+	       driver_data->device_name, hw->card_name, pci_name(pdev));
+
+	spin_lock_init(&hw->lock);
+
+	hw->driver_data = *driver_data;
+	hw->irq = pdev->irq;
+	hw->iobase = pci_resource_start(pdev, 0);
+
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+	hw->hw_membase = (u_char *) pci_resource_start(pdev, 1);
+	hw->membase = ioremap((ulong) hw->hw_membase, 256);
+#else
+	if (!request_region(hw->iobase, 8, hw->card_name)) {
+		printk(KERN_INFO
+		       "HFC-4S/8S: failed to rquest address space at 0x%04x\n",
+		       hw->iobase);
+		goto out;
+	}
+#endif
+
+	pci_set_drvdata(pdev, hw);
+	err = setup_instance(hw);
+	if (!err)
+		card_cnt++;
+	return (err);
+
+      out:
+	kfree(hw);
+	return (err);
+}
+
+/**************************************/
+/* PCI hotplug interface: remove card */
+/**************************************/
+static void __devexit
+hfc4s8s_remove(struct pci_dev *pdev)
+{
+	hfc4s8s_hw *hw = pci_get_drvdata(pdev);
+
+	printk(KERN_INFO "HFC-4S/8S: removing card %d\n", hw->cardnum);
+	hfc_hardware_enable(hw, 0, 0);
+
+	if (hw->irq)
+		free_irq(hw->irq, hw);
+	hw->irq = 0;
+	release_pci_ports(hw);
+
+	card_cnt--;
+	pci_disable_device(pdev);
+	kfree(hw);
+	return;
+}
+
+static struct pci_driver hfc4s8s_driver = {
+      name:"hfc4s8s_l1",
+      probe:hfc4s8s_probe,
+      remove:__devexit_p(hfc4s8s_remove),
+      id_table:hfc4s8s_ids,
+};
+
+/**********************/
+/* driver Module init */
+/**********************/
+static int __init
+hfc4s8s_module_init(void)
+{
+	int err;
+
+	printk(KERN_INFO
+	       "HFC-4S/8S: Layer 1 driver module for HFC-4S/8S isdn chips, %s\n",
+	       hfc4s8s_rev);
+	printk(KERN_INFO
+	       "HFC-4S/8S: (C) 2003 Cornelius Consult, www.cornelius-consult.de\n");
+
+	card_cnt = 0;
+
+	err = pci_register_driver(&hfc4s8s_driver);
+	if (err < 0) {
+		goto out;
+	}
+	printk(KERN_INFO "HFC-4S/8S: found %d cards\n", card_cnt);
+
+#if !defined(CONFIG_HOTPLUG)
+	if (err == 0) {
+		err = -ENODEV;
+		pci_unregister_driver(&hfc4s8s_driver);
+		goto out;
+	}
+#endif
+
+	return 0;
+      out:
+	return (err);
+}				/* hfc4s8s_init_hw */
+
+/*************************************/
+/* driver module exit :              */
+/* release the HFC-4s/8s hardware    */
+/*************************************/
+static void
+hfc4s8s_module_exit(void)
+{
+	pci_unregister_driver(&hfc4s8s_driver);
+	printk(KERN_INFO "HFC-4S/8S: module removed\n");
+}				/* hfc4s8s_release_hw */
+
+module_init(hfc4s8s_module_init);
+module_exit(hfc4s8s_module_exit);
diff --git a/drivers/isdn/hisax/hfc4s8s_l1.h b/drivers/isdn/hisax/hfc4s8s_l1.h
new file mode 100644
index 000000000000..e8f9c077fa85
--- /dev/null
+++ b/drivers/isdn/hisax/hfc4s8s_l1.h
@@ -0,0 +1,88 @@
+/***************************************************************/
+/*  $Id: hfc4s8s_l1.h,v 1.1 2005/02/02 17:28:55 martinb1 Exp $ */
+/*                                                             */
+/*  This file is a minimal required extraction of hfc48scu.h   */
+/*  (Genero 3.2, HFC XML 1.7a for HFC-E1, HFC-4S and HFC-8S)   */
+/*                                                             */
+/*  To get this complete register description contact          */
+/*  Cologne Chip AG :                                          */
+/*  Internet:  http://www.colognechip.com/                     */
+/*  E-Mail:    info@colognechip.com                            */
+/***************************************************************/
+
+#ifndef _HFC4S8S_L1_H_
+#define _HFC4S8S_L1_H_
+
+
+/*
+*  include Genero generated HFC-4S/8S header file hfc48scu.h
+*  for comlete register description. This will define _HFC48SCU_H_
+*  to prevent redefinitions
+*/
+
+// #include "hfc48scu.h"
+
+#ifndef _HFC48SCU_H_
+#define _HFC48SCU_H_
+
+#ifndef PCI_VENDOR_ID_CCD
+#define PCI_VENDOR_ID_CCD	0x1397
+#endif
+
+#define CHIP_ID_4S		0x0C
+#define CHIP_ID_8S		0x08
+#define PCI_DEVICE_ID_4S	0x08B4
+#define PCI_DEVICE_ID_8S	0x16B8
+
+#define R_IRQ_MISC	0x11
+#define M_TI_IRQ	0x02
+#define A_ST_RD_STA	0x30
+#define A_ST_WR_STA	0x30
+#define M_SET_G2_G3	0x80
+#define A_ST_CTRL0	0x31
+#define A_ST_CTRL2	0x33
+#define A_ST_CLK_DLY	0x37
+#define A_Z1		0x04
+#define A_Z2		0x06
+#define R_CIRM		0x00
+#define M_SRES		0x08
+#define R_CTRL		0x01
+#define R_BRG_PCM_CFG	0x02
+#define M_PCM_CLK	0x20
+#define R_RAM_MISC	0x0C
+#define M_FZ_MD		0x80
+#define R_FIFO_MD	0x0D
+#define A_INC_RES_FIFO	0x0E
+#define R_FIFO		0x0F
+#define A_F1		0x0C
+#define A_F2		0x0D
+#define R_IRQ_OVIEW	0x10
+#define R_CHIP_ID	0x16
+#define R_STATUS	0x1C
+#define M_BUSY		0x01
+#define M_MISC_IRQSTA	0x40
+#define M_FR_IRQSTA	0x80
+#define R_CHIP_RV	0x1F
+#define R_IRQ_CTRL	0x13
+#define M_FIFO_IRQ	0x01
+#define M_GLOB_IRQ_EN	0x08
+#define R_PCM_MD0	0x14
+#define M_PCM_MD	0x01
+#define A_FIFO_DATA0	0x80
+#define R_TI_WD		0x1A
+#define R_PWM1		0x39
+#define R_PWM_MD	0x46
+#define R_IRQ_FIFO_BL0	0xC8
+#define A_CON_HDLC	0xFA
+#define A_SUBCH_CFG	0xFB
+#define A_IRQ_MSK	0xFF
+#define R_SCI_MSK	0x12
+#define R_ST_SEL	0x16
+#define R_ST_SYNC	0x17
+#define M_AUTO_SYNC	0x08
+#define R_SCI		0x12
+#define R_IRQMSK_MISC	0x11
+#define M_TI_IRQMSK	0x02
+
+#endif	/* _HFC4S8S_L1_H_ */
+#endif	/* _HFC48SCU_H_ */
diff --git a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c
new file mode 100644
index 000000000000..ebea3feef003
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_2bds0.c
@@ -0,0 +1,1082 @@
+/* $Id: hfc_2bds0.c,v 1.18.2.6 2004/02/11 13:21:33 keil Exp $
+ *
+ * specific routines for CCD's HFC 2BDS0
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hfc_2bds0.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+/*
+#define KDEBUG_DEF
+#include "kdebug.h"
+*/
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static void
+dummyf(struct IsdnCardState *cs, u_char * data, int size)
+{
+	printk(KERN_WARNING "HiSax: hfcd dummy fifo called\n");
+}
+
+static inline u_char
+ReadReg(struct IsdnCardState *cs, int data, u_char reg)
+{
+	register u_char ret;
+
+	if (data) {
+		if (cs->hw.hfcD.cip != reg) { 
+			cs->hw.hfcD.cip = reg;
+			byteout(cs->hw.hfcD.addr | 1, reg);
+		}
+		ret = bytein(cs->hw.hfcD.addr);
+#ifdef HFC_REG_DEBUG
+		if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+			debugl1(cs, "t3c RD %02x %02x", reg, ret);
+#endif
+	} else
+		ret = bytein(cs->hw.hfcD.addr | 1);
+	return (ret);
+}
+
+static inline void
+WriteReg(struct IsdnCardState *cs, int data, u_char reg, u_char value)
+{
+	if (cs->hw.hfcD.cip != reg) { 
+		cs->hw.hfcD.cip = reg;
+		byteout(cs->hw.hfcD.addr | 1, reg);
+	}
+	if (data)
+		byteout(cs->hw.hfcD.addr, value);
+#ifdef HFC_REG_DEBUG
+	if (cs->debug & L1_DEB_HSCX_FIFO && (data != HFCD_DATA_NODEB))
+		debugl1(cs, "t3c W%c %02x %02x", data ? 'D' : 'C', reg, value);
+#endif
+}
+
+/* Interface functions */
+
+static u_char
+readreghfcd(struct IsdnCardState *cs, u_char offset)
+{
+	return(ReadReg(cs, HFCD_DATA, offset));
+}
+
+static void
+writereghfcd(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	WriteReg(cs, HFCD_DATA, offset, value);
+}
+
+static inline int
+WaitForBusy(struct IsdnCardState *cs)
+{
+	int to = 130;
+
+	while (!(ReadReg(cs, HFCD_DATA, HFCD_STAT) & HFCD_BUSY) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: WaitForBusy timeout\n");
+	return (to);
+}
+
+static inline int
+WaitNoBusy(struct IsdnCardState *cs)
+{
+	int to = 130;
+
+	while ((ReadReg(cs, HFCD_STATUS, HFCD_STATUS) & HFCD_BUSY) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to) 
+		printk(KERN_WARNING "HiSax: WaitNoBusy timeout\n");
+	return (to);
+}
+
+static int
+SelFiFo(struct IsdnCardState *cs, u_char FiFo)
+{
+	u_char cip;
+
+	if (cs->hw.hfcD.fifo == FiFo)
+		return(1);
+	switch(FiFo) {
+		case 0: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B1;
+			break;
+		case 1: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B1;
+			break;
+		case 2: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B2;
+			break;
+		case 3: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B2;
+			break;
+		case 4: cip = HFCD_FIFO | HFCD_Z1 | HFCD_SEND;
+			break;
+		case 5: cip = HFCD_FIFO | HFCD_Z1 | HFCD_REC;
+			break;
+		default:
+			debugl1(cs, "SelFiFo Error");
+			return(0);
+	}
+	cs->hw.hfcD.fifo = FiFo;
+	WaitNoBusy(cs);
+	cs->BC_Write_Reg(cs, HFCD_DATA, cip, 0);
+	WaitForBusy(cs);
+	return(2);
+}
+
+static int
+GetFreeFifoBytes_B(struct BCState *bcs)
+{
+	int s;
+
+	if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2)
+		return (bcs->cs->hw.hfcD.bfifosize);
+	s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2];
+	if (s <= 0)
+		s += bcs->cs->hw.hfcD.bfifosize;
+	s = bcs->cs->hw.hfcD.bfifosize - s;
+	return (s);
+}
+
+static int
+GetFreeFifoBytes_D(struct IsdnCardState *cs)
+{
+	int s;
+
+	if (cs->hw.hfcD.f1 == cs->hw.hfcD.f2)
+		return (cs->hw.hfcD.dfifosize);
+	s = cs->hw.hfcD.send[cs->hw.hfcD.f1] - cs->hw.hfcD.send[cs->hw.hfcD.f2];
+	if (s <= 0)
+		s += cs->hw.hfcD.dfifosize;
+	s = cs->hw.hfcD.dfifosize - s;
+	return (s);
+}
+
+static int
+ReadZReg(struct IsdnCardState *cs, u_char reg)
+{
+	int val;
+
+	WaitNoBusy(cs);
+	val = 256 * ReadReg(cs, HFCD_DATA, reg | HFCB_Z_HIGH);
+	WaitNoBusy(cs);
+	val += ReadReg(cs, HFCD_DATA, reg | HFCB_Z_LOW);
+	return (val);
+}
+
+static struct sk_buff
+*hfc_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct sk_buff *skb;
+	struct IsdnCardState *cs = bcs->cs;
+	int idx;
+	int chksum;
+	u_char stat, cip;
+	
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hfc_empty_fifo");
+	idx = 0;
+	if (count > HSCX_BUFMAX + 3) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too large");
+		cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+		while (idx++ < count) {
+			WaitNoBusy(cs);
+			ReadReg(cs, HFCD_DATA_NODEB, cip);
+		}
+		skb = NULL;
+	} else if (count < 4) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too small");
+		cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+#ifdef ERROR_STATISTIC
+		bcs->err_inv++;
+#endif
+		while ((idx++ < count) && WaitNoBusy(cs))
+			ReadReg(cs, HFCD_DATA_NODEB, cip);
+		skb = NULL;
+	} else if (!(skb = dev_alloc_skb(count - 3)))
+		printk(KERN_WARNING "HFC: receive out of memory\n");
+	else {
+		ptr = skb_put(skb, count - 3);
+		idx = 0;
+		cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+		while (idx < (count - 3)) {
+			if (!WaitNoBusy(cs))
+				break;
+			*ptr = ReadReg(cs,  HFCD_DATA_NODEB, cip);
+			ptr++;
+			idx++;
+		}
+		if (idx != count - 3) {
+			debugl1(cs, "RFIFO BUSY error");
+			printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel);
+			dev_kfree_skb_irq(skb);
+			skb = NULL;
+		} else {
+			WaitNoBusy(cs);
+			chksum = (ReadReg(cs, HFCD_DATA, cip) << 8);
+			WaitNoBusy(cs);
+			chksum += ReadReg(cs, HFCD_DATA, cip);
+			WaitNoBusy(cs);
+			stat = ReadReg(cs, HFCD_DATA, cip);
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
+					bcs->channel, chksum, stat);
+			if (stat) {
+				debugl1(cs, "FIFO CRC error");
+				dev_kfree_skb_irq(skb);
+				skb = NULL;
+#ifdef ERROR_STATISTIC
+				bcs->err_crc++;
+#endif
+			}
+		}
+	}
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	stat = ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F2_INC |
+		HFCB_REC | HFCB_CHANNEL(bcs->channel));
+	WaitForBusy(cs);
+	return (skb);
+}
+
+static void
+hfc_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int idx, fcnt;
+	int count;
+	u_char cip;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+	SelFiFo(cs, HFCB_SEND | HFCB_CHANNEL(bcs->channel)); 
+	cip = HFCB_FIFO | HFCB_F1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	bcs->hw.hfc.f1 = ReadReg(cs, HFCD_DATA, cip);
+	WaitNoBusy(cs);
+	cip = HFCB_FIFO | HFCB_F2 | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	bcs->hw.hfc.f2 = ReadReg(cs, HFCD_DATA, cip);
+	bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel));
+ 	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+			bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
+			bcs->hw.hfc.send[bcs->hw.hfc.f1]);
+	fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
+	if (fcnt < 0)
+		fcnt += 32;
+	if (fcnt > 30) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo more as 30 frames");
+		return;
+	}
+	count = GetFreeFifoBytes_B(bcs);
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d count(%ld/%d),%lx",
+			bcs->channel, bcs->tx_skb->len,
+			count, current->state);
+	if (count < bcs->tx_skb->len) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo no fifo mem");
+		return;
+	}
+	cip = HFCB_FIFO | HFCB_FIFO_IN | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+	idx = 0;
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx++]);
+	while (idx < bcs->tx_skb->len) {
+		if (!WaitNoBusy(cs))
+			break;
+		WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx]);
+		idx++;
+	}
+	if (idx != bcs->tx_skb->len) {
+		debugl1(cs, "FIFO Send BUSY error");
+		printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
+	} else {
+		bcs->tx_cnt -= bcs->tx_skb->len;
+		if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+			(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+			u_long	flags;
+			spin_lock_irqsave(&bcs->aclock, flags);
+			bcs->ackcnt += bcs->tx_skb->len;
+			spin_unlock_irqrestore(&bcs->aclock, flags);
+			schedule_event(bcs, B_ACKPENDING);
+		}
+		dev_kfree_skb_any(bcs->tx_skb);
+		bcs->tx_skb = NULL;
+	}
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F1_INC | HFCB_SEND | HFCB_CHANNEL(bcs->channel));
+	WaitForBusy(cs);
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	return;
+}
+
+static void
+hfc_send_data(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	
+	if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		hfc_fill_fifo(bcs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	} else
+		debugl1(cs,"send_data %d blocked", bcs->channel);
+}
+
+void
+main_rec_2bds0(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int z1, z2, rcnt;
+	u_char f1, f2, cip;
+	int receive, count = 5;
+	struct sk_buff *skb;
+
+    Begin:
+	count--;
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs,"rec_data %d blocked", bcs->channel);
+		return;
+	}
+	SelFiFo(cs, HFCB_REC | HFCB_CHANNEL(bcs->channel));
+	cip = HFCB_FIFO | HFCB_F1 | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f1 = ReadReg(cs, HFCD_DATA, cip);
+	cip = HFCB_FIFO | HFCB_F2 | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f2 = ReadReg(cs, HFCD_DATA, cip);
+	if (f1 != f2) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
+				bcs->channel, f1, f2);
+		z1 = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
+		z2 = ReadZReg(cs, HFCB_FIFO | HFCB_Z2 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfcD.bfifosize;
+		rcnt++;
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+				bcs->channel, z1, z2, rcnt);
+		if ((skb = hfc_empty_fifo(bcs, rcnt))) {
+			skb_queue_tail(&bcs->rqueue, skb);
+			schedule_event(bcs, B_RCVBUFREADY);
+		}
+		rcnt = f1 -f2;
+		if (rcnt<0)
+			rcnt += 32;
+		if (rcnt>1)
+			receive = 1;
+		else
+			receive = 0;
+	} else
+		receive = 0;
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	if (count && receive)
+		goto Begin;	
+	return;
+}
+
+void
+mode_2bs0(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "HFCD bchannel mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	switch (mode) {
+		case (L1_MODE_NULL):
+			if (bc) {
+				cs->hw.hfcD.conn |= 0x18;
+				cs->hw.hfcD.sctrl &= ~SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcD.conn |= 0x3;
+				cs->hw.hfcD.sctrl &= ~SCTRL_B1_ENA;
+			}
+			break;
+		case (L1_MODE_TRANS):
+			if (bc) {
+				cs->hw.hfcD.ctmt |= 2;
+				cs->hw.hfcD.conn &= ~0x18;
+				cs->hw.hfcD.sctrl |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcD.ctmt |= 1;
+				cs->hw.hfcD.conn &= ~0x3;
+				cs->hw.hfcD.sctrl |= SCTRL_B1_ENA;
+			}
+			break;
+		case (L1_MODE_HDLC):
+			if (bc) {
+				cs->hw.hfcD.ctmt &= ~2;
+				cs->hw.hfcD.conn &= ~0x18;
+				cs->hw.hfcD.sctrl |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcD.ctmt &= ~1;
+				cs->hw.hfcD.conn &= ~0x3;
+				cs->hw.hfcD.sctrl |= SCTRL_B1_ENA;
+			}
+			break;
+	}
+	WriteReg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl);
+	WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
+	WriteReg(cs, HFCD_DATA, HFCD_CONN, cs->hw.hfcD.conn);
+}
+
+static void
+hfc_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+//				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+			} else {
+//				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->tx_skb = skb;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			mode_2bs0(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			mode_2bs0(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_2bs0(struct BCState *bcs)
+{
+	mode_2bs0(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+static int
+open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_2b(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hfcstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hfc_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+static void
+hfcd_bh(struct IsdnCardState *cs)
+{
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+		switch (cs->dc.hfcd.ph_state) {
+			case (0):
+				l1_msg(cs, HW_RESET | INDICATION, NULL);
+				break;
+			case (3):
+				l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+				break;
+			case (8):
+				l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+				break;
+			case (6):
+				l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+				break;
+			case (7):
+				l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+				break;
+			default:
+				break;
+		}
+	}
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+}
+
+static
+int receive_dmsg(struct IsdnCardState *cs)
+{
+	struct sk_buff *skb;
+	int idx;
+	int rcnt, z1, z2;
+	u_char stat, cip, f1, f2;
+	int chksum;
+	int count=5;
+	u_char *ptr;
+
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_dmsg blocked");
+		return(1);
+	}
+	SelFiFo(cs, 4 | HFCD_REC);
+	cip = HFCD_FIFO | HFCD_F1 | HFCD_REC;
+	WaitNoBusy(cs);
+	f1 = cs->readisac(cs, cip) & 0xf;
+	cip = HFCD_FIFO | HFCD_F2 | HFCD_REC;
+	WaitNoBusy(cs);
+	f2 = cs->readisac(cs, cip) & 0xf;
+	while ((f1 != f2) && count--) {
+		z1 = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_REC);
+		z2 = ReadZReg(cs, HFCD_FIFO | HFCD_Z2 | HFCD_REC);
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfcD.dfifosize;
+		rcnt++;
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfcd recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)",
+				f1, f2, z1, z2, rcnt);
+		idx = 0;
+		cip = HFCD_FIFO | HFCD_FIFO_OUT | HFCD_REC;
+		if (rcnt > MAX_DFRAME_LEN + 3) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "empty_fifo d: incoming packet too large");
+			while (idx < rcnt) {
+				if (!(WaitNoBusy(cs)))
+					break;
+				ReadReg(cs, HFCD_DATA_NODEB, cip);
+				idx++;
+			}
+		} else if (rcnt < 4) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "empty_fifo d: incoming packet too small");
+			while ((idx++ < rcnt) && WaitNoBusy(cs))
+				ReadReg(cs, HFCD_DATA_NODEB, cip);
+		} else if ((skb = dev_alloc_skb(rcnt - 3))) {
+			ptr = skb_put(skb, rcnt - 3);
+			while (idx < (rcnt - 3)) {
+				if (!(WaitNoBusy(cs)))
+					break;
+				*ptr = ReadReg(cs, HFCD_DATA_NODEB, cip);
+				idx++;
+				ptr++;
+			}
+			if (idx != (rcnt - 3)) {
+				debugl1(cs, "RFIFO D BUSY error");
+				printk(KERN_WARNING "HFC DFIFO channel BUSY Error\n");
+				dev_kfree_skb_irq(skb);
+				skb = NULL;
+#ifdef ERROR_STATISTIC
+				cs->err_rx++;
+#endif
+			} else {
+				WaitNoBusy(cs);
+				chksum = (ReadReg(cs, HFCD_DATA, cip) << 8);
+				WaitNoBusy(cs);
+				chksum += ReadReg(cs, HFCD_DATA, cip);
+				WaitNoBusy(cs);
+				stat = ReadReg(cs, HFCD_DATA, cip);
+				if (cs->debug & L1_DEB_ISAC)
+					debugl1(cs, "empty_dfifo chksum %x stat %x",
+						chksum, stat);
+				if (stat) {
+					debugl1(cs, "FIFO CRC error");
+					dev_kfree_skb_irq(skb);
+					skb = NULL;
+#ifdef ERROR_STATISTIC
+					cs->err_crc++;
+#endif
+				} else {
+					skb_queue_tail(&cs->rq, skb);
+					schedule_event(cs, D_RCVBUFREADY);
+				}
+			}
+		} else
+			printk(KERN_WARNING "HFC: D receive out of memory\n");
+		WaitForBusy(cs);
+		cip = HFCD_FIFO | HFCD_F2_INC | HFCD_REC;
+		WaitNoBusy(cs);
+		stat = ReadReg(cs, HFCD_DATA, cip);
+		WaitForBusy(cs);
+		cip = HFCD_FIFO | HFCD_F2 | HFCD_REC;
+		WaitNoBusy(cs);
+		f2 = cs->readisac(cs, cip) & 0xf;
+	}
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	return(1);
+} 
+
+static void
+hfc_fill_dfifo(struct IsdnCardState *cs)
+{
+	int idx, fcnt;
+	int count;
+	u_char cip;
+
+	if (!cs->tx_skb)
+		return;
+	if (cs->tx_skb->len <= 0)
+		return;
+
+	SelFiFo(cs, 4 | HFCD_SEND);
+	cip = HFCD_FIFO | HFCD_F1 | HFCD_SEND;
+	WaitNoBusy(cs);
+	cs->hw.hfcD.f1 = ReadReg(cs, HFCD_DATA, cip) & 0xf;
+	WaitNoBusy(cs);
+	cip = HFCD_FIFO | HFCD_F2 | HFCD_SEND;
+	cs->hw.hfcD.f2 = ReadReg(cs, HFCD_DATA, cip) & 0xf;
+	cs->hw.hfcD.send[cs->hw.hfcD.f1] = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_SEND);
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "hfc_fill_Dfifo f1(%d) f2(%d) z1(%x)",
+			cs->hw.hfcD.f1, cs->hw.hfcD.f2,
+			cs->hw.hfcD.send[cs->hw.hfcD.f1]);
+	fcnt = cs->hw.hfcD.f1 - cs->hw.hfcD.f2;
+	if (fcnt < 0)
+		fcnt += 16;
+	if (fcnt > 14) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_Dfifo more as 14 frames");
+		return;
+	}
+	count = GetFreeFifoBytes_D(cs);
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "hfc_fill_Dfifo count(%ld/%d)",
+			cs->tx_skb->len, count);
+	if (count < cs->tx_skb->len) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfc_fill_Dfifo no fifo mem");
+		return;
+	}
+	cip = HFCD_FIFO | HFCD_FIFO_IN | HFCD_SEND;
+	idx = 0;
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx++]);
+	while (idx < cs->tx_skb->len) {
+		if (!(WaitNoBusy(cs)))
+			break;
+		WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx]);
+		idx++;
+	}
+	if (idx != cs->tx_skb->len) {
+		debugl1(cs, "DFIFO Send BUSY error");
+		printk(KERN_WARNING "HFC S DFIFO channel BUSY Error\n");
+	}
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	ReadReg(cs, HFCD_DATA, HFCD_FIFO | HFCD_F1_INC | HFCD_SEND);
+	dev_kfree_skb_any(cs->tx_skb);
+	cs->tx_skb = NULL;
+	WaitForBusy(cs);
+	return;
+}
+
+static 
+struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+	if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+		return(&cs->bcs[0]);
+	else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+		return(&cs->bcs[1]);
+	else
+		return(NULL);
+}
+
+void
+hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val)
+{
+       	u_char exval;
+       	struct BCState *bcs;
+	int count=15;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFCD irq %x %s", val,
+			test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
+			"locked" : "unlocked");
+	val &= cs->hw.hfcD.int_m1;
+	if (val & 0x40) { /* TE state machine irq */
+		exval = cs->readisac(cs, HFCD_STATES) & 0xf;
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcd.ph_state,
+				exval);
+		cs->dc.hfcd.ph_state = exval;
+		schedule_event(cs, D_L1STATECHANGE);
+		val &= ~0x40;
+	}
+	while (val) {
+		if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			cs->hw.hfcD.int_s1 |= val;
+			return;
+		}
+		if (cs->hw.hfcD.int_s1 & 0x18) {
+			exval = val;
+			val =  cs->hw.hfcD.int_s1;
+			cs->hw.hfcD.int_s1 = exval;
+		}	
+		if (val & 0x08) {
+			if (!(bcs=Sel_BCS(cs, 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x08 IRQ");
+			} else 
+				main_rec_2bds0(bcs);
+		}
+		if (val & 0x10) {
+			if (!(bcs=Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x10 IRQ");
+			} else 
+				main_rec_2bds0(bcs);
+		}
+		if (val & 0x01) {
+			if (!(bcs=Sel_BCS(cs, 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x01 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfc_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs,"fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfc_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs,"fill_data %d blocked", bcs->channel);
+					} else {
+						schedule_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x02) {
+			if (!(bcs=Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x02 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfc_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs,"fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfc_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs,"fill_data %d blocked", bcs->channel);
+					} else {
+						schedule_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x20) {	/* receive dframe */
+			receive_dmsg(cs);
+		}
+		if (val & 0x04) {	/* dframe transmitted */
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			if (cs->tx_skb) {
+				if (cs->tx_skb->len) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfc_fill_dfifo(cs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else {
+						debugl1(cs, "hfc_fill_dfifo irq blocked");
+					}
+					goto afterXPR;
+				} else {
+					dev_kfree_skb_irq(cs->tx_skb);
+					cs->tx_cnt = 0;
+					cs->tx_skb = NULL;
+				}
+			}
+			if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+				cs->tx_cnt = 0;
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfc_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else {
+					debugl1(cs, "hfc_fill_dfifo irq blocked");
+				}
+			} else
+				schedule_event(cs, D_XMTBUFREADY);
+		}
+      afterXPR:
+		if (cs->hw.hfcD.int_s1 && count--) {
+			val = cs->hw.hfcD.int_s1;
+			cs->hw.hfcD.int_s1 = 0;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "HFCD irq %x loop %d", val, 15-count);
+		} else
+			val = 0;
+	}
+}
+
+static void
+HFCD_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	u_long flags;
+	
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfc_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else
+					debugl1(cs, "hfc_fill_dfifo blocked");
+
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				spin_unlock_irqrestore(&cs->lock, flags);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+				hfc_fill_dfifo(cs);
+				test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+			} else
+				debugl1(cs, "hfc_fill_dfifo blocked");
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->writeisac(cs, HFCD_STATES, HFCD_LOAD_STATE | 3); /* HFC ST 3 */
+			udelay(6);
+			cs->writeisac(cs, HFCD_STATES, 3); /* HFC ST 2 */
+			cs->hw.hfcD.mst_m |= HFCD_MASTER;
+			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (HW_ENABLE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_DEACTIVATE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcD.mst_m &= ~HFCD_MASTER;
+			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO3 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcD.mst_m |= HFCD_MASTER;
+			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "hfcd_l1hw unknown pr %4x", pr);
+			break;
+	}
+}
+
+void
+setstack_hfcd(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = HFCD_l1hw;
+}
+
+static void
+hfc_dbusy_timer(struct IsdnCardState *cs)
+{
+}
+
+unsigned int __init
+*init_send_hfcd(int cnt)
+{
+	int i, *send;
+
+	if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for hfcd.send\n");
+		return(NULL);
+	}
+	for (i = 0; i < cnt; i++)
+		send[i] = 0x1fff;
+	return(send);
+}
+
+void __init
+init2bds0(struct IsdnCardState *cs)
+{
+	cs->setstack_d = setstack_hfcd;
+	if (!cs->hw.hfcD.send)
+		cs->hw.hfcD.send = init_send_hfcd(16);
+	if (!cs->bcs[0].hw.hfc.send)
+		cs->bcs[0].hw.hfc.send = init_send_hfcd(32);
+	if (!cs->bcs[1].hw.hfc.send)
+		cs->bcs[1].hw.hfc.send = init_send_hfcd(32);
+	cs->BC_Send_Data = &hfc_send_data;
+	cs->bcs[0].BC_SetStack = setstack_2b;
+	cs->bcs[1].BC_SetStack = setstack_2b;
+	cs->bcs[0].BC_Close = close_2bs0;
+	cs->bcs[1].BC_Close = close_2bs0;
+	mode_2bs0(cs->bcs, 0, 0);
+	mode_2bs0(cs->bcs + 1, 0, 1);
+}
+
+void
+release2bds0(struct IsdnCardState *cs)
+{
+	if (cs->bcs[0].hw.hfc.send) {
+		kfree(cs->bcs[0].hw.hfc.send);
+		cs->bcs[0].hw.hfc.send = NULL;
+	}
+	if (cs->bcs[1].hw.hfc.send) {
+		kfree(cs->bcs[1].hw.hfc.send);
+		cs->bcs[1].hw.hfc.send = NULL;
+	}
+	if (cs->hw.hfcD.send) {
+		kfree(cs->hw.hfcD.send);
+		cs->hw.hfcD.send = NULL;
+	}
+}
+
+void
+set_cs_func(struct IsdnCardState *cs)
+{
+	cs->readisac = &readreghfcd;
+	cs->writeisac = &writereghfcd;
+	cs->readisacfifo = &dummyf;
+	cs->writeisacfifo = &dummyf;
+	cs->BC_Read_Reg = &ReadReg;
+	cs->BC_Write_Reg = &WriteReg;
+	cs->dbusytimer.function = (void *) hfc_dbusy_timer;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+	INIT_WORK(&cs->tqueue, (void *)(void *) hfcd_bh, cs);
+}
diff --git a/drivers/isdn/hisax/hfc_2bds0.h b/drivers/isdn/hisax/hfc_2bds0.h
new file mode 100644
index 000000000000..30f1924db91c
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_2bds0.h
@@ -0,0 +1,128 @@
+/* $Id: hfc_2bds0.h,v 1.6.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * specific defines for CCD's HFC 2BDS0
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define HFCD_CIRM  	0x18
+#define HFCD_CTMT	0x19
+#define HFCD_INT_M1  	0x1A
+#define HFCD_INT_M2  	0x1B
+#define HFCD_INT_S1  	0x1E
+#define HFCD_STAT  	0x1C
+#define HFCD_STAT_DISB  0x1D
+#define HFCD_STATES  	0x30
+#define HFCD_SCTRL  	0x31
+#define HFCD_TEST  	0x32
+#define HFCD_SQ  	0x34
+#define HFCD_CLKDEL  	0x37
+#define HFCD_MST_MODE	0x2E
+#define HFCD_CONN  	0x2F
+
+#define HFCD_FIFO	0x80
+#define HFCD_Z1		0x10
+#define HFCD_Z2		0x18
+#define HFCD_Z_LOW	0x00
+#define HFCD_Z_HIGH	0x04
+#define HFCD_F1_INC	0x12
+#define HFCD_FIFO_IN	0x16
+#define HFCD_F1		0x1a
+#define HFCD_F2		0x1e
+#define HFCD_F2_INC	0x22
+#define HFCD_FIFO_OUT	0x26
+#define HFCD_REC	0x01
+#define HFCD_SEND	0x00
+
+#define HFCB_FIFO	0x80
+#define HFCB_Z1		0x00
+#define HFCB_Z2		0x08
+#define HFCB_Z_LOW	0x00
+#define HFCB_Z_HIGH	0x04
+#define HFCB_F1_INC	0x28
+#define HFCB_FIFO_IN	0x2c
+#define HFCB_F1		0x30
+#define HFCB_F2		0x34
+#define HFCB_F2_INC	0x38
+#define HFCB_FIFO_OUT	0x3c
+#define HFCB_REC	0x01
+#define HFCB_SEND	0x00
+#define HFCB_B1		0x00
+#define HFCB_B2		0x02
+#define HFCB_CHANNEL(ch) (ch ? HFCB_B2 : HFCB_B1)
+
+#define HFCD_STATUS	0
+#define HFCD_DATA	1
+#define HFCD_DATA_NODEB	2
+
+/* Status (READ) */
+#define HFCD_BUSY	0x01
+#define HFCD_BUSY_NBUSY	0x04
+#define HFCD_TIMER_ELAP	0x10
+#define HFCD_STATINT	0x20
+#define HFCD_FRAMEINT	0x40
+#define HFCD_ANYINT	0x80
+
+/* CTMT (Write) */
+#define HFCD_CLTIMER 0x80
+#define HFCD_TIM25  0x00
+#define HFCD_TIM50  0x08
+#define HFCD_TIM400 0x10
+#define HFCD_TIM800 0x18
+#define HFCD_AUTO_TIMER 0x20
+#define HFCD_TRANSB2 0x02
+#define HFCD_TRANSB1 0x01
+
+/* CIRM (Write) */
+#define HFCD_RESET  	0x08
+#define HFCD_MEM8K	0x10
+#define HFCD_INTA	0x01
+#define HFCD_INTB	0x02
+#define HFCD_INTC	0x03
+#define HFCD_INTD	0x04
+#define HFCD_INTE	0x05
+#define HFCD_INTF	0x06
+
+/* INT_M1;INT_S1 */
+#define HFCD_INTS_B1TRANS	0x01
+#define HFCD_INTS_B2TRANS	0x02
+#define HFCD_INTS_DTRANS	0x04
+#define HFCD_INTS_B1REC		0x08
+#define HFCD_INTS_B2REC		0x10
+#define HFCD_INTS_DREC		0x20
+#define HFCD_INTS_L1STATE	0x40
+#define HFCD_INTS_TIMER		0x80
+
+/* INT_M2 */
+#define HFCD_IRQ_ENABLE		0x08
+
+/* STATES */
+#define HFCD_LOAD_STATE		0x10
+#define HFCD_ACTIVATE		0x20
+#define HFCD_DO_ACTION		0x40
+
+/* HFCD_MST_MODE */
+#define HFCD_MASTER		0x01
+
+/* HFCD_SCTRL */
+#define SCTRL_B1_ENA		0x01
+#define SCTRL_B2_ENA		0x02
+#define SCTRL_LOW_PRIO		0x08
+#define SCTRL_SQ_ENA		0x10
+#define SCTRL_TEST		0x20
+#define SCTRL_NONE_CAP		0x40
+#define SCTRL_PWR_DOWN		0x80
+
+/* HFCD_TEST */
+#define HFCD_AUTO_AWAKE		0x01
+
+extern void main_irq_2bds0(struct BCState *bcs);
+extern void init2bds0(struct IsdnCardState *cs);
+extern void release2bds0(struct IsdnCardState *cs);
+extern void hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val);
+extern void set_cs_func(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/hfc_2bs0.c b/drivers/isdn/hisax/hfc_2bs0.c
new file mode 100644
index 000000000000..bb376f39ac89
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_2bs0.c
@@ -0,0 +1,593 @@
+/* $Id: hfc_2bs0.c,v 1.20.2.6 2004/02/11 13:21:33 keil Exp $
+ *
+ * specific routines for CCD's HFC 2BS0
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hfc_2bs0.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+static inline int
+WaitForBusy(struct IsdnCardState *cs)
+{
+	int to = 130;
+	u_char val;
+
+	while (!(cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) {
+		val = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2 |
+				      (cs->hw.hfc.cip & 3));
+		udelay(1);
+		to--;
+	}
+	if (!to) {
+		printk(KERN_WARNING "HiSax: waitforBusy timeout\n");
+		return (0);
+	} else
+		return (to);
+}
+
+static inline int
+WaitNoBusy(struct IsdnCardState *cs)
+{
+	int to = 125;
+
+	while ((cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to) {
+		printk(KERN_WARNING "HiSax: waitforBusy timeout\n");
+		return (0);
+	} else
+		return (to);
+}
+
+int
+GetFreeFifoBytes(struct BCState *bcs)
+{
+	int s;
+
+	if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2)
+		return (bcs->cs->hw.hfc.fifosize);
+	s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2];
+	if (s <= 0)
+		s += bcs->cs->hw.hfc.fifosize;
+	s = bcs->cs->hw.hfc.fifosize - s;
+	return (s);
+}
+
+int
+ReadZReg(struct BCState *bcs, u_char reg)
+{
+	int val;
+
+	WaitNoBusy(bcs->cs);
+	val = 256 * bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_HIGH);
+	WaitNoBusy(bcs->cs);
+	val += bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_LOW);
+	return (val);
+}
+
+static void
+hfc_clear_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int idx, cnt;
+	int rcnt, z1, z2;
+	u_char cip, f1, f2;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hfc_clear_fifo");
+	cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+		cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+		WaitForBusy(cs);
+	}
+	WaitNoBusy(cs);
+	f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	cnt = 32;
+	while (((f1 != f2) || (z1 != z2)) && cnt--) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc clear %d f1(%d) f2(%d)",
+				bcs->channel, f1, f2);
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfc.fifosize;
+		if (rcnt)
+			rcnt++;
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc clear %d z1(%x) z2(%x) cnt(%d)",
+				bcs->channel, z1, z2, rcnt);
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		idx = 0;
+		while ((idx < rcnt) && WaitNoBusy(cs)) {
+			cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+			idx++;
+		}
+		if (f1 != f2) {
+			WaitNoBusy(cs);
+			cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+					HFC_CHANNEL(bcs->channel));
+			WaitForBusy(cs);
+		}
+		cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+		WaitNoBusy(cs);
+		f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+		WaitNoBusy(cs);
+		f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+		z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	}
+	return;
+}
+
+
+static struct sk_buff
+*
+hfc_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct sk_buff *skb;
+	struct IsdnCardState *cs = bcs->cs;
+	int idx;
+	int chksum;
+	u_char stat, cip;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hfc_empty_fifo");
+	idx = 0;
+	if (count > HSCX_BUFMAX + 3) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too large");
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		while ((idx++ < count) && WaitNoBusy(cs))
+			cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+		WaitNoBusy(cs);
+		stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+				       HFC_CHANNEL(bcs->channel));
+		WaitForBusy(cs);
+		return (NULL);
+	}
+	if ((count < 4) && (bcs->mode != L1_MODE_TRANS)) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too small");
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		while ((idx++ < count) && WaitNoBusy(cs))
+			cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+		WaitNoBusy(cs);
+		stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+				       HFC_CHANNEL(bcs->channel));
+		WaitForBusy(cs);
+#ifdef ERROR_STATISTIC
+		bcs->err_inv++;
+#endif
+		return (NULL);
+	}
+	if (bcs->mode == L1_MODE_TRANS)
+	  count -= 1;
+	else
+	  count -= 3;
+	if (!(skb = dev_alloc_skb(count)))
+		printk(KERN_WARNING "HFC: receive out of memory\n");
+	else {
+		ptr = skb_put(skb, count);
+		idx = 0;
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		while ((idx < count) && WaitNoBusy(cs)) {
+			*ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+			idx++;
+		}
+		if (idx != count) {
+			debugl1(cs, "RFIFO BUSY error");
+			printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel);
+			dev_kfree_skb_any(skb);
+			if (bcs->mode != L1_MODE_TRANS) {
+			  WaitNoBusy(cs);
+			  stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+						 HFC_CHANNEL(bcs->channel));
+			  WaitForBusy(cs);
+			}
+			return (NULL);
+		}
+		if (bcs->mode != L1_MODE_TRANS) {
+		  WaitNoBusy(cs);
+		  chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8);
+		  WaitNoBusy(cs);
+		  chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		  WaitNoBusy(cs);
+		  stat = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		  if (cs->debug & L1_DEB_HSCX)
+		    debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
+			    bcs->channel, chksum, stat);
+		  if (stat) {
+		    debugl1(cs, "FIFO CRC error");
+		    dev_kfree_skb_any(skb);
+		    skb = NULL;
+#ifdef ERROR_STATISTIC
+		    bcs->err_crc++;
+#endif
+		  }
+		  WaitNoBusy(cs);
+		  stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+					 HFC_CHANNEL(bcs->channel));
+		  WaitForBusy(cs);
+		}
+	}
+	return (skb);
+}
+
+static void
+hfc_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int idx, fcnt;
+	int count;
+	int z1, z2;
+	u_char cip;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	cip = HFC_CIP | HFC_F1 | HFC_SEND | HFC_CHANNEL(bcs->channel);
+	if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+	  cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+	  WaitForBusy(cs);
+	}
+	WaitNoBusy(cs);
+	if (bcs->mode != L1_MODE_TRANS) {
+	  bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	  cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel);
+	  WaitNoBusy(cs);
+	  bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	  bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel));
+	  if (cs->debug & L1_DEB_HSCX)
+	    debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+		    bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
+		    bcs->hw.hfc.send[bcs->hw.hfc.f1]);
+	  fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
+	  if (fcnt < 0)
+	    fcnt += 32;
+	  if (fcnt > 30) {
+	    if (cs->debug & L1_DEB_HSCX)
+	      debugl1(cs, "hfc_fill_fifo more as 30 frames");
+	    return;
+	  }
+	  count = GetFreeFifoBytes(bcs);
+	} 
+	else {
+	  WaitForBusy(cs);
+	  z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	  z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	  count = z1 - z2;
+	  if (count < 0)
+	    count += cs->hw.hfc.fifosize; 
+	} /* L1_MODE_TRANS */
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d count(%ld/%d)",
+			bcs->channel, bcs->tx_skb->len,
+			count);
+	if (count < bcs->tx_skb->len) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo no fifo mem");
+		return;
+	}
+	cip = HFC_CIP | HFC_FIFO_IN | HFC_SEND | HFC_CHANNEL(bcs->channel);
+	idx = 0;
+	while ((idx < bcs->tx_skb->len) && WaitNoBusy(cs))
+		cs->BC_Write_Reg(cs, HFC_DATA_NODEB, cip, bcs->tx_skb->data[idx++]);
+	if (idx != bcs->tx_skb->len) {
+		debugl1(cs, "FIFO Send BUSY error");
+		printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
+	} else {
+		count =  bcs->tx_skb->len;
+		bcs->tx_cnt -= count;
+		if (PACKET_NOACK == bcs->tx_skb->pkt_type)
+			count = -1;
+		dev_kfree_skb_any(bcs->tx_skb);
+		bcs->tx_skb = NULL;
+		if (bcs->mode != L1_MODE_TRANS) {
+		  WaitForBusy(cs);
+		  WaitNoBusy(cs);
+		  cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel));
+		}
+		if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+			(count >= 0)) {
+			u_long	flags;
+			spin_lock_irqsave(&bcs->aclock, flags);
+			bcs->ackcnt += count;
+			spin_unlock_irqrestore(&bcs->aclock, flags);
+			schedule_event(bcs, B_ACKPENDING);
+		}
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	}
+	return;
+}
+
+void
+main_irq_hfc(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int z1, z2, rcnt;
+	u_char f1, f2, cip;
+	int receive, transmit, count = 5;
+	struct sk_buff *skb;
+
+      Begin:
+	count--;
+	cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+		cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+		WaitForBusy(cs);
+	}
+	WaitNoBusy(cs);
+	receive = 0;
+	if (bcs->mode == L1_MODE_HDLC) {
+		f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+		WaitNoBusy(cs);
+		f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		if (f1 != f2) {
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
+					bcs->channel, f1, f2);
+			receive = 1; 
+		}
+	}
+	if (receive || (bcs->mode == L1_MODE_TRANS)) {
+		WaitForBusy(cs);
+		z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+		z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfc.fifosize;
+		if ((bcs->mode == L1_MODE_HDLC) || (rcnt)) {
+			rcnt++;
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+					bcs->channel, z1, z2, rcnt);
+			/*              sti(); */
+			if ((skb = hfc_empty_fifo(bcs, rcnt))) {
+				skb_queue_tail(&bcs->rqueue, skb);
+				schedule_event(bcs, B_RCVBUFREADY);
+			}
+		}
+		receive = 1;
+	}
+	if (bcs->tx_skb) {
+		transmit = 1;
+		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+		hfc_fill_fifo(bcs);
+		if (test_bit(BC_FLG_BUSY, &bcs->Flag))
+			transmit = 0;
+	} else {
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			transmit = 1;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			hfc_fill_fifo(bcs);
+			if (test_bit(BC_FLG_BUSY, &bcs->Flag))
+				transmit = 0;
+		} else {
+			transmit = 0;
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+	if ((receive || transmit) && count)
+		goto Begin;
+	return;
+}
+
+void
+mode_hfc(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "HFC 2BS0 mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+
+	switch (mode) {
+		case (L1_MODE_NULL):
+		        if (bc) {
+				cs->hw.hfc.ctmt &= ~1;
+				cs->hw.hfc.isac_spcr &= ~0x03;
+			}
+			else {
+				cs->hw.hfc.ctmt &= ~2;
+				cs->hw.hfc.isac_spcr &= ~0x0c;
+			}
+			break;
+		case (L1_MODE_TRANS):
+		        cs->hw.hfc.ctmt &= ~(1 << bc); /* set HDLC mode */ 
+			cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt);
+			hfc_clear_fifo(bcs); /* complete fifo clear */ 
+			if (bc) {
+				cs->hw.hfc.ctmt |= 1;
+				cs->hw.hfc.isac_spcr &= ~0x03;
+				cs->hw.hfc.isac_spcr |= 0x02;
+			} else {
+				cs->hw.hfc.ctmt |= 2;
+				cs->hw.hfc.isac_spcr &= ~0x0c;
+				cs->hw.hfc.isac_spcr |= 0x08;
+			}
+			break;
+		case (L1_MODE_HDLC):
+			if (bc) {
+				cs->hw.hfc.ctmt &= ~1;
+				cs->hw.hfc.isac_spcr &= ~0x03;
+				cs->hw.hfc.isac_spcr |= 0x02;
+			} else {
+				cs->hw.hfc.ctmt &= ~2;
+				cs->hw.hfc.isac_spcr &= ~0x0c;
+				cs->hw.hfc.isac_spcr |= 0x08;
+			}
+			break;
+	}
+	cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt);
+	cs->writeisac(cs, ISAC_SPCR, cs->hw.hfc.isac_spcr);
+	if (mode == L1_MODE_HDLC)
+		hfc_clear_fifo(bcs);
+}
+
+static void
+hfc_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState	*bcs = st->l1.bcs;
+	struct sk_buff	*skb = arg;
+	u_long		flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+			} else {
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->tx_skb = skb;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			mode_hfc(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			mode_hfc(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+
+void
+close_hfcstate(struct BCState *bcs)
+{
+	mode_hfc(bcs, 0, bcs->channel);
+	if (test_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+	test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+}
+
+static int
+open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_hfc(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hfcstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hfc_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void __init
+init_send(struct BCState *bcs)
+{
+	int i;
+
+	if (!(bcs->hw.hfc.send = kmalloc(32 * sizeof(unsigned int), GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for hfc.send\n");
+		return;
+	}
+	for (i = 0; i < 32; i++)
+		bcs->hw.hfc.send[i] = 0x1fff;
+}
+
+void __init
+inithfc(struct IsdnCardState *cs)
+{
+	init_send(&cs->bcs[0]);
+	init_send(&cs->bcs[1]);
+	cs->BC_Send_Data = &hfc_fill_fifo;
+	cs->bcs[0].BC_SetStack = setstack_hfc;
+	cs->bcs[1].BC_SetStack = setstack_hfc;
+	cs->bcs[0].BC_Close = close_hfcstate;
+	cs->bcs[1].BC_Close = close_hfcstate;
+	mode_hfc(cs->bcs, 0, 0);
+	mode_hfc(cs->bcs + 1, 0, 0);
+}
+
+void
+releasehfc(struct IsdnCardState *cs)
+{
+	if (cs->bcs[0].hw.hfc.send) {
+		kfree(cs->bcs[0].hw.hfc.send);
+		cs->bcs[0].hw.hfc.send = NULL;
+	}
+	if (cs->bcs[1].hw.hfc.send) {
+		kfree(cs->bcs[1].hw.hfc.send);
+		cs->bcs[1].hw.hfc.send = NULL;
+	}
+}
diff --git a/drivers/isdn/hisax/hfc_2bs0.h b/drivers/isdn/hisax/hfc_2bs0.h
new file mode 100644
index 000000000000..1a50d4a5c968
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_2bs0.h
@@ -0,0 +1,60 @@
+/* $Id: hfc_2bs0.h,v 1.5.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * specific defines for CCD's HFC 2BS0
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define HFC_CTMT	0xe0
+#define HFC_CIRM  	0xc0
+#define HFC_CIP		0x80
+#define HFC_Z1		0x00
+#define HFC_Z2		0x08
+#define HFC_Z_LOW	0x00
+#define HFC_Z_HIGH	0x04
+#define HFC_F1_INC	0x28
+#define HFC_FIFO_IN	0x2c
+#define HFC_F1		0x30
+#define HFC_F2		0x34
+#define HFC_F2_INC	0x38
+#define HFC_FIFO_OUT	0x3c
+#define HFC_B1          0x00
+#define HFC_B2		0x02
+#define HFC_REC		0x01
+#define HFC_SEND	0x00
+#define HFC_CHANNEL(ch) (ch ? HFC_B2 : HFC_B1)
+
+#define HFC_STATUS	0
+#define HFC_DATA	1
+#define HFC_DATA_NODEB	2
+
+/* Status (READ) */
+#define HFC_BUSY	0x01
+#define HFC_TIMINT	0x02
+#define HFC_EXTINT	0x04
+
+/* CTMT (Write) */
+#define HFC_CLTIMER 0x10
+#define HFC_TIM50MS 0x08
+#define HFC_TIMIRQE 0x04
+#define HFC_TRANSB2 0x02
+#define HFC_TRANSB1 0x01
+
+/* CIRM (Write) */
+#define HFC_RESET  	0x08
+#define HFC_MEM8K	0x10
+#define HFC_INTA	0x01
+#define HFC_INTB	0x02
+#define HFC_INTC	0x03
+#define HFC_INTD	0x04
+#define HFC_INTE	0x05
+#define HFC_INTF	0x06
+
+extern void main_irq_hfc(struct BCState *bcs);
+extern void inithfc(struct IsdnCardState *cs);
+extern void releasehfc(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c
new file mode 100644
index 000000000000..c2db52696a86
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_pci.c
@@ -0,0 +1,1747 @@
+/* $Id: hfc_pci.c,v 1.48.2.4 2004/02/11 13:21:33 keil Exp $
+ *
+ * low level driver for CCD´s hfc-pci based cards
+ *
+ * Author       Werner Cornelius
+ *              based on existing driver for CCD hfc ISA cards
+ * Copyright    by Werner Cornelius  <werner@isdn4linux.de>
+ *              by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include "hisax.h"
+#include "hfc_pci.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+extern const char *CardType[];
+
+static const char *hfcpci_revision = "$Revision: 1.48.2.4 $";
+
+/* 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 */
+
+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},
+};
+
+
+#ifdef CONFIG_PCI
+
+/******************************************/
+/* free hardware resources used by driver */
+/******************************************/
+void
+release_io_hfcpci(struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "HiSax: release hfcpci at %p\n",
+		cs->hw.hfcpci.pci_io);
+	cs->hw.hfcpci.int_m2 = 0;					/* interrupt output off ! */
+	Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+	Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET);			/* Reset On */
+	mdelay(10);
+	Write_hfc(cs, HFCPCI_CIRM, 0);					/* Reset Off */
+	mdelay(10);
+	Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+	pci_write_config_word(cs->hw.hfcpci.dev, PCI_COMMAND, 0);	/* disable memory mapped ports + busmaster */
+	del_timer(&cs->hw.hfcpci.timer);
+	kfree(cs->hw.hfcpci.share_start);
+	cs->hw.hfcpci.share_start = NULL;
+	iounmap((void *)cs->hw.hfcpci.pci_io);
+}
+
+/********************************************************************************/
+/* function called to reset the HFC PCI chip. A complete software reset of chip */
+/* and fifos is done.                                                           */
+/********************************************************************************/
+static void
+reset_hfcpci(struct IsdnCardState *cs)
+{
+	pci_write_config_word(cs->hw.hfcpci.dev, PCI_COMMAND, PCI_ENA_MEMIO);	/* enable memory mapped ports, disable busmaster */
+	cs->hw.hfcpci.int_m2 = 0;	/* interrupt output off ! */
+	Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+
+	printk(KERN_INFO "HFC_PCI: resetting card\n");
+	pci_write_config_word(cs->hw.hfcpci.dev, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER);	/* enable memory ports + busmaster */
+	Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET);	/* Reset On */
+	mdelay(10);
+	Write_hfc(cs, HFCPCI_CIRM, 0);	/* Reset Off */
+	mdelay(10);
+	if (Read_hfc(cs, HFCPCI_STATUS) & 2)
+		printk(KERN_WARNING "HFC-PCI init bit busy\n");
+
+	cs->hw.hfcpci.fifo_en = 0x30;	/* only D fifos enabled */
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+
+	cs->hw.hfcpci.trm = 0 + HFCPCI_BTRANS_THRESMASK;	/* no echo connect , threshold */
+	Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
+
+	Write_hfc(cs, HFCPCI_CLKDEL, CLKDEL_TE); /* ST-Bit delay for TE-Mode */
+	cs->hw.hfcpci.sctrl_e = HFCPCI_AUTO_AWAKE;
+	Write_hfc(cs, HFCPCI_SCTRL_E, cs->hw.hfcpci.sctrl_e);	/* S/T Auto awake */
+	cs->hw.hfcpci.bswapped = 0;	/* no exchange */
+	cs->hw.hfcpci.nt_mode = 0;	/* we are in TE mode */
+	cs->hw.hfcpci.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER;
+	Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt);
+
+	cs->hw.hfcpci.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC |
+	    HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER;
+	Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+
+	/* Clear already pending ints */
+	if (Read_hfc(cs, HFCPCI_INT_S1));
+
+	Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 2);	/* HFC ST 2 */
+	udelay(10);
+	Write_hfc(cs, HFCPCI_STATES, 2);	/* HFC ST 2 */
+	cs->hw.hfcpci.mst_m = HFCPCI_MASTER;	/* HFC Master Mode */
+
+	Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+	cs->hw.hfcpci.sctrl = 0x40;	/* set tx_lo mode, error in datasheet ! */
+	Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);
+	cs->hw.hfcpci.sctrl_r = 0;
+	Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.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 */
+	cs->hw.hfcpci.conn = 0x36;	/* set data flow directions */
+	Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+	Write_hfc(cs, HFCPCI_B1_SSL, 0x80);	/* B1-Slot 0 STIO1 out enabled */
+	Write_hfc(cs, HFCPCI_B2_SSL, 0x81);	/* B2-Slot 1 STIO1 out enabled */
+	Write_hfc(cs, HFCPCI_B1_RSL, 0x80);	/* B1-Slot 0 STIO2 in enabled */
+	Write_hfc(cs, HFCPCI_B2_RSL, 0x81);	/* B2-Slot 1 STIO2 in enabled */
+
+	/* Finally enable IRQ output */
+	cs->hw.hfcpci.int_m2 = HFCPCI_IRQ_ENABLE;
+	Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+	if (Read_hfc(cs, HFCPCI_INT_S1));
+}
+
+/***************************************************/
+/* Timer function called when kernel timer expires */
+/***************************************************/
+static void
+hfcpci_Timer(struct IsdnCardState *cs)
+{
+	cs->hw.hfcpci.timer.expires = jiffies + 75;
+	/* WD RESET */
+/*      WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcpci.ctmt | 0x80);
+   add_timer(&cs->hw.hfcpci.timer);
+ */
+}
+
+
+/*********************************/
+/* schedule a new D-channel task */
+/*********************************/
+static void
+sched_event_D_pci(struct IsdnCardState *cs, int event)
+{
+	test_and_set_bit(event, &cs->event);
+	schedule_work(&cs->tqueue);
+}
+
+/*********************************/
+/* schedule a new b_channel task */
+/*********************************/
+static void
+hfcpci_sched_event(struct BCState *bcs, int event)
+{
+	test_and_set_bit(event, &bcs->event);
+	schedule_work(&bcs->tqueue);
+}
+
+/************************************************/
+/* select a b-channel entry matching and active */
+/************************************************/
+static
+struct BCState *
+Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+	if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+		return (&cs->bcs[0]);
+	else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+		return (&cs->bcs[1]);
+	else
+		return (NULL);
+}
+
+/***************************************/
+/* clear the desired B-channel rx fifo */
+/***************************************/
+static void hfcpci_clear_fifo_rx(struct IsdnCardState *cs, int fifo)
+{       u_char fifo_state;
+        bzfifo_type *bzr;
+
+	if (fifo) {
+	        bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2;
+		fifo_state = cs->hw.hfcpci.fifo_en & HFCPCI_FIFOEN_B2RX;
+	} else {
+	        bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1;
+		fifo_state = cs->hw.hfcpci.fifo_en & HFCPCI_FIFOEN_B1RX;
+	}
+	if (fifo_state)
+	        cs->hw.hfcpci.fifo_en ^= fifo_state;
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+	cs->hw.hfcpci.last_bfifo_cnt[fifo] = 0;
+	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;
+	bzr->f1 = MAX_B_FRAMES;
+	bzr->f2 = bzr->f1;	/* init F pointers to remain constant */
+	if (fifo_state)
+	        cs->hw.hfcpci.fifo_en |= fifo_state;
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+}   
+
+/***************************************/
+/* clear the desired B-channel tx fifo */
+/***************************************/
+static void hfcpci_clear_fifo_tx(struct IsdnCardState *cs, int fifo)
+{       u_char fifo_state;
+        bzfifo_type *bzt;
+
+	if (fifo) {
+	        bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2;
+		fifo_state = cs->hw.hfcpci.fifo_en & HFCPCI_FIFOEN_B2TX;
+	} else {
+	        bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1;
+		fifo_state = cs->hw.hfcpci.fifo_en & HFCPCI_FIFOEN_B1TX;
+	}
+	if (fifo_state)
+	        cs->hw.hfcpci.fifo_en ^= fifo_state;
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+	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;
+	bzt->f1 = MAX_B_FRAMES;
+	bzt->f2 = bzt->f1;	/* init F pointers to remain constant */
+	if (fifo_state)
+	        cs->hw.hfcpci.fifo_en |= fifo_state;
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+}   
+
+/*********************************************/
+/* read a complete B-frame out of the buffer */
+/*********************************************/
+static struct sk_buff
+*
+hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type * bz, u_char * bdata, int count)
+{
+	u_char *ptr, *ptr1, new_f2;
+	struct sk_buff *skb;
+	struct IsdnCardState *cs = bcs->cs;
+	int total, maxlen, new_z2;
+	z_type *zp;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "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 > HSCX_BUFMAX + 3) || (count < 4) ||
+	    (*(bdata + (zp->z1 - B_SUB_VAL)))) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfcpci_empty_fifo: incoming packet invalid length %d or crc", count);
+#ifdef ERROR_STATISTIC
+		bcs->err_inv++;
+#endif
+		bz->za[new_f2].z2 = new_z2;
+		bz->f2 = new_f2;	/* next buffer */
+		skb = NULL;
+	} else if (!(skb = dev_alloc_skb(count - 3)))
+		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(struct IsdnCardState *cs)
+{
+	struct sk_buff *skb;
+	int maxlen;
+	int rcnt, total;
+	int count = 5;
+	u_char *ptr, *ptr1;
+	dfifo_type *df;
+	z_type *zp;
+
+	df = &((fifo_area *) (cs->hw.hfcpci.fifos))->d_chan.d_rx;
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_dmsg blocked");
+		return (1);
+	}
+	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 (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "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 (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "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 = dev_alloc_skb(rcnt - 3))) {
+			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);
+
+			skb_queue_tail(&cs->rq, skb);
+			sched_event_D_pci(cs, D_RCVBUFREADY);
+		} else
+			printk(KERN_WARNING "HFC-PCI: D receive out of memory\n");
+	}
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	return (1);
+}
+
+/*******************************************************************************/
+/* check for transparent receive data and read max one threshold size if avail */
+/*******************************************************************************/
+int
+hfcpci_empty_fifo_trans(struct BCState *bcs, 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 = dev_alloc_skb(fcnt)))
+		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(&bcs->rqueue, skb);
+		hfcpci_sched_event(bcs, B_RCVBUFREADY);
+	}
+
+	*z2r = new_z2;		/* new position */
+	return (1);
+}				/* hfcpci_empty_fifo_trans */
+
+/**********************************/
+/* B-channel main receive routine */
+/**********************************/
+void
+main_rec_hfcpci(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int rcnt, real_fifo;
+	int receive, count = 5;
+	struct sk_buff *skb;
+	bzfifo_type *bz;
+	u_char *bdata;
+	z_type *zp;
+
+
+	if ((bcs->channel) && (!cs->hw.hfcpci.bswapped)) {
+		bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2;
+		bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b2;
+		real_fifo = 1;
+	} else {
+		bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1;
+		bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b1;
+		real_fifo = 0;
+	}
+      Begin:
+	count--;
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_data %d blocked", bcs->channel);
+		return;
+	}
+	if (bz->f1 != bz->f2) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfcpci rec %d f1(%d) f2(%d)",
+				bcs->channel, bz->f1, bz->f2);
+		zp = &bz->za[bz->f2];
+
+		rcnt = zp->z1 - zp->z2;
+		if (rcnt < 0)
+			rcnt += B_FIFO_SIZE;
+		rcnt++;
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfcpci rec %d z1(%x) z2(%x) cnt(%d)",
+				bcs->channel, zp->z1, zp->z2, rcnt);
+		if ((skb = hfcpci_empty_fifo(bcs, bz, bdata, rcnt))) {
+			skb_queue_tail(&bcs->rqueue, skb);
+			hfcpci_sched_event(bcs, B_RCVBUFREADY);
+		}
+		rcnt = bz->f1 - bz->f2;
+		if (rcnt < 0)
+			rcnt += MAX_B_FRAMES + 1;
+		if (cs->hw.hfcpci.last_bfifo_cnt[real_fifo] > rcnt + 1) {
+		        rcnt = 0;
+			hfcpci_clear_fifo_rx(cs, real_fifo);
+		}
+		cs->hw.hfcpci.last_bfifo_cnt[real_fifo] = rcnt;
+		if (rcnt > 1)
+			receive = 1;
+		else
+			receive = 0;
+	} else if (bcs->mode == L1_MODE_TRANS)
+		receive = hfcpci_empty_fifo_trans(bcs, bz, bdata);
+	else
+		receive = 0;
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	if (count && receive)
+		goto Begin;
+	return;
+}
+
+/**************************/
+/* D-channel send routine */
+/**************************/
+static void
+hfcpci_fill_dfifo(struct IsdnCardState *cs)
+{
+	int fcnt;
+	int count, new_z1, maxlen;
+	dfifo_type *df;
+	u_char *src, *dst, new_f1;
+
+	if (!cs->tx_skb)
+		return;
+	if (cs->tx_skb->len <= 0)
+		return;
+
+	df = &((fifo_area *) (cs->hw.hfcpci.fifos))->d_chan.d_tx;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "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 (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfcpci_fill_Dfifo more as 14 frames");
+#ifdef ERROR_STATISTIC
+		cs->err_tx++;
+#endif
+		return;
+	}
+	/* now determine free bytes in FIFO buffer */
+	count = df->za[df->f2 & D_FREG_MASK].z2 - df->za[df->f1 & D_FREG_MASK].z1 - 1;
+	if (count <= 0)
+		count += D_FIFO_SIZE;	/* count now contains available bytes */
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "hfcpci_fill_Dfifo count(%ld/%d)",
+			cs->tx_skb->len, count);
+	if (count < cs->tx_skb->len) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfcpci_fill_Dfifo no fifo mem");
+		return;
+	}
+	count = cs->tx_skb->len;	/* get frame len */
+	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 = cs->tx_skb->data;	/* 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 */
+
+	dev_kfree_skb_any(cs->tx_skb);
+	cs->tx_skb = NULL;
+	return;
+}
+
+/**************************/
+/* B-channel send routine */
+/**************************/
+static void
+hfcpci_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int maxlen, fcnt;
+	int count, new_z1;
+	bzfifo_type *bz;
+	u_char *bdata;
+	u_char new_f1, *src, *dst;
+	unsigned short *z1t, *z2t;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	if ((bcs->channel) && (!cs->hw.hfcpci.bswapped)) {
+		bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2;
+		bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txdat_b2;
+	} else {
+		bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1;
+		bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txdat_b1;
+	}
+
+	if (bcs->mode == L1_MODE_TRANS) {
+		z1t = &bz->za[MAX_B_FRAMES].z1;
+		z2t = z1t + 1;
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfcpci_fill_fifo_trans %d z1(%x) z2(%x)",
+				bcs->channel, *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 */
+
+		while ((fcnt < 2 * HFCPCI_BTRANS_THRESHOLD) && (bcs->tx_skb)) {
+			if (bcs->tx_skb->len < B_FIFO_SIZE - fcnt) {
+				/* data is suitable for fifo */
+				count = bcs->tx_skb->len;
+
+				new_z1 = *z1t + count;	/* new buffer Position */
+				if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
+					new_z1 -= B_FIFO_SIZE;	/* buffer wrap */
+				src = bcs->tx_skb->data;	/* source pointer */
+				dst = bdata + (*z1t - B_SUB_VAL);
+				maxlen = (B_FIFO_SIZE + B_SUB_VAL) - *z1t;	/* end of 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);
+				}
+				bcs->tx_cnt -= bcs->tx_skb->len;
+				fcnt += bcs->tx_skb->len;
+				*z1t = new_z1;	/* now send data */
+			} else if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "hfcpci_fill_fifo_trans %d frame length %d discarded",
+					bcs->channel, bcs->tx_skb->len);
+
+			if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+				(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+				u_long	flags;
+				spin_lock_irqsave(&bcs->aclock, flags);
+				bcs->ackcnt += bcs->tx_skb->len;
+				spin_unlock_irqrestore(&bcs->aclock, flags);
+				schedule_event(bcs, B_ACKPENDING);
+			}
+
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = skb_dequeue(&bcs->squeue);	/* fetch next data */
+		}
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		return;
+	}
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfcpci_fill_fifo_hdlc %d f1(%d) f2(%d) z1(f1)(%x)",
+			bcs->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 (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfcpci_fill_Bfifo more as 14 frames");
+		return;
+	}
+	/* now determine free bytes in FIFO buffer */
+	count = bz->za[bz->f2].z2 - bz->za[bz->f1].z1 - 1;
+	if (count <= 0)
+		count += B_FIFO_SIZE;	/* count now contains available bytes */
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfcpci_fill_fifo %d count(%ld/%d),%lx",
+			bcs->channel, bcs->tx_skb->len,
+			count, current->state);
+
+	if (count < bcs->tx_skb->len) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfcpci_fill_fifo no fifo mem");
+		return;
+	}
+	count = bcs->tx_skb->len;	/* get frame len */
+	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 = bcs->tx_skb->data;	/* 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);
+	}
+	bcs->tx_cnt -= bcs->tx_skb->len;
+	if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+		(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+		u_long	flags;
+		spin_lock_irqsave(&bcs->aclock, flags);
+		bcs->ackcnt += bcs->tx_skb->len;
+		spin_unlock_irqrestore(&bcs->aclock, flags);
+		schedule_event(bcs, B_ACKPENDING);
+	}
+
+	bz->za[new_f1].z1 = new_z1;	/* for next buffer */
+	bz->f1 = new_f1;	/* next frame */
+
+	dev_kfree_skb_any(bcs->tx_skb);
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	return;
+}
+
+/**********************************************/
+/* D-channel l1 state call for leased NT-mode */
+/**********************************************/
+static void
+dch_nt_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (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(cs, "PH_TEST_LOOP B1");
+			if (2 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B2");
+			if (!(3 & (long) arg))
+				debugl1(cs, "PH_TEST_LOOP DISABLED");
+			st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
+			break;
+		default:
+			if (cs->debug)
+				debugl1(cs, "dch_nt_l2l1 msg %04X unhandled", pr);
+			break;
+	}
+}
+
+
+
+/***********************/
+/* set/reset echo mode */
+/***********************/
+static int
+hfcpci_auxcmd(struct IsdnCardState *cs, isdn_ctrl * ic)
+{
+	u_long	flags;
+	int	i = *(unsigned int *) ic->parm.num;
+
+	if ((ic->arg == 98) &&
+	    (!(cs->hw.hfcpci.int_m1 & (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC + HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC)))) {
+	    	spin_lock_irqsave(&cs->lock, flags);
+		Write_hfc(cs, HFCPCI_CLKDEL, CLKDEL_NT); /* ST-Bit delay for NT-Mode */
+		Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 0);	/* HFC ST G0 */
+		udelay(10);
+		cs->hw.hfcpci.sctrl |= SCTRL_MODE_NT;
+		Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);	/* set NT-mode */
+		udelay(10);
+		Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 1);	/* HFC ST G1 */
+		udelay(10);
+		Write_hfc(cs, HFCPCI_STATES, 1 | HFCPCI_ACTIVATE | HFCPCI_DO_ACTION);
+		cs->dc.hfcpci.ph_state = 1;
+		cs->hw.hfcpci.nt_mode = 1;
+		cs->hw.hfcpci.nt_timer = 0;
+		cs->stlist->l2.l2l1 = dch_nt_l2l1;
+		spin_unlock_irqrestore(&cs->lock, flags);
+		debugl1(cs, "NT mode activated");
+		return (0);
+	}
+	if ((cs->chanlimit > 1) || (cs->hw.hfcpci.bswapped) ||
+	    (cs->hw.hfcpci.nt_mode) || (ic->arg != 12))
+		return (-EINVAL);
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if (i) {
+		cs->logecho = 1;
+		cs->hw.hfcpci.trm |= 0x20;	/* enable echo chan */
+		cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_B2REC;
+		cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2RX;
+	} else {
+		cs->logecho = 0;
+		cs->hw.hfcpci.trm &= ~0x20;	/* disable echo chan */
+		cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_B2REC;
+		cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2RX;
+	}
+	cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA;
+	cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA;
+	cs->hw.hfcpci.conn |= 0x10;	/* B2-IOM -> B2-ST */
+	cs->hw.hfcpci.ctmt &= ~2;
+	Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt);
+	Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r);
+	Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);
+	Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+	Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+	Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return (0);
+}				/* hfcpci_auxcmd */
+
+/*****************************/
+/* E-channel receive routine */
+/*****************************/
+static void
+receive_emsg(struct IsdnCardState *cs)
+{
+	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 *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2;
+	bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b2;
+      Begin:
+	count--;
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "echo_rec_data blocked");
+		return;
+	}
+	if (bz->f1 != bz->f2) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "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 (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "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 (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "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 (cs->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 += QuickHex(ptr, e_buffer, total - 3);
+					ptr--;
+					*ptr++ = '\n';
+					*ptr = 0;
+					HiSax_putstatus(cs, NULL, cs->dlog);
+				} else
+					HiSax_putstatus(cs, "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;
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	if (count && receive)
+		goto Begin;
+	return;
+}				/* receive_emsg */
+
+/*********************/
+/* Interrupt handler */
+/*********************/
+static irqreturn_t
+hfcpci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	u_long flags;
+	struct IsdnCardState *cs = dev_id;
+	u_char exval;
+	struct BCState *bcs;
+	int count = 15;
+	u_char val, stat;
+
+	if (!(cs->hw.hfcpci.int_m2 & 0x08)) {
+		debugl1(cs, "HFC-PCI: int_m2 %x not initialised", cs->hw.hfcpci.int_m2);
+		return IRQ_NONE;	/* not initialised */
+	}
+	spin_lock_irqsave(&cs->lock, flags);
+	if (HFCPCI_ANYINT & (stat = Read_hfc(cs, HFCPCI_STATUS))) {
+		val = Read_hfc(cs, HFCPCI_INT_S1);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "HFC-PCI: stat(%02x) s1(%02x)", stat, val);
+	} else {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFC-PCI irq %x %s", val,
+			test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
+			"locked" : "unlocked");
+	val &= cs->hw.hfcpci.int_m1;
+	if (val & 0x40) {	/* state machine irq */
+		exval = Read_hfc(cs, HFCPCI_STATES) & 0xf;
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcpci.ph_state,
+				exval);
+		cs->dc.hfcpci.ph_state = exval;
+		sched_event_D_pci(cs, D_L1STATECHANGE);
+		val &= ~0x40;
+	}
+	if (val & 0x80) {	/* timer irq */
+		if (cs->hw.hfcpci.nt_mode) {
+			if ((--cs->hw.hfcpci.nt_timer) < 0)
+				sched_event_D_pci(cs, D_L1STATECHANGE);
+		}
+		val &= ~0x80;
+		Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER);
+	}
+	while (val) {
+		if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			cs->hw.hfcpci.int_s1 |= val;
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_HANDLED;
+		}
+		if (cs->hw.hfcpci.int_s1 & 0x18) {
+			exval = val;
+			val = cs->hw.hfcpci.int_s1;
+			cs->hw.hfcpci.int_s1 = exval;
+		}
+		if (val & 0x08) {
+			if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1 : 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcpci spurious 0x08 IRQ");
+			} else
+				main_rec_hfcpci(bcs);
+		}
+		if (val & 0x10) {
+			if (cs->logecho)
+				receive_emsg(cs);
+			else if (!(bcs = Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcpci spurious 0x10 IRQ");
+			} else
+				main_rec_hfcpci(bcs);
+		}
+		if (val & 0x01) {
+			if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1 : 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcpci spurious 0x01 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcpci_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs, "fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfcpci_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs, "fill_data %d blocked", bcs->channel);
+					} else {
+						hfcpci_sched_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x02) {
+			if (!(bcs = Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcpci spurious 0x02 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcpci_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs, "fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfcpci_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs, "fill_data %d blocked", bcs->channel);
+					} else {
+						hfcpci_sched_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x20) {	/* receive dframe */
+			receive_dmsg(cs);
+		}
+		if (val & 0x04) {	/* dframe transmitted */
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				sched_event_D_pci(cs, D_CLEARBUSY);
+			if (cs->tx_skb) {
+				if (cs->tx_skb->len) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcpci_fill_dfifo(cs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else {
+						debugl1(cs, "hfcpci_fill_dfifo irq blocked");
+					}
+					goto afterXPR;
+				} else {
+					dev_kfree_skb_irq(cs->tx_skb);
+					cs->tx_cnt = 0;
+					cs->tx_skb = NULL;
+				}
+			}
+			if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+				cs->tx_cnt = 0;
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfcpci_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else {
+					debugl1(cs, "hfcpci_fill_dfifo irq blocked");
+				}
+			} else
+				sched_event_D_pci(cs, D_XMTBUFREADY);
+		}
+	      afterXPR:
+		if (cs->hw.hfcpci.int_s1 && count--) {
+			val = cs->hw.hfcpci.int_s1;
+			cs->hw.hfcpci.int_s1 = 0;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "HFC-PCI irq %x loop %d", val, 15 - count);
+		} else
+			val = 0;
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+/********************************************************************/
+/* timer callback for D-chan busy resolution. Currently no function */
+/********************************************************************/
+static void
+hfcpci_dbusy_timer(struct IsdnCardState *cs)
+{
+}
+
+/*************************************/
+/* Layer 1 D-channel hardware access */
+/*************************************/
+static void
+HFCPCI_l1hw(struct PStack *st, int pr, void *arg)
+{
+	u_long flags;
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfcpci_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else
+					debugl1(cs, "hfcpci_fill_dfifo blocked");
+
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				spin_unlock_irqrestore(&cs->lock, flags);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+				hfcpci_fill_dfifo(cs);
+				test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+			} else
+				debugl1(cs, "hfcpci_fill_dfifo blocked");
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3);	/* HFC ST 3 */
+			udelay(6);
+			Write_hfc(cs, HFCPCI_STATES, 3);	/* HFC ST 2 */
+			cs->hw.hfcpci.mst_m |= HFCPCI_MASTER;
+			Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+			Write_hfc(cs, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (HW_ENABLE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			Write_hfc(cs, HFCPCI_STATES, HFCPCI_DO_ACTION);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_DEACTIVATE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcpci.mst_m &= ~HFCPCI_MASTER;
+			Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO3 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcpci.mst_m |= HFCPCI_MASTER;
+			Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			switch ((int) arg) {
+				case (1):
+					Write_hfc(cs, HFCPCI_B1_SSL, 0x80);	/* tx slot */
+					Write_hfc(cs, HFCPCI_B1_RSL, 0x80);	/* rx slot */
+					cs->hw.hfcpci.conn = (cs->hw.hfcpci.conn & ~7) | 1;
+					Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+					break;
+
+				case (2):
+					Write_hfc(cs, HFCPCI_B2_SSL, 0x81);	/* tx slot */
+					Write_hfc(cs, HFCPCI_B2_RSL, 0x81);	/* rx slot */
+					cs->hw.hfcpci.conn = (cs->hw.hfcpci.conn & ~0x38) | 0x08;
+					Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+					break;
+
+				default:
+					spin_unlock_irqrestore(&cs->lock, flags);
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "hfcpci_l1hw loop invalid %4x", (int) arg);
+					return;
+			}
+			cs->hw.hfcpci.trm |= 0x80;	/* enable IOM-loop */
+			Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "hfcpci_l1hw unknown pr %4x", pr);
+			break;
+	}
+}
+
+/***********************************************/
+/* called during init setting l1 stack pointer */
+/***********************************************/
+void
+setstack_hfcpci(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = HFCPCI_l1hw;
+}
+
+/**************************************/
+/* send B-channel data if not blocked */
+/**************************************/
+static void
+hfcpci_send_data(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		hfcpci_fill_fifo(bcs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	} else
+		debugl1(cs, "send_data %d blocked", bcs->channel);
+}
+
+/***************************************************************/
+/* activate/deactivate hardware for selected channels and mode */
+/***************************************************************/
+void
+mode_hfcpci(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int fifo2;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "HFCPCI bchannel mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	fifo2 = bc;
+	if (cs->chanlimit > 1) {
+		cs->hw.hfcpci.bswapped = 0;	/* B1 and B2 normal mode */
+		cs->hw.hfcpci.sctrl_e &= ~0x80;
+	} else {
+		if (bc) {
+			if (mode != L1_MODE_NULL) {
+				cs->hw.hfcpci.bswapped = 1;	/* B1 and B2 exchanged */
+				cs->hw.hfcpci.sctrl_e |= 0x80;
+			} else {
+				cs->hw.hfcpci.bswapped = 0;	/* B1 and B2 normal mode */
+				cs->hw.hfcpci.sctrl_e &= ~0x80;
+			}
+			fifo2 = 0;
+		} else {
+			cs->hw.hfcpci.bswapped = 0;	/* B1 and B2 normal mode */
+			cs->hw.hfcpci.sctrl_e &= ~0x80;
+		}
+	}
+	switch (mode) {
+		case (L1_MODE_NULL):
+			if (bc) {
+				cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA;
+				cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcpci.sctrl &= ~SCTRL_B1_ENA;
+				cs->hw.hfcpci.sctrl_r &= ~SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2;
+				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+			} else {
+				cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1;
+				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+			}
+			break;
+		case (L1_MODE_TRANS):
+		        hfcpci_clear_fifo_rx(cs, fifo2);
+		        hfcpci_clear_fifo_tx(cs, fifo2);
+			if (bc) {
+				cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2;
+				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+				cs->hw.hfcpci.ctmt |= 2;
+				cs->hw.hfcpci.conn &= ~0x18;
+			} else {
+				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1;
+				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+				cs->hw.hfcpci.ctmt |= 1;
+				cs->hw.hfcpci.conn &= ~0x03;
+			}
+			break;
+		case (L1_MODE_HDLC):
+		        hfcpci_clear_fifo_rx(cs, fifo2);
+		        hfcpci_clear_fifo_tx(cs, fifo2);
+			if (bc) {
+				cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+			        cs->hw.hfcpci.last_bfifo_cnt[1] = 0;  
+				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2;
+				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+				cs->hw.hfcpci.ctmt &= ~2;
+				cs->hw.hfcpci.conn &= ~0x18;
+			} else {
+			        cs->hw.hfcpci.last_bfifo_cnt[0] = 0;  
+				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1;
+				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+				cs->hw.hfcpci.ctmt &= ~1;
+				cs->hw.hfcpci.conn &= ~0x03;
+			}
+			break;
+		case (L1_MODE_EXTRN):
+			if (bc) {
+				cs->hw.hfcpci.conn |= 0x10;
+				cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA;
+				cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2;
+				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+			} else {
+				cs->hw.hfcpci.conn |= 0x02;
+				cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA;
+				cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1;
+				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+			}
+			break;
+	}
+	Write_hfc(cs, HFCPCI_SCTRL_E, cs->hw.hfcpci.sctrl_e);
+	Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+	Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);
+	Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r);
+	Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt);
+	Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+}
+
+/******************************/
+/* Layer2 -> Layer 1 Transfer */
+/******************************/
+static void
+hfcpci_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState	*bcs = st->l1.bcs;
+	u_long		flags;
+	struct sk_buff	*skb = arg;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+//				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ 				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				spin_unlock_irqrestore(&bcs->cs->lock, flags);
+				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+				break;
+			}
+//			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->tx_skb = skb;
+			bcs->cs->BC_Send_Data(bcs);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			mode_hfcpci(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			mode_hfcpci(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+/******************************************/
+/* deactivate B-channel access and queues */
+/******************************************/
+static void
+close_hfcpci(struct BCState *bcs)
+{
+	mode_hfcpci(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+/*************************************/
+/* init B-channel queues and control */
+/*************************************/
+static int
+open_hfcpcistate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+/*********************************/
+/* inits the stack for B-channel */
+/*********************************/
+static int
+setstack_2b(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hfcpcistate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hfcpci_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+/***************************/
+/* handle L1 state changes */
+/***************************/
+static void
+hfcpci_bh(struct IsdnCardState *cs)
+{
+	u_long	flags;
+//      struct PStack *stptr;
+
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+		if (!cs->hw.hfcpci.nt_mode)
+			switch (cs->dc.hfcpci.ph_state) {
+				case (0):
+					l1_msg(cs, HW_RESET | INDICATION, NULL);
+					break;
+				case (3):
+					l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+					break;
+				case (8):
+					l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+					break;
+				case (6):
+					l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+					break;
+				case (7):
+					l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+					break;
+				default:
+					break;
+		} else {
+			spin_lock_irqsave(&cs->lock, flags);
+			switch (cs->dc.hfcpci.ph_state) {
+				case (2):
+					if (cs->hw.hfcpci.nt_timer < 0) {
+						cs->hw.hfcpci.nt_timer = 0;
+						cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
+						Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+						/* Clear already pending ints */
+						if (Read_hfc(cs, HFCPCI_INT_S1));
+						Write_hfc(cs, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE);
+						udelay(10);
+						Write_hfc(cs, HFCPCI_STATES, 4);
+						cs->dc.hfcpci.ph_state = 4;
+					} else {
+						cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_TIMER;
+						Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+						cs->hw.hfcpci.ctmt &= ~HFCPCI_AUTO_TIMER;
+						cs->hw.hfcpci.ctmt |= HFCPCI_TIM3_125;
+						Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER);
+						Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER);
+						cs->hw.hfcpci.nt_timer = NT_T1_COUNT;
+						Write_hfc(cs, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3);	/* allow G2 -> G3 transition */
+					}
+					break;
+				case (1):
+				case (3):
+				case (4):
+					cs->hw.hfcpci.nt_timer = 0;
+					cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
+					Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+					break;
+				default:
+					break;
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+		}
+	}
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+}
+
+
+/********************************/
+/* called for card init message */
+/********************************/
+void __init
+inithfcpci(struct IsdnCardState *cs)
+{
+	cs->bcs[0].BC_SetStack = setstack_2b;
+	cs->bcs[1].BC_SetStack = setstack_2b;
+	cs->bcs[0].BC_Close = close_hfcpci;
+	cs->bcs[1].BC_Close = close_hfcpci;
+	cs->dbusytimer.function = (void *) hfcpci_dbusy_timer;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+	mode_hfcpci(cs->bcs, 0, 0);
+	mode_hfcpci(cs->bcs + 1, 0, 1);
+}
+
+
+
+/*******************************************/
+/* handle card messages from control layer */
+/*******************************************/
+static int
+hfcpci_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFCPCI: card_msg %x", mt);
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_hfcpci(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_RELEASE:
+			release_io_hfcpci(cs);
+			return (0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithfcpci(cs);
+			reset_hfcpci(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			msleep(80);				/* Timeout 80ms */
+			/* now switch timer interrupt off */
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
+			Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+			/* reinit mode reg */
+			Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_TEST:
+			return (0);
+	}
+	return (0);
+}
+
+
+/* this variable is used as card index when more than one cards are present */
+static struct pci_dev *dev_hfcpci __initdata = NULL;
+
+#endif				/* CONFIG_PCI */
+
+int __init
+setup_hfcpci(struct IsdnCard *card)
+{
+	u_long flags;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	int i;
+	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 "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp));
+#ifdef CONFIG_PCI
+	cs->hw.hfcpci.int_s1 = 0;
+	cs->dc.hfcpci.ph_state = 0;
+	cs->hw.hfcpci.fifo = 255;
+	if (cs->typ == ISDN_CTYPE_HFC_PCI) {
+		i = 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);
+				if ((card->para[0]) && (card->para[0] != (tmp_hfcpci->resource[ 0].start & PCI_BASE_ADDRESS_IO_MASK)))
+					continue;
+				else
+					break;
+			}
+		}
+
+		if (tmp_hfcpci) {
+			i--;
+			dev_hfcpci = tmp_hfcpci;	/* old device */
+			cs->hw.hfcpci.dev = dev_hfcpci;
+			cs->irq = dev_hfcpci->irq;
+			if (!cs->irq) {
+				printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n");
+				return (0);
+			}
+			cs->hw.hfcpci.pci_io = (char *) dev_hfcpci->resource[ 1].start;
+			printk(KERN_INFO "HiSax: 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 PCI card found\n");
+			return (0);
+		}
+		if (!cs->hw.hfcpci.pci_io) {
+			printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n");
+			return (0);
+		}
+		/* 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 (!(cs->hw.hfcpci.share_start = kmalloc(65536, GFP_KERNEL))) {
+			printk(KERN_WARNING "HFC-PCI: Error allocating memory for FIFO!\n");
+			return 0;
+		}
+		cs->hw.hfcpci.fifos = (void *)
+		    (((ulong) cs->hw.hfcpci.share_start) & ~0x7FFF) + 0x8000;
+		pci_write_config_dword(cs->hw.hfcpci.dev, 0x80, (u_int) virt_to_bus(cs->hw.hfcpci.fifos));
+		cs->hw.hfcpci.pci_io = ioremap((ulong) cs->hw.hfcpci.pci_io, 256);
+		printk(KERN_INFO
+		       "HFC-PCI: defined at mem %#x fifo %#x(%#x) IRQ %d HZ %d\n",
+		       (u_int) cs->hw.hfcpci.pci_io,
+		       (u_int) cs->hw.hfcpci.fifos,
+		       (u_int) virt_to_bus(cs->hw.hfcpci.fifos),
+		       cs->irq, HZ);
+		spin_lock_irqsave(&cs->lock, flags);
+		pci_write_config_word(cs->hw.hfcpci.dev, PCI_COMMAND, PCI_ENA_MEMIO);	/* enable memory mapped ports, disable busmaster */
+		cs->hw.hfcpci.int_m2 = 0;	/* disable alle interrupts */
+		cs->hw.hfcpci.int_m1 = 0;
+		Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+		Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+		/* At this point the needed PCI config is done */
+		/* fifos are still not enabled */
+		INIT_WORK(&cs->tqueue, (void *)(void *) hfcpci_bh, cs);
+		cs->setstack_d = setstack_hfcpci;
+		cs->BC_Send_Data = &hfcpci_send_data;
+		cs->readisac = NULL;
+		cs->writeisac = NULL;
+		cs->readisacfifo = NULL;
+		cs->writeisacfifo = NULL;
+		cs->BC_Read_Reg = NULL;
+		cs->BC_Write_Reg = NULL;
+		cs->irq_func = &hfcpci_interrupt;
+		cs->irq_flags |= SA_SHIRQ;
+		cs->hw.hfcpci.timer.function = (void *) hfcpci_Timer;
+		cs->hw.hfcpci.timer.data = (long) cs;
+		init_timer(&cs->hw.hfcpci.timer);
+		cs->cardmsg = &hfcpci_card_msg;
+		cs->auxcmd = &hfcpci_auxcmd;
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return (1);
+	} else
+		return (0);	/* no valid card type */
+#else
+	printk(KERN_WARNING "HFC-PCI: NO_PCI_BIOS\n");
+	return (0);
+#endif				/* CONFIG_PCI */
+}
diff --git a/drivers/isdn/hisax/hfc_pci.h b/drivers/isdn/hisax/hfc_pci.h
new file mode 100644
index 000000000000..4df036ed1af0
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_pci.h
@@ -0,0 +1,236 @@
+/* $Id: hfc_pci.h,v 1.10.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * specific defines for CCD's HFC 2BDS0 PCI chips
+ *
+ * Author       Werner Cornelius
+ * Copyright    by Werner Cornelius  <werner@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/*********************************************/
+/* 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
+/* 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.hfcpci.pci_io)+b) = c) 
+#define Read_hfc(a,b) (*(((u_char *)a->hw.hfcpci.pci_io)+b))
+
+extern void main_irq_hcpci(struct BCState *bcs);
+extern void inithfcpci(struct IsdnCardState *cs);
+extern void releasehfcpci(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/hfc_sx.c b/drivers/isdn/hisax/hfc_sx.c
new file mode 100644
index 000000000000..a307fcb6c634
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_sx.c
@@ -0,0 +1,1521 @@
+/* $Id: hfc_sx.c,v 1.12.2.5 2004/02/11 13:21:33 keil Exp $
+ *
+ * level driver for Cologne Chip Designs hfc-s+/sp based cards
+ *
+ * Author       Werner Cornelius
+ *              based on existing driver for CCD HFC PCI cards
+ * Copyright    by Werner Cornelius  <werner@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hfc_sx.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+#include <linux/isapnp.h>
+
+extern const char *CardType[];
+
+static const char *hfcsx_revision = "$Revision: 1.12.2.5 $";
+
+/***************************************/
+/* IRQ-table for CCDs demo board       */
+/* IRQs 6,5,10,11,12,15 are supported  */
+/***************************************/
+
+/* Teles 16.3c Vendor Id TAG2620, Version 1.0, Vendor version 2.1
+ *
+ * Thanks to Uwe Wisniewski
+ *
+ * ISA-SLOT  Signal      PIN
+ * B25        IRQ3     92 IRQ_G
+ * B23        IRQ5     94 IRQ_A
+ * B4         IRQ2/9   95 IRQ_B
+ * D3         IRQ10    96 IRQ_C
+ * D4         IRQ11    97 IRQ_D
+ * D5         IRQ12    98 IRQ_E
+ * D6         IRQ15    99 IRQ_F
+ */
+
+#undef CCD_DEMO_BOARD
+#ifdef CCD_DEMO_BOARD
+static u_char ccd_sp_irqtab[16] = {
+  0,0,0,0,0,2,1,0,0,0,3,4,5,0,0,6
+};
+#else /* Teles 16.3c */
+static u_char ccd_sp_irqtab[16] = {
+  0,0,0,7,0,1,0,0,0,2,3,4,5,0,0,6
+};
+#endif
+#define NT_T1_COUNT 20		/* number of 3.125ms interrupts for G2 timeout */
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+/******************************/
+/* In/Out access to registers */
+/******************************/
+static inline void
+Write_hfc(struct IsdnCardState *cs, u_char regnum, u_char val)
+{
+        byteout(cs->hw.hfcsx.base+1, regnum);
+	byteout(cs->hw.hfcsx.base, val);
+} 
+
+static inline u_char
+Read_hfc(struct IsdnCardState *cs, u_char regnum)
+{
+        u_char ret; 
+
+        byteout(cs->hw.hfcsx.base+1, regnum);
+	ret = bytein(cs->hw.hfcsx.base);
+	return(ret);
+} 
+
+
+/**************************************************/
+/* select a fifo and remember which one for reuse */
+/**************************************************/
+static void
+fifo_select(struct IsdnCardState *cs, u_char fifo)
+{
+        if (fifo == cs->hw.hfcsx.last_fifo) 
+	  return; /* still valid */
+
+        byteout(cs->hw.hfcsx.base+1, HFCSX_FIF_SEL);
+	byteout(cs->hw.hfcsx.base, fifo);
+	while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+	udelay(4);
+	byteout(cs->hw.hfcsx.base, fifo);
+	while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+}
+
+/******************************************/
+/* reset the specified fifo to defaults.  */
+/* If its a send fifo init needed markers */
+/******************************************/
+static void
+reset_fifo(struct IsdnCardState *cs, u_char fifo)
+{
+	fifo_select(cs, fifo); /* first select the fifo */
+	byteout(cs->hw.hfcsx.base+1, HFCSX_CIRM);
+	byteout(cs->hw.hfcsx.base, cs->hw.hfcsx.cirm | 0x80); /* reset cmd */
+	udelay(1);
+	while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+} 
+
+
+/*************************************************************/
+/* write_fifo writes the skb contents to the desired fifo    */
+/* if no space is available or an error occurs 0 is returned */
+/* the skb is not released in any way.                       */
+/*************************************************************/
+static int
+write_fifo(struct IsdnCardState *cs, struct sk_buff *skb, u_char fifo, int trans_max)
+{
+       unsigned short *msp;
+        int fifo_size, count, z1, z2;
+	u_char f_msk, f1, f2, *src;
+
+	if (skb->len <= 0) return(0);
+        if (fifo & 1) return(0); /* no write fifo */
+
+	fifo_select(cs, fifo);
+	if (fifo & 4) {
+	  fifo_size = D_FIFO_SIZE; /* D-channel */
+	  f_msk = MAX_D_FRAMES;
+	  if (trans_max) return(0); /* only HDLC */
+	}
+	else {
+	  fifo_size = cs->hw.hfcsx.b_fifo_size; /* B-channel */
+	  f_msk = MAX_B_FRAMES;
+	}
+
+        z1 = Read_hfc(cs, HFCSX_FIF_Z1H);
+	z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L));
+
+	/* Check for transparent mode */
+	if (trans_max) {
+	  z2 = Read_hfc(cs, HFCSX_FIF_Z2H);
+	  z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L));
+	  count = z2 - z1;
+	  if (count <= 0)
+	    count += fifo_size; /* free bytes */
+	  if (count < skb->len+1) return(0); /* no room */
+	  count = fifo_size - count; /* bytes still not send */
+	  if (count > 2 * trans_max) return(0); /* delay to long */
+	  count = skb->len;
+	  src = skb->data;
+	  while (count--)
+	    Write_hfc(cs, HFCSX_FIF_DWR, *src++);
+	  return(1); /* success */
+	}
+
+        msp = ((struct hfcsx_extra *)(cs->hw.hfcsx.extra))->marker;
+	msp += (((fifo >> 1) & 3) * (MAX_B_FRAMES+1));
+	f1 = Read_hfc(cs, HFCSX_FIF_F1) & f_msk;
+	f2 = Read_hfc(cs, HFCSX_FIF_F2) & f_msk;
+
+	count = f1 - f2; /* frame count actually buffered */
+	if (count < 0)
+		count += (f_msk + 1);	/* if wrap around */
+	if (count > f_msk-1) {
+	  if (cs->debug & L1_DEB_ISAC_FIFO)
+	    debugl1(cs, "hfcsx_write_fifo %d more as %d frames",fifo,f_msk-1);
+	  return(0);
+	}
+
+	*(msp + f1) = z1; /* remember marker */
+
+	if (cs->debug & L1_DEB_ISAC_FIFO)
+		debugl1(cs, "hfcsx_write_fifo %d f1(%x) f2(%x) z1(f1)(%x)",
+			fifo, f1, f2, z1);
+	/* now determine free bytes in FIFO buffer */
+	count = *(msp + f2) - z1;
+	if (count <= 0)
+	  count += fifo_size;	/* count now contains available bytes */
+
+	if (cs->debug & L1_DEB_ISAC_FIFO)
+	  debugl1(cs, "hfcsx_write_fifo %d count(%ld/%d)",
+		  fifo, skb->len, count);
+	if (count < skb->len) {
+	  if (cs->debug & L1_DEB_ISAC_FIFO)
+	    debugl1(cs, "hfcsx_write_fifo %d no fifo mem", fifo);
+	  return(0);
+	}
+	
+	count = skb->len; /* get frame len */
+	src = skb->data;	/* source pointer */
+	while (count--)
+	  Write_hfc(cs, HFCSX_FIF_DWR, *src++);
+	
+	Read_hfc(cs, HFCSX_FIF_INCF1); /* increment F1 */
+	udelay(1);
+	while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+	return(1);
+} 
+
+/***************************************************************/
+/* read_fifo reads data to an skb from the desired fifo        */
+/* if no data is available or an error occurs NULL is returned */
+/* the skb is not released in any way.                         */
+/***************************************************************/
+static struct sk_buff * 
+read_fifo(struct IsdnCardState *cs, u_char fifo, int trans_max)
+{       int fifo_size, count, z1, z2;
+	u_char f_msk, f1, f2, *dst;
+	struct sk_buff *skb;
+
+        if (!(fifo & 1)) return(NULL); /* no read fifo */
+	fifo_select(cs, fifo);
+	if (fifo & 4) {
+	  fifo_size = D_FIFO_SIZE; /* D-channel */
+	  f_msk = MAX_D_FRAMES;
+	  if (trans_max) return(NULL); /* only hdlc */
+	}
+	else {
+	  fifo_size = cs->hw.hfcsx.b_fifo_size; /* B-channel */
+	  f_msk = MAX_B_FRAMES;
+	}
+
+	/* transparent mode */
+	if (trans_max) {
+	  z1 = Read_hfc(cs, HFCSX_FIF_Z1H);
+	  z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L));
+	  z2 = Read_hfc(cs, HFCSX_FIF_Z2H);
+	  z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L));
+	  /* now determine bytes in actual FIFO buffer */
+	  count = z1 - z2;
+	  if (count <= 0)
+	    count += fifo_size;	/* count now contains buffered bytes */
+	  count++;
+	  if (count > trans_max) 
+	    count = trans_max; /* limit length */
+	    if ((skb = dev_alloc_skb(count))) {
+	      dst = skb_put(skb, count);
+	      while (count--) 
+		*dst++ = Read_hfc(cs, HFCSX_FIF_DRD);
+	      return(skb);
+	    }
+	    else return(NULL); /* no memory */
+	}
+
+	do {
+	  f1 = Read_hfc(cs, HFCSX_FIF_F1) & f_msk;
+	  f2 = Read_hfc(cs, HFCSX_FIF_F2) & f_msk;
+
+	  if (f1 == f2) return(NULL); /* no frame available */
+
+	  z1 = Read_hfc(cs, HFCSX_FIF_Z1H);
+	  z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L));
+	  z2 = Read_hfc(cs, HFCSX_FIF_Z2H);
+	  z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L));
+
+	  if (cs->debug & L1_DEB_ISAC_FIFO)
+	    debugl1(cs, "hfcsx_read_fifo %d f1(%x) f2(%x) z1(f2)(%x) z2(f2)(%x)",
+			fifo, f1, f2, z1, z2);
+	  /* now determine bytes in actual FIFO buffer */
+	  count = z1 - z2;
+	  if (count <= 0)
+	    count += fifo_size;	/* count now contains buffered bytes */
+	  count++;
+
+	  if (cs->debug & L1_DEB_ISAC_FIFO)
+	    debugl1(cs, "hfcsx_read_fifo %d count %ld)",
+		    fifo, count);
+
+	  if ((count > fifo_size) || (count < 4)) {
+	    if (cs->debug & L1_DEB_WARN)
+	      debugl1(cs, "hfcsx_read_fifo %d paket inv. len %d ", fifo , count);
+	    while (count) {
+	      count--; /* empty fifo */
+	      Read_hfc(cs, HFCSX_FIF_DRD);
+	    }
+	    skb = NULL;
+	  } else 
+	    if ((skb = dev_alloc_skb(count - 3))) {
+	      count -= 3;
+	      dst = skb_put(skb, count);
+
+	      while (count--) 
+		*dst++ = Read_hfc(cs, HFCSX_FIF_DRD);
+		    
+	      Read_hfc(cs, HFCSX_FIF_DRD); /* CRC 1 */
+	      Read_hfc(cs, HFCSX_FIF_DRD); /* CRC 2 */
+	      if (Read_hfc(cs, HFCSX_FIF_DRD)) {
+		dev_kfree_skb_irq(skb);
+		if (cs->debug & L1_DEB_ISAC_FIFO)
+		  debugl1(cs, "hfcsx_read_fifo %d crc error", fifo);
+		skb = NULL;
+	      }
+	    } else {
+	      printk(KERN_WARNING "HFC-SX: receive out of memory\n");
+	      return(NULL);
+	    }
+
+	  Read_hfc(cs, HFCSX_FIF_INCF2); /* increment F2 */
+	  udelay(1);
+	  while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+	  udelay(1);
+	} while (!skb); /* retry in case of crc error */
+	return(skb);
+} 
+
+/******************************************/
+/* free hardware resources used by driver */
+/******************************************/
+void
+release_io_hfcsx(struct IsdnCardState *cs)
+{
+	cs->hw.hfcsx.int_m2 = 0;	/* interrupt output off ! */
+	Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+	Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET);	/* Reset On */
+	msleep(30);				/* Timeout 30ms */
+	Write_hfc(cs, HFCSX_CIRM, 0);	/* Reset Off */
+	del_timer(&cs->hw.hfcsx.timer);
+	release_region(cs->hw.hfcsx.base, 2); /* release IO-Block */
+	kfree(cs->hw.hfcsx.extra);
+	cs->hw.hfcsx.extra = NULL;
+}
+
+/**********************************************************/
+/* set_fifo_size determines the size of the RAM and FIFOs */
+/* returning 0 -> need to reset the chip again.           */
+/**********************************************************/
+static int set_fifo_size(struct IsdnCardState *cs)
+{
+        
+        if (cs->hw.hfcsx.b_fifo_size) return(1); /* already determined */
+
+	if ((cs->hw.hfcsx.chip >> 4) == 9) {
+	  cs->hw.hfcsx.b_fifo_size = B_FIFO_SIZE_32K;
+	  return(1);
+	}
+
+	  cs->hw.hfcsx.b_fifo_size = B_FIFO_SIZE_8K;
+	  cs->hw.hfcsx.cirm |= 0x10; /* only 8K of ram */
+	  return(0);
+
+}
+
+/********************************************************************************/
+/* function called to reset the HFC SX chip. A complete software reset of chip */
+/* and fifos is done.                                                           */
+/********************************************************************************/
+static void
+reset_hfcsx(struct IsdnCardState *cs)
+{
+	cs->hw.hfcsx.int_m2 = 0;	/* interrupt output off ! */
+	Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+
+	printk(KERN_INFO "HFC_SX: resetting card\n");
+	while (1) {
+	  Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET | cs->hw.hfcsx.cirm ); /* Reset */
+	  mdelay(30);
+	  Write_hfc(cs, HFCSX_CIRM, cs->hw.hfcsx.cirm); /* Reset Off */
+	  mdelay(20);
+	  if (Read_hfc(cs, HFCSX_STATUS) & 2)
+	    printk(KERN_WARNING "HFC-SX init bit busy\n");
+	  cs->hw.hfcsx.last_fifo = 0xff; /* invalidate */
+	  if (!set_fifo_size(cs)) continue;
+	  break;
+	}
+
+	cs->hw.hfcsx.trm = 0 + HFCSX_BTRANS_THRESMASK;	/* no echo connect , threshold */
+	Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm);
+
+	Write_hfc(cs, HFCSX_CLKDEL, 0x0e);	/* ST-Bit delay for TE-Mode */
+	cs->hw.hfcsx.sctrl_e = HFCSX_AUTO_AWAKE;
+	Write_hfc(cs, HFCSX_SCTRL_E, cs->hw.hfcsx.sctrl_e);	/* S/T Auto awake */
+	cs->hw.hfcsx.bswapped = 0;	/* no exchange */
+	cs->hw.hfcsx.nt_mode = 0;	/* we are in TE mode */
+	cs->hw.hfcsx.ctmt = HFCSX_TIM3_125 | HFCSX_AUTO_TIMER;
+	Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt);
+
+	cs->hw.hfcsx.int_m1 = HFCSX_INTS_DTRANS | HFCSX_INTS_DREC | 
+	    HFCSX_INTS_L1STATE | HFCSX_INTS_TIMER;
+	Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+
+	/* Clear already pending ints */
+	if (Read_hfc(cs, HFCSX_INT_S1));
+
+	Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 2);	/* HFC ST 2 */
+	udelay(10);
+	Write_hfc(cs, HFCSX_STATES, 2);	/* HFC ST 2 */
+	cs->hw.hfcsx.mst_m = HFCSX_MASTER;	/* HFC Master Mode */
+
+	Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+	cs->hw.hfcsx.sctrl = 0x40;	/* set tx_lo mode, error in datasheet ! */
+	Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);
+	cs->hw.hfcsx.sctrl_r = 0;
+	Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.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 */
+	cs->hw.hfcsx.conn = 0x36;	/* set data flow directions */
+	Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+	Write_hfc(cs, HFCSX_B1_SSL, 0x80);	/* B1-Slot 0 STIO1 out enabled */
+	Write_hfc(cs, HFCSX_B2_SSL, 0x81);	/* B2-Slot 1 STIO1 out enabled */
+	Write_hfc(cs, HFCSX_B1_RSL, 0x80);	/* B1-Slot 0 STIO2 in enabled */
+	Write_hfc(cs, HFCSX_B2_RSL, 0x81);	/* B2-Slot 1 STIO2 in enabled */
+
+	/* Finally enable IRQ output */
+	cs->hw.hfcsx.int_m2 = HFCSX_IRQ_ENABLE;
+	Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+	if (Read_hfc(cs, HFCSX_INT_S2));
+}
+
+/***************************************************/
+/* Timer function called when kernel timer expires */
+/***************************************************/
+static void
+hfcsx_Timer(struct IsdnCardState *cs)
+{
+	cs->hw.hfcsx.timer.expires = jiffies + 75;
+	/* WD RESET */
+/*      WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcsx.ctmt | 0x80);
+   add_timer(&cs->hw.hfcsx.timer);
+ */
+}
+
+/************************************************/
+/* select a b-channel entry matching and active */
+/************************************************/
+static
+struct BCState *
+Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+	if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+		return (&cs->bcs[0]);
+	else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+		return (&cs->bcs[1]);
+	else
+		return (NULL);
+}
+
+/*******************************/
+/* D-channel receive procedure */
+/*******************************/
+static
+int
+receive_dmsg(struct IsdnCardState *cs)
+{
+	struct sk_buff *skb;
+	int count = 5;
+
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_dmsg blocked");
+		return (1);
+	}
+
+	do {
+	  skb = read_fifo(cs, HFCSX_SEL_D_RX, 0);
+	  if (skb) {
+	    skb_queue_tail(&cs->rq, skb);
+	    schedule_event(cs, D_RCVBUFREADY);
+	  }
+	} while (--count && skb);
+
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	return (1);
+}
+
+/**********************************/
+/* B-channel main receive routine */
+/**********************************/
+void
+main_rec_hfcsx(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int count = 5;
+	struct sk_buff *skb;
+
+      Begin:
+	count--;
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_data %d blocked", bcs->channel);
+		return;
+	}
+	skb = read_fifo(cs, ((bcs->channel) && (!cs->hw.hfcsx.bswapped)) ? 
+			HFCSX_SEL_B2_RX : HFCSX_SEL_B1_RX,
+			(bcs->mode == L1_MODE_TRANS) ? 
+			HFCSX_BTRANS_THRESHOLD : 0);
+
+	if (skb) {
+	  skb_queue_tail(&bcs->rqueue, skb);
+	  schedule_event(bcs, B_RCVBUFREADY);
+	}
+
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	if (count && skb)
+		goto Begin;
+	return;
+}
+
+/**************************/
+/* D-channel send routine */
+/**************************/
+static void
+hfcsx_fill_dfifo(struct IsdnCardState *cs)
+{
+	if (!cs->tx_skb)
+		return;
+	if (cs->tx_skb->len <= 0)
+		return;
+
+	if (write_fifo(cs, cs->tx_skb, HFCSX_SEL_D_TX, 0)) {
+	  dev_kfree_skb_any(cs->tx_skb);
+	  cs->tx_skb = NULL;
+	}
+	return;
+}
+
+/**************************/
+/* B-channel send routine */
+/**************************/
+static void
+hfcsx_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	if (write_fifo(cs, bcs->tx_skb, 
+		       ((bcs->channel) && (!cs->hw.hfcsx.bswapped)) ? 
+		       HFCSX_SEL_B2_TX : HFCSX_SEL_B1_TX,
+		       (bcs->mode == L1_MODE_TRANS) ? 
+		       HFCSX_BTRANS_THRESHOLD : 0)) {
+
+	  bcs->tx_cnt -= bcs->tx_skb->len;
+	  if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+		(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+		u_long	flags;
+		spin_lock_irqsave(&bcs->aclock, flags);
+		bcs->ackcnt += bcs->tx_skb->len;
+		spin_unlock_irqrestore(&bcs->aclock, flags);
+		schedule_event(bcs, B_ACKPENDING);
+	  }
+	  dev_kfree_skb_any(bcs->tx_skb);
+	  bcs->tx_skb = NULL;
+	  test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	}
+}
+
+/**********************************************/
+/* D-channel l1 state call for leased NT-mode */
+/**********************************************/
+static void
+dch_nt_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (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(cs, "PH_TEST_LOOP B1");
+			if (2 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B2");
+			if (!(3 & (long) arg))
+				debugl1(cs, "PH_TEST_LOOP DISABLED");
+			st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
+			break;
+		default:
+			if (cs->debug)
+				debugl1(cs, "dch_nt_l2l1 msg %04X unhandled", pr);
+			break;
+	}
+}
+
+
+
+/***********************/
+/* set/reset echo mode */
+/***********************/
+static int
+hfcsx_auxcmd(struct IsdnCardState *cs, isdn_ctrl * ic)
+{
+	unsigned long flags;
+	int i = *(unsigned int *) ic->parm.num;
+
+	if ((ic->arg == 98) &&
+	    (!(cs->hw.hfcsx.int_m1 & (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC + HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC)))) {
+	    	spin_lock_irqsave(&cs->lock, flags);
+		Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 0);	/* HFC ST G0 */
+		udelay(10);
+		cs->hw.hfcsx.sctrl |= SCTRL_MODE_NT;
+		Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);	/* set NT-mode */
+		udelay(10);
+		Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 1);	/* HFC ST G1 */
+		udelay(10);
+		Write_hfc(cs, HFCSX_STATES, 1 | HFCSX_ACTIVATE | HFCSX_DO_ACTION);
+		cs->dc.hfcsx.ph_state = 1;
+		cs->hw.hfcsx.nt_mode = 1;
+		cs->hw.hfcsx.nt_timer = 0;
+		spin_unlock_irqrestore(&cs->lock, flags);
+		cs->stlist->l2.l2l1 = dch_nt_l2l1;
+		debugl1(cs, "NT mode activated");
+		return (0);
+	}
+	if ((cs->chanlimit > 1) || (cs->hw.hfcsx.bswapped) ||
+	    (cs->hw.hfcsx.nt_mode) || (ic->arg != 12))
+		return (-EINVAL);
+
+	if (i) {
+		cs->logecho = 1;
+		cs->hw.hfcsx.trm |= 0x20;	/* enable echo chan */
+		cs->hw.hfcsx.int_m1 |= HFCSX_INTS_B2REC;
+		/* reset Channel !!!!! */
+	} else {
+		cs->logecho = 0;
+		cs->hw.hfcsx.trm &= ~0x20;	/* disable echo chan */
+		cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_B2REC;
+	}
+	cs->hw.hfcsx.sctrl_r &= ~SCTRL_B2_ENA;
+	cs->hw.hfcsx.sctrl &= ~SCTRL_B2_ENA;
+	cs->hw.hfcsx.conn |= 0x10;	/* B2-IOM -> B2-ST */
+	cs->hw.hfcsx.ctmt &= ~2;
+	spin_lock_irqsave(&cs->lock, flags);
+	Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt);
+	Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.sctrl_r);
+	Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);
+	Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+	Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm);
+	Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return (0);
+}				/* hfcsx_auxcmd */
+
+/*****************************/
+/* E-channel receive routine */
+/*****************************/
+static void
+receive_emsg(struct IsdnCardState *cs)
+{
+	int count = 5;
+	u_char *ptr;
+	struct sk_buff *skb;
+
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "echo_rec_data blocked");
+		return;
+	}
+	do {
+	  skb = read_fifo(cs, HFCSX_SEL_B2_RX, 0);
+	  if (skb) {
+	    if (cs->debug & DEB_DLOG_HEX) {
+	      ptr = cs->dlog;
+	      if ((skb->len) < MAX_DLOG_SPACE / 3 - 10) {
+		*ptr++ = 'E';
+		*ptr++ = 'C';
+		*ptr++ = 'H';
+		*ptr++ = 'O';
+		*ptr++ = ':';
+		ptr += QuickHex(ptr, skb->data, skb->len);
+		ptr--;
+		*ptr++ = '\n';
+		*ptr = 0;
+		HiSax_putstatus(cs, NULL, cs->dlog);
+	      } else
+		HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", skb->len);
+	    }
+	    dev_kfree_skb_any(skb);
+	  }
+	} while (--count && skb);
+
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	return;
+}				/* receive_emsg */
+
+
+/*********************/
+/* Interrupt handler */
+/*********************/
+static irqreturn_t
+hfcsx_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char exval;
+	struct BCState *bcs;
+	int count = 15;
+	u_long flags;
+	u_char val, stat;
+
+	if (!(cs->hw.hfcsx.int_m2 & 0x08))
+		return IRQ_NONE;		/* not initialised */
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if (HFCSX_ANYINT & (stat = Read_hfc(cs, HFCSX_STATUS))) {
+		val = Read_hfc(cs, HFCSX_INT_S1);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "HFC-SX: stat(%02x) s1(%02x)", stat, val);
+	} else {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFC-SX irq %x %s", val,
+			test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
+			"locked" : "unlocked");
+	val &= cs->hw.hfcsx.int_m1;
+	if (val & 0x40) {	/* state machine irq */
+		exval = Read_hfc(cs, HFCSX_STATES) & 0xf;
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcsx.ph_state,
+				exval);
+		cs->dc.hfcsx.ph_state = exval;
+		schedule_event(cs, D_L1STATECHANGE);
+		val &= ~0x40;
+	}
+	if (val & 0x80) {	/* timer irq */
+		if (cs->hw.hfcsx.nt_mode) {
+			if ((--cs->hw.hfcsx.nt_timer) < 0)
+				schedule_event(cs, D_L1STATECHANGE);
+		}
+		val &= ~0x80;
+		Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER);
+	}
+	while (val) {
+		if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			cs->hw.hfcsx.int_s1 |= val;
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_HANDLED;
+		}
+		if (cs->hw.hfcsx.int_s1 & 0x18) {
+			exval = val;
+			val = cs->hw.hfcsx.int_s1;
+			cs->hw.hfcsx.int_s1 = exval;
+		}
+		if (val & 0x08) {
+			if (!(bcs = Sel_BCS(cs, cs->hw.hfcsx.bswapped ? 1 : 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcsx spurious 0x08 IRQ");
+			} else
+				main_rec_hfcsx(bcs);
+		}
+		if (val & 0x10) {
+			if (cs->logecho)
+				receive_emsg(cs);
+			else if (!(bcs = Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcsx spurious 0x10 IRQ");
+			} else
+				main_rec_hfcsx(bcs);
+		}
+		if (val & 0x01) {
+			if (!(bcs = Sel_BCS(cs, cs->hw.hfcsx.bswapped ? 1 : 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcsx spurious 0x01 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcsx_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs, "fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfcsx_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs, "fill_data %d blocked", bcs->channel);
+					} else {
+						schedule_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x02) {
+			if (!(bcs = Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcsx spurious 0x02 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcsx_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs, "fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfcsx_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs, "fill_data %d blocked", bcs->channel);
+					} else {
+						schedule_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x20) {	/* receive dframe */
+			receive_dmsg(cs);
+		}
+		if (val & 0x04) {	/* dframe transmitted */
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			if (cs->tx_skb) {
+				if (cs->tx_skb->len) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcsx_fill_dfifo(cs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else {
+						debugl1(cs, "hfcsx_fill_dfifo irq blocked");
+					}
+					goto afterXPR;
+				} else {
+					dev_kfree_skb_irq(cs->tx_skb);
+					cs->tx_cnt = 0;
+					cs->tx_skb = NULL;
+				}
+			}
+			if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+				cs->tx_cnt = 0;
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfcsx_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else {
+					debugl1(cs, "hfcsx_fill_dfifo irq blocked");
+				}
+			} else
+				schedule_event(cs, D_XMTBUFREADY);
+		}
+	      afterXPR:
+		if (cs->hw.hfcsx.int_s1 && count--) {
+			val = cs->hw.hfcsx.int_s1;
+			cs->hw.hfcsx.int_s1 = 0;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "HFC-SX irq %x loop %d", val, 15 - count);
+		} else
+			val = 0;
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+/********************************************************************/
+/* timer callback for D-chan busy resolution. Currently no function */
+/********************************************************************/
+static void
+hfcsx_dbusy_timer(struct IsdnCardState *cs)
+{
+}
+
+/*************************************/
+/* Layer 1 D-channel hardware access */
+/*************************************/
+static void
+HFCSX_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+				        hfcsx_fill_dfifo(cs); 
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else
+					debugl1(cs, "hfcsx_fill_dfifo blocked");
+
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				spin_unlock_irqrestore(&cs->lock, flags);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+				hfcsx_fill_dfifo(cs); 
+				test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+			} else
+				debugl1(cs, "hfcsx_fill_dfifo blocked");
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 3);	/* HFC ST 3 */
+			udelay(6);
+			Write_hfc(cs, HFCSX_STATES, 3);	/* HFC ST 2 */
+			cs->hw.hfcsx.mst_m |= HFCSX_MASTER;
+			Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+			Write_hfc(cs, HFCSX_STATES, HFCSX_ACTIVATE | HFCSX_DO_ACTION);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (HW_ENABLE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			Write_hfc(cs, HFCSX_STATES, HFCSX_ACTIVATE | HFCSX_DO_ACTION);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_DEACTIVATE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcsx.mst_m &= ~HFCSX_MASTER;
+			Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO3 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcsx.mst_m |= HFCSX_MASTER;
+			Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			switch ((int) arg) {
+				case (1):
+					Write_hfc(cs, HFCSX_B1_SSL, 0x80);	/* tx slot */
+					Write_hfc(cs, HFCSX_B1_RSL, 0x80);	/* rx slot */
+					cs->hw.hfcsx.conn = (cs->hw.hfcsx.conn & ~7) | 1;
+					Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+					break;
+				case (2):
+					Write_hfc(cs, HFCSX_B2_SSL, 0x81);	/* tx slot */
+					Write_hfc(cs, HFCSX_B2_RSL, 0x81);	/* rx slot */
+					cs->hw.hfcsx.conn = (cs->hw.hfcsx.conn & ~0x38) | 0x08;
+					Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+					break;
+				default:
+					spin_unlock_irqrestore(&cs->lock, flags);
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "hfcsx_l1hw loop invalid %4x", (int) arg);
+					return;
+			}
+			cs->hw.hfcsx.trm |= 0x80;	/* enable IOM-loop */
+			Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "hfcsx_l1hw unknown pr %4x", pr);
+			break;
+	}
+}
+
+/***********************************************/
+/* called during init setting l1 stack pointer */
+/***********************************************/
+void
+setstack_hfcsx(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = HFCSX_l1hw;
+}
+
+/**************************************/
+/* send B-channel data if not blocked */
+/**************************************/
+static void
+hfcsx_send_data(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+	  hfcsx_fill_fifo(bcs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	} else
+		debugl1(cs, "send_data %d blocked", bcs->channel);
+}
+
+/***************************************************************/
+/* activate/deactivate hardware for selected channels and mode */
+/***************************************************************/
+void
+mode_hfcsx(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int fifo2;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "HFCSX bchannel mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	fifo2 = bc;
+	if (cs->chanlimit > 1) {
+		cs->hw.hfcsx.bswapped = 0;	/* B1 and B2 normal mode */
+		cs->hw.hfcsx.sctrl_e &= ~0x80;
+	} else {
+		if (bc) {
+			if (mode != L1_MODE_NULL) {
+				cs->hw.hfcsx.bswapped = 1;	/* B1 and B2 exchanged */
+				cs->hw.hfcsx.sctrl_e |= 0x80;
+			} else {
+				cs->hw.hfcsx.bswapped = 0;	/* B1 and B2 normal mode */
+				cs->hw.hfcsx.sctrl_e &= ~0x80;
+			}
+			fifo2 = 0;
+		} else {
+			cs->hw.hfcsx.bswapped = 0;	/* B1 and B2 normal mode */
+			cs->hw.hfcsx.sctrl_e &= ~0x80;
+		}
+	}
+	switch (mode) {
+		case (L1_MODE_NULL):
+			if (bc) {
+				cs->hw.hfcsx.sctrl &= ~SCTRL_B2_ENA;
+				cs->hw.hfcsx.sctrl_r &= ~SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcsx.sctrl &= ~SCTRL_B1_ENA;
+				cs->hw.hfcsx.sctrl_r &= ~SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+			} else {
+				cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+			}
+			break;
+		case (L1_MODE_TRANS):
+			if (bc) {
+				cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+				cs->hw.hfcsx.ctmt |= 2;
+				cs->hw.hfcsx.conn &= ~0x18;
+			} else {
+				cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+				cs->hw.hfcsx.ctmt |= 1;
+				cs->hw.hfcsx.conn &= ~0x03;
+			}
+			break;
+		case (L1_MODE_HDLC):
+			if (bc) {
+				cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+				cs->hw.hfcsx.ctmt &= ~2;
+				cs->hw.hfcsx.conn &= ~0x18;
+			} else {
+				cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+				cs->hw.hfcsx.ctmt &= ~1;
+				cs->hw.hfcsx.conn &= ~0x03;
+			}
+			break;
+		case (L1_MODE_EXTRN):
+			if (bc) {
+				cs->hw.hfcsx.conn |= 0x10;
+				cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA;
+				cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+			} else {
+				cs->hw.hfcsx.conn |= 0x02;
+				cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA;
+				cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+			}
+			break;
+	}
+	Write_hfc(cs, HFCSX_SCTRL_E, cs->hw.hfcsx.sctrl_e);
+	Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+	Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);
+	Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.sctrl_r);
+	Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt);
+	Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+	if (mode != L1_MODE_EXTRN) {
+	  reset_fifo(cs, fifo2 ? HFCSX_SEL_B2_RX : HFCSX_SEL_B1_RX);
+	  reset_fifo(cs, fifo2 ? HFCSX_SEL_B2_TX : HFCSX_SEL_B1_TX);
+	}
+}
+
+/******************************/
+/* Layer2 -> Layer 1 Transfer */
+/******************************/
+static void
+hfcsx_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+//                              test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+			} else {
+//				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->tx_skb = skb;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			mode_hfcsx(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			mode_hfcsx(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+/******************************************/
+/* deactivate B-channel access and queues */
+/******************************************/
+static void
+close_hfcsx(struct BCState *bcs)
+{
+	mode_hfcsx(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+/*************************************/
+/* init B-channel queues and control */
+/*************************************/
+static int
+open_hfcsxstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+/*********************************/
+/* inits the stack for B-channel */
+/*********************************/
+static int
+setstack_2b(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hfcsxstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hfcsx_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+/***************************/
+/* handle L1 state changes */
+/***************************/
+static void
+hfcsx_bh(struct IsdnCardState *cs)
+{
+	u_long flags;
+
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+		if (!cs->hw.hfcsx.nt_mode)
+			switch (cs->dc.hfcsx.ph_state) {
+				case (0):
+					l1_msg(cs, HW_RESET | INDICATION, NULL);
+					break;
+				case (3):
+					l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+					break;
+				case (8):
+					l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+					break;
+				case (6):
+					l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+					break;
+				case (7):
+					l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+					break;
+				default:
+					break;
+		} else {
+			switch (cs->dc.hfcsx.ph_state) {
+				case (2):
+					spin_lock_irqsave(&cs->lock, flags);
+					if (cs->hw.hfcsx.nt_timer < 0) {
+						cs->hw.hfcsx.nt_timer = 0;
+						cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
+						Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+						/* Clear already pending ints */
+						if (Read_hfc(cs, HFCSX_INT_S1));
+
+						Write_hfc(cs, HFCSX_STATES, 4 | HFCSX_LOAD_STATE);
+						udelay(10);
+						Write_hfc(cs, HFCSX_STATES, 4);
+						cs->dc.hfcsx.ph_state = 4;
+					} else {
+						cs->hw.hfcsx.int_m1 |= HFCSX_INTS_TIMER;
+						Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+						cs->hw.hfcsx.ctmt &= ~HFCSX_AUTO_TIMER;
+						cs->hw.hfcsx.ctmt |= HFCSX_TIM3_125;
+						Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER);
+						Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER);
+						cs->hw.hfcsx.nt_timer = NT_T1_COUNT;
+						Write_hfc(cs, HFCSX_STATES, 2 | HFCSX_NT_G2_G3);	/* allow G2 -> G3 transition */
+					}
+					spin_unlock_irqrestore(&cs->lock, flags);
+					break;
+				case (1):
+				case (3):
+				case (4):
+					spin_lock_irqsave(&cs->lock, flags);
+					cs->hw.hfcsx.nt_timer = 0;
+					cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
+					Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+					spin_unlock_irqrestore(&cs->lock, flags);
+					break;
+				default:
+					break;
+			}
+		}
+	}
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+}
+
+
+/********************************/
+/* called for card init message */
+/********************************/
+void __devinit
+inithfcsx(struct IsdnCardState *cs)
+{
+	cs->setstack_d = setstack_hfcsx;
+	cs->BC_Send_Data = &hfcsx_send_data;
+	cs->bcs[0].BC_SetStack = setstack_2b;
+	cs->bcs[1].BC_SetStack = setstack_2b;
+	cs->bcs[0].BC_Close = close_hfcsx;
+	cs->bcs[1].BC_Close = close_hfcsx;
+	mode_hfcsx(cs->bcs, 0, 0);
+	mode_hfcsx(cs->bcs + 1, 0, 1);
+}
+
+
+
+/*******************************************/
+/* handle card messages from control layer */
+/*******************************************/
+static int
+hfcsx_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFCSX: card_msg %x", mt);
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_hfcsx(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_RELEASE:
+			release_io_hfcsx(cs);
+			return (0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithfcsx(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			msleep(80);				/* Timeout 80ms */
+			/* now switch timer interrupt off */
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
+			Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+			/* reinit mode reg */
+			Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_TEST:
+			return (0);
+	}
+	return (0);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id hfc_ids[] __devinitdata = {
+	{ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2620),
+	  ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2620), 
+	  (unsigned long) "Teles 16.3c2" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &hfc_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit
+setup_hfcsx(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, hfcsx_revision);
+	printk(KERN_INFO "HiSax: HFC-SX driver Rev. %s\n", HiSax_getrev(tmp));
+#ifdef __ISAPNP__
+	if (!card->para[1] && isapnp_present()) {
+		struct pnp_dev *pnp_d;
+		while(ipid->card_vendor) {
+			if ((pnp_c = pnp_find_card(ipid->card_vendor,
+				ipid->card_device, pnp_c))) {
+				pnp_d = NULL;
+				if ((pnp_d = pnp_find_dev(pnp_c,
+					ipid->vendor, ipid->function, pnp_d))) {
+					int err;
+
+					printk(KERN_INFO "HiSax: %s detected\n",
+						(char *)ipid->driver_data);
+					pnp_disable_dev(pnp_d);
+					err = pnp_activate_dev(pnp_d);
+					if (err<0) {
+						printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+							__FUNCTION__, err);
+						return(0);
+					}
+					card->para[1] = pnp_port_start(pnp_d, 0);
+					card->para[0] = pnp_irq(pnp_d, 0);
+					if (!card->para[0] || !card->para[1]) {
+						printk(KERN_ERR "HFC PnP:some resources are missing %ld/%lx\n",
+							card->para[0], card->para[1]);
+						pnp_disable_dev(pnp_d);
+						return(0);
+					}
+					break;
+				} else {
+					printk(KERN_ERR "HFC PnP: PnP error card found, no device\n");
+				}
+			}
+			ipid++;
+			pnp_c = NULL;
+		} 
+		if (!ipid->card_vendor) {
+			printk(KERN_INFO "HFC PnP: no ISAPnP card found\n");
+			return(0);
+		}
+	}
+#endif
+	cs->hw.hfcsx.base = card->para[1] & 0xfffe;
+	cs->irq = card->para[0];
+	cs->hw.hfcsx.int_s1 = 0;
+	cs->dc.hfcsx.ph_state = 0;
+	cs->hw.hfcsx.fifo = 255;
+	if ((cs->typ == ISDN_CTYPE_HFC_SX) || 
+	    (cs->typ == ISDN_CTYPE_HFC_SP_PCMCIA)) {
+	        if ((!cs->hw.hfcsx.base) || !request_region(cs->hw.hfcsx.base, 2, "HFCSX isdn")) {
+		  printk(KERN_WARNING
+			 "HiSax: HFC-SX io-base %#lx already in use\n",
+		          cs->hw.hfcsx.base);
+		  return(0);
+		}
+		byteout(cs->hw.hfcsx.base, cs->hw.hfcsx.base & 0xFF);
+		byteout(cs->hw.hfcsx.base + 1,
+			((cs->hw.hfcsx.base >> 8) & 3) | 0x54);
+		udelay(10);
+	        cs->hw.hfcsx.chip = Read_hfc(cs,HFCSX_CHIP_ID);
+                switch (cs->hw.hfcsx.chip >> 4) {
+		  case 1: 
+		    tmp[0] ='+';
+		    break;
+		  case 9: 
+		    tmp[0] ='P';
+		    break;
+		  default:
+		    printk(KERN_WARNING
+			   "HFC-SX: invalid chip id 0x%x\n",
+			   cs->hw.hfcsx.chip >> 4);
+		    release_region(cs->hw.hfcsx.base, 2);
+		    return(0);
+		}  
+		if (!ccd_sp_irqtab[cs->irq & 0xF]) {
+		  printk(KERN_WARNING 
+			 "HFC_SX: invalid irq %d specified\n",cs->irq & 0xF);
+		  release_region(cs->hw.hfcsx.base, 2);
+		  return(0);
+		}  
+		if (!(cs->hw.hfcsx.extra = (void *)
+		      kmalloc(sizeof(struct hfcsx_extra), GFP_ATOMIC))) {
+		  release_region(cs->hw.hfcsx.base, 2);
+		  printk(KERN_WARNING "HFC-SX: unable to allocate memory\n");
+		  return(0);
+		}
+		printk(KERN_INFO "HFC-S%c chip detected at base 0x%x IRQ %d HZ %d\n",
+			tmp[0], (u_int) cs->hw.hfcsx.base, cs->irq, HZ);
+		cs->hw.hfcsx.int_m2 = 0;	/* disable alle interrupts */
+		cs->hw.hfcsx.int_m1 = 0;
+		Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+		Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+	} else
+		return (0);	/* no valid card type */
+
+	cs->dbusytimer.function = (void *) hfcsx_dbusy_timer;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+	INIT_WORK(&cs->tqueue, (void *)(void *) hfcsx_bh, cs);
+	cs->readisac = NULL;
+	cs->writeisac = NULL;
+	cs->readisacfifo = NULL;
+	cs->writeisacfifo = NULL;
+	cs->BC_Read_Reg = NULL;
+	cs->BC_Write_Reg = NULL;
+	cs->irq_func = &hfcsx_interrupt;
+
+	cs->hw.hfcsx.timer.function = (void *) hfcsx_Timer;
+	cs->hw.hfcsx.timer.data = (long) cs;
+	cs->hw.hfcsx.b_fifo_size = 0; /* fifo size still unknown */
+	cs->hw.hfcsx.cirm = ccd_sp_irqtab[cs->irq & 0xF]; /* RAM not evaluated */
+	init_timer(&cs->hw.hfcsx.timer);
+
+	reset_hfcsx(cs);
+	cs->cardmsg = &hfcsx_card_msg;
+	cs->auxcmd = &hfcsx_auxcmd;
+	return (1);
+}
diff --git a/drivers/isdn/hisax/hfc_sx.h b/drivers/isdn/hisax/hfc_sx.h
new file mode 100644
index 000000000000..12f54159344a
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_sx.h
@@ -0,0 +1,197 @@
+/* $Id: hfc_sx.h,v 1.2.6.1 2001/09/23 22:24:48 kai Exp $
+ *
+ * specific defines for CCD's HFC 2BDS0 S+,SP chips
+ *
+ * Author       Werner Cornelius
+ *              based on existing driver for CCD HFC PCI cards
+ * Copyright    by Werner Cornelius  <werner@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/*********************************************/
+/* thresholds for transparent B-channel mode */
+/* change mask and threshold simultaneously  */
+/*********************************************/
+#define HFCSX_BTRANS_THRESHOLD 128
+#define HFCSX_BTRANS_THRESMASK 0x00
+
+/* GCI/IOM bus monitor registers */
+
+#define HFCSX_C_I       0x02
+#define HFCSX_TRxR      0x03
+#define HFCSX_MON1_D    0x0A
+#define HFCSX_MON2_D    0x0B
+
+
+/* GCI/IOM bus timeslot registers */
+
+#define HFCSX_B1_SSL    0x20
+#define HFCSX_B2_SSL    0x21
+#define HFCSX_AUX1_SSL  0x22
+#define HFCSX_AUX2_SSL  0x23
+#define HFCSX_B1_RSL    0x24
+#define HFCSX_B2_RSL    0x25
+#define HFCSX_AUX1_RSL  0x26
+#define HFCSX_AUX2_RSL  0x27
+
+/* GCI/IOM bus data registers */
+
+#define HFCSX_B1_D      0x28
+#define HFCSX_B2_D      0x29
+#define HFCSX_AUX1_D    0x2A
+#define HFCSX_AUX2_D    0x2B
+
+/* GCI/IOM bus configuration registers */
+
+#define HFCSX_MST_EMOD  0x2D
+#define HFCSX_MST_MODE	0x2E
+#define HFCSX_CONNECT 	0x2F
+
+
+/* Interrupt and status registers */
+
+#define HFCSX_TRM       0x12
+#define HFCSX_B_MODE    0x13
+#define HFCSX_CHIP_ID   0x16
+#define HFCSX_CIRM  	0x18
+#define HFCSX_CTMT	0x19
+#define HFCSX_INT_M1  	0x1A
+#define HFCSX_INT_M2  	0x1B
+#define HFCSX_INT_S1  	0x1E
+#define HFCSX_INT_S2  	0x1F
+#define HFCSX_STATUS  	0x1C
+
+/* S/T section registers */
+
+#define HFCSX_STATES  	0x30
+#define HFCSX_SCTRL  	0x31
+#define HFCSX_SCTRL_E   0x32
+#define HFCSX_SCTRL_R   0x33
+#define HFCSX_SQ  	0x34
+#define HFCSX_CLKDEL  	0x37
+#define HFCSX_B1_REC    0x3C
+#define HFCSX_B1_SEND   0x3C
+#define HFCSX_B2_REC    0x3D
+#define HFCSX_B2_SEND   0x3D
+#define HFCSX_D_REC     0x3E
+#define HFCSX_D_SEND    0x3E
+#define HFCSX_E_REC     0x3F
+
+/****************/
+/* FIFO section */
+/****************/
+#define HFCSX_FIF_SEL   0x10
+#define HFCSX_FIF_Z1L   0x80
+#define HFCSX_FIF_Z1H   0x84
+#define HFCSX_FIF_Z2L   0x88
+#define HFCSX_FIF_Z2H   0x8C
+#define HFCSX_FIF_INCF1 0xA8
+#define HFCSX_FIF_DWR   0xAC
+#define HFCSX_FIF_F1    0xB0
+#define HFCSX_FIF_F2    0xB4
+#define HFCSX_FIF_INCF2 0xB8
+#define HFCSX_FIF_DRD   0xBC
+
+/* bits in status register (READ) */
+#define HFCSX_SX_PROC    0x02
+#define HFCSX_NBUSY	 0x04 
+#define HFCSX_TIMER_ELAP 0x10
+#define HFCSX_STATINT	 0x20
+#define HFCSX_FRAMEINT	 0x40
+#define HFCSX_ANYINT	 0x80
+
+/* bits in CTMT (Write) */
+#define HFCSX_CLTIMER    0x80
+#define HFCSX_TIM3_125   0x04
+#define HFCSX_TIM25      0x10
+#define HFCSX_TIM50      0x14
+#define HFCSX_TIM400     0x18
+#define HFCSX_TIM800     0x1C
+#define HFCSX_AUTO_TIMER 0x20
+#define HFCSX_TRANSB2    0x02
+#define HFCSX_TRANSB1    0x01
+
+/* bits in CIRM (Write) */
+#define HFCSX_IRQ_SELMSK 0x07
+#define HFCSX_IRQ_SELDIS 0x00
+#define HFCSX_RESET  	 0x08
+#define HFCSX_FIFO_RESET 0x80
+
+
+/* bits in INT_M1 and INT_S1 */
+#define HFCSX_INTS_B1TRANS  0x01
+#define HFCSX_INTS_B2TRANS  0x02
+#define HFCSX_INTS_DTRANS   0x04
+#define HFCSX_INTS_B1REC    0x08
+#define HFCSX_INTS_B2REC    0x10
+#define HFCSX_INTS_DREC     0x20
+#define HFCSX_INTS_L1STATE  0x40
+#define HFCSX_INTS_TIMER    0x80
+
+/* bits in INT_M2 */
+#define HFCSX_PROC_TRANS    0x01
+#define HFCSX_GCI_I_CHG     0x02
+#define HFCSX_GCI_MON_REC   0x04
+#define HFCSX_IRQ_ENABLE    0x08
+
+/* bits in STATES */
+#define HFCSX_STATE_MSK     0x0F
+#define HFCSX_LOAD_STATE    0x10
+#define HFCSX_ACTIVATE	    0x20
+#define HFCSX_DO_ACTION     0x40
+#define HFCSX_NT_G2_G3      0x80
+
+/* bits in HFCD_MST_MODE */
+#define HFCSX_MASTER	    0x01
+#define HFCSX_SLAVE         0x00
+/* 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 HFCSX_AUTO_AWAKE    0x01
+#define HFCSX_DBIT_1        0x04
+#define HFCSX_IGNORE_COL    0x08
+#define HFCSX_CHG_B1_B2     0x80
+
+/**********************************/
+/* definitions for FIFO selection */
+/**********************************/
+#define HFCSX_SEL_D_RX      5
+#define HFCSX_SEL_D_TX      4
+#define HFCSX_SEL_B1_RX     1
+#define HFCSX_SEL_B1_TX     0
+#define HFCSX_SEL_B2_RX     3
+#define HFCSX_SEL_B2_TX     2
+
+#define MAX_D_FRAMES 15
+#define MAX_B_FRAMES 31
+#define B_SUB_VAL_32K       0x0200
+#define B_FIFO_SIZE_32K    (0x2000 - B_SUB_VAL_32K)
+#define B_SUB_VAL_8K        0x1A00
+#define B_FIFO_SIZE_8K     (0x2000 - B_SUB_VAL_8K)
+#define D_FIFO_SIZE  512
+#define D_FREG_MASK  0xF
+
+/************************************************************/
+/* structure holding additional dynamic data -> send marker */
+/************************************************************/
+struct hfcsx_extra {
+  unsigned short marker[2*(MAX_B_FRAMES+1) + (MAX_D_FRAMES+1)];
+};
+
+extern void main_irq_hfcsx(struct BCState *bcs);
+extern void inithfcsx(struct IsdnCardState *cs);
+extern void releasehfcsx(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c
new file mode 100644
index 000000000000..ffd74b84f502
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_usb.c
@@ -0,0 +1,1828 @@
+/*
+ * hfc_usb.c
+ *
+ * $Id: hfc_usb.c,v 4.34 2005/01/26 17:25:53 martinb1 Exp $
+ *
+ * modular HiSax ISDN driver for Colognechip HFC-S USB chip
+ *
+ * Authors : Peter Sprenger  (sprenger@moving-bytes.de)
+ *           Martin Bachem   (info@colognechip.com)
+ *
+ *           based on the first hfc_usb driver of
+ *           Werner Cornelius (werner@isdn-development.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.
+ *
+ * See Version Histroy at the bottom of this file
+ *
+*/
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel_stat.h>
+#include <linux/usb.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include "hisax.h"
+#include "hisax_if.h"
+#include "hfc_usb.h"
+
+/*
+* Version Information
+* (do not modify the CVS Makros $Revision: 4.34 $ and $Date: 2005/01/26 17:25:53 $ !)
+*/
+static const char *hfcusb_revision =
+    "Revision: 4.34 $ Date: 2005/01/26 17:25:53 $ ";
+
+/* Hisax debug support
+* use "modprobe debug=x" where x is bitfield of USB_DBG & ISDN_DBG
+*/
+#ifdef CONFIG_HISAX_DEBUG
+#include <linux/moduleparam.h>
+#define __debug_variable hfc_debug
+#include "hisax_debug.h"
+static u_int debug;
+module_param(debug, uint, 0);
+int hfc_debug;
+#endif
+
+
+/****************************************/
+/* data defining the devices to be used */
+/****************************************/
+static struct usb_device_id hfc_usb_idtab[] = {
+	{USB_DEVICE(0x0959, 0x2bd0)},	/* Colognechip USB eval TA */
+	{USB_DEVICE(0x0675, 0x1688)},	/* DrayTek miniVigor 128 USB ISDN TA */
+	{USB_DEVICE(0x07b0, 0x0007)},	/* Billion USB TA 2 */
+	{USB_DEVICE(0x0742, 0x2008)},	/* Stollmann USB TA */
+	{USB_DEVICE(0x0742, 0x2009)},	/* Aceex USB ISDN TA */
+	{USB_DEVICE(0x0742, 0x200A)},	/* OEM USB ISDN TA */
+	{USB_DEVICE(0x08e3, 0x0301)},	/* OliTec ISDN USB */
+	{USB_DEVICE(0x07fa, 0x0846)},	/* Bewan ISDN USB TA */
+	{USB_DEVICE(0x07fa, 0x0847)},	/* Djinn Numeris USB */
+	{USB_DEVICE(0x07b0, 0x0006)},	/* Twister ISDN USB TA */
+	{}			/* end with an all-zeroes entry */
+};
+
+/* driver internal device specific data:
+*   VendorID, ProductID, Devicename, LED_SCHEME,
+*   LED's BitMask in HFCUSB_P_DATA Register : LED_USB, LED_S0, LED_B1, LED_B2
+*/
+vendor_data vdata[] = {
+	/* CologneChip Eval TA */
+	{0x0959, 0x2bd0, "ISDN USB TA (Cologne Chip HFC-S USB based)",
+	 LED_OFF, {4, 0, 2, 1}
+	 }
+	,
+	/* DrayTek miniVigor 128 USB ISDN TA */
+	{0x0675, 0x1688, "DrayTek miniVigor 128 USB ISDN TA",
+	 LED_SCHEME1, {1, 2, 0, 0}
+	 }
+	,
+	/* Billion TA */
+	{0x07b0, 0x0007, "Billion tiny USB ISDN TA 128",
+	 LED_SCHEME1, {0x80, -64, -32, -16}
+	 }
+	,
+	/* Stollmann TA */
+	{0x0742, 0x2008, "Stollmann USB TA",
+	 LED_SCHEME1, {4, 0, 2, 1}
+	 }
+	,
+	/* Aceex USB ISDN TA */
+	{0x0742, 0x2009, "Aceex USB ISDN TA",
+	 LED_SCHEME1, {4, 0, 2, 1}
+	 }
+	,
+	/* OEM USB ISDN TA */
+	{0x0742, 0x200A, "OEM USB ISDN TA",
+	 LED_SCHEME1, {4, 0, 2, 1}
+	 }
+	,
+	/* Olitec TA  */
+	{0x08e3, 0x0301, "Olitec USB RNIS",
+	 LED_SCHEME1, {2, 0, 1, 4}
+	 }
+	,
+	/* Bewan TA   */
+	{0x07fa, 0x0846, "Bewan Modem RNIS USB",
+	 LED_SCHEME1, {0x80, -64, -32, -16}
+	 }
+	,
+	/* Bewan TA   */
+	{0x07fa, 0x0847, "Djinn Numeris USB",
+	 LED_SCHEME1, {0x80, -64, -32, -16}
+	 }
+	,
+	/* Twister ISDN TA   */
+	{0x07b0, 0x0006, "Twister ISDN TA",
+	 LED_SCHEME1, {0x80, -64, -32, -16}
+	 }
+	,
+	{0, 0, 0}		/* EOL element */
+};
+
+/***************************************************************/
+/* structure defining input+output fifos (interrupt/bulk mode) */
+/***************************************************************/
+struct usb_fifo;		/* forward definition */
+typedef struct iso_urb_struct {
+	struct urb *purb;
+	__u8 buffer[ISO_BUFFER_SIZE];	/* buffer incoming/outgoing data */
+	struct usb_fifo *owner_fifo;	/* pointer to owner fifo */
+} iso_urb_struct;
+
+
+struct hfcusb_data;		/* forward definition */
+typedef struct usb_fifo {
+	int fifonum;		/* fifo index attached to this structure */
+	int active;		/* fifo is currently active */
+	struct hfcusb_data *hfc;	/* pointer to main structure */
+	int pipe;		/* address of endpoint */
+	__u8 usb_packet_maxlen;	/* maximum length for usb transfer */
+	unsigned int max_size;	/* maximum size of receive/send packet */
+	__u8 intervall;		/* interrupt interval */
+	struct sk_buff *skbuff;	/* actual used buffer */
+	struct urb *urb;	/* transfer structure for usb routines */
+	__u8 buffer[128];	/* buffer incoming/outgoing data */
+	int bit_line;		/* how much bits are in the fifo? */
+
+	volatile __u8 usb_transfer_mode;	/* switched between ISO and INT */
+	iso_urb_struct iso[2];	/* need two urbs to have one always for pending */
+	struct hisax_if *hif;	/* hisax interface */
+	int delete_flg;		/* only delete skbuff once */
+	int last_urblen;	/* remember length of last packet */
+
+} usb_fifo;
+
+/*********************************************/
+/* structure holding all data for one device */
+/*********************************************/
+typedef struct hfcusb_data {
+	/* HiSax Interface for loadable Layer1 drivers */
+	struct hisax_d_if d_if;	/* see hisax_if.h */
+	struct hisax_b_if b_if[2];	/* see hisax_if.h */
+	int protocol;
+
+	struct usb_device *dev;	/* our device */
+	int if_used;		/* used interface number */
+	int alt_used;		/* used alternate config */
+	int ctrl_paksize;	/* control pipe packet size */
+	int ctrl_in_pipe, ctrl_out_pipe;	/* handles for control pipe */
+	int cfg_used;		/* configuration index used */
+	int vend_idx;		/* vendor found */
+	int b_mode[2];		/* B-channel mode */
+	int l1_activated;	/* layer 1 activated */
+	int disc_flag;		/* TRUE if device was disonnected to avoid some USB actions */
+	int packet_size, iso_packet_size;
+
+	/* control pipe background handling */
+	ctrl_buft ctrl_buff[HFC_CTRL_BUFSIZE];	/* buffer holding queued data */
+	volatile int ctrl_in_idx, ctrl_out_idx, ctrl_cnt;	/* input/output pointer + count */
+	struct urb *ctrl_urb;	/* transfer structure for control channel */
+
+	struct usb_ctrlrequest ctrl_write;	/* buffer for control write request */
+	struct usb_ctrlrequest ctrl_read;	/* same for read request */
+
+	__u8 old_led_state, led_state, led_new_data, led_b_active;
+
+	volatile __u8 threshold_mask;	/* threshold actually reported */
+	volatile __u8 bch_enables;	/* or mask for sctrl_r and sctrl register values */
+
+	usb_fifo fifos[HFCUSB_NUM_FIFOS];	/* structure holding all fifo data */
+
+	volatile __u8 l1_state;	/* actual l1 state */
+	struct timer_list t3_timer;	/* timer 3 for activation/deactivation */
+	struct timer_list t4_timer;	/* timer 4 for activation/deactivation */
+	struct timer_list led_timer;	/* timer flashing leds */
+
+} hfcusb_data;
+
+
+static void collect_rx_frame(usb_fifo * fifo, __u8 * data, int len,
+			     int finish);
+
+
+static inline const char *
+symbolic(struct hfcusb_symbolic_list list[], const int num)
+{
+	int i;
+	for (i = 0; list[i].name != NULL; i++)
+		if (list[i].num == num)
+			return (list[i].name);
+	return "<unkown>";
+}
+
+
+/******************************************************/
+/* start next background transfer for control channel */
+/******************************************************/
+static void
+ctrl_start_transfer(hfcusb_data * hfc)
+{
+	if (hfc->ctrl_cnt) {
+		hfc->ctrl_urb->pipe = hfc->ctrl_out_pipe;
+		hfc->ctrl_urb->setup_packet = (u_char *) & hfc->ctrl_write;
+		hfc->ctrl_urb->transfer_buffer = NULL;
+		hfc->ctrl_urb->transfer_buffer_length = 0;
+		hfc->ctrl_write.wIndex =
+		    hfc->ctrl_buff[hfc->ctrl_out_idx].hfc_reg;
+		hfc->ctrl_write.wValue =
+		    hfc->ctrl_buff[hfc->ctrl_out_idx].reg_val;
+
+		usb_submit_urb(hfc->ctrl_urb, GFP_ATOMIC);	/* start transfer */
+	}
+}				/* ctrl_start_transfer */
+
+/************************************/
+/* queue a control transfer request */
+/* return 0 on success.             */
+/************************************/
+static int
+queue_control_request(hfcusb_data * hfc, __u8 reg, __u8 val, int action)
+{
+	ctrl_buft *buf;
+
+	if (hfc->ctrl_cnt >= HFC_CTRL_BUFSIZE)
+		return (1);	/* no space left */
+	buf = &hfc->ctrl_buff[hfc->ctrl_in_idx];	/* pointer to new index */
+	buf->hfc_reg = reg;
+	buf->reg_val = val;
+	buf->action = action;
+	if (++hfc->ctrl_in_idx >= HFC_CTRL_BUFSIZE)
+		hfc->ctrl_in_idx = 0;	/* pointer wrap */
+	if (++hfc->ctrl_cnt == 1)
+		ctrl_start_transfer(hfc);
+	return (0);
+}				/* queue_control_request */
+
+static int
+control_action_handler(hfcusb_data * hfc, int reg, int val, int action)
+{
+	if (!action)
+		return (1);	/* no action defined */
+	return (0);
+}
+
+/***************************************************************/
+/* control completion routine handling background control cmds */
+/***************************************************************/
+static void
+ctrl_complete(struct urb *urb, struct pt_regs *regs)
+{
+	hfcusb_data *hfc = (hfcusb_data *) urb->context;
+	ctrl_buft *buf;
+
+	urb->dev = hfc->dev;
+	if (hfc->ctrl_cnt) {
+		buf = &hfc->ctrl_buff[hfc->ctrl_out_idx];
+		control_action_handler(hfc, buf->hfc_reg, buf->reg_val,
+				       buf->action);
+
+		hfc->ctrl_cnt--;	/* decrement actual count */
+		if (++hfc->ctrl_out_idx >= HFC_CTRL_BUFSIZE)
+			hfc->ctrl_out_idx = 0;	/* pointer wrap */
+
+		ctrl_start_transfer(hfc);	/* start next transfer */
+	}
+}				/* ctrl_complete */
+
+/***************************************************/
+/* write led data to auxport & invert if necessary */
+/***************************************************/
+static void
+write_led(hfcusb_data * hfc, __u8 led_state)
+{
+	if (led_state != hfc->old_led_state) {
+		hfc->old_led_state = led_state;
+		queue_control_request(hfc, HFCUSB_P_DATA, led_state, 1);
+	}
+}
+
+/**************************/
+/* handle LED bits        */
+/**************************/
+static void
+set_led_bit(hfcusb_data * hfc, signed short led_bits, int unset)
+{
+	if (unset) {
+		if (led_bits < 0)
+			hfc->led_state |= abs(led_bits);
+		else
+			hfc->led_state &= ~led_bits;
+	} else {
+		if (led_bits < 0)
+			hfc->led_state &= ~abs(led_bits);
+		else
+			hfc->led_state |= led_bits;
+	}
+}
+
+/******************************************/
+/* invert B-channel LEDs if data is sent  */
+/******************************************/
+static void
+led_timer(hfcusb_data * hfc)
+{
+	static int cnt = 0;
+
+	if (cnt) {
+		if (hfc->led_b_active & 1)
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[2],
+				    0);
+		if (hfc->led_b_active & 2)
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[3],
+				    0);
+	} else {
+		if (!(hfc->led_b_active & 1) || hfc->led_new_data & 1)
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[2],
+				    1);
+		if (!(hfc->led_b_active & 2) || hfc->led_new_data & 2)
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[3],
+				    1);
+	}
+
+	write_led(hfc, hfc->led_state);
+	hfc->led_new_data = 0;
+
+	cnt = !cnt;
+
+	/* restart 4 hz timer */
+	if (!timer_pending(&hfc->led_timer)) {
+		add_timer(&hfc->led_timer);
+		hfc->led_timer.expires = jiffies + (LED_TIME * HZ) / 1000;
+	}
+}
+
+/**************************/
+/* handle LED requests    */
+/**************************/
+static void
+handle_led(hfcusb_data * hfc, int event)
+{
+	/* if no scheme -> no LED action */
+	if (vdata[hfc->vend_idx].led_scheme == LED_OFF)
+		return;
+
+	switch (event) {
+		case LED_POWER_ON:
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[0],
+				    0);
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[1],
+				    1);
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[2],
+				    1);
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[3],
+				    1);
+			break;
+		case LED_POWER_OFF:	/* no Power off handling */
+			break;
+		case LED_S0_ON:
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[1],
+				    0);
+			break;
+		case LED_S0_OFF:
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[1],
+				    1);
+			break;
+		case LED_B1_ON:
+			hfc->led_b_active |= 1;
+			break;
+		case LED_B1_OFF:
+			hfc->led_b_active &= ~1;
+			break;
+		case LED_B1_DATA:
+			hfc->led_new_data |= 1;
+			break;
+		case LED_B2_ON:
+			hfc->led_b_active |= 2;
+			break;
+		case LED_B2_OFF:
+			hfc->led_b_active &= ~2;
+			break;
+		case LED_B2_DATA:
+			hfc->led_new_data |= 2;
+			break;
+	}
+
+	write_led(hfc, hfc->led_state);
+}
+
+/********************************/
+/* called when timer t3 expires */
+/********************************/
+static void
+l1_timer_expire_t3(hfcusb_data * hfc)
+{
+	hfc->d_if.ifc.l1l2(&hfc->d_if.ifc, PH_DEACTIVATE | INDICATION,
+			   NULL);
+#ifdef CONFIG_HISAX_DEBUG
+	DBG(ISDN_DBG,
+	    "HFC-S USB: PH_DEACTIVATE | INDICATION sent (T3 expire)");
+#endif
+	hfc->l1_activated = FALSE;
+	handle_led(hfc, LED_S0_OFF);
+	/* deactivate : */
+	queue_control_request(hfc, HFCUSB_STATES, 0x10, 1);
+	queue_control_request(hfc, HFCUSB_STATES, 3, 1);
+}
+
+/********************************/
+/* called when timer t4 expires */
+/********************************/
+static void
+l1_timer_expire_t4(hfcusb_data * hfc)
+{
+	hfc->d_if.ifc.l1l2(&hfc->d_if.ifc, PH_DEACTIVATE | INDICATION,
+			   NULL);
+#ifdef CONFIG_HISAX_DEBUG
+	DBG(ISDN_DBG,
+	    "HFC-S USB: PH_DEACTIVATE | INDICATION sent (T4 expire)");
+#endif
+	hfc->l1_activated = FALSE;
+	handle_led(hfc, LED_S0_OFF);
+}
+
+/*****************************/
+/* handle S0 state changes   */
+/*****************************/
+static void
+state_handler(hfcusb_data * hfc, __u8 state)
+{
+	__u8 old_state;
+
+	old_state = hfc->l1_state;
+	if (state == old_state || state < 1 || state > 8)
+		return;
+
+#ifdef CONFIG_HISAX_DEBUG
+	DBG(ISDN_DBG, "HFC-S USB: new S0 state:%d old_state:%d", state,
+	    old_state);
+#endif
+	if (state < 4 || state == 7 || state == 8) {
+		if (timer_pending(&hfc->t3_timer))
+			del_timer(&hfc->t3_timer);
+#ifdef CONFIG_HISAX_DEBUG
+		DBG(ISDN_DBG, "HFC-S USB: T3 deactivated");
+#endif
+	}
+	if (state >= 7) {
+		if (timer_pending(&hfc->t4_timer))
+			del_timer(&hfc->t4_timer);
+#ifdef CONFIG_HISAX_DEBUG
+		DBG(ISDN_DBG, "HFC-S USB: T4 deactivated");
+#endif
+	}
+
+	if (state == 7 && !hfc->l1_activated) {
+		hfc->d_if.ifc.l1l2(&hfc->d_if.ifc,
+				   PH_ACTIVATE | INDICATION, NULL);
+#ifdef CONFIG_HISAX_DEBUG
+		DBG(ISDN_DBG, "HFC-S USB: PH_ACTIVATE | INDICATION sent");
+#endif
+		hfc->l1_activated = TRUE;
+		handle_led(hfc, LED_S0_ON);
+	} else if (state <= 3 /* && activated */ ) {
+		if (old_state == 7 || old_state == 8) {
+#ifdef CONFIG_HISAX_DEBUG
+			DBG(ISDN_DBG, "HFC-S USB: T4 activated");
+#endif
+			if (!timer_pending(&hfc->t4_timer)) {
+				hfc->t4_timer.expires =
+				    jiffies + (HFC_TIMER_T4 * HZ) / 1000;
+				add_timer(&hfc->t4_timer);
+			}
+		} else {
+			hfc->d_if.ifc.l1l2(&hfc->d_if.ifc,
+					   PH_DEACTIVATE | INDICATION,
+					   NULL);
+#ifdef CONFIG_HISAX_DEBUG
+			DBG(ISDN_DBG,
+			    "HFC-S USB: PH_DEACTIVATE | INDICATION sent");
+#endif
+			hfc->l1_activated = FALSE;
+			handle_led(hfc, LED_S0_OFF);
+		}
+	}
+	hfc->l1_state = state;
+}
+
+/* prepare iso urb */
+static void
+fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,
+	      void *buf, int num_packets, int packet_size, int interval,
+	      usb_complete_t complete, void *context)
+{
+	int k;
+
+	spin_lock_init(&urb->lock);
+	urb->dev = dev;
+	urb->pipe = pipe;
+	urb->complete = complete;
+	urb->number_of_packets = num_packets;
+	urb->transfer_buffer_length = packet_size * num_packets;
+	urb->context = context;
+	urb->transfer_buffer = buf;
+	urb->transfer_flags = URB_ISO_ASAP;
+	urb->actual_length = 0;
+	urb->interval = interval;
+	for (k = 0; k < num_packets; k++) {
+		urb->iso_frame_desc[k].offset = packet_size * k;
+		urb->iso_frame_desc[k].length = packet_size;
+		urb->iso_frame_desc[k].actual_length = 0;
+	}
+}
+
+/* allocs urbs and start isoc transfer with two pending urbs to avoid
+   gaps in the transfer chain */
+static int
+start_isoc_chain(usb_fifo * fifo, int num_packets_per_urb,
+		 usb_complete_t complete, int packet_size)
+{
+	int i, k, errcode;
+
+	printk(KERN_INFO "HFC-S USB: starting ISO-chain for Fifo %i\n",
+	       fifo->fifonum);
+
+	/* allocate Memory for Iso out Urbs */
+	for (i = 0; i < 2; i++) {
+		if (!(fifo->iso[i].purb)) {
+			fifo->iso[i].purb =
+			    usb_alloc_urb(num_packets_per_urb, GFP_KERNEL);
+			if (!(fifo->iso[i].purb)) {
+				printk(KERN_INFO
+				       "alloc urb for fifo %i failed!!!",
+				       fifo->fifonum);
+			}
+			fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo;
+
+			/* Init the first iso */
+			if (ISO_BUFFER_SIZE >=
+			    (fifo->usb_packet_maxlen *
+			     num_packets_per_urb)) {
+				fill_isoc_urb(fifo->iso[i].purb,
+					      fifo->hfc->dev, fifo->pipe,
+					      fifo->iso[i].buffer,
+					      num_packets_per_urb,
+					      fifo->usb_packet_maxlen,
+					      fifo->intervall, complete,
+					      &fifo->iso[i]);
+				memset(fifo->iso[i].buffer, 0,
+				       sizeof(fifo->iso[i].buffer));
+				/* defining packet delimeters in fifo->buffer */
+				for (k = 0; k < num_packets_per_urb; k++) {
+					fifo->iso[i].purb->
+					    iso_frame_desc[k].offset =
+					    k * packet_size;
+					fifo->iso[i].purb->
+					    iso_frame_desc[k].length =
+					    packet_size;
+				}
+			} else {
+				printk(KERN_INFO
+				       "HFC-S USB: ISO Buffer size to small!\n");
+			}
+		}
+		fifo->bit_line = BITLINE_INF;
+
+		errcode = usb_submit_urb(fifo->iso[i].purb, GFP_KERNEL);
+		fifo->active = (errcode >= 0) ? 1 : 0;
+		if (errcode < 0) {
+			printk(KERN_INFO "HFC-S USB: %s  URB nr:%d\n",
+			       symbolic(urb_errlist, errcode), i);
+		};
+	}
+	return (fifo->active);
+}
+
+/* stops running iso chain and frees their pending urbs */
+static void
+stop_isoc_chain(usb_fifo * fifo)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		if (fifo->iso[i].purb) {
+#ifdef CONFIG_HISAX_DEBUG
+			DBG(USB_DBG,
+			    "HFC-S USB: Stopping iso chain for fifo %i.%i",
+			    fifo->fifonum, i);
+#endif
+			usb_unlink_urb(fifo->iso[i].purb);
+			usb_free_urb(fifo->iso[i].purb);
+			fifo->iso[i].purb = NULL;
+		}
+	}
+	if (fifo->urb) {
+		usb_unlink_urb(fifo->urb);
+		usb_free_urb(fifo->urb);
+		fifo->urb = NULL;
+	}
+	fifo->active = 0;
+}
+
+/* defines how much ISO packets are handled in one URB */
+static int iso_packets[8] =
+    { ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B,
+	ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D
+};
+
+/*****************************************************/
+/* transmit completion routine for all ISO tx fifos */
+/*****************************************************/
+static void
+tx_iso_complete(struct urb *urb, struct pt_regs *regs)
+{
+	iso_urb_struct *context_iso_urb = (iso_urb_struct *) urb->context;
+	usb_fifo *fifo = context_iso_urb->owner_fifo;
+	hfcusb_data *hfc = fifo->hfc;
+	int k, tx_offset, num_isoc_packets, sink, len, current_len,
+	    errcode;
+	int frame_complete, transp_mode, fifon, status;
+	__u8 threshbit;
+	__u8 threshtable[8] = { 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80 };
+
+	fifon = fifo->fifonum;
+	status = urb->status;
+
+	tx_offset = 0;
+
+	if (fifo->active && !status) {
+		transp_mode = 0;
+		if (fifon < 4 && hfc->b_mode[fifon / 2] == L1_MODE_TRANS)
+			transp_mode = TRUE;
+
+		/* is FifoFull-threshold set for our channel? */
+		threshbit = threshtable[fifon] & hfc->threshold_mask;
+		num_isoc_packets = iso_packets[fifon];
+
+		/* predict dataflow to avoid fifo overflow */
+		if (fifon >= HFCUSB_D_TX) {
+			sink = (threshbit) ? SINK_DMIN : SINK_DMAX;
+		} else {
+			sink = (threshbit) ? SINK_MIN : SINK_MAX;
+		}
+		fill_isoc_urb(urb, fifo->hfc->dev, fifo->pipe,
+			      context_iso_urb->buffer, num_isoc_packets,
+			      fifo->usb_packet_maxlen, fifo->intervall,
+			      tx_iso_complete, urb->context);
+		memset(context_iso_urb->buffer, 0,
+		       sizeof(context_iso_urb->buffer));
+		frame_complete = FALSE;
+		/* Generate next Iso Packets */
+		for (k = 0; k < num_isoc_packets; ++k) {
+			if (fifo->skbuff) {
+				len = fifo->skbuff->len;
+				/* we lower data margin every msec */
+				fifo->bit_line -= sink;
+				current_len = (0 - fifo->bit_line) / 8;
+				/* maximum 15 byte for every ISO packet makes our life easier */
+				if (current_len > 14)
+					current_len = 14;
+				current_len =
+				    (len <=
+				     current_len) ? len : current_len;
+				/* how much bit do we put on the line? */
+				fifo->bit_line += current_len * 8;
+
+				context_iso_urb->buffer[tx_offset] = 0;
+				if (current_len == len) {
+					if (!transp_mode) {
+						/* here frame completion */
+						context_iso_urb->
+						    buffer[tx_offset] = 1;
+						/* add 2 byte flags and 16bit CRC at end of ISDN frame */
+						fifo->bit_line += 32;
+					}
+					frame_complete = TRUE;
+				}
+
+				memcpy(context_iso_urb->buffer +
+				       tx_offset + 1, fifo->skbuff->data,
+				       current_len);
+				skb_pull(fifo->skbuff, current_len);
+
+				/* define packet delimeters within the URB buffer */
+				urb->iso_frame_desc[k].offset = tx_offset;
+				urb->iso_frame_desc[k].length =
+				    current_len + 1;
+
+				tx_offset += (current_len + 1);
+				if (!transp_mode) {
+					if (fifon == HFCUSB_B1_TX)
+						handle_led(hfc,
+							   LED_B1_DATA);
+					if (fifon == HFCUSB_B2_TX)
+						handle_led(hfc,
+							   LED_B2_DATA);
+				}
+			} else {
+				urb->iso_frame_desc[k].offset =
+				    tx_offset++;
+
+				urb->iso_frame_desc[k].length = 1;
+				fifo->bit_line -= sink;	/* we lower data margin every msec */
+
+				if (fifo->bit_line < BITLINE_INF) {
+					fifo->bit_line = BITLINE_INF;
+				}
+			}
+
+			if (frame_complete) {
+				fifo->delete_flg = TRUE;
+				fifo->hif->l1l2(fifo->hif,
+						PH_DATA | CONFIRM,
+						(void *) fifo->skbuff->
+						truesize);
+				if (fifo->skbuff && fifo->delete_flg) {
+					dev_kfree_skb_any(fifo->skbuff);
+					fifo->skbuff = NULL;
+					fifo->delete_flg = FALSE;
+				}
+				frame_complete = FALSE;
+			}
+		}
+		errcode = usb_submit_urb(urb, GFP_ATOMIC);
+		if (errcode < 0) {
+			printk(KERN_INFO
+			       "HFC-S USB: error submitting ISO URB: %d \n",
+			       errcode);
+		}
+	} else {
+		if (status && !hfc->disc_flag) {
+			printk(KERN_INFO
+			       "HFC-S USB: tx_iso_complete : urb->status %s (%i), fifonum=%d\n",
+			       symbolic(urb_errlist, status), status,
+			       fifon);
+		}
+	}
+}				/* tx_iso_complete */
+
+/*****************************************************/
+/* receive completion routine for all ISO tx fifos   */
+/*****************************************************/
+static void
+rx_iso_complete(struct urb *urb, struct pt_regs *regs)
+{
+	iso_urb_struct *context_iso_urb = (iso_urb_struct *) urb->context;
+	usb_fifo *fifo = context_iso_urb->owner_fifo;
+	hfcusb_data *hfc = fifo->hfc;
+	int k, len, errcode, offset, num_isoc_packets, fifon, maxlen,
+	    status;
+	unsigned int iso_status;
+	__u8 *buf;
+	static __u8 eof[8];
+#ifdef CONFIG_HISAX_DEBUG
+	__u8 i;
+#endif
+
+	fifon = fifo->fifonum;
+	status = urb->status;
+
+	if (urb->status == -EOVERFLOW) {
+#ifdef CONFIG_HISAX_DEBUG
+		DBG(USB_DBG,
+		    "HFC-USB: ignoring USB DATAOVERRUN  for fifo  %i \n",
+		    fifon);
+#endif
+		status = 0;
+	}
+	if (fifo->active && !status) {
+		num_isoc_packets = iso_packets[fifon];
+		maxlen = fifo->usb_packet_maxlen;
+		for (k = 0; k < num_isoc_packets; ++k) {
+			len = urb->iso_frame_desc[k].actual_length;
+			offset = urb->iso_frame_desc[k].offset;
+			buf = context_iso_urb->buffer + offset;
+			iso_status = urb->iso_frame_desc[k].status;
+#ifdef CONFIG_HISAX_DEBUG
+			if (iso_status && !hfc->disc_flag)
+				DBG(USB_DBG,
+				    "HFC-S USB: ISO packet failure - status:%x",
+				    iso_status);
+
+			if ((fifon == 5) && (debug > 1)) {
+				printk(KERN_INFO
+				       "HFC-S USB: ISO-D-RX lst_urblen:%2d "
+				       "act_urblen:%2d max-urblen:%2d "
+				       "EOF:0x%0x DATA: ",
+				       fifo->last_urblen, len, maxlen,
+				       eof[5]);
+				for (i = 0; i < len; i++)
+					printk("%.2x ", buf[i]);
+				printk("\n");
+			}
+#endif
+			if (fifo->last_urblen != maxlen) {
+				/* the threshold mask is in the 2nd status byte */
+				hfc->threshold_mask = buf[1];
+				/* care for L1 state only for D-Channel
+				   to avoid overlapped iso completions */
+				if (fifon == 5) {
+					/* the S0 state is in the upper half
+					   of the 1st status byte */
+					state_handler(hfc, buf[0] >> 4);
+				}
+				eof[fifon] = buf[0] & 1;
+				if (len > 2)
+					collect_rx_frame(fifo, buf + 2,
+							 len - 2,
+							 (len <
+							  maxlen) ?
+							 eof[fifon] : 0);
+			} else {
+				collect_rx_frame(fifo, buf, len,
+						 (len <
+						  maxlen) ? eof[fifon] :
+						 0);
+			}
+			fifo->last_urblen = len;
+		}
+
+		fill_isoc_urb(urb, fifo->hfc->dev, fifo->pipe,
+			      context_iso_urb->buffer, num_isoc_packets,
+			      fifo->usb_packet_maxlen, fifo->intervall,
+			      rx_iso_complete, urb->context);
+		errcode = usb_submit_urb(urb, GFP_ATOMIC);
+		if (errcode < 0) {
+			printk(KERN_INFO
+			       "HFC-S USB: error submitting ISO URB: %d \n",
+			       errcode);
+		}
+	} else {
+		if (status && !hfc->disc_flag) {
+			printk(KERN_INFO
+			       "HFC-S USB: rx_iso_complete : "
+			       "urb->status %d, fifonum %d\n",
+			       status, fifon);
+		}
+	}
+}				/* rx_iso_complete */
+
+/*****************************************************/
+/* collect data from interrupt or isochron in        */
+/*****************************************************/
+static void
+collect_rx_frame(usb_fifo * fifo, __u8 * data, int len, int finish)
+{
+	hfcusb_data *hfc = fifo->hfc;
+	int transp_mode, fifon;
+#ifdef CONFIG_HISAX_DEBUG
+	int i;
+#endif
+	fifon = fifo->fifonum;
+	transp_mode = 0;
+	if (fifon < 4 && hfc->b_mode[fifon / 2] == L1_MODE_TRANS)
+		transp_mode = TRUE;
+
+	if (!fifo->skbuff) {
+		fifo->skbuff = dev_alloc_skb(fifo->max_size + 3);
+		if (!fifo->skbuff) {
+			printk(KERN_INFO
+			       "HFC-S USB: cannot allocate buffer (dev_alloc_skb) fifo:%d\n",
+			       fifon);
+			return;
+		}
+	}
+	if (len) {
+		if (fifo->skbuff->len + len < fifo->max_size) {
+			memcpy(skb_put(fifo->skbuff, len), data, len);
+		} else {
+#ifdef CONFIG_HISAX_DEBUG
+			printk(KERN_INFO "HFC-S USB: ");
+			for (i = 0; i < 15; i++)
+				printk("%.2x ",
+				       fifo->skbuff->data[fifo->skbuff->
+							  len - 15 + i]);
+			printk("\n");
+#endif
+			printk(KERN_INFO
+			       "HCF-USB: got frame exceeded fifo->max_size:%d on fifo:%d\n",
+			       fifo->max_size, fifon);
+		}
+	}
+	if (transp_mode && fifo->skbuff->len >= 128) {
+		fifo->hif->l1l2(fifo->hif, PH_DATA | INDICATION,
+				fifo->skbuff);
+		fifo->skbuff = NULL;
+		return;
+	}
+	/* we have a complete hdlc packet */
+	if (finish) {
+		if ((!fifo->skbuff->data[fifo->skbuff->len - 1])
+		    && (fifo->skbuff->len > 3)) {
+			/* remove CRC & status */
+			skb_trim(fifo->skbuff, fifo->skbuff->len - 3);
+			if (fifon == HFCUSB_PCM_RX) {
+				fifo->hif->l1l2(fifo->hif,
+						PH_DATA_E | INDICATION,
+						fifo->skbuff);
+			} else
+				fifo->hif->l1l2(fifo->hif,
+						PH_DATA | INDICATION,
+						fifo->skbuff);
+			fifo->skbuff = NULL;	/* buffer was freed from upper layer */
+		} else {
+			if (fifo->skbuff->len > 3) {
+				printk(KERN_INFO
+				       "HFC-S USB: got frame %d bytes but CRC ERROR on fifo:%d!!!\n",
+				       fifo->skbuff->len, fifon);
+#ifdef CONFIG_HISAX_DEBUG
+				if (debug > 1) {
+					printk(KERN_INFO "HFC-S USB: ");
+					for (i = 0; i < 15; i++)
+						printk("%.2x ",
+						       fifo->skbuff->
+						       data[fifo->skbuff->
+							    len - 15 + i]);
+					printk("\n");
+				}
+#endif
+			}
+#ifdef CONFIG_HISAX_DEBUG
+			else {
+				printk(KERN_INFO
+				       "HFC-S USB: frame to small (%d bytes)!!!\n",
+				       fifo->skbuff->len);
+			}
+#endif
+			skb_trim(fifo->skbuff, 0);
+		}
+	}
+
+	/* LED flashing only in HDLC mode */
+	if (!transp_mode) {
+		if (fifon == HFCUSB_B1_RX)
+			handle_led(hfc, LED_B1_DATA);
+		if (fifon == HFCUSB_B2_RX)
+			handle_led(hfc, LED_B2_DATA);
+	}
+}
+
+/***********************************************/
+/* receive completion routine for all rx fifos */
+/***********************************************/
+static void
+rx_complete(struct urb *urb, struct pt_regs *regs)
+{
+	int len;
+	int status;
+	__u8 *buf, maxlen, fifon;
+	usb_fifo *fifo = (usb_fifo *) urb->context;
+	hfcusb_data *hfc = fifo->hfc;
+	static __u8 eof[8];
+#ifdef CONFIG_HISAX_DEBUG
+	__u8 i;
+#endif
+
+	urb->dev = hfc->dev;	/* security init */
+
+	fifon = fifo->fifonum;
+	if ((!fifo->active) || (urb->status)) {
+#ifdef CONFIG_HISAX_DEBUG
+		DBG(USB_DBG, "HFC-S USB: RX-Fifo %i is going down (%i)",
+		    fifon, urb->status);
+#endif
+		fifo->urb->interval = 0;	/* cancel automatic rescheduling */
+		if (fifo->skbuff) {
+			dev_kfree_skb_any(fifo->skbuff);
+			fifo->skbuff = NULL;
+		}
+		return;
+	}
+	len = urb->actual_length;
+	buf = fifo->buffer;
+	maxlen = fifo->usb_packet_maxlen;
+
+#ifdef CONFIG_HISAX_DEBUG
+	if ((fifon == 5) && (debug > 1)) {
+		printk(KERN_INFO
+		       "HFC-S USB: INT-D-RX lst_urblen:%2d act_urblen:%2d max-urblen:%2d EOF:0x%0x DATA: ",
+		       fifo->last_urblen, len, maxlen, eof[5]);
+		for (i = 0; i < len; i++)
+			printk("%.2x ", buf[i]);
+		printk("\n");
+	}
+#endif
+
+	if (fifo->last_urblen != fifo->usb_packet_maxlen) {
+		/* the threshold mask is in the 2nd status byte */
+		hfc->threshold_mask = buf[1];
+		/* the S0 state is in the upper half of the 1st status byte */
+		state_handler(hfc, buf[0] >> 4);
+		eof[fifon] = buf[0] & 1;
+		/* if we have more than the 2 status bytes -> collect data */
+		if (len > 2)
+			collect_rx_frame(fifo, buf + 2,
+					 urb->actual_length - 2,
+					 (len < maxlen) ? eof[fifon] : 0);
+	} else {
+		collect_rx_frame(fifo, buf, urb->actual_length,
+				 (len < maxlen) ? eof[fifon] : 0);
+	}
+	fifo->last_urblen = urb->actual_length;
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status) {
+		printk(KERN_INFO
+		       "HFC-S USB: error resubmitting URN at rx_complete...\n");
+	}
+}				/* rx_complete */
+
+/***************************************************/
+/* start the interrupt transfer for the given fifo */
+/***************************************************/
+static void
+start_int_fifo(usb_fifo * fifo)
+{
+	int errcode;
+
+	printk(KERN_INFO "HFC-S USB: starting intr IN fifo:%d\n",
+	       fifo->fifonum);
+
+	if (!fifo->urb) {
+		fifo->urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!fifo->urb)
+			return;
+	}
+	usb_fill_int_urb(fifo->urb, fifo->hfc->dev, fifo->pipe,
+			 fifo->buffer, fifo->usb_packet_maxlen,
+			 rx_complete, fifo, fifo->intervall);
+	fifo->active = 1;	/* must be marked active */
+	errcode = usb_submit_urb(fifo->urb, GFP_KERNEL);
+	if (errcode) {
+		printk(KERN_INFO
+		       "HFC-S USB: submit URB error(start_int_info): status:%i\n",
+		       errcode);
+		fifo->active = 0;
+		fifo->skbuff = NULL;
+	}
+}				/* start_int_fifo */
+
+/*****************************/
+/* set the B-channel mode    */
+/*****************************/
+static void
+set_hfcmode(hfcusb_data * hfc, int channel, int mode)
+{
+	__u8 val, idx_table[2] = { 0, 2 };
+
+	if (hfc->disc_flag) {
+		return;
+	}
+#ifdef CONFIG_HISAX_DEBUG
+	DBG(ISDN_DBG, "HFC-S USB: setting channel %d to mode %d", channel,
+	    mode);
+#endif
+	hfc->b_mode[channel] = mode;
+
+	/* setup CON_HDLC */
+	val = 0;
+	if (mode != L1_MODE_NULL)
+		val = 8;	/* enable fifo? */
+	if (mode == L1_MODE_TRANS)
+		val |= 2;	/* set transparent bit */
+
+	/* set FIFO to transmit register */
+	queue_control_request(hfc, HFCUSB_FIFO, idx_table[channel], 1);
+	queue_control_request(hfc, HFCUSB_CON_HDLC, val, 1);
+	/* reset fifo */
+	queue_control_request(hfc, HFCUSB_INC_RES_F, 2, 1);
+	/* set FIFO to receive register */
+	queue_control_request(hfc, HFCUSB_FIFO, idx_table[channel] + 1, 1);
+	queue_control_request(hfc, HFCUSB_CON_HDLC, val, 1);
+	/* reset fifo */
+	queue_control_request(hfc, HFCUSB_INC_RES_F, 2, 1);
+
+	val = 0x40;
+	if (hfc->b_mode[0])
+		val |= 1;
+	if (hfc->b_mode[1])
+		val |= 2;
+	queue_control_request(hfc, HFCUSB_SCTRL, val, 1);
+
+	val = 0;
+	if (hfc->b_mode[0])
+		val |= 1;
+	if (hfc->b_mode[1])
+		val |= 2;
+	queue_control_request(hfc, HFCUSB_SCTRL_R, val, 1);
+
+	if (mode == L1_MODE_NULL) {
+		if (channel)
+			handle_led(hfc, LED_B2_OFF);
+		else
+			handle_led(hfc, LED_B1_OFF);
+	} else {
+		if (channel)
+			handle_led(hfc, LED_B2_ON);
+		else
+			handle_led(hfc, LED_B1_ON);
+	}
+}
+
+void
+hfc_usb_l2l1(struct hisax_if *my_hisax_if, int pr, void *arg)
+{
+	usb_fifo *fifo = my_hisax_if->priv;
+	hfcusb_data *hfc = fifo->hfc;
+
+	switch (pr) {
+		case PH_ACTIVATE | REQUEST:
+			if (fifo->fifonum == HFCUSB_D_TX) {
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(ISDN_DBG,
+				    "HFC_USB: hfc_usb_d_l2l1 D-chan: PH_ACTIVATE | REQUEST");
+#endif
+				if (hfc->l1_state != 3
+				    && hfc->l1_state != 7) {
+					hfc->d_if.ifc.l1l2(&hfc->d_if.ifc,
+							   PH_DEACTIVATE |
+							   INDICATION,
+							   NULL);
+#ifdef CONFIG_HISAX_DEBUG
+					DBG(ISDN_DBG,
+					    "HFC-S USB: PH_DEACTIVATE | INDICATION sent (not state 3 or 7)");
+#endif
+				} else {
+					if (hfc->l1_state == 7) {	/* l1 already active */
+						hfc->d_if.ifc.l1l2(&hfc->
+								   d_if.
+								   ifc,
+								   PH_ACTIVATE
+								   |
+								   INDICATION,
+								   NULL);
+#ifdef CONFIG_HISAX_DEBUG
+						DBG(ISDN_DBG,
+						    "HFC-S USB: PH_ACTIVATE | INDICATION sent again ;)");
+#endif
+					} else {
+						/* force sending sending INFO1 */
+						queue_control_request(hfc,
+								      HFCUSB_STATES,
+								      0x14,
+								      1);
+						mdelay(1);
+						/* start l1 activation */
+						queue_control_request(hfc,
+								      HFCUSB_STATES,
+								      0x04,
+								      1);
+						if (!timer_pending
+						    (&hfc->t3_timer)) {
+							hfc->t3_timer.
+							    expires =
+							    jiffies +
+							    (HFC_TIMER_T3 *
+							     HZ) / 1000;
+							add_timer(&hfc->
+								  t3_timer);
+						}
+					}
+				}
+			} else {
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(ISDN_DBG,
+				    "HFC_USB: hfc_usb_d_l2l1 Bx-chan: PH_ACTIVATE | REQUEST");
+#endif
+				set_hfcmode(hfc,
+					    (fifo->fifonum ==
+					     HFCUSB_B1_TX) ? 0 : 1,
+					    (int) arg);
+				fifo->hif->l1l2(fifo->hif,
+						PH_ACTIVATE | INDICATION,
+						NULL);
+			}
+			break;
+		case PH_DEACTIVATE | REQUEST:
+			if (fifo->fifonum == HFCUSB_D_TX) {
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(ISDN_DBG,
+				    "HFC_USB: hfc_usb_d_l2l1 D-chan: PH_DEACTIVATE | REQUEST");
+#endif
+				printk(KERN_INFO
+				       "HFC-S USB: ISDN TE device should not deativate...\n");
+			} else {
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(ISDN_DBG,
+				    "HFC_USB: hfc_usb_d_l2l1 Bx-chan: PH_DEACTIVATE | REQUEST");
+#endif
+				set_hfcmode(hfc,
+					    (fifo->fifonum ==
+					     HFCUSB_B1_TX) ? 0 : 1,
+					    (int) L1_MODE_NULL);
+				fifo->hif->l1l2(fifo->hif,
+						PH_DEACTIVATE | INDICATION,
+						NULL);
+			}
+			break;
+		case PH_DATA | REQUEST:
+			if (fifo->skbuff && fifo->delete_flg) {
+				dev_kfree_skb_any(fifo->skbuff);
+				fifo->skbuff = NULL;
+				fifo->delete_flg = FALSE;
+			}
+			fifo->skbuff = arg;	/* we have a new buffer */
+			break;
+		default:
+			printk(KERN_INFO
+			       "HFC_USB: hfc_usb_d_l2l1: unkown state : %#x\n",
+			       pr);
+			break;
+	}
+}
+
+/***************************************************************************/
+/* usb_init is called once when a new matching device is detected to setup */
+/* main parameters. It registers the driver at the main hisax module.      */
+/* on success 0 is returned.                                               */
+/***************************************************************************/
+static int
+usb_init(hfcusb_data * hfc)
+{
+	usb_fifo *fifo;
+	int i, err;
+	u_char b;
+	struct hisax_b_if *p_b_if[2];
+
+	/* check the chip id */
+	if (read_usb(hfc, HFCUSB_CHIP_ID, &b) != 1) {
+		printk(KERN_INFO "HFC-USB: cannot read chip id\n");
+		return (1);
+	}
+	if (b != HFCUSB_CHIPID) {
+		printk(KERN_INFO "HFC-S USB: Invalid chip id 0x%02x\n", b);
+		return (1);
+	}
+
+	/* first set the needed config, interface and alternate */
+	err = usb_set_interface(hfc->dev, hfc->if_used, hfc->alt_used);
+
+	/* do Chip reset */
+	write_usb(hfc, HFCUSB_CIRM, 8);
+	/* aux = output, reset off */
+	write_usb(hfc, HFCUSB_CIRM, 0x10);
+
+	/* set USB_SIZE to match the the wMaxPacketSize for INT or BULK transfers */
+	write_usb(hfc, HFCUSB_USB_SIZE,
+		  (hfc->packet_size / 8) | ((hfc->packet_size / 8) << 4));
+
+	/* set USB_SIZE_I to match the the wMaxPacketSize for ISO transfers */
+	write_usb(hfc, HFCUSB_USB_SIZE_I, hfc->iso_packet_size);
+
+	/* enable PCM/GCI master mode */
+	write_usb(hfc, HFCUSB_MST_MODE1, 0);	/* set default values */
+	write_usb(hfc, HFCUSB_MST_MODE0, 1);	/* enable master mode */
+
+	/* init the fifos */
+	write_usb(hfc, HFCUSB_F_THRES,
+		  (HFCUSB_TX_THRESHOLD /
+		   8) | ((HFCUSB_RX_THRESHOLD / 8) << 4));
+
+	fifo = hfc->fifos;
+	for (i = 0; i < HFCUSB_NUM_FIFOS; i++) {
+		write_usb(hfc, HFCUSB_FIFO, i);	/* select the desired fifo */
+		fifo[i].skbuff = NULL;	/* init buffer pointer */
+		fifo[i].max_size =
+		    (i <= HFCUSB_B2_RX) ? MAX_BCH_SIZE : MAX_DFRAME_LEN;
+		fifo[i].last_urblen = 0;
+		/* set 2 bit for D- & E-channel */
+		write_usb(hfc, HFCUSB_HDLC_PAR,
+			  ((i <= HFCUSB_B2_RX) ? 0 : 2));
+		/* rx hdlc, enable IFF for D-channel */
+		write_usb(hfc, HFCUSB_CON_HDLC,
+			  ((i == HFCUSB_D_TX) ? 0x09 : 0x08));
+		write_usb(hfc, HFCUSB_INC_RES_F, 2);	/* reset the fifo */
+	}
+
+	write_usb(hfc, HFCUSB_CLKDEL, 0x0f);	/* clock delay value */
+	write_usb(hfc, HFCUSB_STATES, 3 | 0x10);	/* set deactivated mode */
+	write_usb(hfc, HFCUSB_STATES, 3);	/* enable state machine */
+
+	write_usb(hfc, HFCUSB_SCTRL_R, 0);	/* disable both B receivers */
+	write_usb(hfc, HFCUSB_SCTRL, 0x40);	/* disable B transmitters + capacitive mode */
+
+	/* set both B-channel to not connected */
+	hfc->b_mode[0] = L1_MODE_NULL;
+	hfc->b_mode[1] = L1_MODE_NULL;
+
+	hfc->l1_activated = FALSE;
+	hfc->disc_flag = FALSE;
+	hfc->led_state = 0;
+	hfc->led_new_data = 0;
+	hfc->old_led_state = 0;
+
+	/* init the t3 timer */
+	init_timer(&hfc->t3_timer);
+	hfc->t3_timer.data = (long) hfc;
+	hfc->t3_timer.function = (void *) l1_timer_expire_t3;
+
+	/* init the t4 timer */
+	init_timer(&hfc->t4_timer);
+	hfc->t4_timer.data = (long) hfc;
+	hfc->t4_timer.function = (void *) l1_timer_expire_t4;
+
+	/* init the led timer */
+	init_timer(&hfc->led_timer);
+	hfc->led_timer.data = (long) hfc;
+	hfc->led_timer.function = (void *) led_timer;
+
+	/* trigger 4 hz led timer */
+	if (!timer_pending(&hfc->led_timer)) {
+		hfc->led_timer.expires = jiffies + (LED_TIME * HZ) / 1000;
+		add_timer(&hfc->led_timer);
+	}
+
+	/* init the background machinery for control requests */
+	hfc->ctrl_read.bRequestType = 0xc0;
+	hfc->ctrl_read.bRequest = 1;
+	hfc->ctrl_read.wLength = 1;
+	hfc->ctrl_write.bRequestType = 0x40;
+	hfc->ctrl_write.bRequest = 0;
+	hfc->ctrl_write.wLength = 0;
+	usb_fill_control_urb(hfc->ctrl_urb,
+			     hfc->dev,
+			     hfc->ctrl_out_pipe,
+			     (u_char *) & hfc->ctrl_write,
+			     NULL, 0, ctrl_complete, hfc);
+	/* Init All Fifos */
+	for (i = 0; i < HFCUSB_NUM_FIFOS; i++) {
+		hfc->fifos[i].iso[0].purb = NULL;
+		hfc->fifos[i].iso[1].purb = NULL;
+		hfc->fifos[i].active = 0;
+	}
+	/* register Modul to upper Hisax Layers */
+	hfc->d_if.owner = THIS_MODULE;
+	hfc->d_if.ifc.priv = &hfc->fifos[HFCUSB_D_TX];
+	hfc->d_if.ifc.l2l1 = hfc_usb_l2l1;
+	for (i = 0; i < 2; i++) {
+		hfc->b_if[i].ifc.priv = &hfc->fifos[HFCUSB_B1_TX + i * 2];
+		hfc->b_if[i].ifc.l2l1 = hfc_usb_l2l1;
+		p_b_if[i] = &hfc->b_if[i];
+	}
+	/* default Prot: EURO ISDN, should be a module_param */
+	hfc->protocol = 2;
+	hisax_register(&hfc->d_if, p_b_if, "hfc_usb", hfc->protocol);
+
+#ifdef CONFIG_HISAX_DEBUG
+	hfc_debug = debug;
+#endif
+
+	for (i = 0; i < 4; i++)
+		hfc->fifos[i].hif = &p_b_if[i / 2]->ifc;
+	for (i = 4; i < 8; i++)
+		hfc->fifos[i].hif = &hfc->d_if.ifc;
+
+	/* 3 (+1) INT IN + 3 ISO OUT */
+	if (hfc->cfg_used == CNF_3INT3ISO || hfc->cfg_used == CNF_4INT3ISO) {
+		start_int_fifo(hfc->fifos + HFCUSB_D_RX);
+		if (hfc->fifos[HFCUSB_PCM_RX].pipe)
+			start_int_fifo(hfc->fifos + HFCUSB_PCM_RX);
+		start_int_fifo(hfc->fifos + HFCUSB_B1_RX);
+		start_int_fifo(hfc->fifos + HFCUSB_B2_RX);
+	}
+	/* 3 (+1) ISO IN + 3 ISO OUT */
+	if (hfc->cfg_used == CNF_3ISO3ISO || hfc->cfg_used == CNF_4ISO3ISO) {
+		start_isoc_chain(hfc->fifos + HFCUSB_D_RX, ISOC_PACKETS_D,
+				 rx_iso_complete, 16);
+		if (hfc->fifos[HFCUSB_PCM_RX].pipe)
+			start_isoc_chain(hfc->fifos + HFCUSB_PCM_RX,
+					 ISOC_PACKETS_D, rx_iso_complete,
+					 16);
+		start_isoc_chain(hfc->fifos + HFCUSB_B1_RX, ISOC_PACKETS_B,
+				 rx_iso_complete, 16);
+		start_isoc_chain(hfc->fifos + HFCUSB_B2_RX, ISOC_PACKETS_B,
+				 rx_iso_complete, 16);
+	}
+
+	start_isoc_chain(hfc->fifos + HFCUSB_D_TX, ISOC_PACKETS_D,
+			 tx_iso_complete, 1);
+	start_isoc_chain(hfc->fifos + HFCUSB_B1_TX, ISOC_PACKETS_B,
+			 tx_iso_complete, 1);
+	start_isoc_chain(hfc->fifos + HFCUSB_B2_TX, ISOC_PACKETS_B,
+			 tx_iso_complete, 1);
+
+	handle_led(hfc, LED_POWER_ON);
+
+	return (0);
+}				/* usb_init */
+
+/*************************************************/
+/* function called to probe a new plugged device */
+/*************************************************/
+static int
+hfc_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	hfcusb_data *context;
+	struct usb_host_interface *iface = intf->cur_altsetting;
+	struct usb_host_interface *iface_used = NULL;
+	struct usb_host_endpoint *ep;
+	int ifnum = iface->desc.bInterfaceNumber;
+	int i, idx, alt_idx, probe_alt_setting, vend_idx, cfg_used, *vcf,
+	    attr, cfg_found, cidx, ep_addr;
+	int cmptbl[16], small_match, iso_packet_size, packet_size,
+	    alt_used = 0;
+
+	vend_idx = 0xffff;
+	for (i = 0; vdata[i].vendor; i++) {
+		if (dev->descriptor.idVendor == vdata[i].vendor
+		    && dev->descriptor.idProduct == vdata[i].prod_id)
+			vend_idx = i;
+	}
+#ifdef CONFIG_HISAX_DEBUG
+	DBG(USB_DBG,
+	    "HFC-USB: probing interface(%d) actalt(%d) minor(%d)\n", ifnum,
+	    iface->desc.bAlternateSetting, intf->minor);
+#endif
+	printk(KERN_INFO
+	       "HFC-S USB: probing interface(%d) actalt(%d) minor(%d)\n",
+	       ifnum, iface->desc.bAlternateSetting, intf->minor);
+
+	if (vend_idx != 0xffff) {
+#ifdef CONFIG_HISAX_DEBUG
+		DBG(USB_DBG, "HFC-S USB: found vendor idx:%d  name:%s",
+		    vend_idx, vdata[vend_idx].vend_name);
+#endif
+		/* if vendor and product ID is OK, start probing alternate settings */
+		alt_idx = 0;
+		small_match = 0xffff;
+
+		/* default settings */
+		iso_packet_size = 16;
+		packet_size = 64;
+
+		while (alt_idx < intf->num_altsetting) {
+			iface = intf->altsetting + alt_idx;
+			probe_alt_setting = iface->desc.bAlternateSetting;
+			cfg_used = 0;
+
+			/* check for config EOL element */
+			while (validconf[cfg_used][0]) {
+				cfg_found = TRUE;
+				vcf = validconf[cfg_used];
+				/* first endpoint descriptor */
+				ep = iface->endpoint;
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(USB_DBG,
+				    "HFC-S USB: (if=%d alt=%d cfg_used=%d)\n",
+				    ifnum, probe_alt_setting, cfg_used);
+#endif
+				memcpy(cmptbl, vcf, 16 * sizeof(int));
+
+				/* check for all endpoints in this alternate setting */
+				for (i = 0; i < iface->desc.bNumEndpoints;
+				     i++) {
+					ep_addr =
+					    ep->desc.bEndpointAddress;
+					/* get endpoint base */
+					idx = ((ep_addr & 0x7f) - 1) * 2;
+					if (ep_addr & 0x80)
+						idx++;
+					attr = ep->desc.bmAttributes;
+					if (cmptbl[idx] == EP_NUL) {
+						cfg_found = FALSE;
+					}
+					if (attr == USB_ENDPOINT_XFER_INT
+					    && cmptbl[idx] == EP_INT)
+						cmptbl[idx] = EP_NUL;
+					if (attr == USB_ENDPOINT_XFER_BULK
+					    && cmptbl[idx] == EP_BLK)
+						cmptbl[idx] = EP_NUL;
+					if (attr == USB_ENDPOINT_XFER_ISOC
+					    && cmptbl[idx] == EP_ISO)
+						cmptbl[idx] = EP_NUL;
+
+					/* check if all INT endpoints match minimum interval */
+					if (attr == USB_ENDPOINT_XFER_INT
+					    && ep->desc.bInterval <
+					    vcf[17]) {
+#ifdef CONFIG_HISAX_DEBUG
+						if (cfg_found)
+							DBG(USB_DBG,
+							    "HFC-S USB: Interrupt Endpoint interval < %d found - skipping config",
+							    vcf[17]);
+#endif
+						cfg_found = FALSE;
+					}
+					ep++;
+				}
+				for (i = 0; i < 16; i++) {
+					/* all entries must be EP_NOP or EP_NUL for a valid config */
+					if (cmptbl[i] != EP_NOP
+					    && cmptbl[i] != EP_NUL)
+						cfg_found = FALSE;
+				}
+				if (cfg_found) {
+					if (cfg_used < small_match) {
+						small_match = cfg_used;
+						alt_used =
+						    probe_alt_setting;
+						iface_used = iface;
+					}
+#ifdef CONFIG_HISAX_DEBUG
+					DBG(USB_DBG,
+					    "HFC-USB: small_match=%x %x\n",
+					    small_match, alt_used);
+#endif
+				}
+				cfg_used++;
+			}
+			alt_idx++;
+		}		/* (alt_idx < intf->num_altsetting) */
+
+		/* found a valid USB Ta Endpint config */
+		if (small_match != 0xffff) {
+			iface = iface_used;
+			if (!
+			    (context =
+			     kmalloc(sizeof(hfcusb_data), GFP_KERNEL)))
+				return (-ENOMEM);	/* got no mem */
+			memset(context, 0, sizeof(hfcusb_data));
+
+			ep = iface->endpoint;
+			vcf = validconf[small_match];
+
+			for (i = 0; i < iface->desc.bNumEndpoints; i++) {
+				ep_addr = ep->desc.bEndpointAddress;
+				/* get endpoint base */
+				idx = ((ep_addr & 0x7f) - 1) * 2;
+				if (ep_addr & 0x80)
+					idx++;
+				cidx = idx & 7;
+				attr = ep->desc.bmAttributes;
+
+				/* init Endpoints */
+				if (vcf[idx] != EP_NOP
+				    && vcf[idx] != EP_NUL) {
+					switch (attr) {
+						case USB_ENDPOINT_XFER_INT:
+							context->
+							    fifos[cidx].
+							    pipe =
+							    usb_rcvintpipe
+							    (dev,
+							     ep->desc.
+							     bEndpointAddress);
+							context->
+							    fifos[cidx].
+							    usb_transfer_mode
+							    = USB_INT;
+							packet_size =
+							    ep->desc.
+							    wMaxPacketSize;
+							break;
+						case USB_ENDPOINT_XFER_BULK:
+							if (ep_addr & 0x80)
+								context->
+								    fifos
+								    [cidx].
+								    pipe =
+								    usb_rcvbulkpipe
+								    (dev,
+								     ep->
+								     desc.
+								     bEndpointAddress);
+							else
+								context->
+								    fifos
+								    [cidx].
+								    pipe =
+								    usb_sndbulkpipe
+								    (dev,
+								     ep->
+								     desc.
+								     bEndpointAddress);
+							context->
+							    fifos[cidx].
+							    usb_transfer_mode
+							    = USB_BULK;
+							packet_size =
+							    ep->desc.
+							    wMaxPacketSize;
+							break;
+						case USB_ENDPOINT_XFER_ISOC:
+							if (ep_addr & 0x80)
+								context->
+								    fifos
+								    [cidx].
+								    pipe =
+								    usb_rcvisocpipe
+								    (dev,
+								     ep->
+								     desc.
+								     bEndpointAddress);
+							else
+								context->
+								    fifos
+								    [cidx].
+								    pipe =
+								    usb_sndisocpipe
+								    (dev,
+								     ep->
+								     desc.
+								     bEndpointAddress);
+							context->
+							    fifos[cidx].
+							    usb_transfer_mode
+							    = USB_ISOC;
+							iso_packet_size =
+							    ep->desc.
+							    wMaxPacketSize;
+							break;
+						default:
+							context->
+							    fifos[cidx].
+							    pipe = 0;
+					}	/* switch attribute */
+
+					if (context->fifos[cidx].pipe) {
+						context->fifos[cidx].
+						    fifonum = cidx;
+						context->fifos[cidx].hfc =
+						    context;
+						context->fifos[cidx].
+						    usb_packet_maxlen =
+						    ep->desc.
+						    wMaxPacketSize;
+						context->fifos[cidx].
+						    intervall =
+						    ep->desc.bInterval;
+						context->fifos[cidx].
+						    skbuff = NULL;
+					}
+				}
+				ep++;
+			}
+			context->dev = dev;	/* save device */
+			context->if_used = ifnum;	/* save used interface */
+			context->alt_used = alt_used;	/* and alternate config */
+			context->ctrl_paksize = dev->descriptor.bMaxPacketSize0;	/* control size */
+			context->cfg_used = vcf[16];	/* store used config */
+			context->vend_idx = vend_idx;	/* store found vendor */
+			context->packet_size = packet_size;
+			context->iso_packet_size = iso_packet_size;
+
+			/* create the control pipes needed for register access */
+			context->ctrl_in_pipe =
+			    usb_rcvctrlpipe(context->dev, 0);
+			context->ctrl_out_pipe =
+			    usb_sndctrlpipe(context->dev, 0);
+			context->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+			printk(KERN_INFO
+			       "HFC-S USB: detected \"%s\"\n",
+			       vdata[vend_idx].vend_name);
+#ifdef CONFIG_HISAX_DEBUG
+			DBG(USB_DBG,
+			    "HFC-S USB: Endpoint-Config: %s (if=%d alt=%d)\n",
+			    conf_str[small_match], context->if_used,
+			    context->alt_used);
+			printk(KERN_INFO
+			       "HFC-S USB: E-channel (\"ECHO:\") logging ");
+			if (validconf[small_match][18])
+				printk(" possible\n");
+			else
+				printk("NOT possible\n");
+#endif
+			/* init the chip and register the driver */
+			if (usb_init(context)) {
+				if (context->ctrl_urb) {
+					usb_unlink_urb(context->ctrl_urb);
+					usb_free_urb(context->ctrl_urb);
+					context->ctrl_urb = NULL;
+				}
+				kfree(context);
+				return (-EIO);
+			}
+			usb_set_intfdata(intf, context);
+			return (0);
+		}
+	} else {
+		printk(KERN_INFO
+		       "HFC-S USB: no valid vendor found in USB descriptor\n");
+	}
+	return (-EIO);
+}
+
+/****************************************************/
+/* function called when an active device is removed */
+/****************************************************/
+static void
+hfc_usb_disconnect(struct usb_interface
+		   *intf)
+{
+	hfcusb_data *context = usb_get_intfdata(intf);
+	int i;
+	printk(KERN_INFO "HFC-S USB: device disconnect\n");
+	context->disc_flag = TRUE;
+	usb_set_intfdata(intf, NULL);
+	if (!context)
+		return;
+	if (timer_pending(&context->t3_timer))
+		del_timer(&context->t3_timer);
+	if (timer_pending(&context->t4_timer))
+		del_timer(&context->t4_timer);
+	if (timer_pending(&context->led_timer))
+		del_timer(&context->led_timer);
+	/* tell all fifos to terminate */
+	for (i = 0; i < HFCUSB_NUM_FIFOS; i++) {
+		if (context->fifos[i].usb_transfer_mode == USB_ISOC) {
+			if (context->fifos[i].active > 0) {
+				stop_isoc_chain(&context->fifos[i]);
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(USB_DBG,
+				    "HFC-S USB: hfc_usb_disconnect: stopping ISOC chain Fifo no %i",
+				    i);
+#endif
+			}
+		} else {
+			if (context->fifos[i].active > 0) {
+				context->fifos[i].active = 0;
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(USB_DBG,
+				    "HFC-S USB: hfc_usb_disconnect: unlinking URB for Fifo no %i",
+				    i);
+#endif
+			}
+			if (context->fifos[i].urb) {
+				usb_unlink_urb(context->fifos[i].urb);
+				usb_free_urb(context->fifos[i].urb);
+				context->fifos[i].urb = NULL;
+			}
+		}
+		context->fifos[i].active = 0;
+	}
+	/* wait for all URBS to terminate */
+	mdelay(10);
+	if (context->ctrl_urb) {
+		usb_unlink_urb(context->ctrl_urb);
+		usb_free_urb(context->ctrl_urb);
+		context->ctrl_urb = NULL;
+	}
+	hisax_unregister(&context->d_if);
+	kfree(context);		/* free our structure again */
+}				/* hfc_usb_disconnect */
+
+/************************************/
+/* our driver information structure */
+/************************************/
+static struct usb_driver hfc_drv = {
+	.owner = THIS_MODULE,.name =
+	    "hfc_usb",.id_table = hfc_usb_idtab,.probe =
+	    hfc_usb_probe,.disconnect = hfc_usb_disconnect,
+};
+static void __exit
+hfc_usb_exit(void)
+{
+#ifdef CONFIG_HISAX_DEBUG
+	DBG(USB_DBG, "HFC-S USB: calling \"hfc_usb_exit\" ...");
+#endif
+	usb_deregister(&hfc_drv);	/* release our driver */
+	printk(KERN_INFO "HFC-S USB: module removed\n");
+}
+
+static int __init
+hfc_usb_init(void)
+{
+#ifndef CONFIG_HISAX_DEBUG
+	unsigned int debug = -1;
+#endif
+	char revstr[30], datestr[30], dummy[30];
+	sscanf(hfcusb_revision,
+	       "%s %s $ %s %s %s $ ", dummy, revstr,
+	       dummy, datestr, dummy);
+	printk(KERN_INFO
+	       "HFC-S USB: driver module revision %s date %s loaded, (debug=%i)\n",
+	       revstr, datestr, debug);
+	if (usb_register(&hfc_drv)) {
+		printk(KERN_INFO
+		       "HFC-S USB: Unable to register HFC-S USB module at usb stack\n");
+		return (-1);	/* unable to register */
+	}
+	return (0);
+}
+
+module_init(hfc_usb_init);
+module_exit(hfc_usb_exit);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, hfc_usb_idtab);
diff --git a/drivers/isdn/hisax/hfc_usb.h b/drivers/isdn/hisax/hfc_usb.h
new file mode 100644
index 000000000000..b171600cf641
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_usb.h
@@ -0,0 +1,228 @@
+/*
+* hfc_usb.h
+*
+* $Id: hfc_usb.h,v 4.1 2005/01/26 17:25:53 martinb1 Exp $
+*/
+
+#ifndef __HFC_USB_H__
+#define __HFC_USB_H__
+
+#define DRIVER_AUTHOR   "Peter Sprenger (sprenger@moving-byters.de)"
+#define DRIVER_DESC     "HFC-S USB based HiSAX ISDN driver"
+
+#define VERBOSE_USB_DEBUG
+
+#define TRUE  1
+#define FALSE 0
+
+
+/***********/
+/* defines */
+/***********/
+#define HFC_CTRL_TIMEOUT 20	/* 5ms timeout writing/reading regs */
+#define HFC_TIMER_T3 8000	/* timeout for l1 activation timer */
+#define HFC_TIMER_T4 500	/* time for state change interval */
+
+#define HFCUSB_L1_STATECHANGE 0	/* L1 state changed */
+#define HFCUSB_L1_DRX 1		/* D-frame received */
+#define HFCUSB_L1_ERX 2		/* E-frame received */
+#define HFCUSB_L1_DTX 4		/* D-frames completed */
+
+#define MAX_BCH_SIZE 2048	/* allowed B-channel packet size */
+
+#define HFCUSB_RX_THRESHOLD 64	/* threshold for fifo report bit rx */
+#define HFCUSB_TX_THRESHOLD 64	/* threshold for fifo report bit tx */
+
+#define HFCUSB_CHIP_ID		0x16	/* Chip ID register index */
+#define HFCUSB_CIRM		0x00	/* cirm register index */
+#define HFCUSB_USB_SIZE		0x07	/* int length register */
+#define HFCUSB_USB_SIZE_I	0x06	/* iso length register */
+#define HFCUSB_F_CROSS		0x0b	/* bit order register */
+#define HFCUSB_CLKDEL		0x37	/* bit delay register */
+#define HFCUSB_CON_HDLC		0xfa	/* channel connect register */
+#define HFCUSB_HDLC_PAR		0xfb
+#define HFCUSB_SCTRL		0x31	/* S-bus control register (tx) */
+#define HFCUSB_SCTRL_E		0x32	/* same for E and special funcs */
+#define HFCUSB_SCTRL_R		0x33	/* S-bus control register (rx) */
+#define HFCUSB_F_THRES		0x0c	/* threshold register */
+#define HFCUSB_FIFO		0x0f	/* fifo select register */
+#define HFCUSB_F_USAGE		0x1a	/* fifo usage register */
+#define HFCUSB_MST_MODE0	0x14
+#define HFCUSB_MST_MODE1	0x15
+#define HFCUSB_P_DATA		0x1f
+#define HFCUSB_INC_RES_F	0x0e
+#define HFCUSB_STATES		0x30
+
+#define HFCUSB_CHIPID		0x40	/* ID value of HFC-S USB */
+
+/******************/
+/* fifo registers */
+/******************/
+#define HFCUSB_NUM_FIFOS	8	/* maximum number of fifos */
+#define HFCUSB_B1_TX		0	/* index for B1 transmit bulk/int */
+#define HFCUSB_B1_RX		1	/* index for B1 receive bulk/int */
+#define HFCUSB_B2_TX		2
+#define HFCUSB_B2_RX		3
+#define HFCUSB_D_TX		4
+#define HFCUSB_D_RX		5
+#define HFCUSB_PCM_TX		6
+#define HFCUSB_PCM_RX		7
+
+/*
+* used to switch snd_transfer_mode for different TA modes e.g. the Billion USB TA just
+* supports ISO out, while the Cologne Chip EVAL TA just supports BULK out
+*/
+#define USB_INT		0
+#define USB_BULK	1
+#define USB_ISOC	2
+
+#define ISOC_PACKETS_D	8
+#define ISOC_PACKETS_B	8
+#define ISO_BUFFER_SIZE	128
+
+// ISO send definitions
+#define SINK_MAX	68
+#define SINK_MIN	48
+#define SINK_DMIN	12
+#define SINK_DMAX	18
+#define BITLINE_INF	(-64*8)
+
+
+/**********/
+/* macros */
+/**********/
+#define write_usb(a,b,c)usb_control_msg((a)->dev,(a)->ctrl_out_pipe,0,0x40,(c),(b),0,0,HFC_CTRL_TIMEOUT)
+#define read_usb(a,b,c) usb_control_msg((a)->dev,(a)->ctrl_in_pipe,1,0xC0,0,(b),(c),1,HFC_CTRL_TIMEOUT)
+
+
+/*******************/
+/* Debugging Flags */
+/*******************/
+#define USB_DBG   1
+#define ISDN_DBG  2
+
+
+/* *********************/
+/* USB related defines */
+/***********************/
+#define HFC_CTRL_BUFSIZE 32
+
+
+
+/*************************************************/
+/* entry and size of output/input control buffer */
+/*************************************************/
+typedef struct {
+	__u8 hfc_reg;		/* register number */
+	__u8 reg_val;		/* value to be written (or read) */
+	int action;		/* data for action handler */
+} ctrl_buft;
+
+
+/********************/
+/* URB error codes: */
+/********************/
+/* Used to represent a list of values and their respective symbolic names */
+struct hfcusb_symbolic_list {
+	const int num;
+	const char *name;
+};
+
+static struct hfcusb_symbolic_list urb_errlist[] = {
+	{-ENOMEM, "No memory for allocation of internal structures"},
+	{-ENOSPC, "The host controller's bandwidth is already consumed"},
+	{-ENOENT, "URB was canceled by unlink_urb"},
+	{-EXDEV, "ISO transfer only partially completed"},
+	{-EAGAIN, "Too match scheduled for the future"},
+	{-ENXIO, "URB already queued"},
+	{-EFBIG, "Too much ISO frames requested"},
+	{-ENOSR, "Buffer error (overrun)"},
+	{-EPIPE, "Specified endpoint is stalled (device not responding)"},
+	{-EOVERFLOW, "Babble (bad cable?)"},
+	{-EPROTO, "Bit-stuff error (bad cable?)"},
+	{-EILSEQ, "CRC/Timeout"},
+	{-ETIMEDOUT, "NAK (device does not respond)"},
+	{-ESHUTDOWN, "Device unplugged"},
+	{-1, NULL}
+};
+
+
+/*****************************************************/
+/* device dependant information to support different */
+/* ISDN Ta's using the HFC-S USB chip                */
+/*****************************************************/
+
+/* USB descriptor need to contain one of the following EndPoint combination: */
+#define CNF_4INT3ISO	1	// 4 INT IN, 3 ISO OUT
+#define CNF_3INT3ISO	2	// 3 INT IN, 3 ISO OUT
+#define CNF_4ISO3ISO	3	// 4 ISO IN, 3 ISO OUT
+#define CNF_3ISO3ISO	4	// 3 ISO IN, 3 ISO OUT
+
+#define EP_NUL 1		// Endpoint at this position not allowed
+#define EP_NOP 2		// all type of endpoints allowed at this position
+#define EP_ISO 3		// Isochron endpoint mandatory at this position
+#define EP_BLK 4		// Bulk endpoint mandatory at this position
+#define EP_INT 5		// Interrupt endpoint mandatory at this position
+
+/* this array represents all endpoints possible in the HCF-USB the last
+* 3 entries are the configuration number, the minimum interval for
+* Interrupt endpoints & boolean if E-channel logging possible
+*/
+int validconf[][19] = {
+	// INT in, ISO out config
+	{EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT,
+	 EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
+	 CNF_4INT3ISO, 2, 1},
+	{EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL,
+	 EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
+	 CNF_3INT3ISO, 2, 0},
+	// ISO in, ISO out config
+	{EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL,
+	 EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO,
+	 CNF_4ISO3ISO, 2, 1},
+	{EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL,
+	 EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL,
+	 CNF_3ISO3ISO, 2, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}	// EOL element
+};
+
+// string description of chosen config
+char *conf_str[] = {
+	"4 Interrupt IN + 3 Isochron OUT",
+	"3 Interrupt IN + 3 Isochron OUT",
+	"4 Isochron IN + 3 Isochron OUT",
+	"3 Isochron IN + 3 Isochron OUT"
+};
+
+
+typedef struct {
+	int vendor;		// vendor id
+	int prod_id;		// product id
+	char *vend_name;	// vendor string
+	__u8 led_scheme;	// led display scheme
+	signed short led_bits[8];	// array of 8 possible LED bitmask settings
+} vendor_data;
+
+#define LED_OFF      0		// no LED support
+#define LED_SCHEME1  1		// LED standard scheme
+#define LED_SCHEME2  2		// not used yet...
+
+#define LED_POWER_ON	1
+#define LED_POWER_OFF	2
+#define LED_S0_ON	3
+#define LED_S0_OFF	4
+#define LED_B1_ON	5
+#define LED_B1_OFF	6
+#define LED_B1_DATA	7
+#define LED_B2_ON	8
+#define LED_B2_OFF	9
+#define LED_B2_DATA	10
+
+#define LED_NORMAL   0		// LEDs are normal
+#define LED_INVERTED 1		// LEDs are inverted
+
+/* time in ms to perform a Flashing LED when B-Channel has traffic */
+#define LED_TIME      250
+
+
+#endif				// __HFC_USB_H__
diff --git a/drivers/isdn/hisax/hfcscard.c b/drivers/isdn/hisax/hfcscard.c
new file mode 100644
index 000000000000..6fc55fea1702
--- /dev/null
+++ b/drivers/isdn/hisax/hfcscard.c
@@ -0,0 +1,266 @@
+/* $Id: hfcscard.c,v 1.10.2.4 2004/01/14 16:04:48 keil Exp $
+ *
+ * low level stuff for hfcs based cards (Teles3c, ACER P10)
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include "hisax.h"
+#include "hfc_2bds0.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+static const char *hfcs_revision = "$Revision: 1.10.2.4 $";
+
+static irqreturn_t
+hfcs_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if ((HFCD_ANYINT | HFCD_BUSY_NBUSY) & 
+		(stat = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_STAT))) {
+		val = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_INT_S1);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "HFCS: stat(%02x) s1(%02x)", stat, val);
+		hfc2bds0_interrupt(cs, val);
+	} else {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "HFCS: irq_no_irq stat(%02x)", stat);
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+hfcs_Timer(struct IsdnCardState *cs)
+{
+	cs->hw.hfcD.timer.expires = jiffies + 75;
+	/* WD RESET */
+/*	WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt | 0x80);
+	add_timer(&cs->hw.hfcD.timer);
+*/
+}
+
+void
+release_io_hfcs(struct IsdnCardState *cs)
+{
+	release2bds0(cs);
+	del_timer(&cs->hw.hfcD.timer);
+	if (cs->hw.hfcD.addr)
+		release_region(cs->hw.hfcD.addr, 2);
+}
+
+static void
+reset_hfcs(struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "HFCS: resetting card\n");
+	cs->hw.hfcD.cirm = HFCD_RESET;
+	if (cs->typ == ISDN_CTYPE_TELES3C)
+		cs->hw.hfcD.cirm |= HFCD_MEM8K;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* Reset On */
+	mdelay(10);
+	cs->hw.hfcD.cirm = 0;
+	if (cs->typ == ISDN_CTYPE_TELES3C)
+		cs->hw.hfcD.cirm |= HFCD_MEM8K;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* Reset Off */
+	mdelay(10);
+	if (cs->typ == ISDN_CTYPE_TELES3C)
+		cs->hw.hfcD.cirm |= HFCD_INTB;
+	else if (cs->typ == ISDN_CTYPE_ACERP10)
+		cs->hw.hfcD.cirm |= HFCD_INTA;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CLKDEL, 0x0e);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_TEST, HFCD_AUTO_AWAKE); /* S/T Auto awake */
+	cs->hw.hfcD.ctmt = HFCD_TIM25 | HFCD_AUTO_TIMER;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
+	cs->hw.hfcD.int_m2 = HFCD_IRQ_ENABLE;
+	cs->hw.hfcD.int_m1 = HFCD_INTS_B1TRANS | HFCD_INTS_B2TRANS |
+		HFCD_INTS_DTRANS | HFCD_INTS_B1REC | HFCD_INTS_B2REC |
+		HFCD_INTS_DREC | HFCD_INTS_L1STATE;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M1, cs->hw.hfcD.int_m1);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M2, cs->hw.hfcD.int_m2);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, HFCD_LOAD_STATE | 2); /* HFC ST 2 */
+	udelay(10);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, 2); /* HFC ST 2 */
+	cs->hw.hfcD.mst_m = HFCD_MASTER;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m); /* HFC Master */
+	cs->hw.hfcD.sctrl = 0;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl);
+}
+
+static int
+hfcs_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+	int delay;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFCS: card_msg %x", mt);
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_hfcs(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_hfcs(cs);
+			return(0);
+		case CARD_INIT:
+			delay = (75*HZ)/100 +1;
+			cs->hw.hfcD.timer.expires = jiffies + delay;
+			add_timer(&cs->hw.hfcD.timer);
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_hfcs(cs);
+			init2bds0(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			delay = (80*HZ)/1000 +1;
+			msleep(80);
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcD.ctmt |= HFCD_TIM800;
+			cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); 
+			cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id hfc_ids[] __initdata = {
+	{ ISAPNP_VENDOR('A', 'N', 'X'), ISAPNP_FUNCTION(0x1114),
+	  ISAPNP_VENDOR('A', 'N', 'X'), ISAPNP_FUNCTION(0x1114), 
+	  (unsigned long) "Acer P10" },
+	{ ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0002),
+	  ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0002), 
+	  (unsigned long) "Billion 2" },
+	{ ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0001),
+	  ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0001), 
+	  (unsigned long) "Billion 1" },
+	{ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x7410),
+	  ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x7410), 
+	  (unsigned long) "IStar PnP" },
+	{ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2610),
+	  ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2610), 
+	  (unsigned long) "Teles 16.3c" },
+	{ ISAPNP_VENDOR('S', 'F', 'M'), ISAPNP_FUNCTION(0x0001),
+	  ISAPNP_VENDOR('S', 'F', 'M'), ISAPNP_FUNCTION(0x0001), 
+	  (unsigned long) "Tornado Tipa C" },
+	{ ISAPNP_VENDOR('K', 'Y', 'E'), ISAPNP_FUNCTION(0x0001),
+	  ISAPNP_VENDOR('K', 'Y', 'E'), ISAPNP_FUNCTION(0x0001), 
+	  (unsigned long) "Genius Speed Surfer" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __initdata = &hfc_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __init
+setup_hfcs(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, hfcs_revision);
+	printk(KERN_INFO "HiSax: HFC-S driver Rev. %s\n", HiSax_getrev(tmp));
+
+#ifdef __ISAPNP__
+	if (!card->para[1] && isapnp_present()) {
+		struct pnp_dev *pnp_d;
+		while(ipid->card_vendor) {
+			if ((pnp_c = pnp_find_card(ipid->card_vendor,
+				ipid->card_device, pnp_c))) {
+				pnp_d = NULL;
+				if ((pnp_d = pnp_find_dev(pnp_c,
+					ipid->vendor, ipid->function, pnp_d))) {
+					int err;
+
+					printk(KERN_INFO "HiSax: %s detected\n",
+						(char *)ipid->driver_data);
+					pnp_disable_dev(pnp_d);
+					err = pnp_activate_dev(pnp_d);
+					if (err<0) {
+						printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+							__FUNCTION__, err);
+						return(0);
+					}
+					card->para[1] = pnp_port_start(pnp_d, 0);
+					card->para[0] = pnp_irq(pnp_d, 0);
+					if (!card->para[0] || !card->para[1]) {
+						printk(KERN_ERR "HFC PnP:some resources are missing %ld/%lx\n",
+							card->para[0], card->para[1]);
+						pnp_disable_dev(pnp_d);
+						return(0);
+					}
+					break;
+				} else {
+					printk(KERN_ERR "HFC PnP: PnP error card found, no device\n");
+				}
+			}
+			ipid++;
+			pnp_c = NULL;
+		} 
+		if (!ipid->card_vendor) {
+			printk(KERN_INFO "HFC PnP: no ISAPnP card found\n");
+			return(0);
+		}
+	}
+#endif
+	cs->hw.hfcD.addr = card->para[1] & 0xfffe;
+	cs->irq = card->para[0];
+	cs->hw.hfcD.cip = 0;
+	cs->hw.hfcD.int_s1 = 0;
+	cs->hw.hfcD.send = NULL;
+	cs->bcs[0].hw.hfc.send = NULL;
+	cs->bcs[1].hw.hfc.send = NULL;
+	cs->hw.hfcD.dfifosize = 512;
+	cs->dc.hfcd.ph_state = 0;
+	cs->hw.hfcD.fifo = 255;
+	if (cs->typ == ISDN_CTYPE_TELES3C) {
+		cs->hw.hfcD.bfifosize = 1024 + 512;
+	} else if (cs->typ == ISDN_CTYPE_ACERP10) {
+		cs->hw.hfcD.bfifosize = 7*1024 + 512;
+	} else
+		return (0);
+	if (!request_region(cs->hw.hfcD.addr, 2, "HFCS isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.hfcD.addr,
+		       cs->hw.hfcD.addr + 2);
+		return (0);
+	}
+	printk(KERN_INFO
+	       "HFCS: defined at 0x%x IRQ %d HZ %d\n",
+	       cs->hw.hfcD.addr,
+	       cs->irq, HZ);
+	if (cs->typ == ISDN_CTYPE_TELES3C) {
+		/* Teles 16.3c IO ADR is 0x200 | YY0U (YY Bit 15/14 address) */
+		outb(0x00, cs->hw.hfcD.addr);
+		outb(0x56, cs->hw.hfcD.addr | 1);
+	} else if (cs->typ == ISDN_CTYPE_ACERP10) {
+		/* Acer P10 IO ADR is 0x300 */
+		outb(0x00, cs->hw.hfcD.addr);
+		outb(0x57, cs->hw.hfcD.addr | 1);
+	}
+	set_cs_func(cs);
+	cs->hw.hfcD.timer.function = (void *) hfcs_Timer;
+	cs->hw.hfcD.timer.data = (long) cs;
+	init_timer(&cs->hw.hfcD.timer);
+	cs->cardmsg = &hfcs_card_msg;
+	cs->irq_func = &hfcs_interrupt;
+	return (1);
+}
diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h
new file mode 100644
index 000000000000..dc5791728d53
--- /dev/null
+++ b/drivers/isdn/hisax/hisax.h
@@ -0,0 +1,1341 @@
+/* $Id: hisax.h,v 2.64.2.4 2004/02/11 13:21:33 keil Exp $
+ *
+ * Basic declarations, defines and prototypes
+ *
+ * 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/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+#include <linux/tty.h>
+#include <linux/serial_reg.h>
+#include <linux/netdevice.h>
+
+#define ERROR_STATISTIC
+
+#define REQUEST		0
+#define CONFIRM		1
+#define INDICATION	2
+#define RESPONSE	3
+
+#define HW_ENABLE	0x0000
+#define HW_RESET	0x0004
+#define HW_POWERUP	0x0008
+#define HW_ACTIVATE	0x0010
+#define HW_DEACTIVATE	0x0018
+
+#define HW_INFO1	0x0010
+#define HW_INFO2	0x0020
+#define HW_INFO3	0x0030
+#define HW_INFO4	0x0040
+#define HW_INFO4_P8	0x0040
+#define HW_INFO4_P10	0x0048
+#define HW_RSYNC	0x0060
+#define HW_TESTLOOP	0x0070
+#define CARD_RESET	0x00F0
+#define CARD_INIT	0x00F2
+#define CARD_RELEASE	0x00F3
+#define CARD_TEST	0x00F4
+#define CARD_AUX_IND	0x00F5
+
+#define PH_ACTIVATE	0x0100
+#define PH_DEACTIVATE	0x0110
+#define PH_DATA		0x0120
+#define PH_PULL		0x0130
+#define PH_TESTLOOP	0x0140
+#define PH_PAUSE	0x0150
+#define MPH_ACTIVATE	0x0180
+#define MPH_DEACTIVATE	0x0190
+#define MPH_INFORMATION	0x01A0
+
+#define DL_ESTABLISH	0x0200
+#define DL_RELEASE	0x0210
+#define DL_DATA		0x0220
+#define DL_FLUSH	0x0224
+#define DL_UNIT_DATA	0x0230
+
+#define MDL_BC_RELEASE  0x0278  // Formula-n enter:now
+#define MDL_BC_ASSIGN   0x027C  // Formula-n enter:now
+#define MDL_ASSIGN	0x0280
+#define MDL_REMOVE	0x0284
+#define MDL_ERROR	0x0288
+#define MDL_INFO_SETUP	0x02E0
+#define MDL_INFO_CONN	0x02E4
+#define MDL_INFO_REL	0x02E8
+
+#define CC_SETUP	0x0300
+#define CC_RESUME	0x0304
+#define CC_MORE_INFO	0x0310
+#define CC_IGNORE	0x0320
+#define CC_REJECT	0x0324
+#define CC_SETUP_COMPL	0x0330
+#define CC_PROCEEDING	0x0340
+#define CC_ALERTING	0x0344
+#define CC_PROGRESS	0x0348
+#define CC_CONNECT	0x0350
+#define CC_CHARGE	0x0354
+#define CC_NOTIFY	0x0358
+#define CC_DISCONNECT	0x0360
+#define CC_RELEASE	0x0368
+#define CC_SUSPEND	0x0370
+#define CC_PROCEED_SEND 0x0374
+#define CC_REDIR        0x0378
+#define CC_T302		0x0382
+#define CC_T303		0x0383
+#define CC_T304		0x0384
+#define CC_T305		0x0385
+#define CC_T308_1	0x0388
+#define CC_T308_2	0x038A
+#define CC_T309         0x0309
+#define CC_T310		0x0390
+#define CC_T313		0x0393
+#define CC_T318		0x0398
+#define CC_T319		0x0399
+#define CC_TSPID	0x03A0
+#define CC_NOSETUP_RSP	0x03E0
+#define CC_SETUP_ERR	0x03E1
+#define CC_SUSPEND_ERR	0x03E2
+#define CC_RESUME_ERR	0x03E3
+#define CC_CONNECT_ERR	0x03E4
+#define CC_RELEASE_ERR	0x03E5
+#define CC_RESTART	0x03F4
+#define CC_TDSS1_IO     0x13F4    /* DSS1 IO user timer */
+#define CC_TNI1_IO      0x13F5    /* NI1 IO user timer */
+
+/* define maximum number of possible waiting incoming calls */
+#define MAX_WAITING_CALLS 2
+
+
+#ifdef __KERNEL__
+
+/* include l3dss1 & ni1 specific process structures, but no other defines */
+#ifdef CONFIG_HISAX_EURO
+  #define l3dss1_process
+  #include "l3dss1.h" 
+  #undef  l3dss1_process
+#endif /* CONFIG_HISAX_EURO */
+
+#ifdef CONFIG_HISAX_NI1
+  #define l3ni1_process
+  #include "l3ni1.h" 
+  #undef  l3ni1_process
+#endif /* CONFIG_HISAX_NI1 */
+
+#define MAX_DFRAME_LEN	260
+#define MAX_DFRAME_LEN_L1	300
+#define HSCX_BUFMAX	4096
+#define MAX_DATA_SIZE	(HSCX_BUFMAX - 4)
+#define MAX_DATA_MEM	(HSCX_BUFMAX + 64)
+#define RAW_BUFMAX	(((HSCX_BUFMAX*6)/5) + 5)
+#define MAX_HEADER_LEN	4
+#define MAX_WINDOW	8
+#define MAX_MON_FRAME	32
+#define MAX_DLOG_SPACE	2048
+#define MAX_BLOG_SPACE	256
+
+/* #define I4L_IRQ_FLAG SA_INTERRUPT */
+#define I4L_IRQ_FLAG    0
+
+/*
+ * 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;
+};
+
+struct L3Timer {
+	struct l3_process *pc;
+	struct timer_list tl;
+	int event;
+};
+
+#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
+
+struct Layer1 {
+	void *hardware;
+	struct BCState *bcs;
+	struct PStack **stlistp;
+	long Flags;
+	struct FsmInst l1m;
+	struct FsmTimer	timer;
+	void (*l1l2) (struct PStack *, int, void *);
+	void (*l1hw) (struct PStack *, int, void *);
+	void (*l1tei) (struct PStack *, int, void *);
+	int mode, bc;
+	int delay;
+};
+
+#define GROUP_TEI	127
+#define TEI_SAPI	63
+#define CTRL_SAPI	0
+#define PACKET_NOACK	250
+
+/* Layer2 Flags */
+
+#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
+
+struct Layer2 {
+	int tei;
+	int sap;
+	int maxlen;
+	u_long flag;
+	spinlock_t lock;
+	u_int vs, va, vr;
+	int rc;
+	unsigned int window;
+	unsigned int sow;
+	struct sk_buff *windowar[MAX_WINDOW];
+	struct sk_buff_head i_queue;
+	struct sk_buff_head ui_queue;
+	void (*l2l1) (struct PStack *, int, void *);
+	void (*l2l3) (struct PStack *, int, void *);
+	void (*l2tei) (struct PStack *, int, void *);
+	struct FsmInst l2m;
+	struct FsmTimer t200, t203;
+	int T200, N200, T203;
+	int debug;
+	char debug_id[16];
+};
+
+struct Layer3 {
+	void (*l3l4) (struct PStack *, int, void *);
+        void (*l3ml3) (struct PStack *, int, void *);
+	void (*l3l2) (struct PStack *, int, void *);
+	struct FsmInst l3m;
+        struct FsmTimer l3m_timer;
+	struct sk_buff_head squeue;
+	struct l3_process *proc;
+	struct l3_process *global;
+	int N303;
+	int debug;
+	char debug_id[8];
+};
+
+struct LLInterface {
+	void (*l4l3) (struct PStack *, int, void *);
+        int  (*l4l3_proto) (struct PStack *, isdn_ctrl *);
+	void *userdata;
+	u_long flag;
+};
+
+#define	FLG_LLI_L1WAKEUP	1
+#define	FLG_LLI_L2WAKEUP	2
+
+struct Management {
+	int	ri;
+	struct FsmInst tei_m;
+	struct FsmTimer t202;
+	int T202, N202, debug;
+	void (*layer) (struct PStack *, int, void *);
+};
+
+#define NO_CAUSE 254
+
+struct Param {
+	u_char cause;
+	u_char loc;
+	u_char diag[6];
+	int bchannel;
+	int chargeinfo;
+	int spv;		/* SPV Flag */
+	setup_parm setup;	/* from isdnif.h numbers and Serviceindicator */
+	u_char moderate;	/* transfer mode and rate (bearer octet 4) */
+};
+
+
+struct PStack {
+	struct PStack *next;
+	struct Layer1 l1;
+	struct Layer2 l2;
+	struct Layer3 l3;
+	struct LLInterface lli;
+	struct Management ma;
+	int protocol;		/* EDSS1, 1TR6 or NI1 */
+
+        /* protocol specific data fields */
+        union
+	 { u_char uuuu; /* only as dummy */
+#ifdef CONFIG_HISAX_EURO
+           dss1_stk_priv dss1; /* private dss1 data */
+#endif /* CONFIG_HISAX_EURO */              
+#ifdef CONFIG_HISAX_NI1
+           ni1_stk_priv ni1; /* private ni1 data */
+#endif /* CONFIG_HISAX_NI1 */             
+	 } prot;
+};
+
+struct l3_process {
+	int callref;
+	int state;
+	struct L3Timer timer;
+	int N303;
+	int debug;
+	struct Param para;
+	struct Channel *chan;
+	struct PStack *st;
+	struct l3_process *next;
+        ulong redir_result;
+
+        /* protocol specific data fields */
+        union 
+	 { u_char uuuu; /* only when euro not defined, avoiding empty union */
+#ifdef CONFIG_HISAX_EURO 
+           dss1_proc_priv dss1; /* private dss1 data */
+#endif /* CONFIG_HISAX_EURO */            
+#ifdef CONFIG_HISAX_NI1
+           ni1_proc_priv ni1; /* private ni1 data */
+#endif /* CONFIG_HISAX_NI1 */             
+	 } prot;
+};
+
+struct hscx_hw {
+	int hscx;
+	int rcvidx;
+	int count;              /* Current skb sent count */
+	u_char *rcvbuf;         /* B-Channel receive Buffer */
+	u_char tsaxr0;
+	u_char tsaxr1;
+};
+
+struct w6692B_hw {
+	int bchan;
+	int rcvidx;
+	int count;              /* Current skb sent count */
+	u_char *rcvbuf;         /* B-Channel receive Buffer */
+};
+
+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];
+};
+
+struct isar_hw {
+	int dpath;
+	int rcvidx;
+	int txcnt;
+	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 *rcvbuf;         /* B-Channel receive Buffer */
+	u_char conmsg[16];
+	struct isar_reg *reg;
+};
+
+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
+};
+
+struct hdlc_hw {
+	union {
+		u_int ctrl;
+		struct hdlc_stat_reg sr;
+	} ctrl;
+	u_int stat;
+	int rcvidx;
+	int count;              /* Current skb sent count */
+	u_char *rcvbuf;         /* B-Channel receive Buffer */
+};
+
+struct hfcB_hw {
+	unsigned int *send;
+	int f1;
+	int f2;
+};
+
+struct tiger_hw {
+	u_int *send;
+	u_int *s_irq;
+	u_int *s_end;
+	u_int *sendp;
+	u_int *rec;
+	int free;
+	u_char *rcvbuf;
+	u_char *sendbuf;
+	u_char *sp;
+	int sendcnt;
+	u_int s_tot;
+	u_int r_bitcnt;
+	u_int r_tot;
+	u_int r_err;
+	u_int r_fcs;
+	u_char r_state;
+	u_char r_one;
+	u_char r_val;
+	u_char s_state;
+};
+
+struct amd7930_hw {
+	u_char *tx_buff;
+	u_char *rv_buff;
+	int rv_buff_in;
+	int rv_buff_out;
+	struct sk_buff *rv_skb;
+	struct hdlc_state *hdlc_state;
+	struct work_struct tq_rcv;
+	struct work_struct tq_xmt;
+};
+
+#define BC_FLG_INIT	1
+#define BC_FLG_ACTIV	2
+#define BC_FLG_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_FTI_FTS	16
+#define BC_FLG_FRH_WAIT	17
+
+#define L1_MODE_NULL	0
+#define L1_MODE_TRANS	1
+#define L1_MODE_HDLC	2
+#define L1_MODE_EXTRN	3
+#define L1_MODE_HDLC_56K 4
+#define L1_MODE_MODEM	7
+#define L1_MODE_V32	8
+#define L1_MODE_FAX	9
+
+struct BCState {
+	int channel;
+	int mode;
+	u_long Flag;
+	struct IsdnCardState *cs;
+	int tx_cnt;		/* B-Channel transmit counter */
+	struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
+	struct sk_buff_head rqueue;	/* B-Channel receive Queue */
+	struct sk_buff_head squeue;	/* B-Channel send Queue */
+	int ackcnt;
+	spinlock_t aclock;
+	struct PStack *st;
+	u_char *blog;
+	u_char *conmsg;
+	struct timer_list transbusy;
+	struct work_struct tqueue;
+	u_long event;
+	int  (*BC_SetStack) (struct PStack *, struct BCState *);
+	void (*BC_Close) (struct BCState *);
+#ifdef ERROR_STATISTIC
+	int err_crc;
+	int err_tx;
+	int err_rdo;
+	int err_inv;
+#endif
+	union {
+		struct hscx_hw hscx;
+		struct hdlc_hw hdlc;
+		struct isar_hw isar;
+		struct hfcB_hw hfc;
+		struct tiger_hw tiger;
+		struct amd7930_hw  amd7930;
+		struct w6692B_hw w6692;
+		struct hisax_b_if *b_if;
+	} hw;
+};
+
+struct Channel {
+	struct PStack *b_st, *d_st;
+	struct IsdnCardState *cs;
+	struct BCState *bcs;
+	int chan;
+	int incoming;
+	struct FsmInst fi;
+	struct FsmTimer drel_timer, dial_timer;
+	int debug;
+	int l2_protocol, l2_active_protocol;
+	int l3_protocol;
+	int data_open;
+	struct l3_process *proc;
+	setup_parm setup;	/* from isdnif.h numbers and Serviceindicator */
+	u_long Flags;		/* for remembering action done in l4 */
+	int leased;
+};
+
+struct elsa_hw {
+	struct pci_dev *dev;
+	unsigned long base;
+	unsigned int cfg;
+	unsigned int ctrl;
+	unsigned int ale;
+	unsigned int isac;
+	unsigned int itac;
+	unsigned int hscx;
+	unsigned int trig;
+	unsigned int timer;
+	unsigned int counter;
+	unsigned int status;
+	struct timer_list tl;
+	unsigned int MFlag;
+	struct BCState *bcs;
+	u_char *transbuf;
+	u_char *rcvbuf;
+	unsigned int transp;
+	unsigned int rcvp;
+	unsigned int transcnt;
+	unsigned int rcvcnt;
+	u_char IER;
+	u_char FCR;
+	u_char LCR;
+	u_char MCR;
+	u_char ctrl_reg;
+};
+
+struct teles3_hw {
+	unsigned int cfg_reg;
+	signed   int isac;
+	signed   int hscx[2];
+	signed   int isacfifo;
+	signed   int hscxfifo[2];
+};
+
+struct teles0_hw {
+	unsigned int cfg_reg;
+	void __iomem *membase;
+	unsigned long phymem;
+};
+
+struct avm_hw {
+	unsigned int cfg_reg;
+	unsigned int isac;
+	unsigned int hscx[2];
+	unsigned int isacfifo;
+	unsigned int hscxfifo[2];
+	unsigned int counter;
+	struct pci_dev *dev;
+};
+
+struct ix1_hw {
+	unsigned int cfg_reg;
+	unsigned int isac_ale;
+	unsigned int isac;
+	unsigned int hscx_ale;
+	unsigned int hscx;
+};
+
+struct diva_hw {
+	unsigned long cfg_reg;
+	unsigned long pci_cfg;
+	unsigned int ctrl;
+	unsigned long isac_adr;
+	unsigned int isac;
+	unsigned long hscx_adr;
+	unsigned int hscx;
+	unsigned int status;
+	struct timer_list tl;
+	u_char ctrl_reg;
+	struct pci_dev *dev;
+};
+
+struct asus_hw {
+	unsigned int cfg_reg;
+	unsigned int adr;
+	unsigned int isac;
+	unsigned int hscx;
+	unsigned int u7;
+	unsigned int pots;
+};
+
+
+struct hfc_hw {
+	unsigned int addr;
+	unsigned int fifosize;
+	unsigned char cirm;
+	unsigned char ctmt;
+	unsigned char cip;
+	u_char isac_spcr;
+	struct timer_list timer;
+};	
+
+struct sedl_hw {
+	unsigned int cfg_reg;
+	unsigned int adr;
+	unsigned int isac;
+	unsigned int hscx;
+	unsigned int reset_on;
+	unsigned int reset_off;
+	struct isar_reg isar;
+	unsigned int chip;
+	unsigned int bus;
+	struct pci_dev *dev;
+};
+
+struct spt_hw {
+	unsigned int cfg_reg;
+	unsigned int isac;
+	unsigned int hscx[2];
+	unsigned char res_irq;
+};
+
+struct mic_hw {
+	unsigned int cfg_reg;
+	unsigned int adr;
+	unsigned int isac;
+	unsigned int hscx;
+};
+
+struct njet_hw {
+	unsigned long base;
+	unsigned int isac;
+	unsigned int auxa;
+	unsigned char auxd;
+	unsigned char dmactrl;
+	unsigned char ctrl_reg;
+	unsigned char irqmask0;
+	unsigned char irqstat0;
+	unsigned char last_is0;
+	struct pci_dev *dev;
+};
+
+struct hfcPCI_hw {
+	unsigned char cirm;
+	unsigned char ctmt;
+	unsigned char conn;
+	unsigned char mst_m;
+	unsigned char int_m1;
+	unsigned char int_m2;
+	unsigned char int_s1;
+	unsigned char sctrl;
+        unsigned char sctrl_r;
+        unsigned char sctrl_e;
+        unsigned char trm;
+	unsigned char stat;
+	unsigned char fifo;
+        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;
+};
+
+struct hfcSX_hw {
+        unsigned long base;
+	unsigned char cirm;
+	unsigned char ctmt;
+	unsigned char conn;
+	unsigned char mst_m;
+	unsigned char int_m1;
+	unsigned char int_m2;
+	unsigned char int_s1;
+	unsigned char sctrl;
+        unsigned char sctrl_r;
+        unsigned char sctrl_e;
+        unsigned char trm;
+	unsigned char stat;
+	unsigned char fifo;
+        unsigned char bswapped;
+        unsigned char nt_mode;
+        unsigned char chip;
+        int b_fifo_size;
+        unsigned char last_fifo;
+        void *extra;
+        int nt_timer;
+	struct timer_list timer;
+};
+
+struct hfcD_hw {
+	unsigned int addr;
+	unsigned int bfifosize;
+	unsigned int dfifosize;
+	unsigned char cirm;
+	unsigned char ctmt;
+	unsigned char cip;
+	unsigned char conn;
+	unsigned char mst_m;
+	unsigned char int_m1;
+	unsigned char int_m2;
+	unsigned char int_s1;
+	unsigned char sctrl;
+	unsigned char stat;
+	unsigned char fifo;
+	unsigned char f1;
+	unsigned char f2;
+	unsigned int *send;
+	struct timer_list timer;
+};
+
+struct isurf_hw {
+	unsigned int reset;
+	unsigned long phymem;
+	void __iomem *isac;
+	void __iomem *isar;
+	struct isar_reg isar_r;
+};
+
+struct saphir_hw {
+	struct pci_dev *dev;
+	unsigned int cfg_reg;
+	unsigned int ale;
+	unsigned int isac;
+	unsigned int hscx;
+	struct timer_list timer;
+};
+
+struct bkm_hw {
+	struct pci_dev *dev;
+	unsigned long base;
+	/* A4T stuff */
+	unsigned long isac_adr;
+	unsigned int isac_ale;
+	unsigned long jade_adr;
+	unsigned int jade_ale;
+	/* Scitel Quadro stuff */
+	unsigned long plx_adr;
+	unsigned long data_adr;
+};	
+
+struct gazel_hw {
+	struct pci_dev *dev;
+	unsigned int cfg_reg;
+	unsigned int pciaddr[2];
+        signed   int ipac;
+	signed   int isac;
+	signed   int hscx[2];
+	signed   int isacfifo;
+	signed   int hscxfifo[2];
+	unsigned char timeslot;
+	unsigned char iom2;
+};
+
+struct w6692_hw {
+	struct pci_dev *dev;
+	unsigned int iobase;
+	struct timer_list timer;
+};
+
+#ifdef  CONFIG_HISAX_TESTEMU
+struct te_hw {
+	unsigned char *sfifo;
+	unsigned char *sfifo_w;
+	unsigned char *sfifo_r;
+	unsigned char *sfifo_e;
+	int sfifo_cnt;
+	unsigned int stat;
+	wait_queue_head_t rwaitq;
+	wait_queue_head_t swaitq;
+};
+#endif
+
+struct arcofi_msg {
+	struct arcofi_msg *next;
+	u_char receive;
+	u_char len;
+	u_char msg[10];
+};
+
+struct isac_chip {
+	int ph_state;
+	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;
+};
+
+struct hfcd_chip {
+	int ph_state;
+};
+
+struct hfcpci_chip {
+	int ph_state;
+};
+
+struct hfcsx_chip {
+	int ph_state;
+};
+
+struct w6692_chip {
+	int ph_state;
+};
+
+struct amd7930_chip {
+	u_char lmr1;
+	u_char ph_state;
+	u_char old_state;
+	u_char flg_t3;
+	unsigned int tx_xmtlen;
+	struct timer_list timer3;
+	void (*ph_command) (struct IsdnCardState *, u_char, char *);
+	void (*setIrqMask) (struct IsdnCardState *, u_char);
+};
+
+struct icc_chip {
+	int ph_state;
+	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;
+};
+
+#define HW_IOM1			0
+#define HW_IPAC			1
+#define HW_ISAR			2
+#define HW_ARCOFI		3
+#define FLG_TWO_DCHAN		4
+#define FLG_L1_DBUSY		5
+#define FLG_DBUSY_TIMER 	6
+#define FLG_LOCK_ATOMIC 	7
+#define FLG_ARCOFI_TIMER	8
+#define FLG_ARCOFI_ERROR	9
+#define FLG_HW_L1_UINT		10
+
+struct IsdnCardState {
+	spinlock_t	lock;
+	u_char		typ;
+	u_char		subtyp;
+	int		protocol;
+	u_int		irq;
+	u_long		irq_flags;
+	u_long		HW_Flags;
+	int		*busy_flag;
+        int		chanlimit; /* limited number of B-chans to use */
+        int		logecho; /* log echo if supported by card */
+	union {
+		struct elsa_hw elsa;
+		struct teles0_hw teles0;
+		struct teles3_hw teles3;
+		struct avm_hw avm;
+		struct ix1_hw ix1;
+		struct diva_hw diva;
+		struct asus_hw asus;
+		struct hfc_hw hfc;
+		struct sedl_hw sedl;
+		struct spt_hw spt;
+		struct mic_hw mic;
+		struct njet_hw njet;
+		struct hfcD_hw hfcD;
+		struct hfcPCI_hw hfcpci;
+		struct hfcSX_hw hfcsx;
+		struct ix1_hw niccy;
+		struct isurf_hw isurf;
+		struct saphir_hw saphir;
+#ifdef CONFIG_HISAX_TESTEMU
+		struct te_hw te;
+#endif
+		struct bkm_hw ax;
+		struct gazel_hw gazel;
+		struct w6692_hw w6692;
+		struct hisax_d_if *hisax_d_if;
+	} hw;
+	int		myid;
+	isdn_if		iif;
+	spinlock_t	statlock;
+	u_char		*status_buf;
+	u_char		*status_read;
+	u_char		*status_write;
+	u_char		*status_end;
+	u_char		(*readisac) (struct IsdnCardState *, u_char);
+	void		(*writeisac) (struct IsdnCardState *, u_char, u_char);
+	void		(*readisacfifo) (struct IsdnCardState *, u_char *, int);
+	void		(*writeisacfifo) (struct IsdnCardState *, u_char *, int);
+	u_char		(*BC_Read_Reg) (struct IsdnCardState *, int, u_char);
+	void		(*BC_Write_Reg) (struct IsdnCardState *, int, u_char, u_char);
+	void		(*BC_Send_Data) (struct BCState *);
+	int		(*cardmsg) (struct IsdnCardState *, int, void *);
+	void		(*setstack_d) (struct PStack *, struct IsdnCardState *);
+	void		(*DC_Close) (struct IsdnCardState *);
+	int		(*irq_func) (int, void *, struct pt_regs *);
+	int		(*auxcmd) (struct IsdnCardState *, isdn_ctrl *);
+	struct Channel	channel[2+MAX_WAITING_CALLS];
+	struct BCState	bcs[2+MAX_WAITING_CALLS];
+	struct PStack	*stlist;
+	struct sk_buff_head rq, sq; /* D-channel queues */
+	int		cardnr;
+	char		*dlog;
+	int		debug;
+	union {
+		struct isac_chip isac;
+		struct hfcd_chip hfcd;
+		struct hfcpci_chip hfcpci;
+		struct hfcsx_chip hfcsx;
+		struct w6692_chip w6692;
+		struct amd7930_chip amd7930;
+		struct icc_chip icc;
+	} dc;
+	u_char		*rcvbuf;
+	int		rcvidx;
+	struct sk_buff	*tx_skb;
+	int		tx_cnt;
+	u_long		event;
+	struct work_struct tqueue;
+	struct timer_list dbusytimer;
+#ifdef ERROR_STATISTIC
+	int		err_crc;
+	int		err_tx;
+	int		err_rx;
+#endif
+};
+
+
+#define  schedule_event(s, ev)	do {test_and_set_bit(ev, &s->event);schedule_work(&s->tqueue); } while(0)
+
+#define  MON0_RX	1
+#define  MON1_RX	2
+#define  MON0_TX	4
+#define  MON1_TX	8
+
+
+#ifdef ISDN_CHIP_ISAC
+#undef ISDN_CHIP_ISAC
+#endif
+
+#ifdef	CONFIG_HISAX_16_0
+#define  CARD_TELES0 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_TELES0  0
+#endif
+
+#ifdef	CONFIG_HISAX_16_3
+#define  CARD_TELES3 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_TELES3  0
+#endif
+
+#ifdef	CONFIG_HISAX_TELESPCI
+#define  CARD_TELESPCI 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_TELESPCI  0
+#endif
+
+#ifdef	CONFIG_HISAX_AVM_A1
+#define  CARD_AVM_A1 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_AVM_A1  0
+#endif
+
+#ifdef	CONFIG_HISAX_AVM_A1_PCMCIA
+#define  CARD_AVM_A1_PCMCIA 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_AVM_A1_PCMCIA  0
+#endif
+
+#ifdef	CONFIG_HISAX_FRITZPCI
+#define  CARD_FRITZPCI 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_FRITZPCI  0
+#endif
+
+#ifdef	CONFIG_HISAX_ELSA
+#define  CARD_ELSA 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_ELSA  0
+#endif
+
+#ifdef	CONFIG_HISAX_IX1MICROR2
+#define	CARD_IX1MICROR2 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_IX1MICROR2 0
+#endif
+
+#ifdef  CONFIG_HISAX_DIEHLDIVA
+#define CARD_DIEHLDIVA 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_DIEHLDIVA 0
+#endif
+
+#ifdef  CONFIG_HISAX_ASUSCOM
+#define CARD_ASUSCOM 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_ASUSCOM 0
+#endif
+
+#ifdef  CONFIG_HISAX_TELEINT
+#define CARD_TELEINT 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_TELEINT 0
+#endif
+
+#ifdef  CONFIG_HISAX_SEDLBAUER
+#define CARD_SEDLBAUER 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_SEDLBAUER 0
+#endif
+
+#ifdef  CONFIG_HISAX_SPORTSTER
+#define CARD_SPORTSTER 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_SPORTSTER 0
+#endif
+
+#ifdef  CONFIG_HISAX_MIC
+#define CARD_MIC 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_MIC 0
+#endif
+
+#ifdef  CONFIG_HISAX_NETJET
+#define CARD_NETJET_S 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_NETJET_S 0
+#endif
+
+#ifdef	CONFIG_HISAX_HFCS
+#define  CARD_HFCS 1
+#else
+#define  CARD_HFCS 0
+#endif
+
+#ifdef	CONFIG_HISAX_HFC_PCI
+#define  CARD_HFC_PCI 1
+#else
+#define  CARD_HFC_PCI 0
+#endif
+
+#ifdef	CONFIG_HISAX_HFC_SX
+#define  CARD_HFC_SX 1
+#else
+#define  CARD_HFC_SX 0
+#endif
+
+#ifdef  CONFIG_HISAX_AMD7930
+#define CARD_AMD7930 1
+#else
+#define CARD_AMD7930 0
+#endif
+
+#ifdef	CONFIG_HISAX_NICCY
+#define	CARD_NICCY 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_NICCY 0
+#endif
+
+#ifdef	CONFIG_HISAX_ISURF
+#define	CARD_ISURF 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_ISURF 0
+#endif
+
+#ifdef	CONFIG_HISAX_S0BOX
+#define	CARD_S0BOX 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_S0BOX 0
+#endif
+
+#ifdef	CONFIG_HISAX_HSTSAPHIR
+#define	CARD_HSTSAPHIR 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_HSTSAPHIR 0
+#endif
+
+#ifdef	CONFIG_HISAX_TESTEMU
+#define	CARD_TESTEMU 1
+#define ISDN_CTYPE_TESTEMU 99
+#undef ISDN_CTYPE_COUNT
+#define  ISDN_CTYPE_COUNT ISDN_CTYPE_TESTEMU
+#else
+#define CARD_TESTEMU 0
+#endif
+
+#ifdef	CONFIG_HISAX_BKM_A4T
+#define	CARD_BKM_A4T 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_BKM_A4T 0
+#endif
+
+#ifdef	CONFIG_HISAX_SCT_QUADRO
+#define	CARD_SCT_QUADRO 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_SCT_QUADRO 0
+#endif
+
+#ifdef	CONFIG_HISAX_GAZEL
+#define  CARD_GAZEL 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_GAZEL  0
+#endif
+
+#ifdef	CONFIG_HISAX_W6692
+#define	CARD_W6692	1
+#ifndef	ISDN_CHIP_W6692
+#define	ISDN_CHIP_W6692	1
+#endif
+#else
+#define	CARD_W6692	0
+#endif
+
+#ifdef  CONFIG_HISAX_NETJET_U
+#define CARD_NETJET_U 1
+#ifndef ISDN_CHIP_ICC
+#define ISDN_CHIP_ICC 1
+#endif
+#ifndef HISAX_UINTERFACE
+#define HISAX_UINTERFACE 1
+#endif
+#else
+#define CARD_NETJET_U 0
+#endif
+
+#ifdef CONFIG_HISAX_ENTERNOW_PCI
+#define CARD_FN_ENTERNOW_PCI 1
+#endif
+
+#define TEI_PER_CARD 1
+
+/* 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
+
+#define L2FRAME_DEBUG
+
+#ifdef L2FRAME_DEBUG
+extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir);
+#endif
+
+#include "hisax_cfg.h"
+
+void init_bcstate(struct IsdnCardState *cs, int bc);
+
+void setstack_HiSax(struct PStack *st, struct IsdnCardState *cs);
+unsigned int random_ri(void);
+void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st);
+void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st);
+
+void setstack_l1_B(struct PStack *st);
+
+void setstack_tei(struct PStack *st);
+void setstack_manager(struct PStack *st);
+
+void setstack_isdnl2(struct PStack *st, char *debug_id);
+void releasestack_isdnl2(struct PStack *st);
+void setstack_transl2(struct PStack *st);
+void releasestack_transl2(struct PStack *st);
+void lli_writewakeup(struct PStack *st, int len);
+
+void setstack_l3dc(struct PStack *st, struct Channel *chanp);
+void setstack_l3bc(struct PStack *st, struct Channel *chanp);
+void releasestack_isdnl3(struct PStack *st);
+
+u_char *findie(u_char * p, int size, u_char ie, int wanted_set);
+int getcallref(u_char * p);
+int newcallref(void);
+
+int FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount);
+void FsmFree(struct Fsm *fsm);
+int FsmEvent(struct FsmInst *fi, int event, void *arg);
+void FsmChangeState(struct FsmInst *fi, int newstate);
+void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
+int FsmAddTimer(struct FsmTimer *ft, int millisec, int event,
+	void *arg, int where);
+void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event,
+	void *arg, int where);
+void FsmDelTimer(struct FsmTimer *ft, int where);
+int jiftime(char *s, long mark);
+
+int HiSax_command(isdn_ctrl * ic);
+int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb);
+void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...);
+void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args);
+void HiSax_reportcard(int cardnr, int sel);
+int QuickHex(char *txt, u_char * p, int cnt);
+void LogFrame(struct IsdnCardState *cs, u_char * p, int size);
+void dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir);
+void iecpy(u_char * dest, u_char * iestart, int ieoffset);
+#ifdef ISDN_CHIP_ISAC
+void setstack_isac(struct PStack *st, struct IsdnCardState *cs);
+#endif	/* ISDN_CHIP_ISAC */
+#endif	/* __KERNEL__ */
+
+#define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);}
+
+int ll_run(struct IsdnCardState *cs, int addfeatures);
+void ll_stop(struct IsdnCardState *cs);
+int CallcNew(void);
+void CallcFree(void);
+int CallcNewChan(struct IsdnCardState *cs);
+void CallcFreeChan(struct IsdnCardState *cs);
+int Isdnl1New(void);
+void Isdnl1Free(void);
+int Isdnl2New(void);
+void Isdnl2Free(void);
+int Isdnl3New(void);
+void Isdnl3Free(void);
+void init_tei(struct IsdnCardState *cs, int protocol);
+void release_tei(struct IsdnCardState *cs);
+char *HiSax_getrev(const char *revision);
+int TeiNew(void);
+void TeiFree(void);
diff --git a/drivers/isdn/hisax/hisax_cfg.h b/drivers/isdn/hisax/hisax_cfg.h
new file mode 100644
index 000000000000..ca3fe6259bca
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_cfg.h
@@ -0,0 +1,64 @@
+/* $Id: hisax_cfg.h,v 1.1.2.1 2004/01/24 20:47:23 keil Exp $
+ * define of the basic HiSax configuration structures
+ * and pcmcia interface
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define ISDN_CTYPE_16_0			1
+#define ISDN_CTYPE_8_0			2
+#define ISDN_CTYPE_16_3			3
+#define ISDN_CTYPE_PNP			4
+#define ISDN_CTYPE_A1			5
+#define ISDN_CTYPE_ELSA			6
+#define ISDN_CTYPE_ELSA_PNP		7
+#define ISDN_CTYPE_TELESPCMCIA		8
+#define ISDN_CTYPE_IX1MICROR2		9
+#define ISDN_CTYPE_ELSA_PCMCIA		10
+#define ISDN_CTYPE_DIEHLDIVA		11
+#define ISDN_CTYPE_ASUSCOM		12
+#define ISDN_CTYPE_TELEINT		13
+#define ISDN_CTYPE_TELES3C		14
+#define ISDN_CTYPE_SEDLBAUER		15
+#define ISDN_CTYPE_SPORTSTER		16
+#define ISDN_CTYPE_MIC			17
+#define ISDN_CTYPE_ELSA_PCI		18
+#define ISDN_CTYPE_COMPAQ_ISA		19
+#define ISDN_CTYPE_NETJET_S		20
+#define ISDN_CTYPE_TELESPCI		21
+#define ISDN_CTYPE_SEDLBAUER_PCMCIA	22
+#define ISDN_CTYPE_AMD7930		23
+#define ISDN_CTYPE_NICCY		24
+#define ISDN_CTYPE_S0BOX		25
+#define ISDN_CTYPE_A1_PCMCIA		26
+#define ISDN_CTYPE_FRITZPCI		27
+#define ISDN_CTYPE_SEDLBAUER_FAX	28
+#define ISDN_CTYPE_ISURF		29
+#define ISDN_CTYPE_ACERP10		30
+#define ISDN_CTYPE_HSTSAPHIR		31
+#define	ISDN_CTYPE_BKM_A4T		32
+#define	ISDN_CTYPE_SCT_QUADRO		33
+#define ISDN_CTYPE_GAZEL		34
+#define ISDN_CTYPE_HFC_PCI		35
+#define ISDN_CTYPE_W6692		36
+#define ISDN_CTYPE_HFC_SX		37
+#define ISDN_CTYPE_NETJET_U		38
+#define ISDN_CTYPE_HFC_SP_PCMCIA	39
+#define ISDN_CTYPE_DYNAMIC		40
+#define ISDN_CTYPE_ENTERNOW		41
+#define ISDN_CTYPE_COUNT		41
+
+typedef struct IsdnCardState	IsdnCardState_t;
+typedef struct IsdnCard		IsdnCard_t;
+
+struct IsdnCard {
+	int		typ;
+	int 		protocol;	/* EDSS1, 1TR6 or NI1 */
+	unsigned long	para[4];
+	IsdnCardState_t	*cs;
+};
+
+extern void	HiSax_closecard(int);
+extern int	hisax_init_pcmcia(void *, int *, IsdnCard_t *);
diff --git a/drivers/isdn/hisax/hisax_debug.h b/drivers/isdn/hisax/hisax_debug.h
new file mode 100644
index 000000000000..ba518a7a7fb7
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_debug.h
@@ -0,0 +1,81 @@
+/*
+ * Common debugging macros for use with the hisax driver
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * How to use:
+ * 
+ * Before including this file, you need to
+ *   #define __debug_variable my_debug
+ * where my_debug is a variable in your code which
+ * determines the debug bitmask.
+ *
+ * If CONFIG_HISAX_DEBUG is not set, all macros evaluate to nothing
+ *
+ */
+
+#ifndef __HISAX_DEBUG_H__
+#define __HISAX_DEBUG_H__
+
+#include <linux/config.h>
+
+#ifdef CONFIG_HISAX_DEBUG
+
+#define DBG(level, format, arg...) do { \
+if (level & __debug_variable) \
+printk(KERN_DEBUG "%s: " format "\n" , __FUNCTION__ , ## arg); \
+} while (0)
+
+#define DBG_PACKET(level,data,count) \
+  if (level & __debug_variable) dump_packet(__FUNCTION__,data,count)
+
+#define DBG_SKB(level,skb) \
+  if ((level & __debug_variable) && skb) dump_packet(__FUNCTION__,skb->data,skb->len)
+
+
+static void __attribute__((unused))
+dump_packet(const char *name,const u_char *data,int pkt_len)
+{
+#define DUMP_HDR_SIZE 20
+#define DUMP_TLR_SIZE 8
+	if (pkt_len) {
+		int i,len1,len2;
+
+		printk(KERN_DEBUG "%s: length=%d,data=",name,pkt_len);
+
+		if (pkt_len >  DUMP_HDR_SIZE+ DUMP_TLR_SIZE) {
+			len1 = DUMP_HDR_SIZE;
+			len2 = DUMP_TLR_SIZE;
+		} else {
+			len1 = pkt_len > DUMP_HDR_SIZE ? DUMP_HDR_SIZE : pkt_len;
+			len2 = 0;			
+		}
+		for (i = 0; i < len1; ++i) {
+		 	printk ("%.2x", data[i]);
+		}
+		if (len2) {
+		 	printk ("..");
+			for (i = pkt_len-DUMP_TLR_SIZE; i < pkt_len; ++i) {
+				printk ("%.2x", data[i]);
+			}
+		}
+		printk ("\n");
+	}
+#undef DUMP_HDR_SIZE
+#undef DUMP_TLR_SIZE
+}
+
+#else
+
+#define DBG(level, format, arg...) do {} while (0)
+#define DBG_PACKET(level,data,count) do {} while (0)
+#define DBG_SKB(level,skb) do {} while (0)
+
+#endif
+
+#endif
diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c
new file mode 100644
index 000000000000..b4d795d40154
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_fcpcipnp.c
@@ -0,0 +1,1028 @@
+/*
+ * Driver for AVM Fritz!PCI, Fritz!PCI v2, Fritz!PnP ISDN cards
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *              2001 by Karsten Keil       <keil@isdn4linux.de>
+ * 
+ * based upon Karsten Keil's original avm_pci.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Wizard Computersysteme GmbH, Bremervoerde and
+ *           SoHaNet Technology GmbH, Berlin
+ * for supporting the development of this driver
+ */
+
+
+/* TODO:
+ *
+ * o POWER PC
+ * o clean up debugging
+ * o tx_skb at PH_DEACTIVATE time
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+
+#include "hisax_fcpcipnp.h"
+
+// debugging cruft
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 0;
+/* static int hdlcfifosize = 32; */
+module_param(debug, int, 0);
+/* module_param(hdlcfifosize, int, 0); */
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("AVM Fritz!PCI/PnP ISDN driver");
+
+static struct pci_device_id fcpci_ids[] = {
+	{ .vendor      = PCI_VENDOR_ID_AVM,
+	  .device      = PCI_DEVICE_ID_AVM_A1,
+	  .subvendor   = PCI_ANY_ID,
+	  .subdevice   = PCI_ANY_ID,
+	  .driver_data = (unsigned long) "Fritz!Card PCI",
+	},
+	{ .vendor      = PCI_VENDOR_ID_AVM,
+	  .device      = PCI_DEVICE_ID_AVM_A1_V2,
+	  .subvendor   = PCI_ANY_ID,
+	  .subdevice   = PCI_ANY_ID,
+	  .driver_data = (unsigned long) "Fritz!Card PCI v2" },
+	{}
+};
+
+MODULE_DEVICE_TABLE(pci, fcpci_ids);
+
+#ifdef __ISAPNP__
+static struct pnp_device_id fcpnp_ids[] __devinitdata = {
+	{ 
+		.id		= "AVM0900",
+		.driver_data	= (unsigned long) "Fritz!Card PnP",
+	},
+};
+
+MODULE_DEVICE_TABLE(isapnp, fcpnp_ids);
+#endif
+
+static int protocol = 2;       /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+MODULE_LICENSE("GPL");
+
+// ----------------------------------------------------------------------
+
+#define  AVM_INDEX              0x04
+#define  AVM_DATA               0x10
+
+#define	 AVM_IDX_HDLC_1		0x00
+#define	 AVM_IDX_HDLC_2		0x01
+#define	 AVM_IDX_ISAC_FIFO	0x02
+#define	 AVM_IDX_ISAC_REG_LOW	0x04
+#define	 AVM_IDX_ISAC_REG_HIGH	0x06
+
+#define  AVM_STATUS0            0x02
+
+#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            0x03
+#define  AVM_STATUS1_ENA_IOM	0x80
+
+#define  HDLC_FIFO		0x0
+#define  HDLC_STATUS		0x4
+#define  HDLC_CTRL		0x4
+
+#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	0xff00
+
+#define  HDLC_CMD_XRS		0x80
+#define  HDLC_CMD_XME		0x01
+#define  HDLC_CMD_RRS		0x20
+#define  HDLC_CMD_XML_MASK	0xff00
+
+#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
+
+// ----------------------------------------------------------------------
+// Fritz!PCI
+
+static unsigned char fcpci_read_isac(struct isac *isac, unsigned char offset)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned char idx = (offset > 0x2f) ? 
+		AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW;
+	unsigned char val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(idx, adapter->io + AVM_INDEX);
+	val = inb(adapter->io + AVM_DATA + (offset & 0xf));
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, val);
+	return val;
+}
+
+static void fcpci_write_isac(struct isac *isac, unsigned char offset,
+			     unsigned char value)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned char idx = (offset > 0x2f) ? 
+		AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW;
+	unsigned long flags;
+
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, value);
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(idx, adapter->io + AVM_INDEX);
+	outb(value, adapter->io + AVM_DATA + (offset & 0xf));
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci_read_isac_fifo(struct isac *isac, unsigned char * data, 
+				 int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX);
+	insb(adapter->io + AVM_DATA, data, size);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci_write_isac_fifo(struct isac *isac, unsigned char * data, 
+				  int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX);
+	outsb(adapter->io + AVM_DATA, data, size);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static u32 fcpci_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+	u32 val;
+	int idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(idx, adapter->io + AVM_INDEX);
+	val = inl(adapter->io + AVM_DATA + HDLC_STATUS);
+	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+	return val;
+}
+
+static void __fcpci_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	int idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+	DBG(0x40, "hdlc %c wr%x ctrl %x",
+	    'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+	outl(idx, adapter->io + AVM_INDEX);
+	outl(bcs->ctrl.ctrl, adapter->io + AVM_DATA + HDLC_CTRL);
+}
+
+static void fcpci_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	__fcpci_write_ctrl(bcs, which);
+	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+// ----------------------------------------------------------------------
+// Fritz!PCI v2
+
+static unsigned char fcpci2_read_isac(struct isac *isac, unsigned char offset)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned char val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(offset, adapter->io + AVM_ISACSX_INDEX);
+	val = inl(adapter->io + AVM_ISACSX_DATA);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, val);
+
+	return val;
+}
+
+static void fcpci2_write_isac(struct isac *isac, unsigned char offset, 
+			      unsigned char value)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned long flags;
+
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, value);
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(offset, adapter->io + AVM_ISACSX_INDEX);
+	outl(value, adapter->io + AVM_ISACSX_DATA);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci2_read_isac_fifo(struct isac *isac, unsigned char * data, 
+				  int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(0, adapter->io + AVM_ISACSX_INDEX);
+	for (i = 0; i < size; i++)
+		data[i] = inl(adapter->io + AVM_ISACSX_DATA);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci2_write_isac_fifo(struct isac *isac, unsigned char * data, 
+				   int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(0, adapter->io + AVM_ISACSX_INDEX);
+	for (i = 0; i < size; i++)
+		outl(data[i], adapter->io + AVM_ISACSX_DATA);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static u32 fcpci2_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+	int offset = nr ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1;
+
+	return inl(adapter->io + offset);
+}
+
+static void fcpci2_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	int offset = bcs->channel ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1;
+
+	DBG(0x40, "hdlc %c wr%x ctrl %x",
+	    'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+	outl(bcs->ctrl.ctrl, adapter->io + offset);
+}
+
+// ----------------------------------------------------------------------
+// Fritz!PnP (ISAC access as for Fritz!PCI)
+
+static u32 fcpnp_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+	unsigned char idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+	u32 val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(idx, adapter->io + AVM_INDEX);
+	val = inb(adapter->io + AVM_DATA + HDLC_STATUS);
+	if (val & HDLC_INT_RPR)
+		val |= inb(adapter->io + AVM_DATA + HDLC_STATUS + 1) << 8;
+	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+	return val;
+}
+
+static void __fcpnp_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+	DBG(0x40, "hdlc %c wr%x ctrl %x",
+	    'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+	outb(idx, adapter->io + AVM_INDEX);
+	if (which & 4)
+		outb(bcs->ctrl.sr.mode, 
+		     adapter->io + AVM_DATA + HDLC_STATUS + 2);
+	if (which & 2)
+		outb(bcs->ctrl.sr.xml, 
+		     adapter->io + AVM_DATA + HDLC_STATUS + 1);
+	if (which & 1)
+		outb(bcs->ctrl.sr.cmd,
+		     adapter->io + AVM_DATA + HDLC_STATUS + 0);
+}
+
+static void fcpnp_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	__fcpnp_write_ctrl(bcs, which);
+	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+// ----------------------------------------------------------------------
+
+static inline void B_L1L2(struct fritz_bcs *bcs, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if;
+
+	DBG(2, "pr %#x", pr);
+	ifc->l1l2(ifc, pr, arg);
+}
+
+static void hdlc_fill_fifo(struct fritz_bcs *bcs)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	struct sk_buff *skb = bcs->tx_skb;
+	int count;
+	unsigned long flags;
+	unsigned char *p;
+
+	DBG(0x40, "hdlc_fill_fifo");
+
+	if (skb->len == 0)
+		BUG();
+
+	bcs->ctrl.sr.cmd &= ~HDLC_CMD_XME;
+	if (bcs->tx_skb->len > bcs->fifo_size) {
+		count = bcs->fifo_size;
+	} else {
+		count = bcs->tx_skb->len;
+		if (bcs->mode != L1_MODE_TRANS)
+			bcs->ctrl.sr.cmd |= HDLC_CMD_XME;
+	}
+	DBG(0x40, "hdlc_fill_fifo %d/%d", count, bcs->tx_skb->len);
+	p = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt += count;
+	bcs->ctrl.sr.xml = ((count == bcs->fifo_size) ? 0 : count);
+
+	switch (adapter->type) {
+	case AVM_FRITZ_PCI:
+		spin_lock_irqsave(&adapter->hw_lock, flags);
+		// sets the correct AVM_INDEX, too
+		__fcpci_write_ctrl(bcs, 3);
+		outsl(adapter->io + AVM_DATA + HDLC_FIFO,
+		      p, (count + 3) / 4);
+		spin_unlock_irqrestore(&adapter->hw_lock, flags);
+		break;
+	case AVM_FRITZ_PCIV2:
+		fcpci2_write_ctrl(bcs, 3);
+		outsl(adapter->io + 
+		      (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1),
+		      p, (count + 3) / 4);
+		break;
+	case AVM_FRITZ_PNP:
+		spin_lock_irqsave(&adapter->hw_lock, flags);
+		// sets the correct AVM_INDEX, too
+		__fcpnp_write_ctrl(bcs, 3);
+		outsb(adapter->io + AVM_DATA, p, count);
+		spin_unlock_irqrestore(&adapter->hw_lock, flags);
+		break;
+	}
+}
+
+static inline void hdlc_empty_fifo(struct fritz_bcs *bcs, int count)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	unsigned char *p;
+	unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+	DBG(0x10, "hdlc_empty_fifo %d", count);
+	if (bcs->rcvidx + count > HSCX_BUFMAX) {
+		DBG(0x10, "hdlc_empty_fifo: incoming packet too large");
+		return;
+	}
+	p = bcs->rcvbuf + bcs->rcvidx;
+	bcs->rcvidx += count;
+	switch (adapter->type) {
+	case AVM_FRITZ_PCI:
+		spin_lock(&adapter->hw_lock);
+		outl(idx, adapter->io + AVM_INDEX);
+		insl(adapter->io + AVM_DATA + HDLC_FIFO, 
+		     p, (count + 3) / 4);
+		spin_unlock(&adapter->hw_lock);
+		break;
+	case AVM_FRITZ_PCIV2:
+		insl(adapter->io + 
+		     (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1),
+		     p, (count + 3) / 4);
+		break;
+	case AVM_FRITZ_PNP:
+		spin_lock(&adapter->hw_lock);
+		outb(idx, adapter->io + AVM_INDEX);
+		insb(adapter->io + AVM_DATA, p, count);
+		spin_unlock(&adapter->hw_lock);
+		break;
+	}
+}
+
+static inline void hdlc_rpr_irq(struct fritz_bcs *bcs, u32 stat)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	struct sk_buff *skb;
+	int len;
+
+	if (stat & HDLC_STAT_RDO) {
+		DBG(0x10, "RDO");
+		bcs->ctrl.sr.xml = 0;
+		bcs->ctrl.sr.cmd |= HDLC_CMD_RRS;
+		adapter->write_ctrl(bcs, 1);
+		bcs->ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+		adapter->write_ctrl(bcs, 1);
+		bcs->rcvidx = 0;
+		return;
+	}
+
+	len = (stat & HDLC_STAT_RML_MASK) >> 8;
+	if (len == 0)
+		len = bcs->fifo_size;
+
+	hdlc_empty_fifo(bcs, len);
+
+	if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) {
+		if (((stat & HDLC_STAT_CRCVFRRAB)== HDLC_STAT_CRCVFR) ||
+		    (bcs->mode == L1_MODE_TRANS)) {
+			skb = dev_alloc_skb(bcs->rcvidx);
+			if (!skb) {
+				printk(KERN_WARNING "HDLC: receive out of memory\n");
+			} else {
+				memcpy(skb_put(skb, bcs->rcvidx), bcs->rcvbuf,
+				       bcs->rcvidx);
+				DBG_SKB(1, skb);
+				B_L1L2(bcs, PH_DATA | INDICATION, skb);
+			}
+			bcs->rcvidx = 0;
+		} else {
+			DBG(0x10, "ch%d invalid frame %#x",
+			    bcs->channel, stat);
+			bcs->rcvidx = 0;
+		}
+	}
+}
+
+static inline void hdlc_xdu_irq(struct fritz_bcs *bcs)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	
+
+	/* Here we lost an TX interrupt, so
+	 * restart transmitting the whole frame.
+	 */
+	bcs->ctrl.sr.xml = 0;
+	bcs->ctrl.sr.cmd |= HDLC_CMD_XRS;
+	adapter->write_ctrl(bcs, 1);
+	bcs->ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+
+	if (!bcs->tx_skb) {
+		DBG(0x10, "XDU without skb");
+		adapter->write_ctrl(bcs, 1);
+		return;
+	}
+	/* only hdlc restarts the frame, transparent mode must continue */
+	if (bcs->mode == L1_MODE_HDLC) {
+		skb_push(bcs->tx_skb, bcs->tx_cnt);
+		bcs->tx_cnt = 0;
+	}
+}
+
+static inline void hdlc_xpr_irq(struct fritz_bcs *bcs)
+{
+	struct sk_buff *skb;
+
+	skb = bcs->tx_skb;
+	if (!skb)
+		return;
+
+	if (skb->len) {
+		hdlc_fill_fifo(bcs);
+		return;
+	}
+	bcs->tx_cnt = 0;
+	bcs->tx_skb = NULL;
+	B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize);
+	dev_kfree_skb_irq(skb);
+}
+
+static void hdlc_irq_one(struct fritz_bcs *bcs, u32 stat)
+{
+	DBG(0x10, "ch%d stat %#x", bcs->channel, stat);
+	if (stat & HDLC_INT_RPR) {
+		DBG(0x10, "RPR");
+		hdlc_rpr_irq(bcs, stat);
+	}
+	if (stat & HDLC_INT_XDU) {
+		DBG(0x10, "XDU");
+		hdlc_xdu_irq(bcs);
+		hdlc_xpr_irq(bcs);
+		return;
+	}
+	if (stat & HDLC_INT_XPR) {
+		DBG(0x10, "XPR");
+		hdlc_xpr_irq(bcs);
+	}
+}
+
+static inline void hdlc_irq(struct fritz_adapter *adapter)
+{
+	int nr;
+	u32 stat;
+
+	for (nr = 0; nr < 2; nr++) {
+		stat = adapter->read_hdlc_status(adapter, nr);
+		DBG(0x10, "HDLC %c stat %#x", 'A' + nr, stat);
+		if (stat & HDLC_INT_MASK)
+			hdlc_irq_one(&adapter->bcs[nr], stat);
+	}
+}
+
+static void modehdlc(struct fritz_bcs *bcs, int mode)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	
+	DBG(0x40, "hdlc %c mode %d --> %d",
+	    'A' + bcs->channel, bcs->mode, mode);
+
+	if (bcs->mode == mode)
+		return;
+
+	bcs->fifo_size = 32;
+	bcs->ctrl.ctrl = 0;
+	bcs->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+	switch (mode) {
+	case L1_MODE_NULL:
+		bcs->ctrl.sr.mode = HDLC_MODE_TRANS;
+		adapter->write_ctrl(bcs, 5);
+		break;
+	case L1_MODE_TRANS:
+	case L1_MODE_HDLC:
+		bcs->rcvidx = 0;
+		bcs->tx_cnt = 0;
+		bcs->tx_skb = NULL;
+		if (mode == L1_MODE_TRANS) {
+			bcs->ctrl.sr.mode = HDLC_MODE_TRANS;
+		} else {
+			bcs->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+		}
+		adapter->write_ctrl(bcs, 5);
+		bcs->ctrl.sr.cmd = HDLC_CMD_XRS;
+		adapter->write_ctrl(bcs, 1);
+		bcs->ctrl.sr.cmd = 0;
+		break;
+	}
+	bcs->mode = mode;
+}
+
+static void fritz_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+	struct fritz_bcs *bcs = ifc->priv;
+	struct sk_buff *skb = arg;
+	int mode;
+
+	DBG(0x10, "pr %#x", pr);
+
+	switch (pr) {
+	case PH_DATA | REQUEST:
+		if (bcs->tx_skb)
+			BUG();
+		
+		bcs->tx_skb = skb;
+		DBG_SKB(1, skb);
+		hdlc_fill_fifo(bcs);
+		break;
+	case PH_ACTIVATE | REQUEST:
+		mode = (int) arg;
+		DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode);
+		modehdlc(bcs, mode);
+		B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL);
+		break;
+	case PH_DEACTIVATE | REQUEST:
+		DBG(4,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1);
+		modehdlc(bcs, L1_MODE_NULL);
+		B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL);
+		break;
+	}
+}
+
+// ----------------------------------------------------------------------
+
+static irqreturn_t
+fcpci2_irq(int intno, void *dev, struct pt_regs *regs)
+{
+	struct fritz_adapter *adapter = dev;
+	unsigned char val;
+
+	val = inb(adapter->io + AVM_STATUS0);
+	if (!(val & AVM_STATUS0_IRQ_MASK))
+		/* hopefully a shared  IRQ reqest */
+		return IRQ_NONE;
+	DBG(2, "STATUS0 %#x", val);
+	if (val & AVM_STATUS0_IRQ_ISAC)
+		isacsx_irq(&adapter->isac);
+	if (val & AVM_STATUS0_IRQ_HDLC)
+		hdlc_irq(adapter);
+	if (val & AVM_STATUS0_IRQ_ISAC)
+		isacsx_irq(&adapter->isac);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+fcpci_irq(int intno, void *dev, struct pt_regs *regs)
+{
+	struct fritz_adapter *adapter = dev;
+	unsigned char sval;
+
+	sval = inb(adapter->io + 2);
+	if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK)
+		/* possibly a shared  IRQ reqest */
+		return IRQ_NONE;
+	DBG(2, "sval %#x", sval);
+	if (!(sval & AVM_STATUS0_IRQ_ISAC))
+		isac_irq(&adapter->isac);
+
+	if (!(sval & AVM_STATUS0_IRQ_HDLC))
+		hdlc_irq(adapter);
+	return IRQ_HANDLED;
+}
+
+// ----------------------------------------------------------------------
+
+static inline void fcpci2_init(struct fritz_adapter *adapter)
+{
+	outb(AVM_STATUS0_RES_TIMER, adapter->io + AVM_STATUS0);
+	outb(AVM_STATUS0_ENA_IRQ, adapter->io + AVM_STATUS0);
+
+}
+
+static inline void fcpci_init(struct fritz_adapter *adapter)
+{
+	outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | 
+	     AVM_STATUS0_ENA_IRQ, adapter->io + AVM_STATUS0);
+
+	outb(AVM_STATUS1_ENA_IOM | adapter->irq, 
+	     adapter->io + AVM_STATUS1);
+	mdelay(10);
+}
+
+// ----------------------------------------------------------------------
+
+static int __devinit fcpcipnp_setup(struct fritz_adapter *adapter)
+{
+	u32 val = 0;
+	int retval;
+
+	DBG(1,"");
+
+	isac_init(&adapter->isac); // FIXME is this okay now
+
+	retval = -EBUSY;
+	if (!request_region(adapter->io, 32, "fcpcipnp"))
+		goto err;
+
+	switch (adapter->type) {
+	case AVM_FRITZ_PCIV2:
+		retval = request_irq(adapter->irq, fcpci2_irq, SA_SHIRQ, 
+				     "fcpcipnp", adapter);
+		break;
+	case AVM_FRITZ_PCI:
+		retval = request_irq(adapter->irq, fcpci_irq, SA_SHIRQ,
+				     "fcpcipnp", adapter);
+		break;
+	case AVM_FRITZ_PNP:
+		retval = request_irq(adapter->irq, fcpci_irq, 0,
+				     "fcpcipnp", adapter);
+		break;
+	}
+	if (retval)
+		goto err_region;
+
+	switch (adapter->type) {
+	case AVM_FRITZ_PCIV2:
+	case AVM_FRITZ_PCI:
+		val = inl(adapter->io);
+		break;
+	case AVM_FRITZ_PNP:
+		val = inb(adapter->io);
+		val |= inb(adapter->io + 1) << 8;
+		break;
+	}
+
+	DBG(1, "stat %#x Class %X Rev %d",
+	    val, val & 0xff, (val>>8) & 0xff);
+
+	spin_lock_init(&adapter->hw_lock);
+	adapter->isac.priv = adapter;
+	switch (adapter->type) {
+	case AVM_FRITZ_PCIV2:
+		adapter->isac.read_isac       = &fcpci2_read_isac;
+		adapter->isac.write_isac      = &fcpci2_write_isac;
+		adapter->isac.read_isac_fifo  = &fcpci2_read_isac_fifo;
+		adapter->isac.write_isac_fifo = &fcpci2_write_isac_fifo;
+
+		adapter->read_hdlc_status     = &fcpci2_read_hdlc_status;
+		adapter->write_ctrl           = &fcpci2_write_ctrl;
+		break;
+	case AVM_FRITZ_PCI:
+		adapter->isac.read_isac       = &fcpci_read_isac;
+		adapter->isac.write_isac      = &fcpci_write_isac;
+		adapter->isac.read_isac_fifo  = &fcpci_read_isac_fifo;
+		adapter->isac.write_isac_fifo = &fcpci_write_isac_fifo;
+
+		adapter->read_hdlc_status     = &fcpci_read_hdlc_status;
+		adapter->write_ctrl           = &fcpci_write_ctrl;
+		break;
+	case AVM_FRITZ_PNP:
+		adapter->isac.read_isac       = &fcpci_read_isac;
+		adapter->isac.write_isac      = &fcpci_write_isac;
+		adapter->isac.read_isac_fifo  = &fcpci_read_isac_fifo;
+		adapter->isac.write_isac_fifo = &fcpci_write_isac_fifo;
+
+		adapter->read_hdlc_status     = &fcpnp_read_hdlc_status;
+		adapter->write_ctrl           = &fcpnp_write_ctrl;
+		break;
+	}
+
+	// Reset
+	outb(0, adapter->io + AVM_STATUS0);
+	mdelay(10);
+	outb(AVM_STATUS0_RESET, adapter->io + AVM_STATUS0);
+	mdelay(10);
+	outb(0, adapter->io + AVM_STATUS0);
+	mdelay(10);
+
+	switch (adapter->type) {
+	case AVM_FRITZ_PCIV2:
+		fcpci2_init(adapter);
+		isacsx_setup(&adapter->isac);
+		break;
+	case AVM_FRITZ_PCI:
+	case AVM_FRITZ_PNP:
+		fcpci_init(adapter);
+		isac_setup(&adapter->isac);
+		break;
+	}
+	val = adapter->read_hdlc_status(adapter, 0);
+	DBG(0x20, "HDLC A STA %x", val);
+	val = adapter->read_hdlc_status(adapter, 1);
+	DBG(0x20, "HDLC B STA %x", val);
+
+	adapter->bcs[0].mode = -1;
+	adapter->bcs[1].mode = -1;
+	modehdlc(&adapter->bcs[0], L1_MODE_NULL);
+	modehdlc(&adapter->bcs[1], L1_MODE_NULL);
+
+	return 0;
+
+ err_region:
+	release_region(adapter->io, 32);
+ err:
+	return retval;
+}
+
+static void __devexit fcpcipnp_release(struct fritz_adapter *adapter)
+{
+	DBG(1,"");
+
+	outb(0, adapter->io + AVM_STATUS0);
+	free_irq(adapter->irq, adapter);
+	release_region(adapter->io, 32);
+}
+
+// ----------------------------------------------------------------------
+
+static struct fritz_adapter * __devinit 
+new_adapter(void)
+{
+	struct fritz_adapter *adapter;
+	struct hisax_b_if *b_if[2];
+	int i;
+
+	adapter = kmalloc(sizeof(struct fritz_adapter), GFP_KERNEL);
+	if (!adapter)
+		return NULL;
+
+	memset(adapter, 0, sizeof(struct fritz_adapter));
+
+	adapter->isac.hisax_d_if.owner = THIS_MODULE;
+	adapter->isac.hisax_d_if.ifc.priv = &adapter->isac;
+	adapter->isac.hisax_d_if.ifc.l2l1 = isac_d_l2l1;
+	
+	for (i = 0; i < 2; i++) {
+		adapter->bcs[i].adapter = adapter;
+		adapter->bcs[i].channel = i;
+		adapter->bcs[i].b_if.ifc.priv = &adapter->bcs[i];
+		adapter->bcs[i].b_if.ifc.l2l1 = fritz_b_l2l1;
+	}
+
+	for (i = 0; i < 2; i++)
+		b_if[i] = &adapter->bcs[i].b_if;
+
+	hisax_register(&adapter->isac.hisax_d_if, b_if, "fcpcipnp", protocol);
+
+	return adapter;
+}
+
+static void delete_adapter(struct fritz_adapter *adapter)
+{
+	hisax_unregister(&adapter->isac.hisax_d_if);
+	kfree(adapter);
+}
+
+static int __devinit fcpci_probe(struct pci_dev *pdev,
+				 const struct pci_device_id *ent)
+{
+	struct fritz_adapter *adapter;
+	int retval;
+
+	retval = -ENOMEM;
+	adapter = new_adapter();
+	if (!adapter)
+		goto err;
+
+	pci_set_drvdata(pdev, adapter);
+
+	if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2) 
+		adapter->type = AVM_FRITZ_PCIV2;
+	else
+		adapter->type = AVM_FRITZ_PCI;
+
+	retval = pci_enable_device(pdev);
+	if (retval)
+		goto err_free;
+
+	adapter->io = pci_resource_start(pdev, 1);
+	adapter->irq = pdev->irq;
+
+	printk(KERN_INFO "hisax_fcpcipnp: found adapter %s at %s\n",
+	       (char *) ent->driver_data, pci_name(pdev));
+
+	retval = fcpcipnp_setup(adapter);
+	if (retval)
+		goto err_free;
+
+	return 0;
+	
+ err_free:
+	delete_adapter(adapter);
+ err:
+	return retval;
+}
+
+#ifdef __ISAPNP__
+static int __devinit fcpnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
+{
+	struct fritz_adapter *adapter;
+	int retval;
+
+	if (!pdev)
+		return(-ENODEV);
+
+	retval = -ENOMEM;
+	adapter = new_adapter();
+	if (!adapter)
+		goto err;
+
+	pnp_set_drvdata(pdev, adapter);
+
+	adapter->type = AVM_FRITZ_PNP;
+
+	pnp_disable_dev(pdev);
+	retval = pnp_activate_dev(pdev);
+	if (retval < 0) {
+		printk(KERN_WARNING "%s: pnp_activate_dev(%s) ret(%d)\n", __FUNCTION__,
+			(char *)dev_id->driver_data, retval);
+		goto err_free;
+	}
+	adapter->io = pnp_port_start(pdev, 0);
+	adapter->irq = pnp_irq(pdev, 0);
+
+	printk(KERN_INFO "hisax_fcpcipnp: found adapter %s at IO %#x irq %d\n",
+	       (char *) dev_id->driver_data, adapter->io, adapter->irq);
+
+	retval = fcpcipnp_setup(adapter);
+	if (retval)
+		goto err_free;
+
+	return 0;
+	
+ err_free:
+	delete_adapter(adapter);
+ err:
+	return retval;
+}
+
+static void __devexit fcpnp_remove(struct pnp_dev *pdev)
+{
+	struct fritz_adapter *adapter = pnp_get_drvdata(pdev);
+
+	if (adapter) {
+		fcpcipnp_release(adapter);
+		delete_adapter(adapter);
+	}
+	pnp_disable_dev(pdev);
+}
+
+static struct pnp_driver fcpnp_driver = {
+	.name		= "fcpnp",
+	.probe		= fcpnp_probe,
+	.remove		= __devexit_p(fcpnp_remove),
+	.id_table	= fcpnp_ids,
+};
+#endif
+
+static void __devexit fcpci_remove(struct pci_dev *pdev)
+{
+	struct fritz_adapter *adapter = pci_get_drvdata(pdev);
+
+	fcpcipnp_release(adapter);
+	pci_disable_device(pdev);
+	delete_adapter(adapter);
+}
+
+static struct pci_driver fcpci_driver = {
+	.name		= "fcpci",
+	.probe		= fcpci_probe,
+	.remove		= __devexit_p(fcpci_remove),
+	.id_table	= fcpci_ids,
+};
+
+static int __init hisax_fcpcipnp_init(void)
+{
+	int retval;
+
+	printk(KERN_INFO "hisax_fcpcipnp: Fritz!Card PCI/PCIv2/PnP ISDN driver v0.0.1\n");
+
+	retval = pci_register_driver(&fcpci_driver);
+	if (retval)
+		goto out;
+#ifdef __ISAPNP__
+	retval = pnp_register_driver(&fcpnp_driver);
+	if (retval < 0)
+		goto out_unregister_pci;
+#endif
+	return 0;
+
+ out_unregister_pci:
+	pci_unregister_driver(&fcpci_driver);
+ out:
+	return retval;
+}
+
+static void __exit hisax_fcpcipnp_exit(void)
+{
+#ifdef __ISAPNP__
+	pnp_unregister_driver(&fcpnp_driver);
+#endif
+	pci_unregister_driver(&fcpci_driver);
+}
+
+module_init(hisax_fcpcipnp_init);
+module_exit(hisax_fcpcipnp_exit);
diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.h b/drivers/isdn/hisax/hisax_fcpcipnp.h
new file mode 100644
index 000000000000..bd8a22e4d6a2
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_fcpcipnp.h
@@ -0,0 +1,58 @@
+#include "hisax_if.h"
+#include "hisax_isac.h"
+#include <linux/pci.h>
+
+#define HSCX_BUFMAX	4096
+
+enum {
+	AVM_FRITZ_PCI,
+	AVM_FRITZ_PNP,
+	AVM_FRITZ_PCIV2,
+};
+
+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
+};
+
+struct fritz_bcs {
+	struct hisax_b_if b_if;
+	struct fritz_adapter *adapter;
+	int mode;
+	int channel;
+
+	union {
+		u_int ctrl;
+		struct hdlc_stat_reg sr;
+	} ctrl;
+	u_int stat;
+	int rcvidx;
+	int fifo_size;
+	u_char rcvbuf[HSCX_BUFMAX]; /* B-Channel receive Buffer */
+	
+	int tx_cnt;		    /* B-Channel transmit counter */
+	struct sk_buff *tx_skb;     /* B-Channel transmit Buffer */
+};
+
+struct fritz_adapter {
+	int type;
+	spinlock_t hw_lock;
+	unsigned int io;
+	unsigned int irq;
+	struct isac isac;
+
+	struct fritz_bcs bcs[2];
+
+	u32  (*read_hdlc_status) (struct fritz_adapter *adapter, int nr);
+	void (*write_ctrl) (struct fritz_bcs *bcs, int which);
+};
+
diff --git a/drivers/isdn/hisax/hisax_if.h b/drivers/isdn/hisax/hisax_if.h
new file mode 100644
index 000000000000..4898fce2d509
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_if.h
@@ -0,0 +1,66 @@
+/*
+ * Interface between low level (hardware) drivers and 
+ * HiSax protocol stack
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __HISAX_IF_H__
+#define __HISAX_IF_H__
+
+#include <linux/skbuff.h>
+
+#define REQUEST		0
+#define CONFIRM		1
+#define INDICATION	2
+#define RESPONSE	3
+
+#define PH_ACTIVATE	0x0100
+#define PH_DEACTIVATE	0x0110
+#define PH_DATA		0x0120
+#define PH_PULL		0x0130
+#define PH_DATA_E	0x0140
+
+#define L1_MODE_NULL	0
+#define L1_MODE_TRANS	1
+#define L1_MODE_HDLC	2
+#define L1_MODE_EXTRN	3
+#define L1_MODE_HDLC_56K 4
+#define L1_MODE_MODEM	7
+#define L1_MODE_V32	8
+#define L1_MODE_FAX	9
+
+struct hisax_if {
+	void *priv; // private to driver
+	void (*l1l2)(struct hisax_if *, int pr, void *arg);
+	void (*l2l1)(struct hisax_if *, int pr, void *arg);
+};
+
+struct hisax_b_if {
+	struct hisax_if ifc;
+
+	// private to hisax
+	struct BCState *bcs;
+};
+
+struct hisax_d_if {
+	struct hisax_if ifc;
+
+	// private to hisax
+	struct module *owner;
+	struct IsdnCardState *cs;
+	struct hisax_b_if *b_if[2];
+	struct sk_buff_head erq;
+	long ph_state;
+};
+
+int hisax_register(struct hisax_d_if *hisax_if, struct hisax_b_if *b_if[],
+		   char *name, int protocol);
+void hisax_unregister(struct hisax_d_if *hisax_if);
+
+#endif
diff --git a/drivers/isdn/hisax/hisax_isac.c b/drivers/isdn/hisax/hisax_isac.c
new file mode 100644
index 000000000000..f4972f6c1f5d
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_isac.c
@@ -0,0 +1,897 @@
+/*
+ * Driver for ISAC-S and ISAC-SX 
+ * ISDN Subscriber Access Controller for Terminals
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *              2001 by Karsten Keil       <keil@isdn4linux.de>
+ * 
+ * based upon Karsten Keil's original isac.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Wizard Computersysteme GmbH, Bremervoerde and
+ *           SoHaNet Technology GmbH, Berlin
+ * for supporting the development of this driver
+ */
+
+/* TODO:
+ * specifically handle level vs edge triggered?
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include "hisax_isac.h"
+
+// debugging cruft
+
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 1;
+module_param(debug, int, 0);
+
+static char *ISACVer[] = {
+  "2086/2186 V1.1", 
+  "2085 B1", 
+  "2085 B2",
+  "2085 V2.3"
+};
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("ISAC/ISAC-SX driver");
+MODULE_LICENSE("GPL");
+
+#define DBG_WARN      0x0001
+#define DBG_IRQ       0x0002
+#define DBG_L1M       0x0004
+#define DBG_PR        0x0008
+#define DBG_RFIFO     0x0100
+#define DBG_RPACKET   0x0200
+#define DBG_XFIFO     0x1000
+#define DBG_XPACKET   0x2000
+
+// we need to distinguish ISAC-S and ISAC-SX
+#define TYPE_ISAC        0x00
+#define TYPE_ISACSX      0x01
+
+// registers etc.
+#define ISAC_MASK        0x20
+#define ISAC_ISTA        0x20
+#define ISAC_ISTA_EXI    0x01
+#define ISAC_ISTA_SIN    0x02
+#define ISAC_ISTA_CISQ   0x04
+#define ISAC_ISTA_XPR    0x10
+#define ISAC_ISTA_RSC    0x20
+#define ISAC_ISTA_RPF    0x40
+#define ISAC_ISTA_RME    0x80
+
+#define ISAC_STAR        0x21
+#define ISAC_CMDR        0x21
+#define ISAC_CMDR_XRES   0x01
+#define ISAC_CMDR_XME    0x02
+#define ISAC_CMDR_XTF    0x08
+#define ISAC_CMDR_RRES   0x40
+#define ISAC_CMDR_RMC    0x80
+
+#define ISAC_EXIR        0x24
+#define ISAC_EXIR_MOS    0x04
+#define ISAC_EXIR_XDU    0x40
+#define ISAC_EXIR_XMR    0x80
+
+#define ISAC_ADF2        0x39
+#define ISAC_SPCR        0x30
+#define ISAC_ADF1        0x38
+
+#define ISAC_CIR0        0x31
+#define ISAC_CIX0        0x31
+#define ISAC_CIR0_CIC0   0x02
+#define ISAC_CIR0_CIC1   0x01
+
+#define ISAC_CIR1        0x33
+#define ISAC_CIX1        0x33
+#define ISAC_STCR        0x37
+#define ISAC_MODE        0x22
+
+#define ISAC_RSTA        0x27
+#define ISAC_RSTA_RDO    0x40
+#define ISAC_RSTA_CRC    0x20
+#define ISAC_RSTA_RAB    0x10
+
+#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_RES    0x1
+#define ISAC_CMD_SSP    0x2
+#define ISAC_CMD_SCP    0x3
+#define ISAC_CMD_AR8    0x8
+#define ISAC_CMD_AR10   0x9
+#define ISAC_CMD_ARL    0xa
+#define ISAC_CMD_DI     0xf
+
+#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
+
+static struct Fsm l1fsm;
+
+enum {
+	ST_L1_RESET,
+	ST_L1_F3_PDOWN,
+	ST_L1_F3_PUP,
+	ST_L1_F3_PEND_DEACT,
+	ST_L1_F4,
+	ST_L1_F5,
+	ST_L1_F6,
+	ST_L1_F7,
+	ST_L1_F8,
+};
+
+#define L1_STATE_COUNT (ST_L1_F8+1)
+
+static char *strL1State[] =
+{
+	"ST_L1_RESET",
+	"ST_L1_F3_PDOWN",
+	"ST_L1_F3_PUP",
+	"ST_L1_F3_PEND_DEACT",
+	"ST_L1_F4",
+	"ST_L1_F5",
+	"ST_L1_F6",
+	"ST_L1_F7",
+	"ST_L1_F8",
+};
+
+enum {
+	EV_PH_DR,           // 0000
+	EV_PH_RES,          // 0001
+	EV_PH_TMA,          // 0010
+	EV_PH_SLD,          // 0011
+	EV_PH_RSY,          // 0100
+	EV_PH_DR6,          // 0101
+	EV_PH_EI,           // 0110
+	EV_PH_PU,           // 0111
+	EV_PH_AR,           // 1000
+	EV_PH_9,            // 1001
+	EV_PH_ARL,          // 1010
+	EV_PH_CVR,          // 1011
+	EV_PH_AI8,          // 1100
+	EV_PH_AI10,         // 1101
+	EV_PH_AIL,          // 1110
+	EV_PH_DC,           // 1111
+	EV_PH_ACTIVATE_REQ,
+	EV_PH_DEACTIVATE_REQ,
+	EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+static char *strL1Event[] =
+{
+	"EV_PH_DR",           // 0000
+	"EV_PH_RES",          // 0001
+	"EV_PH_TMA",          // 0010
+	"EV_PH_SLD",          // 0011
+	"EV_PH_RSY",          // 0100
+	"EV_PH_DR6",          // 0101
+	"EV_PH_EI",           // 0110
+	"EV_PH_PU",           // 0111
+	"EV_PH_AR",           // 1000
+	"EV_PH_9",            // 1001
+	"EV_PH_ARL",          // 1010
+	"EV_PH_CVR",          // 1011
+	"EV_PH_AI8",          // 1100
+	"EV_PH_AI10",         // 1101
+	"EV_PH_AIL",          // 1110
+	"EV_PH_DC",           // 1111
+	"EV_PH_ACTIVATE_REQ",
+	"EV_PH_DEACTIVATE_REQ",
+	"EV_TIMER3",
+};
+
+static inline void D_L1L2(struct isac *isac, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) &isac->hisax_d_if;
+
+	DBG(DBG_PR, "pr %#x", pr);
+	ifc->l1l2(ifc, pr, arg);
+}
+
+static void ph_command(struct isac *isac, unsigned int command)
+{
+	DBG(DBG_L1M, "ph_command %#x", command);
+	switch (isac->type) {
+	case TYPE_ISAC:
+		isac->write_isac(isac, ISAC_CIX0, (command << 2) | 3);
+		break;
+	case TYPE_ISACSX:
+		isac->write_isac(isac, ISACSX_CIX0, (command << 4) | (7 << 1));
+		break;
+	}
+}
+
+// ----------------------------------------------------------------------
+
+static void l1_di(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_RESET);
+	ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_di_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_RESET);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+	ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f3pdown(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F3_PDOWN);
+}
+
+static void l1_go_f3pend_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F3_PEND_DEACT);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+	ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f3pend(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F3_PEND_DEACT);
+	ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f4(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F4);
+}
+
+static void l1_go_f5(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F5);
+}
+
+static void l1_go_f6(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F6);
+}
+
+static void l1_go_f6_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F6);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void l1_go_f7_act_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmDelTimer(&isac->timer, 0);
+	FsmChangeState(fi, ST_L1_F7);
+	ph_command(isac, ISAC_CMD_AR8);
+	D_L1L2(isac, PH_ACTIVATE | INDICATION, NULL);
+}
+
+static void l1_go_f8(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F8);
+}
+
+static void l1_go_f8_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F8);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void l1_ar8(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmRestartTimer(&isac->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+	ph_command(isac, ISAC_CMD_AR8);
+}
+
+static void l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	ph_command(isac, ISAC_CMD_DI);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+// state machines according to data sheet PSB 2186 / 3186
+
+static struct FsmNode L1FnList[] __initdata =
+{
+	{ST_L1_RESET,         EV_PH_RES,            l1_di},
+	{ST_L1_RESET,         EV_PH_EI,             l1_di},
+	{ST_L1_RESET,         EV_PH_DC,             l1_go_f3pdown},
+	{ST_L1_RESET,         EV_PH_AR,             l1_go_f6},
+	{ST_L1_RESET,         EV_PH_AI8,            l1_go_f7_act_ind},
+
+	{ST_L1_F3_PDOWN,      EV_PH_RES,            l1_di},
+	{ST_L1_F3_PDOWN,      EV_PH_EI,             l1_di},
+	{ST_L1_F3_PDOWN,      EV_PH_AR,             l1_go_f6},
+	{ST_L1_F3_PDOWN,      EV_PH_RSY,            l1_go_f5},
+	{ST_L1_F3_PDOWN,      EV_PH_PU,             l1_go_f4},
+	{ST_L1_F3_PDOWN,      EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F3_PDOWN,      EV_PH_ACTIVATE_REQ,   l1_ar8},
+	{ST_L1_F3_PDOWN,      EV_TIMER3,            l1_timer3},
+	
+	{ST_L1_F3_PEND_DEACT, EV_PH_RES,            l1_di},
+	{ST_L1_F3_PEND_DEACT, EV_PH_EI,             l1_di},
+	{ST_L1_F3_PEND_DEACT, EV_PH_DC,             l1_go_f3pdown},
+	{ST_L1_F3_PEND_DEACT, EV_PH_RSY,            l1_go_f5},
+	{ST_L1_F3_PEND_DEACT, EV_PH_AR,             l1_go_f6},
+	{ST_L1_F3_PEND_DEACT, EV_PH_AI8,            l1_go_f7_act_ind},
+
+	{ST_L1_F4,            EV_PH_RES,            l1_di},
+	{ST_L1_F4,            EV_PH_EI,             l1_di},
+	{ST_L1_F4,            EV_PH_RSY,            l1_go_f5},
+	{ST_L1_F4,            EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F4,            EV_TIMER3,            l1_timer3},
+	{ST_L1_F4,            EV_PH_DC,             l1_go_f3pdown},
+
+	{ST_L1_F5,            EV_PH_RES,            l1_di},
+	{ST_L1_F5,            EV_PH_EI,             l1_di},
+	{ST_L1_F5,            EV_PH_AR,             l1_go_f6},
+	{ST_L1_F5,            EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F5,            EV_TIMER3,            l1_timer3},
+	{ST_L1_F5,            EV_PH_DR,             l1_go_f3pend},
+	{ST_L1_F5,            EV_PH_DC,             l1_go_f3pdown},
+
+	{ST_L1_F6,            EV_PH_RES,            l1_di},
+	{ST_L1_F6,            EV_PH_EI,             l1_di},
+	{ST_L1_F6,            EV_PH_RSY,            l1_go_f8},
+	{ST_L1_F6,            EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F6,            EV_PH_DR6,            l1_go_f3pend},
+	{ST_L1_F6,            EV_TIMER3,            l1_timer3},
+	{ST_L1_F6,            EV_PH_DC,             l1_go_f3pdown},
+
+	{ST_L1_F7,            EV_PH_RES,            l1_di_deact_ind},
+	{ST_L1_F7,            EV_PH_EI,             l1_di_deact_ind},
+	{ST_L1_F7,            EV_PH_AR,             l1_go_f6_deact_ind},
+	{ST_L1_F7,            EV_PH_RSY,            l1_go_f8_deact_ind},
+	{ST_L1_F7,            EV_PH_DR,             l1_go_f3pend_deact_ind},
+
+	{ST_L1_F8,            EV_PH_RES,            l1_di},
+	{ST_L1_F8,            EV_PH_EI,             l1_di},
+	{ST_L1_F8,            EV_PH_AR,             l1_go_f6},
+	{ST_L1_F8,            EV_PH_DR,             l1_go_f3pend},
+	{ST_L1_F8,            EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F8,            EV_TIMER3,            l1_timer3},
+	{ST_L1_F8,            EV_PH_DC,             l1_go_f3pdown},
+};
+
+static void l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	char buf[256];
+	
+	va_start(args, fmt);
+	vsprintf(buf, fmt, args);
+	DBG(DBG_L1M, "%s", buf);
+	va_end(args);
+}
+
+static void isac_version(struct isac *cs)
+{
+	int val;
+
+	val = cs->read_isac(cs, ISAC_RBCH);
+	DBG(1, "ISAC version (%x): %s", val, ISACVer[(val >> 5) & 3]);
+}
+
+static void isac_empty_fifo(struct isac *isac, int count)
+{
+	// this also works for isacsx, since
+	// CMDR(D) register works the same
+	u_char *ptr;
+
+	DBG(DBG_IRQ, "count %d", count);
+
+	if ((isac->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		DBG(DBG_WARN, "overrun %d", isac->rcvidx + count);
+		isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+		isac->rcvidx = 0;
+		return;
+	}
+	ptr = isac->rcvbuf + isac->rcvidx;
+	isac->rcvidx += count;
+	isac->read_isac_fifo(isac, ptr, count);
+	isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+	DBG_PACKET(DBG_RFIFO, ptr, count);
+}
+
+static void isac_fill_fifo(struct isac *isac)
+{
+	// this also works for isacsx, since
+	// CMDR(D) register works the same
+
+	int count;
+	unsigned char cmd;
+	u_char *ptr;
+
+	if (!isac->tx_skb)
+		BUG();
+
+	count = isac->tx_skb->len;
+	if (count <= 0)
+		BUG();
+
+	DBG(DBG_IRQ, "count %d", count);
+
+	if (count > 0x20) {
+		count = 0x20;
+		cmd = ISAC_CMDR_XTF;
+	} else {
+		cmd = ISAC_CMDR_XTF | ISAC_CMDR_XME;
+	}
+
+	ptr = isac->tx_skb->data;
+	skb_pull(isac->tx_skb, count);
+	isac->tx_cnt += count;
+	DBG_PACKET(DBG_XFIFO, ptr, count);
+	isac->write_isac_fifo(isac, ptr, count);
+	isac->write_isac(isac, ISAC_CMDR, cmd);
+}
+
+static void isac_retransmit(struct isac *isac)
+{
+	if (!isac->tx_skb) {
+		DBG(DBG_WARN, "no skb");
+		return;
+	}
+	skb_push(isac->tx_skb, isac->tx_cnt);
+	isac->tx_cnt = 0;
+}
+
+
+static inline void isac_cisq_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISAC_CIR0);
+	DBG(DBG_IRQ, "CIR0 %#x", val);
+	if (val & ISAC_CIR0_CIC0) {
+		DBG(DBG_IRQ, "CODR0 %#x", (val >> 2) & 0xf);
+		FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL);
+	}
+	if (val & ISAC_CIR0_CIC1) {
+		val = isac->read_isac(isac, ISAC_CIR1);
+		DBG(DBG_WARN, "ISAC CIR1 %#x", val );
+	}
+}
+
+static inline void isac_rme_interrupt(struct isac *isac)
+{
+	unsigned char val;
+	int count;
+	struct sk_buff *skb;
+	
+	val = isac->read_isac(isac, ISAC_RSTA);
+	if ((val & (ISAC_RSTA_RDO | ISAC_RSTA_CRC | ISAC_RSTA_RAB) )
+	     != ISAC_RSTA_CRC) {
+		DBG(DBG_WARN, "RSTA %#x, dropped", val);
+		isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+		goto out;
+	}
+
+	count = isac->read_isac(isac, ISAC_RBCL) & 0x1f;
+	DBG(DBG_IRQ, "RBCL %#x", count);
+	if (count == 0)
+		count = 0x20;
+
+	isac_empty_fifo(isac, count);
+	count = isac->rcvidx;
+	if (count < 1) {
+		DBG(DBG_WARN, "count %d < 1", count);
+		goto out;
+	}
+
+	skb = alloc_skb(count, GFP_ATOMIC);
+	if (!skb) {
+		DBG(DBG_WARN, "no memory, dropping\n");
+		goto out;
+	}
+	memcpy(skb_put(skb, count), isac->rcvbuf, count);
+	DBG_SKB(DBG_RPACKET, skb);
+	D_L1L2(isac, PH_DATA | INDICATION, skb);
+ out:
+	isac->rcvidx = 0;
+}
+
+static inline void isac_xpr_interrupt(struct isac *isac)
+{
+	if (!isac->tx_skb)
+		return;
+
+	if (isac->tx_skb->len > 0) {
+		isac_fill_fifo(isac);
+		return;
+	}
+	dev_kfree_skb_irq(isac->tx_skb);
+	isac->tx_cnt = 0;
+	isac->tx_skb = NULL;
+	D_L1L2(isac, PH_DATA | CONFIRM, NULL);
+}
+
+static inline void isac_exi_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISAC_EXIR);
+	DBG(2, "EXIR %#x", val);
+
+	if (val & ISAC_EXIR_XMR) {
+		DBG(DBG_WARN, "ISAC XMR");
+		isac_retransmit(isac);
+	}
+	if (val & ISAC_EXIR_XDU) {
+		DBG(DBG_WARN, "ISAC XDU");
+		isac_retransmit(isac);
+	}
+	if (val & ISAC_EXIR_MOS) {  /* MOS */
+		DBG(DBG_WARN, "MOS");
+		val = isac->read_isac(isac, ISAC_MOSR);
+		DBG(2, "ISAC MOSR %#x", val);
+	}
+}
+
+void isac_irq(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISAC_ISTA);
+	DBG(DBG_IRQ, "ISTA %#x", val);
+
+	if (val & ISAC_ISTA_EXI) {
+		DBG(DBG_IRQ, "EXI");
+		isac_exi_interrupt(isac);
+	}
+	if (val & ISAC_ISTA_XPR) {
+		DBG(DBG_IRQ, "XPR");
+		isac_xpr_interrupt(isac);
+	}
+	if (val & ISAC_ISTA_RME) {
+		DBG(DBG_IRQ, "RME");
+		isac_rme_interrupt(isac);
+	}
+	if (val & ISAC_ISTA_RPF) {
+		DBG(DBG_IRQ, "RPF");
+		isac_empty_fifo(isac, 0x20);
+	}
+	if (val & ISAC_ISTA_CISQ) {
+		DBG(DBG_IRQ, "CISQ");
+		isac_cisq_interrupt(isac);
+	}
+	if (val & ISAC_ISTA_RSC) {
+		DBG(DBG_WARN, "RSC");
+	}
+	if (val & ISAC_ISTA_SIN) {
+		DBG(DBG_WARN, "SIN");
+	}
+	isac->write_isac(isac, ISAC_MASK, 0xff);
+	isac->write_isac(isac, ISAC_MASK, 0x00);
+}
+
+// ======================================================================
+
+static inline void isacsx_cic_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISACSX_CIR0);
+	DBG(DBG_IRQ, "CIR0 %#x", val);
+	if (val & ISACSX_CIR0_CIC0) {
+		DBG(DBG_IRQ, "CODR0 %#x", val >> 4);
+		FsmEvent(&isac->l1m, val >> 4, NULL);
+	}
+}
+
+static inline void isacsx_rme_interrupt(struct isac *isac)
+{
+	int count;
+	struct sk_buff *skb;
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISACSX_RSTAD);
+	if ((val & (ISACSX_RSTAD_VFR | 
+		    ISACSX_RSTAD_RDO | 
+		    ISACSX_RSTAD_CRC | 
+		    ISACSX_RSTAD_RAB)) 
+	    != (ISACSX_RSTAD_VFR | ISACSX_RSTAD_CRC)) {
+		DBG(DBG_WARN, "RSTAD %#x, dropped", val);
+		isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
+		goto out;
+	}
+
+	count = isac->read_isac(isac, ISACSX_RBCLD) & 0x1f;
+	DBG(DBG_IRQ, "RBCLD %#x", count);
+	if (count == 0)
+		count = 0x20;
+
+	isac_empty_fifo(isac, count);
+	// strip trailing status byte
+	count = isac->rcvidx - 1;
+	if (count < 1) {
+		DBG(DBG_WARN, "count %d < 1", count);
+		goto out;
+	}
+
+	skb = dev_alloc_skb(count);
+	if (!skb) {
+		DBG(DBG_WARN, "no memory, dropping");
+		goto out;
+	}
+	memcpy(skb_put(skb, count), isac->rcvbuf, count);
+	DBG_SKB(DBG_RPACKET, skb);
+	D_L1L2(isac, PH_DATA | INDICATION, skb);
+ out:
+	isac->rcvidx = 0;
+}
+
+static inline void isacsx_xpr_interrupt(struct isac *isac)
+{
+	if (!isac->tx_skb)
+		return;
+
+	if (isac->tx_skb->len > 0) {
+		isac_fill_fifo(isac);
+		return;
+	}
+	dev_kfree_skb_irq(isac->tx_skb);
+	isac->tx_skb = NULL;
+	isac->tx_cnt = 0;
+	D_L1L2(isac, PH_DATA | CONFIRM, NULL);
+}
+
+static inline void isacsx_icd_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISACSX_ISTAD);
+	DBG(DBG_IRQ, "ISTAD %#x", val);
+	if (val & ISACSX_ISTAD_XDU) {
+		DBG(DBG_WARN, "ISTAD XDU");
+		isac_retransmit(isac);
+	}
+	if (val & ISACSX_ISTAD_XMR) {
+		DBG(DBG_WARN, "ISTAD XMR");
+		isac_retransmit(isac);
+	}
+	if (val & ISACSX_ISTAD_XPR) {
+		DBG(DBG_IRQ, "ISTAD XPR");
+		isacsx_xpr_interrupt(isac);
+	}
+	if (val & ISACSX_ISTAD_RFO) {
+		DBG(DBG_WARN, "ISTAD RFO");
+		isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
+	}
+	if (val & ISACSX_ISTAD_RME) {
+		DBG(DBG_IRQ, "ISTAD RME");
+		isacsx_rme_interrupt(isac);
+	}
+	if (val & ISACSX_ISTAD_RPF) {
+		DBG(DBG_IRQ, "ISTAD RPF");
+		isac_empty_fifo(isac, 0x20);
+	}
+}
+
+void isacsx_irq(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISACSX_ISTA);
+	DBG(DBG_IRQ, "ISTA %#x", val);
+
+	if (val & ISACSX_ISTA_ICD)
+		isacsx_icd_interrupt(isac);
+	if (val & ISACSX_ISTA_CIC)
+		isacsx_cic_interrupt(isac);
+}
+
+void isac_init(struct isac *isac)
+{
+	isac->tx_skb = NULL;
+	isac->l1m.fsm = &l1fsm;
+	isac->l1m.state = ST_L1_RESET;
+#ifdef CONFIG_HISAX_DEBUG
+	isac->l1m.debug = 1;
+#else
+	isac->l1m.debug = 0;
+#endif
+	isac->l1m.userdata = isac;
+	isac->l1m.printdebug = l1m_debug;
+	FsmInitTimer(&isac->l1m, &isac->timer);
+}
+
+void isac_setup(struct isac *isac)
+{
+	int val, eval;
+
+	isac->type = TYPE_ISAC;
+	isac_version(isac);
+
+	ph_command(isac, ISAC_CMD_RES);
+
+  	isac->write_isac(isac, ISAC_MASK, 0xff);
+  	isac->mocr = 0xaa;
+	if (test_bit(ISAC_IOM1, &isac->flags)) {
+		/* IOM 1 Mode */
+		isac->write_isac(isac, ISAC_ADF2, 0x0);
+		isac->write_isac(isac, ISAC_SPCR, 0xa);
+		isac->write_isac(isac, ISAC_ADF1, 0x2);
+		isac->write_isac(isac, ISAC_STCR, 0x70);
+		isac->write_isac(isac, ISAC_MODE, 0xc9);
+	} else {
+		/* IOM 2 Mode */
+		if (!isac->adf2)
+			isac->adf2 = 0x80;
+		isac->write_isac(isac, ISAC_ADF2, isac->adf2);
+		isac->write_isac(isac, ISAC_SQXR, 0x2f);
+		isac->write_isac(isac, ISAC_SPCR, 0x00);
+		isac->write_isac(isac, ISAC_STCR, 0x70);
+		isac->write_isac(isac, ISAC_MODE, 0xc9);
+		isac->write_isac(isac, ISAC_TIMR, 0x00);
+		isac->write_isac(isac, ISAC_ADF1, 0x00);
+	}
+	val = isac->read_isac(isac, ISAC_STAR);
+	DBG(2, "ISAC STAR %x", val);
+	val = isac->read_isac(isac, ISAC_MODE);
+	DBG(2, "ISAC MODE %x", val);
+	val = isac->read_isac(isac, ISAC_ADF2);
+	DBG(2, "ISAC ADF2 %x", val);
+	val = isac->read_isac(isac, ISAC_ISTA);
+	DBG(2, "ISAC ISTA %x", val);
+	if (val & 0x01) {
+		eval = isac->read_isac(isac, ISAC_EXIR);
+		DBG(2, "ISAC EXIR %x", eval);
+	}
+	val = isac->read_isac(isac, ISAC_CIR0);
+	DBG(2, "ISAC CIR0 %x", val);
+	FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL);
+
+	isac->write_isac(isac, ISAC_MASK, 0x0);
+	// RESET Receiver and Transmitter
+	isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_XRES | ISAC_CMDR_RRES);
+}
+
+void isacsx_setup(struct isac *isac)
+{
+	isac->type = TYPE_ISACSX;
+	// clear LDD
+	isac->write_isac(isac, ISACSX_TR_CONF0, 0x00);
+	// enable transmitter
+	isac->write_isac(isac, ISACSX_TR_CONF2, 0x00);
+	// transparent mode 0, RAC, stop/go
+	isac->write_isac(isac, ISACSX_MODED,    0xc9);
+	// all HDLC IRQ unmasked
+	isac->write_isac(isac, ISACSX_MASKD,    0x03);
+	// unmask ICD, CID IRQs
+	isac->write_isac(isac, ISACSX_MASK,            
+			 ~(ISACSX_ISTA_ICD | ISACSX_ISTA_CIC));
+}
+
+void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg)
+{
+	struct isac *isac = hisax_d_if->priv;
+	struct sk_buff *skb = arg;
+
+	DBG(DBG_PR, "pr %#x", pr);
+
+	switch (pr) {
+	case PH_ACTIVATE | REQUEST:
+		FsmEvent(&isac->l1m, EV_PH_ACTIVATE_REQ, NULL);
+		break;
+	case PH_DEACTIVATE | REQUEST:
+		FsmEvent(&isac->l1m, EV_PH_DEACTIVATE_REQ, NULL);
+		break;
+	case PH_DATA | REQUEST:
+		DBG(DBG_PR, "PH_DATA REQUEST len %d", skb->len);
+		DBG_SKB(DBG_XPACKET, skb);
+		if (isac->l1m.state != ST_L1_F7) {
+			DBG(1, "L1 wrong state %d\n", isac->l1m.state);
+			dev_kfree_skb(skb);
+			break;
+		}
+		if (isac->tx_skb)
+			BUG();
+
+		isac->tx_skb = skb;
+		isac_fill_fifo(isac);
+		break;
+	}
+}
+
+static int __init hisax_isac_init(void)
+{
+	printk(KERN_INFO "hisax_isac: ISAC-S/ISAC-SX ISDN driver v0.1.0\n");
+
+	l1fsm.state_count = L1_STATE_COUNT;
+	l1fsm.event_count = L1_EVENT_COUNT;
+	l1fsm.strState = strL1State;
+	l1fsm.strEvent = strL1Event;
+	return FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList));
+}
+
+static void __exit hisax_isac_exit(void)
+{
+	FsmFree(&l1fsm);
+}
+
+EXPORT_SYMBOL(isac_init);
+EXPORT_SYMBOL(isac_d_l2l1);
+
+EXPORT_SYMBOL(isacsx_setup);
+EXPORT_SYMBOL(isacsx_irq);
+
+EXPORT_SYMBOL(isac_setup);
+EXPORT_SYMBOL(isac_irq);
+
+module_init(hisax_isac_init);
+module_exit(hisax_isac_exit);
diff --git a/drivers/isdn/hisax/hisax_isac.h b/drivers/isdn/hisax/hisax_isac.h
new file mode 100644
index 000000000000..08890cf4d923
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_isac.h
@@ -0,0 +1,45 @@
+#ifndef __HISAX_ISAC_H__
+#define __HISAX_ISAC_H__
+
+#include <linux/kernel.h>
+#include "fsm.h"
+#include "hisax_if.h"
+
+#define TIMER3_VALUE 7000
+#define MAX_DFRAME_LEN_L1 300
+
+#define ISAC_IOM1	0
+
+struct isac {
+	void *priv;
+
+	u_long flags;
+	struct hisax_d_if hisax_d_if;
+	struct FsmInst l1m;
+	struct FsmTimer timer;
+	u_char mocr;
+	u_char adf2;
+	int    type;
+
+	u_char rcvbuf[MAX_DFRAME_LEN_L1];
+	int rcvidx;
+
+	struct sk_buff *tx_skb;
+	int tx_cnt;
+
+	u_char (*read_isac)      (struct isac *, u_char);
+	void   (*write_isac)     (struct isac *, u_char, u_char);
+	void   (*read_isac_fifo) (struct isac *, u_char *, int);
+	void   (*write_isac_fifo)(struct isac *, u_char *, int);
+};
+
+void isac_init(struct isac *isac);
+void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg);
+
+void isac_setup(struct isac *isac);
+void isac_irq(struct isac *isac);
+
+void isacsx_setup(struct isac *isac);
+void isacsx_irq(struct isac *isac);
+
+#endif
diff --git a/drivers/isdn/hisax/hscx.c b/drivers/isdn/hisax/hscx.c
new file mode 100644
index 000000000000..5bbbe3e95125
--- /dev/null
+++ b/drivers/isdn/hisax/hscx.c
@@ -0,0 +1,280 @@
+/* $Id: hscx.c,v 1.24.2.4 2004/01/24 20:47:23 keil Exp $
+ *
+ * HSCX specific routines
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hscx.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+static char *HSCXVer[] =
+{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
+ "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
+
+int
+HscxVersion(struct IsdnCardState *cs, char *s)
+{
+	int verA, verB;
+
+	verA = cs->BC_Read_Reg(cs, 0, HSCX_VSTR) & 0xf;
+	verB = cs->BC_Read_Reg(cs, 1, HSCX_VSTR) & 0xf;
+	printk(KERN_INFO "%s HSCX version A: %s  B: %s\n", s,
+	       HSCXVer[verA], HSCXVer[verB]);
+	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf))
+		return (1);
+	else
+		return (0);
+}
+
+void
+modehscx(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int hscx = bcs->hw.hscx.hscx;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, bc);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	cs->BC_Write_Reg(cs, hscx, HSCX_XAD1, 0xFF);
+	cs->BC_Write_Reg(cs, hscx, HSCX_XAD2, 0xFF);
+	cs->BC_Write_Reg(cs, hscx, HSCX_RAH2, 0xFF);
+	cs->BC_Write_Reg(cs, hscx, HSCX_XBCH, 0x0);
+	cs->BC_Write_Reg(cs, hscx, HSCX_RLCR, 0x0);
+	cs->BC_Write_Reg(cs, hscx, HSCX_CCR1,
+		test_bit(HW_IPAC, &cs->HW_Flags) ? 0x82 : 0x85);
+	cs->BC_Write_Reg(cs, hscx, HSCX_CCR2, 0x30);
+	cs->BC_Write_Reg(cs, hscx, HSCX_XCCR, 7);
+	cs->BC_Write_Reg(cs, hscx, HSCX_RCCR, 7);
+
+	/* Switch IOM 1 SSI */
+	if (test_bit(HW_IOM1, &cs->HW_Flags) && (hscx == 0))
+		bc = 1 - bc;
+
+	if (bc == 0) {
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAX,
+			      test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0);
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAR,
+			      test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0);
+	} else {
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, bcs->hw.hscx.tsaxr1);
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, bcs->hw.hscx.tsaxr1);
+	}
+	switch (mode) {
+		case (L1_MODE_NULL):
+			cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x1f);
+			cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x1f);
+			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x84);
+			break;
+		case (L1_MODE_TRANS):
+			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0xe4);
+			break;
+		case (L1_MODE_HDLC):
+			cs->BC_Write_Reg(cs, hscx, HSCX_CCR1,
+				test_bit(HW_IPAC, &cs->HW_Flags) ? 0x8a : 0x8d);
+			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x8c);
+			break;
+	}
+	if (mode)
+		cs->BC_Write_Reg(cs, hscx, HSCX_CMDR, 0x41);
+	cs->BC_Write_Reg(cs, hscx, HSCX_ISTA, 0x00);
+}
+
+void
+hscx_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	u_long flags;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->hw.hscx.count = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n");
+			} else {
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->tx_skb = skb;
+				bcs->hw.hscx.count = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			modehscx(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			modehscx(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_hscxstate(struct BCState *bcs)
+{
+	modehscx(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hscx.rcvbuf) {
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+		}
+		if (bcs->blog) {
+			kfree(bcs->blog);
+			bcs->blog = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+int
+open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax: No memory for hscx.rcvbuf\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			return (1);
+		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax: No memory for bcs->blog\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+			return (2);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.hscx.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_hscx(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hscxstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hscx_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void
+clear_pending_hscx_ints(struct IsdnCardState *cs)
+{
+	int val, eval;
+
+	val = cs->BC_Read_Reg(cs, 1, HSCX_ISTA);
+	debugl1(cs, "HSCX B ISTA %x", val);
+	if (val & 0x01) {
+		eval = cs->BC_Read_Reg(cs, 1, HSCX_EXIR);
+		debugl1(cs, "HSCX B EXIR %x", eval);
+	}
+	if (val & 0x02) {
+		eval = cs->BC_Read_Reg(cs, 0, HSCX_EXIR);
+		debugl1(cs, "HSCX A EXIR %x", eval);
+	}
+	val = cs->BC_Read_Reg(cs, 0, HSCX_ISTA);
+	debugl1(cs, "HSCX A ISTA %x", val);
+	val = cs->BC_Read_Reg(cs, 1, HSCX_STAR);
+	debugl1(cs, "HSCX B STAR %x", val);
+	val = cs->BC_Read_Reg(cs, 0, HSCX_STAR);
+	debugl1(cs, "HSCX A STAR %x", val);
+	/* disable all IRQ */
+	cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0xFF);
+	cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0xFF);
+}
+
+void
+inithscx(struct IsdnCardState *cs)
+{
+	cs->bcs[0].BC_SetStack = setstack_hscx;
+	cs->bcs[1].BC_SetStack = setstack_hscx;
+	cs->bcs[0].BC_Close = close_hscxstate;
+	cs->bcs[1].BC_Close = close_hscxstate;
+	cs->bcs[0].hw.hscx.hscx = 0;
+	cs->bcs[1].hw.hscx.hscx = 1;
+	cs->bcs[0].hw.hscx.tsaxr0 = 0x2f;
+	cs->bcs[0].hw.hscx.tsaxr1 = 3;
+	cs->bcs[1].hw.hscx.tsaxr0 = 0x2f;
+	cs->bcs[1].hw.hscx.tsaxr1 = 3;
+	modehscx(cs->bcs, 0, 0);
+	modehscx(cs->bcs + 1, 0, 0);
+}
+
+void
+inithscxisac(struct IsdnCardState *cs, int part)
+{
+	if (part & 1) {
+		clear_pending_isac_ints(cs);
+		clear_pending_hscx_ints(cs);
+		initisac(cs);
+		inithscx(cs);
+	}
+	if (part & 2) {
+		/* Reenable all IRQ */
+		cs->writeisac(cs, ISAC_MASK, 0);
+		cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0);
+		cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0);
+		/* RESET Receiver and Transmitter */
+		cs->writeisac(cs, ISAC_CMDR, 0x41);
+	}
+}
diff --git a/drivers/isdn/hisax/hscx.h b/drivers/isdn/hisax/hscx.h
new file mode 100644
index 000000000000..268bfd3549b0
--- /dev/null
+++ b/drivers/isdn/hisax/hscx.h
@@ -0,0 +1,41 @@
+/* $Id: hscx.h,v 1.8.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * HSCX specific defines
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec  */
+
+#define HSCX_ISTA 0x20
+#define HSCX_CCR1 0x2f
+#define HSCX_CCR2 0x2c
+#define HSCX_TSAR 0x31
+#define HSCX_TSAX 0x30
+#define HSCX_XCCR 0x32
+#define HSCX_RCCR 0x33
+#define HSCX_MODE 0x22
+#define HSCX_CMDR 0x21
+#define HSCX_EXIR 0x24
+#define HSCX_XAD1 0x24
+#define HSCX_XAD2 0x25
+#define HSCX_RAH2 0x27
+#define HSCX_RSTA 0x27
+#define HSCX_TIMR 0x23
+#define HSCX_STAR 0x21
+#define HSCX_RBCL 0x25
+#define HSCX_XBCH 0x2d
+#define HSCX_VSTR 0x2e
+#define HSCX_RLCR 0x2e
+#define HSCX_MASK 0x20
+
+extern int HscxVersion(struct IsdnCardState *cs, char *s);
+extern void modehscx(struct BCState *bcs, int mode, int bc);
+extern void clear_pending_hscx_ints(struct IsdnCardState *cs);
+extern void inithscx(struct IsdnCardState *cs);
+extern void inithscxisac(struct IsdnCardState *cs, int part);
diff --git a/drivers/isdn/hisax/hscx_irq.c b/drivers/isdn/hisax/hscx_irq.c
new file mode 100644
index 000000000000..5fe9d42d03a3
--- /dev/null
+++ b/drivers/isdn/hisax/hscx_irq.c
@@ -0,0 +1,292 @@
+/* $Id: hscx_irq.c,v 1.18.2.3 2004/02/11 13:21:34 keil Exp $
+ *
+ * low level b-channel stuff for Siemens HSCX
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * This is an include file for fast inline IRQ stuff
+ *
+ */
+
+
+static inline void
+waitforCEC(struct IsdnCardState *cs, int hscx)
+{
+	int to = 50;
+
+	while ((READHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(struct IsdnCardState *cs, int hscx)
+{
+	int to = 50;
+
+	while ((!(READHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: waitforXFW timeout\n");
+}
+
+static inline void
+WriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data)
+{
+	waitforCEC(cs, hscx);
+	WRITEHSCX(cs, hscx, HSCX_CMDR, data);
+}
+
+
+
+static void
+hscx_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *cs = bcs->cs;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hscx_empty_fifo");
+
+	if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hscx_empty_fifo: incoming packet too large");
+		WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+		bcs->hw.hscx.rcvidx = 0;
+		return;
+	}
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	bcs->hw.hscx.rcvidx += count;
+	READHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+	WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static void
+hscx_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int more, count;
+	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hscx_fill_fifo");
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+	if (bcs->tx_skb->len > fifo_size) {
+		more = !0;
+		count = fifo_size;
+	} else
+		count = bcs->tx_skb->len;
+
+	waitforXFW(cs, bcs->hw.hscx.hscx);
+	ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hscx.count += count;
+	WRITEHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+	WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx)
+{
+	u_char r;
+	struct BCState *bcs = cs->bcs + hscx;
+	struct sk_buff *skb;
+	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+	int count;
+
+	if (!test_bit(BC_FLG_INIT, &bcs->Flag))
+		return;
+
+	if (val & 0x80) {	/* RME */
+		r = READHSCX(cs, hscx, HSCX_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!(r & 0x80)) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX invalid frame");
+#ifdef ERROR_STATISTIC
+				bcs->err_inv++;
+#endif
+			}
+			if ((r & 0x40) && bcs->mode) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX RDO mode=%d",
+						bcs->mode);
+#ifdef ERROR_STATISTIC
+				bcs->err_rdo++;
+#endif
+			}
+			if (!(r & 0x20)) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX CRC error");
+#ifdef ERROR_STATISTIC
+				bcs->err_crc++;
+#endif
+			}
+			WriteHSCXCMDR(cs, hscx, 0x80);
+		} else {
+			count = READHSCX(cs, hscx, HSCX_RBCL) & (
+				test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f);
+			if (count == 0)
+				count = fifo_size;
+			hscx_empty_fifo(bcs, count);
+			if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+				if (cs->debug & L1_DEB_HSCX_FIFO)
+					debugl1(cs, "HX Frame %d", count);
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "HSCX: receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+					skb_queue_tail(&bcs->rqueue, skb);
+				}
+			}
+		}
+		bcs->hw.hscx.rcvidx = 0;
+		schedule_event(bcs, B_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		hscx_empty_fifo(bcs, fifo_size);
+		if (bcs->mode == L1_MODE_TRANS) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(fifo_size)))
+				printk(KERN_WARNING "HiSax: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size);
+				skb_queue_tail(&bcs->rqueue, skb);
+			}
+			bcs->hw.hscx.rcvidx = 0;
+			schedule_event(bcs, B_RCVBUFREADY);
+		}
+	}
+	if (val & 0x10) {	/* XPR */
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				hscx_fill_fifo(bcs);
+				return;
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->hw.hscx.count;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+				dev_kfree_skb_irq(bcs->tx_skb);
+				bcs->hw.hscx.count = 0; 
+				bcs->tx_skb = NULL;
+			}
+		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hscx.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			hscx_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+static inline void
+hscx_int_main(struct IsdnCardState *cs, u_char val)
+{
+
+	u_char exval;
+	struct BCState *bcs;
+
+	if (val & 0x01) {
+		bcs = cs->bcs + 1;
+		exval = READHSCX(cs, 1, HSCX_EXIR);
+		if (exval & 0x40) {
+			if (bcs->mode == 1)
+				hscx_fill_fifo(bcs);
+			else {
+#ifdef ERROR_STATISTIC
+				bcs->err_tx++;
+#endif
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (bcs->tx_skb) {
+					skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+					bcs->tx_cnt += bcs->hw.hscx.count;
+					bcs->hw.hscx.count = 0;
+				}
+				WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX B EXIR %x Lost TX", exval);
+			}
+		} else if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX B EXIR %x", exval);
+	}
+	if (val & 0xf8) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX B interrupt %x", val);
+		hscx_interrupt(cs, val, 1);
+	}
+	if (val & 0x02) {
+		bcs = cs->bcs;
+		exval = READHSCX(cs, 0, HSCX_EXIR);
+		if (exval & 0x40) {
+			if (bcs->mode == L1_MODE_TRANS)
+				hscx_fill_fifo(bcs);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+#ifdef ERROR_STATISTIC
+				bcs->err_tx++;
+#endif
+				if (bcs->tx_skb) {
+					skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+					bcs->tx_cnt += bcs->hw.hscx.count;
+					bcs->hw.hscx.count = 0;
+				}
+				WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX A EXIR %x Lost TX", exval);
+			}
+		} else if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX A EXIR %x", exval);
+	}
+	if (val & 0x04) {
+		exval = READHSCX(cs, 0, HSCX_ISTA);
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX A interrupt %x", exval);
+		hscx_interrupt(cs, exval, 0);
+	}
+}
diff --git a/drivers/isdn/hisax/icc.c b/drivers/isdn/hisax/icc.c
new file mode 100644
index 000000000000..dcf31f83c600
--- /dev/null
+++ b/drivers/isdn/hisax/icc.c
@@ -0,0 +1,685 @@
+/* $Id: icc.c,v 1.8.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * ICC specific routines
+ *
+ * Author       Matt Henderson & Guy Ellis
+ * Copyright    by Traverse Technologies Pty Ltd, www.travers.com.au
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * 1999.6.25 Initial implementation of routines for Siemens ISDN
+ * Communication Controller PEB 2070 based on the ISAC routines
+ * written by Karsten Keil.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "icc.h"
+// #include "arcofi.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+#define DBUSY_TIMER_VALUE 80
+#define ARCOFI_USE 0
+
+static char *ICCVer[] __initdata =
+{"2070 A1/A3", "2070 B1", "2070 B2/B3", "2070 V2.4"};
+
+void __init
+ICCVersion(struct IsdnCardState *cs, char *s)
+{
+	int val;
+
+	val = cs->readisac(cs, ICC_RBCH);
+	printk(KERN_INFO "%s ICC version (%x): %s\n", s, val, ICCVer[(val >> 5) & 3]);
+}
+
+static void
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ph_command %x", command);
+	cs->writeisac(cs, ICC_CIX0, (command << 2) | 3);
+}
+
+
+static void
+icc_new_ph(struct IsdnCardState *cs)
+{
+	switch (cs->dc.icc.ph_state) {
+		case (ICC_IND_EI1):
+			ph_command(cs, ICC_CMD_DI);
+			l1_msg(cs, HW_RESET | INDICATION, NULL);
+			break;
+		case (ICC_IND_DC):
+			l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+			break;
+		case (ICC_IND_DR):
+			l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+			break;
+		case (ICC_IND_PU):
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (ICC_IND_FJ):
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			break;
+		case (ICC_IND_AR):
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+			break;
+		case (ICC_IND_AI):
+			l1_msg(cs, HW_INFO4 | INDICATION, NULL);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+icc_bh(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy cleared");
+		stptr = cs->stlist;
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			stptr = stptr->next;
+		}
+	}
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
+		icc_new_ph(cs);		
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+#if ARCOFI_USE
+	if (!test_bit(HW_ARCOFI, &cs->HW_Flags))
+		return;
+	if (test_and_clear_bit(D_RX_MON1, &cs->event))
+		arcofi_fsm(cs, ARCOFI_RX_END, NULL);
+	if (test_and_clear_bit(D_TX_MON1, &cs->event))
+		arcofi_fsm(cs, ARCOFI_TX_END, NULL);
+#endif
+}
+
+void
+icc_empty_fifo(struct IsdnCardState *cs, int count)
+{
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "icc_empty_fifo");
+
+	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "icc_empty_fifo overrun %d",
+				cs->rcvidx + count);
+		cs->writeisac(cs, ICC_CMDR, 0x80);
+		cs->rcvidx = 0;
+		return;
+	}
+	ptr = cs->rcvbuf + cs->rcvidx;
+	cs->rcvidx += count;
+	cs->readisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ICC_CMDR, 0x80);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "icc_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+static void
+icc_fill_fifo(struct IsdnCardState *cs)
+{
+	int count, more;
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "icc_fill_fifo");
+
+	if (!cs->tx_skb)
+		return;
+
+	count = cs->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = cs->tx_skb->data;
+	skb_pull(cs->tx_skb, count);
+	cs->tx_cnt += count;
+	cs->writeisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ICC_CMDR, more ? 0x8 : 0xa);
+	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		debugl1(cs, "icc_fill_fifo dbusytimer running");
+		del_timer(&cs->dbusytimer);
+	}
+	init_timer(&cs->dbusytimer);
+	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+	add_timer(&cs->dbusytimer);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "icc_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+void
+icc_interrupt(struct IsdnCardState *cs, u_char val)
+{
+	u_char exval, v1;
+	struct sk_buff *skb;
+	unsigned int count;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ICC interrupt %x", val);
+	if (val & 0x80) {	/* RME */
+		exval = cs->readisac(cs, ICC_RSTA);
+		if ((exval & 0x70) != 0x20) {
+			if (exval & 0x40) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ICC RDO");
+#ifdef ERROR_STATISTIC
+				cs->err_rx++;
+#endif
+			}
+			if (!(exval & 0x20)) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ICC CRC error");
+#ifdef ERROR_STATISTIC
+				cs->err_crc++;
+#endif
+			}
+			cs->writeisac(cs, ICC_CMDR, 0x80);
+		} else {
+			count = cs->readisac(cs, ICC_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			icc_empty_fifo(cs, count);
+			if ((count = cs->rcvidx) > 0) {
+				cs->rcvidx = 0;
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "HiSax: D receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), cs->rcvbuf, count);
+					skb_queue_tail(&cs->rq, skb);
+				}
+			}
+		}
+		cs->rcvidx = 0;
+		schedule_event(cs, D_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		icc_empty_fifo(cs, 32);
+	}
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ICC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+		if (cs->tx_skb) {
+			if (cs->tx_skb->len) {
+				icc_fill_fifo(cs);
+				goto afterXPR;
+			} else {
+				dev_kfree_skb_irq(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			}
+		}
+		if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+			cs->tx_cnt = 0;
+			icc_fill_fifo(cs);
+		} else
+			schedule_event(cs, D_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		exval = cs->readisac(cs, ICC_CIR0);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ICC CIR0 %02X", exval );
+		if (exval & 2) {
+			cs->dc.icc.ph_state = (exval >> 2) & 0xf;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ph_state change %x", cs->dc.icc.ph_state);
+			schedule_event(cs, D_L1STATECHANGE);
+		}
+		if (exval & 1) {
+			exval = cs->readisac(cs, ICC_CIR1);
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ICC CIR1 %02X", exval );
+		}
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ICC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = cs->readisac(cs, ICC_EXIR);
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ICC EXIR %02x", exval);
+		if (exval & 0x80) {  /* XMR */
+			debugl1(cs, "ICC XMR");
+			printk(KERN_WARNING "HiSax: ICC XMR\n");
+		}
+		if (exval & 0x40) {  /* XDU */
+			debugl1(cs, "ICC XDU");
+			printk(KERN_WARNING "HiSax: ICC XDU\n");
+#ifdef ERROR_STATISTIC
+			cs->err_tx++;
+#endif
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			if (cs->tx_skb) { /* Restart frame */
+				skb_push(cs->tx_skb, cs->tx_cnt);
+				cs->tx_cnt = 0;
+				icc_fill_fifo(cs);
+			} else {
+				printk(KERN_WARNING "HiSax: ICC XDU no skb\n");
+				debugl1(cs, "ICC XDU no skb");
+			}
+		}
+		if (exval & 0x04) {  /* MOS */
+			v1 = cs->readisac(cs, ICC_MOSR);
+			if (cs->debug & L1_DEB_MONITOR)
+				debugl1(cs, "ICC MOSR %02x", v1);
+#if ARCOFI_USE
+			if (v1 & 0x08) {
+				if (!cs->dc.icc.mon_rx) {
+					if (!(cs->dc.icc.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ICC MON RX out of memory!");
+						cs->dc.icc.mocr &= 0xf0;
+						cs->dc.icc.mocr |= 0x0a;
+						cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+						goto afterMONR0;
+					} else
+						cs->dc.icc.mon_rxp = 0;
+				}
+				if (cs->dc.icc.mon_rxp >= MAX_MON_FRAME) {
+					cs->dc.icc.mocr &= 0xf0;
+					cs->dc.icc.mocr |= 0x0a;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					cs->dc.icc.mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ICC MON RX overflow!");
+					goto afterMONR0;
+				}
+				cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp++] = cs->readisac(cs, ICC_MOR0);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ICC MOR0 %02x", cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp -1]);
+				if (cs->dc.icc.mon_rxp == 1) {
+					cs->dc.icc.mocr |= 0x04;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				}
+			}
+		      afterMONR0:
+			if (v1 & 0x80) {
+				if (!cs->dc.icc.mon_rx) {
+					if (!(cs->dc.icc.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ICC MON RX out of memory!");
+						cs->dc.icc.mocr &= 0x0f;
+						cs->dc.icc.mocr |= 0xa0;
+						cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+						goto afterMONR1;
+					} else
+						cs->dc.icc.mon_rxp = 0;
+				}
+				if (cs->dc.icc.mon_rxp >= MAX_MON_FRAME) {
+					cs->dc.icc.mocr &= 0x0f;
+					cs->dc.icc.mocr |= 0xa0;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					cs->dc.icc.mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ICC MON RX overflow!");
+					goto afterMONR1;
+				}
+				cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp++] = cs->readisac(cs, ICC_MOR1);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ICC MOR1 %02x", cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp -1]);
+				cs->dc.icc.mocr |= 0x40;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+			}
+		      afterMONR1:
+			if (v1 & 0x04) {
+				cs->dc.icc.mocr &= 0xf0;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				cs->dc.icc.mocr |= 0x0a;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				schedule_event(cs, D_RX_MON0);
+			}
+			if (v1 & 0x40) {
+				cs->dc.icc.mocr &= 0x0f;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				cs->dc.icc.mocr |= 0xa0;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				schedule_event(cs, D_RX_MON1);
+			}
+			if (v1 & 0x02) {
+				if ((!cs->dc.icc.mon_tx) || (cs->dc.icc.mon_txc && 
+					(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc) && 
+					!(v1 & 0x08))) {
+					cs->dc.icc.mocr &= 0xf0;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					cs->dc.icc.mocr |= 0x0a;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					if (cs->dc.icc.mon_txc &&
+						(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc))
+						schedule_event(cs, D_TX_MON0);
+					goto AfterMOX0;
+				}
+				if (cs->dc.icc.mon_txc && (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) {
+					schedule_event(cs, D_TX_MON0);
+					goto AfterMOX0;
+				}
+				cs->writeisac(cs, ICC_MOX0,
+					cs->dc.icc.mon_tx[cs->dc.icc.mon_txp++]);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ICC %02x -> MOX0", cs->dc.icc.mon_tx[cs->dc.icc.mon_txp -1]);
+			}
+		      AfterMOX0:
+			if (v1 & 0x20) {
+				if ((!cs->dc.icc.mon_tx) || (cs->dc.icc.mon_txc && 
+					(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc) && 
+					!(v1 & 0x80))) {
+					cs->dc.icc.mocr &= 0x0f;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					cs->dc.icc.mocr |= 0xa0;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					if (cs->dc.icc.mon_txc &&
+						(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc))
+						schedule_event(cs, D_TX_MON1);
+					goto AfterMOX1;
+				}
+				if (cs->dc.icc.mon_txc && (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) {
+					schedule_event(cs, D_TX_MON1);
+					goto AfterMOX1;
+				}
+				cs->writeisac(cs, ICC_MOX1,
+					cs->dc.icc.mon_tx[cs->dc.icc.mon_txp++]);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ICC %02x -> MOX1", cs->dc.icc.mon_tx[cs->dc.icc.mon_txp -1]);
+			}
+		      AfterMOX1:
+#endif
+		}
+	}
+}
+
+static void
+ICC_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	u_long flags;
+	int  val;
+
+	switch (pr) {
+		case (PH_DATA |REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				icc_fill_fifo(cs);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL |INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			icc_fill_fifo(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			if ((cs->dc.icc.ph_state == ICC_IND_EI1) ||
+				(cs->dc.icc.ph_state == ICC_IND_DR))
+			        ph_command(cs, ICC_CMD_DI);
+			else
+				ph_command(cs, ICC_CMD_RES);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_ENABLE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, ICC_CMD_DI);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO1 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, ICC_CMD_AR);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO3 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, ICC_CMD_AI);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			val = 0;
+			if (1 & (long) arg)
+				val |= 0x0c;
+			if (2 & (long) arg)
+				val |= 0x3;
+			if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+				/* IOM 1 Mode */
+				if (!val) {
+					cs->writeisac(cs, ICC_SPCR, 0xa);
+					cs->writeisac(cs, ICC_ADF1, 0x2);
+				} else {
+					cs->writeisac(cs, ICC_SPCR, val);
+					cs->writeisac(cs, ICC_ADF1, 0xa);
+				}
+			} else {
+				/* IOM 2 Mode */
+				cs->writeisac(cs, ICC_SPCR, val);
+				if (val)
+					cs->writeisac(cs, ICC_ADF1, 0x8);
+				else
+					cs->writeisac(cs, ICC_ADF1, 0x0);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_DEACTIVATE | RESPONSE):
+			skb_queue_purge(&cs->rq);
+			skb_queue_purge(&cs->sq);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_skb = NULL;
+			}
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "icc_l1hw unknown %04x", pr);
+			break;
+	}
+}
+
+void
+setstack_icc(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = ICC_l1hw;
+}
+
+void 
+DC_Close_icc(struct IsdnCardState *cs) {
+	if (cs->dc.icc.mon_rx) {
+		kfree(cs->dc.icc.mon_rx);
+		cs->dc.icc.mon_rx = NULL;
+	}
+	if (cs->dc.icc.mon_tx) {
+		kfree(cs->dc.icc.mon_tx);
+		cs->dc.icc.mon_tx = NULL;
+	}
+}
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	int	rbch, star;
+
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		rbch = cs->readisac(cs, ICC_RBCH);
+		star = cs->readisac(cs, ICC_STAR);
+		if (cs->debug) 
+			debugl1(cs, "D-Channel Busy RBCH %02x STAR %02x",
+				rbch, star);
+		if (rbch & ICC_RBCH_XAC) { /* D-Channel Busy */
+			test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+			stptr = cs->stlist;
+			while (stptr != NULL) {
+				stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+				stptr = stptr->next;
+			}
+		} else {
+			/* discard frame; reset transceiver */
+			test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			} else {
+				printk(KERN_WARNING "HiSax: ICC D-Channel Busy no skb\n");
+				debugl1(cs, "D-Channel Busy no skb");
+			}
+			cs->writeisac(cs, ICC_CMDR, 0x01); /* Transmitter reset */
+			cs->irq_func(cs->irq, cs, NULL);
+		}
+	}
+}
+
+void __init
+initicc(struct IsdnCardState *cs)
+{
+	cs->setstack_d = setstack_icc;
+	cs->DC_Close = DC_Close_icc;
+	cs->dc.icc.mon_tx = NULL;
+	cs->dc.icc.mon_rx = NULL;
+  	cs->writeisac(cs, ICC_MASK, 0xff);
+  	cs->dc.icc.mocr = 0xaa;
+	if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+		/* IOM 1 Mode */
+		cs->writeisac(cs, ICC_ADF2, 0x0);
+		cs->writeisac(cs, ICC_SPCR, 0xa);
+		cs->writeisac(cs, ICC_ADF1, 0x2);
+		cs->writeisac(cs, ICC_STCR, 0x70);
+		cs->writeisac(cs, ICC_MODE, 0xc9);
+	} else {
+		/* IOM 2 Mode */
+		if (!cs->dc.icc.adf2)
+			cs->dc.icc.adf2 = 0x80;
+		cs->writeisac(cs, ICC_ADF2, cs->dc.icc.adf2);
+		cs->writeisac(cs, ICC_SQXR, 0xa0);
+		cs->writeisac(cs, ICC_SPCR, 0x20);
+		cs->writeisac(cs, ICC_STCR, 0x70);
+		cs->writeisac(cs, ICC_MODE, 0xca);
+		cs->writeisac(cs, ICC_TIMR, 0x00);
+		cs->writeisac(cs, ICC_ADF1, 0x20);
+	}
+	ph_command(cs, ICC_CMD_RES);
+	cs->writeisac(cs, ICC_MASK, 0x0);
+	ph_command(cs, ICC_CMD_DI);
+}
+
+void __init
+clear_pending_icc_ints(struct IsdnCardState *cs)
+{
+	int val, eval;
+
+	val = cs->readisac(cs, ICC_STAR);
+	debugl1(cs, "ICC STAR %x", val);
+	val = cs->readisac(cs, ICC_MODE);
+	debugl1(cs, "ICC MODE %x", val);
+	val = cs->readisac(cs, ICC_ADF2);
+	debugl1(cs, "ICC ADF2 %x", val);
+	val = cs->readisac(cs, ICC_ISTA);
+	debugl1(cs, "ICC ISTA %x", val);
+	if (val & 0x01) {
+		eval = cs->readisac(cs, ICC_EXIR);
+		debugl1(cs, "ICC EXIR %x", eval);
+	}
+	val = cs->readisac(cs, ICC_CIR0);
+	debugl1(cs, "ICC CIR0 %x", val);
+	cs->dc.icc.ph_state = (val >> 2) & 0xf;
+	schedule_event(cs, D_L1STATECHANGE);
+	/* Disable all IRQ */
+	cs->writeisac(cs, ICC_MASK, 0xFF);
+}
+
+void __devinit
+setup_icc(struct IsdnCardState *cs)
+{
+	INIT_WORK(&cs->tqueue, (void *)(void *) icc_bh, cs);
+	cs->dbusytimer.function = (void *) dbusy_timer_handler;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+}
diff --git a/drivers/isdn/hisax/icc.h b/drivers/isdn/hisax/icc.h
new file mode 100644
index 000000000000..b3bb3d5de532
--- /dev/null
+++ b/drivers/isdn/hisax/icc.h
@@ -0,0 +1,72 @@
+/* $Id: icc.h,v 1.4.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * ICC specific routines
+ *
+ * Author       Matt Henderson & Guy Ellis
+ * Copyright    by Traverse Technologies Pty Ltd, www.travers.com.au
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * 1999.7.14 Initial implementation of routines for Siemens ISDN 
+ * Communication Controller PEB 2070 based on the ISAC routines 
+ * written by Karsten Keil.
+ */
+
+/* All Registers original Siemens Spec  */
+
+#define ICC_MASK 0x20
+#define ICC_ISTA 0x20
+#define ICC_STAR 0x21
+#define ICC_CMDR 0x21
+#define ICC_EXIR 0x24
+#define ICC_ADF2 0x39
+#define ICC_SPCR 0x30
+#define ICC_ADF1 0x38
+#define ICC_CIR0 0x31
+#define ICC_CIX0 0x31
+#define ICC_CIR1 0x33
+#define ICC_CIX1 0x33
+#define ICC_STCR 0x37
+#define ICC_MODE 0x22
+#define ICC_RSTA 0x27
+#define ICC_RBCL 0x25
+#define ICC_RBCH 0x2A
+#define ICC_TIMR 0x23
+#define ICC_SQXR 0x3b
+#define ICC_MOSR 0x3a
+#define ICC_MOCR 0x3a
+#define ICC_MOR0 0x32
+#define ICC_MOX0 0x32
+#define ICC_MOR1 0x34
+#define ICC_MOX1 0x34
+
+#define ICC_RBCH_XAC 0x80
+
+#define ICC_CMD_TIM    0x0
+#define ICC_CMD_RES    0x1
+#define ICC_CMD_DU     0x3
+#define ICC_CMD_EI1    0x4
+#define ICC_CMD_SSP    0x5
+#define ICC_CMD_DT     0x6
+#define ICC_CMD_AR     0x8
+#define ICC_CMD_ARL    0xA
+#define ICC_CMD_AI     0xC
+#define ICC_CMD_DI     0xF
+
+#define ICC_IND_DR     0x0
+#define ICC_IND_FJ     0x2
+#define ICC_IND_EI1    0x4
+#define ICC_IND_INT    0x6
+#define ICC_IND_PU     0x7
+#define ICC_IND_AR     0x8
+#define ICC_IND_ARL    0xA
+#define ICC_IND_AI     0xC
+#define ICC_IND_AIL    0xE
+#define ICC_IND_DC     0xF
+
+extern void __init ICCVersion(struct IsdnCardState *cs, char *s);
+extern void initicc(struct IsdnCardState *cs);
+extern void icc_interrupt(struct IsdnCardState *cs, u_char val);
+extern void clear_pending_icc_ints(struct IsdnCardState *cs);
+extern void setup_icc(struct IsdnCardState *);
diff --git a/drivers/isdn/hisax/ipac.h b/drivers/isdn/hisax/ipac.h
new file mode 100644
index 000000000000..f92a04a92826
--- /dev/null
+++ b/drivers/isdn/hisax/ipac.h
@@ -0,0 +1,29 @@
+/* $Id: ipac.h,v 1.7.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * IPAC specific defines
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec  */
+
+#define IPAC_CONF	0xC0
+#define IPAC_MASK	0xC1
+#define IPAC_ISTA	0xC1
+#define IPAC_ID		0xC2
+#define IPAC_ACFG	0xC3
+#define IPAC_AOE	0xC4
+#define IPAC_ARX	0xC5
+#define IPAC_ATX	0xC5
+#define IPAC_PITA1	0xC6
+#define IPAC_PITA2	0xC7
+#define IPAC_POTA1	0xC8
+#define IPAC_POTA2	0xC9
+#define IPAC_PCFG	0xCA
+#define IPAC_SCFG	0xCB
+#define IPAC_TIMR2	0xCC
diff --git a/drivers/isdn/hisax/ipacx.c b/drivers/isdn/hisax/ipacx.c
new file mode 100644
index 000000000000..6485e232d869
--- /dev/null
+++ b/drivers/isdn/hisax/ipacx.c
@@ -0,0 +1,1004 @@
+/* 
+ *
+ * IPACX specific routines
+ *
+ * Author       Joerg Petersohn
+ * Derived from hisax_isac.c, isac.c, hscx.c and others
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax_if.h"
+#include "hisax.h"
+#include "isdnl1.h"
+#include "ipacx.h"
+
+#define DBUSY_TIMER_VALUE 80
+#define TIMER3_VALUE      7000
+#define MAX_DFRAME_LEN_L1 300
+#define B_FIFO_SIZE       64
+#define D_FIFO_SIZE       32
+
+
+// ipacx interrupt mask values    
+#define _MASK_IMASK     0x2E  // global mask
+#define _MASKB_IMASK    0x0B
+#define _MASKD_IMASK    0x03  // all on
+
+//----------------------------------------------------------
+// local function declarations
+//----------------------------------------------------------
+static void ph_command(struct IsdnCardState *cs, unsigned int command);
+static inline void cic_int(struct IsdnCardState *cs);
+static void dch_l2l1(struct PStack *st, int pr, void *arg);
+static void dbusy_timer_handler(struct IsdnCardState *cs);
+static void ipacx_new_ph(struct IsdnCardState *cs);
+static void dch_bh(struct IsdnCardState *cs);
+static void dch_empty_fifo(struct IsdnCardState *cs, int count);
+static void dch_fill_fifo(struct IsdnCardState *cs);
+static inline void dch_int(struct IsdnCardState *cs);
+static void __devinit dch_setstack(struct PStack *st, struct IsdnCardState *cs);
+static void __devinit dch_init(struct IsdnCardState *cs);
+static void bch_l2l1(struct PStack *st, int pr, void *arg);
+static void bch_empty_fifo(struct BCState *bcs, int count);
+static void bch_fill_fifo(struct BCState *bcs);
+static void bch_int(struct IsdnCardState *cs, u_char hscx);
+static void bch_mode(struct BCState *bcs, int mode, int bc);
+static void bch_close_state(struct BCState *bcs);
+static int bch_open_state(struct IsdnCardState *cs, struct BCState *bcs);
+static int bch_setstack(struct PStack *st, struct BCState *bcs);
+static void __devinit bch_init(struct IsdnCardState *cs, int hscx);
+static void __init clear_pending_ints(struct IsdnCardState *cs);
+
+//----------------------------------------------------------
+// Issue Layer 1 command to chip
+//----------------------------------------------------------
+static void 
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+	if (cs->debug &L1_DEB_ISAC)
+		debugl1(cs, "ph_command (%#x) in (%#x)", command,
+			cs->dc.isac.ph_state);
+//###################################  
+//	printk(KERN_INFO "ph_command (%#x)\n", command);
+//###################################  
+	cs->writeisac(cs, IPACX_CIX0, (command << 4) | 0x0E);
+}
+
+//----------------------------------------------------------
+// Transceiver interrupt handler
+//----------------------------------------------------------
+static inline void 
+cic_int(struct IsdnCardState *cs)
+{
+	u_char event;
+
+	event = cs->readisac(cs, IPACX_CIR0) >> 4;
+	if (cs->debug &L1_DEB_ISAC) debugl1(cs, "cic_int(event=%#x)", event);
+//#########################################  
+//	printk(KERN_INFO "cic_int(%x)\n", event);
+//#########################################  
+  cs->dc.isac.ph_state = event;
+  schedule_event(cs, D_L1STATECHANGE);
+}
+
+//==========================================================
+// D channel functions
+//==========================================================
+
+//----------------------------------------------------------
+// Command entry point
+//----------------------------------------------------------
+static void
+dch_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+  u_char cda1_cr, cda2_cr;
+
+	switch (pr) {
+		case (PH_DATA |REQUEST):
+			if (cs->debug &DEB_DLOG_HEX)     LogFrame(cs, skb->data, skb->len);
+			if (cs->debug &DEB_DLOG_VERBOSE) dlogframe(cs, skb, 0);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG
+				if (cs->debug &L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG
+				if (cs->debug &L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				dch_fill_fifo(cs);
+			}
+			break;
+      
+		case (PH_PULL |INDICATION):
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)     LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE) dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG
+			if (cs->debug & L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			dch_fill_fifo(cs);
+			break;
+      
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG
+			if (cs->debug & L1_DEB_LAPD) debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+
+		case (HW_RESET | REQUEST):
+		case (HW_ENABLE | REQUEST):
+			if ((cs->dc.isac.ph_state == IPACX_IND_RES) ||
+				(cs->dc.isac.ph_state == IPACX_IND_DR) ||
+				(cs->dc.isac.ph_state == IPACX_IND_DC))
+			        ph_command(cs, IPACX_CMD_TIM);
+			else
+				ph_command(cs, IPACX_CMD_RES);
+			break;
+
+		case (HW_INFO3 | REQUEST):
+			ph_command(cs, IPACX_CMD_AR8);
+			break;
+
+		case (HW_TESTLOOP | REQUEST):
+      cs->writeisac(cs, IPACX_CDA_TSDP10, 0x80); // Timeslot 0 is B1
+      cs->writeisac(cs, IPACX_CDA_TSDP11, 0x81); // Timeslot 0 is B1
+      cda1_cr = cs->readisac(cs, IPACX_CDA1_CR);
+      cda2_cr = cs->readisac(cs, IPACX_CDA2_CR);
+			if ((long)arg &1) { // loop B1
+        cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr |0x0a); 
+      }
+      else {  // B1 off
+        cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr &~0x0a); 
+      }
+			if ((long)arg &2) { // loop B2
+        cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr |0x14); 
+      }
+      else {  // B2 off
+        cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr &~0x14); 
+      }
+			break;
+
+		case (HW_DEACTIVATE | RESPONSE):
+			skb_queue_purge(&cs->rq);
+			skb_queue_purge(&cs->sq);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_skb = NULL;
+			}
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			break;
+
+		default:
+			if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_l2l1 unknown %04x", pr);
+			break;
+	}
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+	struct PStack *st;
+	int	rbchd, stard;
+
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		rbchd = cs->readisac(cs, IPACX_RBCHD);
+		stard = cs->readisac(cs, IPACX_STARD);
+		if (cs->debug) 
+      debugl1(cs, "D-Channel Busy RBCHD %02x STARD %02x", rbchd, stard);
+		if (!(stard &0x40)) { // D-Channel Busy
+			set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+      for (st = cs->stlist; st; st = st->next) {
+				st->l1.l1l2(st, PH_PAUSE | INDICATION, NULL); // flow control on
+			}
+		} else {
+			// seems we lost an interrupt; reset transceiver */
+			clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			} else {
+				printk(KERN_WARNING "HiSax: ISAC D-Channel Busy no skb\n");
+				debugl1(cs, "D-Channel Busy no skb");
+			}
+			cs->writeisac(cs, IPACX_CMDRD, 0x01); // Tx reset, generates XPR
+		}
+	}
+}
+
+//----------------------------------------------------------
+// L1 state machine intermediate layer to isdnl1 module
+//----------------------------------------------------------
+static void
+ipacx_new_ph(struct IsdnCardState *cs)
+{
+	switch (cs->dc.isac.ph_state) {
+		case (IPACX_IND_RES):
+			ph_command(cs, IPACX_CMD_DI);
+			l1_msg(cs, HW_RESET | INDICATION, NULL);
+			break;
+      
+		case (IPACX_IND_DC):
+			l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+			break;
+      
+		case (IPACX_IND_DR):
+			l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+			break;
+      
+		case (IPACX_IND_PU):
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+
+		case (IPACX_IND_RSY):
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			break;
+
+		case (IPACX_IND_AR):
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+			break;
+      
+		case (IPACX_IND_AI8):
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+			break;
+      
+		case (IPACX_IND_AI10):
+			l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
+			break;
+      
+		default:
+			break;
+	}
+}
+
+//----------------------------------------------------------
+// bottom half handler for D channel
+//----------------------------------------------------------
+static void
+dch_bh(struct IsdnCardState *cs)
+{
+	struct PStack *st;
+	
+	if (!cs) return;
+  
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+		if (cs->debug) debugl1(cs, "D-Channel Busy cleared");
+		for (st = cs->stlist; st; st = st->next) {
+			st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL);
+		}
+	}
+  
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) {
+		DChannel_proc_rcv(cs);
+  }  
+  
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) {
+		DChannel_proc_xmt(cs);
+  }  
+  
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+    ipacx_new_ph(cs);
+  }  
+}
+
+//----------------------------------------------------------
+// Fill buffer from receive FIFO
+//----------------------------------------------------------
+static void 
+dch_empty_fifo(struct IsdnCardState *cs, int count)
+{
+	u_char *ptr;
+
+	if ((cs->debug &L1_DEB_ISAC) && !(cs->debug &L1_DEB_ISAC_FIFO))
+		debugl1(cs, "dch_empty_fifo()");
+
+  // message too large, remove
+	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		if (cs->debug &L1_DEB_WARN)
+			debugl1(cs, "dch_empty_fifo() incoming message too large");
+	  cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
+		cs->rcvidx = 0;
+		return;
+	}
+  
+	ptr = cs->rcvbuf + cs->rcvidx;
+	cs->rcvidx += count;
+  
+	cs->readisacfifo(cs, ptr, count);
+	cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
+  
+	if (cs->debug &L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "dch_empty_fifo() cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+//----------------------------------------------------------
+// Fill transmit FIFO
+//----------------------------------------------------------
+static void 
+dch_fill_fifo(struct IsdnCardState *cs)
+{
+	int count;
+	u_char cmd, *ptr;
+
+	if ((cs->debug &L1_DEB_ISAC) && !(cs->debug &L1_DEB_ISAC_FIFO))
+		debugl1(cs, "dch_fill_fifo()");
+    
+	if (!cs->tx_skb) return;
+	count = cs->tx_skb->len;
+	if (count <= 0) return;
+
+	if (count > D_FIFO_SIZE) {
+		count = D_FIFO_SIZE;
+		cmd   = 0x08; // XTF
+	} else {
+		cmd   = 0x0A; // XTF | XME
+	}
+  
+	ptr = cs->tx_skb->data;
+	skb_pull(cs->tx_skb, count);
+	cs->tx_cnt += count;
+	cs->writeisacfifo(cs, ptr, count);
+	cs->writeisac(cs, IPACX_CMDRD, cmd);
+  
+  // set timeout for transmission contol
+	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		debugl1(cs, "dch_fill_fifo dbusytimer running");
+		del_timer(&cs->dbusytimer);
+	}
+	init_timer(&cs->dbusytimer);
+	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+	add_timer(&cs->dbusytimer);
+  
+	if (cs->debug &L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "dch_fill_fifo() cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+//----------------------------------------------------------
+// D channel interrupt handler
+//----------------------------------------------------------
+static inline void 
+dch_int(struct IsdnCardState *cs)
+{
+	struct sk_buff *skb;
+	u_char istad, rstad;
+	int count;
+
+	istad = cs->readisac(cs, IPACX_ISTAD);
+//##############################################  
+//	printk(KERN_WARNING "dch_int(istad=%02x)\n", istad);
+//##############################################  
+  
+	if (istad &0x80) {  // RME
+	  rstad = cs->readisac(cs, IPACX_RSTAD);
+		if ((rstad &0xf0) != 0xa0) { // !(VFR && !RDO && CRC && !RAB)
+			if (!(rstad &0x80))
+				if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "dch_int(): invalid frame");
+			if ((rstad &0x40))
+				if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "dch_int(): RDO");
+			if (!(rstad &0x20))
+				if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "dch_int(): CRC error");
+	    cs->writeisac(cs, IPACX_CMDRD, 0x80);  // RMC
+		} else {  // received frame ok
+			count = cs->readisac(cs, IPACX_RBCLD);
+      if (count) count--; // RSTAB is last byte
+			count &= D_FIFO_SIZE-1;
+			if (count == 0) count = D_FIFO_SIZE;
+			dch_empty_fifo(cs, count);
+			if ((count = cs->rcvidx) > 0) {
+	      cs->rcvidx = 0;
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "HiSax dch_int(): receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), cs->rcvbuf, count);
+					skb_queue_tail(&cs->rq, skb);
+				}
+			}
+    }
+	  cs->rcvidx = 0;
+		schedule_event(cs, D_RCVBUFREADY);
+	}
+
+	if (istad &0x40) {  // RPF
+		dch_empty_fifo(cs, D_FIFO_SIZE);
+	}
+
+	if (istad &0x20) {  // RFO
+		if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): RFO");
+	  cs->writeisac(cs, IPACX_CMDRD, 0x40); //RRES
+	}
+  
+  if (istad &0x10) {  // XPR
+		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+    if (cs->tx_skb) {
+      if (cs->tx_skb->len) {
+        dch_fill_fifo(cs);
+        goto afterXPR;
+      }
+      else {
+        dev_kfree_skb_irq(cs->tx_skb);
+        cs->tx_skb = NULL;
+        cs->tx_cnt = 0;
+      }
+    }
+    if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+      cs->tx_cnt = 0;
+      dch_fill_fifo(cs);
+    } 
+    else {
+      schedule_event(cs, D_XMTBUFREADY);
+    }  
+  }  
+  afterXPR:
+
+	if (istad &0x0C) {  // XDU or XMR
+		if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): XDU");
+	  if (cs->tx_skb) {
+	    skb_push(cs->tx_skb, cs->tx_cnt); // retransmit
+	    cs->tx_cnt = 0;
+			dch_fill_fifo(cs);
+		} else {
+			printk(KERN_WARNING "HiSax: ISAC XDU no skb\n");
+			debugl1(cs, "ISAC XDU no skb");
+		}
+  }
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void __devinit
+dch_setstack(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = dch_l2l1;
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void __devinit
+dch_init(struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "HiSax: IPACX ISDN driver v0.1.0\n");
+
+	cs->setstack_d      = dch_setstack;
+  
+	cs->dbusytimer.function = (void *) dbusy_timer_handler;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+
+  cs->writeisac(cs, IPACX_TR_CONF0, 0x00);  // clear LDD
+  cs->writeisac(cs, IPACX_TR_CONF2, 0x00);  // enable transmitter
+  cs->writeisac(cs, IPACX_MODED,    0xC9);  // transparent mode 0, RAC, stop/go
+  cs->writeisac(cs, IPACX_MON_CR,   0x00);  // disable monitor channel
+}
+
+
+//==========================================================
+// B channel functions
+//==========================================================
+
+//----------------------------------------------------------
+// Entry point for commands
+//----------------------------------------------------------
+static void
+bch_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->hw.hscx.count = 0;
+				bch_fill_fifo(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "HiSax bch_l2l1(): this shouldn't happen\n");
+			} else {
+				set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->tx_skb = skb;
+				bcs->hw.hscx.count = 0;
+				bch_fill_fifo(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			bch_mode(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			bch_mode(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+//----------------------------------------------------------
+// Read B channel fifo to receive buffer
+//----------------------------------------------------------
+static void
+bch_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr, hscx;
+	struct IsdnCardState *cs;
+	int cnt;
+
+	cs = bcs->cs;
+  hscx = bcs->hw.hscx.hscx;
+	if ((cs->debug &L1_DEB_HSCX) && !(cs->debug &L1_DEB_HSCX_FIFO))
+		debugl1(cs, "bch_empty_fifo()");
+
+  // message too large, remove
+	if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug &L1_DEB_WARN)
+			debugl1(cs, "bch_empty_fifo() incoming packet too large");
+	  cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80);  // RMC
+		bcs->hw.hscx.rcvidx = 0;
+		return;
+	}
+  
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	cnt = count;
+	while (cnt--) *ptr++ = cs->BC_Read_Reg(cs, hscx, IPACX_RFIFOB); 
+	cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80);  // RMC
+  
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	bcs->hw.hscx.rcvidx += count;
+  
+	if (cs->debug &L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "bch_empty_fifo() B-%d cnt %d", hscx, count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+//----------------------------------------------------------
+// Fill buffer to transmit FIFO
+//----------------------------------------------------------
+static void
+bch_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs;
+	int more, count, cnt;
+	u_char *ptr, *p, hscx;
+
+	cs = bcs->cs;
+	if ((cs->debug &L1_DEB_HSCX) && !(cs->debug &L1_DEB_HSCX_FIFO))
+		debugl1(cs, "bch_fill_fifo()");
+
+	if (!bcs->tx_skb)           return;
+	if (bcs->tx_skb->len <= 0)  return;
+
+	hscx = bcs->hw.hscx.hscx;
+	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+	if (bcs->tx_skb->len > B_FIFO_SIZE) {
+		more  = 1;
+		count = B_FIFO_SIZE;
+	} else {
+		count = bcs->tx_skb->len;
+	}  
+	cnt = count;
+    
+	p = ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hscx.count += count;
+	while (cnt--) cs->BC_Write_Reg(cs, hscx, IPACX_XFIFOB, *p++); 
+	cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, (more ? 0x08 : 0x0a));
+  
+	if (cs->debug &L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "chb_fill_fifo() B-%d cnt %d", hscx, count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+//----------------------------------------------------------
+// B channel interrupt handler
+//----------------------------------------------------------
+static void
+bch_int(struct IsdnCardState *cs, u_char hscx)
+{
+	u_char istab;
+	struct BCState *bcs;
+	struct sk_buff *skb;
+	int count;
+	u_char rstab;
+
+	bcs = cs->bcs + hscx;
+	istab = cs->BC_Read_Reg(cs, hscx, IPACX_ISTAB);
+//##############################################  
+//	printk(KERN_WARNING "bch_int(istab=%02x)\n", istab);
+//##############################################  
+	if (!test_bit(BC_FLG_INIT, &bcs->Flag)) return;
+
+	if (istab &0x80) {	// RME
+		rstab = cs->BC_Read_Reg(cs, hscx, IPACX_RSTAB);
+		if ((rstab &0xf0) != 0xa0) { // !(VFR && !RDO && CRC && !RAB)
+			if (!(rstab &0x80))
+				if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "bch_int() B-%d: invalid frame", hscx);
+			if ((rstab &0x40) && (bcs->mode != L1_MODE_NULL))
+				if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "bch_int() B-%d: RDO mode=%d", hscx, bcs->mode);
+			if (!(rstab &0x20))
+				if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "bch_int() B-%d: CRC error", hscx);
+	    cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80);  // RMC
+		} 
+    else {  // received frame ok
+			count = cs->BC_Read_Reg(cs, hscx, IPACX_RBCLB) &(B_FIFO_SIZE-1);
+			if (count == 0) count = B_FIFO_SIZE;
+			bch_empty_fifo(bcs, count);
+			if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+				if (cs->debug &L1_DEB_HSCX_FIFO)
+					debugl1(cs, "bch_int Frame %d", count);
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "HiSax bch_int(): receive frame out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+					skb_queue_tail(&bcs->rqueue, skb);
+				}
+			}
+		}
+		bcs->hw.hscx.rcvidx = 0;
+		schedule_event(bcs, B_RCVBUFREADY);
+	}
+  
+	if (istab &0x40) {	// RPF
+		bch_empty_fifo(bcs, B_FIFO_SIZE);
+
+		if (bcs->mode == L1_MODE_TRANS) { // queue every chunk
+			// receive transparent audio data
+			if (!(skb = dev_alloc_skb(B_FIFO_SIZE)))
+				printk(KERN_WARNING "HiSax bch_int(): receive transparent out of memory\n");
+			else {
+				memcpy(skb_put(skb, B_FIFO_SIZE), bcs->hw.hscx.rcvbuf, B_FIFO_SIZE);
+				skb_queue_tail(&bcs->rqueue, skb);
+			}
+			bcs->hw.hscx.rcvidx = 0;
+			schedule_event(bcs, B_RCVBUFREADY);
+		}
+	}
+  
+	if (istab &0x20) {	// RFO
+		if (cs->debug &L1_DEB_WARN) 
+			debugl1(cs, "bch_int() B-%d: RFO error", hscx);
+		cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x40);  // RRES
+	}
+
+	if (istab &0x10) {	// XPR
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				bch_fill_fifo(bcs);
+				goto afterXPR;
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->hw.hscx.count;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+			}
+			dev_kfree_skb_irq(bcs->tx_skb);
+			bcs->hw.hscx.count = 0;
+			bcs->tx_skb = NULL;
+    		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hscx.count = 0;
+			set_bit(BC_FLG_BUSY, &bcs->Flag);
+			bch_fill_fifo(bcs);
+		} else {
+			clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+  afterXPR:
+
+	if (istab &0x04) {	// XDU
+    if (bcs->mode == L1_MODE_TRANS) {
+			bch_fill_fifo(bcs);
+    }  
+    else {
+      if (bcs->tx_skb) {  // restart transmitting the whole frame
+        skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+        bcs->tx_cnt += bcs->hw.hscx.count;
+        bcs->hw.hscx.count = 0;
+      }
+	    cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x01);  // XRES
+      if (cs->debug &L1_DEB_WARN)
+        debugl1(cs, "bch_int() B-%d XDU error", hscx);
+    }
+	}
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+bch_mode(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int hscx = bcs->hw.hscx.hscx;
+
+        bc = bc ? 1 : 0;  // in case bc is greater than 1
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "mode_bch() switch B-% mode %d chan %d", hscx, mode, bc);
+	bcs->mode = mode;
+	bcs->channel = bc;
+  
+  // map controller to according timeslot
+  if (!hscx)
+  {
+    cs->writeisac(cs, IPACX_BCHA_TSDP_BC1, 0x80 | bc);
+    cs->writeisac(cs, IPACX_BCHA_CR,       0x88); 
+  }
+  else
+  {
+    cs->writeisac(cs, IPACX_BCHB_TSDP_BC1, 0x80 | bc);
+    cs->writeisac(cs, IPACX_BCHB_CR,       0x88); 
+  }
+
+	switch (mode) {
+		case (L1_MODE_NULL):
+		    cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0xC0);  // rec off
+		    cs->BC_Write_Reg(cs, hscx, IPACX_EXMB,  0x30);  // std adj.
+		    cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, 0xFF);  // ints off
+		    cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41);  // validate adjustments
+		    break;
+		case (L1_MODE_TRANS):
+		    cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0x88);  // ext transp mode
+		    cs->BC_Write_Reg(cs, hscx, IPACX_EXMB,  0x00);  // xxx00000
+		    cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41);  // validate adjustments
+		    cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, _MASKB_IMASK);
+		    break;
+		case (L1_MODE_HDLC):
+		    cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0xC8);  // transp mode 0
+		    cs->BC_Write_Reg(cs, hscx, IPACX_EXMB,  0x01);  // idle=hdlc flags crc enabled
+		    cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41);  // validate adjustments
+		    cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, _MASKB_IMASK);
+		    break;
+	}
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+bch_close_state(struct BCState *bcs)
+{
+	bch_mode(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hscx.rcvbuf) {
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+		}
+		if (bcs->blog) {
+			kfree(bcs->blog);
+			bcs->blog = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static int
+bch_open_state(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax open_bchstate(): No memory for hscx.rcvbuf\n");
+			clear_bit(BC_FLG_INIT, &bcs->Flag);
+			return (1);
+		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax open_bchstate: No memory for bcs->blog\n");
+			clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+			return (2);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.hscx.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static int
+bch_setstack(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (bch_open_state(st->l1.hardware, bcs)) return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = bch_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void __devinit
+bch_init(struct IsdnCardState *cs, int hscx)
+{
+	cs->bcs[hscx].BC_SetStack   = bch_setstack;
+	cs->bcs[hscx].BC_Close      = bch_close_state;
+	cs->bcs[hscx].hw.hscx.hscx  = hscx;
+	cs->bcs[hscx].cs            = cs;
+	bch_mode(cs->bcs + hscx, 0, hscx);
+}
+
+
+//==========================================================
+// Shared functions
+//==========================================================
+
+//----------------------------------------------------------
+// Main interrupt handler
+//----------------------------------------------------------
+void 
+interrupt_ipacx(struct IsdnCardState *cs)
+{
+	u_char ista;
+  
+	while ((ista = cs->readisac(cs, IPACX_ISTA))) {
+//#################################################  
+//		printk(KERN_WARNING "interrupt_ipacx(ista=%02x)\n", ista);
+//#################################################  
+    if (ista &0x80) bch_int(cs, 0); // B channel interrupts
+    if (ista &0x40) bch_int(cs, 1);
+    
+    if (ista &0x01) dch_int(cs);    // D channel
+    if (ista &0x10) cic_int(cs);    // Layer 1 state
+  }  
+}
+
+//----------------------------------------------------------
+// Clears chip interrupt status
+//----------------------------------------------------------
+static void __init
+clear_pending_ints(struct IsdnCardState *cs)
+{
+	int ista;
+
+  // all interrupts off
+  cs->writeisac(cs, IPACX_MASK, 0xff);
+	cs->writeisac(cs, IPACX_MASKD, 0xff);
+	cs->BC_Write_Reg(cs, 0, IPACX_MASKB, 0xff);
+	cs->BC_Write_Reg(cs, 1, IPACX_MASKB, 0xff);
+  
+  ista = cs->readisac(cs, IPACX_ISTA); 
+  if (ista &0x80) cs->BC_Read_Reg(cs, 0, IPACX_ISTAB);
+  if (ista &0x40) cs->BC_Read_Reg(cs, 1, IPACX_ISTAB);
+  if (ista &0x10) cs->readisac(cs, IPACX_CIR0);
+  if (ista &0x01) cs->readisac(cs, IPACX_ISTAD); 
+}
+
+//----------------------------------------------------------
+// Does chip configuration work
+// Work to do depends on bit mask in part
+//----------------------------------------------------------
+void __init
+init_ipacx(struct IsdnCardState *cs, int part)
+{
+	if (part &1) {  // initialise chip
+//##################################################  
+//	printk(KERN_INFO "init_ipacx(%x)\n", part);
+//##################################################  
+		clear_pending_ints(cs);
+		bch_init(cs, 0);
+		bch_init(cs, 1);
+		dch_init(cs);
+	}
+	if (part &2) {  // reenable all interrupts and start chip
+		cs->BC_Write_Reg(cs, 0, IPACX_MASKB, _MASKB_IMASK);
+		cs->BC_Write_Reg(cs, 1, IPACX_MASKB, _MASKB_IMASK);
+		cs->writeisac(cs, IPACX_MASKD, _MASKD_IMASK);
+		cs->writeisac(cs, IPACX_MASK, _MASK_IMASK); // global mask register
+
+		// reset HDLC Transmitters/receivers
+		cs->writeisac(cs, IPACX_CMDRD, 0x41); 
+		cs->BC_Write_Reg(cs, 0, IPACX_CMDRB, 0x41);
+		cs->BC_Write_Reg(cs, 1, IPACX_CMDRB, 0x41);
+		ph_command(cs, IPACX_CMD_RES);
+	}
+}
+
+
+void __devinit
+setup_ipacx(struct IsdnCardState *cs)
+{
+	INIT_WORK(&cs->tqueue, (void *)(void *) dch_bh, cs);
+	cs->dbusytimer.function = (void *) dbusy_timer_handler;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+}
+//----------------- end of file -----------------------
+
diff --git a/drivers/isdn/hisax/ipacx.h b/drivers/isdn/hisax/ipacx.h
new file mode 100644
index 000000000000..e8a22e8f34b6
--- /dev/null
+++ b/drivers/isdn/hisax/ipacx.h
@@ -0,0 +1,162 @@
+/*
+ *
+ * IPACX specific defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec  */
+
+#ifndef INCLUDE_IPACX_H
+#define INCLUDE_IPACX_H
+
+/* D-channel registers   */
+#define IPACX_RFIFOD        0x00    /* RD       */
+#define IPACX_XFIFOD        0x00    /* WR       */
+#define IPACX_ISTAD         0x20    /* RD       */
+#define IPACX_MASKD         0x20    /* WR       */
+#define IPACX_STARD         0x21    /* RD       */
+#define IPACX_CMDRD         0x21    /* WR       */
+#define IPACX_MODED         0x22    /* RD/WR    */
+#define IPACX_EXMD1         0x23    /* RD/WR    */
+#define IPACX_TIMR1         0x24    /* RD/WR    */
+#define IPACX_SAP1          0x25    /* WR       */
+#define IPACX_SAP2          0x26    /* WR       */
+#define IPACX_RBCLD         0x26    /* RD       */
+#define IPACX_RBCHD         0x27    /* RD       */
+#define IPACX_TEI1          0x27    /* WR       */
+#define IPACX_TEI2          0x28    /* WR       */
+#define IPACX_RSTAD         0x28    /* RD       */
+#define IPACX_TMD           0x29    /* RD/WR    */
+#define IPACX_CIR0          0x2E    /* RD       */
+#define IPACX_CIX0          0x2E    /* WR       */
+#define IPACX_CIR1          0x2F    /* RD       */
+#define IPACX_CIX1          0x2F    /* WR       */
+
+/* Transceiver registers    */
+#define IPACX_TR_CONF0      0x30    /* RD/WR    */
+#define IPACX_TR_CONF1      0x31    /* RD/WR    */
+#define IPACX_TR_CONF2      0x32    /* RD/WR    */
+#define IPACX_TR_STA        0x33    /* RD       */
+#define IPACX_TR_CMD        0x34    /* RD/WR    */
+#define IPACX_SQRR1         0x35    /* RD       */
+#define IPACX_SQXR1         0x35    /* WR       */
+#define IPACX_SQRR2         0x36    /* RD       */
+#define IPACX_SQXR2         0x36    /* WR       */
+#define IPACX_SQRR3         0x37    /* RD       */
+#define IPACX_SQXR3         0x37    /* WR       */
+#define IPACX_ISTATR        0x38    /* RD       */
+#define IPACX_MASKTR        0x39    /* RD/WR    */
+#define IPACX_TR_MODE       0x3A    /* RD/WR    */
+#define IPACX_ACFG1         0x3C    /* RD/WR    */
+#define IPACX_ACFG2         0x3D    /* RD/WR    */
+#define IPACX_AOE           0x3E    /* RD/WR    */
+#define IPACX_ARX           0x3F    /* RD       */
+#define IPACX_ATX           0x3F    /* WR       */
+
+/* IOM: Timeslot, DPS, CDA  */
+#define IPACX_CDA10         0x40    /* RD/WR    */
+#define IPACX_CDA11         0x41    /* RD/WR    */
+#define IPACX_CDA20         0x42    /* RD/WR    */
+#define IPACX_CDA21         0x43    /* RD/WR    */
+#define IPACX_CDA_TSDP10    0x44    /* RD/WR    */
+#define IPACX_CDA_TSDP11    0x45    /* RD/WR    */
+#define IPACX_CDA_TSDP20    0x46    /* RD/WR    */
+#define IPACX_CDA_TSDP21    0x47    /* RD/WR    */
+#define IPACX_BCHA_TSDP_BC1 0x48    /* RD/WR    */
+#define IPACX_BCHA_TSDP_BC2 0x49    /* RD/WR    */
+#define IPACX_BCHB_TSDP_BC1 0x4A    /* RD/WR    */
+#define IPACX_BCHB_TSDP_BC2 0x4B    /* RD/WR    */
+#define IPACX_TR_TSDP_BC1   0x4C    /* RD/WR    */
+#define IPACX_TR_TSDP_BC2   0x4D    /* RD/WR    */
+#define IPACX_CDA1_CR       0x4E    /* RD/WR    */
+#define IPACX_CDA2_CR       0x4F    /* RD/WR    */
+
+/* IOM: Contol, Sync transfer, Monitor    */
+#define IPACX_TR_CR         0x50    /* RD/WR    */
+#define IPACX_TRC_CR        0x50    /* RD/WR    */
+#define IPACX_BCHA_CR       0x51    /* RD/WR    */
+#define IPACX_BCHB_CR       0x52    /* RD/WR    */
+#define IPACX_DCI_CR        0x53    /* RD/WR    */
+#define IPACX_DCIC_CR       0x53    /* RD/WR    */
+#define IPACX_MON_CR        0x54    /* RD/WR    */
+#define IPACX_SDS1_CR       0x55    /* RD/WR    */
+#define IPACX_SDS2_CR       0x56    /* RD/WR    */
+#define IPACX_IOM_CR        0x57    /* RD/WR    */
+#define IPACX_STI           0x58    /* RD       */
+#define IPACX_ASTI          0x58    /* WR       */
+#define IPACX_MSTI          0x59    /* RD/WR    */
+#define IPACX_SDS_CONF      0x5A    /* RD/WR    */
+#define IPACX_MCDA          0x5B    /* RD       */
+#define IPACX_MOR           0x5C    /* RD       */
+#define IPACX_MOX           0x5C    /* WR       */
+#define IPACX_MOSR          0x5D    /* RD       */
+#define IPACX_MOCR          0x5E    /* RD/WR    */
+#define IPACX_MSTA          0x5F    /* RD       */
+#define IPACX_MCONF         0x5F    /* WR       */
+
+/* Interrupt and general registers */
+#define IPACX_ISTA          0x60    /* RD       */
+#define IPACX_MASK          0x60    /* WR       */
+#define IPACX_AUXI          0x61    /* RD       */
+#define IPACX_AUXM          0x61    /* WR       */
+#define IPACX_MODE1         0x62    /* RD/WR    */
+#define IPACX_MODE2         0x63    /* RD/WR    */
+#define IPACX_ID            0x64    /* RD       */
+#define IPACX_SRES          0x64    /* WR       */
+#define IPACX_TIMR2         0x65    /* RD/WR    */
+
+/* B-channel registers */
+#define IPACX_OFF_B1        0x70
+#define IPACX_OFF_B2        0x80
+
+#define IPACX_ISTAB         0x00    /* RD       */
+#define IPACX_MASKB         0x00    /* WR       */
+#define IPACX_STARB         0x01    /* RD       */
+#define IPACX_CMDRB         0x01    /* WR       */
+#define IPACX_MODEB         0x02    /* RD/WR    */
+#define IPACX_EXMB          0x03    /* RD/WR    */
+#define IPACX_RAH1          0x05    /* WR       */
+#define IPACX_RAH2          0x06    /* WR       */
+#define IPACX_RBCLB         0x06    /* RD       */
+#define IPACX_RBCHB         0x07    /* RD       */
+#define IPACX_RAL1          0x07    /* WR       */
+#define IPACX_RAL2          0x08    /* WR       */
+#define IPACX_RSTAB         0x08    /* RD       */
+#define IPACX_TMB           0x09    /* RD/WR    */
+#define IPACX_RFIFOB        0x0A    /*- RD      */
+#define IPACX_XFIFOB        0x0A    /*- WR      */
+
+/* Layer 1 Commands */
+#define IPACX_CMD_TIM    0x0
+#define IPACX_CMD_RES    0x1
+#define IPACX_CMD_SSP    0x2
+#define IPACX_CMD_SCP    0x3
+#define IPACX_CMD_AR8    0x8
+#define IPACX_CMD_AR10   0x9
+#define IPACX_CMD_ARL    0xa
+#define IPACX_CMD_DI     0xf
+
+/* Layer 1 Indications */
+#define IPACX_IND_DR     0x0
+#define IPACX_IND_RES    0x1
+#define IPACX_IND_TMA    0x2
+#define IPACX_IND_SLD    0x3
+#define IPACX_IND_RSY    0x4
+#define IPACX_IND_DR6    0x5
+#define IPACX_IND_PU     0x7
+#define IPACX_IND_AR     0x8
+#define IPACX_IND_ARL    0xa
+#define IPACX_IND_CVR    0xb
+#define IPACX_IND_AI8    0xc
+#define IPACX_IND_AI10   0xd
+#define IPACX_IND_AIL    0xe
+#define IPACX_IND_DC     0xf
+
+extern void init_ipacx(struct IsdnCardState *, int);
+extern void interrupt_ipacx(struct IsdnCardState *);
+extern void setup_isac(struct IsdnCardState *);
+
+#endif
diff --git a/drivers/isdn/hisax/isac.c b/drivers/isdn/hisax/isac.c
new file mode 100644
index 000000000000..20b949952952
--- /dev/null
+++ b/drivers/isdn/hisax/isac.c
@@ -0,0 +1,684 @@
+/* $Id: isac.c,v 1.31.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * ISAC specific routines
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ */
+
+#include "hisax.h"
+#include "isac.h"
+#include "arcofi.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#define DBUSY_TIMER_VALUE 80
+#define ARCOFI_USE 1
+
+static char *ISACVer[] __devinitdata =
+{"2086/2186 V1.1", "2085 B1", "2085 B2",
+ "2085 V2.3"};
+
+void
+ISACVersion(struct IsdnCardState *cs, char *s)
+{
+	int val;
+
+	val = cs->readisac(cs, ISAC_RBCH);
+	printk(KERN_INFO "%s ISAC version (%x): %s\n", s, val, ISACVer[(val >> 5) & 3]);
+}
+
+static void
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ph_command %x", command);
+	cs->writeisac(cs, ISAC_CIX0, (command << 2) | 3);
+}
+
+
+static void
+isac_new_ph(struct IsdnCardState *cs)
+{
+	switch (cs->dc.isac.ph_state) {
+		case (ISAC_IND_RS):
+		case (ISAC_IND_EI):
+			ph_command(cs, ISAC_CMD_DUI);
+			l1_msg(cs, HW_RESET | INDICATION, NULL);
+			break;
+		case (ISAC_IND_DID):
+			l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+			break;
+		case (ISAC_IND_DR):
+			l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+			break;
+		case (ISAC_IND_PU):
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (ISAC_IND_RSY):
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			break;
+		case (ISAC_IND_ARD):
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+			break;
+		case (ISAC_IND_AI8):
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+			break;
+		case (ISAC_IND_AI10):
+			l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+isac_bh(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy cleared");
+		stptr = cs->stlist;
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			stptr = stptr->next;
+		}
+	}
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
+		isac_new_ph(cs);		
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+#if ARCOFI_USE
+	if (!test_bit(HW_ARCOFI, &cs->HW_Flags))
+		return;
+	if (test_and_clear_bit(D_RX_MON1, &cs->event))
+		arcofi_fsm(cs, ARCOFI_RX_END, NULL);
+	if (test_and_clear_bit(D_TX_MON1, &cs->event))
+		arcofi_fsm(cs, ARCOFI_TX_END, NULL);
+#endif
+}
+
+void
+isac_empty_fifo(struct IsdnCardState *cs, int count)
+{
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "isac_empty_fifo");
+
+	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "isac_empty_fifo overrun %d",
+				cs->rcvidx + count);
+		cs->writeisac(cs, ISAC_CMDR, 0x80);
+		cs->rcvidx = 0;
+		return;
+	}
+	ptr = cs->rcvbuf + cs->rcvidx;
+	cs->rcvidx += count;
+	cs->readisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ISAC_CMDR, 0x80);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *cs)
+{
+	int count, more;
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "isac_fill_fifo");
+
+	if (!cs->tx_skb)
+		return;
+
+	count = cs->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = cs->tx_skb->data;
+	skb_pull(cs->tx_skb, count);
+	cs->tx_cnt += count;
+	cs->writeisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ISAC_CMDR, more ? 0x8 : 0xa);
+	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		debugl1(cs, "isac_fill_fifo dbusytimer running");
+		del_timer(&cs->dbusytimer);
+	}
+	init_timer(&cs->dbusytimer);
+	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+	add_timer(&cs->dbusytimer);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "isac_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+void
+isac_interrupt(struct IsdnCardState *cs, u_char val)
+{
+	u_char exval, v1;
+	struct sk_buff *skb;
+	unsigned int count;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ISAC interrupt %x", val);
+	if (val & 0x80) {	/* RME */
+		exval = cs->readisac(cs, ISAC_RSTA);
+		if ((exval & 0x70) != 0x20) {
+			if (exval & 0x40) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ISAC RDO");
+#ifdef ERROR_STATISTIC
+				cs->err_rx++;
+#endif
+			}
+			if (!(exval & 0x20)) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ISAC CRC error");
+#ifdef ERROR_STATISTIC
+				cs->err_crc++;
+#endif
+			}
+			cs->writeisac(cs, ISAC_CMDR, 0x80);
+		} else {
+			count = cs->readisac(cs, ISAC_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			isac_empty_fifo(cs, count);
+			if ((count = cs->rcvidx) > 0) {
+				cs->rcvidx = 0;
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "HiSax: D receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), cs->rcvbuf, count);
+					skb_queue_tail(&cs->rq, skb);
+				}
+			}
+		}
+		cs->rcvidx = 0;
+		schedule_event(cs, D_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		isac_empty_fifo(cs, 32);
+	}
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+		if (cs->tx_skb) {
+			if (cs->tx_skb->len) {
+				isac_fill_fifo(cs);
+				goto afterXPR;
+			} else {
+				dev_kfree_skb_irq(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			}
+		}
+		if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+			cs->tx_cnt = 0;
+			isac_fill_fifo(cs);
+		} else
+			schedule_event(cs, D_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		exval = cs->readisac(cs, ISAC_CIR0);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC CIR0 %02X", exval );
+		if (exval & 2) {
+			cs->dc.isac.ph_state = (exval >> 2) & 0xf;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ph_state change %x", cs->dc.isac.ph_state);
+			schedule_event(cs, D_L1STATECHANGE);
+		}
+		if (exval & 1) {
+			exval = cs->readisac(cs, ISAC_CIR1);
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ISAC CIR1 %02X", exval );
+		}
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ISAC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = cs->readisac(cs, ISAC_EXIR);
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ISAC EXIR %02x", exval);
+		if (exval & 0x80) {  /* XMR */
+			debugl1(cs, "ISAC XMR");
+			printk(KERN_WARNING "HiSax: ISAC XMR\n");
+		}
+		if (exval & 0x40) {  /* XDU */
+			debugl1(cs, "ISAC XDU");
+			printk(KERN_WARNING "HiSax: ISAC XDU\n");
+#ifdef ERROR_STATISTIC
+			cs->err_tx++;
+#endif
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			if (cs->tx_skb) { /* Restart frame */
+				skb_push(cs->tx_skb, cs->tx_cnt);
+				cs->tx_cnt = 0;
+				isac_fill_fifo(cs);
+			} else {
+				printk(KERN_WARNING "HiSax: ISAC XDU no skb\n");
+				debugl1(cs, "ISAC XDU no skb");
+			}
+		}
+		if (exval & 0x04) {  /* MOS */
+			v1 = cs->readisac(cs, ISAC_MOSR);
+			if (cs->debug & L1_DEB_MONITOR)
+				debugl1(cs, "ISAC MOSR %02x", v1);
+#if ARCOFI_USE
+			if (v1 & 0x08) {
+				if (!cs->dc.isac.mon_rx) {
+					if (!(cs->dc.isac.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ISAC MON RX out of memory!");
+						cs->dc.isac.mocr &= 0xf0;
+						cs->dc.isac.mocr |= 0x0a;
+						cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+						goto afterMONR0;
+					} else
+						cs->dc.isac.mon_rxp = 0;
+				}
+				if (cs->dc.isac.mon_rxp >= MAX_MON_FRAME) {
+					cs->dc.isac.mocr &= 0xf0;
+					cs->dc.isac.mocr |= 0x0a;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+					cs->dc.isac.mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ISAC MON RX overflow!");
+					goto afterMONR0;
+				}
+				cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp++] = cs->readisac(cs, ISAC_MOR0);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC MOR0 %02x", cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp -1]);
+				if (cs->dc.isac.mon_rxp == 1) {
+					cs->dc.isac.mocr |= 0x04;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+				}
+			}
+		      afterMONR0:
+			if (v1 & 0x80) {
+				if (!cs->dc.isac.mon_rx) {
+					if (!(cs->dc.isac.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ISAC MON RX out of memory!");
+						cs->dc.isac.mocr &= 0x0f;
+						cs->dc.isac.mocr |= 0xa0;
+						cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+						goto afterMONR1;
+					} else
+						cs->dc.isac.mon_rxp = 0;
+				}
+				if (cs->dc.isac.mon_rxp >= MAX_MON_FRAME) {
+					cs->dc.isac.mocr &= 0x0f;
+					cs->dc.isac.mocr |= 0xa0;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+					cs->dc.isac.mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ISAC MON RX overflow!");
+					goto afterMONR1;
+				}
+				cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp++] = cs->readisac(cs, ISAC_MOR1);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC MOR1 %02x", cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp -1]);
+				cs->dc.isac.mocr |= 0x40;
+				cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+			}
+		      afterMONR1:
+			if (v1 & 0x04) {
+				cs->dc.isac.mocr &= 0xf0;
+				cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+				cs->dc.isac.mocr |= 0x0a;
+				cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+				schedule_event(cs, D_RX_MON0);
+			}
+			if (v1 & 0x40) {
+				cs->dc.isac.mocr &= 0x0f;
+				cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+				cs->dc.isac.mocr |= 0xa0;
+				cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+				schedule_event(cs, D_RX_MON1);
+			}
+			if (v1 & 0x02) {
+				if ((!cs->dc.isac.mon_tx) || (cs->dc.isac.mon_txc && 
+					(cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc) && 
+					!(v1 & 0x08))) {
+					cs->dc.isac.mocr &= 0xf0;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+					cs->dc.isac.mocr |= 0x0a;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+					if (cs->dc.isac.mon_txc &&
+						(cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc))
+						schedule_event(cs, D_TX_MON0);
+					goto AfterMOX0;
+				}
+				if (cs->dc.isac.mon_txc && (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) {
+					schedule_event(cs, D_TX_MON0);
+					goto AfterMOX0;
+				}
+				cs->writeisac(cs, ISAC_MOX0,
+					cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC %02x -> MOX0", cs->dc.isac.mon_tx[cs->dc.isac.mon_txp -1]);
+			}
+		      AfterMOX0:
+			if (v1 & 0x20) {
+				if ((!cs->dc.isac.mon_tx) || (cs->dc.isac.mon_txc && 
+					(cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc) && 
+					!(v1 & 0x80))) {
+					cs->dc.isac.mocr &= 0x0f;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+					cs->dc.isac.mocr |= 0xa0;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+					if (cs->dc.isac.mon_txc &&
+						(cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc))
+						schedule_event(cs, D_TX_MON1);
+					goto AfterMOX1;
+				}
+				if (cs->dc.isac.mon_txc && (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) {
+					schedule_event(cs, D_TX_MON1);
+					goto AfterMOX1;
+				}
+				cs->writeisac(cs, ISAC_MOX1,
+					cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC %02x -> MOX1", cs->dc.isac.mon_tx[cs->dc.isac.mon_txp -1]);
+			}
+		      AfterMOX1:;
+#endif
+		}
+	}
+}
+
+static void
+ISAC_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	u_long flags;
+	int  val;
+
+	switch (pr) {
+		case (PH_DATA |REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				isac_fill_fifo(cs);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL |INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+			} else {
+				if (cs->debug & DEB_DLOG_HEX)
+					LogFrame(cs, skb->data, skb->len);
+				if (cs->debug & DEB_DLOG_VERBOSE)
+					dlogframe(cs, skb, 0);
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+				isac_fill_fifo(cs);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			if ((cs->dc.isac.ph_state == ISAC_IND_EI) ||
+				(cs->dc.isac.ph_state == ISAC_IND_DR) ||
+				(cs->dc.isac.ph_state == ISAC_IND_RS))
+			        ph_command(cs, ISAC_CMD_TIM);
+			else
+				ph_command(cs, ISAC_CMD_RS);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_ENABLE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, ISAC_CMD_TIM);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO3 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, ISAC_CMD_AR8);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			val = 0;
+			if (1 & (long) arg)
+				val |= 0x0c;
+			if (2 & (long) arg)
+				val |= 0x3;
+			if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+				/* IOM 1 Mode */
+				if (!val) {
+					cs->writeisac(cs, ISAC_SPCR, 0xa);
+					cs->writeisac(cs, ISAC_ADF1, 0x2);
+				} else {
+					cs->writeisac(cs, ISAC_SPCR, val);
+					cs->writeisac(cs, ISAC_ADF1, 0xa);
+				}
+			} else {
+				/* IOM 2 Mode */
+				cs->writeisac(cs, ISAC_SPCR, val);
+				if (val)
+					cs->writeisac(cs, ISAC_ADF1, 0x8);
+				else
+					cs->writeisac(cs, ISAC_ADF1, 0x0);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_DEACTIVATE | RESPONSE):
+			skb_queue_purge(&cs->rq);
+			skb_queue_purge(&cs->sq);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_skb = NULL;
+			}
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isac_l1hw unknown %04x", pr);
+			break;
+	}
+}
+
+void
+setstack_isac(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = ISAC_l1hw;
+}
+
+void 
+DC_Close_isac(struct IsdnCardState *cs) {
+	if (cs->dc.isac.mon_rx) {
+		kfree(cs->dc.isac.mon_rx);
+		cs->dc.isac.mon_rx = NULL;
+	}
+	if (cs->dc.isac.mon_tx) {
+		kfree(cs->dc.isac.mon_tx);
+		cs->dc.isac.mon_tx = NULL;
+	}
+}
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	int	rbch, star;
+
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		rbch = cs->readisac(cs, ISAC_RBCH);
+		star = cs->readisac(cs, ISAC_STAR);
+		if (cs->debug) 
+			debugl1(cs, "D-Channel Busy RBCH %02x STAR %02x",
+				rbch, star);
+		if (rbch & ISAC_RBCH_XAC) { /* D-Channel Busy */
+			test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+			stptr = cs->stlist;
+			while (stptr != NULL) {
+				stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+				stptr = stptr->next;
+			}
+		} else {
+			/* discard frame; reset transceiver */
+			test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			} else {
+				printk(KERN_WARNING "HiSax: ISAC D-Channel Busy no skb\n");
+				debugl1(cs, "D-Channel Busy no skb");
+			}
+			cs->writeisac(cs, ISAC_CMDR, 0x01); /* Transmitter reset */
+			cs->irq_func(cs->irq, cs, NULL);
+		}
+	}
+}
+
+void __devinit
+initisac(struct IsdnCardState *cs)
+{
+	cs->setstack_d = setstack_isac;
+	cs->DC_Close = DC_Close_isac;
+	cs->dc.isac.mon_tx = NULL;
+	cs->dc.isac.mon_rx = NULL;
+  	cs->writeisac(cs, ISAC_MASK, 0xff);
+  	cs->dc.isac.mocr = 0xaa;
+	if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+		/* IOM 1 Mode */
+		cs->writeisac(cs, ISAC_ADF2, 0x0);
+		cs->writeisac(cs, ISAC_SPCR, 0xa);
+		cs->writeisac(cs, ISAC_ADF1, 0x2);
+		cs->writeisac(cs, ISAC_STCR, 0x70);
+		cs->writeisac(cs, ISAC_MODE, 0xc9);
+	} else {
+		/* IOM 2 Mode */
+		if (!cs->dc.isac.adf2)
+			cs->dc.isac.adf2 = 0x80;
+		cs->writeisac(cs, ISAC_ADF2, cs->dc.isac.adf2);
+		cs->writeisac(cs, ISAC_SQXR, 0x2f);
+		cs->writeisac(cs, ISAC_SPCR, 0x00);
+		cs->writeisac(cs, ISAC_STCR, 0x70);
+		cs->writeisac(cs, ISAC_MODE, 0xc9);
+		cs->writeisac(cs, ISAC_TIMR, 0x00);
+		cs->writeisac(cs, ISAC_ADF1, 0x00);
+	}
+	ph_command(cs, ISAC_CMD_RS);
+	cs->writeisac(cs, ISAC_MASK, 0x0);
+}
+
+void __devinit
+clear_pending_isac_ints(struct IsdnCardState *cs)
+{
+	int val, eval;
+
+	val = cs->readisac(cs, ISAC_STAR);
+	debugl1(cs, "ISAC STAR %x", val);
+	val = cs->readisac(cs, ISAC_MODE);
+	debugl1(cs, "ISAC MODE %x", val);
+	val = cs->readisac(cs, ISAC_ADF2);
+	debugl1(cs, "ISAC ADF2 %x", val);
+	val = cs->readisac(cs, ISAC_ISTA);
+	debugl1(cs, "ISAC ISTA %x", val);
+	if (val & 0x01) {
+		eval = cs->readisac(cs, ISAC_EXIR);
+		debugl1(cs, "ISAC EXIR %x", eval);
+	}
+	val = cs->readisac(cs, ISAC_CIR0);
+	debugl1(cs, "ISAC CIR0 %x", val);
+	cs->dc.isac.ph_state = (val >> 2) & 0xf;
+	schedule_event(cs, D_L1STATECHANGE);
+	/* Disable all IRQ */
+	cs->writeisac(cs, ISAC_MASK, 0xFF);
+}
+
+void __devinit
+setup_isac(struct IsdnCardState *cs)
+{
+	INIT_WORK(&cs->tqueue, (void *)(void *) isac_bh, cs);
+	cs->dbusytimer.function = (void *) dbusy_timer_handler;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+}
diff --git a/drivers/isdn/hisax/isac.h b/drivers/isdn/hisax/isac.h
new file mode 100644
index 000000000000..8f8331e44866
--- /dev/null
+++ b/drivers/isdn/hisax/isac.h
@@ -0,0 +1,70 @@
+/* $Id: isac.h,v 1.9.2.2 2004/01/12 22:52:27 keil Exp $
+ *
+ * ISAC specific defines
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec  */
+
+#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
+
+extern void ISACVersion(struct IsdnCardState *, char *);
+extern void setup_isac(struct IsdnCardState *);
+extern void initisac(struct IsdnCardState *);
+extern void isac_interrupt(struct IsdnCardState *, u_char);
+extern void clear_pending_isac_ints(struct IsdnCardState *);
diff --git a/drivers/isdn/hisax/isar.c b/drivers/isdn/hisax/isar.c
new file mode 100644
index 000000000000..ee081321efb2
--- /dev/null
+++ b/drivers/isdn/hisax/isar.c
@@ -0,0 +1,1909 @@
+/* $Id: isar.c,v 1.22.2.6 2004/02/11 13:21:34 keil Exp $
+ *
+ * isar.c   ISAR (Siemens PSB 7110) specific routines
+ *
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ * This file is (c) under GNU General Public License
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isar.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+#define DBG_LOADFIRM	0
+#define DUMP_MBOXFRAME	2
+
+#define DLE	0x10
+#define ETX	0x03
+
+#define FAXMODCNT	13
+const	u_char	faxmodulation[] = {3,24,48,72,73,74,96,97,98,121,122,145,146};
+static	u_int	modmask = 0x1fff;
+static	int	frm_extra_delay = 2;
+static	int	para_TOA = 6;
+const   u_char  *FC1_CMD[] = {"FAE", "FTS", "FRS", "FTM", "FRM", "FTH", "FRH", "CTRL" };
+
+void isar_setup(struct IsdnCardState *cs);
+static void isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para);
+static void ll_deliver_faxstat(struct BCState *bcs, u_char status);
+
+static inline int
+waitforHIA(struct IsdnCardState *cs, int timeout)
+{
+
+	while ((cs->BC_Read_Reg(cs, 0, ISAR_HIA) & 1) && timeout) {
+		udelay(1);
+		timeout--;
+	}
+	if (!timeout)
+		printk(KERN_WARNING "HiSax: ISAR waitforHIA timeout\n");
+	return(timeout);
+}
+
+
+int
+sendmsg(struct IsdnCardState *cs, u_char his, u_char creg, u_char len,
+	u_char *msg)
+{
+	int i;
+	
+	if (!waitforHIA(cs, 4000))
+		return(0);
+#if DUMP_MBOXFRAME
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "sendmsg(%02x,%02x,%d)", his, creg, len);
+#endif
+	cs->BC_Write_Reg(cs, 0, ISAR_CTRL_H, creg);
+	cs->BC_Write_Reg(cs, 0, ISAR_CTRL_L, len);
+	cs->BC_Write_Reg(cs, 0, ISAR_WADR, 0);
+	if (msg && len) {
+		cs->BC_Write_Reg(cs, 1, ISAR_MBOX, msg[0]);
+		for (i=1; i<len; i++)
+			cs->BC_Write_Reg(cs, 2, ISAR_MBOX, msg[i]);
+#if DUMP_MBOXFRAME>1
+		if (cs->debug & L1_DEB_HSCX_FIFO) {
+			char tmp[256], *t;
+			
+			i = len;
+			while (i>0) {
+				t = tmp;
+				t += sprintf(t, "sendmbox cnt %d", len);
+				QuickHex(t, &msg[len-i], (i>64) ? 64:i);
+				debugl1(cs, tmp);
+				i -= 64;
+			}
+		}
+#endif
+	}
+	cs->BC_Write_Reg(cs, 1, ISAR_HIS, his);
+	waitforHIA(cs, 10000);
+	return(1);
+}
+
+/* Call only with IRQ disabled !!! */
+inline void
+rcv_mbox(struct IsdnCardState *cs, struct isar_reg *ireg, u_char *msg)
+{
+	int i;
+
+	cs->BC_Write_Reg(cs, 1, ISAR_RADR, 0);
+	if (msg && ireg->clsb) {
+		msg[0] = cs->BC_Read_Reg(cs, 1, ISAR_MBOX);
+		for (i=1; i < ireg->clsb; i++)
+			 msg[i] = cs->BC_Read_Reg(cs, 2, ISAR_MBOX);
+#if DUMP_MBOXFRAME>1
+		if (cs->debug & L1_DEB_HSCX_FIFO) {
+			char tmp[256], *t;
+			
+			i = ireg->clsb;
+			while (i>0) {
+				t = tmp;
+				t += sprintf(t, "rcv_mbox cnt %d", ireg->clsb);
+				QuickHex(t, &msg[ireg->clsb-i], (i>64) ? 64:i);
+				debugl1(cs, tmp);
+				i -= 64;
+			}
+		}
+#endif
+	}
+	cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+}
+
+/* Call only with IRQ disabled !!! */
+inline void
+get_irq_infos(struct IsdnCardState *cs, struct isar_reg *ireg)
+{
+	ireg->iis = cs->BC_Read_Reg(cs, 1, ISAR_IIS);
+	ireg->cmsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_H);
+	ireg->clsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_L);
+#if DUMP_MBOXFRAME
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "irq_stat(%02x,%02x,%d)", ireg->iis, ireg->cmsb,
+			ireg->clsb);
+#endif
+}
+
+int
+waitrecmsg(struct IsdnCardState *cs, u_char *len,
+	u_char *msg, int maxdelay)
+{
+	int timeout = 0;
+	struct isar_reg *ir = cs->bcs[0].hw.isar.reg;
+	
+	
+	while((!(cs->BC_Read_Reg(cs, 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(cs, ir);
+	rcv_mbox(cs, ir, msg);
+	*len = ir->clsb;
+	return(1);
+}
+
+int
+ISARVersion(struct IsdnCardState *cs, char *s)
+{
+	int ver;
+	u_char msg[] = ISAR_MSG_HWVER;
+	u_char tmp[64];
+	u_char len;
+	u_long flags;
+	int debug;
+
+	cs->cardmsg(cs, CARD_RESET,  NULL);
+	spin_lock_irqsave(&cs->lock, flags);
+	/* disable ISAR IRQ */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	debug = cs->debug;
+	cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
+	if (!sendmsg(cs, ISAR_HIS_VNR, 0, 3, msg)) {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return(-1);
+	}
+	if (!waitrecmsg(cs, &len, tmp, 100000)) {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return(-2);
+	}
+	cs->debug = debug;
+	if (cs->bcs[0].hw.isar.reg->iis == ISAR_IIS_VNR) {
+		if (len == 1) {
+			ver = tmp[0] & 0xf;
+			printk(KERN_INFO "%s ISAR version %d\n", s, ver);
+		} else
+			ver = -3;
+	} else
+		ver = -4;
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return(ver);
+}
+
+int
+isar_load_firmware(struct IsdnCardState *cs, u_char __user *buf)
+{
+	int ret, size, cnt, debug;
+	u_char len, nom, noc;
+	u_short sadr, left, *sp;
+	u_char __user *p = buf;
+	u_char *msg, *tmpmsg, *mp, tmp[64];
+	u_long flags;
+	struct isar_reg *ireg = cs->bcs[0].hw.isar.reg;
+	
+	struct {u_short sadr;
+		u_short len;
+		u_short d_key;
+	} blk_head;
+		
+#define	BLK_HEAD_SIZE 6
+	if (1 != (ret = ISARVersion(cs, "Testing"))) {
+		printk(KERN_ERR"isar_load_firmware wrong isar version %d\n", ret);
+		return(1);
+	}
+	debug = cs->debug;
+#if DBG_LOADFIRM<2
+	cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
+#endif
+	
+	if ((ret = copy_from_user(&size, p, sizeof(int)))) {
+		printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+		return ret;
+	}
+	p += sizeof(int);
+	printk(KERN_DEBUG"isar_load_firmware size: %d\n", size);
+	cnt = 0;
+	/* disable ISAR IRQ */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	if (!(msg = kmalloc(256, GFP_KERNEL))) {
+		printk(KERN_ERR"isar_load_firmware no buffer\n");
+		return (1);
+	}
+	if (!(tmpmsg = kmalloc(256, GFP_KERNEL))) {
+		printk(KERN_ERR"isar_load_firmware no tmp buffer\n");
+		kfree(msg);
+		return (1);
+	}
+	spin_lock_irqsave(&cs->lock, flags);
+	/* disable ISAR IRQ */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	while (cnt < size) {
+		if ((ret = copy_from_user(&blk_head, p, BLK_HEAD_SIZE))) {
+			printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+			goto reterror;
+		}
+#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;
+		spin_lock_irqsave(&cs->lock, flags);
+		if (!sendmsg(cs, ISAR_HIS_DKEY, blk_head.d_key & 0xff, 0, NULL)) {
+			printk(KERN_ERR"isar sendmsg dkey failed\n");
+			ret = 1;goto reterr_unlock;
+		}
+		if (!waitrecmsg(cs, &len, tmp, 100000)) {
+			printk(KERN_ERR"isar waitrecmsg dkey failed\n");
+			ret = 1;goto reterr_unlock;
+		}
+		if ((ireg->iis != ISAR_IIS_DKEY) || ireg->cmsb || len) {
+			printk(KERN_ERR"isar wrong dkey response (%x,%x,%x)\n",
+				ireg->iis, ireg->cmsb, len);
+			ret = 1;goto reterr_unlock;
+		}
+		spin_unlock_irqrestore(&cs->lock, flags);
+		while (left>0) {
+			if (left > 126)
+				noc = 126;
+			else
+				noc = left;
+			nom = 2*noc;
+			mp  = msg;
+			*mp++ = sadr / 256;
+			*mp++ = sadr % 256;
+			left -= noc;
+			*mp++ = noc;
+			if ((ret = copy_from_user(tmpmsg, p, nom))) {
+				printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+				goto reterror;
+			}
+			p += nom;
+			cnt += nom;
+			nom += 3;
+			sp = (u_short *)tmpmsg;
+#if DBG_LOADFIRM
+			printk(KERN_DEBUG"isar: load %3d words at %04x left %d\n",
+				 noc, sadr, left);
+#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--;
+			}
+			spin_lock_irqsave(&cs->lock, flags);
+			if (!sendmsg(cs, ISAR_HIS_FIRM, 0, nom, msg)) {
+				printk(KERN_ERR"isar sendmsg prog failed\n");
+				ret = 1;goto reterr_unlock;
+			}
+			if (!waitrecmsg(cs, &len, tmp, 100000)) {
+				printk(KERN_ERR"isar waitrecmsg prog failed\n");
+				ret = 1;goto reterr_unlock;
+			}
+			if ((ireg->iis != ISAR_IIS_FIRM) || ireg->cmsb || len) {
+				printk(KERN_ERR"isar wrong prog response (%x,%x,%x)\n",
+					ireg->iis, ireg->cmsb, len);
+				ret = 1;goto reterr_unlock;
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+		}
+		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;
+	ireg->bstat = 0;
+	spin_lock_irqsave(&cs->lock, flags);
+	if (!sendmsg(cs, ISAR_HIS_STDSP, 0, 2, msg)) {
+		printk(KERN_ERR"isar sendmsg start dsp failed\n");
+		ret = 1;goto reterr_unlock;
+	}
+	if (!waitrecmsg(cs, &len, tmp, 100000)) {
+		printk(KERN_ERR"isar waitrecmsg start dsp failed\n");
+		ret = 1;goto reterr_unlock;
+	}
+	if ((ireg->iis != ISAR_IIS_STDSP) || ireg->cmsb || len) {
+		printk(KERN_ERR"isar wrong start dsp response (%x,%x,%x)\n",
+			ireg->iis, ireg->cmsb, len);
+		ret = 1;goto reterr_unlock;
+	} else
+		printk(KERN_DEBUG"isar start dsp success\n");
+	/* NORMAL mode entered */
+	/* Enable IRQs of ISAR */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, ISAR_IRQSTA);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	cnt = 1000; /* max 1s */
+	while ((!ireg->bstat) && cnt) {
+		udelay(1000);
+		cnt--;
+	}
+	if (!cnt) {
+		printk(KERN_ERR"isar no general status event received\n");
+		ret = 1;goto reterror;
+	} else {
+		printk(KERN_DEBUG"isar general status event %x\n",
+			ireg->bstat);
+	}
+	/* 10ms delay */
+	cnt = 10;
+	while (cnt--)
+		udelay(1000);
+	spin_lock_irqsave(&cs->lock, flags);
+	ireg->iis = 0;
+	if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) {
+		printk(KERN_ERR"isar sendmsg self tst failed\n");
+		ret = 1;goto reterr_unlock;
+	}
+	cnt = 10000; /* max 100 ms */
+	spin_unlock_irqrestore(&cs->lock, flags);
+	while ((ireg->iis != ISAR_IIS_DIAG) && cnt) {
+		udelay(10);
+		cnt--;
+	}
+	udelay(1000);
+	if (!cnt) {
+		printk(KERN_ERR"isar no self tst response\n");
+		ret = 1;goto reterror;
+	}
+	if ((ireg->cmsb == ISAR_CTRL_STST) && (ireg->clsb == 1)
+		&& (ireg->par[0] == 0)) {
+		printk(KERN_DEBUG"isar selftest OK\n");
+	} else {
+		printk(KERN_DEBUG"isar selftest not OK %x/%x/%x\n",
+			ireg->cmsb, ireg->clsb, ireg->par[0]);
+		ret = 1;goto reterror;
+	}
+	spin_lock_irqsave(&cs->lock, flags);
+	ireg->iis = 0;
+	if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) {
+		printk(KERN_ERR"isar RQST SVN failed\n");
+		ret = 1;goto reterr_unlock;
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	cnt = 30000; /* max 300 ms */
+	while ((ireg->iis != ISAR_IIS_DIAG) && cnt) {
+		udelay(10);
+		cnt--;
+	}
+	udelay(1000);
+	if (!cnt) {
+		printk(KERN_ERR"isar no SVN response\n");
+		ret = 1;goto reterror;
+	} else {
+		if ((ireg->cmsb == ISAR_CTRL_SWVER) && (ireg->clsb == 1))
+			printk(KERN_DEBUG"isar software version %#x\n",
+				ireg->par[0]);
+		else {
+			printk(KERN_ERR"isar wrong swver response (%x,%x) cnt(%d)\n",
+				ireg->cmsb, ireg->clsb, cnt);
+			ret = 1;goto reterror;
+		}
+	}
+	spin_lock_irqsave(&cs->lock, flags);
+	cs->debug = debug;
+	isar_setup(cs);
+
+	ret = 0;
+reterr_unlock:
+	spin_unlock_irqrestore(&cs->lock, flags);
+reterror:
+	cs->debug = debug;
+	if (ret)
+		/* disable ISAR IRQ */
+		cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	kfree(msg);
+	kfree(tmpmsg);
+	return(ret);
+}
+
+extern void BChannel_bh(struct BCState *);
+#define B_LL_NOCARRIER	8
+#define B_LL_CONNECT	9
+#define B_LL_OK		10
+
+static void
+isar_bh(struct BCState *bcs)
+{
+	BChannel_bh(bcs);
+	if (test_and_clear_bit(B_LL_NOCARRIER, &bcs->event))
+		ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_NOCARR);
+	if (test_and_clear_bit(B_LL_CONNECT, &bcs->event))
+		ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+	if (test_and_clear_bit(B_LL_OK, &bcs->event))
+		ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_OK);
+}
+
+static void
+send_DLE_ETX(struct BCState *bcs)
+{
+	u_char dleetx[2] = {DLE,ETX};
+	struct sk_buff *skb;
+	
+	if ((skb = dev_alloc_skb(2))) {
+		memcpy(skb_put(skb, 2), dleetx, 2);
+		skb_queue_tail(&bcs->rqueue, skb);
+		schedule_event(bcs, B_RCVBUFREADY);
+	} else {
+		printk(KERN_WARNING "HiSax: skb out of memory\n");
+	}
+}
+
+static inline int
+dle_count(unsigned char *buf, int len)
+{
+	int count = 0;
+
+	while (len--)
+		if (*buf++ == DLE)
+			count++;
+	return count;
+}
+
+static inline void
+insert_dle(unsigned char *dest, unsigned char *src, int count) {
+	/* <DLE> in input stream have to be flagged as <DLE><DLE> */
+	while (count--) {
+		*dest++ = *src;
+		if (*src++ == DLE)
+			*dest++ = DLE;
+	}
+}
+ 
+static void
+isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	u_char *ptr;
+	struct sk_buff *skb;
+	struct isar_reg *ireg = bcs->hw.isar.reg;
+	
+	if (!ireg->clsb) {
+		debugl1(cs, "isar zero len frame");
+		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		return;
+	}
+	switch (bcs->mode) {
+	case L1_MODE_NULL:
+		debugl1(cs, "isar mode 0 spurious IIS_RDATA %x/%x/%x",
+			ireg->iis, ireg->cmsb, ireg->clsb);
+		printk(KERN_WARNING"isar mode 0 spurious IIS_RDATA %x/%x/%x\n",
+			ireg->iis, ireg->cmsb, ireg->clsb);
+		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		break;
+	case L1_MODE_TRANS:
+	case L1_MODE_V32:
+		if ((skb = dev_alloc_skb(ireg->clsb))) {
+			rcv_mbox(cs, ireg, (u_char *)skb_put(skb, ireg->clsb));
+			skb_queue_tail(&bcs->rqueue, skb);
+			schedule_event(bcs, B_RCVBUFREADY);
+		} else {
+			printk(KERN_WARNING "HiSax: skb out of memory\n");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		}
+		break;
+	case L1_MODE_HDLC:
+		if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: incoming packet too large");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+		} else if (ireg->cmsb & HDLC_ERROR) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar frame error %x len %d",
+					ireg->cmsb, ireg->clsb);
+#ifdef ERROR_STATISTIC
+			if (ireg->cmsb & HDLC_ERR_RER)
+				bcs->err_inv++;
+			if (ireg->cmsb & HDLC_ERR_CER)
+				bcs->err_crc++;
+#endif
+			bcs->hw.isar.rcvidx = 0;
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		} else {
+			if (ireg->cmsb & HDLC_FSD)
+				bcs->hw.isar.rcvidx = 0;
+			ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx;
+			bcs->hw.isar.rcvidx += ireg->clsb;
+			rcv_mbox(cs, ireg, ptr);
+			if (ireg->cmsb & HDLC_FED) {
+				if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar frame to short %d",
+							bcs->hw.isar.rcvidx);
+				} else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx-2))) {
+					printk(KERN_WARNING "ISAR: receive out of memory\n");
+				} else {
+					memcpy(skb_put(skb, bcs->hw.isar.rcvidx-2),
+						bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx-2);
+					skb_queue_tail(&bcs->rqueue, skb);
+					schedule_event(bcs, B_RCVBUFREADY);
+				}
+				bcs->hw.isar.rcvidx = 0;
+			}
+		}
+		break;
+	case L1_MODE_FAX:
+		if (bcs->hw.isar.state != STFAX_ACTIV) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: not ACTIV");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+			break;
+		}
+		if (bcs->hw.isar.cmd == PCTRL_CMD_FRM) {
+			rcv_mbox(cs, ireg, bcs->hw.isar.rcvbuf);
+			bcs->hw.isar.rcvidx = ireg->clsb +
+				dle_count(bcs->hw.isar.rcvbuf, ireg->clsb);
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "isar_rcv_frame: raw(%d) dle(%d)",
+					ireg->clsb, bcs->hw.isar.rcvidx);
+			if ((skb = dev_alloc_skb(bcs->hw.isar.rcvidx))) {
+				insert_dle((u_char *)skb_put(skb, bcs->hw.isar.rcvidx),
+					bcs->hw.isar.rcvbuf, ireg->clsb);
+				skb_queue_tail(&bcs->rqueue, skb);
+				schedule_event(bcs, B_RCVBUFREADY);
+				if (ireg->cmsb & SART_NMD) { /* ABORT */
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar_rcv_frame: no more data");
+					bcs->hw.isar.rcvidx = 0;
+					send_DLE_ETX(bcs);
+					sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) |
+						ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC,
+						0, NULL);
+					bcs->hw.isar.state = STFAX_ESCAPE;
+					schedule_event(bcs, B_LL_NOCARRIER);
+				}
+			} else {
+				printk(KERN_WARNING "HiSax: skb out of memory\n");
+			}
+			break;
+		}
+		if (bcs->hw.isar.cmd != PCTRL_CMD_FRH) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: unknown fax mode %x",
+					bcs->hw.isar.cmd);
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+			break;
+		}
+		/* PCTRL_CMD_FRH */
+		if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: incoming packet too large");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+		} else if (ireg->cmsb & HDLC_ERROR) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar frame error %x len %d",
+					ireg->cmsb, ireg->clsb);
+			bcs->hw.isar.rcvidx = 0;
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		} else {
+			if (ireg->cmsb & HDLC_FSD) {
+				bcs->hw.isar.rcvidx = 0;
+			}
+			ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx;
+			bcs->hw.isar.rcvidx += ireg->clsb;
+			rcv_mbox(cs, ireg, ptr);
+			if (ireg->cmsb & HDLC_FED) {
+				int len = bcs->hw.isar.rcvidx +
+					dle_count(bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx);
+				if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar frame to short %d",
+							bcs->hw.isar.rcvidx);
+					printk(KERN_WARNING "ISAR: frame to short %d\n",
+						bcs->hw.isar.rcvidx);
+				} else if (!(skb = dev_alloc_skb(len))) {
+					printk(KERN_WARNING "ISAR: receive out of memory\n");
+				} else {
+					insert_dle((u_char *)skb_put(skb, len),
+						bcs->hw.isar.rcvbuf,
+						bcs->hw.isar.rcvidx);
+					skb_queue_tail(&bcs->rqueue, skb);
+					schedule_event(bcs, B_RCVBUFREADY);
+					send_DLE_ETX(bcs);
+					schedule_event(bcs, B_LL_OK);
+					test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag);
+				}
+				bcs->hw.isar.rcvidx = 0;
+			}
+		}
+		if (ireg->cmsb & SART_NMD) { /* ABORT */
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: no more data");
+			bcs->hw.isar.rcvidx = 0;
+			sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) |
+				ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL);
+			bcs->hw.isar.state = STFAX_ESCAPE;
+			if (test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag)) {
+				send_DLE_ETX(bcs);
+				schedule_event(bcs, B_LL_NOCARRIER);
+			}
+		}
+		break;
+	default:
+		printk(KERN_ERR"isar_rcv_frame mode (%x)error\n", bcs->mode);
+		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		break;
+	}
+}
+
+void
+isar_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int count;
+	u_char msb;
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "isar_fill_fifo");
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+	if (!(bcs->hw.isar.reg->bstat & 
+		(bcs->hw.isar.dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2)))
+		return;
+	if (bcs->tx_skb->len > bcs->hw.isar.mml) {
+		msb = 0;
+		count = bcs->hw.isar.mml;
+	} else {
+		count = bcs->tx_skb->len;
+		msb = HDLC_FED;
+	}
+	ptr = bcs->tx_skb->data;
+	if (!bcs->hw.isar.txcnt) {
+		msb |= HDLC_FST;
+		if ((bcs->mode == L1_MODE_FAX) &&
+			(bcs->hw.isar.cmd == PCTRL_CMD_FTH)) {
+			if (bcs->tx_skb->len > 1) {
+				if ((ptr[0]== 0xff) && (ptr[1] == 0x13))
+					/* last frame */
+					test_and_set_bit(BC_FLG_LASTDATA,
+						&bcs->Flag);
+			}  
+		}
+	}
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.isar.txcnt += count;
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+			printk(KERN_ERR"isar_fill_fifo wrong mode 0\n");
+			break;
+		case L1_MODE_TRANS:
+		case L1_MODE_V32:
+			sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+				0, count, ptr);
+			break;
+		case L1_MODE_HDLC:
+			sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+				msb, count, ptr);
+			break;
+		case L1_MODE_FAX:
+			if (bcs->hw.isar.state != STFAX_ACTIV) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "isar_fill_fifo: not ACTIV");
+			} else if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) { 
+				sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+					msb, count, ptr);
+			} else if (bcs->hw.isar.cmd == PCTRL_CMD_FTM) {
+				sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+					0, count, ptr);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "isar_fill_fifo: not FTH/FTM");
+			}
+			break;
+		default:
+			if (cs->debug)
+				debugl1(cs, "isar_fill_fifo mode(%x) error", bcs->mode);
+			printk(KERN_ERR"isar_fill_fifo mode(%x) error\n", bcs->mode);
+			break;
+	}
+}
+
+inline
+struct BCState *sel_bcs_isar(struct IsdnCardState *cs, u_char dpath)
+{
+	if ((!dpath) || (dpath == 3))
+		return(NULL);
+	if (cs->bcs[0].hw.isar.dpath == dpath)
+		return(&cs->bcs[0]);
+	if (cs->bcs[1].hw.isar.dpath == dpath)
+		return(&cs->bcs[1]);
+	return(NULL);
+}
+
+void
+send_frames(struct BCState *bcs)
+{
+	if (bcs->tx_skb) {
+		if (bcs->tx_skb->len) {
+			isar_fill_fifo(bcs);
+			return;
+		} else {
+			if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+				(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+				u_long	flags;
+				spin_lock_irqsave(&bcs->aclock, flags);
+				bcs->ackcnt += bcs->hw.isar.txcnt;
+				spin_unlock_irqrestore(&bcs->aclock, flags);
+				schedule_event(bcs, B_ACKPENDING);
+			}
+			if (bcs->mode == L1_MODE_FAX) {
+				if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) {
+					if (test_bit(BC_FLG_LASTDATA, &bcs->Flag)) {
+						test_and_set_bit(BC_FLG_NMD_DATA, &bcs->Flag);
+					}
+				} else if (bcs->hw.isar.cmd == PCTRL_CMD_FTM) {
+					if (test_bit(BC_FLG_DLEETX, &bcs->Flag)) {
+						test_and_set_bit(BC_FLG_LASTDATA, &bcs->Flag);
+						test_and_set_bit(BC_FLG_NMD_DATA, &bcs->Flag);
+					}
+				}
+			}
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->hw.isar.txcnt = 0; 
+			bcs->tx_skb = NULL;
+		}
+	}
+	if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+		bcs->hw.isar.txcnt = 0;
+		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+		isar_fill_fifo(bcs);
+	} else {
+		if (test_and_clear_bit(BC_FLG_DLEETX, &bcs->Flag)) {
+			if (test_and_clear_bit(BC_FLG_LASTDATA, &bcs->Flag)) {
+				if (test_and_clear_bit(BC_FLG_NMD_DATA, &bcs->Flag)) {
+					u_char dummy = 0;
+					sendmsg(bcs->cs, SET_DPS(bcs->hw.isar.dpath) |
+						ISAR_HIS_SDATA, 0x01, 1, &dummy);
+				}
+				test_and_set_bit(BC_FLG_LL_OK, &bcs->Flag);
+			} else {
+				schedule_event(bcs, B_LL_CONNECT);
+			}
+		}
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		schedule_event(bcs, B_XMTBUFREADY);
+	}
+}
+
+inline void
+check_send(struct IsdnCardState *cs, u_char rdm)
+{
+	struct BCState *bcs;
+	
+	if (rdm & BSTAT_RDM1) {
+		if ((bcs = sel_bcs_isar(cs, 1))) {
+			if (bcs->mode) {
+				send_frames(bcs);
+			}
+		}
+	}
+	if (rdm & BSTAT_RDM2) {
+		if ((bcs = sel_bcs_isar(cs, 2))) {
+			if (bcs->mode) {
+				send_frames(bcs);
+			}
+		}
+	}
+	
+}
+
+const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4",
+			"300", "600", "1200", "2400", "4800", "7200",
+			"9600nt", "9600t", "12000", "14400", "WRONG"};
+const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21",
+			"Bell103", "V23", "Bell202", "V17", "V29", "V27ter"};
+
+static void
+isar_pump_status_rsp(struct BCState *bcs, struct isar_reg *ireg) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char ril = ireg->par[0];
+	u_char rim;
+
+	if (!test_and_clear_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags))
+		return; 
+	if (ril > 14) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "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(bcs->hw.isar.conmsg,"%s %s", dmril[ril], dmrim[rim]);
+	bcs->conmsg = bcs->hw.isar.conmsg;
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "pump strsp %s", bcs->conmsg);
+}
+
+static void
+isar_pump_statev_modem(struct BCState *bcs, u_char devt) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+
+	switch(devt) {
+		case PSEV_10MS_TIMER:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev TIMER");
+			break;
+		case PSEV_CON_ON:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev CONNECT");
+			l1_msg_b(bcs->st, PH_ACTIVATE | REQUEST, NULL);
+			break;
+		case PSEV_CON_OFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev NO CONNECT");
+			sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+			l1_msg_b(bcs->st, PH_DEACTIVATE | REQUEST, NULL);
+			break;
+		case PSEV_V24_OFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev V24 OFF");
+			break;
+		case PSEV_CTS_ON:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev CTS ON");
+			break;
+		case PSEV_CTS_OFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev CTS OFF");
+			break;
+		case PSEV_DCD_ON:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev CARRIER ON");
+			test_and_set_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags); 
+			sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+			break;
+		case PSEV_DCD_OFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev CARRIER OFF");
+			break;
+		case PSEV_DSR_ON:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev DSR ON");
+			break;
+		case PSEV_DSR_OFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev DSR_OFF");
+			break;
+		case PSEV_REM_RET:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev REMOTE RETRAIN");
+			break;
+		case PSEV_REM_REN:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev REMOTE RENEGOTIATE");
+			break;
+		case PSEV_GSTN_CLR:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev GSTN CLEAR", devt);
+			break;
+		default:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "unknown pump stev %x", devt);
+			break;
+	}
+}
+
+static void
+ll_deliver_faxstat(struct BCState *bcs, u_char status)
+{
+        isdn_ctrl ic;
+	struct Channel *chanp = (struct Channel *) bcs->st->lli.userdata;
+ 
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs, "HL->LL FAXIND %x", status);
+	ic.driver = bcs->cs->myid;
+	ic.command = ISDN_STAT_FAXIND;
+	ic.arg = chanp->chan;
+	ic.parm.aux.cmd = status;
+	bcs->cs->iif.statcallb(&ic);
+}
+
+static void
+isar_pump_statev_fax(struct BCState *bcs, u_char devt) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char p1;
+
+	switch(devt) {
+		case PSEV_10MS_TIMER:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev TIMER");
+			break;
+		case PSEV_RSP_READY:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_READY");
+			bcs->hw.isar.state = STFAX_READY;
+			l1_msg_b(bcs->st, PH_ACTIVATE | REQUEST, NULL);
+			if (test_bit(BC_FLG_ORIG, &bcs->Flag)) {
+				isar_pump_cmd(bcs, ISDN_FAX_CLASS1_FRH, 3);
+			} else {
+				isar_pump_cmd(bcs, ISDN_FAX_CLASS1_FTH, 3);
+			}
+			break;
+		case PSEV_LINE_TX_H:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_TX_H");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_TX_H wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_LINE_RX_H:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_RX_H");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_RX_H wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_LINE_TX_B:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_TX_B");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_TX_B wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_LINE_RX_B:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_RX_B");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_RX_B wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_RSP_CONN:
+			if (bcs->hw.isar.state == STFAX_CONT) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev RSP_CONN");
+				bcs->hw.isar.state = STFAX_ACTIV;
+				test_and_set_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags);
+				sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+				if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) {
+					/* 1s Flags before data */
+					if (test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag))
+						del_timer(&bcs->hw.isar.ftimer);
+					/* 1000 ms */
+					bcs->hw.isar.ftimer.expires =
+						jiffies + ((1000 * HZ)/1000);
+					test_and_set_bit(BC_FLG_LL_CONN,
+						&bcs->Flag);
+					add_timer(&bcs->hw.isar.ftimer);
+				} else {
+					schedule_event(bcs, B_LL_CONNECT);
+				}
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev RSP_CONN wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_FLAGS_DET:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev FLAGS_DET");
+			break;
+		case PSEV_RSP_DISC:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_DISC");
+			if (bcs->hw.isar.state == STFAX_ESCAPE) {
+				p1 = 5;
+				switch(bcs->hw.isar.newcmd) {
+					case 0:
+						bcs->hw.isar.state = STFAX_READY;
+						break;
+					case PCTRL_CMD_FTM:
+						p1 = 2;
+					case PCTRL_CMD_FTH:
+						sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+							PCTRL_CMD_SILON, 1, &p1);
+						bcs->hw.isar.state = STFAX_SILDET;
+						break;
+					case PCTRL_CMD_FRM:
+						if (frm_extra_delay)
+							mdelay(frm_extra_delay);
+					case PCTRL_CMD_FRH:
+						p1 = bcs->hw.isar.mod = bcs->hw.isar.newmod;
+						bcs->hw.isar.newmod = 0;
+						bcs->hw.isar.cmd = bcs->hw.isar.newcmd;
+						bcs->hw.isar.newcmd = 0;
+						sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+							bcs->hw.isar.cmd, 1, &p1);
+						bcs->hw.isar.state = STFAX_LINE;
+						bcs->hw.isar.try_mod = 3;
+						break;
+					default:
+						if (cs->debug & L1_DEB_HSCX)
+							debugl1(cs, "RSP_DISC unknown newcmd %x", bcs->hw.isar.newcmd);
+						break;
+				}
+			} else if (bcs->hw.isar.state == STFAX_ACTIV) {
+				if (test_and_clear_bit(BC_FLG_LL_OK, &bcs->Flag)) {
+					schedule_event(bcs, B_LL_OK);
+				} else if (bcs->hw.isar.cmd == PCTRL_CMD_FRM) {
+					send_DLE_ETX(bcs);
+					schedule_event(bcs, B_LL_NOCARRIER);
+				} else {
+					ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR);
+				}
+				bcs->hw.isar.state = STFAX_READY;
+			} else {
+				bcs->hw.isar.state = STFAX_READY;
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR);
+			}
+			break;
+		case PSEV_RSP_SILDET:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_SILDET");
+			if (bcs->hw.isar.state == STFAX_SILDET) {
+				p1 = bcs->hw.isar.mod = bcs->hw.isar.newmod;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.cmd = bcs->hw.isar.newcmd;
+				bcs->hw.isar.newcmd = 0;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+					bcs->hw.isar.cmd, 1, &p1);
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.try_mod = 3;
+			}
+			break;
+		case PSEV_RSP_SILOFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_SILOFF");
+			break;
+		case PSEV_RSP_FCERR:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev RSP_FCERR try %d",
+						bcs->hw.isar.try_mod);
+				if (bcs->hw.isar.try_mod--) {
+					sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+						bcs->hw.isar.cmd, 1,
+						&bcs->hw.isar.mod);
+					break;
+				}
+			}
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_FCERR");
+			bcs->hw.isar.state = STFAX_ESCAPE;
+			sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL);
+			ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR);
+			break;
+		default:
+			break;
+	}
+}
+
+static char debbuf[128];
+
+void
+isar_int_main(struct IsdnCardState *cs)
+{
+	struct isar_reg *ireg = cs->bcs[0].hw.isar.reg;
+	struct BCState *bcs;
+
+	get_irq_infos(cs, ireg);
+	switch (ireg->iis & ISAR_IIS_MSCMSD) {
+		case ISAR_IIS_RDATA:
+			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+				isar_rcv_frame(cs, bcs);
+			} else {
+				debugl1(cs, "isar spurious IIS_RDATA %x/%x/%x",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			}
+			break;
+		case ISAR_IIS_GSTEV:
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			ireg->bstat |= ireg->cmsb;
+			check_send(cs, ireg->cmsb);
+			break;
+		case ISAR_IIS_BSTEV:
+#ifdef ERROR_STATISTIC
+			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+				if (ireg->cmsb == BSTEV_TBO)
+					bcs->err_tx++;
+				if (ireg->cmsb == BSTEV_RBO)
+					bcs->err_rdo++;
+			}
+#endif
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "Buffer STEV dpath%d msb(%x)",
+					ireg->iis>>6, ireg->cmsb);
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			break;
+		case ISAR_IIS_PSTEV:
+			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+				rcv_mbox(cs, ireg, (u_char *)ireg->par);
+				if (bcs->mode == L1_MODE_V32) {
+					isar_pump_statev_modem(bcs, ireg->cmsb);
+				} else if (bcs->mode == L1_MODE_FAX) {
+					isar_pump_statev_fax(bcs, ireg->cmsb);
+				} else if (ireg->cmsb == PSEV_10MS_TIMER) {
+					if (cs->debug & L1_DEB_HSCX)
+						debugl1(cs, "pump stev TIMER");
+				} else {
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar IIS_PSTEV pmode %d stat %x",
+							bcs->mode, ireg->cmsb);
+				}
+			} else {
+				debugl1(cs, "isar spurious IIS_PSTEV %x/%x/%x",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			}
+			break;
+		case ISAR_IIS_PSTRSP:
+			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+				rcv_mbox(cs, ireg, (u_char *)ireg->par);
+				isar_pump_status_rsp(bcs, ireg);
+			} else {
+				debugl1(cs, "isar spurious IIS_PSTRSP %x/%x/%x",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			}
+			break;
+		case ISAR_IIS_DIAG:
+		case ISAR_IIS_BSTRSP:
+		case ISAR_IIS_IOM2RSP:
+			rcv_mbox(cs, ireg, (u_char *)ireg->par);
+			if ((cs->debug & (L1_DEB_HSCX | L1_DEB_HSCX_FIFO))
+				== L1_DEB_HSCX) {
+				u_char *tp=debbuf;
+
+				tp += sprintf(debbuf, "msg iis(%x) msb(%x)",
+					ireg->iis, ireg->cmsb);
+				QuickHex(tp, (u_char *)ireg->par, ireg->clsb);
+				debugl1(cs, debbuf);
+			}
+			break;
+		case ISAR_IIS_INVMSG:
+			rcv_mbox(cs, ireg, debbuf);
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "invalid msg his:%x",
+					ireg->cmsb);
+			break;
+		default:
+			rcv_mbox(cs, ireg, debbuf);
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "unhandled msg iis(%x) ctrl(%x/%x)",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+			break;
+	}
+}
+
+static void
+ftimer_handler(struct BCState *bcs) {
+	if (bcs->cs->debug)
+		debugl1(bcs->cs, "ftimer flags %04x",
+			bcs->Flag);
+	test_and_clear_bit(BC_FLG_FTI_RUN, &bcs->Flag);
+	if (test_and_clear_bit(BC_FLG_LL_CONN, &bcs->Flag)) {
+		schedule_event(bcs, B_LL_CONNECT);
+	}
+	if (test_and_clear_bit(BC_FLG_FTI_FTS, &bcs->Flag)) {
+		schedule_event(bcs, B_LL_OK);
+	}
+}
+
+static void
+setup_pump(struct BCState *bcs) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char ctrl, param[6];
+
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+		case L1_MODE_TRANS:
+		case L1_MODE_HDLC:
+			sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL);
+			break;
+		case L1_MODE_V32:
+			ctrl = PMOD_DATAMODEM;
+			if (test_bit(BC_FLG_ORIG, &bcs->Flag)) {
+				ctrl |= PCTRL_ORIG;
+				param[5] = PV32P6_CTN;
+			} else {
+				param[5] = PV32P6_ATN;
+			}
+			param[0] = para_TOA; /* 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(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param);
+			break;
+		case L1_MODE_FAX:
+			ctrl = PMOD_FAX;
+			if (test_bit(BC_FLG_ORIG, &bcs->Flag)) {
+				ctrl |= PCTRL_ORIG;
+				param[1] = PFAXP2_CTN;
+			} else {
+				param[1] = PFAXP2_ATN;
+			}
+			param[0] = para_TOA; /* 6 db */
+			sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param);
+			bcs->hw.isar.state = STFAX_NULL;
+			bcs->hw.isar.newcmd = 0;
+			bcs->hw.isar.newmod = 0;
+			test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag);
+			break;
+	}
+	udelay(1000);
+	sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+	udelay(1000);
+}
+
+static void
+setup_sart(struct BCState *bcs) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char ctrl, param[2];
+	
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0,
+				NULL);
+			break;
+		case L1_MODE_TRANS:
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2,
+				"\0\0");
+			break;
+		case L1_MODE_HDLC:
+			param[0] = 0;
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1,
+				param);
+			break;
+		case L1_MODE_V32:
+			ctrl = SMODE_V14 | SCTRL_HDMC_BOTH;
+			param[0] = S_P1_CHS_8;
+			param[1] = S_P2_BFT_DEF;
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, ctrl, 2,
+				param);
+			break;
+		case L1_MODE_FAX:
+			/* SART must not configured with FAX */
+			break;
+	}
+	udelay(1000);
+	sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL);
+	udelay(1000);
+}
+
+static void
+setup_iom2(struct BCState *bcs) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD,0,0,0,0};
+	
+	if (bcs->channel)
+		msg[1] = msg[3] = 1;
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+			cmsb = 0;
+			/* dummy slot */
+			msg[1] = msg[3] = bcs->hw.isar.dpath + 2;
+			break;
+		case L1_MODE_TRANS:
+		case L1_MODE_HDLC:
+			break;
+		case L1_MODE_V32:
+		case L1_MODE_FAX:
+			cmsb |= IOM_CTRL_ALAW | IOM_CTRL_RCV;
+			break;
+	}
+	sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg);
+	udelay(1000);
+	sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL);
+	udelay(1000);
+}
+
+int
+modeisar(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	/* Here we are selecting the best datapath for requested mode */
+	if(bcs->mode == L1_MODE_NULL) { /* New Setup */
+		bcs->channel = bc;
+		switch (mode) {
+			case L1_MODE_NULL: /* init */
+				if (!bcs->hw.isar.dpath)
+					/* no init for dpath 0 */
+					return(0);
+				break;
+			case L1_MODE_TRANS:
+			case L1_MODE_HDLC:
+				/* best is datapath 2 */
+				if (!test_and_set_bit(ISAR_DP2_USE, 
+					&bcs->hw.isar.reg->Flags))
+					bcs->hw.isar.dpath = 2;
+				else if (!test_and_set_bit(ISAR_DP1_USE,
+					&bcs->hw.isar.reg->Flags))
+					bcs->hw.isar.dpath = 1;
+				else {
+					printk(KERN_WARNING"isar modeisar both pathes in use\n");
+					return(1);
+				}
+				break;
+			case L1_MODE_V32:
+			case L1_MODE_FAX:
+				/* only datapath 1 */
+				if (!test_and_set_bit(ISAR_DP1_USE, 
+					&bcs->hw.isar.reg->Flags))
+					bcs->hw.isar.dpath = 1;
+				else {
+					printk(KERN_WARNING"isar modeisar analog funktions only with DP1\n");
+					debugl1(cs, "isar modeisar analog funktions only with DP1");
+					return(1);
+				}
+				break;
+		}
+	}
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "isar dp%d mode %d->%d ichan %d",
+			bcs->hw.isar.dpath, bcs->mode, mode, bc);
+	bcs->mode = mode;
+	setup_pump(bcs);
+	setup_iom2(bcs);
+	setup_sart(bcs);
+	if (bcs->mode == L1_MODE_NULL) {
+		/* Clear resources */
+		if (bcs->hw.isar.dpath == 1)
+			test_and_clear_bit(ISAR_DP1_USE, &bcs->hw.isar.reg->Flags);
+		else if (bcs->hw.isar.dpath == 2)
+			test_and_clear_bit(ISAR_DP2_USE, &bcs->hw.isar.reg->Flags);
+		bcs->hw.isar.dpath = 0;
+	}
+	return(0);
+}
+
+static void
+isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para) 
+{
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char ctrl = 0, nom = 0, p1 = 0;
+
+	switch(cmd) {
+		case ISDN_FAX_CLASS1_FTM:
+			test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag);
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FTM;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+				bcs->hw.isar.try_mod = 3; 
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FTM) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FTM;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAX_CLASS1_FTH:
+			test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag);
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FTH;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+				bcs->hw.isar.try_mod = 3; 
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FTH) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FTH;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAX_CLASS1_FRM:
+			test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag);
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FRM;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+				bcs->hw.isar.try_mod = 3; 
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FRM) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FRM;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAX_CLASS1_FRH:
+			test_and_set_bit(BC_FLG_FRH_WAIT, &bcs->Flag);
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FRH;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+				bcs->hw.isar.try_mod = 3; 
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FRH) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FRH;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAXPUMP_HALT:
+			bcs->hw.isar.state = STFAX_NULL;
+			nom = 0;
+			ctrl = PCTRL_CMD_HALT;
+			break;
+	}
+	if (ctrl)
+		sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1);
+}
+
+void
+isar_setup(struct IsdnCardState *cs)
+{
+	u_char msg;
+	int i;
+	
+	/* Dpath 1, 2 */
+	msg = 61;
+	for (i=0; i<2; i++) {
+		/* Buffer Config */
+		sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
+			ISAR_HIS_P12CFG, 4, 1, &msg);
+		cs->bcs[i].hw.isar.mml = msg;
+		cs->bcs[i].mode = 0;
+		cs->bcs[i].hw.isar.dpath = i + 1;
+		modeisar(&cs->bcs[i], 0, 0);
+		INIT_WORK(&cs->bcs[i].tqueue, (void *)(void *) isar_bh, &cs->bcs[i]);
+	}
+}
+
+void
+isar_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	int ret;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				if (bcs->cs->debug & L1_DEB_HSCX)
+					debugl1(bcs->cs, "DRQ set BC_FLG_BUSY");
+				bcs->hw.isar.txcnt = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "isar_l2l1: this shouldn't happen\n");
+			} else {
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				if (bcs->cs->debug & L1_DEB_HSCX)
+					debugl1(bcs->cs, "PUI set BC_FLG_BUSY");
+				bcs->tx_skb = skb;
+				bcs->hw.isar.txcnt = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			bcs->hw.isar.conmsg[0] = 0;
+			if (test_bit(FLG_ORIG, &st->l2.flag))
+				test_and_set_bit(BC_FLG_ORIG, &bcs->Flag);
+			else
+				test_and_clear_bit(BC_FLG_ORIG, &bcs->Flag);
+			switch(st->l1.mode) {
+				case L1_MODE_TRANS:
+				case L1_MODE_HDLC:
+					ret = modeisar(bcs, st->l1.mode, st->l1.bc);
+					spin_unlock_irqrestore(&bcs->cs->lock, flags);
+					if (ret)
+						l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg);
+					else
+						l1_msg_b(st, PH_ACTIVATE | REQUEST, arg);
+					break;
+				case L1_MODE_V32:
+				case L1_MODE_FAX:
+					ret = modeisar(bcs, st->l1.mode, st->l1.bc);
+					spin_unlock_irqrestore(&bcs->cs->lock, flags);
+					if (ret)
+						l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg);
+					break;
+				default:
+					spin_unlock_irqrestore(&bcs->cs->lock, flags);
+					break;
+			}
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			switch(st->l1.mode) {
+				case L1_MODE_TRANS:
+				case L1_MODE_HDLC:
+				case L1_MODE_V32:
+					break;
+				case L1_MODE_FAX:
+					isar_pump_cmd(bcs, ISDN_FAXPUMP_HALT, 0);
+					break;
+			}
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			if (bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(bcs->cs, "PDAC clear BC_FLG_BUSY");
+			modeisar(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_isarstate(struct BCState *bcs)
+{
+	modeisar(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.isar.rcvbuf) {
+			kfree(bcs->hw.isar.rcvbuf);
+			bcs->hw.isar.rcvbuf = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			if (bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(bcs->cs, "closeisar clear BC_FLG_BUSY");
+		}
+	}
+	del_timer(&bcs->hw.isar.ftimer);
+}
+
+int
+open_isarstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.isar.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for isar.rcvbuf\n");
+			return (1);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "openisar clear BC_FLG_BUSY");
+	bcs->event = 0;
+	bcs->hw.isar.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_isar(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_isarstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = isar_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+int
+isar_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) {
+	u_long adr;
+	int features, i;
+	struct BCState *bcs;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "isar_auxcmd cmd/ch %x/%d", ic->command, ic->arg);
+	switch (ic->command) {
+		case (ISDN_CMD_FAXCMD):
+			bcs = cs->channel[ic->arg].bcs;
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "isar_auxcmd cmd/subcmd %d/%d",
+					ic->parm.aux.cmd, ic->parm.aux.subcmd);
+			switch(ic->parm.aux.cmd) {
+				case ISDN_FAX_CLASS1_CTRL:
+					if (ic->parm.aux.subcmd == ETX)
+						test_and_set_bit(BC_FLG_DLEETX,
+							&bcs->Flag);
+					break;
+				case ISDN_FAX_CLASS1_FTS:
+					if (ic->parm.aux.subcmd == AT_QUERY) {
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_OK;
+						cs->iif.statcallb(ic);
+						return(0);
+					} else if (ic->parm.aux.subcmd == AT_EQ_QUERY) {
+						strcpy(ic->parm.aux.para, "0-255");
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY;
+						cs->iif.statcallb(ic);
+						return(0);
+					} else if (ic->parm.aux.subcmd == AT_EQ_VALUE) {
+						if (cs->debug & L1_DEB_HSCX)
+							debugl1(cs, "isar_auxcmd %s=%d",
+								FC1_CMD[ic->parm.aux.cmd], ic->parm.aux.para[0]);
+						if (bcs->hw.isar.state == STFAX_READY) {
+							if (! ic->parm.aux.para[0]) {
+								ic->command = ISDN_STAT_FAXIND;
+								ic->parm.aux.cmd = ISDN_FAX_CLASS1_OK;
+								cs->iif.statcallb(ic);
+								return(0);
+							}
+							if (! test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag)) {
+								/* n*10 ms */
+								bcs->hw.isar.ftimer.expires =
+									jiffies + ((ic->parm.aux.para[0] * 10 * HZ)/1000);
+								test_and_set_bit(BC_FLG_FTI_FTS, &bcs->Flag);
+								add_timer(&bcs->hw.isar.ftimer);
+								return(0);
+							} else {
+								if (cs->debug)
+									debugl1(cs, "isar FTS=%d and FTI busy",
+										ic->parm.aux.para[0]);
+							}
+						} else {
+							if (cs->debug)
+								debugl1(cs, "isar FTS=%d and isar.state not ready(%x)",
+									ic->parm.aux.para[0],bcs->hw.isar.state);
+						}
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_ERROR;
+						cs->iif.statcallb(ic);
+					}
+					break;
+				case ISDN_FAX_CLASS1_FRM:
+				case ISDN_FAX_CLASS1_FRH:
+				case ISDN_FAX_CLASS1_FTM:
+				case ISDN_FAX_CLASS1_FTH:
+					if (ic->parm.aux.subcmd == AT_QUERY) {
+						sprintf(ic->parm.aux.para,
+							"%d", bcs->hw.isar.mod);
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY;
+						cs->iif.statcallb(ic);
+						return(0);
+					} else if (ic->parm.aux.subcmd == AT_EQ_QUERY) {
+						char *p = ic->parm.aux.para;
+						for(i=0;i<FAXMODCNT;i++)
+							if ((1<<i) & modmask)
+								p += sprintf(p, "%d,", faxmodulation[i]);
+						p--;
+						*p=0;
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY;
+						cs->iif.statcallb(ic);
+						return(0);
+					} else if (ic->parm.aux.subcmd == AT_EQ_VALUE) {
+						if (cs->debug & L1_DEB_HSCX)
+							debugl1(cs, "isar_auxcmd %s=%d",
+								FC1_CMD[ic->parm.aux.cmd], ic->parm.aux.para[0]);
+						for(i=0;i<FAXMODCNT;i++)
+							if (faxmodulation[i]==ic->parm.aux.para[0])
+								break;
+						if ((i < FAXMODCNT) && ((1<<i) & modmask) && 
+							test_bit(BC_FLG_INIT, &bcs->Flag)) {
+							isar_pump_cmd(bcs,
+								ic->parm.aux.cmd,
+								ic->parm.aux.para[0]);
+							return(0);
+						}
+					}
+					/* wrong modulation or not activ */
+					/* fall through */
+				default:
+					ic->command = ISDN_STAT_FAXIND;
+					ic->parm.aux.cmd = ISDN_FAX_CLASS1_ERROR;
+					cs->iif.statcallb(ic);
+			}
+			break;
+		case (ISDN_CMD_IOCTL):
+			switch (ic->arg) {
+				case 9: /* load firmware */
+					features = ISDN_FEATURE_L2_MODEM |
+						ISDN_FEATURE_L2_FAX |
+						ISDN_FEATURE_L3_FCLASS1;
+					memcpy(&adr, ic->parm.num, sizeof(ulong));
+					if (isar_load_firmware(cs, (u_char __user *)adr))
+						return(1);
+					else 
+						ll_run(cs, features);
+					break;
+				case 20:
+					features = *(unsigned int *) ic->parm.num;
+					printk(KERN_DEBUG "HiSax: max modulation old(%04x) new(%04x)\n",
+						modmask, features);
+					modmask = features;
+					break;
+				case 21:
+					features = *(unsigned int *) ic->parm.num;
+					printk(KERN_DEBUG "HiSax: FRM extra delay old(%d) new(%d) ms\n",
+						frm_extra_delay, features);
+					if (features >= 0)
+						frm_extra_delay = features;
+					break;
+				case 22:
+					features = *(unsigned int *) ic->parm.num;
+					printk(KERN_DEBUG "HiSax: TOA old(%d) new(%d) db\n",
+						para_TOA, features);
+					if (features >= 0 && features < 32)
+						para_TOA = features;
+					break;
+				default:
+					printk(KERN_DEBUG "HiSax: invalid ioctl %d\n",
+					       (int) ic->arg);
+					return(-EINVAL);
+			}
+			break;
+		default:
+			return(-EINVAL);
+	}
+	return(0);
+}
+
+void __devinit
+initisar(struct IsdnCardState *cs)
+{
+	cs->bcs[0].BC_SetStack = setstack_isar;
+	cs->bcs[1].BC_SetStack = setstack_isar;
+	cs->bcs[0].BC_Close = close_isarstate;
+	cs->bcs[1].BC_Close = close_isarstate;
+	cs->bcs[0].hw.isar.ftimer.function = (void *) ftimer_handler;
+	cs->bcs[0].hw.isar.ftimer.data = (long) &cs->bcs[0];
+	init_timer(&cs->bcs[0].hw.isar.ftimer);
+	cs->bcs[1].hw.isar.ftimer.function = (void *) ftimer_handler;
+	cs->bcs[1].hw.isar.ftimer.data = (long) &cs->bcs[1];
+	init_timer(&cs->bcs[1].hw.isar.ftimer);
+}
diff --git a/drivers/isdn/hisax/isar.h b/drivers/isdn/hisax/isar.h
new file mode 100644
index 000000000000..bf7676586392
--- /dev/null
+++ b/drivers/isdn/hisax/isar.h
@@ -0,0 +1,222 @@
+/* $Id: isar.h,v 1.11.2.2 2004/01/12 22:52:27 keil Exp $
+ *
+ * ISAR (Siemens PSB 7110) specific defines
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+ 
+#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_WAITSTATE	0x27
+#define ISAR_HIS_TIMERIRQ	0x25
+#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_CMD_TIMERIRQ_OFF	0x20
+#define ISAR_CMD_TIMERIRQ_ON	0x21
+
+
+#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_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
+
+#define ISDN_FAXPUMP_HALT	100
+
+extern int ISARVersion(struct IsdnCardState *cs, char *s);
+extern void isar_int_main(struct IsdnCardState *cs);
+extern void initisar(struct IsdnCardState *cs);
+extern void isar_fill_fifo(struct BCState *bcs);
+extern int isar_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic);
diff --git a/drivers/isdn/hisax/isdnhdlc.c b/drivers/isdn/hisax/isdnhdlc.c
new file mode 100644
index 000000000000..cbdf54c5af84
--- /dev/null
+++ b/drivers/isdn/hisax/isdnhdlc.c
@@ -0,0 +1,628 @@
+/*
+ * isdnhdlc.c  --  General purpose ISDN HDLC decoder.
+ *
+ *Copyright (C) 2002	Wolfgang Mües      <wolfgang@iksw-muees.de>
+ *		2001 	Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 	Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/crc-ccitt.h>
+#include "isdnhdlc.h"
+
+/*-------------------------------------------------------------------*/
+
+MODULE_AUTHOR("Wolfgang Mües <wolfgang@iksw-muees.de>, "
+	      "Frode Isaksen <fisaksen@bewan.com>, "
+	      "Kai Germaschewski <kai.germaschewski@gmx.de>");
+MODULE_DESCRIPTION("General purpose ISDN HDLC decoder");
+MODULE_LICENSE("GPL");
+
+/*-------------------------------------------------------------------*/
+
+/* bit swap table.
+ * Very handy for devices with different bit order,
+ * and neccessary for each transparent B-channel access for all
+ * devices which works with this HDLC decoder without bit reversal.
+ */
+const unsigned char isdnhdlc_bit_rev_tab[256] = {
+	0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,
+	0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,
+	0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,
+	0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,
+	0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,
+	0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,
+	0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,
+	0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,
+	0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,
+	0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,
+	0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,
+	0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,
+	0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,
+	0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,
+	0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,
+	0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF
+};
+
+enum {
+	HDLC_FAST_IDLE,HDLC_GET_FLAG_B0,HDLC_GETFLAG_B1A6,HDLC_GETFLAG_B7,
+	HDLC_GET_DATA,HDLC_FAST_FLAG
+};
+
+enum {
+	HDLC_SEND_DATA,HDLC_SEND_CRC1,HDLC_SEND_FAST_FLAG,
+	HDLC_SEND_FIRST_FLAG,HDLC_SEND_CRC2,HDLC_SEND_CLOSING_FLAG,
+	HDLC_SEND_IDLE1,HDLC_SEND_FAST_IDLE,HDLC_SENDFLAG_B0,
+	HDLC_SENDFLAG_B1A6,HDLC_SENDFLAG_B7,STOPPED
+};
+
+void isdnhdlc_rcv_init (struct isdnhdlc_vars *hdlc, int do_adapt56)
+{
+   	hdlc->bit_shift = 0;
+	hdlc->hdlc_bits1 = 0;
+	hdlc->data_bits = 0;
+	hdlc->ffbit_shift = 0;
+	hdlc->data_received = 0;
+	hdlc->state = HDLC_GET_DATA;
+	hdlc->do_adapt56 = do_adapt56;
+	hdlc->dchannel = 0;
+	hdlc->crc = 0;
+	hdlc->cbin = 0;
+	hdlc->shift_reg = 0;
+	hdlc->ffvalue = 0;
+	hdlc->dstpos = 0;
+}
+
+void isdnhdlc_out_init (struct isdnhdlc_vars *hdlc, int is_d_channel, int do_adapt56)
+{
+   	hdlc->bit_shift = 0;
+	hdlc->hdlc_bits1 = 0;
+	hdlc->data_bits = 0;
+	hdlc->ffbit_shift = 0;
+	hdlc->data_received = 0;
+	hdlc->do_closing = 0;
+	hdlc->ffvalue = 0;
+	if (is_d_channel) {
+		hdlc->dchannel = 1;
+		hdlc->state = HDLC_SEND_FIRST_FLAG;
+	} else {
+		hdlc->dchannel = 0;
+		hdlc->state = HDLC_SEND_FAST_FLAG;
+		hdlc->ffvalue = 0x7e;
+	}
+	hdlc->cbin = 0x7e;
+	hdlc->bit_shift = 0;
+	if(do_adapt56){
+		hdlc->do_adapt56 = 1;
+		hdlc->data_bits = 0;
+		hdlc->state = HDLC_SENDFLAG_B0;
+	} else {
+		hdlc->do_adapt56 = 0;
+		hdlc->data_bits = 8;
+	}
+	hdlc->shift_reg = 0;
+}
+
+/*
+  isdnhdlc_decode - decodes HDLC frames from a transparent bit stream.
+
+  The source buffer is scanned for valid HDLC frames looking for
+  flags (01111110) to indicate the start of a frame. If the start of
+  the frame is found, the bit stuffing is removed (0 after 5 1's).
+  When a new flag is found, the complete frame has been received
+  and the CRC is checked.
+  If a valid frame is found, the function returns the frame length
+  excluding the CRC with the bit HDLC_END_OF_FRAME set.
+  If the beginning of a valid frame is found, the function returns
+  the length.
+  If a framing error is found (too many 1s and not a flag) the function
+  returns the length with the bit HDLC_FRAMING_ERROR set.
+  If a CRC error is found the function returns the length with the
+  bit HDLC_CRC_ERROR set.
+  If the frame length exceeds the destination buffer size, the function
+  returns the length with the bit HDLC_LENGTH_ERROR set.
+
+  src - source buffer
+  slen - source buffer length
+  count - number of bytes removed (decoded) from the source buffer
+  dst _ destination buffer
+  dsize - destination buffer size
+  returns - number of decoded bytes in the destination buffer and status
+  flag.
+ */
+int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src,
+		     int slen, int *count, unsigned char *dst, int dsize)
+{
+	int status=0;
+
+	static const unsigned char fast_flag[]={
+		0x00,0x00,0x00,0x20,0x30,0x38,0x3c,0x3e,0x3f
+	};
+
+	static const unsigned char fast_flag_value[]={
+		0x00,0x7e,0xfc,0xf9,0xf3,0xe7,0xcf,0x9f,0x3f
+	};
+
+	static const unsigned char fast_abort[]={
+		0x00,0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff
+	};
+
+	*count = slen;
+
+	while(slen > 0){
+		if(hdlc->bit_shift==0){
+			hdlc->cbin = *src++;
+			slen--;
+			hdlc->bit_shift = 8;
+			if(hdlc->do_adapt56){
+				hdlc->bit_shift --;
+			}
+		}
+
+		switch(hdlc->state){
+		case STOPPED:
+			return 0;
+		case HDLC_FAST_IDLE:
+			if(hdlc->cbin == 0xff){
+				hdlc->bit_shift = 0;
+				break;
+			}
+			hdlc->state = HDLC_GET_FLAG_B0;
+			hdlc->hdlc_bits1 = 0;
+			hdlc->bit_shift = 8;
+			break;
+		case HDLC_GET_FLAG_B0:
+			if(!(hdlc->cbin & 0x80)) {
+				hdlc->state = HDLC_GETFLAG_B1A6;
+				hdlc->hdlc_bits1 = 0;
+			} else {
+				if(!hdlc->do_adapt56){
+					if(++hdlc->hdlc_bits1 >=8 ) if(hdlc->bit_shift==1)
+						hdlc->state = HDLC_FAST_IDLE;
+				}
+			}
+			hdlc->cbin<<=1;
+			hdlc->bit_shift --;
+			break;
+		case HDLC_GETFLAG_B1A6:
+			if(hdlc->cbin & 0x80){
+				hdlc->hdlc_bits1++;
+				if(hdlc->hdlc_bits1==6){
+					hdlc->state = HDLC_GETFLAG_B7;
+				}
+			} else {
+				hdlc->hdlc_bits1 = 0;
+			}
+			hdlc->cbin<<=1;
+			hdlc->bit_shift --;
+			break;
+		case HDLC_GETFLAG_B7:
+			if(hdlc->cbin & 0x80) {
+				hdlc->state = HDLC_GET_FLAG_B0;
+			} else {
+				hdlc->state = HDLC_GET_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->shift_reg = 0;
+				hdlc->hdlc_bits1 = 0;
+				hdlc->data_bits = 0;
+				hdlc->data_received = 0;
+			}
+			hdlc->cbin<<=1;
+			hdlc->bit_shift --;
+			break;
+		case HDLC_GET_DATA:
+			if(hdlc->cbin & 0x80){
+				hdlc->hdlc_bits1++;
+				switch(hdlc->hdlc_bits1){
+				case 6:
+					break;
+				case 7:
+					if(hdlc->data_received) {
+						// bad frame
+						status = -HDLC_FRAMING_ERROR;
+					}
+					if(!hdlc->do_adapt56){
+						if(hdlc->cbin==fast_abort[hdlc->bit_shift+1]){
+							hdlc->state = HDLC_FAST_IDLE;
+							hdlc->bit_shift=1;
+							break;
+						}
+					} else {
+						hdlc->state = HDLC_GET_FLAG_B0;
+					}
+					break;
+				default:
+					hdlc->shift_reg>>=1;
+					hdlc->shift_reg |= 0x80;
+					hdlc->data_bits++;
+					break;
+				}
+			} else {
+				switch(hdlc->hdlc_bits1){
+				case 5:
+					break;
+				case 6:
+					if(hdlc->data_received){
+						if (hdlc->dstpos < 2) {
+							status = -HDLC_FRAMING_ERROR;
+						} else if (hdlc->crc != 0xf0b8){
+							// crc error
+							status = -HDLC_CRC_ERROR;
+						} else {
+							// remove CRC
+							hdlc->dstpos -= 2;
+							// good frame
+							status = hdlc->dstpos;
+						}
+					}
+					hdlc->crc = 0xffff;
+					hdlc->shift_reg = 0;
+					hdlc->data_bits = 0;
+					if(!hdlc->do_adapt56){
+						if(hdlc->cbin==fast_flag[hdlc->bit_shift]){
+							hdlc->ffvalue = fast_flag_value[hdlc->bit_shift];
+							hdlc->state = HDLC_FAST_FLAG;
+							hdlc->ffbit_shift = hdlc->bit_shift;
+							hdlc->bit_shift = 1;
+						} else {
+							hdlc->state = HDLC_GET_DATA;
+							hdlc->data_received = 0;
+						}
+					} else {
+						hdlc->state = HDLC_GET_DATA;
+						hdlc->data_received = 0;
+					}
+					break;
+				default:
+					hdlc->shift_reg>>=1;
+					hdlc->data_bits++;
+					break;
+				}
+				hdlc->hdlc_bits1 = 0;
+			}
+			if (status) {
+				hdlc->dstpos = 0;
+				*count -= slen;
+				hdlc->cbin <<= 1;
+				hdlc->bit_shift--;
+				return status;
+			}
+			if(hdlc->data_bits==8){
+				hdlc->data_bits = 0;
+				hdlc->data_received = 1;
+				hdlc->crc = crc_ccitt_byte(hdlc->crc, hdlc->shift_reg);
+
+				// good byte received
+				if (hdlc->dstpos < dsize) {
+					dst[hdlc->dstpos++] = hdlc->shift_reg;
+				} else {
+					// frame too long
+					status = -HDLC_LENGTH_ERROR;
+					hdlc->dstpos = 0;
+				}
+			}
+			hdlc->cbin <<= 1;
+			hdlc->bit_shift--;
+			break;
+		case HDLC_FAST_FLAG:
+			if(hdlc->cbin==hdlc->ffvalue){
+				hdlc->bit_shift = 0;
+				break;
+			} else {
+				if(hdlc->cbin == 0xff){
+					hdlc->state = HDLC_FAST_IDLE;
+					hdlc->bit_shift=0;
+				} else if(hdlc->ffbit_shift==8){
+					hdlc->state = HDLC_GETFLAG_B7;
+					break;
+				} else {
+					hdlc->shift_reg = fast_abort[hdlc->ffbit_shift-1];
+					hdlc->hdlc_bits1 = hdlc->ffbit_shift-2;
+					if(hdlc->hdlc_bits1<0)hdlc->hdlc_bits1 = 0;
+					hdlc->data_bits = hdlc->ffbit_shift-1;
+					hdlc->state = HDLC_GET_DATA;
+					hdlc->data_received = 0;
+				}
+			}
+			break;
+		default:
+			break;
+		}
+	}
+	*count -= slen;
+	return 0;
+}
+
+/*
+  isdnhdlc_encode - encodes HDLC frames to a transparent bit stream.
+
+  The bit stream starts with a beginning flag (01111110). After
+  that each byte is added to the bit stream with bit stuffing added
+  (0 after 5 1's).
+  When the last byte has been removed from the source buffer, the
+  CRC (2 bytes is added) and the frame terminates with the ending flag.
+  For the dchannel, the idle character (all 1's) is also added at the end.
+  If this function is called with empty source buffer (slen=0), flags or
+  idle character will be generated.
+
+  src - source buffer
+  slen - source buffer length
+  count - number of bytes removed (encoded) from source buffer
+  dst _ destination buffer
+  dsize - destination buffer size
+  returns - number of encoded bytes in the destination buffer
+*/
+int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
+		unsigned short slen, int *count,
+		unsigned char *dst, int dsize)
+{
+	static const unsigned char xfast_flag_value[] = {
+		0x7e,0x3f,0x9f,0xcf,0xe7,0xf3,0xf9,0xfc,0x7e
+	};
+
+	int len = 0;
+
+	*count = slen;
+
+	while (dsize > 0) {
+		if(hdlc->bit_shift==0){
+			if(slen && !hdlc->do_closing){
+				hdlc->shift_reg = *src++;
+				slen--;
+				if (slen == 0)
+					hdlc->do_closing = 1;  /* closing sequence, CRC + flag(s) */
+				hdlc->bit_shift = 8;
+			} else {
+				if(hdlc->state == HDLC_SEND_DATA){
+					if(hdlc->data_received){
+						hdlc->state = HDLC_SEND_CRC1;
+						hdlc->crc ^= 0xffff;
+						hdlc->bit_shift = 8;
+						hdlc->shift_reg = hdlc->crc & 0xff;
+					} else if(!hdlc->do_adapt56){
+						hdlc->state = HDLC_SEND_FAST_FLAG;
+					} else {
+						hdlc->state = HDLC_SENDFLAG_B0;
+					}
+				}
+
+			}
+		}
+
+		switch(hdlc->state){
+		case STOPPED:
+			while (dsize--)
+				*dst++ = 0xff;
+
+			return dsize;
+		case HDLC_SEND_FAST_FLAG:
+			hdlc->do_closing = 0;
+			if(slen == 0){
+				*dst++ = hdlc->ffvalue;
+				len++;
+				dsize--;
+				break;
+			}
+			if(hdlc->bit_shift==8){
+				hdlc->cbin = hdlc->ffvalue>>(8-hdlc->data_bits);
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+				hdlc->data_received = 1;
+			}
+			break;
+		case HDLC_SENDFLAG_B0:
+			hdlc->do_closing = 0;
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			hdlc->hdlc_bits1 = 0;
+			hdlc->state = HDLC_SENDFLAG_B1A6;
+			break;
+		case HDLC_SENDFLAG_B1A6:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			hdlc->cbin++;
+			if(++hdlc->hdlc_bits1 == 6)
+				hdlc->state = HDLC_SENDFLAG_B7;
+			break;
+		case HDLC_SENDFLAG_B7:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(slen == 0){
+				hdlc->state = HDLC_SENDFLAG_B0;
+				break;
+			}
+			if(hdlc->bit_shift==8){
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+				hdlc->data_received = 1;
+			}
+			break;
+		case HDLC_SEND_FIRST_FLAG:
+			hdlc->data_received = 1;
+			if(hdlc->data_bits==8){
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->shift_reg & 0x01)
+				hdlc->cbin++;
+			hdlc->shift_reg >>= 1;
+			hdlc->bit_shift--;
+			if(hdlc->bit_shift==0){
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+			}
+			break;
+		case HDLC_SEND_DATA:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->bit_shift==8){
+				hdlc->crc = crc_ccitt_byte(hdlc->crc, hdlc->shift_reg);
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->hdlc_bits1++;
+				hdlc->cbin++;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			} else {
+				hdlc->hdlc_bits1 = 0;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			}
+			break;
+		case HDLC_SEND_CRC1:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->hdlc_bits1++;
+				hdlc->cbin++;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			} else {
+				hdlc->hdlc_bits1 = 0;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			}
+			if(hdlc->bit_shift==0){
+				hdlc->shift_reg = (hdlc->crc >> 8);
+				hdlc->state = HDLC_SEND_CRC2;
+				hdlc->bit_shift = 8;
+			}
+			break;
+		case HDLC_SEND_CRC2:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->hdlc_bits1++;
+				hdlc->cbin++;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			} else {
+				hdlc->hdlc_bits1 = 0;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			}
+			if(hdlc->bit_shift==0){
+				hdlc->shift_reg = 0x7e;
+				hdlc->state = HDLC_SEND_CLOSING_FLAG;
+				hdlc->bit_shift = 8;
+			}
+			break;
+		case HDLC_SEND_CLOSING_FLAG:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->cbin++;
+			}
+			hdlc->shift_reg >>= 1;
+			hdlc->bit_shift--;
+			if(hdlc->bit_shift==0){
+				hdlc->ffvalue = xfast_flag_value[hdlc->data_bits];
+				if(hdlc->dchannel){
+					hdlc->ffvalue = 0x7e;
+					hdlc->state = HDLC_SEND_IDLE1;
+					hdlc->bit_shift = 8-hdlc->data_bits;
+					if(hdlc->bit_shift==0)
+						hdlc->state = HDLC_SEND_FAST_IDLE;
+				} else {
+					if(!hdlc->do_adapt56){
+						hdlc->state = HDLC_SEND_FAST_FLAG;
+						hdlc->data_received = 0;
+					} else {
+						hdlc->state = HDLC_SENDFLAG_B0;
+						hdlc->data_received = 0;
+					}
+					// Finished with this frame, send flags
+					if (dsize > 1) dsize = 1;
+				}
+			}
+			break;
+		case HDLC_SEND_IDLE1:
+			hdlc->do_closing = 0;
+			hdlc->cbin <<= 1;
+			hdlc->cbin++;
+			hdlc->data_bits++;
+			hdlc->bit_shift--;
+			if(hdlc->bit_shift==0){
+				hdlc->state = HDLC_SEND_FAST_IDLE;
+				hdlc->bit_shift = 0;
+			}
+			break;
+		case HDLC_SEND_FAST_IDLE:
+			hdlc->do_closing = 0;
+			hdlc->cbin = 0xff;
+			hdlc->data_bits = 8;
+			if(hdlc->bit_shift == 8){
+				hdlc->cbin = 0x7e;
+				hdlc->state = HDLC_SEND_FIRST_FLAG;
+			} else {
+				*dst++ = hdlc->cbin;
+				hdlc->bit_shift = hdlc->data_bits = 0;
+				len++;
+				dsize = 0;
+			}
+			break;
+		default:
+			break;
+		}
+		if(hdlc->do_adapt56){
+			if(hdlc->data_bits==7){
+				hdlc->cbin <<= 1;
+				hdlc->cbin++;
+				hdlc->data_bits++;
+			}
+		}
+		if(hdlc->data_bits==8){
+			*dst++ = hdlc->cbin;
+			hdlc->data_bits = 0;
+			len++;
+			dsize--;
+		}
+	}
+	*count -= slen;
+
+	return len;
+}
+
+EXPORT_SYMBOL(isdnhdlc_bit_rev_tab);
+EXPORT_SYMBOL(isdnhdlc_rcv_init);
+EXPORT_SYMBOL(isdnhdlc_decode);
+EXPORT_SYMBOL(isdnhdlc_out_init);
+EXPORT_SYMBOL(isdnhdlc_encode);
diff --git a/drivers/isdn/hisax/isdnhdlc.h b/drivers/isdn/hisax/isdnhdlc.h
new file mode 100644
index 000000000000..269315988dc8
--- /dev/null
+++ b/drivers/isdn/hisax/isdnhdlc.h
@@ -0,0 +1,72 @@
+/*
+ * isdnhdlc.h  --  General purpose ISDN HDLC decoder.
+ *
+ * Implementation of a HDLC decoder/encoder in software.
+ * Neccessary because some ISDN devices don't have HDLC
+ * controllers. Also included: a bit reversal table.
+ *
+ *Copyright (C) 2002    Wolfgang Mües      <wolfgang@iksw-muees.de>
+ *		2001 	Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 	Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ISDNHDLC_H__
+#define __ISDNHDLC_H__
+
+struct isdnhdlc_vars {
+	int bit_shift;
+	int hdlc_bits1;
+	int data_bits;
+	int ffbit_shift; 	// encoding only
+	int state;
+	int dstpos;
+
+	unsigned short crc;
+
+	unsigned char cbin;
+	unsigned char shift_reg;
+	unsigned char ffvalue;
+
+	int data_received:1; 	// set if transferring data
+	int dchannel:1; 	// set if D channel (send idle instead of flags)
+	int do_adapt56:1; 	// set if 56K adaptation
+        int do_closing:1; 	// set if in closing phase (need to send CRC + flag
+};
+
+
+/*
+  The return value from isdnhdlc_decode is
+  the frame length, 0 if no complete frame was decoded,
+  or a negative error number
+*/
+#define HDLC_FRAMING_ERROR     1
+#define HDLC_CRC_ERROR         2
+#define HDLC_LENGTH_ERROR      3
+
+extern const unsigned char isdnhdlc_bit_rev_tab[256];
+
+extern void isdnhdlc_rcv_init (struct isdnhdlc_vars *hdlc, int do_adapt56);
+
+extern int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src, int slen,int *count,
+	                    unsigned char *dst, int dsize);
+
+extern void isdnhdlc_out_init (struct isdnhdlc_vars *hdlc,int is_d_channel,int do_adapt56);
+
+extern int isdnhdlc_encode (struct isdnhdlc_vars *hdlc,const unsigned char *src,unsigned short slen,int *count,
+	                    unsigned char *dst,int dsize);
+
+#endif /* __ISDNHDLC_H__ */
diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c
new file mode 100644
index 000000000000..4d08d27f1499
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl1.c
@@ -0,0 +1,932 @@
+/* $Id: isdnl1.c,v 2.46.2.5 2004/02/11 13:21:34 keil Exp $
+ *
+ * common low level stuff for Siemens Chipsetbased isdn cards
+ *
+ * Author       Karsten Keil
+ *              based on the teles driver from Jan den Ouden
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ */
+
+const char *l1_revision = "$Revision: 2.46.2.5 $";
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isdnl1.h"
+
+#define TIMER3_VALUE 7000
+
+static struct Fsm l1fsm_b;
+static struct Fsm l1fsm_s;
+
+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 HISAX_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_RSYNC_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_RSYNC_IND", 
+	"EV_INFO2_IND",
+	"EV_INFO4_IND",
+	"EV_TIMER_DEACT",
+	"EV_TIMER_ACT",
+	"EV_TIMER3",
+};
+
+void
+debugl1(struct IsdnCardState *cs, char *fmt, ...)
+{
+	va_list args;
+	char tmp[8];
+	
+	va_start(args, fmt);
+	sprintf(tmp, "Card%d ", cs->cardnr + 1);
+	VHiSax_putstatus(cs, tmp, fmt, args);
+	va_end(args);
+}
+
+static void
+l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+	char tmp[8];
+	
+	va_start(args, fmt);
+	sprintf(tmp, "Card%d ", cs->cardnr + 1);
+	VHiSax_putstatus(cs, tmp, fmt, args);
+	va_end(args);
+}
+
+void
+L1activated(struct IsdnCardState *cs)
+{
+	struct PStack *st;
+
+	st = cs->stlist;
+	while (st) {
+		if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+			st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+		else
+			st->l1.l1l2(st, PH_ACTIVATE | INDICATION, NULL);
+		st = st->next;
+	}
+}
+
+void
+L1deactivated(struct IsdnCardState *cs)
+{
+	struct PStack *st;
+
+	st = cs->stlist;
+	while (st) {
+		if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL);
+		st->l1.l1l2(st, PH_DEACTIVATE | INDICATION, NULL);
+		st = st->next;
+	}
+	test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+}
+
+void
+DChannel_proc_xmt(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+
+	if (cs->tx_skb)
+		return;
+
+	stptr = cs->stlist;
+	while (stptr != NULL) {
+		if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) {
+			stptr->l1.l1l2(stptr, PH_PULL | CONFIRM, NULL);
+			break;
+		} else
+			stptr = stptr->next;
+	}
+}
+
+void
+DChannel_proc_rcv(struct IsdnCardState *cs)
+{
+	struct sk_buff *skb, *nskb;
+	struct PStack *stptr = cs->stlist;
+	int found, tei, sapi;
+
+	if (stptr)
+		if (test_bit(FLG_L1_ACTTIMER, &stptr->l1.Flags))
+			FsmEvent(&stptr->l1.l1m, EV_TIMER_ACT, NULL);	
+	while ((skb = skb_dequeue(&cs->rq))) {
+#ifdef L2FRAME_DEBUG		/* psa */
+		if (cs->debug & L1_DEB_LAPD)
+			Logl2Frame(cs, skb, "PH_DATA", 1);
+#endif
+		stptr = cs->stlist;
+		if (skb->len<3) {
+			debugl1(cs, "D-channel frame too short(%d)",skb->len);
+			dev_kfree_skb(skb);
+			return;
+		}
+		if ((skb->data[0] & 1) || !(skb->data[1] &1)) {
+			debugl1(cs, "D-channel frame wrong EA0/EA1");
+			dev_kfree_skb(skb);
+			return;
+		}
+		sapi = skb->data[0] >> 2;
+		tei = skb->data[1] >> 1;
+		if (cs->debug & DEB_DLOG_HEX)
+			LogFrame(cs, skb->data, skb->len);
+		if (cs->debug & DEB_DLOG_VERBOSE)
+			dlogframe(cs, skb, 1);
+		if (tei == GROUP_TEI) {
+			if (sapi == CTRL_SAPI) { /* sapi 0 */
+				while (stptr != NULL) {
+					if ((nskb = skb_clone(skb, GFP_ATOMIC)))
+						stptr->l1.l1l2(stptr, PH_DATA | INDICATION, nskb);
+					else
+						printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n");
+					stptr = stptr->next;
+				}
+			} else if (sapi == TEI_SAPI) {
+				while (stptr != NULL) {
+					if ((nskb = skb_clone(skb, GFP_ATOMIC)))
+						stptr->l1.l1tei(stptr, PH_DATA | INDICATION, nskb);
+					else
+						printk(KERN_WARNING "HiSax: tei broadcast buffer shortage\n");
+					stptr = stptr->next;
+				}
+			}
+			dev_kfree_skb(skb);
+		} else if (sapi == CTRL_SAPI) { /* sapi 0 */
+			found = 0;
+			while (stptr != NULL)
+				if (tei == stptr->l2.tei) {
+					stptr->l1.l1l2(stptr, PH_DATA | INDICATION, skb);
+					found = !0;
+					break;
+				} else
+					stptr = stptr->next;
+			if (!found)
+				dev_kfree_skb(skb);
+		} else
+			dev_kfree_skb(skb);
+	}
+}
+
+static void
+BChannel_proc_xmt(struct BCState *bcs)
+{
+	struct PStack *st = bcs->st;
+
+	if (test_bit(BC_FLG_BUSY, &bcs->Flag)) {
+		debugl1(bcs->cs, "BC_BUSY Error");
+		return;
+	}
+
+	if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags))
+		st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+	if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) {
+		if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue))) {
+			st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+		}
+	}
+}
+
+static void
+BChannel_proc_rcv(struct BCState *bcs)
+{
+	struct sk_buff *skb;
+
+	if (bcs->st->l1.l1m.state == ST_L1_WAIT_ACT) {
+		FsmDelTimer(&bcs->st->l1.timer, 4);
+		FsmEvent(&bcs->st->l1.l1m, EV_TIMER_ACT, NULL);
+	}
+	while ((skb = skb_dequeue(&bcs->rqueue))) {
+		bcs->st->l1.l1l2(bcs->st, PH_DATA | INDICATION, skb);
+	}
+}
+
+static void
+BChannel_proc_ack(struct BCState *bcs)
+{
+	u_long	flags;
+	int	ack;
+
+	spin_lock_irqsave(&bcs->aclock, flags);
+	ack = bcs->ackcnt;
+	bcs->ackcnt = 0;
+	spin_unlock_irqrestore(&bcs->aclock, flags);
+	if (ack)
+		lli_writewakeup(bcs->st, ack);
+}
+
+void
+BChannel_bh(struct BCState *bcs)
+{
+	if (!bcs)
+		return;
+	if (test_and_clear_bit(B_RCVBUFREADY, &bcs->event))
+		BChannel_proc_rcv(bcs);
+	if (test_and_clear_bit(B_XMTBUFREADY, &bcs->event))
+		BChannel_proc_xmt(bcs);
+	if (test_and_clear_bit(B_ACKPENDING, &bcs->event))
+		BChannel_proc_ack(bcs);
+}
+
+void
+HiSax_addlist(struct IsdnCardState *cs,
+	      struct PStack *st)
+{
+	st->next = cs->stlist;
+	cs->stlist = st;
+}
+
+void
+HiSax_rmlist(struct IsdnCardState *cs,
+	     struct PStack *st)
+{
+	struct PStack *p;
+
+	FsmDelTimer(&st->l1.timer, 0);
+	if (cs->stlist == st)
+		cs->stlist = st->next;
+	else {
+		p = cs->stlist;
+		while (p)
+			if (p->next == st) {
+				p->next = st->next;
+				return;
+			} else
+				p = p->next;
+	}
+}
+
+void
+init_bcstate(struct IsdnCardState *cs, int bc)
+{
+	struct BCState *bcs = cs->bcs + bc;
+
+	bcs->cs = cs;
+	bcs->channel = bc;
+	INIT_WORK(&bcs->tqueue, (void *)(void *) BChannel_bh, bcs);
+	spin_lock_init(&bcs->aclock);
+	bcs->BC_SetStack = NULL;
+	bcs->BC_Close = NULL;
+	bcs->Flag = 0;
+}
+
+#ifdef L2FRAME_DEBUG		/* psa */
+
+char *
+l2cmd(u_char cmd)
+{
+	switch (cmd & ~0x10) {
+		case 1:
+			return "RR";
+		case 5:
+			return "RNR";
+		case 9:
+			return "REJ";
+		case 0x6f:
+			return "SABME";
+		case 0x0f:
+			return "DM";
+		case 3:
+			return "UI";
+		case 0x43:
+			return "DISC";
+		case 0x63:
+			return "UA";
+		case 0x87:
+			return "FRMR";
+		case 0xaf:
+			return "XID";
+		default:
+			if (!(cmd & 1))
+				return "I";
+			else
+				return "invalid command";
+	}
+}
+
+static char tmpdeb[32];
+
+char *
+l2frames(u_char * ptr)
+{
+	switch (ptr[2] & ~0x10) {
+		case 1:
+		case 5:
+		case 9:
+			sprintf(tmpdeb, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1);
+			break;
+		case 0x6f:
+		case 0x0f:
+		case 3:
+		case 0x43:
+		case 0x63:
+		case 0x87:
+		case 0xaf:
+			sprintf(tmpdeb, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4);
+			break;
+		default:
+			if (!(ptr[2] & 1)) {
+				sprintf(tmpdeb, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1);
+				break;
+			} else
+				return "invalid command";
+	}
+
+
+	return tmpdeb;
+}
+
+void
+Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir)
+{
+	u_char *ptr;
+
+	ptr = skb->data;
+
+	if (ptr[0] & 1 || !(ptr[1] & 1))
+		debugl1(cs, "Address not LAPD");
+	else
+		debugl1(cs, "%s %s: %s%c (sapi %d, tei %d)",
+			(dir ? "<-" : "->"), buf, l2frames(ptr),
+			((ptr[0] & 2) >> 1) == dir ? 'C' : 'R', ptr[0] >> 2, ptr[1] >> 1);
+}
+#endif
+
+static void
+l1_reset(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_deact_cnf(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F3);
+	if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+		st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
+}
+
+static void
+l1_deact_req_s(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F3);
+	FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
+	test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+}
+
+static void
+l1_power_up_s(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) {
+		FsmChangeState(fi, ST_L1_F4);
+		st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
+		FsmRestartTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+		test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags);
+	} else
+		FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_go_F5(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F5);
+}
+
+static void
+l1_go_F8(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F8);
+}
+
+static void
+l1_info2_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+#ifdef HISAX_UINTERFACE
+	if (test_bit(FLG_L1_UINT, &st->l1.Flags))
+		FsmChangeState(fi, ST_L1_SYNC2);
+	else
+#endif
+		FsmChangeState(fi, ST_L1_F6);
+	st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
+}
+
+static void
+l1_info4_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+#ifdef HISAX_UINTERFACE
+	if (test_bit(FLG_L1_UINT, &st->l1.Flags))
+		FsmChangeState(fi, ST_L1_TRANS);
+	else
+#endif
+		FsmChangeState(fi, ST_L1_F7);
+	st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
+	if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags))
+		FsmDelTimer(&st->l1.timer, 4);
+	if (!test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) {
+		if (test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags))
+			FsmDelTimer(&st->l1.timer, 3);
+		FsmRestartTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2);
+		test_and_set_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
+	}
+}
+
+static void
+l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags);	
+	if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+		L1deactivated(st->l1.hardware);
+
+#ifdef HISAX_UINTERFACE
+	if (!test_bit(FLG_L1_UINT, &st->l1.Flags))
+#endif
+	if (st->l1.l1m.state != ST_L1_F6) {
+		FsmChangeState(fi, ST_L1_F3);
+		st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
+	}
+}
+
+static void
+l1_timer_act(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	
+	test_and_clear_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
+	test_and_set_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
+	L1activated(st->l1.hardware);
+}
+
+static void
+l1_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	
+	test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+	test_and_clear_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
+	L1deactivated(st->l1.hardware);
+	st->l1.l1hw(st, HW_DEACTIVATE | RESPONSE, NULL);
+}
+
+static void
+l1_activate_s(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+                
+	st->l1.l1hw(st, HW_RESET | REQUEST, NULL);
+}
+
+static void
+l1_activate_no(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if ((!test_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) && (!test_bit(FLG_L1_T3RUN, &st->l1.Flags))) {
+		test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
+		L1deactivated(st->l1.hardware);
+	}
+}
+
+static struct FsmNode L1SFnList[] __initdata =
+{
+	{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_RSYNC_IND, l1_go_F5},
+	{ST_L1_F6, EV_RSYNC_IND, l1_go_F8},
+	{ST_L1_F7, EV_RSYNC_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 HISAX_UINTERFACE
+static void
+l1_deact_req_u(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_RESET);
+	FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
+	test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+	st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
+}
+
+static void
+l1_power_up_u(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmRestartTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+	test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags);
+}
+
+static void
+l1_info0_ind(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_DEACT);
+}
+
+static void
+l1_activate_u(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+                
+	st->l1.l1hw(st, HW_INFO1 | REQUEST, NULL);
+}
+
+static struct FsmNode L1UFnList[] __initdata =
+{
+	{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)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_WAIT_ACT);
+	FsmRestartTimer(&st->l1.timer, st->l1.delay, EV_TIMER_ACT, NULL, 2);
+}
+
+static void
+l1b_deactivate(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_WAIT_DEACT);
+	FsmRestartTimer(&st->l1.timer, 10, EV_TIMER_DEACT, NULL, 2);
+}
+
+static void
+l1b_timer_act(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_ACTIV);
+	st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+}
+
+static void
+l1b_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_NULL);
+	st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+}
+
+static struct FsmNode L1BFnList[] __initdata =
+{
+	{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))
+
+int __init 
+Isdnl1New(void)
+{
+	int retval;
+
+	l1fsm_s.state_count = L1S_STATE_COUNT;
+	l1fsm_s.event_count = L1_EVENT_COUNT;
+	l1fsm_s.strEvent = strL1Event;
+	l1fsm_s.strState = strL1SState;
+	retval = FsmNew(&l1fsm_s, L1SFnList, L1S_FN_COUNT);
+	if (retval)
+		return retval;
+
+	l1fsm_b.state_count = L1B_STATE_COUNT;
+	l1fsm_b.event_count = L1_EVENT_COUNT;
+	l1fsm_b.strEvent = strL1Event;
+	l1fsm_b.strState = strL1BState;
+	retval = FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT);
+	if (retval) {
+		FsmFree(&l1fsm_s);
+		return retval;
+	}
+#ifdef HISAX_UINTERFACE
+	l1fsm_u.state_count = L1U_STATE_COUNT;
+	l1fsm_u.event_count = L1_EVENT_COUNT;
+	l1fsm_u.strEvent = strL1Event;
+	l1fsm_u.strState = strL1UState;
+	retval = FsmNew(&l1fsm_u, L1UFnList, L1U_FN_COUNT);
+	if (retval) {
+		FsmFree(&l1fsm_s);
+		FsmFree(&l1fsm_b);
+		return retval;
+	}
+#endif
+	return 0;
+}
+
+void Isdnl1Free(void)
+{
+#ifdef HISAX_UINTERFACE
+	FsmFree(&l1fsm_u);
+#endif
+	FsmFree(&l1fsm_s);
+	FsmFree(&l1fsm_b);
+}
+
+static void
+dch_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (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):
+			if (cs->debug)
+				debugl1(cs, "PH_ACTIVATE_REQ %s",
+					st->l1.l1m.fsm->strState[st->l1.l1m.state]);
+			if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags))
+				st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+			else {
+				test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
+				FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg);
+			}
+			break;
+		case (PH_TESTLOOP | REQUEST):
+			if (1 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B1");
+			if (2 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B2");
+			if (!(3 & (long) arg))
+				debugl1(cs, "PH_TEST_LOOP DISABLED");
+			st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
+			break;
+		default:
+			if (cs->debug)
+				debugl1(cs, "dch_l2l1 msg %04X unhandled", pr);
+			break;
+	}
+}
+
+void
+l1_msg(struct IsdnCardState *cs, int pr, void *arg) {
+	struct PStack *st;
+
+	st = cs->stlist;
+	
+	while (st) {
+		switch(pr) {
+			case (HW_RESET | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_RESET_IND, arg);
+				break;
+			case (HW_DEACTIVATE | CONFIRM):
+				FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg);
+				break;
+			case (HW_DEACTIVATE | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg);
+				break;
+			case (HW_POWERUP | CONFIRM):
+				FsmEvent(&st->l1.l1m, EV_POWER_UP, arg);
+				break;
+			case (HW_RSYNC | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg);
+				break;
+			case (HW_INFO2 | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg);
+				break;
+			case (HW_INFO4_P8 | INDICATION):
+			case (HW_INFO4_P10 | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg);
+				break;
+			default:
+				if (cs->debug)
+					debugl1(cs, "l1msg %04X unhandled", pr);
+				break;
+		}
+		st = st->next;
+	}
+}
+
+void
+l1_msg_b(struct PStack *st, int pr, void *arg) {
+	switch(pr) {
+		case (PH_ACTIVATE | REQUEST):
+			FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, NULL);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			FsmEvent(&st->l1.l1m, EV_PH_DEACTIVATE, NULL);
+			break;
+	}
+}
+
+void
+setstack_HiSax(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.hardware = cs;
+	st->protocol = cs->protocol;
+	st->l1.l1m.fsm = &l1fsm_s;
+	st->l1.l1m.state = ST_L1_F3;
+	st->l1.Flags = 0;
+#ifdef HISAX_UINTERFACE
+	if (test_bit(FLG_HW_L1_UINT, &cs->HW_Flags)) {
+		st->l1.l1m.fsm = &l1fsm_u;
+		st->l1.l1m.state = ST_L1_RESET;
+		st->l1.Flags = FLG_L1_UINT;
+	}
+#endif
+	st->l1.l1m.debug = cs->debug;
+	st->l1.l1m.userdata = st;
+	st->l1.l1m.userint = 0;
+	st->l1.l1m.printdebug = l1m_debug;
+	FsmInitTimer(&st->l1.l1m, &st->l1.timer);
+	setstack_tei(st);
+	setstack_manager(st);
+	st->l1.stlistp = &(cs->stlist);
+	st->l2.l2l1  = dch_l2l1;
+	if (cs->setstack_d)
+		cs->setstack_d(st, cs);
+}
+
+void
+setstack_l1_B(struct PStack *st)
+{
+	struct IsdnCardState *cs = st->l1.hardware;
+
+	st->l1.l1m.fsm = &l1fsm_b;
+	st->l1.l1m.state = ST_L1_NULL;
+	st->l1.l1m.debug = cs->debug;
+	st->l1.l1m.userdata = st;
+	st->l1.l1m.userint = 0;
+	st->l1.l1m.printdebug = l1m_debug;
+	st->l1.Flags = 0;
+	FsmInitTimer(&st->l1.l1m, &st->l1.timer);
+}
diff --git a/drivers/isdn/hisax/isdnl1.h b/drivers/isdn/hisax/isdnl1.h
new file mode 100644
index 000000000000..0e88cfabdf10
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl1.h
@@ -0,0 +1,32 @@
+/* $Id: isdnl1.h,v 2.12.2.3 2004/02/11 13:21:34 keil Exp $
+ *
+ * Layer 1 defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#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 E_RCVBUFREADY	8
+
+#define B_RCVBUFREADY	0
+#define B_XMTBUFREADY	1
+#define B_ACKPENDING	2
+
+extern void debugl1(struct IsdnCardState *cs, char *fmt, ...);
+extern void DChannel_proc_xmt(struct IsdnCardState *cs);
+extern void DChannel_proc_rcv(struct IsdnCardState *cs);
+extern void l1_msg(struct IsdnCardState *cs, int pr, void *arg);
+extern void l1_msg_b(struct PStack *st, int pr, void *arg);
+
+#ifdef L2FRAME_DEBUG
+extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir);
+#endif
diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c
new file mode 100644
index 000000000000..d311b5fbf895
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl2.c
@@ -0,0 +1,1860 @@
+/* $Id: isdnl2.c,v 2.30.2.4 2004/02/11 13:21:34 keil Exp $
+ *
+ * Author       Karsten Keil
+ *              based on the teles driver from Jan den Ouden
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isdnl2.h"
+
+const char *l2_revision = "$Revision: 2.30.2.4 $";
+
+static void l2m_debug(struct FsmInst *fi, char *fmt, ...);
+
+static struct Fsm l2fsm;
+
+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_UNIT_DATA,
+	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_UNIT_DATA",
+	"EV_L2_DL_ESTABLISH_REQ",
+	"EV_L2_DL_RELEASE_REQ",
+	"EV_L2_MDL_ASSIGN",
+	"EV_L2_MDL_REMOVE",
+	"EV_L2_MDL_ERROR",
+	"EV_L1_DEACTIVATE",
+	"EV_L2_T200",
+	"EV_L2_T203",
+	"EV_L2_SET_OWN_BUSY",
+	"EV_L2_CLEAR_OWN_BUSY",
+	"EV_L2_FRAME_ERROR",
+};
+
+static int l2addrsize(struct Layer2 *l2);
+
+static void
+set_peer_busy(struct Layer2 *l2) {
+	test_and_set_bit(FLG_PEER_BUSY, &l2->flag);
+	if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue))
+		test_and_set_bit(FLG_L2BLOCK, &l2->flag);
+}
+
+static void
+clear_peer_busy(struct Layer2 *l2) {
+	if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag))
+		test_and_clear_bit(FLG_L2BLOCK, &l2->flag);
+}
+
+static void
+InitWin(struct Layer2 *l2)
+{
+	int i;
+
+	for (i = 0; i < MAX_WINDOW; i++)
+		l2->windowar[i] = NULL;
+}
+
+static int
+freewin1(struct Layer2 *l2)
+{
+	int i, cnt = 0;
+
+	for (i = 0; i < MAX_WINDOW; i++) {
+		if (l2->windowar[i]) {
+			cnt++;
+			dev_kfree_skb(l2->windowar[i]);
+			l2->windowar[i] = NULL;
+		}
+	}
+	return cnt;
+}
+
+inline void
+freewin(struct PStack *st)
+{
+	freewin1(&st->l2);
+}
+
+static void
+ReleaseWin(struct Layer2 *l2)
+{
+	int cnt;
+
+	if((cnt = freewin1(l2)))
+		printk(KERN_WARNING "isdl2 freed %d skbuffs in release\n", cnt);
+}
+
+inline unsigned int
+cansend(struct PStack *st)
+{
+	unsigned int p1;
+
+	if(test_bit(FLG_MOD128, &st->l2.flag))
+		p1 = (st->l2.vs - st->l2.va) % 128;
+	else
+		p1 = (st->l2.vs - st->l2.va) % 8;
+	return ((p1 < st->l2.window) && !test_bit(FLG_PEER_BUSY, &st->l2.flag));
+}
+
+inline void
+clear_exception(struct Layer2 *l2)
+{
+	test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+	test_and_clear_bit(FLG_REJEXC, &l2->flag);
+	test_and_clear_bit(FLG_OWN_BUSY, &l2->flag);
+	clear_peer_busy(l2);
+}
+
+inline int
+l2headersize(struct Layer2 *l2, int ui)
+{
+	return (((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) +
+		(test_bit(FLG_LAPD, &l2->flag) ? 2 : 1));
+}
+
+inline int
+l2addrsize(struct Layer2 *l2)
+{
+	return (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1);
+}
+
+static int
+sethdraddr(struct Layer2 *l2, u_char * header, int rsp)
+{
+	u_char *ptr = header;
+	int crbit = rsp;
+
+	if (test_bit(FLG_LAPD, &l2->flag)) {
+		*ptr++ = (l2->sap << 2) | (rsp ? 2 : 0);
+		*ptr++ = (l2->tei << 1) | 1;
+		return (2);
+	} else {
+		if (test_bit(FLG_ORIG, &l2->flag))
+			crbit = !crbit;
+		if (crbit)
+			*ptr++ = 1;
+		else
+			*ptr++ = 3;
+		return (1);
+	}
+}
+
+inline static void
+enqueue_super(struct PStack *st,
+	      struct sk_buff *skb)
+{
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l1.bcs->tx_cnt += skb->len;
+	st->l2.l2l1(st, PH_DATA | REQUEST, skb);
+}
+
+#define enqueue_ui(a, b) enqueue_super(a, b)
+
+inline int
+IsUI(u_char * data)
+{
+	return ((data[0] & 0xef) == UI);
+}
+
+inline int
+IsUA(u_char * data)
+{
+	return ((data[0] & 0xef) == UA);
+}
+
+inline int
+IsDM(u_char * data)
+{
+	return ((data[0] & 0xef) == DM);
+}
+
+inline int
+IsDISC(u_char * data)
+{
+	return ((data[0] & 0xef) == DISC);
+}
+
+inline int
+IsRR(u_char * data, struct PStack *st)
+{
+	if (test_bit(FLG_MOD128, &st->l2.flag))
+		return (data[0] == RR);
+	else
+		return ((data[0] & 0xf) == 1);
+}
+
+inline int
+IsSFrame(u_char * data, struct PStack *st)
+{
+	register u_char d = *data;
+	
+	if (!test_bit(FLG_MOD128, &st->l2.flag))
+		d &= 0xf;
+	return(((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c));
+}
+
+inline int
+IsSABME(u_char * data, struct PStack *st)
+{
+	u_char d = data[0] & ~0x10;
+
+	return (test_bit(FLG_MOD128, &st->l2.flag) ? d == SABME : d == SABM);
+}
+
+inline int
+IsREJ(u_char * data, struct PStack *st)
+{
+	return (test_bit(FLG_MOD128, &st->l2.flag) ? data[0] == REJ : (data[0] & 0xf) == REJ);
+}
+
+inline int
+IsFRMR(u_char * data)
+{
+	return ((data[0] & 0xef) == FRMR);
+}
+
+inline int
+IsRNR(u_char * data, struct PStack *st)
+{
+	return (test_bit(FLG_MOD128, &st->l2.flag) ? data[0] == RNR : (data[0] & 0xf) == RNR);
+}
+
+int
+iframe_error(struct PStack *st, struct sk_buff *skb)
+{
+	int i = l2addrsize(&st->l2) + (test_bit(FLG_MOD128, &st->l2.flag) ? 2 : 1);
+	int rsp = *skb->data & 0x2;
+
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
+
+	if (rsp)
+		return 'L';
+
+
+	if (skb->len < i)
+		return 'N';
+
+	if ((skb->len - i) > st->l2.maxlen)
+		return 'O';
+
+
+	return 0;
+}
+
+int
+super_error(struct PStack *st, struct sk_buff *skb)
+{
+	if (skb->len != l2addrsize(&st->l2) +
+	    (test_bit(FLG_MOD128, &st->l2.flag) ? 2 : 1))
+		return 'N';
+
+	return 0;
+}
+
+int
+unnum_error(struct PStack *st, struct sk_buff *skb, int wantrsp)
+{
+	int rsp = (*skb->data & 0x2) >> 1;
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
+
+	if (rsp != wantrsp)
+		return 'L';
+
+	if (skb->len != l2addrsize(&st->l2) + 1)
+		return 'N';
+
+	return 0;
+}
+
+int
+UI_error(struct PStack *st, struct sk_buff *skb)
+{
+	int rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
+
+	if (rsp)
+		return 'L';
+
+	if (skb->len > st->l2.maxlen + l2addrsize(&st->l2) + 1)
+		return 'O';
+
+	return 0;
+}
+
+int
+FRMR_error(struct PStack *st, struct sk_buff *skb)
+{
+	int headers = l2addrsize(&st->l2) + 1;
+	u_char *datap = skb->data + headers;
+	int rsp = *skb->data & 0x2;
+
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
+
+	if (!rsp)
+		return 'L';
+
+	if (test_bit(FLG_MOD128, &st->l2.flag)) {
+		if (skb->len < headers + 5)
+			return 'N';
+		else
+			l2m_debug(&st->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
+			l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x",
+				datap[0], datap[1], datap[2]);
+	}
+
+	return 0;
+}
+
+static unsigned int
+legalnr(struct PStack *st, unsigned int nr)
+{
+        struct Layer2 *l2 = &st->l2;
+
+	if(test_bit(FLG_MOD128, &l2->flag))
+		return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128);
+	else
+		return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8);
+}
+
+static void
+setva(struct PStack *st, unsigned int nr)
+{
+	struct Layer2 *l2 = &st->l2;
+	int len;
+	u_long flags;
+
+	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;
+		len = l2->windowar[l2->sow]->len;
+		if (PACKET_NOACK == l2->windowar[l2->sow]->pkt_type)
+			len = -1;
+		dev_kfree_skb(l2->windowar[l2->sow]);
+		l2->windowar[l2->sow] = NULL;
+		l2->sow = (l2->sow + 1) % l2->window;
+		spin_unlock_irqrestore(&l2->lock, flags);
+		if (test_bit(FLG_LLI_L2WAKEUP, &st->lli.flag) && (len >=0))
+			lli_writewakeup(st, len);
+		spin_lock_irqsave(&l2->lock, flags);
+	}
+	spin_unlock_irqrestore(&l2->lock, flags);
+}
+
+static void
+send_uframe(struct PStack *st, u_char cmd, u_char cr)
+{
+	struct sk_buff *skb;
+	u_char tmp[MAX_HEADER_LEN];
+	int i;
+
+	i = sethdraddr(&st->l2, tmp, cr);
+	tmp[i++] = cmd;
+	if (!(skb = alloc_skb(i, GFP_ATOMIC))) {
+		printk(KERN_WARNING "isdl2 can't alloc sbbuff for send_uframe\n");
+		return;
+	}
+	memcpy(skb_put(skb, i), tmp, i);
+	enqueue_super(st, skb);
+}
+
+inline u_char
+get_PollFlag(struct PStack * st, struct sk_buff * skb)
+{
+	return (skb->data[l2addrsize(&(st->l2))] & 0x10);
+}
+
+inline void
+FreeSkb(struct sk_buff *skb)
+{
+	dev_kfree_skb(skb);
+}
+
+
+inline u_char
+get_PollFlagFree(struct PStack *st, struct sk_buff *skb)
+{
+	u_char PF;
+
+	PF = get_PollFlag(st, skb);
+	FreeSkb(skb);
+	return (PF);
+}
+
+inline void
+start_t200(struct PStack *st, int i)
+{
+	FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, i);
+	test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+}
+
+inline void
+restart_t200(struct PStack *st, int i)
+{
+	FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, i);
+	test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+}
+
+inline void
+stop_t200(struct PStack *st, int i)
+{
+	if(test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+		FsmDelTimer(&st->l2.t200, i);
+}
+
+inline void
+st5_dl_release_l2l3(struct PStack *st)
+{
+		int pr;
+
+		if(test_and_clear_bit(FLG_PEND_REL, &st->l2.flag))
+			pr = DL_RELEASE | CONFIRM;
+		else
+			pr = DL_RELEASE | INDICATION;
+
+		st->l2.l2l3(st, pr, NULL);
+}
+
+inline void
+lapb_dl_release_l2l3(struct PStack *st, int f)
+{
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st->l2.l2l3(st, DL_RELEASE | f, NULL);
+}
+
+static void
+establishlink(struct FsmInst *fi)
+{
+	struct PStack *st = fi->userdata;
+	u_char cmd;
+
+	clear_exception(&st->l2);
+	st->l2.rc = 0;
+	cmd = (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) | 0x10;
+	send_uframe(st, cmd, CMD);
+	FsmDelTimer(&st->l2.t203, 1);
+	restart_t200(st, 1);
+	test_and_clear_bit(FLG_PEND_REL, &st->l2.flag);
+	freewin(st);
+	FsmChangeState(fi, ST_L2_5);
+}
+
+static void
+l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct PStack *st = fi->userdata;
+
+	if (get_PollFlagFree(st, skb))
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'C');
+	else
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'D');
+}
+
+static void
+l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct PStack *st = fi->userdata;
+
+	if (get_PollFlagFree(st, skb))
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B');
+	else {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E');
+		establishlink(fi);
+		test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+	}
+}
+
+static void
+l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct PStack *st = fi->userdata;
+
+	if (get_PollFlagFree(st, skb))
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B');
+	else {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E');
+	}
+	establishlink(fi);
+	test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+l2_go_st3(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L2_3); 
+}
+
+static void
+l2_mdl_assign(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L2_3); 
+	st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL);
+}
+
+static void
+l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_tail(&st->l2.ui_queue, skb);
+	FsmChangeState(fi, ST_L2_2);
+	st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL);
+}
+
+static void
+l2_queue_ui(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_tail(&st->l2.ui_queue, skb);
+}
+
+static void
+tx_ui(struct PStack *st)
+{
+	struct sk_buff *skb;
+	u_char header[MAX_HEADER_LEN];
+	int i;
+
+	i = sethdraddr(&(st->l2), header, CMD);
+	header[i++] = UI;
+	while ((skb = skb_dequeue(&st->l2.ui_queue))) {
+		memcpy(skb_push(skb, i), header, i);
+		enqueue_ui(st, skb);
+	}
+}
+
+static void
+l2_send_ui(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_tail(&st->l2.ui_queue, skb);
+	tx_ui(st);
+}
+
+static void
+l2_got_ui(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_pull(skb, l2headersize(&st->l2, 1));
+	st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb);
+/*	^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *		in states 1-3 for broadcast
+ */
+
+
+}
+
+static void
+l2_establish(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	establishlink(fi);
+	test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+	test_and_clear_bit(FLG_PEND_REL, &st->l2.flag);
+}
+
+static void
+l2_l3_reestablish(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	establishlink(fi);
+	test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+l2_release(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+}
+
+static void
+l2_pend_rel(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	test_and_set_bit(FLG_PEND_REL, &st->l2.flag);
+}
+
+static void
+l2_disconnect(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	freewin(st);
+	FsmChangeState(fi, ST_L2_6);
+	st->l2.rc = 0;
+	send_uframe(st, DISC | 0x10, CMD);
+	FsmDelTimer(&st->l2.t203, 1);
+	restart_t200(st, 2);
+}
+
+static void
+l2_start_multi(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	send_uframe(st, UA | get_PollFlagFree(st, skb), RSP);
+
+	clear_exception(&st->l2);
+	st->l2.vs = 0;
+	st->l2.va = 0;
+	st->l2.vr = 0;
+	st->l2.sow = 0;
+	FsmChangeState(fi, ST_L2_7);
+	FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
+
+	st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL);
+}
+
+static void
+l2_send_UA(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	send_uframe(st, UA | get_PollFlagFree(st, skb), RSP);
+}
+
+static void
+l2_send_DM(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	send_uframe(st, DM | get_PollFlagFree(st, skb), RSP);
+}
+
+static void
+l2_restart_multi(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int est = 0, state;
+
+	state = fi->state;
+
+	send_uframe(st, UA | get_PollFlagFree(st, skb), RSP);
+
+	st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'F');
+
+	if (st->l2.vs != st->l2.va) {
+		skb_queue_purge(&st->l2.i_queue);
+		est = 1;
+	}
+
+	clear_exception(&st->l2);
+	st->l2.vs = 0;
+	st->l2.va = 0;
+	st->l2.vr = 0;
+	st->l2.sow = 0;
+	FsmChangeState(fi, ST_L2_7);
+	stop_t200(st, 3);
+	FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
+
+	if (est)
+		st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL);
+
+	if ((ST_L2_7==state) || (ST_L2_8 == state))
+		if (skb_queue_len(&st->l2.i_queue) && cansend(st))
+			st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+}
+
+static void
+l2_stop_multi(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	FsmChangeState(fi, ST_L2_4);
+	FsmDelTimer(&st->l2.t203, 3);
+	stop_t200(st, 4);
+
+	send_uframe(st, UA | get_PollFlagFree(st, skb), RSP);
+
+	skb_queue_purge(&st->l2.i_queue);
+	freewin(st);
+	lapb_dl_release_l2l3(st, INDICATION);
+}
+
+static void
+l2_connected(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int pr=-1;
+
+	if (!get_PollFlag(st, skb)) {
+		l2_mdl_error_ua(fi, event, arg);
+		return;
+	}
+	FreeSkb(skb);
+
+	if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag))
+		l2_disconnect(fi, event, arg);
+
+	if (test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) {
+		pr = DL_ESTABLISH | CONFIRM;
+	} else if (st->l2.vs != st->l2.va) {
+		skb_queue_purge(&st->l2.i_queue);
+		pr = DL_ESTABLISH | INDICATION;
+	}
+
+	stop_t200(st, 5);
+
+	st->l2.vr = 0;
+	st->l2.vs = 0;
+	st->l2.va = 0;
+	st->l2.sow = 0;
+	FsmChangeState(fi, ST_L2_7);
+	FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 4);
+
+	if (pr != -1)
+		st->l2.l2l3(st, pr, NULL);
+
+	if (skb_queue_len(&st->l2.i_queue) && cansend(st))
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+}
+
+static void
+l2_released(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (!get_PollFlag(st, skb)) {
+		l2_mdl_error_ua(fi, event, arg);
+		return;
+	}
+	FreeSkb(skb);
+
+	stop_t200(st, 6);
+	lapb_dl_release_l2l3(st, CONFIRM);
+	FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2_reestablish(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (!get_PollFlagFree(st, skb)) {
+		establishlink(fi);
+		test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+	}
+}
+
+static void
+l2_st5_dm_release(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (get_PollFlagFree(st, skb)) {
+		stop_t200(st, 7);
+	 	if (!test_bit(FLG_L3_INIT, &st->l2.flag))
+			skb_queue_purge(&st->l2.i_queue);
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st5_dl_release_l2l3(st);
+		FsmChangeState(fi, ST_L2_4);
+	}
+}
+
+static void
+l2_st6_dm_release(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (get_PollFlagFree(st, skb)) {
+		stop_t200(st, 8);
+		lapb_dl_release_l2l3(st, CONFIRM);
+		FsmChangeState(fi, ST_L2_4);
+	}
+}
+
+inline void
+enquiry_cr(struct PStack *st, u_char typ, u_char cr, u_char pf)
+{
+	struct sk_buff *skb;
+	struct Layer2 *l2;
+	u_char tmp[MAX_HEADER_LEN];
+	int i;
+
+	l2 = &st->l2;
+	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 "isdl2 can't alloc sbbuff for enquiry_cr\n");
+		return;
+	}
+	memcpy(skb_put(skb, i), tmp, i);
+	enqueue_super(st, skb);
+}
+
+inline void
+enquiry_response(struct PStack *st)
+{
+	if (test_bit(FLG_OWN_BUSY, &st->l2.flag))
+		enquiry_cr(st, RNR, RSP, 1);
+	else
+		enquiry_cr(st, RR, RSP, 1);
+	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+}
+
+inline void
+transmit_enquiry(struct PStack *st)
+{
+	if (test_bit(FLG_OWN_BUSY, &st->l2.flag))
+		enquiry_cr(st, RNR, CMD, 1);
+	else
+		enquiry_cr(st, RR, CMD, 1);
+	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+	start_t200(st, 9);
+}
+
+
+static void
+nrerrorrecovery(struct FsmInst *fi)
+{
+	struct PStack *st = fi->userdata;
+
+	st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'J');
+	establishlink(fi);
+	test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+invoke_retransmission(struct PStack *st, unsigned int nr)
+{
+	struct Layer2 *l2 = &st->l2;
+	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 (test_bit(FLG_LAPB, &l2->flag))
+				st->l1.bcs->tx_cnt += l2->windowar[p1]->len + l2headersize(l2, 0);
+			skb_queue_head(&l2->i_queue, l2->windowar[p1]);
+			l2->windowar[p1] = NULL;
+		}
+		spin_unlock_irqrestore(&l2->lock, flags);
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+		return;
+	}
+	spin_unlock_irqrestore(&l2->lock, flags);
+}
+
+static void
+l2_st7_got_super(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int PollFlag, rsp, typ = RR;
+	unsigned int nr;
+	struct Layer2 *l2 = &st->l2;
+
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &l2->flag))
+		rsp = !rsp;
+
+	skb_pull(skb, l2addrsize(l2));
+	if (IsRNR(skb->data, st)) {
+		set_peer_busy(l2);
+		typ = RNR;
+	} else
+		clear_peer_busy(l2);
+	if (IsREJ(skb->data, st))
+		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;
+	}
+	FreeSkb(skb);
+
+	if (PollFlag) {
+		if (rsp)
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'A');
+		else
+			enquiry_response(st);
+	}
+	if (legalnr(st, nr)) {
+		if (typ == REJ) {
+			setva(st, nr);
+			invoke_retransmission(st, nr);
+			stop_t200(st, 10);
+			if (FsmAddTimer(&st->l2.t203, st->l2.T203,
+					EV_L2_T203, NULL, 6))
+				l2m_debug(&st->l2.l2m, "Restart T203 ST7 REJ");
+		} else if ((nr == l2->vs) && (typ == RR)) {
+			setva(st, nr);
+			stop_t200(st, 11);
+			FsmRestartTimer(&st->l2.t203, st->l2.T203,
+					EV_L2_T203, NULL, 7);
+		} else if ((l2->va != nr) || (typ == RNR)) {
+			setva(st, nr);
+			if(typ != RR) FsmDelTimer(&st->l2.t203, 9);
+			restart_t200(st, 12);
+		}
+		if (skb_queue_len(&st->l2.i_queue) && (typ == RR))
+			st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+	} else
+		nrerrorrecovery(fi);
+}
+
+static void
+l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0);
+	if (!test_bit(FLG_L3_INIT, &st->l2.flag))
+		skb_queue_tail(&st->l2.i_queue, skb);
+	else
+		FreeSkb(skb);
+}
+
+static void
+l2_feed_i_pull(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0);
+	skb_queue_tail(&st->l2.i_queue, skb);
+	st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+}
+
+static void
+l2_feed_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0);
+	skb_queue_tail(&st->l2.i_queue, skb);
+}
+
+static void
+l2_got_iframe(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	struct Layer2 *l2 = &(st->l2);
+	int PollFlag, ns, i;
+	unsigned int nr;
+
+	i = l2addrsize(l2);
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		PollFlag = ((skb->data[i + 1] & 0x1) == 0x1);
+		ns = skb->data[i] >> 1;
+		nr = (skb->data[i + 1] >> 1) & 0x7f;
+	} else {
+		PollFlag = (skb->data[i] & 0x10);
+		ns = (skb->data[i] >> 1) & 0x7;
+		nr = (skb->data[i] >> 5) & 0x7;
+	}
+	if (test_bit(FLG_OWN_BUSY, &l2->flag)) {
+		FreeSkb(skb);
+		if(PollFlag) enquiry_response(st);
+	} else if (l2->vr == ns) {
+		(l2->vr)++;
+		if(test_bit(FLG_MOD128, &l2->flag))
+			l2->vr %= 128;
+		else
+			l2->vr %= 8;
+		test_and_clear_bit(FLG_REJEXC, &l2->flag);
+
+		if (PollFlag)
+			enquiry_response(st);
+		else
+			test_and_set_bit(FLG_ACK_PEND, &l2->flag);
+		skb_pull(skb, l2headersize(l2, 0));
+		st->l2.l2l3(st, DL_DATA | INDICATION, skb);
+	} else {
+		/* n(s)!=v(r) */
+		FreeSkb(skb);
+		if (test_and_set_bit(FLG_REJEXC, &l2->flag)) {
+			if (PollFlag)
+				enquiry_response(st);
+		} else {
+			enquiry_cr(st, REJ, RSP, PollFlag);
+			test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+		}
+	}
+
+	if (legalnr(st, nr)) {
+		if (!test_bit(FLG_PEER_BUSY, &st->l2.flag) && (fi->state == ST_L2_7)) {
+			if (nr == st->l2.vs) {
+				stop_t200(st, 13);
+				FsmRestartTimer(&st->l2.t203, st->l2.T203,
+						EV_L2_T203, NULL, 7);
+			} else if (nr != st->l2.va)
+				restart_t200(st, 14);
+		}
+		setva(st, nr);
+	} else {
+		nrerrorrecovery(fi);
+		return;
+	}
+
+	if (skb_queue_len(&st->l2.i_queue) && (fi->state == ST_L2_7))
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+	if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag))
+		enquiry_cr(st, RR, RSP, 0);
+}
+
+static void
+l2_got_tei(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->l2.tei = (long) arg;
+
+	if (fi->state == ST_L2_3) {
+		establishlink(fi);
+		test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+	} else
+		FsmChangeState(fi, ST_L2_4);
+	if (skb_queue_len(&st->l2.ui_queue))
+		tx_ui(st);
+}
+
+static void
+l2_st5_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+	} else if (st->l2.rc == st->l2.N200) {
+		FsmChangeState(fi, ST_L2_4);
+		test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+		skb_queue_purge(&st->l2.i_queue);
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'G');
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st5_dl_release_l2l3(st);
+	} else {
+		st->l2.rc++;
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+		send_uframe(st, (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM)
+			    | 0x10, CMD);
+	}
+}
+
+static void
+l2_st6_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+	} else if (st->l2.rc == st->l2.N200) {
+		FsmChangeState(fi, ST_L2_4);
+		test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'H');
+		lapb_dl_release_l2l3(st, CONFIRM);
+	} else {
+		st->l2.rc++;
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200,
+			    NULL, 9);
+		send_uframe(st, DISC | 0x10, CMD);
+	}
+}
+
+static void
+l2_st7_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+		return;
+	}
+	test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+	st->l2.rc = 0;
+	FsmChangeState(fi, ST_L2_8);
+
+	transmit_enquiry(st);
+	st->l2.rc++;
+}
+
+static void
+l2_st8_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+		return;
+	}
+	test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+	if (st->l2.rc == st->l2.N200) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'I');
+		establishlink(fi);
+		test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+	} else {
+		transmit_enquiry(st);
+		st->l2.rc++;
+	}
+}
+
+static void
+l2_st7_tout_203(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 9);
+		return;
+	}
+	FsmChangeState(fi, ST_L2_8);
+	transmit_enquiry(st);
+	st->l2.rc = 0;
+}
+
+static void
+l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb, *oskb;
+	struct Layer2 *l2 = &st->l2;
+	u_char header[MAX_HEADER_LEN];
+	int i;
+	int unsigned p1;
+	u_long flags;
+
+	if (!cansend(st))
+		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_clone(skb, GFP_ATOMIC);
+
+	i = sethdraddr(&st->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);
+	p1 = skb->data - skb->head;
+	if (p1 >= i)
+		memcpy(skb_push(skb, i), header, i);
+	else {
+		printk(KERN_WARNING
+		"isdl2 pull_iqueue skb header(%d/%d) too short\n", i, p1);
+		oskb = skb;
+		skb = alloc_skb(oskb->len + i, GFP_ATOMIC);
+		memcpy(skb_put(skb, i), header, i);
+		memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len);
+		FreeSkb(oskb);
+	}
+	st->l2.l2l1(st, PH_PULL | INDICATION, skb);
+	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+	if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) {
+		FsmDelTimer(&st->l2.t203, 13);
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11);
+	}
+	if (skb_queue_len(&l2->i_queue) && cansend(st))
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+}
+
+static void
+l2_st8_got_super(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int PollFlag, rsp, rnr = 0;
+	unsigned int nr;
+	struct Layer2 *l2 = &st->l2;
+
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &l2->flag))
+		rsp = !rsp;
+
+	skb_pull(skb, l2addrsize(l2));
+
+	if (IsRNR(skb->data, st)) {
+		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;
+	}
+	FreeSkb(skb);
+
+	if (rsp && PollFlag) {
+		if (legalnr(st, nr)) {
+			if (rnr) {
+				restart_t200(st, 15);
+			} else {
+				stop_t200(st, 16);
+				FsmAddTimer(&l2->t203, l2->T203,
+					    EV_L2_T203, NULL, 5);
+				setva(st, nr);
+			}
+			invoke_retransmission(st, nr);
+			FsmChangeState(fi, ST_L2_7);
+			if (skb_queue_len(&l2->i_queue) && cansend(st))
+				st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+		} else
+			nrerrorrecovery(fi);
+	} else {
+		if (!rsp && PollFlag)
+			enquiry_response(st);
+		if (legalnr(st, nr)) {
+			setva(st, nr);
+		} else
+			nrerrorrecovery(fi);
+	}
+}
+
+static void
+l2_got_FRMR(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_pull(skb, l2addrsize(&st->l2) + 1);
+
+	if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) ||		/* I or S */
+	    (IsUA(skb->data) && (fi->state == ST_L2_7))) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'K');
+		establishlink(fi);
+		test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+	}
+	FreeSkb(skb);
+}
+
+static void
+l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.ui_queue);
+	st->l2.tei = -1;
+	FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.ui_queue);
+	st->l2.tei = -1;
+	st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+	FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	skb_queue_purge(&st->l2.ui_queue);
+	freewin(st);
+	st->l2.tei = -1;
+	stop_t200(st, 17);
+	st5_dl_release_l2l3(st);
+	FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.ui_queue);
+	st->l2.tei = -1;
+	stop_t200(st, 18);
+	st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+	FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	skb_queue_purge(&st->l2.ui_queue);
+	freewin(st);
+	st->l2.tei = -1;
+	stop_t200(st, 17);
+	FsmDelTimer(&st->l2.t203, 19);
+	st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+	FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	
+	skb_queue_purge(&st->l2.i_queue);
+	skb_queue_purge(&st->l2.ui_queue);
+	if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+		st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+}
+
+static void
+l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	skb_queue_purge(&st->l2.ui_queue);
+	freewin(st);
+	stop_t200(st, 19);
+	st5_dl_release_l2l3(st);
+	FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.ui_queue);
+	stop_t200(st, 20);
+	st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+	FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	skb_queue_purge(&st->l2.ui_queue);
+	freewin(st);
+	stop_t200(st, 19);
+	FsmDelTimer(&st->l2.t203, 19);
+	st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+	FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2_set_own_busy(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if(!test_and_set_bit(FLG_OWN_BUSY, &st->l2.flag)) {
+		enquiry_cr(st, RNR, RSP, 0);
+		test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+	}
+}
+
+static void
+l2_clear_own_busy(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if(!test_and_clear_bit(FLG_OWN_BUSY, &st->l2.flag)) {
+		enquiry_cr(st, RR, RSP, 0);
+		test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+	}
+}
+
+static void
+l2_frame_error(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->ma.layer(st, MDL_ERROR | INDICATION, arg);
+}
+
+static void
+l2_frame_error_reest(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->ma.layer(st, MDL_ERROR | INDICATION, arg);
+	establishlink(fi);
+	test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static struct FsmNode L2FnList[] __initdata =
+{
+	{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_UNIT_DATA, l2_queue_ui_assign},
+	{ST_L2_2, EV_L2_DL_UNIT_DATA, l2_queue_ui},
+	{ST_L2_3, EV_L2_DL_UNIT_DATA, l2_queue_ui},
+	{ST_L2_4, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_5, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_6, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_7, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_8, EV_L2_DL_UNIT_DATA, 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 void
+isdnl2_l1l2(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *datap;
+	int ret = 1, len;
+	int c = 0;
+
+	switch (pr) {
+		case (PH_DATA | INDICATION):
+			datap = skb->data;
+			len = l2addrsize(&st->l2);
+			if (skb->len > len)
+				datap += len;
+			else {
+				FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *) 'N');
+				FreeSkb(skb);
+				return;
+			}
+			if (!(*datap & 1)) {	/* I-Frame */
+				if(!(c = iframe_error(st, skb)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb);
+			} else if (IsSFrame(datap, st)) {	/* S-Frame */
+				if(!(c = super_error(st, skb)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_SUPER, skb);
+			} else if (IsUI(datap)) {
+				if(!(c = UI_error(st, skb)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb);
+			} else if (IsSABME(datap, st)) {
+				if(!(c = unnum_error(st, skb, CMD)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_SABME, skb);
+			} else if (IsUA(datap)) {
+				if(!(c = unnum_error(st, skb, RSP)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb);
+			} else if (IsDISC(datap)) {
+				if(!(c = unnum_error(st, skb, CMD)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, skb);
+			} else if (IsDM(datap)) {
+				if(!(c = unnum_error(st, skb, RSP)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_DM, skb);
+			} else if (IsFRMR(datap)) {
+				if(!(c = FRMR_error(st,skb)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, skb);
+			} else {
+				FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *) 'L');
+				FreeSkb(skb);
+				ret = 0;
+			}
+			if(c) {
+				FreeSkb(skb);
+				FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *)(long)c);
+				ret = 0;
+			}
+			if (ret)
+				FreeSkb(skb);
+			break;
+		case (PH_PULL | CONFIRM):
+			FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg);
+			break;
+		case (PH_PAUSE | INDICATION):
+			test_and_set_bit(FLG_DCHAN_BUSY, &st->l2.flag);
+			break;
+		case (PH_PAUSE | CONFIRM):
+			test_and_clear_bit(FLG_DCHAN_BUSY, &st->l2.flag);
+			break;
+		case (PH_ACTIVATE | CONFIRM):
+		case (PH_ACTIVATE | INDICATION):
+			test_and_set_bit(FLG_L1_ACTIV, &st->l2.flag);
+			if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+				FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH_REQ, arg);
+			break;
+		case (PH_DEACTIVATE | INDICATION):
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag);
+			FsmEvent(&st->l2.l2m, EV_L1_DEACTIVATE, arg);
+			break;
+		default:
+			l2m_debug(&st->l2.l2m, "l2 unknown pr %04x", pr);
+			break;
+	}
+}
+
+static void
+isdnl2_l3l2(struct PStack *st, int pr, void *arg)
+{
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+			if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) {
+				dev_kfree_skb((struct sk_buff *) arg);
+			}
+			break;
+		case (DL_UNIT_DATA | REQUEST):
+			if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) {
+				dev_kfree_skb((struct sk_buff *) arg);
+			}
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			if (test_bit(FLG_L1_ACTIV, &st->l2.flag)) {
+				if (test_bit(FLG_LAPD, &st->l2.flag) ||
+					test_bit(FLG_ORIG, &st->l2.flag)) {
+					FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH_REQ, arg);
+				}
+			} else {
+				if (test_bit(FLG_LAPD, &st->l2.flag) ||
+					test_bit(FLG_ORIG, &st->l2.flag)) {
+					test_and_set_bit(FLG_ESTAB_PEND, &st->l2.flag);
+				}
+				st->l2.l2l1(st, PH_ACTIVATE, NULL);
+			}
+			break;
+		case (DL_RELEASE | REQUEST):
+			if (test_bit(FLG_LAPB, &st->l2.flag)) {
+				st->l2.l2l1(st, PH_DEACTIVATE, NULL);
+			}
+			FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE_REQ, arg);
+			break;
+		case (MDL_ASSIGN | REQUEST):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg);
+			break;
+		case (MDL_REMOVE | REQUEST):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg);
+			break;
+		case (MDL_ERROR | RESPONSE):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_ERROR, arg);
+			break;
+	}
+}
+
+void
+releasestack_isdnl2(struct PStack *st)
+{
+	FsmDelTimer(&st->l2.t200, 21);
+	FsmDelTimer(&st->l2.t203, 16);
+	skb_queue_purge(&st->l2.i_queue);
+	skb_queue_purge(&st->l2.ui_queue);
+	ReleaseWin(&st->l2);
+}
+
+static void
+l2m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	struct PStack *st = fi->userdata;
+
+	va_start(args, fmt);
+	VHiSax_putstatus(st->l1.hardware, st->l2.debug_id, fmt, args);
+	va_end(args);
+}
+
+void
+setstack_isdnl2(struct PStack *st, char *debug_id)
+{
+	spin_lock_init(&st->l2.lock);
+	st->l1.l1l2 = isdnl2_l1l2;
+	st->l3.l3l2 = isdnl2_l3l2;
+
+	skb_queue_head_init(&st->l2.i_queue);
+	skb_queue_head_init(&st->l2.ui_queue);
+	InitWin(&st->l2);
+	st->l2.debug = 0;
+
+	st->l2.l2m.fsm = &l2fsm;
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l2.l2m.state = ST_L2_4;
+	else
+	st->l2.l2m.state = ST_L2_1;
+	st->l2.l2m.debug = 0;
+	st->l2.l2m.userdata = st;
+	st->l2.l2m.userint = 0;
+	st->l2.l2m.printdebug = l2m_debug;
+	strcpy(st->l2.debug_id, debug_id);
+
+	FsmInitTimer(&st->l2.l2m, &st->l2.t200);
+	FsmInitTimer(&st->l2.l2m, &st->l2.t203);
+}
+
+static void
+transl2_l3l2(struct PStack *st, int pr, void *arg)
+{
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+		case (DL_UNIT_DATA | REQUEST):
+			st->l2.l2l1(st, PH_DATA | REQUEST, arg);
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+			break;
+		case (DL_RELEASE | REQUEST):
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+			break;
+	}
+}
+
+void
+setstack_transl2(struct PStack *st)
+{
+	st->l3.l3l2 = transl2_l3l2;
+}
+
+void
+releasestack_transl2(struct PStack *st)
+{
+}
+
+int __init
+Isdnl2New(void)
+{
+	l2fsm.state_count = L2_STATE_COUNT;
+	l2fsm.event_count = L2_EVENT_COUNT;
+	l2fsm.strEvent = strL2Event;
+	l2fsm.strState = strL2State;
+	return FsmNew(&l2fsm, L2FnList, L2_FN_COUNT);
+}
+
+void
+Isdnl2Free(void)
+{
+	FsmFree(&l2fsm);
+}
diff --git a/drivers/isdn/hisax/isdnl2.h b/drivers/isdn/hisax/isdnl2.h
new file mode 100644
index 000000000000..0cdab1b73fac
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl2.h
@@ -0,0 +1,26 @@
+/* $Id: isdnl2.h,v 1.3.6.2 2001/09/23 22:24:49 kai Exp $
+ *
+ * Layer 2 defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#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
+
diff --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c
new file mode 100644
index 000000000000..f571b5d18e91
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl3.c
@@ -0,0 +1,610 @@
+/* $Id: isdnl3.c,v 2.22.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * Author       Karsten Keil
+ *              based on the teles driver from Jan den Ouden
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isdnl3.h"
+#include <linux/config.h>
+
+const char *l3_revision = "$Revision: 2.22.2.3 $";
+
+static struct Fsm l3fsm;
+
+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, ...)
+{
+	va_list args;
+	struct PStack *st = fi->userdata;
+
+	va_start(args, fmt);
+	VHiSax_putstatus(st->l1.hardware, st->l3.debug_id, fmt, args);
+	va_end(args);
+}
+
+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 (*p & 0x80)
+			p++;
+		else {
+			if (codeset == wanted_set) {
+				if (*p == ie)
+                                  { /* improved length check (Werner Cornelius) */
+                                    if ((pend - p) < 2) 
+                                      return(NULL); 
+                                    if (*(p+1) > (pend - (p+2))) 
+                                      return(NULL); 
+                                    return (p);
+                                  }           
+                                  
+				if (*p > ie)
+					return (NULL);
+			}
+			p++;
+			l = *p++;
+			p += l;
+			codeset = maincodeset;
+		}
+	}
+	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);
+}
+
+static int OrigCallRef = 0;
+
+int
+newcallref(void)
+{
+	if (OrigCallRef == 127)
+		OrigCallRef = 1;
+	else
+		OrigCallRef++;
+	return (OrigCallRef);
+}
+
+void
+newl3state(struct l3_process *pc, int state)
+{
+	if (pc->debug & L3_DEB_STATE)
+		l3_debug(pc->st, "newstate cr %d %d --> %d", 
+			 pc->callref & 0x7F,
+			 pc->state, state);
+	pc->state = state;
+}
+
+static void
+L3ExpireTimer(struct L3Timer *t)
+{
+	t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc);
+}
+
+void
+L3InitTimer(struct l3_process *pc, struct L3Timer *t)
+{
+	t->pc = pc;
+	t->tl.function = (void *) L3ExpireTimer;
+	t->tl.data = (long) t;
+	init_timer(&t->tl);
+}
+
+void
+L3DelTimer(struct L3Timer *t)
+{
+	del_timer(&t->tl);
+}
+
+int
+L3AddTimer(struct L3Timer *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(struct l3_process *pc)
+{
+	L3DelTimer(&pc->timer);
+}
+
+struct sk_buff *
+l3_alloc_skb(int len)
+{
+	struct sk_buff *skb;
+
+	if (!(skb = alloc_skb(len + MAX_HEADER_LEN, GFP_ATOMIC))) {
+		printk(KERN_WARNING "HiSax: No skb for D-channel\n");
+		return (NULL);
+	}
+	skb_reserve(skb, MAX_HEADER_LEN);
+	return (skb);
+}
+
+static void
+no_l3_proto(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	HiSax_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 "HiSax: no specific protocol handler for proto %lu\n",ic->arg & 0xFF);
+	return(-1);
+}
+
+#ifdef	CONFIG_HISAX_EURO
+extern void setstack_dss1(struct PStack *st);
+#endif
+
+#ifdef  CONFIG_HISAX_NI1
+extern void setstack_ni1(struct PStack *st);
+#endif
+
+#ifdef	CONFIG_HISAX_1TR6
+extern void setstack_1tr6(struct PStack *st);
+#endif
+
+struct l3_process
+*getl3proc(struct PStack *st, int cr)
+{
+	struct l3_process *p = st->l3.proc;
+
+	while (p)
+		if (p->callref == cr)
+			return (p);
+		else
+			p = p->next;
+	return (NULL);
+}
+
+struct l3_process
+*new_l3_process(struct PStack *st, int cr)
+{
+	struct l3_process *p, *np;
+
+	if (!(p = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+		printk(KERN_ERR "HiSax can't get memory for cr %d\n", cr);
+		return (NULL);
+	}
+	if (!st->l3.proc)
+		st->l3.proc = p;
+	else {
+		np = st->l3.proc;
+		while (np->next)
+			np = np->next;
+		np->next = p;
+	}
+	p->next = NULL;
+	p->debug = st->l3.debug;
+	p->callref = cr;
+	p->state = 0;
+	p->chan = NULL;
+	p->st = st;
+	p->N303 = st->l3.N303;
+	L3InitTimer(p, &p->timer);
+	return (p);
+};
+
+void
+release_l3_process(struct l3_process *p)
+{
+	struct l3_process *np, *pp = NULL;
+
+	if (!p)
+		return;
+	np = p->st->l3.proc;
+	while (np) {
+		if (np == p) {
+			StopAllL3Timer(p);
+			if (pp)
+				pp->next = np->next;
+			else if (!(p->st->l3.proc = np->next) &&
+				!test_bit(FLG_PTP, &p->st->l2.flag)) {
+				if (p->debug)
+					l3_debug(p->st, "release_l3_process: last process");
+				if (!skb_queue_len(&p->st->l3.squeue)) {
+					if (p->debug)
+						l3_debug(p->st, "release_l3_process: release link");
+					if (p->st->protocol != ISDN_PTYPE_NI1)
+						FsmEvent(&p->st->l3.l3m, EV_RELEASE_REQ, NULL);
+					else
+						FsmEvent(&p->st->l3.l3m, EV_RELEASE_IND, NULL);
+				} else {
+					if (p->debug)
+						l3_debug(p->st, "release_l3_process: not release link");
+				}
+			} 
+			kfree(p);
+			return;
+		}
+		pp = np;
+		np = np->next;
+	}
+	printk(KERN_ERR "HiSax internal L3 error CR(%d) not in list\n", p->callref);
+	l3_debug(p->st, "HiSax internal L3 error CR(%d) not in list", p->callref);
+};
+
+static void
+l3ml3p(struct PStack *st, int pr)
+{
+	struct l3_process *p = st->l3.proc;
+	struct l3_process *np;
+
+	while (p) {
+		/* p might be kfreed under us, so we need to save where we want to go on */
+		np = p->next;
+		st->l3.l3ml3(st, pr, p);
+		p = np;
+	}
+}
+
+void
+setstack_l3dc(struct PStack *st, struct Channel *chanp)
+{
+	char tmp[64];
+
+	st->l3.proc   = NULL;
+	st->l3.global = NULL;
+	skb_queue_head_init(&st->l3.squeue);
+	st->l3.l3m.fsm = &l3fsm;
+	st->l3.l3m.state = ST_L3_LC_REL;
+	st->l3.l3m.debug = 1;
+	st->l3.l3m.userdata = st;
+	st->l3.l3m.userint = 0;
+	st->l3.l3m.printdebug = l3m_debug;
+        FsmInitTimer(&st->l3.l3m, &st->l3.l3m_timer);
+	strcpy(st->l3.debug_id, "L3DC ");
+	st->lli.l4l3_proto = no_l3_proto_spec;
+
+#ifdef	CONFIG_HISAX_EURO
+	if (st->protocol == ISDN_PTYPE_EURO) {
+		setstack_dss1(st);
+	} else
+#endif
+#ifdef  CONFIG_HISAX_NI1
+	if (st->protocol == ISDN_PTYPE_NI1) {
+		setstack_ni1(st);
+	} else
+#endif
+#ifdef	CONFIG_HISAX_1TR6
+	if (st->protocol == ISDN_PTYPE_1TR6) {
+		setstack_1tr6(st);
+	} else
+#endif
+	if (st->protocol == ISDN_PTYPE_LEASED) {
+		st->lli.l4l3 = no_l3_proto;
+		st->l2.l2l3 = no_l3_proto;
+                st->l3.l3ml3 = no_l3_proto;
+		printk(KERN_INFO "HiSax: Leased line mode\n");
+	} else {
+		st->lli.l4l3 = no_l3_proto;
+		st->l2.l2l3 = no_l3_proto;
+                st->l3.l3ml3 = no_l3_proto;
+		sprintf(tmp, "protocol %s not supported",
+			(st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" :
+			(st->protocol == ISDN_PTYPE_EURO) ? "euro" :
+			(st->protocol == ISDN_PTYPE_NI1) ? "ni1" :
+			"unknown");
+		printk(KERN_WARNING "HiSax: %s\n", tmp);
+		st->protocol = -1;
+	}
+}
+
+void
+isdnl3_trans(struct PStack *st, int pr, void *arg) {
+	st->l3.l3l2(st, pr, arg);
+}
+
+void
+releasestack_isdnl3(struct PStack *st)
+{
+	while (st->l3.proc)
+		release_l3_process(st->l3.proc);
+	if (st->l3.global) {
+		StopAllL3Timer(st->l3.global);
+		kfree(st->l3.global);
+		st->l3.global = NULL;
+	}
+	FsmDelTimer(&st->l3.l3m_timer, 54);
+	skb_queue_purge(&st->l3.squeue);
+}
+
+void
+setstack_l3bc(struct PStack *st, struct Channel *chanp)
+{
+
+	st->l3.proc   = NULL;
+	st->l3.global = NULL;
+	skb_queue_head_init(&st->l3.squeue);
+	st->l3.l3m.fsm = &l3fsm;
+	st->l3.l3m.state = ST_L3_LC_REL;
+	st->l3.l3m.debug = 1;
+	st->l3.l3m.userdata = st;
+	st->l3.l3m.userint = 0;
+	st->l3.l3m.printdebug = l3m_debug;
+	strcpy(st->l3.debug_id, "L3BC ");
+	st->lli.l4l3 = isdnl3_trans;
+}
+
+#define DREL_TIMER_VALUE 40000
+
+static void
+lc_activate(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT);
+	st->l3.l3l2(st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+lc_connect(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int dequeued = 0;
+
+	FsmChangeState(fi, ST_L3_LC_ESTAB);
+	while ((skb = skb_dequeue(&st->l3.squeue))) {
+		st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+		dequeued++;
+	}
+	if ((!st->l3.proc) &&  dequeued) {
+		if (st->l3.debug)
+			l3_debug(st, "lc_connect: release link");
+		FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
+	} else
+		l3ml3p(st, DL_ESTABLISH | INDICATION);
+}
+
+static void
+lc_connected(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int dequeued = 0;
+
+	FsmDelTimer(&st->l3.l3m_timer, 51);
+	FsmChangeState(fi, ST_L3_LC_ESTAB);
+	while ((skb = skb_dequeue(&st->l3.squeue))) {
+		st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+		dequeued++;
+	}
+	if ((!st->l3.proc) &&  dequeued) {
+		if (st->l3.debug)
+			l3_debug(st, "lc_connected: release link");
+		FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
+	} else
+		l3ml3p(st, DL_ESTABLISH | CONFIRM);
+}
+
+static void
+lc_start_delay(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack *st = fi->userdata;
+
+       FsmChangeState(fi, ST_L3_LC_REL_DELAY);
+       FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50);
+}
+
+static void
+lc_start_delay_check(struct FsmInst *fi, int event, void *arg)
+/* 20/09/00 - GE timer not user for NI-1 as layer 2 should stay up */
+{
+       struct PStack *st = fi->userdata;
+
+       FsmChangeState(fi, ST_L3_LC_REL_DELAY);
+       /* 19/09/00 - GE timer not user for NI-1 */
+       if (st->protocol != ISDN_PTYPE_NI1) 
+       		FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50);
+}
+
+static void
+lc_release_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_L2BLOCK, &st->l2.flag)) {
+		if (st->l3.debug)
+			l3_debug(st, "lc_release_req: l2 blocked");
+		/* restart release timer */
+		FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51);
+	} else {
+		FsmChangeState(fi, ST_L3_LC_REL_WAIT);
+		st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL);
+	}
+}
+
+static void
+lc_release_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmDelTimer(&st->l3.l3m_timer, 52);
+	FsmChangeState(fi, ST_L3_LC_REL);
+	skb_queue_purge(&st->l3.squeue);
+	l3ml3p(st, DL_RELEASE | INDICATION);
+}
+
+static void
+lc_release_cnf(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L3_LC_REL);
+	skb_queue_purge(&st->l3.squeue);
+	l3ml3p(st, DL_RELEASE | CONFIRM);
+}
+
+
+/* *INDENT-OFF* */
+static struct FsmNode L3FnList[] __initdata =
+{
+	{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_check},
+        {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))
+
+void
+l3_msg(struct PStack *st, int pr, void *arg)
+{
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+			if (st->l3.l3m.state == ST_L3_LC_ESTAB) {
+				st->l3.l3l2(st, pr, arg);
+			} else {
+				struct sk_buff *skb = arg;
+
+				skb_queue_tail(&st->l3.squeue, skb);
+				FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL); 
+			}
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL);
+			break;
+		case (DL_ESTABLISH | CONFIRM):
+			FsmEvent(&st->l3.l3m, EV_ESTABLISH_CNF, NULL);
+			break;
+		case (DL_ESTABLISH | INDICATION):
+			FsmEvent(&st->l3.l3m, EV_ESTABLISH_IND, NULL);
+			break;
+		case (DL_RELEASE | INDICATION):
+			FsmEvent(&st->l3.l3m, EV_RELEASE_IND, NULL);
+			break;
+		case (DL_RELEASE | CONFIRM):
+			FsmEvent(&st->l3.l3m, EV_RELEASE_CNF, NULL);
+			break;
+		case (DL_RELEASE | REQUEST):
+			FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
+			break;
+	}
+}
+
+int __init
+Isdnl3New(void)
+{
+	l3fsm.state_count = L3_STATE_COUNT;
+	l3fsm.event_count = L3_EVENT_COUNT;
+	l3fsm.strEvent = strL3Event;
+	l3fsm.strState = strL3State;
+	return FsmNew(&l3fsm, L3FnList, L3_FN_COUNT);
+}
+
+void
+Isdnl3Free(void)
+{
+	FsmFree(&l3fsm);
+}
diff --git a/drivers/isdn/hisax/isdnl3.h b/drivers/isdn/hisax/isdnl3.h
new file mode 100644
index 000000000000..1dbe0297a506
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl3.h
@@ -0,0 +1,37 @@
+/* $Id: isdnl3.h,v 2.6.6.2 2001/09/23 22:24:49 kai Exp $
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#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
+
+struct stateentry {
+	int state;
+	int primitive;
+	void (*rout) (struct l3_process *, u8, void *);
+};
+
+#define l3_debug(st, fmt, args...) HiSax_putstatus(st->l1.hardware, "l3 ", fmt, ## args)
+
+extern void newl3state(struct l3_process *pc, int state);
+extern void L3InitTimer(struct l3_process *pc, struct L3Timer *t);
+extern void L3DelTimer(struct L3Timer *t);
+extern int L3AddTimer(struct L3Timer *t, int millisec, int event);
+extern void StopAllL3Timer(struct l3_process *pc);
+extern struct sk_buff *l3_alloc_skb(int len);
+extern struct l3_process *new_l3_process(struct PStack *st, int cr);
+extern void release_l3_process(struct l3_process *p);
+extern struct l3_process *getl3proc(struct PStack *st, int cr);
+extern void l3_msg(struct PStack *st, int pr, void *arg);
diff --git a/drivers/isdn/hisax/isurf.c b/drivers/isdn/hisax/isurf.c
new file mode 100644
index 000000000000..af5171da7345
--- /dev/null
+++ b/drivers/isdn/hisax/isurf.c
@@ -0,0 +1,306 @@
+/* $Id: isurf.c,v 1.12.2.4 2004/01/13 21:46:03 keil Exp $
+ *
+ * low level stuff for Siemens I-Surf/I-Talk cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isar.h"
+#include "isdnl1.h"
+#include <linux/isapnp.h>
+
+extern const char *CardType[];
+
+static const char *ISurf_revision = "$Revision: 1.12.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ISURF_ISAR_RESET	1
+#define ISURF_ISAC_RESET	2
+#define ISURF_ISAR_EA		4
+#define ISURF_ARCOFI_RESET	8
+#define ISURF_RESET (ISURF_ISAR_RESET | ISURF_ISAC_RESET | ISURF_ARCOFI_RESET)
+
+#define ISURF_ISAR_OFFSET	0
+#define ISURF_ISAC_OFFSET	0x100
+#define ISURF_IOMEM_SIZE	0x400
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readb(cs->hw.isurf.isac + offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writeb(value, cs->hw.isurf.isac + offset); mb();
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	register int i;
+	for (i = 0; i < size; i++)
+		data[i] = readb(cs->hw.isurf.isac);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	register int i;
+	for (i = 0; i < size; i++){
+		writeb(data[i], cs->hw.isurf.isac);mb();
+	}
+}
+
+/* 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(struct IsdnCardState *cs, int mode, u_char offset)
+{	
+	return(readb(cs->hw.isurf.isar + offset));
+}
+
+static void
+WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value)
+{
+	writeb(value, cs->hw.isurf.isar + offset);mb();
+}
+
+static irqreturn_t
+isurf_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	int cnt = 5;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readb(cs->hw.isurf.isar + ISAR_IRQBIT);
+      Start_ISAR:
+	if (val & ISAR_IRQSTA)
+		isar_int_main(cs);
+	val = readb(cs->hw.isurf.isac + ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readb(cs->hw.isurf.isar + ISAR_IRQBIT);
+	if ((val & ISAR_IRQSTA) && --cnt) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "ISAR IntStat after IntRoutine");
+		goto Start_ISAR;
+	}
+	val = readb(cs->hw.isurf.isac + ISAC_ISTA);
+	if (val && --cnt) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (!cnt)
+		printk(KERN_WARNING "ISurf IRQ LOOP\n");
+
+	writeb(0, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
+	writeb(0xFF, cs->hw.isurf.isac + ISAC_MASK);mb();
+	writeb(0, cs->hw.isurf.isac + ISAC_MASK);mb();
+	writeb(ISAR_IRQMSK, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_isurf(struct IsdnCardState *cs)
+{
+	release_region(cs->hw.isurf.reset, 1);
+	iounmap(cs->hw.isurf.isar);
+	release_mem_region(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE);
+}
+
+static void
+reset_isurf(struct IsdnCardState *cs, u_char chips)
+{
+	printk(KERN_INFO "ISurf: resetting card\n");
+
+	byteout(cs->hw.isurf.reset, chips); /* Reset On */
+	mdelay(10);
+	byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */
+	mdelay(10);
+}
+
+static int
+ISurf_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_isurf(cs, ISURF_RESET);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_isurf(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_isurf(cs, ISURF_RESET);
+			clear_pending_isac_ints(cs);
+			writeb(0, cs->hw.isurf.isar+ISAR_IRQBIT);mb();
+			initisac(cs);
+			initisar(cs);
+			/* Reenable ISAC IRQ */
+			cs->writeisac(cs, ISAC_MASK, 0);
+			/* RESET Receiver and Transmitter */
+			cs->writeisac(cs, ISAC_CMDR, 0x41);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static int
+isurf_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) {
+	int ret;
+	u_long flags;
+
+	if ((ic->command == ISDN_CMD_IOCTL) && (ic->arg == 9)) {
+		ret = isar_auxcmd(cs, ic);
+		spin_lock_irqsave(&cs->lock, flags);
+		if (!ret) {
+			reset_isurf(cs, ISURF_ISAR_EA | ISURF_ISAC_RESET |
+				ISURF_ARCOFI_RESET);
+			initisac(cs);
+			cs->writeisac(cs, ISAC_MASK, 0);
+			cs->writeisac(cs, ISAC_CMDR, 0x41);
+		}
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return(ret);
+	}
+	return(isar_auxcmd(cs, ic));
+}
+
+#ifdef __ISAPNP__
+static struct pnp_card *pnp_c __initdata = NULL;
+#endif
+
+int __init
+setup_isurf(struct IsdnCard *card)
+{
+	int ver;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, ISurf_revision);
+	printk(KERN_INFO "HiSax: ISurf driver Rev. %s\n", HiSax_getrev(tmp));
+	
+ 	if (cs->typ != ISDN_CTYPE_ISURF)
+ 		return(0);
+	if (card->para[1] && card->para[2]) {
+		cs->hw.isurf.reset = card->para[1];
+		cs->hw.isurf.phymem = card->para[2];
+		cs->irq = card->para[0];
+	} else {
+#ifdef __ISAPNP__
+		if (isapnp_present()) {
+			struct pnp_dev *pnp_d = NULL;
+			int err;
+
+			cs->subtyp = 0;
+			if ((pnp_c = pnp_find_card(
+				ISAPNP_VENDOR('S', 'I', 'E'),
+				ISAPNP_FUNCTION(0x0010), pnp_c))) {
+				if (!(pnp_d = pnp_find_dev(pnp_c,
+					ISAPNP_VENDOR('S', 'I', 'E'),
+					ISAPNP_FUNCTION(0x0010), pnp_d))) {
+					printk(KERN_ERR "ISurfPnP: PnP error card found, no device\n");
+					return (0);
+				}
+				pnp_disable_dev(pnp_d);
+				err = pnp_activate_dev(pnp_d);
+				cs->hw.isurf.reset = pnp_port_start(pnp_d, 0);
+				cs->hw.isurf.phymem = pnp_mem_start(pnp_d, 1);
+				cs->irq = pnp_irq(pnp_d, 0);
+				if (!cs->irq || !cs->hw.isurf.reset || !cs->hw.isurf.phymem) {
+					printk(KERN_ERR "ISurfPnP:some resources are missing %d/%x/%lx\n",
+						cs->irq, cs->hw.isurf.reset, cs->hw.isurf.phymem);
+					pnp_disable_dev(pnp_d);
+					return(0);
+				}
+			} else {
+				printk(KERN_INFO "ISurfPnP: no ISAPnP card found\n");
+				return(0);
+			}
+		} else {
+			printk(KERN_INFO "ISurfPnP: no ISAPnP bus found\n");
+			return(0);
+		}
+#else
+		printk(KERN_WARNING "HiSax: %s port/mem not set\n",
+			CardType[card->typ]);
+		return (0);
+#endif
+	}
+	if (!request_region(cs->hw.isurf.reset, 1, "isurf isdn")) {
+		printk(KERN_WARNING
+			"HiSax: %s config port %x already in use\n",
+			CardType[card->typ],
+			cs->hw.isurf.reset);
+			return (0);
+	}
+	if (!request_region(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE, "isurf iomem")) {
+		printk(KERN_WARNING
+			"HiSax: %s memory region %lx-%lx already in use\n",
+			CardType[card->typ],
+			cs->hw.isurf.phymem,
+			cs->hw.isurf.phymem + ISURF_IOMEM_SIZE);
+		release_region(cs->hw.isurf.reset, 1);
+		return (0);
+	}
+	cs->hw.isurf.isar = ioremap(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE);
+	cs->hw.isurf.isac = cs->hw.isurf.isar + ISURF_ISAC_OFFSET;
+	printk(KERN_INFO
+	       "ISurf: defined at 0x%x 0x%lx IRQ %d\n",
+	       cs->hw.isurf.reset,
+	       cs->hw.isurf.phymem,
+	       cs->irq);
+
+	setup_isac(cs);
+	cs->cardmsg = &ISurf_card_msg;
+	cs->irq_func = &isurf_interrupt;
+	cs->auxcmd = &isurf_auxcmd;
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->bcs[0].hw.isar.reg = &cs->hw.isurf.isar_r;
+	cs->bcs[1].hw.isar.reg = &cs->hw.isurf.isar_r;
+	test_and_set_bit(HW_ISAR, &cs->HW_Flags);
+	ISACVersion(cs, "ISurf:");
+	cs->BC_Read_Reg = &ReadISAR;
+	cs->BC_Write_Reg = &WriteISAR;
+	cs->BC_Send_Data = &isar_fill_fifo;
+	ver = ISARVersion(cs, "ISurf:");
+	if (ver < 0) {
+		printk(KERN_WARNING
+			"ISurf: wrong ISAR version (ret = %d)\n", ver);
+		release_io_isurf(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/ix1_micro.c b/drivers/isdn/hisax/ix1_micro.c
new file mode 100644
index 000000000000..b843b7509ae2
--- /dev/null
+++ b/drivers/isdn/hisax/ix1_micro.c
@@ -0,0 +1,318 @@
+/* $Id: ix1_micro.c,v 2.12.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for ITK ix1-micro Rev.2 isdn cards
+ * derived from the original file teles3.c from Karsten Keil
+ *
+ * Author       Klaus-Peter Nischke
+ * Copyright    by Klaus-Peter Nischke, ITK AG
+ *                                   <klaus@nischke.do.eunet.de>
+ *              by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Klaus-Peter Nischke
+ * Deusener Str. 287
+ * 44369 Dortmund
+ * Germany
+ */
+
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *ix1_revision = "$Revision: 2.12.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define SPECIAL_PORT_OFFSET 3
+
+#define ISAC_COMMAND_OFFSET 2
+#define ISAC_DATA_OFFSET 0
+#define HSCX_COMMAND_OFFSET 2
+#define HSCX_DATA_OFFSET 1
+
+#define TIMEOUT 50
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.ix1.hscx_ale,
+			cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.ix1.hscx_ale,
+		 cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0);
+	writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0);
+	writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_ix1micro(struct IsdnCardState *cs)
+{
+	if (cs->hw.ix1.cfg_reg)
+		release_region(cs->hw.ix1.cfg_reg, 4);
+}
+
+static void
+ix1_reset(struct IsdnCardState *cs)
+{
+	int cnt;
+
+	/* reset isac */
+	cnt = 3 * (HZ / 10) + 1;
+	while (cnt--) {
+		byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 1);
+		HZDELAY(1);	/* wait >=10 ms */
+	}
+	byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 0);
+}
+
+static int
+ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			ix1_reset(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_ix1micro(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			ix1_reset(cs);
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id itk_ids[] __initdata = {
+	{ ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25),
+	  ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25), 
+	  (unsigned long) "ITK micro 2" },
+	{ ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x29),
+	  ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x29), 
+	  (unsigned long) "ITK micro 2." },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __initdata = &itk_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+
+int __init
+setup_ix1micro(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, ix1_revision);
+	printk(KERN_INFO "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_IX1MICROR2)
+		return (0);
+
+#ifdef __ISAPNP__
+	if (!card->para[1] && isapnp_present()) {
+		struct pnp_dev *pnp_d;
+		while(ipid->card_vendor) {
+			if ((pnp_c = pnp_find_card(ipid->card_vendor,
+				ipid->card_device, pnp_c))) {
+				pnp_d = NULL;
+				if ((pnp_d = pnp_find_dev(pnp_c,
+					ipid->vendor, ipid->function, pnp_d))) {
+					int err;
+
+					printk(KERN_INFO "HiSax: %s detected\n",
+						(char *)ipid->driver_data);
+					pnp_disable_dev(pnp_d);
+					err = pnp_activate_dev(pnp_d);
+					if (err<0) {
+						printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+							__FUNCTION__, err);
+						return(0);
+					}
+					card->para[1] = pnp_port_start(pnp_d, 0);
+					card->para[0] = pnp_irq(pnp_d, 0);
+					if (!card->para[0] || !card->para[1]) {
+						printk(KERN_ERR "ITK PnP:some resources are missing %ld/%lx\n",
+							card->para[0], card->para[1]);
+						pnp_disable_dev(pnp_d);
+						return(0);
+					}
+					break;
+				} else {
+					printk(KERN_ERR "ITK PnP: PnP error card found, no device\n");
+				}
+			}
+			ipid++;
+			pnp_c = NULL;
+		} 
+		if (!ipid->card_vendor) {
+			printk(KERN_INFO "ITK PnP: no ISAPnP card found\n");
+			return(0);
+		}
+	}
+#endif
+	/* IO-Ports */
+	cs->hw.ix1.isac_ale = card->para[1] + ISAC_COMMAND_OFFSET;
+	cs->hw.ix1.hscx_ale = card->para[1] + HSCX_COMMAND_OFFSET;
+	cs->hw.ix1.isac = card->para[1] + ISAC_DATA_OFFSET;
+	cs->hw.ix1.hscx = card->para[1] + HSCX_DATA_OFFSET;
+	cs->hw.ix1.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	if (cs->hw.ix1.cfg_reg) {
+		if (!request_region(cs->hw.ix1.cfg_reg, 4, "ix1micro cfg")) {
+			printk(KERN_WARNING
+			  "HiSax: %s config port %x-%x already in use\n",
+			       CardType[card->typ],
+			       cs->hw.ix1.cfg_reg,
+			       cs->hw.ix1.cfg_reg + 4);
+			return (0);
+		}
+	}
+	printk(KERN_INFO "HiSax: %s config irq:%d io:0x%X\n",
+		CardType[cs->typ], cs->irq, cs->hw.ix1.cfg_reg);
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &ix1_card_msg;
+	cs->irq_func = &ix1micro_interrupt;
+	ISACVersion(cs, "ix1-Micro:");
+	if (HscxVersion(cs, "ix1-Micro:")) {
+		printk(KERN_WARNING
+		    "ix1-Micro: wrong HSCX versions check IO address\n");
+		release_io_ix1micro(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/jade.c b/drivers/isdn/hisax/jade.c
new file mode 100644
index 000000000000..f05d52757557
--- /dev/null
+++ b/drivers/isdn/hisax/jade.c
@@ -0,0 +1,318 @@
+/* $Id: jade.c,v 1.9.2.4 2004/01/14 16:04:48 keil Exp $
+ *
+ * JADE stuff (derived from original hscx.c)
+ *
+ * Author       Roland Klabunde
+ * Copyright    by Roland Klabunde   <R.Klabunde@Berkom.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hscx.h"
+#include "jade.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+
+int __init
+JadeVersion(struct IsdnCardState *cs, char *s)
+{
+    int ver,i;
+    int to = 50;
+    cs->BC_Write_Reg(cs, -1, 0x50, 0x19);
+    i=0;
+    while (to) {
+    	udelay(1);
+	ver = cs->BC_Read_Reg(cs, -1, 0x60);
+	to--;
+	if (ver)
+    	    break;
+	if (!to) {
+	    printk(KERN_INFO "%s JADE version not obtainable\n", s);
+    	    return (0);
+        }
+    }
+    /* Wait for the JADE */
+    udelay(10);
+    /* Read version */
+    ver = cs->BC_Read_Reg(cs, -1, 0x60);
+    printk(KERN_INFO "%s JADE version: %d\n", s, ver);
+    return (1);
+}
+
+/* Write to indirect accessible jade register set */
+static void
+jade_write_indirect(struct IsdnCardState *cs, u_char reg, u_char value)
+{
+    int to = 50;
+    u_char ret;
+
+    /* Write the data */
+    cs->BC_Write_Reg(cs, -1, COMM_JADE+1, value);
+    /* Say JADE we wanna write indirect reg 'reg' */
+    cs->BC_Write_Reg(cs, -1, COMM_JADE, reg);
+    to = 50;
+    /* Wait for RDY goes high */
+    while (to) {
+    	udelay(1);
+	ret = cs->BC_Read_Reg(cs, -1, COMM_JADE);
+	to--;
+	if (ret & 1)
+	    /* Got acknowledge */
+	    break;
+	if (!to) {
+    	    printk(KERN_INFO "Can not see ready bit from JADE DSP (reg=0x%X, value=0x%X)\n", reg, value);
+	    return;
+	}
+    }
+}
+
+
+
+void
+modejade(struct BCState *bcs, int mode, int bc)
+{
+    struct IsdnCardState *cs = bcs->cs;
+    int jade = bcs->hw.hscx.hscx;
+
+    if (cs->debug & L1_DEB_HSCX) {
+	char tmp[40];
+	sprintf(tmp, "jade %c mode %d ichan %d",
+		'A' + jade, mode, bc);
+	debugl1(cs, tmp);
+    }
+    bcs->mode = mode;
+    bcs->channel = bc;
+	
+    cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (mode == L1_MODE_TRANS ? jadeMODE_TMO:0x00));
+    cs->BC_Write_Reg(cs, jade, jade_HDLC_CCR0, (jadeCCR0_PU|jadeCCR0_ITF));
+    cs->BC_Write_Reg(cs, jade, jade_HDLC_CCR1, 0x00);
+
+    jade_write_indirect(cs, jade_HDLC1SERRXPATH, 0x08);
+    jade_write_indirect(cs, jade_HDLC2SERRXPATH, 0x08);
+    jade_write_indirect(cs, jade_HDLC1SERTXPATH, 0x00);
+    jade_write_indirect(cs, jade_HDLC2SERTXPATH, 0x00);
+
+    cs->BC_Write_Reg(cs, jade, jade_HDLC_XCCR, 0x07);
+    cs->BC_Write_Reg(cs, jade, jade_HDLC_RCCR, 0x07);
+
+    if (bc == 0) {
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAX, 0x00);
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAR, 0x00);
+    } else {
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAX, 0x04);
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAR, 0x04);
+    }
+    switch (mode) {
+	case (L1_MODE_NULL):
+		cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, jadeMODE_TMO);
+		break;
+	case (L1_MODE_TRANS):
+		cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (jadeMODE_TMO|jadeMODE_RAC|jadeMODE_XAC));
+		break;
+	case (L1_MODE_HDLC):
+		cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (jadeMODE_RAC|jadeMODE_XAC));
+		break;
+    }
+    if (mode) {
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_RCMD, (jadeRCMD_RRES|jadeRCMD_RMC));
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_XCMD, jadeXCMD_XRES);
+	/* Unmask ints */
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_IMR, 0xF8);
+    }
+    else
+	/* Mask ints */
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_IMR, 0x00);
+}
+
+static void
+jade_l2l1(struct PStack *st, int pr, void *arg)
+{
+    struct BCState *bcs = st->l1.bcs;
+    struct sk_buff *skb = arg;
+    u_long flags;
+
+    switch (pr) {
+	case (PH_DATA | REQUEST):
+		spin_lock_irqsave(&bcs->cs->lock, flags);
+		if (bcs->tx_skb) {
+			skb_queue_tail(&bcs->squeue, skb);
+		} else {
+			bcs->tx_skb = skb;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->hw.hscx.count = 0;
+			bcs->cs->BC_Send_Data(bcs);
+		}
+		spin_unlock_irqrestore(&bcs->cs->lock, flags);
+		break;
+	case (PH_PULL | INDICATION):
+		spin_lock_irqsave(&bcs->cs->lock, flags);
+		if (bcs->tx_skb) {
+			printk(KERN_WARNING "jade_l2l1: this shouldn't happen\n");
+		} else {
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->tx_skb = skb;
+			bcs->hw.hscx.count = 0;
+			bcs->cs->BC_Send_Data(bcs);
+		}
+		spin_unlock_irqrestore(&bcs->cs->lock, flags);
+		break;
+	case (PH_PULL | REQUEST):
+		if (!bcs->tx_skb) {
+		    test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+		    st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+		} else
+		    test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+		break;
+	case (PH_ACTIVATE | REQUEST):
+		spin_lock_irqsave(&bcs->cs->lock, flags);
+		test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+		modejade(bcs, st->l1.mode, st->l1.bc);
+		spin_unlock_irqrestore(&bcs->cs->lock, flags);
+		l1_msg_b(st, pr, arg);
+		break;
+	case (PH_DEACTIVATE | REQUEST):
+		l1_msg_b(st, pr, arg);
+		break;
+	case (PH_DEACTIVATE | CONFIRM):
+		spin_lock_irqsave(&bcs->cs->lock, flags);
+		test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		modejade(bcs, 0, st->l1.bc);
+		spin_unlock_irqrestore(&bcs->cs->lock, flags);
+		st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+		break;
+    }
+}
+
+void
+close_jadestate(struct BCState *bcs)
+{
+    modejade(bcs, 0, bcs->channel);
+    if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+	if (bcs->hw.hscx.rcvbuf) {
+		kfree(bcs->hw.hscx.rcvbuf);
+		bcs->hw.hscx.rcvbuf = NULL;
+	}
+	if (bcs->blog) {
+		kfree(bcs->blog);
+		bcs->blog = NULL;
+	}
+	skb_queue_purge(&bcs->rqueue);
+	skb_queue_purge(&bcs->squeue);
+	if (bcs->tx_skb) {
+		dev_kfree_skb_any(bcs->tx_skb);
+		bcs->tx_skb = NULL;
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	}
+    }
+}
+
+static int
+open_jadestate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for hscx.rcvbuf\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			return (1);
+		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax: No memory for bcs->blog\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+			return (2);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.hscx.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+
+int
+setstack_jade(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_jadestate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = jade_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void __init
+clear_pending_jade_ints(struct IsdnCardState *cs)
+{
+	int val;
+	char tmp[64];
+
+	cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0x00);
+	cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0x00);
+
+	val = cs->BC_Read_Reg(cs, 1, jade_HDLC_ISR);
+	sprintf(tmp, "jade B ISTA %x", val);
+	debugl1(cs, tmp);
+	val = cs->BC_Read_Reg(cs, 0, jade_HDLC_ISR);
+	sprintf(tmp, "jade A ISTA %x", val);
+	debugl1(cs, tmp);
+	val = cs->BC_Read_Reg(cs, 1, jade_HDLC_STAR);
+	sprintf(tmp, "jade B STAR %x", val);
+	debugl1(cs, tmp);
+	val = cs->BC_Read_Reg(cs, 0, jade_HDLC_STAR);
+	sprintf(tmp, "jade A STAR %x", val);
+	debugl1(cs, tmp);
+	/* Unmask ints */
+	cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0xF8);
+	cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0xF8);
+}
+
+void __init
+initjade(struct IsdnCardState *cs)
+{
+	cs->bcs[0].BC_SetStack = setstack_jade;
+	cs->bcs[1].BC_SetStack = setstack_jade;
+	cs->bcs[0].BC_Close = close_jadestate;
+	cs->bcs[1].BC_Close = close_jadestate;
+	cs->bcs[0].hw.hscx.hscx = 0;
+	cs->bcs[1].hw.hscx.hscx = 1;
+
+	/* Stop DSP audio tx/rx */
+	jade_write_indirect(cs, 0x11, 0x0f);
+	jade_write_indirect(cs, 0x17, 0x2f);
+
+	/* Transparent Mode, RxTx inactive, No Test, No RFS/TFS */
+	cs->BC_Write_Reg(cs, 0, jade_HDLC_MODE, jadeMODE_TMO);
+	cs->BC_Write_Reg(cs, 1, jade_HDLC_MODE, jadeMODE_TMO);
+	/* Power down, 1-Idle, RxTx least significant bit first */
+	cs->BC_Write_Reg(cs, 0, jade_HDLC_CCR0, 0x00);
+	cs->BC_Write_Reg(cs, 1, jade_HDLC_CCR0, 0x00);
+	/* Mask all interrupts */
+	cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR,  0x00);
+	cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR,  0x00);
+	/* Setup host access to hdlc controller */
+	jade_write_indirect(cs, jade_HDLCCNTRACCESS, (jadeINDIRECT_HAH1|jadeINDIRECT_HAH2));
+	/* Unmask HDLC int (don´t forget DSP int later on)*/
+	cs->BC_Write_Reg(cs, -1,jade_INT, (jadeINT_HDLC1|jadeINT_HDLC2));
+
+	/* once again TRANSPARENT */	
+	modejade(cs->bcs, 0, 0);
+	modejade(cs->bcs + 1, 0, 0);
+}
+
diff --git a/drivers/isdn/hisax/jade.h b/drivers/isdn/hisax/jade.h
new file mode 100644
index 000000000000..fa2944485994
--- /dev/null
+++ b/drivers/isdn/hisax/jade.h
@@ -0,0 +1,135 @@
+/* $Id: jade.h,v 1.5.2.3 2004/01/14 16:04:48 keil Exp $
+ *
+ * JADE specific defines
+ *
+ * Author       Roland Klabunde
+ * Copyright    by Roland Klabunde   <R.Klabunde@Berkom.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec  */
+#ifndef	__JADE_H__
+#define	__JADE_H__
+
+/* Special registers for access to indirect accessible JADE regs */
+#define	DIRECT_IO_JADE	0x0000	/* Jade direct io access area */
+#define	COMM_JADE	0x0040	/* Jade communication area */	   	
+
+/********************************************************************/
+/* JADE-HDLC registers         									    */
+/********************************************************************/
+#define jade_HDLC_RFIFO	   				0x00				   /* R */
+#define jade_HDLC_XFIFO	   				0x00				   /* W */
+
+#define	jade_HDLC_STAR	   				0x20				   /* R */
+	#define	jadeSTAR_XDOV				0x80
+	#define	jadeSTAR_XFW 				0x40 /* Does not work*/
+	#define	jadeSTAR_XCEC 				0x20
+	#define	jadeSTAR_RCEC				0x10
+	#define	jadeSTAR_BSY 				0x08
+	#define	jadeSTAR_RNA 				0x04
+	#define	jadeSTAR_STR 				0x02
+	#define	jadeSTAR_STX				0x01
+
+#define	jade_HDLC_XCMD	   				0x20				   /* W */
+	#define	jadeXCMD_XF				0x80
+	#define	jadeXCMD_XME				0x40
+	#define	jadeXCMD_XRES				0x20
+	#define	jadeXCMD_STX				0x01
+
+#define	jade_HDLC_RSTA	   				0x21				   /* R */
+    #define	jadeRSTA_VFR				0x80
+    #define	jadeRSTA_RDO				0x40
+    #define	jadeRSTA_CRC				0x20
+    #define	jadeRSTA_RAB				0x10
+    #define	jadeRSTA_MASK			   	0xF0
+
+#define	jade_HDLC_MODE					0x22				   /* RW*/
+    #define	jadeMODE_TMO				0x80
+    #define	jadeMODE_RAC				0x40
+    #define	jadeMODE_XAC				0x20
+    #define	jadeMODE_TLP				0x10
+    #define	jadeMODE_ERFS				0x02
+    #define	jadeMODE_ETFS				0x01
+
+#define	jade_HDLC_RBCH					0x24				   /* R */
+
+#define	jade_HDLC_RBCL	 				0x25				   /* R */
+#define	jade_HDLC_RCMD	 				0x25				   /* W */
+	#define	jadeRCMD_RMC 				0x80
+	#define	jadeRCMD_RRES				0x40
+	#define	jadeRCMD_RMD				0x20
+	#define	jadeRCMD_STR				0x02
+
+#define	jade_HDLC_CCR0					0x26				   /* RW*/
+	#define	jadeCCR0_PU  				0x80
+	#define	jadeCCR0_ITF				0x40
+	#define	jadeCCR0_C32				0x20
+	#define	jadeCCR0_CRL				0x10
+	#define	jadeCCR0_RCRC				0x08
+	#define	jadeCCR0_XCRC				0x04
+	#define	jadeCCR0_RMSB				0x02
+	#define	jadeCCR0_XMSB				0x01
+
+#define	jade_HDLC_CCR1					0x27				   /* RW*/
+    #define	jadeCCR1_RCS0				0x80
+    #define	jadeCCR1_RCONT				0x40
+    #define	jadeCCR1_RFDIS				0x20
+    #define	jadeCCR1_XCS0				0x10
+    #define	jadeCCR1_XCONT				0x08
+    #define	jadeCCR1_XFDIS				0x04
+
+#define	jade_HDLC_TSAR					0x28				   /* RW*/
+#define	jade_HDLC_TSAX					0x29				   /* RW*/
+#define	jade_HDLC_RCCR					0x2A				   /* RW*/
+#define	jade_HDLC_XCCR					0x2B				   /* RW*/
+
+#define	jade_HDLC_ISR 					0x2C				   /* R */
+#define	jade_HDLC_IMR 					0x2C				   /* W */
+	#define	jadeISR_RME					0x80
+	#define	jadeISR_RPF					0x40
+	#define	jadeISR_RFO					0x20
+	#define	jadeISR_XPR					0x10
+	#define	jadeISR_XDU					0x08
+	#define	jadeISR_ALLS				0x04
+
+#define jade_INT            			0x75
+    #define jadeINT_HDLC1   			0x02
+    #define jadeINT_HDLC2   			0x01
+    #define jadeINT_DSP				0x04
+#define jade_INTR            			0x70
+
+/********************************************************************/
+/* Indirect accessible JADE registers of common interest		   	*/
+/********************************************************************/
+#define	jade_CHIPVERSIONNR				0x00 /* Does not work*/
+
+#define	jade_HDLCCNTRACCESS				0x10		
+	#define	jadeINDIRECT_HAH1			0x02
+	#define	jadeINDIRECT_HAH2			0x01
+
+#define	jade_HDLC1SERRXPATH				0x1D
+#define	jade_HDLC1SERTXPATH				0x1E
+#define	jade_HDLC2SERRXPATH				0x1F
+#define	jade_HDLC2SERTXPATH				0x20
+	#define	jadeINDIRECT_SLIN1			0x10
+	#define	jadeINDIRECT_SLIN0			0x08
+	#define	jadeINDIRECT_LMOD1			0x04
+	#define	jadeINDIRECT_LMOD0			0x02
+	#define	jadeINDIRECT_HHR			0x01
+	#define	jadeINDIRECT_HHX			0x01
+
+#define	jade_RXAUDIOCH1CFG				0x11
+#define	jade_RXAUDIOCH2CFG				0x14
+#define	jade_TXAUDIOCH1CFG				0x17
+#define	jade_TXAUDIOCH2CFG				0x1A
+
+extern int JadeVersion(struct IsdnCardState *cs, char *s);
+extern void modejade(struct BCState *bcs, int mode, int bc);
+extern void clear_pending_jade_ints(struct IsdnCardState *cs);
+extern void initjade(struct IsdnCardState *cs);
+
+#endif	/* __JADE_H__ */
diff --git a/drivers/isdn/hisax/jade_irq.c b/drivers/isdn/hisax/jade_irq.c
new file mode 100644
index 000000000000..08563400e4fd
--- /dev/null
+++ b/drivers/isdn/hisax/jade_irq.c
@@ -0,0 +1,236 @@
+/* $Id: jade_irq.c,v 1.7.2.4 2004/02/11 13:21:34 keil Exp $
+ *
+ * Low level JADE IRQ stuff (derived from original hscx_irq.c)
+ *
+ * Author       Roland Klabunde
+ * Copyright    by Roland Klabunde   <R.Klabunde@Berkom.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+static inline void
+waitforCEC(struct IsdnCardState *cs, int jade, int reg)
+{
+  	int to = 50;
+  	int mask = (reg == jade_HDLC_XCMD ? jadeSTAR_XCEC : jadeSTAR_RCEC);
+  	while ((READJADE(cs, jade, jade_HDLC_STAR) & mask) && to) {
+  		udelay(1);
+  		to--;
+  	}
+  	if (!to)
+  		printk(KERN_WARNING "HiSax: waitforCEC (jade) timeout\n");
+}
+
+
+static inline void
+waitforXFW(struct IsdnCardState *cs, int jade)
+{
+  	/* Does not work on older jade versions, don't care */
+}
+
+static inline void
+WriteJADECMDR(struct IsdnCardState *cs, int jade, int reg, u_char data)
+{
+	waitforCEC(cs, jade, reg);
+	WRITEJADE(cs, jade, reg, data);
+}
+
+
+
+static void
+jade_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *cs = bcs->cs;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "jade_empty_fifo");
+
+	if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "jade_empty_fifo: incoming packet too large");
+		WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_RCMD, jadeRCMD_RMC);
+		bcs->hw.hscx.rcvidx = 0;
+		return;
+	}
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	bcs->hw.hscx.rcvidx += count;
+	READJADEFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+	WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_RCMD, jadeRCMD_RMC);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "jade_empty_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static void
+jade_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int more, count;
+	int fifo_size = 32;
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "jade_fill_fifo");
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+	if (bcs->tx_skb->len > fifo_size) {
+		more = !0;
+		count = fifo_size;
+	} else
+		count = bcs->tx_skb->len;
+
+	waitforXFW(cs, bcs->hw.hscx.hscx);
+	ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hscx.count += count;
+	WRITEJADEFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+	WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_XCMD, more ? jadeXCMD_XF : (jadeXCMD_XF|jadeXCMD_XME));
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "jade_fill_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+
+static inline void
+jade_interrupt(struct IsdnCardState *cs, u_char val, u_char jade)
+{
+	u_char r;
+	struct BCState *bcs = cs->bcs + jade;
+	struct sk_buff *skb;
+	int fifo_size = 32;
+	int count;
+	int i_jade = (int) jade; /* To satisfy the compiler */
+	
+	if (!test_bit(BC_FLG_INIT, &bcs->Flag))
+		return;
+
+	if (val & 0x80) {	/* RME */
+		r = READJADE(cs, i_jade, jade_HDLC_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!(r & 0x80))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "JADE %s invalid frame", (jade ? "B":"A"));
+			if ((r & 0x40) && bcs->mode)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "JADE %c RDO mode=%d", 'A'+jade, bcs->mode);
+			if (!(r & 0x20))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "JADE %c CRC error", 'A'+jade);
+			WriteJADECMDR(cs, jade, jade_HDLC_RCMD, jadeRCMD_RMC);
+		} else {
+			count = READJADE(cs, i_jade, jade_HDLC_RBCL) & 0x1F;
+			if (count == 0)
+				count = fifo_size;
+			jade_empty_fifo(bcs, count);
+			if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+				if (cs->debug & L1_DEB_HSCX_FIFO)
+					debugl1(cs, "HX Frame %d", count);
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "JADE %s receive out of memory\n", (jade ? "B":"A"));
+				else {
+					memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+					skb_queue_tail(&bcs->rqueue, skb);
+				}
+			}
+		}
+		bcs->hw.hscx.rcvidx = 0;
+		schedule_event(bcs, B_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		jade_empty_fifo(bcs, fifo_size);
+		if (bcs->mode == L1_MODE_TRANS) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(fifo_size)))
+				printk(KERN_WARNING "HiSax: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size);
+				skb_queue_tail(&bcs->rqueue, skb);
+			}
+			bcs->hw.hscx.rcvidx = 0;
+			schedule_event(bcs, B_RCVBUFREADY);
+		}
+	}
+	if (val & 0x10) {	/* XPR */
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				jade_fill_fifo(bcs);
+				return;
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->hw.hscx.count;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+				dev_kfree_skb_irq(bcs->tx_skb);
+				bcs->hw.hscx.count = 0;
+				bcs->tx_skb = NULL;
+			}
+		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hscx.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			jade_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+static inline void
+jade_int_main(struct IsdnCardState *cs, u_char val, int jade)
+{
+	struct BCState *bcs;
+	bcs = cs->bcs + jade;
+	
+	if (val & jadeISR_RFO) {
+		/* handled with RDO */
+		val &= ~jadeISR_RFO;
+	}
+	if (val & jadeISR_XDU) {
+		/* relevant in HDLC mode only */
+		/* don't reset XPR here */
+		if (bcs->mode == 1)
+			jade_fill_fifo(bcs);
+		else {
+			/* Here we lost an TX interrupt, so
+			   * restart transmitting the whole frame.
+			 */
+			if (bcs->tx_skb) {
+			   	skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+				bcs->tx_cnt += bcs->hw.hscx.count;
+				bcs->hw.hscx.count = 0;
+			}
+			WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_XCMD, jadeXCMD_XRES);
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "JADE %c EXIR %x Lost TX", 'A'+jade, val);
+		}
+	}
+	if (val & (jadeISR_RME|jadeISR_RPF|jadeISR_XPR)) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "JADE %c interrupt %x", 'A'+jade, val);
+		jade_interrupt(cs, val, jade);
+	}
+}
diff --git a/drivers/isdn/hisax/l3_1tr6.c b/drivers/isdn/hisax/l3_1tr6.c
new file mode 100644
index 000000000000..d6c1c8f8329d
--- /dev/null
+++ b/drivers/isdn/hisax/l3_1tr6.c
@@ -0,0 +1,955 @@
+/* $Id: l3_1tr6.c,v 2.15.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * German 1TR6 D-channel protocol
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ */
+
+#include "hisax.h"
+#include "l3_1tr6.h"
+#include "isdnl3.h"
+#include <linux/ctype.h>
+
+extern char *HiSax_getrev(const char *revision);
+const char *l3_1tr6_revision = "$Revision: 2.15.2.3 $";
+
+#define MsgHead(ptr, cref, mty, dis) \
+	*ptr++ = dis; \
+	*ptr++ = 0x1; \
+	*ptr++ = cref ^ 0x80; \
+	*ptr++ = mty
+
+static void
+l3_1TR6_message(struct l3_process *pc, u_char mt, u_char pd)
+{
+	struct sk_buff *skb;
+	u_char *p;
+
+	if (!(skb = l3_alloc_skb(4)))
+		return;
+	p = skb_put(skb, 4);
+	MsgHead(p, pc->callref, mt, pd);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3_1tr6_release_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	StopAllL3Timer(pc);
+	newl3state(pc, 19);
+	l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3_1tr6_invalid(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	dev_kfree_skb(skb);
+	l3_1tr6_release_req(pc, 0, NULL);
+}
+
+static void
+l3_1tr6_error(struct l3_process *pc, u_char *msg, struct sk_buff *skb)
+{
+	dev_kfree_skb(skb);
+	if (pc->st->l3.debug & L3_DEB_WARN)
+		l3_debug(pc->st, msg);
+	l3_1tr6_release_req(pc, 0, NULL);
+}
+
+static void
+l3_1tr6_setup_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+	u_char *teln;
+	u_char *eaz;
+	u_char channel = 0;
+	int l;
+
+	MsgHead(p, pc->callref, MT_N1_SETUP, PROTO_DIS_N1);
+	teln = pc->para.setup.phone;
+	pc->para.spv = 0;
+	if (!isdigit(*teln)) {
+		switch (0x5f & *teln) {
+			case 'S':
+				pc->para.spv = 1;
+				break;
+			case 'C':
+				channel = 0x08;
+			case 'P':
+				channel |= 0x80;
+				teln++;
+				if (*teln == '1')
+					channel |= 0x01;
+				else
+					channel |= 0x02;
+				break;
+			default:
+				if (pc->st->l3.debug & L3_DEB_WARN)
+					l3_debug(pc->st, "Wrong MSN Code");
+				break;
+		}
+		teln++;
+	}
+	if (channel) {
+		*p++ = 0x18;	/* channel indicator */
+		*p++ = 1;
+		*p++ = channel;
+	}
+	if (pc->para.spv) {	/* SPV ? */
+		/* NSF SPV */
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_SPV;	/* SPV */
+		*p++ = pc->para.setup.si1;	/* 0 for all Services */
+		*p++ = pc->para.setup.si2;	/* 0 for all Services */
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_Activate;	/* aktiviere SPV (default) */
+		*p++ = pc->para.setup.si1;	/* 0 for all Services */
+		*p++ = pc->para.setup.si2;	/* 0 for all Services */
+	}
+	eaz = pc->para.setup.eazmsn;
+	if (*eaz) {
+		*p++ = WE0_origAddr;
+		*p++ = strlen(eaz) + 1;
+		/* Classify as AnyPref. */
+		*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+		while (*eaz)
+			*p++ = *eaz++ & 0x7f;
+	}
+	*p++ = WE0_destAddr;
+	*p++ = strlen(teln) + 1;
+	/* Classify as AnyPref. */
+	*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+	while (*teln)
+		*p++ = *teln++ & 0x7f;
+
+	*p++ = WE_Shift_F6;
+	/* Codesatz 6 fuer Service */
+	*p++ = WE6_serviceInd;
+	*p++ = 2;		/* len=2 info,info2 */
+	*p++ = pc->para.setup.si1;
+	*p++ = pc->para.setup.si2;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T303, CC_T303);
+	newl3state(pc, 1);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3_1tr6_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	int bcfound = 0;
+	char tmp[80];
+	struct sk_buff *skb = arg;
+
+	p = skb->data;
+
+	/* Channel Identification */
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+		if (p[1] != 1) {
+			l3_1tr6_error(pc, "setup wrong chanID len", skb);
+			return;
+		}
+		if ((p[2] & 0xf4) != 0x80) {
+			l3_1tr6_error(pc, "setup wrong WE0_chanID", skb);
+			return;
+		}
+		if ((pc->para.bchannel = p[2] & 0x3))
+				bcfound++;
+	} else {
+		l3_1tr6_error(pc, "missing setup chanID", skb);
+		return;
+	}
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE6_serviceInd, 6))) {
+		pc->para.setup.si1 = p[2];
+		pc->para.setup.si2 = p[3];
+	} else {
+		l3_1tr6_error(pc, "missing setup SI", skb);
+		return;
+	}
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_destAddr, 0)))
+		iecpy(pc->para.setup.eazmsn, p, 1);
+	else
+		pc->para.setup.eazmsn[0] = 0;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_origAddr, 0))) {
+		iecpy(pc->para.setup.phone, p, 1);
+	} else
+		pc->para.setup.phone[0] = 0;
+
+	p = skb->data;
+	pc->para.spv = 0;
+	if ((p = findie(p, skb->len, WE0_netSpecFac, 0))) {
+		if ((FAC_SPV == p[3]) || (FAC_Activate == p[3]))
+			pc->para.spv = 1;
+	}
+	dev_kfree_skb(skb);
+
+	/* Signal all services, linklevel takes care of Service-Indicator */
+	if (bcfound) {
+		if ((pc->para.setup.si1 != 7) && (pc->st->l3.debug & L3_DEB_WARN)) {
+			sprintf(tmp, "non-digital call: %s -> %s",
+				pc->para.setup.phone,
+				pc->para.setup.eazmsn);
+			l3_debug(pc->st, tmp);
+		}
+		newl3state(pc, 6);
+		pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+	} else
+		release_l3_process(pc);
+}
+
+static void
+l3_1tr6_setup_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	newl3state(pc, 2);
+	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+		if (p[1] != 1) {
+			l3_1tr6_error(pc, "setup_ack wrong chanID len", skb);
+			return;
+		}
+		if ((p[2] & 0xf4) != 0x80) {
+			l3_1tr6_error(pc, "setup_ack wrong WE0_chanID", skb);
+			return;
+		}
+		pc->para.bchannel = p[2] & 0x3;
+	} else {
+		l3_1tr6_error(pc, "missing setup_ack WE0_chanID", skb);
+		return;
+	}
+	dev_kfree_skb(skb);
+	L3AddTimer(&pc->timer, T304, CC_T304);
+	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+}
+
+static void
+l3_1tr6_call_sent(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+		if (p[1] != 1) {
+			l3_1tr6_error(pc, "call sent wrong chanID len", skb);
+			return;
+		}
+		if ((p[2] & 0xf4) != 0x80) {
+			l3_1tr6_error(pc, "call sent wrong WE0_chanID", skb);
+			return;
+		}
+		if ((pc->state == 2) && (pc->para.bchannel != (p[2] & 0x3))) {
+			l3_1tr6_error(pc, "call sent wrong chanID value", skb);
+			return;
+		}
+		pc->para.bchannel = p[2] & 0x3;
+	} else {
+		l3_1tr6_error(pc, "missing call sent WE0_chanID", skb);
+		return;
+	}
+	dev_kfree_skb(skb);
+	L3AddTimer(&pc->timer, T310, CC_T310);
+	newl3state(pc, 3);
+	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+}
+
+static void
+l3_1tr6_alert(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	dev_kfree_skb(skb);
+	L3DelTimer(&pc->timer);	/* T304 */
+	newl3state(pc, 4);
+	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+}
+
+static void
+l3_1tr6_info(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	int i, tmpcharge = 0;
+	char a_charge[8], tmp[32];
+	struct sk_buff *skb = arg;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) {
+		iecpy(a_charge, p, 1);
+		for (i = 0; i < strlen(a_charge); i++) {
+			tmpcharge *= 10;
+			tmpcharge += a_charge[i] & 0xf;
+		}
+		if (tmpcharge > pc->para.chargeinfo) {
+			pc->para.chargeinfo = tmpcharge;
+			pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+		}
+		if (pc->st->l3.debug & L3_DEB_CHARGE) {
+			sprintf(tmp, "charging info %d", pc->para.chargeinfo);
+			l3_debug(pc->st, tmp);
+		}
+	} else if (pc->st->l3.debug & L3_DEB_CHARGE)
+		l3_debug(pc->st, "charging info not found");
+	dev_kfree_skb(skb);
+
+}
+
+static void
+l3_1tr6_info_s2(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	dev_kfree_skb(skb);
+}
+
+static void
+l3_1tr6_connect(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&pc->timer);	/* T310 */
+	if (!findie(skb->data, skb->len, WE6_date, 6)) {
+		l3_1tr6_error(pc, "missing connect date", skb);
+		return;
+	}
+	newl3state(pc, 10);
+	dev_kfree_skb(skb);
+	pc->para.chargeinfo = 0;
+	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+}
+
+static void
+l3_1tr6_rel(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_cause, 0))) {
+		if (p[1] > 0) {
+			pc->para.cause = p[2];
+			if (p[1] > 1)
+				pc->para.loc = p[3];
+			else
+				pc->para.loc = 0;
+		} else {
+			pc->para.cause = 0;
+			pc->para.loc = 0;
+		}
+	} else {
+		pc->para.cause = NO_CAUSE;
+		l3_1tr6_error(pc, "missing REL cause", skb);
+		return;
+	}
+	dev_kfree_skb(skb);
+	StopAllL3Timer(pc);
+	newl3state(pc, 0);
+	l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	release_l3_process(pc);
+}
+
+static void
+l3_1tr6_rel_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	dev_kfree_skb(skb);
+	StopAllL3Timer(pc);
+	newl3state(pc, 0);
+	pc->para.cause = NO_CAUSE;
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+	release_l3_process(pc);
+}
+
+static void
+l3_1tr6_disc(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+	int i, tmpcharge = 0;
+	char a_charge[8], tmp[32];
+
+	StopAllL3Timer(pc);
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) {
+		iecpy(a_charge, p, 1);
+		for (i = 0; i < strlen(a_charge); i++) {
+			tmpcharge *= 10;
+			tmpcharge += a_charge[i] & 0xf;
+		}
+		if (tmpcharge > pc->para.chargeinfo) {
+			pc->para.chargeinfo = tmpcharge;
+			pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+		}
+		if (pc->st->l3.debug & L3_DEB_CHARGE) {
+			sprintf(tmp, "charging info %d", pc->para.chargeinfo);
+			l3_debug(pc->st, tmp);
+		}
+	} else if (pc->st->l3.debug & L3_DEB_CHARGE)
+		l3_debug(pc->st, "charging info not found");
+
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_cause, 0))) {
+		if (p[1] > 0) {
+			pc->para.cause = p[2];
+			if (p[1] > 1)
+				pc->para.loc = p[3];
+			else
+				pc->para.loc = 0;
+		} else {
+			pc->para.cause = 0;
+			pc->para.loc = 0;
+		}
+	} else {
+		if (pc->st->l3.debug & L3_DEB_WARN)
+			l3_debug(pc->st, "cause not found");
+		pc->para.cause = NO_CAUSE;
+	}
+	if (!findie(skb->data, skb->len, WE6_date, 6)) {
+		l3_1tr6_error(pc, "missing connack date", skb);
+		return;
+	}
+	dev_kfree_skb(skb);
+	newl3state(pc, 12);
+	pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+}
+
+
+static void
+l3_1tr6_connect_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	if (!findie(skb->data, skb->len, WE6_date, 6)) {
+		l3_1tr6_error(pc, "missing connack date", skb);
+		return;
+	}
+	dev_kfree_skb(skb);
+	newl3state(pc, 10);
+	pc->para.chargeinfo = 0;
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+}
+
+static void
+l3_1tr6_alert_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	newl3state(pc, 7);
+	l3_1TR6_message(pc, MT_N1_ALERT, PROTO_DIS_N1);
+}
+
+static void
+l3_1tr6_setup_rsp(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[24];
+	u_char *p = tmp;
+	int l;
+
+	MsgHead(p, pc->callref, MT_N1_CONN, PROTO_DIS_N1);
+	if (pc->para.spv) {	/* SPV ? */
+		/* NSF SPV */
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_SPV;	/* SPV */
+		*p++ = pc->para.setup.si1;
+		*p++ = pc->para.setup.si2;
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_Activate;	/* aktiviere SPV */
+		*p++ = pc->para.setup.si1;
+		*p++ = pc->para.setup.si2;
+	}
+	newl3state(pc, 8);
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T313, CC_T313);
+}
+
+static void
+l3_1tr6_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+	release_l3_process(pc);
+}
+
+static void
+l3_1tr6_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 0x10;
+	u_char clen = 1;
+
+	if (pc->para.cause > 0)
+		cause = pc->para.cause;
+	/* Map DSS1 causes */
+	switch (cause & 0x7f) {
+		case 0x10:
+			clen = 0;
+			break;
+                case 0x11:
+                        cause = CAUSE_UserBusy;
+                        break;
+		case 0x15:
+			cause = CAUSE_CallRejected;
+			break;
+	}
+	StopAllL3Timer(pc);
+	MsgHead(p, pc->callref, MT_N1_DISC, PROTO_DIS_N1);
+	*p++ = WE0_cause;
+	*p++ = clen;		/* Laenge */
+	if (clen)
+		*p++ = cause | 0x80;
+	newl3state(pc, 11);
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T305, CC_T305);
+}
+
+static void
+l3_1tr6_t303(struct l3_process *pc, u_char pr, void *arg)
+{
+	if (pc->N303 > 0) {
+		pc->N303--;
+		L3DelTimer(&pc->timer);
+		l3_1tr6_setup_req(pc, pr, arg);
+	} else {
+		L3DelTimer(&pc->timer);
+		pc->para.cause = 0;
+		l3_1tr6_disconnect_req(pc, 0, NULL);
+	}
+}
+
+static void
+l3_1tr6_t304(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3_1tr6_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3_1tr6_t305(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 0x90;
+	u_char clen = 1;
+
+	L3DelTimer(&pc->timer);
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+	/* Map DSS1 causes */
+	switch (cause & 0x7f) {
+		case 0x10:
+			clen = 0;
+			break;
+		case 0x15:
+			cause = CAUSE_CallRejected;
+			break;
+	}
+	MsgHead(p, pc->callref, MT_N1_REL, PROTO_DIS_N1);
+	*p++ = WE0_cause;
+	*p++ = clen;		/* Laenge */
+	if (clen)
+		*p++ = cause;
+	newl3state(pc, 19);
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3_1tr6_t310(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3_1tr6_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3_1tr6_t313(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3_1tr6_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+}
+
+static void
+l3_1tr6_t308_1(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
+	L3AddTimer(&pc->timer, T308, CC_T308_2);
+	newl3state(pc, 19);
+}
+
+static void
+l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+	release_l3_process(pc);
+}
+
+static void
+l3_1tr6_dl_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+        pc->para.cause = CAUSE_LocalProcErr;
+        l3_1tr6_disconnect_req(pc, pr, NULL);
+        pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3_1tr6_dl_release(struct l3_process *pc, u_char pr, void *arg)
+{
+        newl3state(pc, 0);
+        pc->para.cause = 0x1b;          /* Destination out of order */
+        pc->para.loc = 0;
+        pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+        release_l3_process(pc);
+}
+
+/* *INDENT-OFF* */
+static struct stateentry downstl[] =
+{
+	{SBIT(0),
+	 CC_SETUP | REQUEST, l3_1tr6_setup_req},
+   	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) |
+    	 SBIT(10),
+    	 CC_DISCONNECT | REQUEST, l3_1tr6_disconnect_req},
+	{SBIT(12),
+	 CC_RELEASE | REQUEST, l3_1tr6_release_req},
+	{SBIT(6),
+	 CC_IGNORE | REQUEST, l3_1tr6_reset},
+	{SBIT(6),
+	 CC_REJECT | REQUEST, l3_1tr6_disconnect_req},
+	{SBIT(6),
+	 CC_ALERTING | REQUEST, l3_1tr6_alert_req},
+	{SBIT(6) | SBIT(7),
+	 CC_SETUP | RESPONSE, l3_1tr6_setup_rsp},
+	{SBIT(1),
+	 CC_T303, l3_1tr6_t303},
+	{SBIT(2),
+	 CC_T304, l3_1tr6_t304},
+	{SBIT(3),
+	 CC_T310, l3_1tr6_t310},
+	{SBIT(8),
+	 CC_T313, l3_1tr6_t313},
+	{SBIT(11),
+	 CC_T305, l3_1tr6_t305},
+	{SBIT(19),
+	 CC_T308_1, l3_1tr6_t308_1},
+	{SBIT(19),
+	 CC_T308_2, l3_1tr6_t308_2},
+};
+
+#define DOWNSTL_LEN \
+	(sizeof(downstl) / sizeof(struct stateentry))
+
+static struct stateentry datastln1[] =
+{
+	{SBIT(0),
+	 MT_N1_INVALID, l3_1tr6_invalid},
+	{SBIT(0),
+	 MT_N1_SETUP, l3_1tr6_setup},
+	{SBIT(1),
+	 MT_N1_SETUP_ACK, l3_1tr6_setup_ack},
+	{SBIT(1) | SBIT(2),
+	 MT_N1_CALL_SENT, l3_1tr6_call_sent},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10),
+	 MT_N1_DISC, l3_1tr6_disc},
+	{SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_N1_ALERT, l3_1tr6_alert},
+	{SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_N1_CONN, l3_1tr6_connect},
+	{SBIT(2),
+	 MT_N1_INFO, l3_1tr6_info_s2},
+	{SBIT(8),
+	 MT_N1_CONN_ACK, l3_1tr6_connect_ack},
+	{SBIT(10),
+	 MT_N1_INFO, l3_1tr6_info},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) |
+	 SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17),
+	 MT_N1_REL, l3_1tr6_rel},
+	{SBIT(19),
+	 MT_N1_REL, l3_1tr6_rel_ack},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) |
+	 SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17),
+	 MT_N1_REL_ACK, l3_1tr6_invalid},
+	{SBIT(19),
+	 MT_N1_REL_ACK, l3_1tr6_rel_ack}
+};
+
+#define DATASTLN1_LEN \
+	(sizeof(datastln1) / sizeof(struct stateentry))
+
+static struct stateentry manstatelist[] =
+{
+        {SBIT(2),
+         DL_ESTABLISH | INDICATION, l3_1tr6_dl_reset},
+        {ALL_STATES,
+         DL_RELEASE | INDICATION, l3_1tr6_dl_release},
+};
+ 
+#define MANSLLEN \
+        (sizeof(manstatelist) / sizeof(struct stateentry))
+/* *INDENT-ON* */
+
+static void
+up1tr6(struct PStack *st, int pr, void *arg)
+{
+	int i, mt, cr;
+	struct l3_process *proc;
+	struct sk_buff *skb = arg;
+	char tmp[80];
+
+	switch (pr) {
+		case (DL_DATA | INDICATION):
+		case (DL_UNIT_DATA | INDICATION):
+			break;
+		case (DL_ESTABLISH | CONFIRM):
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			l3_msg(st, pr, arg);
+			return;
+			break;
+	}
+	if (skb->len < 4) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "up1tr6 len only %d", skb->len);
+			l3_debug(st, tmp);
+		}
+		dev_kfree_skb(skb);
+		return;
+	}
+	if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				skb->data[0], skb->len);
+			l3_debug(st, tmp);
+		}
+		dev_kfree_skb(skb);
+		return;
+	}
+	if (skb->data[1] != 1) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "up1tr6 CR len not 1");
+			l3_debug(st, tmp);
+		}
+		dev_kfree_skb(skb);
+		return;
+	}
+	cr = skb->data[2];
+	mt = skb->data[3];
+	if (skb->data[0] == PROTO_DIS_N0) {
+		dev_kfree_skb(skb);
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "up1tr6%s N0 mt %x unhandled",
+			     (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", mt);
+			l3_debug(st, tmp);
+		}
+	} else if (skb->data[0] == PROTO_DIS_N1) {
+		if (!(proc = getl3proc(st, cr))) {
+			if (mt == MT_N1_SETUP) { 
+				if (cr < 128) {
+					if (!(proc = new_l3_process(st, cr))) {
+						if (st->l3.debug & L3_DEB_PROTERR) {
+							sprintf(tmp, "up1tr6 no roc mem");
+							l3_debug(st, tmp);
+						}
+						dev_kfree_skb(skb);
+						return;
+					}
+				} else {
+					dev_kfree_skb(skb);
+					return;
+				}
+			} else if ((mt == MT_N1_REL) || (mt == MT_N1_REL_ACK) ||
+				(mt == MT_N1_CANC_ACK) || (mt == MT_N1_CANC_REJ) ||
+				(mt == MT_N1_REG_ACK) || (mt == MT_N1_REG_REJ) ||
+				(mt == MT_N1_SUSP_ACK) || (mt == MT_N1_RES_REJ) ||
+				(mt == MT_N1_INFO)) {
+				dev_kfree_skb(skb);
+				return;
+			} else {
+				if (!(proc = new_l3_process(st, cr))) {
+					if (st->l3.debug & L3_DEB_PROTERR) {
+						sprintf(tmp, "up1tr6 no roc mem");
+						l3_debug(st, tmp);
+					}
+					dev_kfree_skb(skb);
+					return;
+				}
+				mt = MT_N1_INVALID;
+			}
+		}
+		for (i = 0; i < DATASTLN1_LEN; i++)
+			if ((mt == datastln1[i].primitive) &&
+			    ((1 << proc->state) & datastln1[i].state))
+				break;
+		if (i == DATASTLN1_LEN) {
+			dev_kfree_skb(skb);
+			if (st->l3.debug & L3_DEB_STATE) {
+				sprintf(tmp, "up1tr6%sstate %d mt %x unhandled",
+				  (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+					proc->state, mt);
+				l3_debug(st, tmp);
+			}
+			return;
+		} else {
+			if (st->l3.debug & L3_DEB_STATE) {
+				sprintf(tmp, "up1tr6%sstate %d mt %x",
+				  (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+					proc->state, mt);
+				l3_debug(st, tmp);
+			}
+			datastln1[i].rout(proc, pr, skb);
+		}
+	}
+}
+
+static void
+down1tr6(struct PStack *st, int pr, void *arg)
+{
+	int i, cr;
+	struct l3_process *proc;
+	struct Channel *chan;
+	char tmp[80];
+
+	if ((DL_ESTABLISH | REQUEST)== pr) {
+		l3_msg(st, pr, NULL);
+		return;
+	} else if ((CC_SETUP | REQUEST) == pr) {
+		chan = arg;
+		cr = newcallref();
+		cr |= 0x80;
+		if (!(proc = new_l3_process(st, cr))) {
+			return;
+		} else {
+			proc->chan = chan;
+			chan->proc = proc;
+			memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm));
+			proc->callref = cr;
+		}
+	} else {
+		proc = arg;
+	}
+
+	for (i = 0; i < DOWNSTL_LEN; i++)
+		if ((pr == downstl[i].primitive) &&
+		    ((1 << proc->state) & downstl[i].state))
+			break;
+	if (i == DOWNSTL_LEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "down1tr6 state %d prim %d unhandled",
+				proc->state, pr);
+			l3_debug(st, tmp);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "down1tr6 state %d prim %d",
+				proc->state, pr);
+			l3_debug(st, tmp);
+		}
+		downstl[i].rout(proc, pr, arg);
+	}
+}
+
+static void
+man1tr6(struct PStack *st, int pr, void *arg)
+{
+        int i;
+        struct l3_process *proc = arg;
+ 
+        if (!proc) {
+                printk(KERN_ERR "HiSax man1tr6 without proc pr=%04x\n", pr);
+                return;
+        }
+        for (i = 0; i < MANSLLEN; i++)
+                if ((pr == manstatelist[i].primitive) &&
+                    ((1 << proc->state) & manstatelist[i].state))
+                        break;
+        if (i == MANSLLEN) {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d man1tr6 state %d prim %d unhandled",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+        } else {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d man1tr6 state %d prim %d",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+                manstatelist[i].rout(proc, pr, arg);
+        }
+}
+ 
+void
+setstack_1tr6(struct PStack *st)
+{
+	char tmp[64];
+
+	st->lli.l4l3 = down1tr6;
+	st->l2.l2l3 = up1tr6;
+	st->l3.l3ml3 = man1tr6;
+	st->l3.N303 = 0;
+
+	strcpy(tmp, l3_1tr6_revision);
+	printk(KERN_INFO "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp));
+}
diff --git a/drivers/isdn/hisax/l3_1tr6.h b/drivers/isdn/hisax/l3_1tr6.h
new file mode 100644
index 000000000000..43215c00cada
--- /dev/null
+++ b/drivers/isdn/hisax/l3_1tr6.h
@@ -0,0 +1,164 @@
+/* $Id: l3_1tr6.h,v 2.2.6.2 2001/09/23 22:24:49 kai Exp $
+ *
+ * German 1TR6 D-channel protocol defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef l3_1tr6
+#define l3_1tr6
+
+#define PROTO_DIS_N0 0x40
+#define PROTO_DIS_N1 0x41
+
+/*
+ * MsgType N0
+ */
+#define MT_N0_REG_IND 0x61
+#define MT_N0_CANC_IND 0x62
+#define MT_N0_FAC_STA 0x63
+#define MT_N0_STA_ACK 0x64
+#define MT_N0_STA_REJ 0x65
+#define MT_N0_FAC_INF 0x66
+#define MT_N0_INF_ACK 0x67
+#define MT_N0_INF_REJ 0x68
+#define MT_N0_CLOSE   0x75
+#define MT_N0_CLO_ACK 0x77
+
+/*
+ * MsgType N1
+ */
+
+#define MT_N1_ESC 0x00
+#define MT_N1_ALERT 0x01
+#define MT_N1_CALL_SENT 0x02
+#define MT_N1_CONN 0x07
+#define MT_N1_CONN_ACK 0x0F
+#define MT_N1_SETUP 0x05
+#define MT_N1_SETUP_ACK 0x0D
+#define MT_N1_RES 0x26
+#define MT_N1_RES_ACK 0x2E
+#define MT_N1_RES_REJ 0x22
+#define MT_N1_SUSP 0x25
+#define MT_N1_SUSP_ACK 0x2D
+#define MT_N1_SUSP_REJ 0x21
+#define MT_N1_USER_INFO 0x20
+#define MT_N1_DET 0x40
+#define MT_N1_DISC 0x45
+#define MT_N1_REL 0x4D
+#define MT_N1_REL_ACK 0x5A
+#define MT_N1_CANC_ACK 0x6E
+#define MT_N1_CANC_REJ 0x67
+#define MT_N1_CON_CON 0x69
+#define MT_N1_FAC 0x60
+#define MT_N1_FAC_ACK 0x68
+#define MT_N1_FAC_CAN 0x66
+#define MT_N1_FAC_REG 0x64
+#define MT_N1_FAC_REJ 0x65
+#define MT_N1_INFO 0x6D
+#define MT_N1_REG_ACK 0x6C
+#define MT_N1_REG_REJ 0x6F
+#define MT_N1_STAT 0x63
+#define MT_N1_INVALID 0
+
+/*
+ * W Elemente
+ */
+
+#define WE_Shift_F0 0x90
+#define WE_Shift_F6 0x96
+#define WE_Shift_OF0 0x98
+#define WE_Shift_OF6 0x9E
+
+#define WE0_cause 0x08
+#define WE0_connAddr 0x0C
+#define WE0_callID 0x10
+#define WE0_chanID 0x18
+#define WE0_netSpecFac 0x20
+#define WE0_display 0x28
+#define WE0_keypad 0x2C
+#define WE0_origAddr 0x6C
+#define WE0_destAddr 0x70
+#define WE0_userInfo 0x7E
+
+#define WE0_moreData 0xA0
+#define WE0_congestLevel 0xB0
+
+#define WE6_serviceInd 0x01
+#define WE6_chargingInfo 0x02
+#define WE6_date 0x03
+#define WE6_facSelect 0x05
+#define WE6_facStatus 0x06
+#define WE6_statusCalled 0x07
+#define WE6_addTransAttr 0x08
+
+/*
+ * FacCodes
+ */
+#define FAC_Sperre 0x01
+#define FAC_Sperre_All 0x02
+#define FAC_Sperre_Fern 0x03
+#define FAC_Sperre_Intl 0x04
+#define FAC_Sperre_Interk 0x05
+
+#define FAC_Forward1 0x02
+#define FAC_Forward2 0x03
+#define FAC_Konferenz 0x06
+#define FAC_GrabBchan 0x0F
+#define FAC_Reactivate 0x10
+#define FAC_Konferenz3 0x11
+#define FAC_Dienstwechsel1 0x12
+#define FAC_Dienstwechsel2 0x13
+#define FAC_NummernIdent 0x14
+#define FAC_GBG 0x15
+#define FAC_DisplayUebergeben 0x17
+#define FAC_DisplayUmgeleitet 0x1A
+#define FAC_Unterdruecke 0x1B
+#define FAC_Deactivate 0x1E
+#define FAC_Activate 0x1D
+#define FAC_SPV 0x1F
+#define FAC_Rueckwechsel 0x23
+#define FAC_Umleitung 0x24
+
+/*
+ * Cause codes
+ */
+#define CAUSE_InvCRef 0x01
+#define CAUSE_BearerNotImpl 0x03
+#define CAUSE_CIDunknown 0x07
+#define CAUSE_CIDinUse 0x08
+#define CAUSE_NoChans 0x0A
+#define CAUSE_FacNotImpl 0x10
+#define CAUSE_FacNotSubscr 0x11
+#define CAUSE_OutgoingBarred 0x20
+#define CAUSE_UserAccessBusy 0x21
+#define CAUSE_NegativeGBG 0x22
+#define CAUSE_UnknownGBG 0x23
+#define CAUSE_NoSPVknown 0x25
+#define CAUSE_DestNotObtain 0x35
+#define CAUSE_NumberChanged 0x38
+#define CAUSE_OutOfOrder 0x39
+#define CAUSE_NoUserResponse 0x3A
+#define CAUSE_UserBusy 0x3B
+#define CAUSE_IncomingBarred 0x3D
+#define CAUSE_CallRejected 0x3E
+#define CAUSE_NetworkCongestion 0x59
+#define CAUSE_RemoteUser 0x5A
+#define CAUSE_LocalProcErr 0x70
+#define CAUSE_RemoteProcErr 0x71
+#define CAUSE_RemoteUserSuspend 0x72
+#define CAUSE_RemoteUserResumed 0x73
+#define CAUSE_UserInfoDiscarded 0x7F
+
+#define T303	4000
+#define T304	20000
+#define T305	4000
+#define T308	4000
+#define T310	120000
+#define T313	4000
+#define T318	4000
+#define T319	4000
+
+#endif
diff --git a/drivers/isdn/hisax/l3dss1.c b/drivers/isdn/hisax/l3dss1.c
new file mode 100644
index 000000000000..ec92308c1efc
--- /dev/null
+++ b/drivers/isdn/hisax/l3dss1.c
@@ -0,0 +1,3238 @@
+/* $Id: l3dss1.c,v 2.32.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * EURO/DSS1 D-channel protocol
+ *
+ * German 1TR6 D-channel protocol
+ *
+ * Author       Karsten Keil
+ *              based on the teles driver from Jan den Ouden
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include "hisax.h"
+#include "isdnl3.h"
+#include "l3dss1.h"
+#include <linux/ctype.h>
+#include <linux/config.h>
+
+extern char *HiSax_getrev(const char *revision);
+const char *dss1_revision = "$Revision: 2.32.2.3 $";
+
+#define EXT_BEARER_CAPS 1
+
+#define	MsgHead(ptr, cref, mty) \
+	*ptr++ = 0x8; \
+	if (cref == -1) { \
+		*ptr++ = 0x0; \
+	} else { \
+		*ptr++ = 0x1; \
+		*ptr++ = cref^0x80; \
+	} \
+	*ptr++ = mty
+
+
+/**********************************************/
+/* get a new invoke id for remote operations. */
+/* Only a return value != 0 is valid          */
+/**********************************************/
+static unsigned char new_invoke_id(struct PStack *p)
+{
+	unsigned char retval;
+	int i;
+  
+	i = 32; /* maximum search depth */
+
+	retval = p->prot.dss1.last_invoke_id + 1; /* try new id */
+	while ((i) && (p->prot.dss1.invoke_used[retval >> 3] == 0xFF)) {
+		p->prot.dss1.last_invoke_id = (retval & 0xF8) + 8;
+		i--;
+	}  
+	if (i) {
+		while (p->prot.dss1.invoke_used[retval >> 3] & (1 << (retval & 7)))
+		retval++; 
+	} else
+		retval = 0;
+	p->prot.dss1.last_invoke_id = retval;
+	p->prot.dss1.invoke_used[retval >> 3] |= (1 << (retval & 7));
+	return(retval);  
+} /* new_invoke_id */
+
+/*************************/
+/* free a used invoke id */
+/*************************/
+static void free_invoke_id(struct PStack *p, unsigned char id)
+{
+
+  if (!id) return; /* 0 = invalid value */
+
+  p->prot.dss1.invoke_used[id >> 3] &= ~(1 << (id & 7));
+} /* free_invoke_id */  
+
+
+/**********************************************************/
+/* create a new l3 process and fill in dss1 specific data */
+/**********************************************************/
+static struct l3_process
+*dss1_new_l3_process(struct PStack *st, int cr)
+{  struct l3_process *proc;
+
+   if (!(proc = new_l3_process(st, cr))) 
+     return(NULL);
+
+   proc->prot.dss1.invoke_id = 0;
+   proc->prot.dss1.remote_operation = 0;
+   proc->prot.dss1.uus1_data[0] = '\0';
+   
+   return(proc);
+} /* dss1_new_l3_process */
+
+/************************************************/
+/* free a l3 process and all dss1 specific data */
+/************************************************/ 
+static void
+dss1_release_l3_process(struct l3_process *p)
+{
+   free_invoke_id(p->st,p->prot.dss1.invoke_id);
+   release_l3_process(p);
+} /* dss1_release_l3_process */
+ 
+/********************************************************/
+/* search a process with invoke id id and dummy callref */
+/********************************************************/
+static struct l3_process *
+l3dss1_search_dummy_proc(struct PStack *st, int id)
+{ struct l3_process *pc = st->l3.proc; /* start of processes */
+
+  if (!id) return(NULL);
+
+  while (pc)
+   { if ((pc->callref == -1) && (pc->prot.dss1.invoke_id == id))
+       return(pc);
+     pc = pc->next;
+   } 
+  return(NULL);
+} /* l3dss1_search_dummy_proc */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return result is delivered. id specifies the invoke id.   */
+/*******************************************************************/ 
+static void 
+l3dss1_dummy_return_result(struct PStack *st, int id, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+  struct l3_process *pc = NULL; 
+
+  if ((pc = l3dss1_search_dummy_proc(st, id)))
+   { L3DelTimer(&pc->timer); /* remove timer */
+
+     cs = pc->st->l1.hardware;
+     ic.driver = cs->myid;
+     ic.command = ISDN_STAT_PROT;
+     ic.arg = DSS1_STAT_INVOKE_RES;
+     ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id;
+     ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id;
+     ic.parm.dss1_io.proc = pc->prot.dss1.proc;
+     ic.parm.dss1_io.timeout= 0;
+     ic.parm.dss1_io.datalen = nlen;
+     ic.parm.dss1_io.data = p;
+     free_invoke_id(pc->st, pc->prot.dss1.invoke_id);
+     pc->prot.dss1.invoke_id = 0; /* reset id */
+
+     cs->iif.statcallb(&ic);
+     dss1_release_l3_process(pc); 
+   }
+  else
+   l3_debug(st, "dummy return result id=0x%x result len=%d",id,nlen);
+} /* l3dss1_dummy_return_result */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return error is delivered. id specifies the invoke id.    */
+/*******************************************************************/ 
+static void 
+l3dss1_dummy_error_return(struct PStack *st, int id, ulong error)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+  struct l3_process *pc = NULL; 
+
+  if ((pc = l3dss1_search_dummy_proc(st, id)))
+   { L3DelTimer(&pc->timer); /* remove timer */
+
+     cs = pc->st->l1.hardware;
+     ic.driver = cs->myid;
+     ic.command = ISDN_STAT_PROT;
+     ic.arg = DSS1_STAT_INVOKE_ERR;
+     ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id;
+     ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id;
+     ic.parm.dss1_io.proc = pc->prot.dss1.proc;
+     ic.parm.dss1_io.timeout= error;
+     ic.parm.dss1_io.datalen = 0;
+     ic.parm.dss1_io.data = NULL;
+     free_invoke_id(pc->st, pc->prot.dss1.invoke_id);
+     pc->prot.dss1.invoke_id = 0; /* reset id */
+
+     cs->iif.statcallb(&ic);
+     dss1_release_l3_process(pc); 
+   }
+  else
+   l3_debug(st, "dummy return error id=0x%x error=0x%lx",id,error);
+} /* l3dss1_error_return */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a invoke is delivered. id specifies the invoke id.          */
+/*******************************************************************/ 
+static void 
+l3dss1_dummy_invoke(struct PStack *st, int cr, int id, 
+                    int ident, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+  
+  l3_debug(st, "dummy invoke %s id=0x%x ident=0x%x datalen=%d",
+               (cr == -1) ? "local" : "broadcast",id,ident,nlen);
+  if (cr >= -1) return; /* ignore local data */
+
+  cs = st->l1.hardware;
+  ic.driver = cs->myid;
+  ic.command = ISDN_STAT_PROT;
+  ic.arg = DSS1_STAT_INVOKE_BRD;
+  ic.parm.dss1_io.hl_id = id;
+  ic.parm.dss1_io.ll_id = 0;
+  ic.parm.dss1_io.proc = ident;
+  ic.parm.dss1_io.timeout= 0;
+  ic.parm.dss1_io.datalen = nlen;
+  ic.parm.dss1_io.data = p;
+
+  cs->iif.statcallb(&ic);
+} /* l3dss1_dummy_invoke */
+
+static void
+l3dss1_parse_facility(struct PStack *st, struct l3_process *pc,
+                      int cr, u_char * p)
+{
+	int qd_len = 0;
+	unsigned char nlen = 0, ilen, cp_tag;
+	int ident, id;
+	ulong err_ret;
+
+	if (pc) 
+		st = pc->st; /* valid Stack */
+	else
+		if ((!st) || (cr >= 0)) return; /* neither pc nor st specified */
+
+	p++;
+	qd_len = *p++;
+	if (qd_len == 0) {
+		l3_debug(st, "qd_len == 0");
+		return;
+	}
+	if ((*p & 0x1F) != 0x11) {	/* Service discriminator, supplementary service */
+		l3_debug(st, "supplementary service != 0x11");
+		return;
+	}
+	while (qd_len > 0 && !(*p & 0x80)) {	/* extension ? */
+		p++;
+		qd_len--;
+	}
+	if (qd_len < 2) {
+		l3_debug(st, "qd_len < 2");
+		return;
+	}
+	p++;
+	qd_len--;
+	if ((*p & 0xE0) != 0xA0) {	/* class and form */
+		l3_debug(st, "class and form != 0xA0");
+		return;
+	}
+       
+        cp_tag = *p & 0x1F; /* remember tag value */
+
+        p++;
+	qd_len--;
+	if (qd_len < 1) 
+          { l3_debug(st, "qd_len < 1");
+	    return;
+	  }
+	if (*p & 0x80) 
+          { /* length format indefinite or limited */
+	    nlen = *p++ & 0x7F; /* number of len bytes or indefinite */
+            if ((qd_len-- < ((!nlen) ? 3 : (1 + nlen))) ||
+                (nlen > 1))   
+	     { l3_debug(st, "length format error or not implemented");
+	       return;
+             }
+            if (nlen == 1)
+	     { nlen = *p++; /* complete length */
+               qd_len--;
+             } 
+            else
+	     { qd_len -= 2; /* trailing null bytes */
+               if ((*(p+qd_len)) || (*(p+qd_len+1)))
+		{ l3_debug(st,"length format indefinite error");
+                  return;
+                }
+               nlen = qd_len;
+             }
+	  }
+        else
+	  { nlen = *p++;
+	    qd_len--;
+          } 
+	if (qd_len < nlen) 
+          { l3_debug(st, "qd_len < nlen");
+	    return;
+	  }
+	qd_len -= nlen;
+
+	if (nlen < 2) 
+          { l3_debug(st, "nlen < 2");
+	    return;
+	  }
+        if (*p != 0x02) 
+          {  /* invoke identifier tag */
+	     l3_debug(st, "invoke identifier tag !=0x02");
+	     return;
+	  }
+	p++;
+	nlen--;
+	if (*p & 0x80) 
+          { /* length format */
+	    l3_debug(st, "invoke id length format 2");
+	    return;
+	  }
+	ilen = *p++;
+	nlen--;
+	if (ilen > nlen || ilen == 0) 
+          { l3_debug(st, "ilen > nlen || ilen == 0");
+	    return;
+	  }
+	nlen -= ilen;
+	id = 0;
+	while (ilen > 0) 
+          { id = (id << 8) | (*p++ & 0xFF);	/* invoke identifier */
+	    ilen--;
+	  }
+
+	switch (cp_tag) {	/* component tag */
+		case 1:	/* invoke */
+				if (nlen < 2) {
+					l3_debug(st, "nlen < 2 22");
+					return;
+				}
+				if (*p != 0x02) {	/* operation value */
+					l3_debug(st, "operation value !=0x02");
+					return;
+				}
+				p++;
+				nlen--;
+				ilen = *p++;
+				nlen--;
+				if (ilen > nlen || ilen == 0) {
+					l3_debug(st, "ilen > nlen || ilen == 0 22");
+					return;
+				}
+				nlen -= ilen;
+				ident = 0;
+				while (ilen > 0) {
+					ident = (ident << 8) | (*p++ & 0xFF);
+					ilen--;
+				}
+
+                                if (!pc) 
+			         { l3dss1_dummy_invoke(st, cr, id, ident, p, nlen);
+                                   return;
+                                 } 
+#if HISAX_DE_AOC
+			{
+
+#define FOO1(s,a,b) \
+	    while(nlen > 1) {		\
+		    int ilen = p[1];	\
+		    if(nlen < ilen+2) {	\
+			    l3_debug(st, "FOO1  nlen < ilen+2"); \
+			    return;		\
+		    }			\
+		    nlen -= ilen+2;		\
+		    if((*p & 0xFF) == (a)) {	\
+			    int nlen = ilen;	\
+			    p += 2;		\
+			    b;		\
+		    } else {		\
+			    p += ilen+2;	\
+		    }			\
+	    }
+
+				switch (ident) {
+					case 0x22:	/* during */
+						FOO1("1A", 0x30, FOO1("1C", 0xA1, FOO1("1D", 0x30, FOO1("1E", 0x02, ( {
+							       ident = 0;
+							nlen = (nlen)?nlen:0; /* Make gcc happy */
+							while (ilen > 0) {
+														     ident = (ident << 8) | *p++;
+								  ilen--;
+									}
+														     if (ident > pc->para.chargeinfo) {
+														     pc->para.chargeinfo = ident;
+														     st->l3.l3l4(st, CC_CHARGE | INDICATION, pc);
+									}
+														     if (st->l3.debug & L3_DEB_CHARGE) {
+														     if (*(p + 2) == 0) {
+														     l3_debug(st, "charging info during %d", pc->para.chargeinfo);
+									}
+								   else {
+														     l3_debug(st, "charging info final %d", pc->para.chargeinfo);
+									}
+									}
+									}
+								    )))))
+							break;
+					case 0x24:	/* final */
+						FOO1("2A", 0x30, FOO1("2B", 0x30, FOO1("2C", 0xA1, FOO1("2D", 0x30, FOO1("2E", 0x02, ( {
+							       ident = 0;
+							nlen = (nlen)?nlen:0; /* Make gcc happy */
+							while (ilen > 0) {
+																      ident = (ident << 8) | *p++;
+								  ilen--;
+									}
+																      if (ident > pc->para.chargeinfo) {
+																      pc->para.chargeinfo = ident;
+																      st->l3.l3l4(st, CC_CHARGE | INDICATION, pc);
+									}
+																      if (st->l3.debug & L3_DEB_CHARGE) {
+																      l3_debug(st, "charging info final %d", pc->para.chargeinfo);
+									}
+									}
+								   ))))))
+							break;
+					default:
+                                                       l3_debug(st, "invoke break invalid ident %02x",ident);
+						break;
+				}
+#undef FOO1
+
+			}
+#else  /* not HISAX_DE_AOC */
+                        l3_debug(st, "invoke break");
+#endif /* not HISAX_DE_AOC */
+			break;
+		case 2:	/* return result */
+			 /* if no process available handle separately */ 
+                        if (!pc)
+			 { if (cr == -1) 
+                             l3dss1_dummy_return_result(st, id, p, nlen);
+                           return; 
+                         }   
+                        if ((pc->prot.dss1.invoke_id) && (pc->prot.dss1.invoke_id == id))
+                          { /* Diversion successful */
+                            free_invoke_id(st,pc->prot.dss1.invoke_id);
+                            pc->prot.dss1.remote_result = 0; /* success */     
+                            pc->prot.dss1.invoke_id = 0;
+                            pc->redir_result = pc->prot.dss1.remote_result; 
+                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);                                  } /* Diversion successful */
+                        else
+                          l3_debug(st,"return error unknown identifier");
+			break;
+		case 3:	/* return error */
+                            err_ret = 0;
+	                    if (nlen < 2) 
+                              { l3_debug(st, "return error nlen < 2");
+	                        return;
+	                      }
+                            if (*p != 0x02) 
+                              { /* result tag */
+	                        l3_debug(st, "invoke error tag !=0x02");
+	                        return;
+	                      }
+	                    p++;
+	                    nlen--;
+	                    if (*p > 4) 
+                              { /* length format */
+	                        l3_debug(st, "invoke return errlen > 4 ");
+	                        return;
+	                      }
+	                    ilen = *p++;
+	                    nlen--;
+	                    if (ilen > nlen || ilen == 0) 
+                              { l3_debug(st, "error return ilen > nlen || ilen == 0");
+	                        return;
+	                       }
+	                    nlen -= ilen;
+	                    while (ilen > 0) 
+                             { err_ret = (err_ret << 8) | (*p++ & 0xFF);	/* error value */
+	                       ilen--;
+	                     }
+			 /* if no process available handle separately */ 
+                        if (!pc)
+			 { if (cr == -1)
+                             l3dss1_dummy_error_return(st, id, err_ret);
+                           return; 
+                         }   
+                        if ((pc->prot.dss1.invoke_id) && (pc->prot.dss1.invoke_id == id))
+                          { /* Deflection error */
+                            free_invoke_id(st,pc->prot.dss1.invoke_id);
+                            pc->prot.dss1.remote_result = err_ret; /* result */
+                            pc->prot.dss1.invoke_id = 0; 
+                            pc->redir_result = pc->prot.dss1.remote_result; 
+                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);  
+                          } /* Deflection error */
+                        else
+                          l3_debug(st,"return result unknown identifier");
+			break;
+		default:
+			l3_debug(st, "facility default break tag=0x%02x",cp_tag);
+			break;
+	}
+}
+
+static void
+l3dss1_message(struct l3_process *pc, u_char mt)
+{
+	struct sk_buff *skb;
+	u_char *p;
+
+	if (!(skb = l3_alloc_skb(4)))
+		return;
+	p = skb_put(skb, 4);
+	MsgHead(p, pc->callref, mt);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_message_cause(struct l3_process *pc, u_char mt, u_char cause)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+
+	MsgHead(p, pc->callref, mt);
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_status_send(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+
+	MsgHead(p, pc->callref, MT_STATUS);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = pc->para.cause | 0x80;
+
+	*p++ = IE_CALL_STATE;
+	*p++ = 0x1;
+	*p++ = pc->state & 0x3f;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+	/* 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
+	 */
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+
+	switch (pc->para.cause) {
+		case 81:	/* invalid callreference */
+		case 88:	/* incomp destination */
+		case 96:	/* mandory IE missing */
+		case 100:       /* invalid IE contents */
+		case 101:	/* incompatible Callstate */
+			MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+			*p++ = IE_CAUSE;
+			*p++ = 0x2;
+			*p++ = 0x80;
+			*p++ = pc->para.cause | 0x80;
+			break;
+		default:
+			printk(KERN_ERR "HiSax l3dss1_msg_without_setup wrong cause %d\n",
+				pc->para.cause);
+			return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	dss1_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_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_DISPLAY, IE_FACILITY, -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(struct l3_process *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(struct l3_process *pc, struct sk_buff *skb, int *checklist)
+{
+	int *cl = checklist;
+	u_char mt;
+	u_char *p, ie;
+	int l, newpos, oldpos;
+	int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0;
+	u_char codeset = 0;
+	u_char old_codeset = 0;
+	u_char codelock = 1;
+	
+	p = skb->data;
+	/* skip cr */
+	p++;
+	l = (*p++) & 0xf;
+	p += l;
+	mt = *p++;
+	oldpos = 0;
+	while ((p - skb->data) < skb->len) {
+		if ((*p & 0xf0) == 0x90) { /* shift codeset */
+			old_codeset = codeset;
+			codeset = *p & 7;
+			if (*p & 0x08)
+				codelock = 0;
+			else
+				codelock = 1;
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "check IE shift%scodeset %d->%d",
+					codelock ? " locking ": " ", old_codeset, codeset);
+			p++;
+			continue;
+		}
+		if (!codeset) { /* only codeset 0 */
+			if ((newpos = ie_in_set(pc, *p, cl))) {
+				if (newpos > 0) {
+					if (newpos < oldpos)
+						err_seq++;
+					else
+						oldpos = newpos;
+				}
+			} else {
+				if (ie_in_set(pc, *p, comp_required))
+					err_compr++;
+				else
+					err_ureg++;
+			}
+		}
+		ie = *p++;
+		if (ie & 0x80) {
+			l = 1;
+		} else {
+			l = *p++;
+			p += l;
+			l += 2;
+		}
+		if (!codeset && (l > getmax_ie_len(ie)))
+			err_len++;
+		if (!codelock) {
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "check IE shift back codeset %d->%d",
+					codeset, old_codeset);
+			codeset = old_codeset;
+			codelock = 1;
+		}
+	}
+	if (err_compr | err_ureg | err_len | err_seq) {
+		if (pc->debug & L3_DEB_CHECK)
+			l3_debug(pc->st, "check IE MT(%x) %d/%d/%d/%d",
+				mt, 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(struct l3_process *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->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "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->debug & (L3_DEB_CHECK | L3_DEB_WARN))
+				l3_debug(pc->st, "l3dss1_check_messagetype_validity mt(%x) fail", mt);
+			pc->para.cause = 97;
+			l3dss1_status_send(pc, 0, NULL);
+			return(1);
+	}
+	return(0);
+}
+
+static void
+l3dss1_std_ie_err(struct l3_process *pc, int ret) {
+
+	if (pc->debug & L3_DEB_CHECK)
+		l3_debug(pc->st, "check_infoelements ret %d", ret);
+	switch(ret) {
+		case 0: 
+			break;
+		case ERR_IE_COMPREHENSION:
+			pc->para.cause = 96;
+			l3dss1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_UNRECOGNIZED:
+			pc->para.cause = 99;
+			l3dss1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_LENGTH:
+			pc->para.cause = 100;
+			l3dss1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_SEQUENCE:
+		default:
+			break;
+	}
+}
+
+static int
+l3dss1_get_channel_id(struct l3_process *pc, struct sk_buff *skb) {
+	u_char *p;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		p++;
+		if (*p != 1) { /* len for BRI = 1 */
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "wrong chid len %d", *p);
+			return (-2);
+		}
+		p++;
+		if (*p & 0x60) { /* only base rate interface */
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "wrong chid %x", *p);
+			return (-3);
+		}
+		return(*p & 0x3);
+	} else
+		return(-1);
+}
+
+static int
+l3dss1_get_cause(struct l3_process *pc, struct sk_buff *skb) {
+	u_char l, i=0;
+	u_char *p;
+
+	p = skb->data;
+	pc->para.cause = 31;
+	pc->para.loc = 0;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		l = *p++;
+		if (l>30)
+			return(1);
+		if (l) {
+			pc->para.loc = *p++;
+			l--;
+		} else {
+			return(2);
+		}
+		if (l && !(pc->para.loc & 0x80)) {
+			l--;
+			p++; /* skip recommendation */
+		}
+		if (l) {
+			pc->para.cause = *p++;
+			l--;
+			if (!(pc->para.cause & 0x80))
+				return(3);
+		} else
+			return(4);
+		while (l && (i<6)) {
+			pc->para.diag[i++] = *p++;
+			l--;
+		}
+	} else
+		return(-1);
+	return(0);
+}
+
+static void
+l3dss1_msg_with_uus(struct l3_process *pc, u_char cmd)
+{
+	struct sk_buff *skb;
+	u_char tmp[16+40];
+	u_char *p = tmp;
+	int l;
+
+	MsgHead(p, pc->callref, cmd);
+
+        if (pc->prot.dss1.uus1_data[0])
+	 { *p++ = IE_USER_USER; /* UUS info element */
+           *p++ = strlen(pc->prot.dss1.uus1_data) + 1;
+           *p++ = 0x04; /* IA5 chars */
+           strcpy(p,pc->prot.dss1.uus1_data);
+           p += strlen(pc->prot.dss1.uus1_data);
+           pc->prot.dss1.uus1_data[0] = '\0';   
+         } 
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3dss1_msg_with_uus */
+
+static void
+l3dss1_release_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	StopAllL3Timer(pc);
+	newl3state(pc, 19);
+	if (!pc->prot.dss1.uus1_data[0]) 
+		l3dss1_message(pc, MT_RELEASE);
+	else
+		l3dss1_msg_with_uus(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3dss1_get_cause(pc, skb))>0) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "RELCMPL get_cause ret(%d)",ret);
+	} else if (ret < 0)
+		pc->para.cause = NO_CAUSE;
+	StopAllL3Timer(pc);
+	newl3state(pc, 0);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+	dss1_release_l3_process(pc);
+}
+
+#if EXT_BEARER_CAPS
+
+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)
+{
+	info &= 0x7f;
+	switch (info) {
+		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(struct sk_buff *skb)
+{
+	u_char *p;		//, *pend=skb->data + skb->len;
+
+	if ((p = findie(skb->data, skb->len, 0x7c, 0))) {
+		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;
+}
+
+#endif
+
+
+static void
+l3dss1_setup_req(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+	u_char channel = 0;
+
+        u_char send_keypad;
+	u_char screen = 0x80;
+	u_char *teln;
+	u_char *msn;
+	u_char *sub;
+	u_char *sp;
+	int l;
+
+	MsgHead(p, pc->callref, MT_SETUP);
+
+	teln = pc->para.setup.phone;
+#ifndef CONFIG_HISAX_NO_KEYPAD
+        send_keypad = (strchr(teln,'*') || strchr(teln,'#')) ? 1 : 0; 
+#else
+	send_keypad = 0;
+#endif
+#ifndef CONFIG_HISAX_NO_SENDCOMPLETE
+	if (!send_keypad)
+		*p++ = 0xa1;		/* complete indicator */
+#endif
+	/*
+	 * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
+	 */
+	switch (pc->para.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 Std. CCITT, unrestr. dig. Inform. */
+		*p++ = 0x90;	  /* Circuit-Mode 64kbps                      */
+		break;
+	}
+
+	if (send_keypad) {
+		*p++ = IE_KEYPAD;
+		*p++ = strlen(teln);
+		while (*teln)
+			*p++ = (*teln++) & 0x7F;
+	}
+	  
+	/*
+	 * What about info2? Mapping to High-Layer-Compatibility?
+	 */
+	if ((*teln) && (!send_keypad)) {
+		/* parse number for special things */
+		if (!isdigit(*teln)) {
+			switch (0x5f & *teln) {
+				case 'C':
+					channel = 0x08;
+				case 'P':
+					channel |= 0x80;
+					teln++;
+					if (*teln == '1')
+						channel |= 0x01;
+					else
+						channel |= 0x02;
+					break;
+				case 'R':
+					screen = 0xA0;
+					break;
+				case 'D':
+					screen = 0x80;
+					break;
+				
+			        default:
+					if (pc->debug & L3_DEB_WARN)
+						l3_debug(pc->st, "Wrong MSN Code");
+					break;
+			}
+			teln++;
+		}
+	}
+	if (channel) {
+		*p++ = IE_CHANNEL_ID;
+		*p++ = 1;
+		*p++ = channel;
+	}
+	msn = pc->para.setup.eazmsn;
+	sub = NULL;
+	sp = msn;
+	while (*sp) {
+		if ('.' == *sp) {
+			sub = sp;
+			*sp = 0;
+		} else
+			sp++;
+	}
+	if (*msn) {
+		*p++ = IE_CALLING_PN;
+		*p++ = strlen(msn) + (screen ? 2 : 1);
+		/* Classify as AnyPref. */
+		if (screen) {
+			*p++ = 0x01;	/* Ext = '0'B, Type = '000'B, Plan = '0001'B. */
+			*p++ = screen;
+		} else
+			*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+		while (*msn)
+			*p++ = *msn++ & 0x7f;
+	}
+	if (sub) {
+		*sub++ = '.';
+		*p++ = IE_CALLING_SUB;
+		*p++ = strlen(sub) + 2;
+		*p++ = 0x80;	/* NSAP coded */
+		*p++ = 0x50;	/* local IDI format */
+		while (*sub)
+			*p++ = *sub++ & 0x7f;
+	}
+	sub = NULL;
+	sp = teln;
+	while (*sp) {
+		if ('.' == *sp) {
+			sub = sp;
+			*sp = 0;
+		} else
+			sp++;
+	}
+	
+        if (!send_keypad) {      
+		*p++ = IE_CALLED_PN;
+		*p++ = strlen(teln) + 1;
+		/* Classify as AnyPref. */
+		*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+		while (*teln)
+			*p++ = *teln++ & 0x7f;
+		
+		if (sub) {
+			*sub++ = '.';
+			*p++ = IE_CALLED_SUB;
+			*p++ = strlen(sub) + 2;
+			*p++ = 0x80;	/* NSAP coded */
+			*p++ = 0x50;	/* local IDI format */
+			while (*sub)
+				*p++ = *sub++ & 0x7f;
+		}
+        }
+#if EXT_BEARER_CAPS
+	if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) {	// sync. Bitratenadaption, V.110/X.30
+
+		*p++ = IE_LLC;
+		*p++ = 0x04;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
+	} else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) {	// sync. Bitratenadaption, V.120
+
+		*p++ = IE_LLC;
+		*p++ = 0x05;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x28;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
+		*p++ = 0x82;
+	} else if (pc->para.setup.si2 >= 192) {		// async. Bitratenadaption, V.110/X.30
+
+		*p++ = IE_LLC;
+		*p++ = 0x06;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
+#ifndef CONFIG_HISAX_NO_LLC
+	} else {
+	  switch (pc->para.setup.si1) {
+		case 1:	                /* Telephony                                */
+			*p++ = IE_LLC;
+			*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_LLC;
+			*p++ = 0x2;	/* Length                                   */
+			*p++ = 0x88;	/* Coding Std. CCITT, unrestr. dig. Inform. */
+			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
+			break;
+	  }
+#endif
+	}
+#endif
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T303, CC_T303);
+	newl3state(pc, 1);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup answer with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3dss1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else if (1 == pc->state) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3dss1_status_send(pc, pr, NULL);
+		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);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	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);
+	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+}
+
+static void
+l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup answer with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3dss1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3dss1_status_send(pc, pr, NULL);
+		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);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	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);
+	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+}
+
+static void
+l3dss1_disconnect(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+	int ret;
+	u_char cause = 0;
+
+	StopAllL3Timer(pc);
+	if ((ret = l3dss1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "DISC get_cause ret(%d)", ret);
+		if (ret < 0)
+			cause = 96;
+		else if (ret > 0)
+			cause = 100;
+	} 
+	if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+		l3dss1_parse_facility(pc->st, pc, pc->callref, p);
+	ret = check_infoelements(pc, skb, ie_DISCONNECT);
+	if (ERR_IE_COMPREHENSION == ret)
+		cause = 96;
+	else if ((!cause) && (ERR_IE_UNRECOGNIZED == ret))
+		cause = 99;
+	ret = pc->state;
+	newl3state(pc, 12);
+	if (cause)
+		newl3state(pc, 19);
+       	if (11 != ret)
+		pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+       	else if (!cause)
+		   l3dss1_release_req(pc, pr, NULL);
+	if (cause) {
+		l3dss1_message_cause(pc, MT_RELEASE, cause);
+		L3AddTimer(&pc->timer, T308, CC_T308_1);
+	}
+}
+
+static void
+l3dss1_connect(struct l3_process *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);
+		return;
+	}
+	L3DelTimer(&pc->timer);	/* T310 */
+	newl3state(pc, 10);
+	pc->para.chargeinfo = 0;
+	/* here should inserted COLP handling KKe */
+	if (ret)
+		l3dss1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+}
+
+static void
+l3dss1_alerting(struct l3_process *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);
+		return;
+	}
+	L3DelTimer(&pc->timer);	/* T304 */
+	newl3state(pc, 4);
+	if (ret)
+		l3dss1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+}
+
+static void
+l3dss1_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	int bcfound = 0;
+	char tmp[80];
+	struct sk_buff *skb = arg;
+	int id;
+	int err = 0;
+
+	/*
+	 * Bearer Capabilities
+	 */
+	p = skb->data;
+	/* only the first occurence 'll be detected ! */
+	if ((p = findie(p, skb->len, 0x04, 0))) {
+		if ((p[1] < 2) || (p[1] > 11))
+			err = 1;
+		else {
+			pc->para.setup.si2 = 0;
+			switch (p[2] & 0x7f) {
+				case 0x00: /* Speech */
+				case 0x10: /* 3.1 Khz audio */
+					pc->para.setup.si1 = 1;
+					break;
+				case 0x08: /* Unrestricted digital information */
+					pc->para.setup.si1 = 7;
+/* JIM, 05.11.97 I wanna set service indicator 2 */
+#if EXT_BEARER_CAPS
+					pc->para.setup.si2 = DecodeSI2(skb);
+#endif
+					break;
+				case 0x09: /* Restricted digital information */
+					pc->para.setup.si1 = 2;
+					break;
+				case 0x11:
+					/* Unrestr. digital information  with 
+					 * tones/announcements ( or 7 kHz audio
+					 */
+					pc->para.setup.si1 = 3;
+					break;
+				case 0x18: /* Video */
+					pc->para.setup.si1 = 4;
+					break;
+				default:
+					err = 2;
+					break;
+			}
+			switch (p[3] & 0x7f) {
+				case 0x40: /* packed mode */
+					pc->para.setup.si1 = 8;
+					break;
+				case 0x10: /* 64 kbit */
+				case 0x11: /* 2*64 kbit */
+				case 0x13: /* 384 kbit */
+				case 0x15: /* 1536 kbit */
+				case 0x17: /* 1920 kbit */
+					pc->para.moderate = p[3] & 0x7f;
+					break;
+				default:
+					err = 3;
+					break;
+			}
+		}
+		if (pc->debug & L3_DEB_SI)
+			l3_debug(pc->st, "SI=%d, AI=%d",
+				pc->para.setup.si1, pc->para.setup.si2);
+		if (err) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup with wrong bearer(l=%d:%x,%x)",
+					p[1], p[2], p[3]);
+			pc->para.cause = 100;
+			l3dss1_msg_without_setup(pc, pr, NULL);
+			return;
+		}
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup without bearer capabilities");
+		/* ETS 300-104 1.3.3 */
+		pc->para.cause = 96;
+		l3dss1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	/*
+	 * Channel Identification
+	 */
+	if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) {
+		if ((pc->para.bchannel = id)) {
+			if ((3 == id) && (0x10 == pc->para.moderate)) {
+				if (pc->debug & L3_DEB_WARN)
+					l3_debug(pc->st, "setup with wrong chid %x",
+						id);
+				pc->para.cause = 100;
+				l3dss1_msg_without_setup(pc, pr, NULL);
+				return;
+			}
+			bcfound++;
+		} else 
+                   { if (pc->debug & L3_DEB_WARN)
+			 l3_debug(pc->st, "setup without bchannel, call waiting");
+                     bcfound++;
+                   } 
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup with wrong chid ret %d", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3dss1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	err = check_infoelements(pc, skb, ie_SETUP);
+	if (ERR_IE_COMPREHENSION == err) {
+		pc->para.cause = 96;
+		l3dss1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x70, 0)))
+		iecpy(pc->para.setup.eazmsn, p, 1);
+	else
+		pc->para.setup.eazmsn[0] = 0;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x71, 0))) {
+		/* Called party subaddress */
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.eazmsn, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong called subaddress");
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x6c, 0))) {
+		pc->para.setup.plan = p[2];
+		if (p[2] & 0x80) {
+			iecpy(pc->para.setup.phone, p, 1);
+			pc->para.setup.screen = 0;
+		} else {
+			iecpy(pc->para.setup.phone, p, 2);
+			pc->para.setup.screen = p[3];
+		}
+	} else {
+		pc->para.setup.phone[0] = 0;
+		pc->para.setup.plan = 0;
+		pc->para.setup.screen = 0;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x6d, 0))) {
+		/* Calling party subaddress */
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.phone, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong calling subaddress");
+	}
+	newl3state(pc, 6);
+	if (err) /* STATUS for none mandatory IE errors after actions are taken */
+		l3dss1_std_ie_err(pc, err);
+	pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+}
+
+static void
+l3dss1_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16+40];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 16;
+
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	StopAllL3Timer(pc);
+
+	MsgHead(p, pc->callref, MT_DISCONNECT);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+        if (pc->prot.dss1.uus1_data[0])
+	 { *p++ = IE_USER_USER; /* UUS info element */
+           *p++ = strlen(pc->prot.dss1.uus1_data) + 1;
+           *p++ = 0x04; /* IA5 chars */
+           strcpy(p,pc->prot.dss1.uus1_data);
+           p += strlen(pc->prot.dss1.uus1_data);
+           pc->prot.dss1.uus1_data[0] = '\0';   
+         } 
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 11);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T305, CC_T305);
+}
+
+static void
+l3dss1_setup_rsp(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+        if (!pc->para.bchannel) 
+	 { if (pc->debug & L3_DEB_WARN)
+	       l3_debug(pc->st, "D-chan connect for waiting call");
+           l3dss1_disconnect_req(pc, pr, arg);
+           return;
+         }
+	newl3state(pc, 8);
+	l3dss1_message(pc, MT_CONNECT);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T313, CC_T313);
+}
+
+static void
+l3dss1_connect_ack(struct l3_process *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);
+		return;
+	}
+	newl3state(pc, 10);
+	L3DelTimer(&pc->timer);
+	if (ret)
+		l3dss1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+}
+
+static void
+l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 21;
+
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	newl3state(pc, 0);
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_release(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+	int ret, cause=0;
+
+	StopAllL3Timer(pc);
+	if ((ret = l3dss1_get_cause(pc, skb))>0) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "REL get_cause ret(%d)", ret);
+	} else if (ret<0)
+		pc->para.cause = NO_CAUSE;
+	if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+		l3dss1_parse_facility(pc->st, pc, pc->callref, p);
+	}
+	if ((ret<0) && (pc->state != 11))
+		cause = 96;
+	else if (ret>0)
+		cause = 100;
+	ret = check_infoelements(pc, skb, ie_RELEASE);
+	if (ERR_IE_COMPREHENSION == ret)
+		cause = 96;
+	else if ((ERR_IE_UNRECOGNIZED == ret) && (!cause))
+		cause = 99;  
+	if (cause)
+		l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
+	else
+		l3dss1_message(pc, MT_RELEASE_COMPLETE);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	newl3state(pc, 0);
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_alert_req(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+	newl3state(pc, 7);
+	if (!pc->prot.dss1.uus1_data[0]) 
+		l3dss1_message(pc, MT_ALERTING);
+	else
+		l3dss1_msg_with_uus(pc, MT_ALERTING); 
+}
+
+static void
+l3dss1_proceed_req(struct l3_process *pc, u_char pr,
+		   void *arg)
+{
+	newl3state(pc, 9);
+	l3dss1_message(pc, MT_CALL_PROCEEDING);
+	pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); 
+}
+
+static void
+l3dss1_setup_ack_req(struct l3_process *pc, u_char pr,
+		   void *arg)
+{
+	newl3state(pc, 25);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T302, CC_T302);
+	l3dss1_message(pc, MT_SETUP_ACKNOWLEDGE);
+}
+
+/********************************************/
+/* deliver a incoming display message to HL */
+/********************************************/
+static void
+l3dss1_deliver_display(struct l3_process *pc, int pr, u_char *infp)
+{       u_char len;
+        isdn_ctrl ic; 
+	struct IsdnCardState *cs;
+        char *p; 
+
+        if (*infp++ != IE_DISPLAY) return;
+        if ((len = *infp++) > 80) return; /* total length <= 82 */
+	if (!pc->chan) return;
+
+	p = ic.parm.display; 
+        while (len--)
+	  *p++ = *infp++;
+	*p = '\0';
+	ic.command = ISDN_STAT_DISPLAY;
+	cs = pc->st->l1.hardware;
+	ic.driver = cs->myid;
+	ic.arg = pc->chan->chan; 
+	cs->iif.statcallb(&ic);
+} /* l3dss1_deliver_display */
+
+
+static void
+l3dss1_progress(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int err = 0;
+	u_char *p;
+
+	if ((p = findie(skb->data, skb->len, IE_PROGRESS, 0))) {
+		if (p[1] != 2) {
+			err = 1;
+			pc->para.cause = 100;
+		} else if (!(p[2] & 0x70)) {
+			switch (p[2]) {
+				case 0x80:
+				case 0x81:
+				case 0x82:
+				case 0x84:
+				case 0x85:
+				case 0x87:
+				case 0x8a:
+					switch (p[3]) {
+						case 0x81:
+						case 0x82:
+						case 0x83:
+						case 0x84:
+						case 0x88:
+							break;
+						default:
+							err = 2;
+							pc->para.cause = 100;
+							break;
+					}
+					break;
+				default:
+					err = 3;
+					pc->para.cause = 100;
+					break;
+			}
+		}
+	} else {
+		pc->para.cause = 96;
+		err = 4;
+	}
+	if (err) {	
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "progress error %d", err);
+		l3dss1_status_send(pc, pr, NULL);
+		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)
+		pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc);
+}
+
+static void
+l3dss1_notify(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int err = 0;
+	u_char *p;
+
+	if ((p = findie(skb->data, skb->len, IE_NOTIFY, 0))) {
+		if (p[1] != 1) {
+			err = 1;
+			pc->para.cause = 100;
+		} else {
+			switch (p[2]) {
+				case 0x80:
+				case 0x81:
+				case 0x82:
+					break;
+				default:
+					pc->para.cause = 100;
+					err = 2;
+					break;
+			}
+		}
+	} else {
+		pc->para.cause = 96;
+		err = 3;
+	}
+	if (err) {	
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "notify error %d", err);
+		l3dss1_status_send(pc, pr, NULL);
+		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)
+		pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc);
+}
+
+static void
+l3dss1_status_enq(struct l3_process *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);
+	pc->para.cause = 30; /* response to STATUS_ENQUIRY */
+        l3dss1_status_send(pc, pr, NULL);
+}
+
+static void
+l3dss1_information(struct l3_process *pc, u_char pr, void *arg)
+{
+	int ret;
+	struct sk_buff *skb = arg;
+	u_char *p;
+	char tmp[32];
+
+	ret = check_infoelements(pc, skb, ie_INFORMATION);
+	if (ret)
+		l3dss1_std_ie_err(pc, ret);
+	if (pc->state == 25) { /* overlap receiving */
+		L3DelTimer(&pc->timer);
+		p = skb->data;
+		if ((p = findie(p, skb->len, 0x70, 0))) {
+			iecpy(tmp, p, 1);
+			strcat(pc->para.setup.eazmsn, tmp);
+			pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+		}
+		L3AddTimer(&pc->timer, T302, CC_T302);
+	}
+}
+
+/******************************/
+/* handle deflection requests */
+/******************************/
+static void l3dss1_redir_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+        u_char *subp;
+        u_char len_phone = 0;
+        u_char len_sub = 0;
+	int l; 
+
+
+        strcpy(pc->prot.dss1.uus1_data,pc->chan->setup.eazmsn); /* copy uus element if available */
+        if (!pc->chan->setup.phone[0])
+          { pc->para.cause = -1;
+            l3dss1_disconnect_req(pc,pr,arg); /* disconnect immediately */
+            return;
+          } /* only uus */
+ 
+        if (pc->prot.dss1.invoke_id) 
+          free_invoke_id(pc->st,pc->prot.dss1.invoke_id);
+ 
+        if (!(pc->prot.dss1.invoke_id = new_invoke_id(pc->st))) 
+          return;
+
+        MsgHead(p, pc->callref, MT_FACILITY);
+
+        for (subp = pc->chan->setup.phone; (*subp) && (*subp != '.'); subp++) len_phone++; /* len of phone number */
+        if (*subp++ == '.') len_sub = strlen(subp) + 2; /* length including info subaddress element */ 
+
+	*p++ = 0x1c;   /* Facility info element */
+        *p++ = len_phone + len_sub + 2 + 2 + 8 + 3 + 3; /* length of element */
+        *p++ = 0x91;  /* remote operations protocol */
+        *p++ = 0xa1;  /* invoke component */
+	  
+        *p++ = len_phone + len_sub + 2 + 2 + 8 + 3; /* length of data */
+        *p++ = 0x02;  /* invoke id tag, integer */
+	*p++ = 0x01;  /* length */
+        *p++ = pc->prot.dss1.invoke_id;  /* invoke id */ 
+        *p++ = 0x02;  /* operation value tag, integer */
+	*p++ = 0x01;  /* length */
+        *p++ = 0x0D;  /* Call Deflect */
+	  
+        *p++ = 0x30;  /* sequence phone number */
+        *p++ = len_phone + 2 + 2 + 3 + len_sub; /* length */
+	  
+        *p++ = 0x30;  /* Deflected to UserNumber */
+        *p++ = len_phone+2+len_sub; /* length */
+        *p++ = 0x80; /* NumberDigits */
+	*p++ = len_phone; /* length */
+        for (l = 0; l < len_phone; l++)
+	 *p++ = pc->chan->setup.phone[l];
+
+        if (len_sub)
+	  { *p++ = 0x04; /* called party subaddress */
+            *p++ = len_sub - 2;
+            while (*subp) *p++ = *subp++;
+          }
+
+        *p++ = 0x01; /* screening identifier */
+        *p++ = 0x01;
+        *p++ = pc->chan->setup.screen;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l))) return;
+	memcpy(skb_put(skb, l), tmp, l);
+
+        l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3dss1_redir_req */
+
+/********************************************/
+/* handle deflection request in early state */
+/********************************************/
+static void l3dss1_redir_req_early(struct l3_process *pc, u_char pr, void *arg)
+{
+  l3dss1_proceed_req(pc,pr,arg);
+  l3dss1_redir_req(pc,pr,arg);
+} /* l3dss1_redir_req_early */
+
+/***********************************************/
+/* handle special commands for this protocol.  */
+/* Examples are call independant services like */
+/* remote operations with dummy  callref.      */
+/***********************************************/
+static int l3dss1_cmd_global(struct PStack *st, isdn_ctrl *ic)
+{ u_char id;
+  u_char temp[265];
+  u_char *p = temp;
+  int i, l, proc_len; 
+  struct sk_buff *skb;
+  struct l3_process *pc = NULL;
+
+  switch (ic->arg)
+   { case DSS1_CMD_INVOKE:
+       if (ic->parm.dss1_io.datalen < 0) return(-2); /* invalid parameter */ 
+
+       for (proc_len = 1, i = ic->parm.dss1_io.proc >> 8; i; i++) 
+         i = i >> 8; /* add one byte */    
+       l = ic->parm.dss1_io.datalen + proc_len + 8; /* length excluding ie header */
+       if (l > 255) 
+         return(-2); /* too long */
+
+       if (!(id = new_invoke_id(st))) 
+         return(0); /* first get a invoke id -> return if no available */
+       
+       i = -1; 
+       MsgHead(p, i, MT_FACILITY); /* build message head */
+       *p++ = 0x1C; /* Facility IE */
+       *p++ = l; /* length of ie */
+       *p++ = 0x91; /* remote operations */
+       *p++ = 0xA1; /* invoke */
+       *p++ = l - 3; /* length of invoke */
+       *p++ = 0x02; /* invoke id tag */
+       *p++ = 0x01; /* length is 1 */
+       *p++ = id; /* invoke id */
+       *p++ = 0x02; /* operation */
+       *p++ = proc_len; /* length of operation */
+       
+       for (i = proc_len; i; i--)
+         *p++ = (ic->parm.dss1_io.proc >> (i-1)) & 0xFF;
+       memcpy(p, ic->parm.dss1_io.data, ic->parm.dss1_io.datalen); /* copy data */
+       l = (p - temp) + ic->parm.dss1_io.datalen; /* total length */         
+
+       if (ic->parm.dss1_io.timeout > 0)
+        if (!(pc = dss1_new_l3_process(st, -1)))
+          { free_invoke_id(st, id);
+            return(-2);
+          } 
+       pc->prot.dss1.ll_id = ic->parm.dss1_io.ll_id; /* remember id */ 
+       pc->prot.dss1.proc = ic->parm.dss1_io.proc; /* and procedure */
+
+       if (!(skb = l3_alloc_skb(l))) 
+         { free_invoke_id(st, id);
+           if (pc) dss1_release_l3_process(pc);
+           return(-2);
+         }
+       memcpy(skb_put(skb, l), temp, l);
+       
+       if (pc)
+        { pc->prot.dss1.invoke_id = id; /* remember id */
+          L3AddTimer(&pc->timer, ic->parm.dss1_io.timeout, CC_TDSS1_IO | REQUEST);
+        }
+       
+       l3_msg(st, DL_DATA | REQUEST, skb);
+       ic->parm.dss1_io.hl_id = id; /* return id */
+       return(0);
+
+     case DSS1_CMD_INVOKE_ABORT:
+       if ((pc = l3dss1_search_dummy_proc(st, ic->parm.dss1_io.hl_id)))
+	{ L3DelTimer(&pc->timer); /* remove timer */
+          dss1_release_l3_process(pc);
+          return(0); 
+        } 
+       else
+	{ l3_debug(st, "l3dss1_cmd_global abort unknown id");
+          return(-2);
+        } 
+       break;
+    
+     default: 
+       l3_debug(st, "l3dss1_cmd_global unknown cmd 0x%lx", ic->arg);
+       return(-1);  
+   } /* switch ic-> arg */
+  return(-1);
+} /* l3dss1_cmd_global */
+
+static void 
+l3dss1_io_timer(struct l3_process *pc)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs = pc->st->l1.hardware;
+
+  L3DelTimer(&pc->timer); /* remove timer */
+
+  ic.driver = cs->myid;
+  ic.command = ISDN_STAT_PROT;
+  ic.arg = DSS1_STAT_INVOKE_ERR;
+  ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id;
+  ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id;
+  ic.parm.dss1_io.proc = pc->prot.dss1.proc;
+  ic.parm.dss1_io.timeout= -1;
+  ic.parm.dss1_io.datalen = 0;
+  ic.parm.dss1_io.data = NULL;
+  free_invoke_id(pc->st, pc->prot.dss1.invoke_id);
+  pc->prot.dss1.invoke_id = 0; /* reset id */
+
+  cs->iif.statcallb(&ic);
+
+  dss1_release_l3_process(pc); 
+} /* l3dss1_io_timer */
+
+static void
+l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int callState = 0;
+	p = skb->data;
+
+	if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
+		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
+		 */
+		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		newl3state(pc, 0);
+		dss1_release_l3_process(pc);
+	} else {
+		pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
+	}
+}
+
+static void
+l3dss1_dummy(struct l3_process *pc, u_char pr, void *arg)
+{
+}
+
+static void
+l3dss1_t302(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 28; /* invalid number */
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3dss1_t303(struct l3_process *pc, u_char pr, void *arg)
+{
+	if (pc->N303 > 0) {
+		pc->N303--;
+		L3DelTimer(&pc->timer);
+		l3dss1_setup_req(pc, pr, arg);
+	} else {
+		L3DelTimer(&pc->timer);
+		l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, 102);
+		pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
+		dss1_release_l3_process(pc);
+	}
+}
+
+static void
+l3dss1_t304(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+
+}
+
+static void
+l3dss1_t305(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+	u_char cause = 16;
+
+	L3DelTimer(&pc->timer);
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	MsgHead(p, pc->callref, MT_RELEASE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 19);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3dss1_t310(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3dss1_t313(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+}
+
+static void
+l3dss1_t308_1(struct l3_process *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(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_t318(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 102;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	newl3state(pc, 19);
+	l3dss1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3dss1_t319(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 102;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	newl3state(pc, 10);
+}
+
+static void
+l3dss1_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_status(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int ret; 
+	u_char cause = 0, callState = 0;
+	
+	if ((ret = l3dss1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "STATUS get_cause ret(%d)",ret);
+		if (ret < 0)
+			cause = 96;
+		else if (ret > 0)
+			cause = 100;
+	}
+	if ((p = findie(skb->data, skb->len, IE_CALL_STATE, 0))) {
+		p++;
+		if (1 == *p++) {
+			callState = *p;
+			if (!ie_in_set(pc, *p, l3_valid_states))
+				cause = 100;
+		} else
+			cause = 100;
+	} else
+		cause = 96;
+	if (!cause) { /*  no error before */
+		ret = check_infoelements(pc, skb, ie_STATUS);
+		if (ERR_IE_COMPREHENSION == ret)
+			cause = 96;
+		else if (ERR_IE_UNRECOGNIZED == ret)
+			cause = 99;
+	}
+	if (cause) {
+		u_char tmp;
+		
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "STATUS error(%d/%d)",ret,cause);
+		tmp = pc->para.cause;
+		pc->para.cause = cause;
+		l3dss1_status_send(pc, 0, NULL);
+		if (cause == 99)
+			pc->para.cause = tmp;
+		else
+			return;
+	}
+	cause = pc->para.cause;
+	if (((cause & 0x7f) == 111) && (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
+		 */
+		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		newl3state(pc, 0);
+		dss1_release_l3_process(pc);
+	}
+}
+
+static void
+l3dss1_facility(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+	
+	ret = check_infoelements(pc, skb, ie_FACILITY);
+	l3dss1_std_ie_err(pc, ret);
+ 	  {
+		u_char *p;
+		if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+			l3dss1_parse_facility(pc->st, pc, pc->callref, p);
+	}
+}
+
+static void
+l3dss1_suspend_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->chan->setup.phone;
+
+	MsgHead(p, pc->callref, MT_SUSPEND);
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = IE_CALL_ID;
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else if (l) {
+		l3_debug(pc->st, "SUS wrong CALL_ID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 15);
+	L3AddTimer(&pc->timer, T319, CC_T319);
+}
+
+static void
+l3dss1_suspend_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 0);
+	pc->para.cause = NO_CAUSE;
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+	/* We don't handle suspend_ack for IE errors now */
+	if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE)))
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "SUSPACK check ie(%d)",ret);
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_suspend_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3dss1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "SUSP_REJ get_cause ret(%d)",ret);
+		if (ret < 0) 
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3dss1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3dss1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	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_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->para.setup.phone;
+
+	MsgHead(p, pc->callref, MT_RESUME);
+
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = IE_CALL_ID;
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else if (l) {
+		l3_debug(pc->st, "RES wrong CALL_ID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 17);
+	L3AddTimer(&pc->timer, T318, CC_T318);
+}
+
+static void
+l3dss1_resume_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3dss1_get_channel_id(pc, skb)) > 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "resume ack with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3dss1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else if (1 == pc->state) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "resume ack without chid (ret %d)", id);
+		pc->para.cause = 96;
+		l3dss1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3dss1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+	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(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3dss1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "RES_REJ get_cause ret(%d)",ret);
+		if (ret < 0) 
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3dss1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_RESUME_REJECT);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3dss1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	newl3state(pc, 0);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3dss1_std_ie_err(pc, ret);
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[32];
+	u_char *p;
+	u_char ri, ch = 0, chan = 0;
+	int l;
+	struct sk_buff *skb = arg;
+	struct l3_process *up;
+
+	newl3state(pc, 2);
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) {
+		ri = p[2];
+		l3_debug(pc->st, "Restart %x", ri);
+	} else {
+		l3_debug(pc->st, "Restart without restart IE");
+		ri = 0x86;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		chan = p[2] & 3;
+		ch = p[2];
+		if (pc->st->l3.debug)
+			l3_debug(pc->st, "Restart for channel %d", chan);
+	}
+	newl3state(pc, 2);
+	up = pc->st->l3.proc;
+	while (up) {
+		if ((ri & 7) == 7)
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+		else if (up->para.bchannel == chan)
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+		up = up->next;
+	}
+	p = tmp;
+	MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE);
+	if (chan) {
+		*p++ = IE_CHANNEL_ID;
+		*p++ = 1;
+		*p++ = ch | 0x80;
+	}
+	*p++ = 0x79;		/* RESTART Ind */
+	*p++ = 1;
+	*p++ = ri;
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 0);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_dl_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+        pc->para.cause = 0x29;          /* Temporary failure */
+        pc->para.loc = 0;
+        l3dss1_disconnect_req(pc, pr, NULL);
+        pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3dss1_dl_release(struct l3_process *pc, u_char pr, void *arg)
+{
+        newl3state(pc, 0);
+        pc->para.cause = 0x1b;          /* Destination out of order */
+        pc->para.loc = 0;
+        pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+        release_l3_process(pc);
+}
+
+static void
+l3dss1_dl_reestablish(struct l3_process *pc, u_char pr, void *arg)
+{
+        L3DelTimer(&pc->timer);
+        L3AddTimer(&pc->timer, T309, CC_T309);
+        l3_msg(pc->st, DL_ESTABLISH | REQUEST, NULL);
+}
+ 
+static void
+l3dss1_dl_reest_status(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+ 
+ 	pc->para.cause = 0x1F; /* normal, unspecified */
+	l3dss1_status_send(pc, 0, NULL);
+}
+
+/* *INDENT-OFF* */
+static struct stateentry downstatelist[] =
+{
+	{SBIT(0),
+	 CC_SETUP | REQUEST, l3dss1_setup_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(12),
+	 CC_RELEASE | REQUEST, l3dss1_release_req},
+	{ALL_STATES,
+	 CC_RESTART | REQUEST, l3dss1_restart},
+	{SBIT(6) | SBIT(25),
+	 CC_IGNORE | REQUEST, l3dss1_reset},
+	{SBIT(6) | SBIT(25),
+	 CC_REJECT | REQUEST, l3dss1_reject_req},
+	{SBIT(6) | SBIT(25),
+	 CC_PROCEED_SEND | REQUEST, l3dss1_proceed_req},
+	{SBIT(6),
+	 CC_MORE_INFO | REQUEST, l3dss1_setup_ack_req},
+	{SBIT(25),
+	 CC_MORE_INFO | REQUEST, l3dss1_dummy},
+	{SBIT(6) | SBIT(9) | SBIT(25),
+	 CC_ALERTING | REQUEST, l3dss1_alert_req},
+	{SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25),
+	 CC_SETUP | RESPONSE, l3dss1_setup_rsp},
+	{SBIT(10),
+	 CC_SUSPEND | REQUEST, l3dss1_suspend_req},
+        {SBIT(7) | SBIT(9) | SBIT(25),
+         CC_REDIR | REQUEST, l3dss1_redir_req},
+        {SBIT(6),
+         CC_REDIR | REQUEST, l3dss1_redir_req_early},
+        {SBIT(9) | SBIT(25),
+         CC_DISCONNECT | REQUEST, l3dss1_disconnect_req},
+	{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},
+};
+
+#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},
+};
+
+#define MANSLLEN \
+        (sizeof(manstatelist) / sizeof(struct stateentry))
+/* *INDENT-ON* */
+
+
+static void
+global_handler(struct PStack *st, int mt, struct sk_buff *skb)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	int i;
+	struct l3_process *proc = st->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 (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1 global state %d mt %x unhandled",
+				proc->state, mt);
+		}
+		MsgHead(p, proc->callref, MT_STATUS);
+		*p++ = IE_CAUSE;
+		*p++ = 0x2;
+		*p++ = 0x80;
+		*p++ = 81 |0x80;	/* invalid cr */
+		*p++ = 0x14;		/* CallState */
+		*p++ = 0x1;
+		*p++ = proc->state & 0x3f;
+		l = p - tmp;
+		if (!(skb = l3_alloc_skb(l)))
+			return;
+		memcpy(skb_put(skb, l), tmp, l);
+		l3_msg(proc->st, DL_DATA | REQUEST, skb);
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1 global %d mt %x",
+				proc->state, mt);
+		}
+		globalmes_list[i].rout(proc, mt, skb);
+	}
+}
+
+static void
+dss1up(struct PStack *st, int pr, void *arg)
+{
+	int i, mt, cr, cause, callState;
+	char *ptr;
+	u_char *p;
+	struct sk_buff *skb = arg;
+	struct l3_process *proc;
+
+	switch (pr) {
+		case (DL_DATA | INDICATION):
+		case (DL_UNIT_DATA | INDICATION):
+			break;
+		case (DL_ESTABLISH | CONFIRM):
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			l3_msg(st, pr, arg);
+			return;
+			break;
+                default:
+                        printk(KERN_ERR "HiSax dss1up unknown pr=%04x\n", pr);
+                        return;
+	}
+	if (skb->len < 3) {
+		l3_debug(st, "dss1up frame too short(%d)", skb->len);
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	if (skb->data[0] != PROTO_DIS_EURO) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			l3_debug(st, "dss1up%sunexpected discriminator %x message len %d",
+				 (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				 skb->data[0], skb->len);
+		}
+		dev_kfree_skb(skb);
+		return;
+	}
+	cr = getcallref(skb->data);
+	if (skb->len < ((skb->data[1] & 0x0f) + 3)) {
+		l3_debug(st, "dss1up frame too short(%d)", skb->len);
+		dev_kfree_skb(skb);
+		return;
+	}
+	mt = skb->data[skb->data[1] + 2];
+	if (st->l3.debug & L3_DEB_STATE)
+		l3_debug(st, "dss1up cr %d", cr);
+	if (cr == -2) {  /* wrong Callref */
+		if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "dss1up wrong Callref");
+		dev_kfree_skb(skb);
+		return;
+	} else if (cr == -1) {	/* Dummy Callref */
+		if (mt == MT_FACILITY)
+			if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+				l3dss1_parse_facility(st, NULL, 
+					(pr == (DL_DATA | INDICATION)) ? -1 : -2, p); 
+				dev_kfree_skb(skb);
+				return;  
+			}
+		if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "dss1up dummy Callref (no facility msg or ie)");
+		dev_kfree_skb(skb);
+		return;
+	} else if ((((skb->data[1] & 0x0f) == 1) && (0==(cr & 0x7f))) ||
+		(((skb->data[1] & 0x0f) == 2) && (0==(cr & 0x7fff)))) {	/* Global CallRef */
+		if (st->l3.debug & L3_DEB_STATE)
+			l3_debug(st, "dss1up Global CallRef");
+		global_handler(st, mt, skb);
+		dev_kfree_skb(skb);
+		return;
+	} else if (!(proc = getl3proc(st, cr))) {
+		/* No transaction process exist, that means no call with
+		 * this callreference is active
+		 */
+		if (mt == MT_SETUP) {
+			/* Setup creates a new transaction process */
+			if (skb->data[2] & 0x80) {
+				/* Setup with wrong CREF flag */
+				if (st->l3.debug & L3_DEB_STATE)
+					l3_debug(st, "dss1up wrong CRef flag");
+				dev_kfree_skb(skb);
+				return;
+			}
+			if (!(proc = dss1_new_l3_process(st, cr))) {
+				/* 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;
+			}
+		} else if (mt == MT_STATUS) {
+			cause = 0;
+			if ((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				cause = *ptr & 0x7f;
+			}
+			callState = 0;
+			if ((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				callState = *ptr;
+			}
+			/* 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 = dss1_new_l3_process(st, cr))) {
+					proc->para.cause = 101;
+					l3dss1_msg_without_setup(proc, 0, NULL);
+				}
+			}
+			dev_kfree_skb(skb);
+			return;
+		} else if (mt == MT_RELEASE_COMPLETE) {
+			dev_kfree_skb(skb);
+			return;
+		} 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 */
+			dev_kfree_skb(skb);
+			if ((proc = dss1_new_l3_process(st, cr))) {
+				proc->para.cause = 81;
+				l3dss1_msg_without_setup(proc, 0, NULL);
+			}
+			return;
+		}
+	}
+	if (l3dss1_check_messagetype_validity(proc, mt, skb)) {
+		dev_kfree_skb(skb);
+		return;
+	}
+	if ((p = findie(skb->data, skb->len, IE_DISPLAY, 0)) != NULL) 
+	  l3dss1_deliver_display(proc, pr, p); /* Display IE included */
+	for (i = 0; i < DATASLLEN; i++)
+		if ((mt == datastatelist[i].primitive) &&
+		    ((1 << proc->state) & datastatelist[i].state))
+			break;
+	if (i == DATASLLEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1up%sstate %d mt %#x unhandled",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				proc->state, mt);
+		}
+		if ((MT_RELEASE_COMPLETE != mt) && (MT_RELEASE != mt)) {
+			proc->para.cause = 101;
+			l3dss1_status_send(proc, pr, skb);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1up%sstate %d mt %x",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				proc->state, mt);
+		}
+		datastatelist[i].rout(proc, pr, skb);
+	}
+	dev_kfree_skb(skb);
+	return;
+}
+
+static void
+dss1down(struct PStack *st, int pr, void *arg)
+{
+	int i, cr;
+	struct l3_process *proc;
+	struct Channel *chan;
+
+	if ((DL_ESTABLISH | REQUEST) == pr) {
+		l3_msg(st, pr, NULL);
+		return;
+	} else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) {
+		chan = arg;
+		cr = newcallref();
+		cr |= 0x80;
+		if ((proc = dss1_new_l3_process(st, cr))) {
+			proc->chan = chan;
+			chan->proc = proc;
+			memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm));
+			proc->callref = cr;
+		}
+	} else {
+		proc = arg;
+	}
+	if (!proc) {
+		printk(KERN_ERR "HiSax dss1down without proc pr=%04x\n", pr);
+		return;
+	}
+
+	if ( pr == (CC_TDSS1_IO | REQUEST)) {
+		l3dss1_io_timer(proc); /* timer expires */ 
+		return;
+	}  
+
+	for (i = 0; i < DOWNSLLEN; i++)
+		if ((pr == downstatelist[i].primitive) &&
+		    ((1 << proc->state) & downstatelist[i].state))
+			break;
+	if (i == DOWNSLLEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1down state %d prim %#x unhandled",
+				proc->state, pr);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1down state %d prim %#x",
+				proc->state, pr);
+		}
+		downstatelist[i].rout(proc, pr, arg);
+	}
+}
+
+static void
+dss1man(struct PStack *st, int pr, void *arg)
+{
+        int i;
+        struct l3_process *proc = arg;
+ 
+        if (!proc) {
+                printk(KERN_ERR "HiSax dss1man without proc pr=%04x\n", pr);
+                return;
+        }
+        for (i = 0; i < MANSLLEN; i++)
+                if ((pr == manstatelist[i].primitive) &&
+                    ((1 << proc->state) & manstatelist[i].state))
+                        break;
+        if (i == MANSLLEN) {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d dss1man state %d prim %#x unhandled",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+        } else {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d dss1man state %d prim %#x",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+                manstatelist[i].rout(proc, pr, arg);
+        }
+}
+ 
+void
+setstack_dss1(struct PStack *st)
+{
+	char tmp[64];
+	int i;
+
+	st->lli.l4l3 = dss1down;
+	st->lli.l4l3_proto = l3dss1_cmd_global;
+	st->l2.l2l3 = dss1up;
+	st->l3.l3ml3 = dss1man;
+	st->l3.N303 = 1;
+	st->prot.dss1.last_invoke_id = 0;
+	st->prot.dss1.invoke_used[0] = 1; /* Bit 0 must always be set to 1 */
+	i = 1;
+	while (i < 32) 
+		st->prot.dss1.invoke_used[i++] = 0;   
+
+	if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+		printk(KERN_ERR "HiSax can't get memory for dss1 global CR\n");
+	} else {
+		st->l3.global->state = 0;
+		st->l3.global->callref = 0;
+		st->l3.global->next = NULL;
+		st->l3.global->debug = L3_DEB_WARN;
+		st->l3.global->st = st;
+		st->l3.global->N303 = 1;
+		st->l3.global->prot.dss1.invoke_id = 0; 
+
+		L3InitTimer(st->l3.global, &st->l3.global->timer);
+	}
+	strcpy(tmp, dss1_revision);
+	printk(KERN_INFO "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp));
+}
diff --git a/drivers/isdn/hisax/l3dss1.h b/drivers/isdn/hisax/l3dss1.h
new file mode 100644
index 000000000000..6da47f05ef2a
--- /dev/null
+++ b/drivers/isdn/hisax/l3dss1.h
@@ -0,0 +1,124 @@
+/* $Id: l3dss1.h,v 1.10.6.2 2001/09/23 22:24:50 kai Exp $
+ *
+ * DSS1 (Euro) D-channel protocol defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#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
+
+/*
+ * 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
+
+#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 */
+    u8 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 --git a/drivers/isdn/hisax/l3ni1.c b/drivers/isdn/hisax/l3ni1.c
new file mode 100644
index 000000000000..3ab3a54daac1
--- /dev/null
+++ b/drivers/isdn/hisax/l3ni1.c
@@ -0,0 +1,3189 @@
+/* $Id: l3ni1.c,v 2.8.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * NI1 D-channel protocol
+ *
+ * Author       Matt Henderson & Guy Ellis
+ * Copyright    by Traverse Technologies Pty Ltd, www.travers.com.au
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * 2000.6.6 Initial implementation of routines for US NI1 
+ * Layer 3 protocol based on the EURO/DSS1 D-channel protocol 
+ * driver written by Karsten Keil et al.  
+ * NI-1 Hall of Fame - Thanks to.... 
+ * Ragnar Paulson - for some handy code fragments
+ * Will Scales - beta tester extraordinaire
+ * Brett Whittacre - beta tester and remote devel system in Vegas
+ *
+ */
+
+#include "hisax.h"
+#include "isdnl3.h"
+#include "l3ni1.h"
+#include <linux/ctype.h>
+
+extern char *HiSax_getrev(const char *revision);
+const char *ni1_revision = "$Revision: 2.8.2.3 $";
+
+#define EXT_BEARER_CAPS 1
+
+#define	MsgHead(ptr, cref, mty) \
+	*ptr++ = 0x8; \
+	if (cref == -1) { \
+		*ptr++ = 0x0; \
+	} else { \
+		*ptr++ = 0x1; \
+		*ptr++ = cref^0x80; \
+	} \
+	*ptr++ = mty
+
+
+/**********************************************/
+/* get a new invoke id for remote operations. */
+/* Only a return value != 0 is valid          */
+/**********************************************/
+static unsigned char new_invoke_id(struct PStack *p)
+{
+	unsigned char retval;
+	int i;
+  
+	i = 32; /* maximum search depth */
+
+	retval = p->prot.ni1.last_invoke_id + 1; /* try new id */
+	while ((i) && (p->prot.ni1.invoke_used[retval >> 3] == 0xFF)) {
+		p->prot.ni1.last_invoke_id = (retval & 0xF8) + 8;
+		i--;
+	}  
+	if (i) {
+		while (p->prot.ni1.invoke_used[retval >> 3] & (1 << (retval & 7)))
+		retval++; 
+	} else
+		retval = 0;
+	p->prot.ni1.last_invoke_id = retval;
+	p->prot.ni1.invoke_used[retval >> 3] |= (1 << (retval & 7));
+	return(retval);  
+} /* new_invoke_id */
+
+/*************************/
+/* free a used invoke id */
+/*************************/
+static void free_invoke_id(struct PStack *p, unsigned char id)
+{
+
+  if (!id) return; /* 0 = invalid value */
+
+  p->prot.ni1.invoke_used[id >> 3] &= ~(1 << (id & 7));
+} /* free_invoke_id */  
+
+
+/**********************************************************/
+/* create a new l3 process and fill in ni1 specific data */
+/**********************************************************/
+static struct l3_process
+*ni1_new_l3_process(struct PStack *st, int cr)
+{  struct l3_process *proc;
+
+   if (!(proc = new_l3_process(st, cr))) 
+     return(NULL);
+
+   proc->prot.ni1.invoke_id = 0;
+   proc->prot.ni1.remote_operation = 0;
+   proc->prot.ni1.uus1_data[0] = '\0';
+   
+   return(proc);
+} /* ni1_new_l3_process */
+
+/************************************************/
+/* free a l3 process and all ni1 specific data */
+/************************************************/ 
+static void
+ni1_release_l3_process(struct l3_process *p)
+{
+   free_invoke_id(p->st,p->prot.ni1.invoke_id);
+   release_l3_process(p);
+} /* ni1_release_l3_process */
+ 
+/********************************************************/
+/* search a process with invoke id id and dummy callref */
+/********************************************************/
+static struct l3_process *
+l3ni1_search_dummy_proc(struct PStack *st, int id)
+{ struct l3_process *pc = st->l3.proc; /* start of processes */
+
+  if (!id) return(NULL);
+
+  while (pc)
+   { if ((pc->callref == -1) && (pc->prot.ni1.invoke_id == id))
+       return(pc);
+     pc = pc->next;
+   } 
+  return(NULL);
+} /* l3ni1_search_dummy_proc */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return result is delivered. id specifies the invoke id.   */
+/*******************************************************************/ 
+static void 
+l3ni1_dummy_return_result(struct PStack *st, int id, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+  struct l3_process *pc = NULL; 
+
+  if ((pc = l3ni1_search_dummy_proc(st, id)))
+   { L3DelTimer(&pc->timer); /* remove timer */
+
+     cs = pc->st->l1.hardware;
+     ic.driver = cs->myid;
+     ic.command = ISDN_STAT_PROT;
+     ic.arg = NI1_STAT_INVOKE_RES;
+     ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id;
+     ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id;
+     ic.parm.ni1_io.proc = pc->prot.ni1.proc;
+     ic.parm.ni1_io.timeout= 0;
+     ic.parm.ni1_io.datalen = nlen;
+     ic.parm.ni1_io.data = p;
+     free_invoke_id(pc->st, pc->prot.ni1.invoke_id);
+     pc->prot.ni1.invoke_id = 0; /* reset id */
+
+     cs->iif.statcallb(&ic);
+     ni1_release_l3_process(pc); 
+   }
+  else
+   l3_debug(st, "dummy return result id=0x%x result len=%d",id,nlen);
+} /* l3ni1_dummy_return_result */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return error is delivered. id specifies the invoke id.    */
+/*******************************************************************/ 
+static void 
+l3ni1_dummy_error_return(struct PStack *st, int id, ulong error)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+  struct l3_process *pc = NULL; 
+
+  if ((pc = l3ni1_search_dummy_proc(st, id)))
+   { L3DelTimer(&pc->timer); /* remove timer */
+
+     cs = pc->st->l1.hardware;
+     ic.driver = cs->myid;
+     ic.command = ISDN_STAT_PROT;
+     ic.arg = NI1_STAT_INVOKE_ERR;
+     ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id;
+     ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id;
+     ic.parm.ni1_io.proc = pc->prot.ni1.proc;
+     ic.parm.ni1_io.timeout= error;
+     ic.parm.ni1_io.datalen = 0;
+     ic.parm.ni1_io.data = NULL;
+     free_invoke_id(pc->st, pc->prot.ni1.invoke_id);
+     pc->prot.ni1.invoke_id = 0; /* reset id */
+
+     cs->iif.statcallb(&ic);
+     ni1_release_l3_process(pc); 
+   }
+  else
+   l3_debug(st, "dummy return error id=0x%x error=0x%lx",id,error);
+} /* l3ni1_error_return */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a invoke is delivered. id specifies the invoke id.          */
+/*******************************************************************/ 
+static void 
+l3ni1_dummy_invoke(struct PStack *st, int cr, int id, 
+                    int ident, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+
+  l3_debug(st, "dummy invoke %s id=0x%x ident=0x%x datalen=%d",
+               (cr == -1) ? "local" : "broadcast",id,ident,nlen);
+  if (cr >= -1) return; /* ignore local data */
+
+  cs = st->l1.hardware;
+  ic.driver = cs->myid;
+  ic.command = ISDN_STAT_PROT;
+  ic.arg = NI1_STAT_INVOKE_BRD;
+  ic.parm.ni1_io.hl_id = id;
+  ic.parm.ni1_io.ll_id = 0;
+  ic.parm.ni1_io.proc = ident;
+  ic.parm.ni1_io.timeout= 0;
+  ic.parm.ni1_io.datalen = nlen;
+  ic.parm.ni1_io.data = p;
+
+  cs->iif.statcallb(&ic);
+} /* l3ni1_dummy_invoke */
+
+static void
+l3ni1_parse_facility(struct PStack *st, struct l3_process *pc,
+                      int cr, u_char * p)
+{
+	int qd_len = 0;
+	unsigned char nlen = 0, ilen, cp_tag;
+	int ident, id;
+	ulong err_ret;
+
+	if (pc) 
+		st = pc->st; /* valid Stack */
+	else
+		if ((!st) || (cr >= 0)) return; /* neither pc nor st specified */
+
+	p++;
+	qd_len = *p++;
+	if (qd_len == 0) {
+		l3_debug(st, "qd_len == 0");
+		return;
+	}
+	if ((*p & 0x1F) != 0x11) {	/* Service discriminator, supplementary service */
+		l3_debug(st, "supplementary service != 0x11");
+		return;
+	}
+	while (qd_len > 0 && !(*p & 0x80)) {	/* extension ? */
+		p++;
+		qd_len--;
+	}
+	if (qd_len < 2) {
+		l3_debug(st, "qd_len < 2");
+		return;
+	}
+	p++;
+	qd_len--;
+	if ((*p & 0xE0) != 0xA0) {	/* class and form */
+		l3_debug(st, "class and form != 0xA0");
+		return;
+	}
+       
+        cp_tag = *p & 0x1F; /* remember tag value */
+
+        p++;
+	qd_len--;
+	if (qd_len < 1) 
+          { l3_debug(st, "qd_len < 1");
+	    return;
+	  }
+	if (*p & 0x80) 
+          { /* length format indefinite or limited */
+	    nlen = *p++ & 0x7F; /* number of len bytes or indefinite */
+            if ((qd_len-- < ((!nlen) ? 3 : (1 + nlen))) ||
+                (nlen > 1))   
+	     { l3_debug(st, "length format error or not implemented");
+	       return;
+             }
+            if (nlen == 1)
+	     { nlen = *p++; /* complete length */
+               qd_len--;
+             } 
+            else
+	     { qd_len -= 2; /* trailing null bytes */
+               if ((*(p+qd_len)) || (*(p+qd_len+1)))
+		{ l3_debug(st,"length format indefinite error");
+                  return;
+                }
+               nlen = qd_len;
+             }
+	  }
+        else
+	  { nlen = *p++;
+	    qd_len--;
+          } 
+	if (qd_len < nlen) 
+          { l3_debug(st, "qd_len < nlen");
+	    return;
+	  }
+	qd_len -= nlen;
+
+	if (nlen < 2) 
+          { l3_debug(st, "nlen < 2");
+	    return;
+	  }
+        if (*p != 0x02) 
+          {  /* invoke identifier tag */
+	     l3_debug(st, "invoke identifier tag !=0x02");
+	     return;
+	  }
+	p++;
+	nlen--;
+	if (*p & 0x80) 
+          { /* length format */
+	    l3_debug(st, "invoke id length format 2");
+	    return;
+	  }
+	ilen = *p++;
+	nlen--;
+	if (ilen > nlen || ilen == 0) 
+          { l3_debug(st, "ilen > nlen || ilen == 0");
+	    return;
+	  }
+	nlen -= ilen;
+	id = 0;
+	while (ilen > 0) 
+          { id = (id << 8) | (*p++ & 0xFF);	/* invoke identifier */
+	    ilen--;
+	  }
+
+	switch (cp_tag) {	/* component tag */
+		case 1:	/* invoke */
+				if (nlen < 2) {
+					l3_debug(st, "nlen < 2 22");
+					return;
+				}
+				if (*p != 0x02) {	/* operation value */
+					l3_debug(st, "operation value !=0x02");
+					return;
+				}
+				p++;
+				nlen--;
+				ilen = *p++;
+				nlen--;
+				if (ilen > nlen || ilen == 0) {
+					l3_debug(st, "ilen > nlen || ilen == 0 22");
+					return;
+				}
+				nlen -= ilen;
+				ident = 0;
+				while (ilen > 0) {
+					ident = (ident << 8) | (*p++ & 0xFF);
+					ilen--;
+				}
+
+				if (!pc) 
+				{
+					l3ni1_dummy_invoke(st, cr, id, ident, p, nlen);
+					return;
+				} 
+				l3_debug(st, "invoke break");
+				break;
+		case 2:	/* return result */
+			 /* if no process available handle separately */ 
+                        if (!pc)
+			 { if (cr == -1) 
+                             l3ni1_dummy_return_result(st, id, p, nlen);
+                           return; 
+                         }   
+                        if ((pc->prot.ni1.invoke_id) && (pc->prot.ni1.invoke_id == id))
+                          { /* Diversion successful */
+                            free_invoke_id(st,pc->prot.ni1.invoke_id);
+                            pc->prot.ni1.remote_result = 0; /* success */     
+                            pc->prot.ni1.invoke_id = 0;
+                            pc->redir_result = pc->prot.ni1.remote_result; 
+                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);                                  } /* Diversion successful */
+                        else
+                          l3_debug(st,"return error unknown identifier");
+			break;
+		case 3:	/* return error */
+                            err_ret = 0;
+	                    if (nlen < 2) 
+                              { l3_debug(st, "return error nlen < 2");
+	                        return;
+	                      }
+                            if (*p != 0x02) 
+                              { /* result tag */
+	                        l3_debug(st, "invoke error tag !=0x02");
+	                        return;
+	                      }
+	                    p++;
+	                    nlen--;
+	                    if (*p > 4) 
+                              { /* length format */
+	                        l3_debug(st, "invoke return errlen > 4 ");
+	                        return;
+	                      }
+	                    ilen = *p++;
+	                    nlen--;
+	                    if (ilen > nlen || ilen == 0) 
+                              { l3_debug(st, "error return ilen > nlen || ilen == 0");
+	                        return;
+	                       }
+	                    nlen -= ilen;
+	                    while (ilen > 0) 
+                             { err_ret = (err_ret << 8) | (*p++ & 0xFF);	/* error value */
+	                       ilen--;
+	                     }
+			 /* if no process available handle separately */ 
+                        if (!pc)
+			 { if (cr == -1)
+                             l3ni1_dummy_error_return(st, id, err_ret);
+                           return; 
+                         }   
+                        if ((pc->prot.ni1.invoke_id) && (pc->prot.ni1.invoke_id == id))
+                          { /* Deflection error */
+                            free_invoke_id(st,pc->prot.ni1.invoke_id);
+                            pc->prot.ni1.remote_result = err_ret; /* result */
+                            pc->prot.ni1.invoke_id = 0; 
+                            pc->redir_result = pc->prot.ni1.remote_result; 
+                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);  
+                          } /* Deflection error */
+                        else
+                          l3_debug(st,"return result unknown identifier");
+			break;
+		default:
+			l3_debug(st, "facility default break tag=0x%02x",cp_tag);
+			break;
+	}
+}
+
+static void
+l3ni1_message(struct l3_process *pc, u_char mt)
+{
+	struct sk_buff *skb;
+	u_char *p;
+
+	if (!(skb = l3_alloc_skb(4)))
+		return;
+	p = skb_put(skb, 4);
+	MsgHead(p, pc->callref, mt);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_message_plus_chid(struct l3_process *pc, u_char mt)
+/* sends an l3 messages plus channel id -  added GE 05/09/00 */
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	u_char chid;
+
+	chid = (u_char)(pc->para.bchannel & 0x03) | 0x88;
+	MsgHead(p, pc->callref, mt);
+	*p++ = IE_CHANNEL_ID;
+	*p++ = 0x01;
+	*p++ = chid;
+
+	if (!(skb = l3_alloc_skb(7)))
+		return;
+	memcpy(skb_put(skb, 7), tmp, 7);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_message_cause(struct l3_process *pc, u_char mt, u_char cause)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+
+	MsgHead(p, pc->callref, mt);
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_status_send(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+
+	MsgHead(p, pc->callref, MT_STATUS);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = pc->para.cause | 0x80;
+
+	*p++ = IE_CALL_STATE;
+	*p++ = 0x1;
+	*p++ = pc->state & 0x3f;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+	/* This routine is called if here was no SETUP made (checks in ni1up and in
+	 * l3ni1_setup) and a RELEASE_COMPLETE have to be sent with an error code
+	 * MT_STATUS_ENQUIRE in the NULL state is handled too
+	 */
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+
+	switch (pc->para.cause) {
+		case 81:	/* invalid callreference */
+		case 88:	/* incomp destination */
+		case 96:	/* mandory IE missing */
+		case 100:       /* invalid IE contents */
+		case 101:	/* incompatible Callstate */
+			MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+			*p++ = IE_CAUSE;
+			*p++ = 0x2;
+			*p++ = 0x80;
+			*p++ = pc->para.cause | 0x80;
+			break;
+		default:
+			printk(KERN_ERR "HiSax l3ni1_msg_without_setup wrong cause %d\n",
+				pc->para.cause);
+			return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	ni1_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_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_DISPLAY, IE_FACILITY, -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(struct l3_process *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(struct l3_process *pc, struct sk_buff *skb, int *checklist)
+{
+	int *cl = checklist;
+	u_char mt;
+	u_char *p, ie;
+	int l, newpos, oldpos;
+	int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0;
+	u_char codeset = 0;
+	u_char old_codeset = 0;
+	u_char codelock = 1;
+	
+	p = skb->data;
+	/* skip cr */
+	p++;
+	l = (*p++) & 0xf;
+	p += l;
+	mt = *p++;
+	oldpos = 0;
+	while ((p - skb->data) < skb->len) {
+		if ((*p & 0xf0) == 0x90) { /* shift codeset */
+			old_codeset = codeset;
+			codeset = *p & 7;
+			if (*p & 0x08)
+				codelock = 0;
+			else
+				codelock = 1;
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "check IE shift%scodeset %d->%d",
+					codelock ? " locking ": " ", old_codeset, codeset);
+			p++;
+			continue;
+		}
+		if (!codeset) { /* only codeset 0 */
+			if ((newpos = ie_in_set(pc, *p, cl))) {
+				if (newpos > 0) {
+					if (newpos < oldpos)
+						err_seq++;
+					else
+						oldpos = newpos;
+				}
+			} else {
+				if (ie_in_set(pc, *p, comp_required))
+					err_compr++;
+				else
+					err_ureg++;
+			}
+		}
+		ie = *p++;
+		if (ie & 0x80) {
+			l = 1;
+		} else {
+			l = *p++;
+			p += l;
+			l += 2;
+		}
+		if (!codeset && (l > getmax_ie_len(ie)))
+			err_len++;
+		if (!codelock) {
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "check IE shift back codeset %d->%d",
+					codeset, old_codeset);
+			codeset = old_codeset;
+			codelock = 1;
+		}
+	}
+	if (err_compr | err_ureg | err_len | err_seq) {
+		if (pc->debug & L3_DEB_CHECK)
+			l3_debug(pc->st, "check IE MT(%x) %d/%d/%d/%d",
+				mt, 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
+l3ni1_check_messagetype_validity(struct l3_process *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->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "l3ni1_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->debug & (L3_DEB_CHECK | L3_DEB_WARN))
+				l3_debug(pc->st, "l3ni1_check_messagetype_validity mt(%x) fail", mt);
+			pc->para.cause = 97;
+			l3ni1_status_send(pc, 0, NULL);
+			return(1);
+	}
+	return(0);
+}
+
+static void
+l3ni1_std_ie_err(struct l3_process *pc, int ret) {
+
+	if (pc->debug & L3_DEB_CHECK)
+		l3_debug(pc->st, "check_infoelements ret %d", ret);
+	switch(ret) {
+		case 0: 
+			break;
+		case ERR_IE_COMPREHENSION:
+			pc->para.cause = 96;
+			l3ni1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_UNRECOGNIZED:
+			pc->para.cause = 99;
+			l3ni1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_LENGTH:
+			pc->para.cause = 100;
+			l3ni1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_SEQUENCE:
+		default:
+			break;
+	}
+}
+
+static int
+l3ni1_get_channel_id(struct l3_process *pc, struct sk_buff *skb) {
+	u_char *p;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		p++;
+		if (*p != 1) { /* len for BRI = 1 */
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "wrong chid len %d", *p);
+			return (-2);
+		}
+		p++;
+		if (*p & 0x60) { /* only base rate interface */
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "wrong chid %x", *p);
+			return (-3);
+		}
+		return(*p & 0x3);
+	} else
+		return(-1);
+}
+
+static int
+l3ni1_get_cause(struct l3_process *pc, struct sk_buff *skb) {
+	u_char l, i=0;
+	u_char *p;
+
+	p = skb->data;
+	pc->para.cause = 31;
+	pc->para.loc = 0;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		l = *p++;
+		if (l>30)
+			return(1);
+		if (l) {
+			pc->para.loc = *p++;
+			l--;
+		} else {
+			return(2);
+		}
+		if (l && !(pc->para.loc & 0x80)) {
+			l--;
+			p++; /* skip recommendation */
+		}
+		if (l) {
+			pc->para.cause = *p++;
+			l--;
+			if (!(pc->para.cause & 0x80))
+				return(3);
+		} else
+			return(4);
+		while (l && (i<6)) {
+			pc->para.diag[i++] = *p++;
+			l--;
+		}
+	} else
+		return(-1);
+	return(0);
+}
+
+static void
+l3ni1_msg_with_uus(struct l3_process *pc, u_char cmd)
+{
+	struct sk_buff *skb;
+	u_char tmp[16+40];
+	u_char *p = tmp;
+	int l;
+
+	MsgHead(p, pc->callref, cmd);
+
+        if (pc->prot.ni1.uus1_data[0])
+	 { *p++ = IE_USER_USER; /* UUS info element */
+           *p++ = strlen(pc->prot.ni1.uus1_data) + 1;
+           *p++ = 0x04; /* IA5 chars */
+           strcpy(p,pc->prot.ni1.uus1_data);
+           p += strlen(pc->prot.ni1.uus1_data);
+           pc->prot.ni1.uus1_data[0] = '\0';   
+         } 
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3ni1_msg_with_uus */
+
+static void
+l3ni1_release_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	StopAllL3Timer(pc);
+	newl3state(pc, 19);
+	if (!pc->prot.ni1.uus1_data[0]) 
+		l3ni1_message(pc, MT_RELEASE);
+	else
+		l3ni1_msg_with_uus(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3ni1_release_cmpl(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3ni1_get_cause(pc, skb))>0) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "RELCMPL get_cause ret(%d)",ret);
+	} else if (ret < 0)
+		pc->para.cause = NO_CAUSE;
+	StopAllL3Timer(pc);
+	newl3state(pc, 0);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+	ni1_release_l3_process(pc);
+}
+
+#if EXT_BEARER_CAPS
+
+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)
+{
+	info &= 0x7f;
+	switch (info) {
+		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(struct sk_buff *skb)
+{
+	u_char *p;		//, *pend=skb->data + skb->len;
+
+	if ((p = findie(skb->data, skb->len, 0x7c, 0))) {
+		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;
+}
+
+#endif
+
+
+static void
+l3ni1_setup_req(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+
+	u_char *teln;
+	u_char *sub;
+	u_char *sp;
+	int l;
+
+	MsgHead(p, pc->callref, MT_SETUP);
+
+	teln = pc->para.setup.phone;
+
+	*p++ = 0xa1;		/* complete indicator */
+	/*
+	 * Set Bearer Capability, Map info from 1TR6-convention to NI1
+	 */
+	switch (pc->para.setup.si1) {
+	case 1:	                  /* Telephony                                */
+		*p++ = IE_BEARER;
+		*p++ = 0x3;	  /* Length                                   */
+		*p++ = 0x90;	  /* 3.1khz Audio      			      */
+		*p++ = 0x90;	  /* Circuit-Mode 64kbps                      */
+		*p++ = 0xa2;	  /* u-Law Audio                              */
+		break;
+	case 5:	                  /* Datatransmission 64k, BTX                */
+	case 7:	                  /* Datatransmission 64k                     */
+	default:
+		*p++ = IE_BEARER;
+		*p++ = 0x2;	  /* Length                                   */
+		*p++ = 0x88;	  /* Coding Std. CCITT, unrestr. dig. Inform. */
+		*p++ = 0x90;	  /* Circuit-Mode 64kbps                      */
+		break;
+	}
+
+	sub = NULL;
+	sp = teln;
+	while (*sp) {
+		if ('.' == *sp) {
+			sub = sp;
+			*sp = 0;
+		} else
+			sp++;
+	}
+	
+	*p++ = IE_KEYPAD;
+	*p++ = strlen(teln);
+	while (*teln)
+		*p++ = (*teln++) & 0x7F;
+
+	if (sub)
+		*sub++ = '.';
+	
+#if EXT_BEARER_CAPS
+	if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) {	// sync. Bitratenadaption, V.110/X.30
+
+		*p++ = IE_LLC;
+		*p++ = 0x04;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
+	} else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) {	// sync. Bitratenadaption, V.120
+
+		*p++ = IE_LLC;
+		*p++ = 0x05;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x28;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
+		*p++ = 0x82;
+	} else if (pc->para.setup.si2 >= 192) {		// async. Bitratenadaption, V.110/X.30
+
+		*p++ = IE_LLC;
+		*p++ = 0x06;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
+	} else {
+	  switch (pc->para.setup.si1) {
+		case 1:	                /* Telephony                                */
+			*p++ = IE_LLC;
+			*p++ = 0x3;	/* Length                                   */
+			*p++ = 0x90;	/* Coding Std. CCITT, 3.1 kHz audio         */
+			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
+			*p++ = 0xa2;	/* u-Law Audio                              */
+			break;
+		case 5:	                /* Datatransmission 64k, BTX                */
+		case 7:	                /* Datatransmission 64k                     */
+		default:
+			*p++ = IE_LLC;
+			*p++ = 0x2;	/* Length                                   */
+			*p++ = 0x88;	/* Coding Std. CCITT, unrestr. dig. Inform. */
+			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
+			break;
+	  }
+	}
+#endif
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+{
+		return;
+}
+	memcpy(skb_put(skb, l), tmp, l);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T303, CC_T303);
+	newl3state(pc, 1);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_call_proc(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3ni1_get_channel_id(pc, skb)) >= 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup answer with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3ni1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else if (1 == pc->state) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	ret = check_infoelements(pc, skb, ie_CALL_PROCEEDING);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 3);
+	L3AddTimer(&pc->timer, T310, CC_T310);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+}
+
+static void
+l3ni1_setup_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3ni1_get_channel_id(pc, skb)) >= 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup answer with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3ni1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	ret = check_infoelements(pc, skb, ie_SETUP_ACKNOWLEDGE);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 2);
+	L3AddTimer(&pc->timer, T304, CC_T304);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+}
+
+static void
+l3ni1_disconnect(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+	int ret;
+	u_char cause = 0;
+
+	StopAllL3Timer(pc);
+	if ((ret = l3ni1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "DISC get_cause ret(%d)", ret);
+		if (ret < 0)
+			cause = 96;
+		else if (ret > 0)
+			cause = 100;
+	} 
+	if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+		l3ni1_parse_facility(pc->st, pc, pc->callref, p);
+	ret = check_infoelements(pc, skb, ie_DISCONNECT);
+	if (ERR_IE_COMPREHENSION == ret)
+		cause = 96;
+	else if ((!cause) && (ERR_IE_UNRECOGNIZED == ret))
+		cause = 99;
+	ret = pc->state;
+	newl3state(pc, 12);
+	if (cause)
+		newl3state(pc, 19);
+       	if (11 != ret)
+		pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+       	else if (!cause)
+		   l3ni1_release_req(pc, pr, NULL);
+	if (cause) {
+		l3ni1_message_cause(pc, MT_RELEASE, cause);
+		L3AddTimer(&pc->timer, T308, CC_T308_1);
+	}
+}
+
+static void
+l3ni1_connect(struct l3_process *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) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);	/* T310 */
+	newl3state(pc, 10);
+	pc->para.chargeinfo = 0;
+	/* here should inserted COLP handling KKe */
+	if (ret)
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+}
+
+static void
+l3ni1_alerting(struct l3_process *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) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);	/* T304 */
+	newl3state(pc, 4);
+	if (ret)
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+}
+
+static void
+l3ni1_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	int bcfound = 0;
+	char tmp[80];
+	struct sk_buff *skb = arg;
+	int id;
+	int err = 0;
+
+	/*
+	 * Bearer Capabilities
+	 */
+	p = skb->data;
+	/* only the first occurence 'll be detected ! */
+	if ((p = findie(p, skb->len, 0x04, 0))) {
+		if ((p[1] < 2) || (p[1] > 11))
+			err = 1;
+		else {
+			pc->para.setup.si2 = 0;
+			switch (p[2] & 0x7f) {
+				case 0x00: /* Speech */
+				case 0x10: /* 3.1 Khz audio */
+					pc->para.setup.si1 = 1;
+					break;
+				case 0x08: /* Unrestricted digital information */
+					pc->para.setup.si1 = 7;
+/* JIM, 05.11.97 I wanna set service indicator 2 */
+#if EXT_BEARER_CAPS
+					pc->para.setup.si2 = DecodeSI2(skb);
+#endif
+					break;
+				case 0x09: /* Restricted digital information */
+					pc->para.setup.si1 = 2;
+					break;
+				case 0x11:
+					/* Unrestr. digital information  with 
+					 * tones/announcements ( or 7 kHz audio
+					 */
+					pc->para.setup.si1 = 3;
+					break;
+				case 0x18: /* Video */
+					pc->para.setup.si1 = 4;
+					break;
+				default:
+					err = 2;
+					break;
+			}
+			switch (p[3] & 0x7f) {
+				case 0x40: /* packed mode */
+					pc->para.setup.si1 = 8;
+					break;
+				case 0x10: /* 64 kbit */
+				case 0x11: /* 2*64 kbit */
+				case 0x13: /* 384 kbit */
+				case 0x15: /* 1536 kbit */
+				case 0x17: /* 1920 kbit */
+					pc->para.moderate = p[3] & 0x7f;
+					break;
+				default:
+					err = 3;
+					break;
+			}
+		}
+		if (pc->debug & L3_DEB_SI)
+			l3_debug(pc->st, "SI=%d, AI=%d",
+				pc->para.setup.si1, pc->para.setup.si2);
+		if (err) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup with wrong bearer(l=%d:%x,%x)",
+					p[1], p[2], p[3]);
+			pc->para.cause = 100;
+			l3ni1_msg_without_setup(pc, pr, NULL);
+			return;
+		}
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup without bearer capabilities");
+		/* ETS 300-104 1.3.3 */
+		pc->para.cause = 96;
+		l3ni1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	/*
+	 * Channel Identification
+	 */
+	if ((id = l3ni1_get_channel_id(pc, skb)) >= 0) {
+		if ((pc->para.bchannel = id)) {
+			if ((3 == id) && (0x10 == pc->para.moderate)) {
+				if (pc->debug & L3_DEB_WARN)
+					l3_debug(pc->st, "setup with wrong chid %x",
+						id);
+				pc->para.cause = 100;
+				l3ni1_msg_without_setup(pc, pr, NULL);
+				return;
+			}
+			bcfound++;
+		} else 
+                   { if (pc->debug & L3_DEB_WARN)
+			 l3_debug(pc->st, "setup without bchannel, call waiting");
+                     bcfound++;
+                   } 
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup with wrong chid ret %d", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	err = check_infoelements(pc, skb, ie_SETUP);
+	if (ERR_IE_COMPREHENSION == err) {
+		pc->para.cause = 96;
+		l3ni1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x70, 0)))
+		iecpy(pc->para.setup.eazmsn, p, 1);
+	else
+		pc->para.setup.eazmsn[0] = 0;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x71, 0))) {
+		/* Called party subaddress */
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.eazmsn, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong called subaddress");
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x6c, 0))) {
+		pc->para.setup.plan = p[2];
+		if (p[2] & 0x80) {
+			iecpy(pc->para.setup.phone, p, 1);
+			pc->para.setup.screen = 0;
+		} else {
+			iecpy(pc->para.setup.phone, p, 2);
+			pc->para.setup.screen = p[3];
+		}
+	} else {
+		pc->para.setup.phone[0] = 0;
+		pc->para.setup.plan = 0;
+		pc->para.setup.screen = 0;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x6d, 0))) {
+		/* Calling party subaddress */
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.phone, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong calling subaddress");
+	}
+	newl3state(pc, 6);
+	if (err) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, err);
+	pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+}
+
+static void
+l3ni1_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16+40];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 16;
+
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	StopAllL3Timer(pc);
+
+	MsgHead(p, pc->callref, MT_DISCONNECT);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+        if (pc->prot.ni1.uus1_data[0])
+	 { *p++ = IE_USER_USER; /* UUS info element */
+           *p++ = strlen(pc->prot.ni1.uus1_data) + 1;
+           *p++ = 0x04; /* IA5 chars */
+           strcpy(p,pc->prot.ni1.uus1_data);
+           p += strlen(pc->prot.ni1.uus1_data);
+           pc->prot.ni1.uus1_data[0] = '\0';   
+         } 
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 11);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T305, CC_T305);
+}
+
+static void
+l3ni1_setup_rsp(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+        if (!pc->para.bchannel) 
+	 { if (pc->debug & L3_DEB_WARN)
+	       l3_debug(pc->st, "D-chan connect for waiting call");
+           l3ni1_disconnect_req(pc, pr, arg);
+           return;
+         }
+	newl3state(pc, 8);
+	if (pc->debug & L3_DEB_WARN)
+		l3_debug(pc->st, "D-chan connect for waiting call");
+	l3ni1_message_plus_chid(pc, MT_CONNECT); /* GE 05/09/00 */ 
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T313, CC_T313);
+}
+
+static void
+l3ni1_connect_ack(struct l3_process *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) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	newl3state(pc, 10);
+	L3DelTimer(&pc->timer);
+	if (ret)
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+}
+
+static void
+l3ni1_reject_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 21;
+
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	newl3state(pc, 0);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_release(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+	int ret, cause=0;
+
+	StopAllL3Timer(pc);
+	if ((ret = l3ni1_get_cause(pc, skb))>0) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "REL get_cause ret(%d)", ret);
+	} else if (ret<0)
+		pc->para.cause = NO_CAUSE;
+	if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+		l3ni1_parse_facility(pc->st, pc, pc->callref, p);
+	}
+	if ((ret<0) && (pc->state != 11))
+		cause = 96;
+	else if (ret>0)
+		cause = 100;
+	ret = check_infoelements(pc, skb, ie_RELEASE);
+	if (ERR_IE_COMPREHENSION == ret)
+		cause = 96;
+	else if ((ERR_IE_UNRECOGNIZED == ret) && (!cause))
+		cause = 99;  
+	if (cause)
+		l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
+	else
+		l3ni1_message(pc, MT_RELEASE_COMPLETE);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	newl3state(pc, 0);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_alert_req(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+	newl3state(pc, 7);
+	if (!pc->prot.ni1.uus1_data[0]) 
+		l3ni1_message(pc, MT_ALERTING);
+	else
+		l3ni1_msg_with_uus(pc, MT_ALERTING); 
+}
+
+static void
+l3ni1_proceed_req(struct l3_process *pc, u_char pr,
+		   void *arg)
+{
+	newl3state(pc, 9);
+	l3ni1_message(pc, MT_CALL_PROCEEDING);
+	pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); 
+}
+
+static void
+l3ni1_setup_ack_req(struct l3_process *pc, u_char pr,
+		   void *arg)
+{
+	newl3state(pc, 25);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T302, CC_T302);
+	l3ni1_message(pc, MT_SETUP_ACKNOWLEDGE);
+}
+
+/********************************************/
+/* deliver a incoming display message to HL */
+/********************************************/
+static void
+l3ni1_deliver_display(struct l3_process *pc, int pr, u_char *infp)
+{       u_char len;
+        isdn_ctrl ic; 
+	struct IsdnCardState *cs;
+        char *p; 
+
+        if (*infp++ != IE_DISPLAY) return;
+        if ((len = *infp++) > 80) return; /* total length <= 82 */
+	if (!pc->chan) return;
+
+	p = ic.parm.display; 
+        while (len--)
+	  *p++ = *infp++;
+	*p = '\0';
+	ic.command = ISDN_STAT_DISPLAY;
+	cs = pc->st->l1.hardware;
+	ic.driver = cs->myid;
+	ic.arg = pc->chan->chan; 
+	cs->iif.statcallb(&ic);
+} /* l3ni1_deliver_display */
+
+
+static void
+l3ni1_progress(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int err = 0;
+	u_char *p;
+
+	if ((p = findie(skb->data, skb->len, IE_PROGRESS, 0))) {
+		if (p[1] != 2) {
+			err = 1;
+			pc->para.cause = 100;
+		} else if (!(p[2] & 0x70)) {
+			switch (p[2]) {
+				case 0x80:
+				case 0x81:
+				case 0x82:
+				case 0x84:
+				case 0x85:
+				case 0x87:
+				case 0x8a:
+					switch (p[3]) {
+						case 0x81:
+						case 0x82:
+						case 0x83:
+						case 0x84:
+						case 0x88:
+							break;
+						default:
+							err = 2;
+							pc->para.cause = 100;
+							break;
+					}
+					break;
+				default:
+					err = 3;
+					pc->para.cause = 100;
+					break;
+			}
+		}
+	} else {
+		pc->para.cause = 96;
+		err = 4;
+	}
+	if (err) {	
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "progress error %d", err);
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	err = check_infoelements(pc, skb, ie_PROGRESS);
+	if (err)
+		l3ni1_std_ie_err(pc, err);
+	if (ERR_IE_COMPREHENSION != err)
+		pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc);
+}
+
+static void
+l3ni1_notify(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int err = 0;
+	u_char *p;
+
+	if ((p = findie(skb->data, skb->len, IE_NOTIFY, 0))) {
+		if (p[1] != 1) {
+			err = 1;
+			pc->para.cause = 100;
+		} else {
+			switch (p[2]) {
+				case 0x80:
+				case 0x81:
+				case 0x82:
+					break;
+				default:
+					pc->para.cause = 100;
+					err = 2;
+					break;
+			}
+		}
+	} else {
+		pc->para.cause = 96;
+		err = 3;
+	}
+	if (err) {	
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "notify error %d", err);
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	err = check_infoelements(pc, skb, ie_NOTIFY);
+	if (err)
+		l3ni1_std_ie_err(pc, err);
+	if (ERR_IE_COMPREHENSION != err)
+		pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc);
+}
+
+static void
+l3ni1_status_enq(struct l3_process *pc, u_char pr, void *arg)
+{
+	int ret;
+	struct sk_buff *skb = arg;
+
+	ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY);
+	l3ni1_std_ie_err(pc, ret);
+	pc->para.cause = 30; /* response to STATUS_ENQUIRY */
+        l3ni1_status_send(pc, pr, NULL);
+}
+
+static void
+l3ni1_information(struct l3_process *pc, u_char pr, void *arg)
+{
+	int ret;
+	struct sk_buff *skb = arg;
+	u_char *p;
+	char tmp[32];
+
+	ret = check_infoelements(pc, skb, ie_INFORMATION);
+	if (ret)
+		l3ni1_std_ie_err(pc, ret);
+	if (pc->state == 25) { /* overlap receiving */
+		L3DelTimer(&pc->timer);
+		p = skb->data;
+		if ((p = findie(p, skb->len, 0x70, 0))) {
+			iecpy(tmp, p, 1);
+			strcat(pc->para.setup.eazmsn, tmp);
+			pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+		}
+		L3AddTimer(&pc->timer, T302, CC_T302);
+	}
+}
+
+/******************************/
+/* handle deflection requests */
+/******************************/
+static void l3ni1_redir_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+        u_char *subp;
+        u_char len_phone = 0;
+        u_char len_sub = 0;
+	int l; 
+
+
+        strcpy(pc->prot.ni1.uus1_data,pc->chan->setup.eazmsn); /* copy uus element if available */
+        if (!pc->chan->setup.phone[0])
+          { pc->para.cause = -1;
+            l3ni1_disconnect_req(pc,pr,arg); /* disconnect immediately */
+            return;
+          } /* only uus */
+ 
+        if (pc->prot.ni1.invoke_id) 
+          free_invoke_id(pc->st,pc->prot.ni1.invoke_id);
+ 
+        if (!(pc->prot.ni1.invoke_id = new_invoke_id(pc->st))) 
+          return;
+
+        MsgHead(p, pc->callref, MT_FACILITY);
+
+        for (subp = pc->chan->setup.phone; (*subp) && (*subp != '.'); subp++) len_phone++; /* len of phone number */
+        if (*subp++ == '.') len_sub = strlen(subp) + 2; /* length including info subaddress element */ 
+
+	*p++ = 0x1c;   /* Facility info element */
+        *p++ = len_phone + len_sub + 2 + 2 + 8 + 3 + 3; /* length of element */
+        *p++ = 0x91;  /* remote operations protocol */
+        *p++ = 0xa1;  /* invoke component */
+	  
+        *p++ = len_phone + len_sub + 2 + 2 + 8 + 3; /* length of data */
+        *p++ = 0x02;  /* invoke id tag, integer */
+	*p++ = 0x01;  /* length */
+        *p++ = pc->prot.ni1.invoke_id;  /* invoke id */ 
+        *p++ = 0x02;  /* operation value tag, integer */
+	*p++ = 0x01;  /* length */
+        *p++ = 0x0D;  /* Call Deflect */
+	  
+        *p++ = 0x30;  /* sequence phone number */
+        *p++ = len_phone + 2 + 2 + 3 + len_sub; /* length */
+	  
+        *p++ = 0x30;  /* Deflected to UserNumber */
+        *p++ = len_phone+2+len_sub; /* length */
+        *p++ = 0x80; /* NumberDigits */
+	*p++ = len_phone; /* length */
+        for (l = 0; l < len_phone; l++)
+	 *p++ = pc->chan->setup.phone[l];
+
+        if (len_sub)
+	  { *p++ = 0x04; /* called party subaddress */
+            *p++ = len_sub - 2;
+            while (*subp) *p++ = *subp++;
+          }
+
+        *p++ = 0x01; /* screening identifier */
+        *p++ = 0x01;
+        *p++ = pc->chan->setup.screen;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l))) return;
+	memcpy(skb_put(skb, l), tmp, l);
+
+        l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3ni1_redir_req */
+
+/********************************************/
+/* handle deflection request in early state */
+/********************************************/
+static void l3ni1_redir_req_early(struct l3_process *pc, u_char pr, void *arg)
+{
+  l3ni1_proceed_req(pc,pr,arg);
+  l3ni1_redir_req(pc,pr,arg);
+} /* l3ni1_redir_req_early */
+
+/***********************************************/
+/* handle special commands for this protocol.  */
+/* Examples are call independant services like */
+/* remote operations with dummy  callref.      */
+/***********************************************/
+static int l3ni1_cmd_global(struct PStack *st, isdn_ctrl *ic)
+{ u_char id;
+  u_char temp[265];
+  u_char *p = temp;
+  int i, l, proc_len; 
+  struct sk_buff *skb;
+  struct l3_process *pc = NULL;
+
+  switch (ic->arg)
+   { case NI1_CMD_INVOKE:
+       if (ic->parm.ni1_io.datalen < 0) return(-2); /* invalid parameter */ 
+
+       for (proc_len = 1, i = ic->parm.ni1_io.proc >> 8; i; i++) 
+         i = i >> 8; /* add one byte */    
+       l = ic->parm.ni1_io.datalen + proc_len + 8; /* length excluding ie header */
+       if (l > 255) 
+         return(-2); /* too long */
+
+       if (!(id = new_invoke_id(st))) 
+         return(0); /* first get a invoke id -> return if no available */
+       
+       i = -1; 
+       MsgHead(p, i, MT_FACILITY); /* build message head */
+       *p++ = 0x1C; /* Facility IE */
+       *p++ = l; /* length of ie */
+       *p++ = 0x91; /* remote operations */
+       *p++ = 0xA1; /* invoke */
+       *p++ = l - 3; /* length of invoke */
+       *p++ = 0x02; /* invoke id tag */
+       *p++ = 0x01; /* length is 1 */
+       *p++ = id; /* invoke id */
+       *p++ = 0x02; /* operation */
+       *p++ = proc_len; /* length of operation */
+       
+       for (i = proc_len; i; i--)
+         *p++ = (ic->parm.ni1_io.proc >> (i-1)) & 0xFF;
+       memcpy(p, ic->parm.ni1_io.data, ic->parm.ni1_io.datalen); /* copy data */
+       l = (p - temp) + ic->parm.ni1_io.datalen; /* total length */         
+
+       if (ic->parm.ni1_io.timeout > 0)
+        if (!(pc = ni1_new_l3_process(st, -1)))
+          { free_invoke_id(st, id);
+            return(-2);
+          } 
+       pc->prot.ni1.ll_id = ic->parm.ni1_io.ll_id; /* remember id */ 
+       pc->prot.ni1.proc = ic->parm.ni1_io.proc; /* and procedure */
+
+       if (!(skb = l3_alloc_skb(l))) 
+         { free_invoke_id(st, id);
+           if (pc) ni1_release_l3_process(pc);
+           return(-2);
+         }
+       memcpy(skb_put(skb, l), temp, l);
+       
+       if (pc)
+        { pc->prot.ni1.invoke_id = id; /* remember id */
+          L3AddTimer(&pc->timer, ic->parm.ni1_io.timeout, CC_TNI1_IO | REQUEST);
+        }
+       
+       l3_msg(st, DL_DATA | REQUEST, skb);
+       ic->parm.ni1_io.hl_id = id; /* return id */
+       return(0);
+
+     case NI1_CMD_INVOKE_ABORT:
+       if ((pc = l3ni1_search_dummy_proc(st, ic->parm.ni1_io.hl_id)))
+	{ L3DelTimer(&pc->timer); /* remove timer */
+          ni1_release_l3_process(pc);
+          return(0); 
+        } 
+       else
+	{ l3_debug(st, "l3ni1_cmd_global abort unknown id");
+          return(-2);
+        } 
+       break;
+    
+     default: 
+       l3_debug(st, "l3ni1_cmd_global unknown cmd 0x%lx", ic->arg);
+       return(-1);  
+   } /* switch ic-> arg */
+  return(-1);
+} /* l3ni1_cmd_global */
+
+static void 
+l3ni1_io_timer(struct l3_process *pc)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs = pc->st->l1.hardware;
+
+  L3DelTimer(&pc->timer); /* remove timer */
+
+  ic.driver = cs->myid;
+  ic.command = ISDN_STAT_PROT;
+  ic.arg = NI1_STAT_INVOKE_ERR;
+  ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id;
+  ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id;
+  ic.parm.ni1_io.proc = pc->prot.ni1.proc;
+  ic.parm.ni1_io.timeout= -1;
+  ic.parm.ni1_io.datalen = 0;
+  ic.parm.ni1_io.data = NULL;
+  free_invoke_id(pc->st, pc->prot.ni1.invoke_id);
+  pc->prot.ni1.invoke_id = 0; /* reset id */
+
+  cs->iif.statcallb(&ic);
+
+  ni1_release_l3_process(pc); 
+} /* l3ni1_io_timer */
+
+static void
+l3ni1_release_ind(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int callState = 0;
+	p = skb->data;
+
+	if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
+		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
+		 */
+		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		newl3state(pc, 0);
+		ni1_release_l3_process(pc);
+	} else {
+		pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
+	}
+}
+
+static void
+l3ni1_dummy(struct l3_process *pc, u_char pr, void *arg)
+{
+}
+
+static void
+l3ni1_t302(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 28; /* invalid number */
+	l3ni1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3ni1_t303(struct l3_process *pc, u_char pr, void *arg)
+{
+	if (pc->N303 > 0) {
+		pc->N303--;
+		L3DelTimer(&pc->timer);
+		l3ni1_setup_req(pc, pr, arg);
+	} else {
+		L3DelTimer(&pc->timer);
+		l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, 102);
+		pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
+		ni1_release_l3_process(pc);
+	}
+}
+
+static void
+l3ni1_t304(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3ni1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+
+}
+
+static void
+l3ni1_t305(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+	u_char cause = 16;
+
+	L3DelTimer(&pc->timer);
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	MsgHead(p, pc->callref, MT_RELEASE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 19);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3ni1_t310(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3ni1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3ni1_t313(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3ni1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+}
+
+static void
+l3ni1_t308_1(struct l3_process *pc, u_char pr, void *arg)
+{
+	newl3state(pc, 19);
+	L3DelTimer(&pc->timer);
+	l3ni1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_2);
+}
+
+static void
+l3ni1_t308_2(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_t318(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 102;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	newl3state(pc, 19);
+	l3ni1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3ni1_t319(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 102;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	newl3state(pc, 10);
+}
+
+static void
+l3ni1_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_status(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int ret; 
+	u_char cause = 0, callState = 0;
+	
+	if ((ret = l3ni1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "STATUS get_cause ret(%d)",ret);
+		if (ret < 0)
+			cause = 96;
+		else if (ret > 0)
+			cause = 100;
+	}
+	if ((p = findie(skb->data, skb->len, IE_CALL_STATE, 0))) {
+		p++;
+		if (1 == *p++) {
+			callState = *p;
+			if (!ie_in_set(pc, *p, l3_valid_states))
+				cause = 100;
+		} else
+			cause = 100;
+	} else
+		cause = 96;
+	if (!cause) { /*  no error before */
+		ret = check_infoelements(pc, skb, ie_STATUS);
+		if (ERR_IE_COMPREHENSION == ret)
+			cause = 96;
+		else if (ERR_IE_UNRECOGNIZED == ret)
+			cause = 99;
+	}
+	if (cause) {
+		u_char tmp;
+		
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "STATUS error(%d/%d)",ret,cause);
+		tmp = pc->para.cause;
+		pc->para.cause = cause;
+		l3ni1_status_send(pc, 0, NULL);
+		if (cause == 99)
+			pc->para.cause = tmp;
+		else
+			return;
+	}
+	cause = pc->para.cause;
+	if (((cause & 0x7f) == 111) && (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
+		 */
+		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		newl3state(pc, 0);
+		ni1_release_l3_process(pc);
+	}
+}
+
+static void
+l3ni1_facility(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+	
+	ret = check_infoelements(pc, skb, ie_FACILITY);
+	l3ni1_std_ie_err(pc, ret);
+ 	  {
+		u_char *p;
+		if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+			l3ni1_parse_facility(pc->st, pc, pc->callref, p);
+	}
+}
+
+static void
+l3ni1_suspend_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->chan->setup.phone;
+
+	MsgHead(p, pc->callref, MT_SUSPEND);
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = IE_CALL_ID;
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else if (l) {
+		l3_debug(pc->st, "SUS wrong CALL_ID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 15);
+	L3AddTimer(&pc->timer, T319, CC_T319);
+}
+
+static void
+l3ni1_suspend_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 0);
+	pc->para.cause = NO_CAUSE;
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+	/* We don't handle suspend_ack for IE errors now */
+	if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE)))
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "SUSPACK check ie(%d)",ret);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_suspend_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3ni1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "SUSP_REJ get_cause ret(%d)",ret);
+		if (ret < 0) 
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	newl3state(pc, 10);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+}
+
+static void
+l3ni1_resume_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->para.setup.phone;
+
+	MsgHead(p, pc->callref, MT_RESUME);
+
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = IE_CALL_ID;
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else if (l) {
+		l3_debug(pc->st, "RES wrong CALL_ID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 17);
+	L3AddTimer(&pc->timer, T318, CC_T318);
+}
+
+static void
+l3ni1_resume_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3ni1_get_channel_id(pc, skb)) > 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "resume ack with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3ni1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else if (1 == pc->state) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "resume ack without chid (ret %d)", id);
+		pc->para.cause = 96;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+	newl3state(pc, 10);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+}
+
+static void
+l3ni1_resume_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3ni1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "RES_REJ get_cause ret(%d)",ret);
+		if (ret < 0) 
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_RESUME_REJECT);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	newl3state(pc, 0);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_global_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[32];
+	u_char *p;
+	u_char ri, ch = 0, chan = 0;
+	int l;
+	struct sk_buff *skb = arg;
+	struct l3_process *up;
+
+	newl3state(pc, 2);
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) {
+		ri = p[2];
+		l3_debug(pc->st, "Restart %x", ri);
+	} else {
+		l3_debug(pc->st, "Restart without restart IE");
+		ri = 0x86;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		chan = p[2] & 3;
+		ch = p[2];
+		if (pc->st->l3.debug)
+			l3_debug(pc->st, "Restart for channel %d", chan);
+	}
+	newl3state(pc, 2);
+	up = pc->st->l3.proc;
+	while (up) {
+		if ((ri & 7) == 7)
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+		else if (up->para.bchannel == chan)
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+		
+		up = up->next;
+	}
+	p = tmp;
+	MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE);
+	if (chan) {
+		*p++ = IE_CHANNEL_ID;
+		*p++ = 1;
+		*p++ = ch | 0x80;
+	}
+	*p++ = 0x79;		/* RESTART Ind */
+	*p++ = 1;
+	*p++ = ri;
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 0);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_dl_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+        pc->para.cause = 0x29;          /* Temporary failure */
+        pc->para.loc = 0;
+        l3ni1_disconnect_req(pc, pr, NULL);
+        pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3ni1_dl_release(struct l3_process *pc, u_char pr, void *arg)
+{
+        newl3state(pc, 0);
+        pc->para.cause = 0x1b;          /* Destination out of order */
+        pc->para.loc = 0;
+        pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+        release_l3_process(pc);
+}
+
+static void
+l3ni1_dl_reestablish(struct l3_process *pc, u_char pr, void *arg)
+{
+        L3DelTimer(&pc->timer);
+        L3AddTimer(&pc->timer, T309, CC_T309);
+        l3_msg(pc->st, DL_ESTABLISH | REQUEST, NULL);
+}
+ 
+static void
+l3ni1_dl_reest_status(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+ 
+ 	pc->para.cause = 0x1F; /* normal, unspecified */
+	l3ni1_status_send(pc, 0, NULL);
+}
+
+static void l3ni1_SendSpid( struct l3_process *pc, u_char pr, struct sk_buff *skb, int iNewState )
+{
+	u_char         * p;
+	char           * pSPID;
+	struct Channel * pChan = pc->st->lli.userdata;
+	int              l;
+
+	if ( skb )
+		dev_kfree_skb( skb);
+
+	if ( !( pSPID = strchr( pChan->setup.eazmsn, ':' ) ) )
+	{
+		printk( KERN_ERR "SPID not supplied in EAZMSN %s\n", pChan->setup.eazmsn );
+		newl3state( pc, 0 );
+		pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL );
+		return;
+	}
+
+	l = strlen( ++pSPID );
+	if ( !( skb = l3_alloc_skb( 5+l ) ) )
+	{
+		printk( KERN_ERR "HiSax can't get memory to send SPID\n" );
+		return;
+	}
+
+	p = skb_put( skb, 5 );
+	*p++ = PROTO_DIS_EURO;
+	*p++ = 0;
+	*p++ = MT_INFORMATION;
+	*p++ = IE_SPID;
+	*p++ = l;
+
+	memcpy( skb_put( skb, l ), pSPID, l );
+
+	newl3state( pc, iNewState );
+
+	L3DelTimer( &pc->timer );
+	L3AddTimer( &pc->timer, TSPID, CC_TSPID );
+
+	pc->st->l3.l3l2( pc->st, DL_DATA | REQUEST, skb );
+}
+
+static void l3ni1_spid_send( struct l3_process *pc, u_char pr, void *arg )
+{
+	l3ni1_SendSpid( pc, pr, arg, 20 );
+}
+
+void l3ni1_spid_epid( struct l3_process *pc, u_char pr, void *arg )
+{
+	struct sk_buff *skb = arg;
+
+	if ( skb->data[ 1 ] == 0 )
+		if ( skb->data[ 3 ] == IE_ENDPOINT_ID )
+		{
+			L3DelTimer( &pc->timer );
+			newl3state( pc, 0 );
+			l3_msg( pc->st, DL_ESTABLISH | CONFIRM, NULL );
+		}
+	dev_kfree_skb( skb);
+}
+
+static void l3ni1_spid_tout( struct l3_process *pc, u_char pr, void *arg )
+{
+	if ( pc->state < 22 )
+		l3ni1_SendSpid( pc, pr, arg, pc->state+1 );
+	else
+	{
+		L3DelTimer( &pc->timer );
+		dev_kfree_skb( arg);
+
+		printk( KERN_ERR "SPID not accepted\n" );
+		newl3state( pc, 0 );
+		pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL );
+	}
+}
+
+/* *INDENT-OFF* */
+static struct stateentry downstatelist[] =
+{
+	{SBIT(0),
+	 CC_SETUP | REQUEST, l3ni1_setup_req},
+	{SBIT(0),
+	 CC_RESUME | REQUEST, l3ni1_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, l3ni1_disconnect_req},
+	{SBIT(12),
+	 CC_RELEASE | REQUEST, l3ni1_release_req},
+	{ALL_STATES,
+	 CC_RESTART | REQUEST, l3ni1_restart},
+	{SBIT(6) | SBIT(25),
+	 CC_IGNORE | REQUEST, l3ni1_reset},
+	{SBIT(6) | SBIT(25),
+	 CC_REJECT | REQUEST, l3ni1_reject_req},
+	{SBIT(6) | SBIT(25),
+	 CC_PROCEED_SEND | REQUEST, l3ni1_proceed_req},
+	{SBIT(6),
+	 CC_MORE_INFO | REQUEST, l3ni1_setup_ack_req},
+	{SBIT(25),
+	 CC_MORE_INFO | REQUEST, l3ni1_dummy},
+	{SBIT(6) | SBIT(9) | SBIT(25),
+	 CC_ALERTING | REQUEST, l3ni1_alert_req},
+	{SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25),
+	 CC_SETUP | RESPONSE, l3ni1_setup_rsp},
+	{SBIT(10),
+	 CC_SUSPEND | REQUEST, l3ni1_suspend_req},
+        {SBIT(7) | SBIT(9) | SBIT(25),
+         CC_REDIR | REQUEST, l3ni1_redir_req},
+        {SBIT(6),
+         CC_REDIR | REQUEST, l3ni1_redir_req_early},
+        {SBIT(9) | SBIT(25),
+         CC_DISCONNECT | REQUEST, l3ni1_disconnect_req},
+	{SBIT(25),
+	 CC_T302, l3ni1_t302},
+	{SBIT(1),
+	 CC_T303, l3ni1_t303},
+	{SBIT(2),
+	 CC_T304, l3ni1_t304},
+	{SBIT(3),
+	 CC_T310, l3ni1_t310},
+	{SBIT(8),
+	 CC_T313, l3ni1_t313},
+	{SBIT(11),
+	 CC_T305, l3ni1_t305},
+	{SBIT(15),
+	 CC_T319, l3ni1_t319},
+	{SBIT(17),
+	 CC_T318, l3ni1_t318},
+	{SBIT(19),
+	 CC_T308_1, l3ni1_t308_1},
+	{SBIT(19),
+	 CC_T308_2, l3ni1_t308_2},
+	{SBIT(10),
+	 CC_T309, l3ni1_dl_release},
+	{ SBIT( 20 ) | SBIT( 21 ) | SBIT( 22 ),
+	 CC_TSPID, l3ni1_spid_tout },
+};
+
+#define DOWNSLLEN \
+	(sizeof(downstatelist) / sizeof(struct stateentry))
+
+static struct stateentry datastatelist[] =
+{
+	{ALL_STATES,
+	 MT_STATUS_ENQUIRY, l3ni1_status_enq},
+	{ALL_STATES,
+	 MT_FACILITY, l3ni1_facility},
+	{SBIT(19),
+	 MT_STATUS, l3ni1_release_ind},
+	{ALL_STATES,
+	 MT_STATUS, l3ni1_status},
+	{SBIT(0),
+	 MT_SETUP, l3ni1_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, l3ni1_dummy},
+	{SBIT(1) | SBIT(2),
+	 MT_CALL_PROCEEDING, l3ni1_call_proc},
+	{SBIT(1),
+	 MT_SETUP_ACKNOWLEDGE, l3ni1_setup_ack},
+	{SBIT(2) | SBIT(3),
+	 MT_ALERTING, l3ni1_alerting},
+	{SBIT(2) | SBIT(3),
+	 MT_PROGRESS, l3ni1_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, l3ni1_information},
+	{SBIT(10) | SBIT(11) | SBIT(15),
+	 MT_NOTIFY, l3ni1_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, l3ni1_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, l3ni1_release},
+	{SBIT(19),  MT_RELEASE, l3ni1_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, l3ni1_disconnect},
+	{SBIT(19),
+	 MT_DISCONNECT, l3ni1_dummy},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_CONNECT, l3ni1_connect},
+	{SBIT(8),
+	 MT_CONNECT_ACKNOWLEDGE, l3ni1_connect_ack},
+	{SBIT(15),
+	 MT_SUSPEND_ACKNOWLEDGE, l3ni1_suspend_ack},
+	{SBIT(15),
+	 MT_SUSPEND_REJECT, l3ni1_suspend_rej},
+	{SBIT(17),
+	 MT_RESUME_ACKNOWLEDGE, l3ni1_resume_ack},
+	{SBIT(17),
+	 MT_RESUME_REJECT, l3ni1_resume_rej},
+};
+
+#define DATASLLEN \
+	(sizeof(datastatelist) / sizeof(struct stateentry))
+
+static struct stateentry globalmes_list[] =
+{
+	{ALL_STATES,
+	 MT_STATUS, l3ni1_status},
+	{SBIT(0),
+	 MT_RESTART, l3ni1_global_restart},
+/*	{SBIT(1),
+	 MT_RESTART_ACKNOWLEDGE, l3ni1_restart_ack},
+*/
+	{ SBIT( 0 ), MT_DL_ESTABLISHED, l3ni1_spid_send },
+	{ SBIT( 20 ) | SBIT( 21 ) | SBIT( 22 ), MT_INFORMATION, l3ni1_spid_epid },
+};
+#define GLOBALM_LEN \
+	(sizeof(globalmes_list) / sizeof(struct stateentry))
+
+static struct stateentry manstatelist[] =
+{
+        {SBIT(2),
+         DL_ESTABLISH | INDICATION, l3ni1_dl_reset},
+        {SBIT(10),
+         DL_ESTABLISH | CONFIRM, l3ni1_dl_reest_status},
+        {SBIT(10),
+         DL_RELEASE | INDICATION, l3ni1_dl_reestablish},
+        {ALL_STATES,
+         DL_RELEASE | INDICATION, l3ni1_dl_release},
+};
+
+#define MANSLLEN \
+        (sizeof(manstatelist) / sizeof(struct stateentry))
+/* *INDENT-ON* */
+
+
+static void
+global_handler(struct PStack *st, int mt, struct sk_buff *skb)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	int i;
+	struct l3_process *proc = st->l3.global;
+
+	if ( skb )	
+		proc->callref = skb->data[2]; /* cr flag */
+	else
+		proc->callref = 0;
+	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 (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1 global state %d mt %x unhandled",
+				proc->state, mt);
+		}
+		MsgHead(p, proc->callref, MT_STATUS);
+		*p++ = IE_CAUSE;
+		*p++ = 0x2;
+		*p++ = 0x80;
+		*p++ = 81 |0x80;	/* invalid cr */
+		*p++ = 0x14;		/* CallState */
+		*p++ = 0x1;
+		*p++ = proc->state & 0x3f;
+		l = p - tmp;
+		if (!(skb = l3_alloc_skb(l)))
+			return;
+		memcpy(skb_put(skb, l), tmp, l);
+		l3_msg(proc->st, DL_DATA | REQUEST, skb);
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1 global %d mt %x",
+				proc->state, mt);
+		}
+		globalmes_list[i].rout(proc, mt, skb);
+	}
+}
+
+static void
+ni1up(struct PStack *st, int pr, void *arg)
+{
+	int i, mt, cr, cause, callState;
+	char *ptr;
+	u_char *p;
+	struct sk_buff *skb = arg;
+	struct l3_process *proc;
+
+	switch (pr) {
+		case (DL_DATA | INDICATION):
+		case (DL_UNIT_DATA | INDICATION):
+			break;
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			l3_msg(st, pr, arg);
+			return;
+			break;
+
+		case (DL_ESTABLISH | CONFIRM):
+			global_handler( st, MT_DL_ESTABLISHED, NULL );
+			return;
+
+		default:
+			printk(KERN_ERR "HiSax ni1up unknown pr=%04x\n", pr);
+			return;
+	}
+	if (skb->len < 3) {
+		l3_debug(st, "ni1up frame too short(%d)", skb->len);
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	if (skb->data[0] != PROTO_DIS_EURO) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			l3_debug(st, "ni1up%sunexpected discriminator %x message len %d",
+				 (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				 skb->data[0], skb->len);
+		}
+		dev_kfree_skb(skb);
+		return;
+	}
+	cr = getcallref(skb->data);
+	if (skb->len < ((skb->data[1] & 0x0f) + 3)) {
+		l3_debug(st, "ni1up frame too short(%d)", skb->len);
+		dev_kfree_skb(skb);
+		return;
+	}
+	mt = skb->data[skb->data[1] + 2];
+	if (st->l3.debug & L3_DEB_STATE)
+		l3_debug(st, "ni1up cr %d", cr);
+	if (cr == -2) {  /* wrong Callref */
+		if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "ni1up wrong Callref");
+		dev_kfree_skb(skb);
+		return;
+	} else if (cr == -1) {	/* Dummy Callref */
+		if (mt == MT_FACILITY)
+		{
+			if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+				l3ni1_parse_facility(st, NULL, 
+					(pr == (DL_DATA | INDICATION)) ? -1 : -2, p); 
+				dev_kfree_skb(skb);
+				return;  
+			}
+		}
+		else
+		{
+			global_handler(st, mt, skb);
+			return;
+		}
+				
+		if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "ni1up dummy Callref (no facility msg or ie)");
+		dev_kfree_skb(skb);
+		return;
+	} else if ((((skb->data[1] & 0x0f) == 1) && (0==(cr & 0x7f))) ||
+		(((skb->data[1] & 0x0f) == 2) && (0==(cr & 0x7fff)))) {	/* Global CallRef */
+		if (st->l3.debug & L3_DEB_STATE)
+			l3_debug(st, "ni1up Global CallRef");
+		global_handler(st, mt, skb);
+		dev_kfree_skb(skb);
+		return;
+	} else if (!(proc = getl3proc(st, cr))) {
+		/* No transaction process exist, that means no call with
+		 * this callreference is active
+		 */
+		if (mt == MT_SETUP) {
+			/* Setup creates a new transaction process */
+			if (skb->data[2] & 0x80) {
+				/* Setup with wrong CREF flag */
+				if (st->l3.debug & L3_DEB_STATE)
+					l3_debug(st, "ni1up wrong CRef flag");
+				dev_kfree_skb(skb);
+				return;
+			}
+			if (!(proc = ni1_new_l3_process(st, cr))) {
+				/* 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;
+			}
+		} else if (mt == MT_STATUS) {
+			cause = 0;
+			if ((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				cause = *ptr & 0x7f;
+			}
+			callState = 0;
+			if ((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				callState = *ptr;
+			}
+			/* 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 = ni1_new_l3_process(st, cr))) {
+					proc->para.cause = 101;
+					l3ni1_msg_without_setup(proc, 0, NULL);
+				}
+			}
+			dev_kfree_skb(skb);
+			return;
+		} else if (mt == MT_RELEASE_COMPLETE) {
+			dev_kfree_skb(skb);
+			return;
+		} 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 */
+			dev_kfree_skb(skb);
+			if ((proc = ni1_new_l3_process(st, cr))) {
+				proc->para.cause = 81;
+				l3ni1_msg_without_setup(proc, 0, NULL);
+			}
+			return;
+		}
+	}
+	if (l3ni1_check_messagetype_validity(proc, mt, skb)) {
+		dev_kfree_skb(skb);
+		return;
+	}
+	if ((p = findie(skb->data, skb->len, IE_DISPLAY, 0)) != NULL) 
+	  l3ni1_deliver_display(proc, pr, p); /* Display IE included */
+	for (i = 0; i < DATASLLEN; i++)
+		if ((mt == datastatelist[i].primitive) &&
+		    ((1 << proc->state) & datastatelist[i].state))
+			break;
+	if (i == DATASLLEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1up%sstate %d mt %#x unhandled",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				proc->state, mt);
+		}
+		if ((MT_RELEASE_COMPLETE != mt) && (MT_RELEASE != mt)) {
+			proc->para.cause = 101;
+			l3ni1_status_send(proc, pr, skb);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1up%sstate %d mt %x",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				proc->state, mt);
+		}
+		datastatelist[i].rout(proc, pr, skb);
+	}
+	dev_kfree_skb(skb);
+	return;
+}
+
+static void
+ni1down(struct PStack *st, int pr, void *arg)
+{
+	int i, cr;
+	struct l3_process *proc;
+	struct Channel *chan;
+
+	if ((DL_ESTABLISH | REQUEST) == pr) {
+		l3_msg(st, pr, NULL);
+		return;
+	} else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) {
+		chan = arg;
+		cr = newcallref();
+		cr |= 0x80;
+		if ((proc = ni1_new_l3_process(st, cr))) {
+			proc->chan = chan;
+			chan->proc = proc;
+			memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm));
+			proc->callref = cr;
+		}
+	} else {
+		proc = arg;
+	}
+	if (!proc) {
+		printk(KERN_ERR "HiSax ni1down without proc pr=%04x\n", pr);
+		return;
+	}
+
+	if ( pr == (CC_TNI1_IO | REQUEST)) {
+		l3ni1_io_timer(proc); /* timer expires */ 
+		return;
+	}  
+
+	for (i = 0; i < DOWNSLLEN; i++)
+		if ((pr == downstatelist[i].primitive) &&
+		    ((1 << proc->state) & downstatelist[i].state))
+			break;
+	if (i == DOWNSLLEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1down state %d prim %#x unhandled",
+				proc->state, pr);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1down state %d prim %#x",
+				proc->state, pr);
+		}
+		downstatelist[i].rout(proc, pr, arg);
+	}
+}
+
+static void
+ni1man(struct PStack *st, int pr, void *arg)
+{
+        int i;
+        struct l3_process *proc = arg;
+
+        if (!proc) {
+                printk(KERN_ERR "HiSax ni1man without proc pr=%04x\n", pr);
+                return;
+        }
+        for (i = 0; i < MANSLLEN; i++)
+                if ((pr == manstatelist[i].primitive) &&
+                    ((1 << proc->state) & manstatelist[i].state))
+                        break;
+        if (i == MANSLLEN) {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d ni1man state %d prim %#x unhandled",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+        } else {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d ni1man state %d prim %#x",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+                manstatelist[i].rout(proc, pr, arg);
+        }
+}
+ 
+void
+setstack_ni1(struct PStack *st)
+{
+	char tmp[64];
+	int i;
+
+	st->lli.l4l3 = ni1down;
+	st->lli.l4l3_proto = l3ni1_cmd_global;
+	st->l2.l2l3 = ni1up;
+	st->l3.l3ml3 = ni1man;
+	st->l3.N303 = 1;
+	st->prot.ni1.last_invoke_id = 0;
+	st->prot.ni1.invoke_used[0] = 1; /* Bit 0 must always be set to 1 */
+	i = 1;
+	while (i < 32) 
+		st->prot.ni1.invoke_used[i++] = 0;   
+
+	if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+		printk(KERN_ERR "HiSax can't get memory for ni1 global CR\n");
+	} else {
+		st->l3.global->state = 0;
+		st->l3.global->callref = 0;
+		st->l3.global->next = NULL;
+		st->l3.global->debug = L3_DEB_WARN;
+		st->l3.global->st = st;
+		st->l3.global->N303 = 1;
+		st->l3.global->prot.ni1.invoke_id = 0; 
+
+		L3InitTimer(st->l3.global, &st->l3.global->timer);
+	}
+	strcpy(tmp, ni1_revision);
+	printk(KERN_INFO "HiSax: National ISDN-1 Rev. %s\n", HiSax_getrev(tmp));
+}
diff --git a/drivers/isdn/hisax/l3ni1.h b/drivers/isdn/hisax/l3ni1.h
new file mode 100644
index 000000000000..4066da2fe5a2
--- /dev/null
+++ b/drivers/isdn/hisax/l3ni1.h
@@ -0,0 +1,136 @@
+/* $Id: l3ni1.h,v 2.3.6.2 2001/09/23 22:24:50 kai Exp $
+ *
+ * NI1 D-channel protocol
+ *
+ * Author       Matt Henderson & Guy Ellis
+ * Copyright    by Traverse Technologies Pty Ltd, www.travers.com.au
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * 2000.6.6 Initial implementation of routines for US NI1 
+ * Layer 3 protocol based on the EURO/DSS1 D-channel protocol 
+ * driver written by Karsten Keil et al.  Thanks also for the 
+ * code provided by Ragnar Paulson.
+ *
+ */
+
+#ifndef l3ni1_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 TSPID	5000 /* was 2000 - Guy Ellis */
+
+/*
+ * 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 MT_DL_ESTABLISHED	0xfe
+
+#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_SPID		0x3a
+#define IE_ENDPOINT_ID	0x3b
+#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
+
+#else /* only l3ni1_process */
+
+/* l3ni1 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 */
+    u8 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 */
+  } ni1_proc_priv;
+
+/* l3dni1 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 */
+  } ni1_stk_priv;        
+
+#endif /* only l3dni1_process */
diff --git a/drivers/isdn/hisax/lmgr.c b/drivers/isdn/hisax/lmgr.c
new file mode 100644
index 000000000000..d4f86d654de0
--- /dev/null
+++ b/drivers/isdn/hisax/lmgr.c
@@ -0,0 +1,50 @@
+/* $Id: lmgr.c,v 1.7.6.2 2001/09/23 22:24:50 kai Exp $
+ *
+ * Layermanagement module
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "hisax.h"
+
+static void
+error_handling_dchan(struct PStack *st, int Error)
+{
+	switch (Error) {
+		case 'C':
+		case 'D':
+		case 'G':
+		case 'H':
+			st->l2.l2tei(st, MDL_ERROR | REQUEST, NULL);
+			break;
+	}
+}
+
+static void
+hisax_manager(struct PStack *st, int pr, void *arg)
+{
+	long Code;
+
+	switch (pr) {
+		case (MDL_ERROR | INDICATION):
+			Code = (long) arg;
+			HiSax_putstatus(st->l1.hardware, "manager: MDL_ERROR",
+				" %c %s", (char)Code, 
+				test_bit(FLG_LAPD, &st->l2.flag) ?
+				"D-channel" : "B-channel");
+			if (test_bit(FLG_LAPD, &st->l2.flag))
+				error_handling_dchan(st, Code);
+			break;
+	}
+}
+
+void
+setstack_manager(struct PStack *st)
+{
+	st->ma.layer = hisax_manager;
+}
diff --git a/drivers/isdn/hisax/mic.c b/drivers/isdn/hisax/mic.c
new file mode 100644
index 000000000000..3ac4484a4886
--- /dev/null
+++ b/drivers/isdn/hisax/mic.c
@@ -0,0 +1,239 @@
+/* $Id: mic.c,v 1.12.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for mic cards
+ *
+ * Author       Stephan von Krawczynski
+ * Copyright    by Stephan von Krawczynski <skraw@ithnet.com>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *mic_revision = "$Revision: 1.12.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define MIC_ISAC	2
+#define MIC_HSCX	1
+#define MIC_ADR		7
+
+/* CARD_ADR (Write) */
+#define MIC_RESET      0x3	/* same as DOS driver */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.mic.adr, cs->hw.mic.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.mic.adr, cs->hw.mic.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.mic.adr,
+			cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.mic.adr,
+		 cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+mic_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0x0);
+	writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_mic(struct IsdnCardState *cs)
+{
+	int bytecnt = 8;
+
+	if (cs->hw.mic.cfg_reg)
+		release_region(cs->hw.mic.cfg_reg, bytecnt);
+}
+
+static int
+mic_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			release_io_mic(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscx(cs); /* /RTSA := ISAC RST */
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+int __init
+setup_mic(struct IsdnCard *card)
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, mic_revision);
+	printk(KERN_INFO "HiSax: mic driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_MIC)
+		return (0);
+
+	bytecnt = 8;
+	cs->hw.mic.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	cs->hw.mic.adr = cs->hw.mic.cfg_reg + MIC_ADR;
+	cs->hw.mic.isac = cs->hw.mic.cfg_reg + MIC_ISAC;
+	cs->hw.mic.hscx = cs->hw.mic.cfg_reg + MIC_HSCX;
+
+	if (!request_region(cs->hw.mic.cfg_reg, bytecnt, "mic isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.mic.cfg_reg,
+		       cs->hw.mic.cfg_reg + bytecnt);
+		return (0);
+	}
+	printk(KERN_INFO "mic: defined at 0x%x IRQ %d\n",
+		cs->hw.mic.cfg_reg, cs->irq);
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &mic_card_msg;
+	cs->irq_func = &mic_interrupt;
+	ISACVersion(cs, "mic:");
+	if (HscxVersion(cs, "mic:")) {
+		printk(KERN_WARNING
+		    "mic: wrong HSCX versions check IO address\n");
+		release_io_mic(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c
new file mode 100644
index 000000000000..fe61d26365d3
--- /dev/null
+++ b/drivers/isdn/hisax/netjet.c
@@ -0,0 +1,996 @@
+/* $Id: netjet.c,v 1.29.2.4 2004/02/11 13:21:34 keil Exp $
+ *
+ * low level stuff for Traverse Technologie NETJet ISDN cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Traverse Technologies Australia for documents and information
+ *
+ * 16-Apr-2002 - led code added - Guy Ellis (guy@traverse.com.au)
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include <asm/io.h>
+#include "netjet.h"
+
+const char *NETjet_revision = "$Revision: 1.29.2.4 $";
+
+/* Interface functions */
+
+u_char
+NETjet_ReadIC(struct IsdnCardState *cs, u_char offset)
+{
+	u_char ret;
+	
+	cs->hw.njet.auxd &= 0xfc;
+	cs->hw.njet.auxd |= (offset>>4) & 3;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	ret = bytein(cs->hw.njet.isac + ((offset & 0xf)<<2));
+	return(ret);
+}
+
+void
+NETjet_WriteIC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	cs->hw.njet.auxd &= 0xfc;
+	cs->hw.njet.auxd |= (offset>>4) & 3;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	byteout(cs->hw.njet.isac + ((offset & 0xf)<<2), value);
+}
+
+void
+NETjet_ReadICfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	cs->hw.njet.auxd &= 0xfc;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	insb(cs->hw.njet.isac, data, size);
+}
+
+void 
+NETjet_WriteICfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	cs->hw.njet.auxd &= 0xfc;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	outsb(cs->hw.njet.isac, data, size);
+}
+
+void fill_mem(struct BCState *bcs, u_int *pos, u_int cnt, int chan, u_char fill)
+{
+	u_int mask=0x000000ff, val = 0, *p=pos;
+	u_int i;
+	
+	val |= fill;
+	if (chan) {
+		val  <<= 8;
+		mask <<= 8;
+	}
+	mask ^= 0xffffffff;
+	for (i=0; i<cnt; i++) {
+		*p   &= mask;
+		*p++ |= val;
+		if (p > bcs->hw.tiger.s_end)
+			p = bcs->hw.tiger.send;
+	}
+}
+
+void
+mode_tiger(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+        u_char led;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "Tiger mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	switch (mode) {
+		case (L1_MODE_NULL):
+			fill_mem(bcs, bcs->hw.tiger.send,
+				NETJET_DMA_TXSIZE, bc, 0xff);
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "Tiger stat rec %d/%d send %d",
+					bcs->hw.tiger.r_tot, bcs->hw.tiger.r_err,
+					bcs->hw.tiger.s_tot); 
+			if ((cs->bcs[0].mode == L1_MODE_NULL) &&
+				(cs->bcs[1].mode == L1_MODE_NULL)) {
+				cs->hw.njet.dmactrl = 0;
+				byteout(cs->hw.njet.base + NETJET_DMACTRL,
+					cs->hw.njet.dmactrl);
+				byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+			}
+                        if (cs->typ == ISDN_CTYPE_NETJET_S)
+                        {
+                                // led off
+                                led = bc & 0x01;
+                                led = 0x01 << (6 + led); // convert to mask
+                                led = ~led;
+                                cs->hw.njet.auxd &= led;
+                                byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+                        }
+			break;
+		case (L1_MODE_TRANS):
+			break;
+		case (L1_MODE_HDLC_56K):
+		case (L1_MODE_HDLC):
+			fill_mem(bcs, bcs->hw.tiger.send,
+				NETJET_DMA_TXSIZE, bc, 0xff);
+			bcs->hw.tiger.r_state = HDLC_ZERO_SEARCH;
+			bcs->hw.tiger.r_tot = 0;
+			bcs->hw.tiger.r_bitcnt = 0;
+			bcs->hw.tiger.r_one = 0;
+			bcs->hw.tiger.r_err = 0;
+			bcs->hw.tiger.s_tot = 0;
+			if (! cs->hw.njet.dmactrl) {
+				fill_mem(bcs, bcs->hw.tiger.send,
+					NETJET_DMA_TXSIZE, !bc, 0xff);
+				cs->hw.njet.dmactrl = 1;
+				byteout(cs->hw.njet.base + NETJET_DMACTRL,
+					cs->hw.njet.dmactrl);
+				byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0x0f);
+			/* was 0x3f now 0x0f for TJ300 and TJ320  GE 13/07/00 */
+			}
+			bcs->hw.tiger.sendp = bcs->hw.tiger.send;
+			bcs->hw.tiger.free = NETJET_DMA_TXSIZE;
+			test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
+                        if (cs->typ == ISDN_CTYPE_NETJET_S)
+                        {
+                                // led on
+                                led = bc & 0x01;
+                                led = 0x01 << (6 + led); // convert to mask
+                                cs->hw.njet.auxd |= led;
+                                byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+                        }
+			break;
+	}
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "tiger: set %x %x %x  %x/%x  pulse=%d",
+			bytein(cs->hw.njet.base + NETJET_DMACTRL),
+			bytein(cs->hw.njet.base + NETJET_IRQMASK0),
+			bytein(cs->hw.njet.base + NETJET_IRQSTAT0),
+			inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+			inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+			bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+}
+
+static void printframe(struct IsdnCardState *cs, u_char *buf, int count, char *s) {
+	char tmp[128];
+	char *t = tmp;
+	int i=count,j;
+	u_char *p = buf;
+
+	t += sprintf(t, "tiger %s(%4d)", s, count);
+	while (i>0) {
+		if (i>16)
+			j=16;
+		else
+			j=i;
+		QuickHex(t, p, j);
+		debugl1(cs, tmp);
+		p += j;
+		i -= j;
+		t = tmp;
+		t += sprintf(t, "tiger %s      ", s);
+	}
+}
+
+// macro for 64k
+
+#define MAKE_RAW_BYTE for (j=0; j<8; j++) { \
+			bitcnt++;\
+			s_val >>= 1;\
+			if (val & 1) {\
+				s_one++;\
+				s_val |= 0x80;\
+			} else {\
+				s_one = 0;\
+				s_val &= 0x7f;\
+			}\
+			if (bitcnt==8) {\
+				bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+				bitcnt = 0;\
+			}\
+			if (s_one == 5) {\
+				s_val >>= 1;\
+				s_val &= 0x7f;\
+				bitcnt++;\
+				s_one = 0;\
+			}\
+			if (bitcnt==8) {\
+				bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+				bitcnt = 0;\
+			}\
+			val >>= 1;\
+		}
+
+static int make_raw_data(struct BCState *bcs) {
+// this make_raw is for 64k
+	register u_int i,s_cnt=0;
+	register u_char j;
+	register u_char val;
+	register u_char s_one = 0;
+	register u_char s_val = 0;
+	register u_char bitcnt = 0;
+	u_int fcs;
+	
+	if (!bcs->tx_skb) {
+		debugl1(bcs->cs, "tiger make_raw: NULL skb");
+		return(1);
+	}
+	bcs->hw.tiger.sendbuf[s_cnt++] = HDLC_FLAG_VALUE;
+	fcs = PPP_INITFCS;
+	for (i=0; i<bcs->tx_skb->len; i++) {
+		val = bcs->tx_skb->data[i];
+		fcs = PPP_FCS (fcs, val);
+		MAKE_RAW_BYTE;
+	}
+	fcs ^= 0xffff;
+	val = fcs & 0xff;
+	MAKE_RAW_BYTE;
+	val = (fcs>>8) & 0xff;
+	MAKE_RAW_BYTE;
+	val = HDLC_FLAG_VALUE;
+	for (j=0; j<8; j++) { 
+		bitcnt++;
+		s_val >>= 1;
+		if (val & 1)
+			s_val |= 0x80;
+		else
+			s_val &= 0x7f;
+		if (bitcnt==8) {
+			bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+			bitcnt = 0;
+		}
+		val >>= 1;
+	}
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger make_raw: in %ld out %d.%d",
+			bcs->tx_skb->len, s_cnt, bitcnt);
+	if (bitcnt) {
+		while (8>bitcnt++) {
+			s_val >>= 1;
+			s_val |= 0x80;
+		}
+		bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+		bcs->hw.tiger.sendbuf[s_cnt++] = 0xff;	// NJ<->NJ thoughput bug fix
+	}
+	bcs->hw.tiger.sendcnt = s_cnt;
+	bcs->tx_cnt -= bcs->tx_skb->len;
+	bcs->hw.tiger.sp = bcs->hw.tiger.sendbuf;
+	return(0);
+}
+
+// macro for 56k
+
+#define MAKE_RAW_BYTE_56K for (j=0; j<8; j++) { \
+			bitcnt++;\
+			s_val >>= 1;\
+			if (val & 1) {\
+				s_one++;\
+				s_val |= 0x80;\
+			} else {\
+				s_one = 0;\
+				s_val &= 0x7f;\
+			}\
+			if (bitcnt==7) {\
+				s_val >>= 1;\
+				s_val |= 0x80;\
+				bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+				bitcnt = 0;\
+			}\
+			if (s_one == 5) {\
+				s_val >>= 1;\
+				s_val &= 0x7f;\
+				bitcnt++;\
+				s_one = 0;\
+			}\
+			if (bitcnt==7) {\
+				s_val >>= 1;\
+				s_val |= 0x80;\
+				bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+				bitcnt = 0;\
+			}\
+			val >>= 1;\
+		}
+
+static int make_raw_data_56k(struct BCState *bcs) {
+// this make_raw is for 56k
+	register u_int i,s_cnt=0;
+	register u_char j;
+	register u_char val;
+	register u_char s_one = 0;
+	register u_char s_val = 0;
+	register u_char bitcnt = 0;
+	u_int fcs;
+	
+	if (!bcs->tx_skb) {
+		debugl1(bcs->cs, "tiger make_raw_56k: NULL skb");
+		return(1);
+	}
+	val = HDLC_FLAG_VALUE;
+	for (j=0; j<8; j++) { 
+		bitcnt++;
+		s_val >>= 1;
+		if (val & 1)
+			s_val |= 0x80;
+		else
+			s_val &= 0x7f;
+		if (bitcnt==7) {
+			s_val >>= 1;
+			s_val |= 0x80;
+			bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+			bitcnt = 0;
+		}
+		val >>= 1;
+	};
+	fcs = PPP_INITFCS;
+	for (i=0; i<bcs->tx_skb->len; i++) {
+		val = bcs->tx_skb->data[i];
+		fcs = PPP_FCS (fcs, val);
+		MAKE_RAW_BYTE_56K;
+	}
+	fcs ^= 0xffff;
+	val = fcs & 0xff;
+	MAKE_RAW_BYTE_56K;
+	val = (fcs>>8) & 0xff;
+	MAKE_RAW_BYTE_56K;
+	val = HDLC_FLAG_VALUE;
+	for (j=0; j<8; j++) { 
+		bitcnt++;
+		s_val >>= 1;
+		if (val & 1)
+			s_val |= 0x80;
+		else
+			s_val &= 0x7f;
+		if (bitcnt==7) {
+			s_val >>= 1;
+			s_val |= 0x80;
+			bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+			bitcnt = 0;
+		}
+		val >>= 1;
+	}
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger make_raw_56k: in %ld out %d.%d",
+			bcs->tx_skb->len, s_cnt, bitcnt);
+	if (bitcnt) {
+		while (8>bitcnt++) {
+			s_val >>= 1;
+			s_val |= 0x80;
+		}
+		bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+		bcs->hw.tiger.sendbuf[s_cnt++] = 0xff;	// NJ<->NJ thoughput bug fix
+	}
+	bcs->hw.tiger.sendcnt = s_cnt;
+	bcs->tx_cnt -= bcs->tx_skb->len;
+	bcs->hw.tiger.sp = bcs->hw.tiger.sendbuf;
+	return(0);
+}
+
+static void got_frame(struct BCState *bcs, int count) {
+	struct sk_buff *skb;
+		
+	if (!(skb = dev_alloc_skb(count)))
+		printk(KERN_WARNING "TIGER: receive out of memory\n");
+	else {
+		memcpy(skb_put(skb, count), bcs->hw.tiger.rcvbuf, count);
+		skb_queue_tail(&bcs->rqueue, skb);
+	}
+	test_and_set_bit(B_RCVBUFREADY, &bcs->event);
+	schedule_work(&bcs->tqueue);
+	
+	if (bcs->cs->debug & L1_DEB_RECEIVE_FRAME)
+		printframe(bcs->cs, bcs->hw.tiger.rcvbuf, count, "rec");
+}
+
+
+
+static void read_raw(struct BCState *bcs, u_int *buf, int cnt){
+	int i;
+	register u_char j;
+	register u_char val;
+	u_int  *pend = bcs->hw.tiger.rec +NETJET_DMA_RXSIZE -1;
+	register u_char state = bcs->hw.tiger.r_state;
+	register u_char r_one = bcs->hw.tiger.r_one;
+	register u_char r_val = bcs->hw.tiger.r_val;
+	register u_int bitcnt = bcs->hw.tiger.r_bitcnt;
+	u_int *p = buf;
+	int bits;
+	u_char mask;
+
+        if (bcs->mode == L1_MODE_HDLC) { // it's 64k
+		mask = 0xff;
+		bits = 8;
+	}
+	else { // it's 56K
+		mask = 0x7f;
+		bits = 7;
+	};
+	for (i=0;i<cnt;i++) {
+		val = bcs->channel ? ((*p>>8) & 0xff) : (*p & 0xff);
+		p++;
+		if (p > pend)
+			p = bcs->hw.tiger.rec;
+		if ((val & mask) == mask) {
+			state = HDLC_ZERO_SEARCH;
+			bcs->hw.tiger.r_tot++;
+			bitcnt = 0;
+			r_one = 0;
+			continue;
+		}
+		for (j=0;j<bits;j++) {
+			if (state == HDLC_ZERO_SEARCH) {
+				if (val & 1) {
+					r_one++;
+				} else {
+					r_one=0;
+					state= HDLC_FLAG_SEARCH;
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs,"tiger read_raw: zBit(%d,%d,%d) %x",
+							bcs->hw.tiger.r_tot,i,j,val);
+				}
+			} else if (state == HDLC_FLAG_SEARCH) { 
+				if (val & 1) {
+					r_one++;
+					if (r_one>6) {
+						state=HDLC_ZERO_SEARCH;
+					}
+				} else {
+					if (r_one==6) {
+						bitcnt=0;
+						r_val=0;
+						state=HDLC_FLAG_FOUND;
+						if (bcs->cs->debug & L1_DEB_HSCX)
+							debugl1(bcs->cs,"tiger read_raw: flag(%d,%d,%d) %x",
+								bcs->hw.tiger.r_tot,i,j,val);
+					}
+					r_one=0;
+				}
+			} else if (state ==  HDLC_FLAG_FOUND) {
+				if (val & 1) {
+					r_one++;
+					if (r_one>6) {
+						state=HDLC_ZERO_SEARCH;
+					} else {
+						r_val >>= 1;
+						r_val |= 0x80;
+						bitcnt++;
+					}
+				} else {
+					if (r_one==6) {
+						bitcnt=0;
+						r_val=0;
+						r_one=0;
+						val >>= 1;
+						continue;
+					} else if (r_one!=5) {
+						r_val >>= 1;
+						r_val &= 0x7f;
+						bitcnt++;
+					}
+					r_one=0;	
+				}
+				if ((state != HDLC_ZERO_SEARCH) &&
+					!(bitcnt & 7)) {
+					state=HDLC_FRAME_FOUND;
+					bcs->hw.tiger.r_fcs = PPP_INITFCS;
+					bcs->hw.tiger.rcvbuf[0] = r_val;
+					bcs->hw.tiger.r_fcs = PPP_FCS (bcs->hw.tiger.r_fcs, r_val);
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs,"tiger read_raw: byte1(%d,%d,%d) rval %x val %x i %x",
+							bcs->hw.tiger.r_tot,i,j,r_val,val,
+							bcs->cs->hw.njet.irqstat0);
+				}
+			} else if (state ==  HDLC_FRAME_FOUND) {
+				if (val & 1) {
+					r_one++;
+					if (r_one>6) {
+						state=HDLC_ZERO_SEARCH;
+						bitcnt=0;
+					} else {
+						r_val >>= 1;
+						r_val |= 0x80;
+						bitcnt++;
+					}
+				} else {
+					if (r_one==6) {
+						r_val=0; 
+						r_one=0;
+						bitcnt++;
+						if (bitcnt & 7) {
+							debugl1(bcs->cs, "tiger: frame not byte aligned");
+							state=HDLC_FLAG_SEARCH;
+							bcs->hw.tiger.r_err++;
+#ifdef ERROR_STATISTIC
+							bcs->err_inv++;
+#endif
+						} else {
+							if (bcs->cs->debug & L1_DEB_HSCX)
+								debugl1(bcs->cs,"tiger frame end(%d,%d): fcs(%x) i %x",
+									i,j,bcs->hw.tiger.r_fcs, bcs->cs->hw.njet.irqstat0);
+							if (bcs->hw.tiger.r_fcs == PPP_GOODFCS) {
+								got_frame(bcs, (bitcnt>>3)-3);
+							} else {
+								if (bcs->cs->debug) {
+									debugl1(bcs->cs, "tiger FCS error");
+									printframe(bcs->cs, bcs->hw.tiger.rcvbuf,
+										(bitcnt>>3)-1, "rec");
+									bcs->hw.tiger.r_err++;
+								}
+#ifdef ERROR_STATISTIC
+							bcs->err_crc++;
+#endif
+							}
+							state=HDLC_FLAG_FOUND;
+						}
+						bitcnt=0;
+					} else if (r_one==5) {
+						val >>= 1;
+						r_one=0;
+						continue;
+					} else {
+						r_val >>= 1;
+						r_val &= 0x7f;
+						bitcnt++;
+					}
+					r_one=0;	
+				}
+				if ((state == HDLC_FRAME_FOUND) &&
+					!(bitcnt & 7)) {
+					if ((bitcnt>>3)>=HSCX_BUFMAX) {
+						debugl1(bcs->cs, "tiger: frame too big");
+						r_val=0; 
+						state=HDLC_FLAG_SEARCH;
+						bcs->hw.tiger.r_err++;
+#ifdef ERROR_STATISTIC
+						bcs->err_inv++;
+#endif
+					} else {
+						bcs->hw.tiger.rcvbuf[(bitcnt>>3)-1] = r_val;
+						bcs->hw.tiger.r_fcs = 
+							PPP_FCS (bcs->hw.tiger.r_fcs, r_val);
+					}
+				}
+			}
+			val >>= 1;
+		}
+		bcs->hw.tiger.r_tot++;
+	}
+	bcs->hw.tiger.r_state = state;
+	bcs->hw.tiger.r_one = r_one;
+	bcs->hw.tiger.r_val = r_val;
+	bcs->hw.tiger.r_bitcnt = bitcnt;
+}
+
+void read_tiger(struct IsdnCardState *cs) {
+	u_int *p;
+	int cnt = NETJET_DMA_RXSIZE/2;
+	
+	if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_READ) {
+		debugl1(cs,"tiger warn read double dma %x/%x",
+			cs->hw.njet.irqstat0, cs->hw.njet.last_is0);
+#ifdef ERROR_STATISTIC
+		if (cs->bcs[0].mode)
+			cs->bcs[0].err_rdo++;
+		if (cs->bcs[1].mode)
+			cs->bcs[1].err_rdo++;
+#endif
+		return;
+	} else {
+		cs->hw.njet.last_is0 &= ~NETJET_IRQM0_READ;
+		cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ);
+	}	
+	if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ_1)
+		p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1;
+	else
+		p = cs->bcs[0].hw.tiger.rec + cnt - 1;
+	if ((cs->bcs[0].mode == L1_MODE_HDLC) || (cs->bcs[0].mode == L1_MODE_HDLC_56K))
+		read_raw(cs->bcs, p, cnt);
+
+	if ((cs->bcs[1].mode == L1_MODE_HDLC) || (cs->bcs[1].mode == L1_MODE_HDLC_56K))
+		read_raw(cs->bcs + 1, p, cnt);
+	cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_READ;
+}
+
+static void write_raw(struct BCState *bcs, u_int *buf, int cnt);
+
+void netjet_fill_dma(struct BCState *bcs)
+{
+	register u_int *p, *sp;
+	register int cnt;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger fill_dma1: c%d %4x", bcs->channel,
+			bcs->Flag);
+	if (test_and_set_bit(BC_FLG_BUSY, &bcs->Flag))
+		return;
+	if (bcs->mode == L1_MODE_HDLC) { // it's 64k
+		if (make_raw_data(bcs))
+			return;		
+	}
+	else { // it's 56k
+		if (make_raw_data_56k(bcs))
+			return;		
+	};
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger fill_dma2: c%d %4x", bcs->channel,
+			bcs->Flag);
+	if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) {
+		write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free);
+	} else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
+		p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR));
+		sp = bcs->hw.tiger.sendp;
+		if (p == bcs->hw.tiger.s_end)
+			p = bcs->hw.tiger.send -1;
+		if (sp == bcs->hw.tiger.s_end)
+			sp = bcs->hw.tiger.send -1;
+		cnt = p - sp;
+		if (cnt <0) {
+			write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free);
+		} else {
+			p++;
+			cnt++;
+			if (p > bcs->hw.tiger.s_end)
+				p = bcs->hw.tiger.send;
+			p++;
+			cnt++;
+			if (p > bcs->hw.tiger.s_end)
+				p = bcs->hw.tiger.send;
+			write_raw(bcs, p, bcs->hw.tiger.free - cnt);
+		}
+	} else if (test_and_clear_bit(BC_FLG_EMPTY, &bcs->Flag)) {
+		p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR));
+		cnt = bcs->hw.tiger.s_end - p;
+		if (cnt < 2) {
+			p = bcs->hw.tiger.send + 1;
+			cnt = NETJET_DMA_TXSIZE/2 - 2;
+		} else {
+			p++;
+			p++;
+			if (cnt <= (NETJET_DMA_TXSIZE/2))
+				cnt += NETJET_DMA_TXSIZE/2;
+			cnt--;
+			cnt--;
+		}
+		write_raw(bcs, p, cnt);
+	}
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger fill_dma3: c%d %4x", bcs->channel,
+			bcs->Flag);
+}
+
+static void write_raw(struct BCState *bcs, u_int *buf, int cnt) {
+	u_int mask, val, *p=buf;
+	u_int i, s_cnt;
+        
+        if (cnt <= 0)
+        	return;
+	if (test_bit(BC_FLG_BUSY, &bcs->Flag)) {
+		if (bcs->hw.tiger.sendcnt> cnt) {
+			s_cnt = cnt;
+			bcs->hw.tiger.sendcnt -= cnt;
+		} else {
+			s_cnt = bcs->hw.tiger.sendcnt;
+			bcs->hw.tiger.sendcnt = 0;
+		}
+		if (bcs->channel)
+			mask = 0xffff00ff;
+		else
+			mask = 0xffffff00;
+		for (i=0; i<s_cnt; i++) {
+			val = bcs->channel ? ((bcs->hw.tiger.sp[i] <<8) & 0xff00) :
+				(bcs->hw.tiger.sp[i]);
+			*p   &= mask;
+			*p++ |= val;
+			if (p>bcs->hw.tiger.s_end)
+				p = bcs->hw.tiger.send;
+		}
+		bcs->hw.tiger.s_tot += s_cnt;
+		if (bcs->cs->debug & L1_DEB_HSCX)
+			debugl1(bcs->cs,"tiger write_raw: c%d %p-%p %d/%d %d %x", bcs->channel,
+				buf, p, s_cnt, cnt,
+				bcs->hw.tiger.sendcnt, bcs->cs->hw.njet.irqstat0);
+		if (bcs->cs->debug & L1_DEB_HSCX_FIFO)
+			printframe(bcs->cs, bcs->hw.tiger.sp, s_cnt, "snd");
+		bcs->hw.tiger.sp += s_cnt;
+		bcs->hw.tiger.sendp = p;
+		if (!bcs->hw.tiger.sendcnt) {
+			if (!bcs->tx_skb) {
+				debugl1(bcs->cs,"tiger write_raw: NULL skb s_cnt %d", s_cnt);
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->tx_skb->len;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+				dev_kfree_skb_any(bcs->tx_skb);
+				bcs->tx_skb = NULL;
+			}
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->hw.tiger.free = cnt - s_cnt;
+			if (bcs->hw.tiger.free > (NETJET_DMA_TXSIZE/2))
+				test_and_set_bit(BC_FLG_HALF, &bcs->Flag);
+			else {
+				test_and_clear_bit(BC_FLG_HALF, &bcs->Flag);
+				test_and_set_bit(BC_FLG_NOFRAME, &bcs->Flag);
+			}
+			if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+				netjet_fill_dma(bcs);
+			} else {
+				mask ^= 0xffffffff;
+				if (s_cnt < cnt) {
+					for (i=s_cnt; i<cnt;i++) {
+						*p++ |= mask;
+						if (p>bcs->hw.tiger.s_end)
+							p = bcs->hw.tiger.send;
+					}
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs, "tiger write_raw: fill rest %d",
+							cnt - s_cnt);
+				}
+				test_and_set_bit(B_XMTBUFREADY, &bcs->event);
+				schedule_work(&bcs->tqueue);
+			}
+		}
+	} else if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) {
+		test_and_set_bit(BC_FLG_HALF, &bcs->Flag);
+		fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
+		bcs->hw.tiger.free += cnt;
+		if (bcs->cs->debug & L1_DEB_HSCX)
+			debugl1(bcs->cs,"tiger write_raw: fill half");
+	} else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
+		test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
+		fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
+		if (bcs->cs->debug & L1_DEB_HSCX)
+			debugl1(bcs->cs,"tiger write_raw: fill full");
+	}
+}
+
+void write_tiger(struct IsdnCardState *cs) {
+	u_int *p, cnt = NETJET_DMA_TXSIZE/2;
+	
+	if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) {
+		debugl1(cs,"tiger warn write double dma %x/%x",
+			cs->hw.njet.irqstat0, cs->hw.njet.last_is0);
+#ifdef ERROR_STATISTIC
+		if (cs->bcs[0].mode)
+			cs->bcs[0].err_tx++;
+		if (cs->bcs[1].mode)
+			cs->bcs[1].err_tx++;
+#endif
+		return;
+	} else {
+		cs->hw.njet.last_is0 &= ~NETJET_IRQM0_WRITE;
+		cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE);
+	}	
+	if (cs->hw.njet.irqstat0  & NETJET_IRQM0_WRITE_1)
+		p = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1;
+	else
+		p = cs->bcs[0].hw.tiger.send + cnt - 1;
+	if ((cs->bcs[0].mode == L1_MODE_HDLC) || (cs->bcs[0].mode == L1_MODE_HDLC_56K))
+		write_raw(cs->bcs, p, cnt);
+	if ((cs->bcs[1].mode == L1_MODE_HDLC) || (cs->bcs[1].mode == L1_MODE_HDLC_56K))
+		write_raw(cs->bcs + 1, p, cnt);
+	cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_WRITE;
+}
+
+static void
+tiger_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "tiger_l2l1: this shouldn't happen\n");
+			} else {
+				bcs->tx_skb = skb;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			mode_tiger(bcs, st->l1.mode, st->l1.bc);
+			/* 2001/10/04 Christoph Ersfeld, Formula-n Europe AG */
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			bcs->cs->cardmsg(bcs->cs, MDL_BC_ASSIGN, (void *)(&st->l1.bc));
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			/* 2001/10/04 Christoph Ersfeld, Formula-n Europe AG */
+			bcs->cs->cardmsg(bcs->cs, MDL_BC_RELEASE, (void *)(&st->l1.bc));
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			mode_tiger(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+
+void
+close_tigerstate(struct BCState *bcs)
+{
+	mode_tiger(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.tiger.rcvbuf) {
+			kfree(bcs->hw.tiger.rcvbuf);
+			bcs->hw.tiger.rcvbuf = NULL;
+		}
+		if (bcs->hw.tiger.sendbuf) {
+			kfree(bcs->hw.tiger.sendbuf);
+			bcs->hw.tiger.sendbuf = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+static int
+open_tigerstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.tiger.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for tiger.rcvbuf\n");
+			return (1);
+		}
+		if (!(bcs->hw.tiger.sendbuf = kmalloc(RAW_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for tiger.sendbuf\n");
+			return (1);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	bcs->hw.tiger.sendcnt = 0;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_tiger(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_tigerstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = tiger_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+ 
+void __init
+inittiger(struct IsdnCardState *cs)
+{
+	if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_TXSIZE * sizeof(unsigned int),
+		GFP_KERNEL | GFP_DMA))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for tiger.send\n");
+		return;
+	}
+	cs->bcs[0].hw.tiger.s_irq = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE/2 - 1;
+	cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1;
+	cs->bcs[1].hw.tiger.send = cs->bcs[0].hw.tiger.send;
+	cs->bcs[1].hw.tiger.s_irq = cs->bcs[0].hw.tiger.s_irq;
+	cs->bcs[1].hw.tiger.s_end = cs->bcs[0].hw.tiger.s_end;
+	
+	memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_TXSIZE * sizeof(unsigned int));
+	debugl1(cs, "tiger: send buf %p - %p", cs->bcs[0].hw.tiger.send,
+		cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.send),
+		cs->hw.njet.base + NETJET_DMA_READ_START);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.s_irq),
+		cs->hw.njet.base + NETJET_DMA_READ_IRQ);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.s_end),
+		cs->hw.njet.base + NETJET_DMA_READ_END);
+	if (!(cs->bcs[0].hw.tiger.rec = kmalloc(NETJET_DMA_RXSIZE * sizeof(unsigned int),
+		GFP_KERNEL | GFP_DMA))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for tiger.rec\n");
+		return;
+	}
+	debugl1(cs, "tiger: rec buf %p - %p", cs->bcs[0].hw.tiger.rec,
+		cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1);
+	cs->bcs[1].hw.tiger.rec = cs->bcs[0].hw.tiger.rec;
+	memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_RXSIZE * sizeof(unsigned int));
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec),
+		cs->hw.njet.base + NETJET_DMA_WRITE_START);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE/2 - 1),
+		cs->hw.njet.base + NETJET_DMA_WRITE_IRQ);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1),
+		cs->hw.njet.base + NETJET_DMA_WRITE_END);
+	debugl1(cs, "tiger: dmacfg  %x/%x  pulse=%d",
+		inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+		inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+		bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+	cs->hw.njet.last_is0 = 0;
+	cs->bcs[0].BC_SetStack = setstack_tiger;
+	cs->bcs[1].BC_SetStack = setstack_tiger;
+	cs->bcs[0].BC_Close = close_tigerstate;
+	cs->bcs[1].BC_Close = close_tigerstate;
+}
+
+void
+releasetiger(struct IsdnCardState *cs)
+{
+	if (cs->bcs[0].hw.tiger.send) {
+		kfree(cs->bcs[0].hw.tiger.send);
+		cs->bcs[0].hw.tiger.send = NULL;
+	}
+	if (cs->bcs[1].hw.tiger.send) {
+		cs->bcs[1].hw.tiger.send = NULL;
+	}
+	if (cs->bcs[0].hw.tiger.rec) {
+		kfree(cs->bcs[0].hw.tiger.rec);
+		cs->bcs[0].hw.tiger.rec = NULL;
+	}
+	if (cs->bcs[1].hw.tiger.rec) {
+		cs->bcs[1].hw.tiger.rec = NULL;
+	}
+}
+
+void
+release_io_netjet(struct IsdnCardState *cs)
+{
+	byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+	byteout(cs->hw.njet.base + NETJET_IRQMASK1, 0);
+	releasetiger(cs);
+	release_region(cs->hw.njet.base, 256);
+}
+
diff --git a/drivers/isdn/hisax/netjet.h b/drivers/isdn/hisax/netjet.h
new file mode 100644
index 000000000000..1080508f3c6a
--- /dev/null
+++ b/drivers/isdn/hisax/netjet.h
@@ -0,0 +1,72 @@
+/* $Id: netjet.h,v 2.8.2.2 2004/01/12 22:52:28 keil Exp $
+ *
+ * NETjet common header file
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *              by Matt Henderson,
+ *                 Traverse Technologies P/L www.traverse.com.au
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+extern const char *CardType[];
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define NETJET_CTRL	0x00
+#define NETJET_DMACTRL	0x01
+#define NETJET_AUXCTRL	0x02
+#define NETJET_AUXDATA	0x03
+#define NETJET_IRQMASK0 0x04
+#define NETJET_IRQMASK1 0x05
+#define NETJET_IRQSTAT0 0x06
+#define NETJET_IRQSTAT1 0x07
+#define NETJET_DMA_READ_START	0x08
+#define NETJET_DMA_READ_IRQ	0x0c
+#define NETJET_DMA_READ_END	0x10
+#define NETJET_DMA_READ_ADR	0x14
+#define NETJET_DMA_WRITE_START	0x18
+#define NETJET_DMA_WRITE_IRQ	0x1c
+#define NETJET_DMA_WRITE_END	0x20
+#define NETJET_DMA_WRITE_ADR	0x24
+#define NETJET_PULSE_CNT	0x28
+
+#define NETJET_ISAC_OFF	0xc0
+#define NETJET_ISACIRQ	0x10
+#define NETJET_IRQM0_READ	0x0c
+#define NETJET_IRQM0_READ_1	0x04
+#define NETJET_IRQM0_READ_2	0x08
+#define NETJET_IRQM0_WRITE	0x03
+#define NETJET_IRQM0_WRITE_1	0x01
+#define NETJET_IRQM0_WRITE_2	0x02
+
+#define NETJET_DMA_TXSIZE 512
+#define NETJET_DMA_RXSIZE 128
+
+#define HDLC_ZERO_SEARCH 0
+#define HDLC_FLAG_SEARCH 1
+#define HDLC_FLAG_FOUND  2
+#define HDLC_FRAME_FOUND 3
+#define HDLC_NULL 4
+#define HDLC_PART 5
+#define HDLC_FULL 6
+
+#define HDLC_FLAG_VALUE	0x7e
+
+u_char NETjet_ReadIC(struct IsdnCardState *cs, u_char offset);
+void NETjet_WriteIC(struct IsdnCardState *cs, u_char offset, u_char value);
+void NETjet_ReadICfifo(struct IsdnCardState *cs, u_char *data, int size);
+void NETjet_WriteICfifo(struct IsdnCardState *cs, u_char *data, int size);
+
+void read_tiger(struct IsdnCardState *cs);
+void write_tiger(struct IsdnCardState *cs);
+
+void netjet_fill_dma(struct BCState *bcs);
+void netjet_interrupt(int intno, void *dev_id, struct pt_regs *regs);
+void inittiger(struct IsdnCardState *cs);
+void release_io_netjet(struct IsdnCardState *cs);
+
diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c
new file mode 100644
index 000000000000..cf77d8360975
--- /dev/null
+++ b/drivers/isdn/hisax/niccy.c
@@ -0,0 +1,389 @@
+/* $Id: niccy.c,v 1.21.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and
+ * compatible (SAGEM cybermodem)
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ * 
+ * Thanks to Dr. Neuhaus and SAGEM for information
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+
+extern const char *CardType[];
+const char *niccy_revision = "$Revision: 1.21.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ISAC_PCI_DATA	0
+#define HSCX_PCI_DATA	1
+#define ISAC_PCI_ADDR	2
+#define HSCX_PCI_ADDR	3
+#define ISAC_PNP	0
+#define HSCX_PNP	1
+
+/* SUB Types */
+#define NICCY_PNP	1
+#define NICCY_PCI	2
+
+/* PCI stuff */
+#define PCI_IRQ_CTRL_REG	0x38
+#define PCI_IRQ_ENABLE		0x1f00
+#define PCI_IRQ_DISABLE		0xff0000
+#define PCI_IRQ_ASSERT		0x800000
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.niccy.hscx_ale,
+			cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.niccy.hscx_ale,
+		 cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if (cs->subtyp == NICCY_PCI) {
+		int ival;
+		ival = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		if (!(ival & PCI_IRQ_ASSERT)) { /* IRQ not for us (shared) */
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_NONE;
+		}
+		outl(ival, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+	}
+	val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0);
+	writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0);
+	writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_niccy(struct IsdnCardState *cs)
+{
+	if (cs->subtyp == NICCY_PCI) {
+		int val;
+		
+		val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		val &= PCI_IRQ_DISABLE;
+		outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		release_region(cs->hw.niccy.cfg_reg, 0x40);
+		release_region(cs->hw.niccy.isac, 4);
+	} else {
+		release_region(cs->hw.niccy.isac, 2);
+		release_region(cs->hw.niccy.isac_ale, 2);
+	}
+}
+
+static void
+niccy_reset(struct IsdnCardState *cs)
+{
+	if (cs->subtyp == NICCY_PCI) {
+		int val;
+
+		val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		val |= PCI_IRQ_ENABLE;
+		outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+	}
+	inithscxisac(cs, 3);
+}
+
+static int
+niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			niccy_reset(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_niccy(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			niccy_reset(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static struct pci_dev *niccy_dev __initdata = NULL;
+#ifdef __ISAPNP__
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __init
+setup_niccy(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, niccy_revision);
+	printk(KERN_INFO "HiSax: Niccy driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_NICCY)
+		return (0);
+#ifdef __ISAPNP__
+	if (!card->para[1] && isapnp_present()) {
+		struct pnp_dev *pnp_d = NULL;
+		int err;
+
+		if ((pnp_c = pnp_find_card(
+			ISAPNP_VENDOR('S', 'D', 'A'),
+			ISAPNP_FUNCTION(0x0150), pnp_c))) {
+			if (!(pnp_d = pnp_find_dev(pnp_c,
+				ISAPNP_VENDOR('S', 'D', 'A'),
+				ISAPNP_FUNCTION(0x0150), pnp_d))) {
+				printk(KERN_ERR "NiccyPnP: PnP error card found, no device\n");
+				return (0);
+			}
+			pnp_disable_dev(pnp_d);
+			err = pnp_activate_dev(pnp_d);
+			if (err<0) {
+				printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+					__FUNCTION__, err);
+				return(0);
+			}
+			card->para[1] = pnp_port_start(pnp_d, 0);
+			card->para[2] = pnp_port_start(pnp_d, 1);
+			card->para[0] = pnp_irq(pnp_d, 0);
+			if (!card->para[0] || !card->para[1] || !card->para[2]) {
+				printk(KERN_ERR "NiccyPnP:some resources are missing %ld/%lx/%lx\n",
+					card->para[0], card->para[1], card->para[2]);
+				pnp_disable_dev(pnp_d);
+				return(0);
+			}
+		} else {
+			printk(KERN_INFO "NiccyPnP: no ISAPnP card found\n");
+		}
+	}
+#endif
+	if (card->para[1]) {
+		cs->hw.niccy.isac = card->para[1] + ISAC_PNP;
+		cs->hw.niccy.hscx = card->para[1] + HSCX_PNP;
+		cs->hw.niccy.isac_ale = card->para[2] + ISAC_PNP;
+		cs->hw.niccy.hscx_ale = card->para[2] + HSCX_PNP;
+		cs->hw.niccy.cfg_reg = 0;
+		cs->subtyp = NICCY_PNP;
+		cs->irq = card->para[0];
+		if (!request_region(cs->hw.niccy.isac, 2, "niccy data")) {
+			printk(KERN_WARNING
+				"HiSax: %s data port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.isac,
+				cs->hw.niccy.isac + 1);
+			return (0);
+		}
+		if (!request_region(cs->hw.niccy.isac_ale, 2, "niccy addr")) {
+			printk(KERN_WARNING
+				"HiSax: %s address port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.isac_ale,
+				cs->hw.niccy.isac_ale + 1);
+			release_region(cs->hw.niccy.isac, 2);
+			return (0);
+		}
+	} else {
+#ifdef CONFIG_PCI
+		u_int pci_ioaddr;
+		cs->subtyp = 0;
+		if ((niccy_dev = pci_find_device(PCI_VENDOR_ID_SATSAGEM,
+			PCI_DEVICE_ID_SATSAGEM_NICCY, niccy_dev))) {
+			if (pci_enable_device(niccy_dev))
+				return(0);
+			/* get IRQ */
+			if (!niccy_dev->irq) {
+				printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n");
+				return(0);
+			}
+			cs->irq = niccy_dev->irq;
+			cs->hw.niccy.cfg_reg = pci_resource_start(niccy_dev, 0);
+			if (!cs->hw.niccy.cfg_reg) {
+				printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n");
+				return(0);
+			}
+			pci_ioaddr = pci_resource_start(niccy_dev, 1);
+			if (!pci_ioaddr) {
+				printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+			cs->subtyp = NICCY_PCI;
+		} else {
+			printk(KERN_WARNING "Niccy: No PCI card found\n");
+			return(0);
+		}
+		cs->irq_flags |= SA_SHIRQ;
+		cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA;
+		cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR;
+		cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA;
+		cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR;
+		if (!request_region(cs->hw.niccy.isac, 4, "niccy")) {
+			printk(KERN_WARNING
+				"HiSax: %s data port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.isac,
+				cs->hw.niccy.isac + 4);
+			return (0);
+		}
+		if (!request_region(cs->hw.niccy.cfg_reg, 0x40, "niccy pci")) {
+			printk(KERN_WARNING
+			       "HiSax: %s pci port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.cfg_reg,
+				cs->hw.niccy.cfg_reg + 0x40);
+			release_region(cs->hw.niccy.isac, 4);
+			return (0);
+		}
+#else
+		printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n");
+		printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n");
+		return (0);
+#endif /* CONFIG_PCI */
+	}
+	printk(KERN_INFO "HiSax: %s %s config irq:%d data:0x%X ale:0x%X\n",
+		CardType[cs->typ], (cs->subtyp==1) ? "PnP":"PCI",
+		cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale);
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &niccy_card_msg;
+	cs->irq_func = &niccy_interrupt;
+	ISACVersion(cs, "Niccy:");
+	if (HscxVersion(cs, "Niccy:")) {
+		printk(KERN_WARNING
+		    "Niccy: wrong HSCX versions check IO address\n");
+		release_io_niccy(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/nj_s.c b/drivers/isdn/hisax/nj_s.c
new file mode 100644
index 000000000000..fd664697f821
--- /dev/null
+++ b/drivers/isdn/hisax/nj_s.c
@@ -0,0 +1,278 @@
+/* $Id: nj_s.c,v 2.13.2.4 2004/01/16 01:53:48 keil Exp $
+ *
+ * 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/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include "netjet.h"
+
+const char *NETjet_S_revision = "$Revision: 2.13.2.4 $";
+
+static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off)
+{
+	return(5);
+}
+
+static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value)
+{
+}
+
+static irqreturn_t
+netjet_s_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, s1val, s0val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	s1val = bytein(cs->hw.njet.base + NETJET_IRQSTAT1);
+	if (!(s1val & NETJET_ISACIRQ)) {
+		val = NETjet_ReadIC(cs, ISAC_ISTA);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "tiger: i1 %x %x", s1val, val);
+		if (val) {
+			isac_interrupt(cs, val);
+			NETjet_WriteIC(cs, ISAC_MASK, 0xFF);
+			NETjet_WriteIC(cs, ISAC_MASK, 0x0);
+		}
+		s1val = 1;
+	} else
+		s1val = 0;
+	/* 
+	 * read/write stat0 is better, because lower IRQ rate
+	 * Note the IRQ is on for 125 us if a condition match
+	 * thats long on modern CPU and so the IRQ is reentered
+	 * all the time.
+	 */
+	s0val = bytein(cs->hw.njet.base + NETJET_IRQSTAT0);
+	if ((s0val | s1val)==0) { // shared IRQ
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	} 
+	if (s0val)
+		byteout(cs->hw.njet.base + NETJET_IRQSTAT0, s0val);
+	/* start new code 13/07/00 GE */
+	/* set bits in sval to indicate which page is free */
+	if (inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR) <
+		inl(cs->hw.njet.base + NETJET_DMA_WRITE_IRQ))
+		/* the 2nd write page is free */
+		s0val = 0x08;
+	else	/* the 1st write page is free */
+		s0val = 0x04;	
+	if (inl(cs->hw.njet.base + NETJET_DMA_READ_ADR) <
+		inl(cs->hw.njet.base + NETJET_DMA_READ_IRQ))
+		/* the 2nd read page is free */
+		s0val |= 0x02;
+	else	/* the 1st read page is free */
+		s0val |= 0x01;	
+	if (s0val != cs->hw.njet.last_is0) /* we have a DMA interrupt */
+	{
+		if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			printk(KERN_WARNING "nj LOCK_ATOMIC s0val %x->%x\n",
+				cs->hw.njet.last_is0, s0val);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_HANDLED;
+		}
+		cs->hw.njet.irqstat0 = s0val;
+		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) != 
+			(cs->hw.njet.last_is0 & NETJET_IRQM0_READ))
+			/* we have a read dma int */
+			read_tiger(cs);
+		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) !=
+			(cs->hw.njet.last_is0 & NETJET_IRQM0_WRITE))
+			/* we have a write dma int */
+			write_tiger(cs);
+		/* end new code 13/07/00 GE */
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+reset_netjet_s(struct IsdnCardState *cs)
+{
+	cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	mdelay(10);
+	/* now edge triggered for TJ320 GE 13/07/00 */
+	/* see comment in IRQ function */
+	if (cs->subtyp) /* TJ320 */
+		cs->hw.njet.ctrl_reg = 0x40;  /* Reset Off and status read clear */
+	else
+		cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	mdelay(10);
+	cs->hw.njet.auxd = 0;
+	cs->hw.njet.dmactrl = 0;
+	byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+	byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+}
+
+static int
+NETjet_S_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_netjet_s(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_netjet(cs);
+			return(0);
+		case CARD_INIT:
+			reset_netjet_s(cs);
+			inittiger(cs);
+			spin_lock_irqsave(&cs->lock, flags);
+			clear_pending_isac_ints(cs);
+			initisac(cs);
+			/* Reenable all IRQ */
+			cs->writeisac(cs, ISAC_MASK, 0);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static struct pci_dev *dev_netjet __initdata = NULL;
+
+int __init
+setup_netjet_s(struct IsdnCard *card)
+{
+	int bytecnt,cfg;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+	strcpy(tmp, NETjet_S_revision);
+	printk(KERN_INFO "HiSax: Traverse Tech. NETjet-S driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_NETJET_S)
+		return(0);
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+
+#ifdef CONFIG_PCI
+
+	for ( ;; )
+	{
+		if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+			PCI_DEVICE_ID_TIGERJET_300,  dev_netjet))) {
+			if (pci_enable_device(dev_netjet))
+				return(0);
+			pci_set_master(dev_netjet);
+			cs->irq = dev_netjet->irq;
+			if (!cs->irq) {
+				printk(KERN_WARNING "NETjet-S: No IRQ for PCI card found\n");
+				return(0);
+			}
+			cs->hw.njet.base = pci_resource_start(dev_netjet, 0);
+			if (!cs->hw.njet.base) {
+				printk(KERN_WARNING "NETjet-S: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+			/* the TJ300 and TJ320 must be detected, the IRQ handling is different
+			 * unfortunatly the chips use the same device ID, but the TJ320 has
+			 * the bit20 in status PCI cfg register set
+			 */
+			pci_read_config_dword(dev_netjet, 0x04, &cfg);
+			if (cfg & 0x00100000)
+				cs->subtyp = 1; /* TJ320 */
+			else
+				cs->subtyp = 0; /* TJ300 */
+			/* 2001/10/04 Christoph Ersfeld, Formula-n Europe AG www.formula-n.com */
+			if ((dev_netjet->subsystem_vendor == 0x55) &&
+				(dev_netjet->subsystem_device == 0x02)) {
+				printk(KERN_WARNING "Netjet: You tried to load this driver with an incompatible TigerJet-card\n");
+				printk(KERN_WARNING "Use type=41 for Formula-n enter:now ISDN PCI and compatible\n");
+				return(0);
+			}
+			/* end new code */
+		} else {
+			printk(KERN_WARNING "NETjet-S: No PCI card found\n");
+			return(0);
+		}
+
+		cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
+		cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF;
+
+		cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+		byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+		mdelay(10);
+
+		cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
+		byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+		mdelay(10);
+
+		cs->hw.njet.auxd = 0xC0;
+		cs->hw.njet.dmactrl = 0;
+
+		byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+		byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+		byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+
+		switch ( ( ( NETjet_ReadIC( cs, ISAC_RBCH ) >> 5 ) & 3 ) )
+		{
+			case 0 :
+				break;
+
+			case 3 :
+				printk( KERN_WARNING "NETjet-S: NETspider-U PCI card found\n" );
+				continue;
+
+			default :
+				printk( KERN_WARNING "NETjet-S: No PCI card found\n" );
+				return 0;
+                }
+                break;
+	}
+#else
+
+	printk(KERN_WARNING "NETjet-S: NO_PCI_BIOS\n");
+	printk(KERN_WARNING "NETjet-S: unable to config NETJET-S PCI\n");
+	return (0);
+
+#endif /* CONFIG_PCI */
+
+	bytecnt = 256;
+
+	printk(KERN_INFO
+		"NETjet-S: %s card configured at %#lx IRQ %d\n",
+		cs->subtyp ? "TJ320" : "TJ300", cs->hw.njet.base, cs->irq);
+	if (!request_region(cs->hw.njet.base, bytecnt, "netjet-s isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %#lx-%#lx already in use\n",
+		       CardType[card->typ],
+		       cs->hw.njet.base,
+		       cs->hw.njet.base + bytecnt);
+		return (0);
+	}
+	cs->readisac  = &NETjet_ReadIC;
+	cs->writeisac = &NETjet_WriteIC;
+	cs->readisacfifo  = &NETjet_ReadICfifo;
+	cs->writeisacfifo = &NETjet_WriteICfifo;
+	cs->BC_Read_Reg  = &dummyrr;
+	cs->BC_Write_Reg = &dummywr;
+	cs->BC_Send_Data = &netjet_fill_dma;
+	setup_isac(cs);
+	cs->cardmsg = &NETjet_S_card_msg;
+	cs->irq_func = &netjet_s_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+	ISACVersion(cs, "NETjet-S:");
+	return (1);
+}
diff --git a/drivers/isdn/hisax/nj_u.c b/drivers/isdn/hisax/nj_u.c
new file mode 100644
index 000000000000..3d6441e9633c
--- /dev/null
+++ b/drivers/isdn/hisax/nj_u.c
@@ -0,0 +1,244 @@
+/* $Id: nj_u.c,v 2.14.2.3 2004/01/13 14:31:26 keil Exp $ 
+ *
+ * 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/init.h>
+#include "hisax.h"
+#include "icc.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include "netjet.h"
+
+const char *NETjet_U_revision = "$Revision: 2.14.2.3 $";
+
+static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off)
+{
+	return(5);
+}
+
+static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value)
+{
+}
+
+static irqreturn_t
+netjet_u_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) &
+		NETJET_ISACIRQ)) {
+		val = NETjet_ReadIC(cs, ICC_ISTA);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "tiger: i1 %x %x", sval, val);
+		if (val) {
+			icc_interrupt(cs, val);
+			NETjet_WriteIC(cs, ICC_MASK, 0xFF);
+			NETjet_WriteIC(cs, ICC_MASK, 0x0);
+		}
+	}
+	/* start new code 13/07/00 GE */
+	/* set bits in sval to indicate which page is free */
+	if (inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR) <
+		inl(cs->hw.njet.base + NETJET_DMA_WRITE_IRQ))
+		/* the 2nd write page is free */
+		sval = 0x08;
+	else	/* the 1st write page is free */
+		sval = 0x04;	
+	if (inl(cs->hw.njet.base + NETJET_DMA_READ_ADR) <
+		inl(cs->hw.njet.base + NETJET_DMA_READ_IRQ))
+		/* the 2nd read page is free */
+		sval = sval | 0x02;
+	else	/* the 1st read page is free */
+		sval = sval | 0x01;	
+	if (sval != cs->hw.njet.last_is0) /* we have a DMA interrupt */
+	{
+		if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_HANDLED;
+		}
+		cs->hw.njet.irqstat0 = sval;
+		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) != 
+			(cs->hw.njet.last_is0 & NETJET_IRQM0_READ))
+			/* we have a read dma int */
+			read_tiger(cs);
+		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) !=
+			(cs->hw.njet.last_is0 & NETJET_IRQM0_WRITE))
+			/* we have a write dma int */
+			write_tiger(cs);
+		/* end new code 13/07/00 GE */
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+reset_netjet_u(struct IsdnCardState *cs)
+{
+	cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	mdelay(10);
+	cs->hw.njet.ctrl_reg = 0x40;  /* Reset Off and status read clear */
+	/* now edge triggered for TJ320 GE 13/07/00 */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	mdelay(10);
+	cs->hw.njet.auxd = 0xC0;
+	cs->hw.njet.dmactrl = 0;
+	byteout(cs->hw.njet.auxa, 0);
+	byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+	byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+}
+
+static int
+NETjet_U_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_netjet_u(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_netjet(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inittiger(cs);
+			reset_netjet_u(cs);
+			clear_pending_icc_ints(cs);
+			initicc(cs);
+			/* Reenable all IRQ */
+			cs->writeisac(cs, ICC_MASK, 0);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static struct pci_dev *dev_netjet __initdata = NULL;
+
+int __init
+setup_netjet_u(struct IsdnCard *card)
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+#ifdef CONFIG_PCI
+#endif
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+	strcpy(tmp, NETjet_U_revision);
+	printk(KERN_INFO "HiSax: Traverse Tech. NETspider-U driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_NETJET_U)
+		return(0);
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+
+#ifdef CONFIG_PCI
+
+	for ( ;; )
+	{
+		if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+			PCI_DEVICE_ID_TIGERJET_300,  dev_netjet))) {
+			if (pci_enable_device(dev_netjet))
+				return(0);
+			pci_set_master(dev_netjet);
+			cs->irq = dev_netjet->irq;
+			if (!cs->irq) {
+				printk(KERN_WARNING "NETspider-U: No IRQ for PCI card found\n");
+				return(0);
+			}
+			cs->hw.njet.base = pci_resource_start(dev_netjet, 0);
+			if (!cs->hw.njet.base) {
+				printk(KERN_WARNING "NETspider-U: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+		} else {
+			printk(KERN_WARNING "NETspider-U: No PCI card found\n");
+			return(0);
+		}
+
+		cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
+		cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF;
+		mdelay(10);
+
+		cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+		byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+		mdelay(10);
+
+		cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
+		byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+		mdelay(10);
+
+		cs->hw.njet.auxd = 0xC0;
+		cs->hw.njet.dmactrl = 0;
+
+		byteout(cs->hw.njet.auxa, 0);
+		byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+		byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+		byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+
+		switch ( ( ( NETjet_ReadIC( cs, ICC_RBCH ) >> 5 ) & 3 ) )
+		{
+			case 3 :
+				break;
+
+			case 0 :
+				printk( KERN_WARNING "NETspider-U: NETjet-S PCI card found\n" );
+				continue;
+
+			default :
+				printk( KERN_WARNING "NETspider-U: No PCI card found\n" );
+				return 0;
+                }
+                break;
+	}
+#else
+
+	printk(KERN_WARNING "NETspider-U: NO_PCI_BIOS\n");
+	printk(KERN_WARNING "NETspider-U: unable to config NETspider-U PCI\n");
+	return (0);
+
+#endif /* CONFIG_PCI */
+
+	bytecnt = 256;
+
+	printk(KERN_INFO
+		"NETspider-U: PCI card configured at %#lx IRQ %d\n",
+		cs->hw.njet.base, cs->irq);
+	if (!request_region(cs->hw.njet.base, bytecnt, "netspider-u isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %#lx-%#lx already in use\n",
+		       CardType[card->typ],
+		       cs->hw.njet.base,
+		       cs->hw.njet.base + bytecnt);
+		return (0);
+	}
+	setup_icc(cs);
+	cs->readisac  = &NETjet_ReadIC;
+	cs->writeisac = &NETjet_WriteIC;
+	cs->readisacfifo  = &NETjet_ReadICfifo;
+	cs->writeisacfifo = &NETjet_WriteICfifo;
+	cs->BC_Read_Reg  = &dummyrr;
+	cs->BC_Write_Reg = &dummywr;
+	cs->BC_Send_Data = &netjet_fill_dma;
+	cs->cardmsg = &NETjet_U_card_msg;
+	cs->irq_func = &netjet_u_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+	ICCVersion(cs, "NETspider-U:");
+	return (1);
+}
diff --git a/drivers/isdn/hisax/q931.c b/drivers/isdn/hisax/q931.c
new file mode 100644
index 000000000000..170fcd4a3984
--- /dev/null
+++ b/drivers/isdn/hisax/q931.c
@@ -0,0 +1,1522 @@
+/* $Id: q931.c,v 1.12.2.3 2004/01/13 14:31:26 keil Exp $
+ *
+ * code to decode ITU Q.931 call control messages
+ *
+ * Author       Jan den Ouden
+ * Copyright    by Jan den Ouden
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Changelog:
+ *
+ * Pauline Middelink    general improvements
+ * Beat Doebeli         cause texts, display information element
+ * Karsten Keil         cause texts, display information element for 1TR6
+ *
+ */
+
+
+#include "hisax.h"
+#include "l3_1tr6.h"
+
+void
+iecpy(u_char * dest, u_char * iestart, int ieoffset)
+{
+	u_char *p;
+	int l;
+
+	p = iestart + ieoffset + 2;
+	l = iestart[1] - ieoffset;
+	while (l--)
+		*dest++ = *p++;
+	*dest++ = '\0';
+}
+
+/*
+ * According to Table 4-2/Q.931
+ */
+static
+struct MessageType {
+	u_char nr;
+	char *descr;
+} mtlist[] = {
+
+	{
+		0x1, "ALERTING"
+	},
+	{
+		0x2, "CALL PROCEEDING"
+	},
+	{
+		0x7, "CONNECT"
+	},
+	{
+		0xf, "CONNECT ACKNOWLEDGE"
+	},
+	{
+		0x3, "PROGRESS"
+	},
+	{
+		0x5, "SETUP"
+	},
+	{
+		0xd, "SETUP ACKNOWLEDGE"
+	},
+	{
+		0x24, "HOLD"
+	},
+	{
+		0x28, "HOLD ACKNOWLEDGE"
+	},
+	{
+		0x30, "HOLD REJECT"
+	},
+	{
+		0x31, "RETRIEVE"
+	},
+	{
+		0x33, "RETRIEVE ACKNOWLEDGE"
+	},
+	{
+		0x37, "RETRIEVE REJECT"
+	},
+	{
+		0x26, "RESUME"
+	},
+	{
+		0x2e, "RESUME ACKNOWLEDGE"
+	},
+	{
+		0x22, "RESUME REJECT"
+	},
+	{
+		0x25, "SUSPEND"
+	},
+	{
+		0x2d, "SUSPEND ACKNOWLEDGE"
+	},
+	{
+		0x21, "SUSPEND REJECT"
+	},
+	{
+		0x20, "USER INFORMATION"
+	},
+	{
+		0x45, "DISCONNECT"
+	},
+	{
+		0x4d, "RELEASE"
+	},
+	{
+		0x5a, "RELEASE COMPLETE"
+	},
+	{
+		0x46, "RESTART"
+	},
+	{
+		0x4e, "RESTART ACKNOWLEDGE"
+	},
+	{
+		0x60, "SEGMENT"
+	},
+	{
+		0x79, "CONGESTION CONTROL"
+	},
+	{
+		0x7b, "INFORMATION"
+	},
+	{
+		0x62, "FACILITY"
+	},
+	{
+		0x6e, "NOTIFY"
+	},
+	{
+		0x7d, "STATUS"
+	},
+	{
+		0x75, "STATUS ENQUIRY"
+	}
+};
+
+#define MTSIZE sizeof(mtlist)/sizeof(struct MessageType)
+
+static
+struct MessageType mt_n0[] =
+{
+	{MT_N0_REG_IND, "REGister INDication"},
+	{MT_N0_CANC_IND, "CANCel INDication"},
+	{MT_N0_FAC_STA, "FACility STAtus"},
+	{MT_N0_STA_ACK, "STAtus ACKnowledge"},
+	{MT_N0_STA_REJ, "STAtus REJect"},
+	{MT_N0_FAC_INF, "FACility INFormation"},
+	{MT_N0_INF_ACK, "INFormation ACKnowledge"},
+	{MT_N0_INF_REJ, "INFormation REJect"},
+	{MT_N0_CLOSE, "CLOSE"},
+	{MT_N0_CLO_ACK, "CLOse ACKnowledge"}
+};
+
+#define MT_N0_LEN (sizeof(mt_n0) / sizeof(struct MessageType))
+
+static
+struct MessageType mt_n1[] =
+{
+	{MT_N1_ESC, "ESCape"},
+	{MT_N1_ALERT, "ALERT"},
+	{MT_N1_CALL_SENT, "CALL SENT"},
+	{MT_N1_CONN, "CONNect"},
+	{MT_N1_CONN_ACK, "CONNect ACKnowledge"},
+	{MT_N1_SETUP, "SETUP"},
+	{MT_N1_SETUP_ACK, "SETUP ACKnowledge"},
+	{MT_N1_RES, "RESume"},
+	{MT_N1_RES_ACK, "RESume ACKnowledge"},
+	{MT_N1_RES_REJ, "RESume REJect"},
+	{MT_N1_SUSP, "SUSPend"},
+	{MT_N1_SUSP_ACK, "SUSPend ACKnowledge"},
+	{MT_N1_SUSP_REJ, "SUSPend REJect"},
+	{MT_N1_USER_INFO, "USER INFO"},
+	{MT_N1_DET, "DETach"},
+	{MT_N1_DISC, "DISConnect"},
+	{MT_N1_REL, "RELease"},
+	{MT_N1_REL_ACK, "RELease ACKnowledge"},
+	{MT_N1_CANC_ACK, "CANCel ACKnowledge"},
+	{MT_N1_CANC_REJ, "CANCel REJect"},
+	{MT_N1_CON_CON, "CONgestion CONtrol"},
+	{MT_N1_FAC, "FACility"},
+	{MT_N1_FAC_ACK, "FACility ACKnowledge"},
+	{MT_N1_FAC_CAN, "FACility CANcel"},
+	{MT_N1_FAC_REG, "FACility REGister"},
+	{MT_N1_FAC_REJ, "FACility REJect"},
+	{MT_N1_INFO, "INFOrmation"},
+	{MT_N1_REG_ACK, "REGister ACKnowledge"},
+	{MT_N1_REG_REJ, "REGister REJect"},
+	{MT_N1_STAT, "STATus"}
+};
+
+#define MT_N1_LEN (sizeof(mt_n1) / sizeof(struct MessageType))
+
+
+static int
+prbits(char *dest, u_char b, int start, int len)
+{
+	char *dp = dest;
+
+	b = b << (8 - start);
+	while (len--) {
+		if (b & 0x80)
+			*dp++ = '1';
+		else
+			*dp++ = '0';
+		b = b << 1;
+	}
+	return (dp - dest);
+}
+
+static
+u_char *
+skipext(u_char * p)
+{
+	while (!(*p++ & 0x80));
+	return (p);
+}
+
+/*
+ * Cause Values According to Q.850
+ * edescr: English description
+ * ddescr: German description used by Swissnet II (Swiss Telecom
+ *         not yet written...
+ */
+
+static
+struct CauseValue {
+	u_char nr;
+	char *edescr;
+	char *ddescr;
+} cvlist[] = {
+
+	{
+		0x01, "Unallocated (unassigned) number", "Nummer nicht zugeteilt"
+	},
+	{
+		0x02, "No route to specified transit network", ""
+	},
+	{
+		0x03, "No route to destination", ""
+	},
+	{
+		0x04, "Send special information tone", ""
+	},
+	{
+		0x05, "Misdialled trunk prefix", ""
+	},
+	{
+		0x06, "Channel unacceptable", "Kanal nicht akzeptierbar"
+	},
+	{
+		0x07, "Channel awarded and being delivered in an established channel", ""
+	},
+	{
+		0x08, "Preemption", ""
+	},
+	{
+		0x09, "Preemption - circuit reserved for reuse", ""
+	},
+	{
+		0x10, "Normal call clearing", "Normale Ausloesung"
+	},
+	{
+		0x11, "User busy", "TNB besetzt"
+	},
+	{
+		0x12, "No user responding", ""
+	},
+	{
+		0x13, "No answer from user (user alerted)", ""
+	},
+	{
+		0x14, "Subscriber absent", ""
+	},
+	{
+		0x15, "Call rejected", ""
+	},
+	{
+		0x16, "Number changed", ""
+	},
+	{
+		0x1a, "non-selected user clearing", ""
+	},
+	{
+		0x1b, "Destination out of order", ""
+	},
+	{
+		0x1c, "Invalid number format (address incomplete)", ""
+	},
+	{
+		0x1d, "Facility rejected", ""
+	},
+	{
+		0x1e, "Response to Status enquiry", ""
+	},
+	{
+		0x1f, "Normal, unspecified", ""
+	},
+	{
+		0x22, "No circuit/channel available", ""
+	},
+	{
+		0x26, "Network out of order", ""
+	},
+	{
+		0x27, "Permanent frame mode connection out-of-service", ""
+	},
+	{
+		0x28, "Permanent frame mode connection operational", ""
+	},
+	{
+		0x29, "Temporary failure", ""
+	},
+	{
+		0x2a, "Switching equipment congestion", ""
+	},
+	{
+		0x2b, "Access information discarded", ""
+	},
+	{
+		0x2c, "Requested circuit/channel not available", ""
+	},
+	{
+		0x2e, "Precedence call blocked", ""
+	},
+	{
+		0x2f, "Resource unavailable, unspecified", ""
+	},
+	{
+		0x31, "Quality of service unavailable", ""
+	},
+	{
+		0x32, "Requested facility not subscribed", ""
+	},
+	{
+		0x35, "Outgoing calls barred within CUG", ""
+	},
+	{
+		0x37, "Incoming calls barred within CUG", ""
+	},
+	{
+		0x39, "Bearer capability not authorized", ""
+	},
+	{
+		0x3a, "Bearer capability not presently available", ""
+	},
+	{
+		0x3e, "Inconsistency in designated outgoing access information and subscriber class ", " "
+	},
+	{
+		0x3f, "Service or option not available, unspecified", ""
+	},
+	{
+		0x41, "Bearer capability not implemented", ""
+	},
+	{
+		0x42, "Channel type not implemented", ""
+	},
+	{
+		0x43, "Requested facility not implemented", ""
+	},
+	{
+		0x44, "Only restricted digital information bearer capability is available", ""
+	},
+	{
+		0x4f, "Service or option not implemented", ""
+	},
+	{
+		0x51, "Invalid call reference value", ""
+	},
+	{
+		0x52, "Identified channel does not exist", ""
+	},
+	{
+		0x53, "A suspended call exists, but this call identity does not", ""
+	},
+	{
+		0x54, "Call identity in use", ""
+	},
+	{
+		0x55, "No call suspended", ""
+	},
+	{
+		0x56, "Call having the requested call identity has been cleared", ""
+	},
+	{
+		0x57, "User not member of CUG", ""
+	},
+	{
+		0x58, "Incompatible destination", ""
+	},
+	{
+		0x5a, "Non-existent CUG", ""
+	},
+	{
+		0x5b, "Invalid transit network selection", ""
+	},
+	{
+		0x5f, "Invalid message, unspecified", ""
+	},
+	{
+		0x60, "Mandatory information element is missing", ""
+	},
+	{
+		0x61, "Message type non-existent or not implemented", ""
+	},
+	{
+		0x62, "Message not compatible with call state or message type non-existent or not implemented ", " "
+	},
+	{
+		0x63, "Information element/parameter non-existent or not implemented", ""
+	},
+	{
+		0x64, "Invalid information element contents", ""
+	},
+	{
+		0x65, "Message not compatible with call state", ""
+	},
+	{
+		0x66, "Recovery on timer expiry", ""
+	},
+	{
+		0x67, "Parameter non-existent or not implemented - passed on", ""
+	},
+	{
+		0x6e, "Message with unrecognized parameter discarded", ""
+	},
+	{
+		0x6f, "Protocol error, unspecified", ""
+	},
+	{
+		0x7f, "Interworking, unspecified", ""
+	},
+};
+
+#define CVSIZE sizeof(cvlist)/sizeof(struct CauseValue)
+
+static
+int
+prcause(char *dest, u_char * p)
+{
+	u_char *end;
+	char *dp = dest;
+	int i, cause;
+
+	end = p + p[1] + 1;
+	p += 2;
+	dp += sprintf(dp, "    coding ");
+	dp += prbits(dp, *p, 7, 2);
+	dp += sprintf(dp, " location ");
+	dp += prbits(dp, *p, 4, 4);
+	*dp++ = '\n';
+	p = skipext(p);
+
+	cause = 0x7f & *p++;
+
+	/* locate cause value */
+	for (i = 0; i < CVSIZE; i++)
+		if (cvlist[i].nr == cause)
+			break;
+
+	/* display cause value if it exists */
+	if (i == CVSIZE)
+		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+	else
+		dp += sprintf(dp, "  cause value %x : %s \n", cause, cvlist[i].edescr);
+
+	while (!0) {
+		if (p > end)
+			break;
+		dp += sprintf(dp, "    diag attribute %d ", *p++ & 0x7f);
+		dp += sprintf(dp, " rej %d ", *p & 0x7f);
+		if (*p & 0x80) {
+			*dp++ = '\n';
+			break;
+		} else
+			dp += sprintf(dp, " av %d\n", (*++p) & 0x7f);
+	}
+	return (dp - dest);
+
+}
+
+static
+struct MessageType cause_1tr6[] =
+{
+	{CAUSE_InvCRef, "Invalid Call Reference"},
+	{CAUSE_BearerNotImpl, "Bearer Service Not Implemented"},
+	{CAUSE_CIDunknown, "Caller Identity unknown"},
+	{CAUSE_CIDinUse, "Caller Identity in Use"},
+	{CAUSE_NoChans, "No Channels available"},
+	{CAUSE_FacNotImpl, "Facility Not Implemented"},
+	{CAUSE_FacNotSubscr, "Facility Not Subscribed"},
+	{CAUSE_OutgoingBarred, "Outgoing calls barred"},
+	{CAUSE_UserAccessBusy, "User Access Busy"},
+	{CAUSE_NegativeGBG, "Negative GBG"},
+	{CAUSE_UnknownGBG, "Unknown  GBG"},
+	{CAUSE_NoSPVknown, "No SPV known"},
+	{CAUSE_DestNotObtain, "Destination not obtainable"},
+	{CAUSE_NumberChanged, "Number changed"},
+	{CAUSE_OutOfOrder, "Out Of Order"},
+	{CAUSE_NoUserResponse, "No User Response"},
+	{CAUSE_UserBusy, "User Busy"},
+	{CAUSE_IncomingBarred, "Incoming Barred"},
+	{CAUSE_CallRejected, "Call Rejected"},
+	{CAUSE_NetworkCongestion, "Network Congestion"},
+	{CAUSE_RemoteUser, "Remote User initiated"},
+	{CAUSE_LocalProcErr, "Local Procedure Error"},
+	{CAUSE_RemoteProcErr, "Remote Procedure Error"},
+	{CAUSE_RemoteUserSuspend, "Remote User Suspend"},
+	{CAUSE_RemoteUserResumed, "Remote User Resumed"},
+	{CAUSE_UserInfoDiscarded, "User Info Discarded"}
+};
+
+int cause_1tr6_len = (sizeof(cause_1tr6) / sizeof(struct MessageType));
+
+static int
+prcause_1tr6(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int i, cause;
+
+	p++;
+	if (0 == *p) {
+		dp += sprintf(dp, "   OK (cause length=0)\n");
+		return (dp - dest);
+	} else if (*p > 1) {
+		dp += sprintf(dp, "    coding ");
+		dp += prbits(dp, p[2], 7, 2);
+		dp += sprintf(dp, " location ");
+		dp += prbits(dp, p[2], 4, 4);
+		*dp++ = '\n';
+	}
+	p++;
+	cause = 0x7f & *p;
+
+	/* locate cause value */
+	for (i = 0; i < cause_1tr6_len; i++)
+		if (cause_1tr6[i].nr == cause)
+			break;
+
+	/* display cause value if it exists */
+	if (i == cause_1tr6_len)
+		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+	else
+		dp += sprintf(dp, "  cause value %x : %s \n", cause, cause_1tr6[i].descr);
+
+	return (dp - dest);
+
+}
+
+static int
+prchident(char *dest, u_char * p)
+{
+	char *dp = dest;
+
+	p += 2;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static int
+prcalled(char *dest, u_char * p)
+{
+	int l;
+	char *dp = dest;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p++, 8, 8);
+	*dp++ = '\n';
+	dp += sprintf(dp, "    number digits ");
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+static int
+prcalling(char *dest, u_char * p)
+{
+	int l;
+	char *dp = dest;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	if (!(*p & 0x80)) {
+		dp += sprintf(dp, "    octet 3a ");
+		dp += prbits(dp, *++p, 8, 8);
+		*dp++ = '\n';
+		l--;
+	};
+	p++;
+
+	dp += sprintf(dp, "    number digits ");
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static
+int
+prbearer(char *dest, u_char * p)
+{
+	char *dp = dest, ch;
+
+	p += 2;
+	dp += sprintf(dp, "    octet 3  ");
+	dp += prbits(dp, *p++, 8, 8);
+	*dp++ = '\n';
+	dp += sprintf(dp, "    octet 4  ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	if ((*p++ & 0x1f) == 0x18) {
+		dp += sprintf(dp, "    octet 4.1 ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	/* check for user information layer 1 */
+	if ((*p & 0x60) == 0x20) {
+		ch = ' ';
+		do {
+			dp += sprintf(dp, "    octet 5%c ", ch);
+			dp += prbits(dp, *p, 8, 8);
+			*dp++ = '\n';
+			if (ch == ' ')
+				ch = 'a';
+			else
+				ch++;
+		}
+		while (!(*p++ & 0x80));
+	}
+	/* check for user information layer 2 */
+	if ((*p & 0x60) == 0x40) {
+		dp += sprintf(dp, "    octet 6  ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	/* check for user information layer 3 */
+	if ((*p & 0x60) == 0x60) {
+		dp += sprintf(dp, "    octet 7  ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	return (dp - dest);
+}
+
+
+static
+int
+prbearer_ni1(char *dest, u_char * p)
+{
+	char *dp = dest;
+	u_char len;
+
+	p++;
+	len = *p++;
+	dp += sprintf(dp, "    octet 3  ");
+	dp += prbits(dp, *p, 8, 8);
+	switch (*p++) {
+		case 0x80:
+			dp += sprintf(dp, " Speech");
+			break;
+		case 0x88:
+			dp += sprintf(dp, " Unrestricted digital information");
+			break;
+		case 0x90:
+			dp += sprintf(dp, " 3.1 kHz audio");
+			break;
+		default:
+			dp += sprintf(dp, " Unknown information-transfer capability");
+	}
+	*dp++ = '\n';
+	dp += sprintf(dp, "    octet 4  ");
+	dp += prbits(dp, *p, 8, 8);
+	switch (*p++) {
+		case 0x90:
+			dp += sprintf(dp, " 64 kbps, circuit mode");
+			break;
+		case 0xc0:
+			dp += sprintf(dp, " Packet mode");
+			break;
+		default:
+			dp += sprintf(dp, " Unknown transfer mode");
+	}
+	*dp++ = '\n';
+	if (len > 2) {
+		dp += sprintf(dp, "    octet 5  ");
+		dp += prbits(dp, *p, 8, 8);
+		switch (*p++) {
+			case 0x21:
+				dp += sprintf(dp, " Rate adaption\n");
+				dp += sprintf(dp, "    octet 5a ");
+				dp += prbits(dp, *p, 8, 8);
+				break;
+			case 0xa2:
+				dp += sprintf(dp, " u-law");
+				break;
+			default:
+				dp += sprintf(dp, " Unknown UI layer 1 protocol");
+		}
+		*dp++ = '\n';
+	}
+	return (dp - dest);
+}
+
+static int
+general(char *dest, u_char * p)
+{
+	char *dp = dest;
+	char ch = ' ';
+	int l, octet = 3;
+
+	p++;
+	l = *p++;
+	/* Iterate over all octets in the information element */
+	while (l--) {
+		dp += sprintf(dp, "    octet %d%c ", octet, ch);
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+
+		/* last octet in group? */
+		if (*p & 0x80) {
+			octet++;
+			ch = ' ';
+		} else if (ch == ' ')
+			ch = 'a';
+		else
+			ch++;
+	}
+	return (dp - dest);
+}
+
+static int
+general_ni1(char *dest, u_char * p)
+{
+	char *dp = dest;
+	char ch = ' ';
+	int l, octet = 3;
+
+	p++;
+	l = *p++;
+	/* Iterate over all octets in the information element */
+	while (l--) {
+		dp += sprintf(dp, "    octet %d%c ", octet, ch);
+		dp += prbits(dp, *p, 8, 8);
+		*dp++ = '\n';
+
+		/* last octet in group? */
+		if (*p++ & 0x80) {
+			octet++;
+			ch = ' ';
+		} else if (ch == ' ')
+			ch = 'a';
+		else
+			ch++;
+	}
+	return (dp - dest);
+}
+
+static int
+prcharge(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int l;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    GEA ");
+	dp += prbits(dp, *p++, 8, 8);
+	dp += sprintf(dp, "  Anzahl: ");
+	/* Iterate over all octets in the * information element */
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+static int
+prtext(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int l;
+
+	p++;
+	l = *p++;
+	dp += sprintf(dp, "    ");
+	/* Iterate over all octets in the * information element */
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static int
+prfeatureind(char *dest, u_char * p)
+{
+	char *dp = dest;
+
+	p += 2; /* skip id, len */
+	dp += sprintf(dp, "    octet 3  ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	if (!(*p++ & 80)) {
+		dp += sprintf(dp, "    octet 4  ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	dp += sprintf(dp, "    Status:  ");
+	switch (*p) {
+		case 0:
+			dp += sprintf(dp, "Idle");
+			break;
+		case 1:
+			dp += sprintf(dp, "Active");
+			break;
+		case 2:
+			dp += sprintf(dp, "Prompt");
+			break;
+		case 3:
+			dp += sprintf(dp, "Pending");
+			break;
+		default:
+			dp += sprintf(dp, "(Reserved)");
+			break;
+	}
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static
+struct DTag { /* Display tags */
+	u_char nr;
+	char *descr;
+} dtaglist[] = {
+	{ 0x82, "Continuation" },
+	{ 0x83, "Called address" },
+	{ 0x84, "Cause" },
+	{ 0x85, "Progress indicator" },
+	{ 0x86, "Notification indicator" },
+	{ 0x87, "Prompt" },
+	{ 0x88, "Accumlated digits" },
+	{ 0x89, "Status" },
+	{ 0x8a, "Inband" },
+	{ 0x8b, "Calling address" },
+	{ 0x8c, "Reason" },
+	{ 0x8d, "Calling party name" },
+	{ 0x8e, "Called party name" },
+	{ 0x8f, "Orignal called name" },
+	{ 0x90, "Redirecting name" },
+	{ 0x91, "Connected name" },
+	{ 0x92, "Originating restrictions" },
+	{ 0x93, "Date & time of day" },
+	{ 0x94, "Call Appearance ID" },
+	{ 0x95, "Feature address" },
+	{ 0x96, "Redirection name" },
+	{ 0x9e, "Text" },
+};
+#define DTAGSIZE sizeof(dtaglist)/sizeof(struct DTag)
+
+static int
+disptext_ni1(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int l, tag, len, i;
+
+	p++;
+	l = *p++ - 1;
+	if (*p++ != 0x80) {
+		dp += sprintf(dp, "    Unknown display type\n");
+		return (dp - dest);
+	}
+	/* Iterate over all tag,length,text fields */
+	while (l > 0) {
+		tag = *p++;
+		len = *p++;
+		l -= len + 2;
+		/* Don't space or skip */
+		if ((tag == 0x80) || (tag == 0x81)) p++;
+		else {
+			for (i = 0; i < DTAGSIZE; i++)
+				if (tag == dtaglist[i].nr)
+					break;
+
+			/* When not found, give appropriate msg */
+			if (i != DTAGSIZE) {
+				dp += sprintf(dp, "    %s: ", dtaglist[i].descr);
+				while (len--)
+					*dp++ = *p++;
+			} else {
+				dp += sprintf(dp, "    (unknown display tag %2x): ", tag);
+				while (len--)
+					*dp++ = *p++;
+			}
+			dp += sprintf(dp, "\n");
+                }
+	}
+	return (dp - dest);
+}
+static int
+display(char *dest, u_char * p)
+{
+	char *dp = dest;
+	char ch = ' ';
+	int l, octet = 3;
+
+	p++;
+	l = *p++;
+	/* Iterate over all octets in the * display-information element */
+	dp += sprintf(dp, "   \"");
+	while (l--) {
+		dp += sprintf(dp, "%c", *p++);
+
+		/* last octet in group? */
+		if (*p & 0x80) {
+			octet++;
+			ch = ' ';
+		} else if (ch == ' ')
+			ch = 'a';
+
+		else
+			ch++;
+	}
+	*dp++ = '\"';
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+int
+prfacility(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int l, l2;
+
+	p++;
+	l = *p++;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p++, 8, 8);
+	dp += sprintf(dp, "\n");
+	l -= 1;
+
+	while (l > 0) {
+		dp += sprintf(dp, "   octet 4 ");
+		dp += prbits(dp, *p++, 8, 8);
+		dp += sprintf(dp, "\n");
+		dp += sprintf(dp, "   octet 5 %d\n", l2 = *p++ & 0x7f);
+		l -= 2;
+		dp += sprintf(dp, "   contents ");
+		while (l2--) {
+			dp += sprintf(dp, "%2x ", *p++);
+			l--;
+		}
+		dp += sprintf(dp, "\n");
+	}
+
+	return (dp - dest);
+}
+
+static
+struct InformationElement {
+	u_char nr;
+	char *descr;
+	int (*f) (char *, u_char *);
+} ielist[] = {
+
+	{
+		0x00, "Segmented message", general
+	},
+	{
+		0x04, "Bearer capability", prbearer
+	},
+	{
+		0x08, "Cause", prcause
+	},
+	{
+		0x10, "Call identity", general
+	},
+	{
+		0x14, "Call state", general
+	},
+	{
+		0x18, "Channel identification", prchident
+	},
+	{
+		0x1c, "Facility", prfacility
+	},
+	{
+		0x1e, "Progress indicator", general
+	},
+	{
+		0x20, "Network-specific facilities", general
+	},
+	{
+		0x27, "Notification indicator", general
+	},
+	{
+		0x28, "Display", display
+	},
+	{
+		0x29, "Date/Time", general
+	},
+	{
+		0x2c, "Keypad facility", general
+	},
+	{
+		0x34, "Signal", general
+	},
+	{
+		0x40, "Information rate", general
+	},
+	{
+		0x42, "End-to-end delay", general
+	},
+	{
+		0x43, "Transit delay selection and indication", general
+	},
+	{
+		0x44, "Packet layer binary parameters", general
+	},
+	{
+		0x45, "Packet layer window size", general
+	},
+	{
+		0x46, "Packet size", general
+	},
+	{
+		0x47, "Closed user group", general
+	},
+	{
+		0x4a, "Reverse charge indication", general
+	},
+	{
+		0x6c, "Calling party number", prcalling
+	},
+	{
+		0x6d, "Calling party subaddress", general
+	},
+	{
+		0x70, "Called party number", prcalled
+	},
+	{
+		0x71, "Called party subaddress", general
+	},
+	{
+		0x74, "Redirecting number", general
+	},
+	{
+		0x78, "Transit network selection", general
+	},
+	{
+		0x79, "Restart indicator", general
+	},
+	{
+		0x7c, "Low layer compatibility", general
+	},
+	{
+		0x7d, "High layer compatibility", general
+	},
+	{
+		0x7e, "User-user", general
+	},
+	{
+		0x7f, "Escape for extension", general
+	},
+};
+
+
+#define IESIZE sizeof(ielist)/sizeof(struct InformationElement)
+
+static
+struct InformationElement ielist_ni1[] = {
+	{ 0x04, "Bearer Capability", prbearer_ni1 },
+	{ 0x08, "Cause", prcause },
+	{ 0x14, "Call State", general_ni1 },
+	{ 0x18, "Channel Identification", prchident },
+	{ 0x1e, "Progress Indicator", general_ni1 },
+	{ 0x27, "Notification Indicator", general_ni1 },
+	{ 0x2c, "Keypad Facility", prtext },
+	{ 0x32, "Information Request", general_ni1 },
+	{ 0x34, "Signal", general_ni1 },
+	{ 0x38, "Feature Activation", general_ni1 },
+	{ 0x39, "Feature Indication", prfeatureind },
+	{ 0x3a, "Service Profile Identification (SPID)", prtext },
+	{ 0x3b, "Endpoint Identifier", general_ni1 },
+	{ 0x6c, "Calling Party Number", prcalling },
+	{ 0x6d, "Calling Party Subaddress", general_ni1 },
+	{ 0x70, "Called Party Number", prcalled },
+	{ 0x71, "Called Party Subaddress", general_ni1 },
+	{ 0x74, "Redirecting Number", general_ni1 },
+	{ 0x78, "Transit Network Selection", general_ni1 },
+	{ 0x7c, "Low Layer Compatibility", general_ni1 },
+	{ 0x7d, "High Layer Compatibility", general_ni1 },
+};
+
+
+#define IESIZE_NI1 sizeof(ielist_ni1)/sizeof(struct InformationElement)
+
+static
+struct InformationElement ielist_ni1_cs5[] = {
+	{ 0x1d, "Operator system access", general_ni1 },
+	{ 0x2a, "Display text", disptext_ni1 },
+};
+
+#define IESIZE_NI1_CS5 sizeof(ielist_ni1_cs5)/sizeof(struct InformationElement)
+
+static
+struct InformationElement ielist_ni1_cs6[] = {
+	{ 0x7b, "Call appearance", general_ni1 },
+};
+
+#define IESIZE_NI1_CS6 sizeof(ielist_ni1_cs6)/sizeof(struct InformationElement)
+
+static struct InformationElement we_0[] =
+{
+	{WE0_cause, "Cause", prcause_1tr6},
+	{WE0_connAddr, "Connecting Address", prcalled},
+	{WE0_callID, "Call IDentity", general},
+	{WE0_chanID, "Channel IDentity", general},
+	{WE0_netSpecFac, "Network Specific Facility", general},
+	{WE0_display, "Display", general},
+	{WE0_keypad, "Keypad", general},
+	{WE0_origAddr, "Origination Address", prcalled},
+	{WE0_destAddr, "Destination Address", prcalled},
+	{WE0_userInfo, "User Info", general}
+};
+
+#define WE_0_LEN (sizeof(we_0) / sizeof(struct InformationElement))
+
+static struct InformationElement we_6[] =
+{
+	{WE6_serviceInd, "Service Indicator", general},
+	{WE6_chargingInfo, "Charging Information", prcharge},
+	{WE6_date, "Date", prtext},
+	{WE6_facSelect, "Facility Select", general},
+	{WE6_facStatus, "Facility Status", general},
+	{WE6_statusCalled, "Status Called", general},
+	{WE6_addTransAttr, "Additional Transmission Attributes", general}
+};
+#define WE_6_LEN (sizeof(we_6) / sizeof(struct InformationElement))
+
+int
+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);
+}
+
+void
+LogFrame(struct IsdnCardState *cs, u_char * buf, int size)
+{
+	char *dp;
+
+	if (size < 1)
+		return;
+	dp = cs->dlog;
+	if (size < MAX_DLOG_SPACE / 3 - 10) {
+		*dp++ = 'H';
+		*dp++ = 'E';
+		*dp++ = 'X';
+		*dp++ = ':';
+		dp += QuickHex(dp, buf, size);
+		dp--;
+		*dp++ = '\n';
+		*dp = 0;
+		HiSax_putstatus(cs, NULL, cs->dlog);
+	} else
+		HiSax_putstatus(cs, "LogFrame: ", "warning Frame too big (%d)", size);
+}
+
+void
+dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir)
+{
+	u_char *bend, *buf;
+	char *dp;
+	unsigned char pd, cr_l, cr, mt;
+	unsigned char sapi, tei, ftyp;
+	int i, cset = 0, cs_old = 0, cs_fest = 0;
+	int size, finish = 0;
+
+	if (skb->len < 3)
+		return;
+	/* display header */
+	dp = cs->dlog;
+	dp += jiftime(dp, jiffies);
+	*dp++ = ' ';
+	sapi = skb->data[0] >> 2;
+	tei  = skb->data[1] >> 1;
+	ftyp = skb->data[2];
+	buf = skb->data;
+	dp += sprintf(dp, "frame %s ", dir ? "network->user" : "user->network");
+	size = skb->len;
+	
+	if (tei == GROUP_TEI) {
+		if (sapi == CTRL_SAPI) { /* sapi 0 */
+			if (ftyp == 3) {
+				dp += sprintf(dp, "broadcast\n");
+				buf += 3;
+				size -= 3;
+			} else {
+				dp += sprintf(dp, "no UI broadcast\n");
+				finish = 1;
+			}
+		} else if (sapi == TEI_SAPI) {
+			dp += sprintf(dp, "tei management\n");
+			finish = 1;
+		} else {
+			dp += sprintf(dp, "unknown sapi %d broadcast\n", sapi);
+			finish = 1;
+		}
+	} else {
+		if (sapi == CTRL_SAPI) {
+			if (!(ftyp & 1)) { /* IFrame */
+				dp += sprintf(dp, "with tei %d\n", tei);
+				buf += 4;
+				size -= 4;
+			} else {
+				dp += sprintf(dp, "SFrame with tei %d\n", tei);
+				finish = 1;
+			}
+		} else {
+			dp += sprintf(dp, "unknown sapi %d tei %d\n", sapi, tei);
+			finish = 1;
+		}
+	}
+	bend = skb->data + skb->len;
+	if (buf >= bend) {
+		dp += sprintf(dp, "frame too short\n");
+		finish = 1;
+	}
+	if (finish) {
+		*dp = 0;
+		HiSax_putstatus(cs, NULL, cs->dlog);
+		return;
+	}
+	if ((0xfe & buf[0]) == PROTO_DIS_N0) {	/* 1TR6 */
+		/* locate message type */
+		pd = *buf++;
+		cr_l = *buf++;
+		if (cr_l)
+			cr = *buf++;
+		else
+			cr = 0;
+		mt = *buf++;
+		if (pd == PROTO_DIS_N0) {	/* N0 */
+			for (i = 0; i < MT_N0_LEN; i++)
+				if (mt_n0[i].nr == mt)
+					break;
+			/* display message type if it exists */
+			if (i == MT_N0_LEN)
+				dp += sprintf(dp, "callref %d %s size %d unknown message type N0 %x!\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt);
+			else
+				dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt_n0[i].descr);
+		} else {	/* N1 */
+			for (i = 0; i < MT_N1_LEN; i++)
+				if (mt_n1[i].nr == mt)
+					break;
+			/* display message type if it exists */
+			if (i == MT_N1_LEN)
+				dp += sprintf(dp, "callref %d %s size %d unknown message type N1 %x!\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt);
+			else
+				dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt_n1[i].descr);
+		}
+
+		/* display each information element */
+		while (buf < bend) {
+			/* Is it a single octet information element? */
+			if (*buf & 0x80) {
+				switch ((*buf >> 4) & 7) {
+					case 1:
+						dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
+						cs_old = cset;
+						cset = *buf & 7;
+						cs_fest = *buf & 8;
+						break;
+					case 3:
+						dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
+						break;
+					case 2:
+						if (*buf == 0xa0) {
+							dp += sprintf(dp, "  More data\n");
+							break;
+						}
+						if (*buf == 0xa1) {
+							dp += sprintf(dp, "  Sending complete\n");
+						}
+						break;
+						/* fall through */
+					default:
+						dp += sprintf(dp, "  Reserved %x\n", *buf);
+						break;
+				}
+				buf++;
+				continue;
+			}
+			/* No, locate it in the table */
+			if (cset == 0) {
+				for (i = 0; i < WE_0_LEN; i++)
+					if (*buf == we_0[i].nr)
+						break;
+
+				/* When found, give appropriate msg */
+				if (i != WE_0_LEN) {
+					dp += sprintf(dp, "  %s\n", we_0[i].descr);
+					dp += we_0[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+			} else if (cset == 6) {
+				for (i = 0; i < WE_6_LEN; i++)
+					if (*buf == we_6[i].nr)
+						break;
+
+				/* When found, give appropriate msg */
+				if (i != WE_6_LEN) {
+					dp += sprintf(dp, "  %s\n", we_6[i].descr);
+					dp += we_6[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+			} else
+				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+			/* Skip to next element */
+			if (cs_fest == 8) {
+				cset = cs_old;
+				cs_old = 0;
+				cs_fest = 0;
+			}
+			buf += buf[1] + 2;
+		}
+	} else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_NI1)) {	/* NI-1 */
+		/* locate message type */
+		buf++;
+		cr_l = *buf++;
+		if (cr_l)
+			cr = *buf++;
+		else
+			cr = 0;
+		mt = *buf++;
+		for (i = 0; i < MTSIZE; i++)
+			if (mtlist[i].nr == mt)
+				break;
+
+		/* display message type if it exists */
+		if (i == MTSIZE)
+			dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mt);
+		else
+			dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mtlist[i].descr);
+
+		/* display each information element */
+		while (buf < bend) {
+			/* Is it a single octet information element? */
+			if (*buf & 0x80) {
+				switch ((*buf >> 4) & 7) {
+					case 1:
+						dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
+						cs_old = cset;
+						cset = *buf & 7;
+						cs_fest = *buf & 8;
+						break;
+					default:
+						dp += sprintf(dp, "  Unknown single-octet IE %x\n", *buf);
+						break;
+				}
+				buf++;
+				continue;
+			}
+			/* No, locate it in the table */
+			if (cset == 0) {
+				for (i = 0; i < IESIZE; i++)
+					if (*buf == ielist_ni1[i].nr)
+						break;
+
+				/* When not found, give appropriate msg */
+				if (i != IESIZE) {
+					dp += sprintf(dp, "  %s\n", ielist_ni1[i].descr);
+					dp += ielist_ni1[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+			} else if (cset == 5) {
+				for (i = 0; i < IESIZE_NI1_CS5; i++)
+					if (*buf == ielist_ni1_cs5[i].nr)
+						break;
+
+				/* When not found, give appropriate msg */
+				if (i != IESIZE_NI1_CS5) {
+					dp += sprintf(dp, "  %s\n", ielist_ni1_cs5[i].descr);
+					dp += ielist_ni1_cs5[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+			} else if (cset == 6) {
+				for (i = 0; i < IESIZE_NI1_CS6; i++)
+					if (*buf == ielist_ni1_cs6[i].nr)
+						break;
+
+				/* When not found, give appropriate msg */
+				if (i != IESIZE_NI1_CS6) {
+					dp += sprintf(dp, "  %s\n", ielist_ni1_cs6[i].descr);
+					dp += ielist_ni1_cs6[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+			} else
+				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+
+			/* Skip to next element */
+			if (cs_fest == 8) {
+				cset = cs_old;
+				cs_old = 0;
+				cs_fest = 0;
+			}
+			buf += buf[1] + 2;
+		}
+	} else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_EURO)) { /* EURO */
+		/* locate message type */
+		buf++;
+		cr_l = *buf++;
+		if (cr_l)
+			cr = *buf++;
+		else
+			cr = 0;
+		mt = *buf++;
+		for (i = 0; i < MTSIZE; i++)
+			if (mtlist[i].nr == mt)
+				break;
+
+		/* display message type if it exists */
+		if (i == MTSIZE)
+			dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mt);
+		else
+			dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mtlist[i].descr);
+
+		/* display each information element */
+		while (buf < bend) {
+			/* Is it a single octet information element? */
+			if (*buf & 0x80) {
+				switch ((*buf >> 4) & 7) {
+					case 1:
+						dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
+						break;
+					case 3:
+						dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
+						break;
+					case 5:
+						dp += sprintf(dp, "  Repeat indicator %x\n", *buf & 0xf);
+						break;
+					case 2:
+						if (*buf == 0xa0) {
+							dp += sprintf(dp, "  More data\n");
+							break;
+						}
+						if (*buf == 0xa1) {
+							dp += sprintf(dp, "  Sending complete\n");
+						}
+						break;
+						/* fall through */
+					default:
+						dp += sprintf(dp, "  Reserved %x\n", *buf);
+						break;
+				}
+				buf++;
+				continue;
+			}
+			/* No, locate it in the table */
+			for (i = 0; i < IESIZE; i++)
+				if (*buf == ielist[i].nr)
+					break;
+
+			/* When not found, give appropriate msg */
+			if (i != IESIZE) {
+				dp += sprintf(dp, "  %s\n", ielist[i].descr);
+				dp += ielist[i].f(dp, buf);
+			} else
+				dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+
+			/* Skip to next element */
+			buf += buf[1] + 2;
+		}
+	} else {
+		dp += sprintf(dp, "Unknown protocol %x!", buf[0]);
+	}
+	*dp = 0;
+	HiSax_putstatus(cs, NULL, cs->dlog);
+}
diff --git a/drivers/isdn/hisax/s0box.c b/drivers/isdn/hisax/s0box.c
new file mode 100644
index 000000000000..f3c481384a4e
--- /dev/null
+++ b/drivers/isdn/hisax/s0box.c
@@ -0,0 +1,266 @@
+/* $Id: s0box.c,v 2.6.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for Creatix S0BOX
+ *
+ * Author       Enrik Berkhan
+ * Copyright    by Enrik Berkhan <enrik@starfleet.inka.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *s0box_revision = "$Revision: 2.6.2.4 $";
+
+static inline void
+writereg(unsigned int padr, signed int addr, u_char off, u_char val) {
+	outb_p(0x1c,padr+2);
+	outb_p(0x14,padr+2);
+	outb_p((addr+off)&0x7f,padr);
+	outb_p(0x16,padr+2);
+	outb_p(val,padr);
+	outb_p(0x17,padr+2);
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+}
+
+static u_char nibtab[] = { 1, 9, 5, 0xd, 3, 0xb, 7, 0xf,
+			 0, 0, 0, 0, 0, 0, 0, 0,
+			 0, 8, 4, 0xc, 2, 0xa, 6, 0xe } ;
+
+static inline u_char
+readreg(unsigned int padr, signed int addr, u_char off) {
+	register u_char n1, n2;
+
+	outb_p(0x1c,padr+2);
+	outb_p(0x14,padr+2);
+	outb_p((addr+off)|0x80,padr);
+	outb_p(0x16,padr+2);
+	outb_p(0x17,padr+2);
+	n1 = (inb_p(padr+1) >> 3) & 0x17;
+	outb_p(0x16,padr+2);
+	n2 = (inb_p(padr+1) >> 3) & 0x17;
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	return nibtab[n1] | (nibtab[n2] << 4);
+}
+
+static inline void
+read_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+	int i;
+	register u_char n1, n2;
+	
+	outb_p(0x1c, padr+2);
+	outb_p(0x14, padr+2);
+	outb_p(adr|0x80, padr);
+	outb_p(0x16, padr+2);
+	for (i=0; i<size; i++) {
+		outb_p(0x17, padr+2);
+		n1 = (inb_p(padr+1) >> 3) & 0x17;
+		outb_p(0x16,padr+2);
+		n2 = (inb_p(padr+1) >> 3) & 0x17;
+		*(data++)=nibtab[n1] | (nibtab[n2] << 4);
+	}
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	return;
+}
+
+static inline void
+write_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+	int i;
+	outb_p(0x1c, padr+2);
+	outb_p(0x14, padr+2);
+	outb_p(adr&0x7f, padr);
+	for (i=0; i<size; i++) {
+		outb_p(0x16, padr+2);
+		outb_p(*(data++), padr);
+		outb_p(0x17, padr+2);
+	}
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	return;
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 5
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+	int count = 0;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	count++;
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (count >= MAXCOUNT)
+		printk(KERN_WARNING "S0Box: more than %d loops in s0box_interrupt\n", count);
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF);
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF);
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0x0);
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_s0box(struct IsdnCardState *cs)
+{
+	release_region(cs->hw.teles3.cfg_reg, 8);
+}
+
+static int
+S0Box_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			break;
+		case CARD_RELEASE:
+			release_io_s0box(cs);
+			break;
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case CARD_TEST:
+			break;
+	}
+	return(0);
+}
+
+int __init
+setup_s0box(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, s0box_revision);
+	printk(KERN_INFO "HiSax: S0Box IO driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_S0BOX)
+		return (0);
+
+	cs->hw.teles3.cfg_reg = card->para[1];
+	cs->hw.teles3.hscx[0] = -0x20;
+	cs->hw.teles3.hscx[1] = 0x0;
+	cs->hw.teles3.isac = 0x20;
+	cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e;
+	cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
+	cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
+	cs->irq = card->para[0];
+	if (!request_region(cs->hw.teles3.cfg_reg,8, "S0Box parallel I/O")) {
+		printk(KERN_WARNING
+		       "HiSax: %s ports %x-%x already in use\n",
+		       CardType[cs->typ],
+                       cs->hw.teles3.cfg_reg,
+                       cs->hw.teles3.cfg_reg + 7);
+		return 0;
+	}
+	printk(KERN_INFO
+		"HiSax: %s config irq:%d isac:0x%x  cfg:0x%x\n",
+		CardType[cs->typ], cs->irq,
+		cs->hw.teles3.isac, cs->hw.teles3.cfg_reg);
+	printk(KERN_INFO
+		"HiSax: hscx A:0x%x  hscx B:0x%x\n",
+		cs->hw.teles3.hscx[0], cs->hw.teles3.hscx[1]);
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &S0Box_card_msg;
+	cs->irq_func = &s0box_interrupt;
+	ISACVersion(cs, "S0Box:");
+	if (HscxVersion(cs, "S0Box:")) {
+		printk(KERN_WARNING
+		       "S0Box: wrong HSCX versions check IO address\n");
+		release_io_s0box(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/saphir.c b/drivers/isdn/hisax/saphir.c
new file mode 100644
index 000000000000..9e6d3d686cce
--- /dev/null
+++ b/drivers/isdn/hisax/saphir.c
@@ -0,0 +1,300 @@
+/* $Id: saphir.c,v 1.10.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for HST Saphir 1
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to    HST High Soft Tech GmbH
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+static char *saphir_rev = "$Revision: 1.10.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ISAC_DATA	0
+#define HSCX_DATA	1
+#define ADDRESS_REG	2
+#define IRQ_REG		3
+#define SPARE_REG	4
+#define RESET_REG	5
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.saphir.ale, cs->hw.saphir.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.saphir.ale, cs->hw.saphir.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx,
+		offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx,
+		offset + (hscx ? 0x40 : 0), value);
+}
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.saphir.ale, \
+		cs->hw.saphir.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.saphir.ale, \
+		cs->hw.saphir.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.saphir.ale, \
+		cs->hw.saphir.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.saphir.ale, \
+		cs->hw.saphir.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+saphir_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	/* Watchdog */
+	if (cs->hw.saphir.timer.function) 
+		mod_timer(&cs->hw.saphir.timer, jiffies+1*HZ);
+	else
+		printk(KERN_WARNING "saphir: Spurious timer!\n");
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_MASK, 0);
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK, 0);
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK + 0x40, 0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+SaphirWatchDog(struct IsdnCardState *cs)
+{
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+        /* 5 sec WatchDog, so read at least every 4 sec */
+	cs->readisac(cs, ISAC_RBCH);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	mod_timer(&cs->hw.saphir.timer, jiffies+1*HZ);
+}
+
+void
+release_io_saphir(struct IsdnCardState *cs)
+{
+	byteout(cs->hw.saphir.cfg_reg + IRQ_REG, 0xff);
+	del_timer(&cs->hw.saphir.timer);
+	cs->hw.saphir.timer.function = NULL;
+	if (cs->hw.saphir.cfg_reg)
+		release_region(cs->hw.saphir.cfg_reg, 6);
+}
+
+static int
+saphir_reset(struct IsdnCardState *cs)
+{
+	u_char irq_val;
+
+	switch(cs->irq) {
+		case 5: irq_val = 0;
+			break;
+		case 3: irq_val = 1;
+			break;
+		case 11:
+			irq_val = 2;
+			break;
+		case 12:
+			irq_val = 3;
+			break;
+		case 15:
+			irq_val = 4;
+			break;
+		default:
+			printk(KERN_WARNING "HiSax: saphir wrong IRQ %d\n",
+				cs->irq);
+			return (1);
+	}
+	byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val);
+	byteout(cs->hw.saphir.cfg_reg + RESET_REG, 1);
+	mdelay(10);
+	byteout(cs->hw.saphir.cfg_reg + RESET_REG, 0);
+	mdelay(10);
+	byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val);
+	byteout(cs->hw.saphir.cfg_reg + SPARE_REG, 0x02);
+	return (0);
+}
+
+static int
+saphir_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			saphir_reset(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_saphir(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+
+int __init
+setup_saphir(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, saphir_rev);
+	printk(KERN_INFO "HiSax: HST Saphir driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_HSTSAPHIR)
+		return (0);
+
+	/* IO-Ports */
+	cs->hw.saphir.cfg_reg = card->para[1];
+	cs->hw.saphir.isac = card->para[1] + ISAC_DATA;
+	cs->hw.saphir.hscx = card->para[1] + HSCX_DATA;
+	cs->hw.saphir.ale = card->para[1] + ADDRESS_REG;
+	cs->irq = card->para[0];
+	if (!request_region(cs->hw.saphir.cfg_reg, 6, "saphir")) {
+		printk(KERN_WARNING
+			"HiSax: %s config port %x-%x already in use\n",
+			CardType[card->typ],
+			cs->hw.saphir.cfg_reg,
+			cs->hw.saphir.cfg_reg + 5);
+		return (0);
+	}
+
+	printk(KERN_INFO "HiSax: %s config irq:%d io:0x%X\n",
+		CardType[cs->typ], cs->irq, cs->hw.saphir.cfg_reg);
+
+	setup_isac(cs);
+	cs->hw.saphir.timer.function = (void *) SaphirWatchDog;
+	cs->hw.saphir.timer.data = (long) cs;
+	init_timer(&cs->hw.saphir.timer);
+	cs->hw.saphir.timer.expires = jiffies + 4*HZ;
+	add_timer(&cs->hw.saphir.timer);
+	if (saphir_reset(cs)) {
+		release_io_saphir(cs);
+		return (0);
+	}
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &saphir_card_msg;
+	cs->irq_func = &saphir_interrupt;
+	ISACVersion(cs, "saphir:");
+	if (HscxVersion(cs, "saphir:")) {
+		printk(KERN_WARNING
+		    "saphir: wrong HSCX versions check IO address\n");
+		release_io_saphir(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c
new file mode 100644
index 000000000000..8390f1606853
--- /dev/null
+++ b/drivers/isdn/hisax/sedlbauer.c
@@ -0,0 +1,833 @@
+/* $Id: sedlbauer.c,v 1.34.2.6 2004/01/24 20:47:24 keil Exp $
+ *
+ * low level stuff for Sedlbauer cards
+ * includes support for the Sedlbauer speed star (speed star II),
+ * support for the Sedlbauer speed fax+,
+ * support for the Sedlbauer ISDN-Controller PC/104 and
+ * support for the Sedlbauer speed pci
+ * derived from the original file asuscom.c from Karsten Keil
+ *
+ * Author       Marcus Niemann
+ * Copyright    by Marcus Niemann    <niemann@www-bib.fh-bielefeld.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to  Karsten Keil
+ *            Sedlbauer AG for informations
+ *            Edgar Toernig
+ *
+ */
+
+/* Supported cards:
+ * Card:	Chip:		Configuration:	Comment:
+ * ---------------------------------------------------------------------
+ * Speed Card	ISAC_HSCX	DIP-SWITCH
+ * Speed Win	ISAC_HSCX	ISAPNP
+ * Speed Fax+	ISAC_ISAR	ISAPNP		Full analog support
+ * Speed Star	ISAC_HSCX	CARDMGR
+ * Speed Win2	IPAC		ISAPNP
+ * ISDN PC/104	IPAC		DIP-SWITCH
+ * Speed Star2	IPAC		CARDMGR
+ * Speed PCI	IPAC		PCI PNP
+ * 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.
+ * For example: hisaxctrl <DriverID> 9 ISAR.BIN
+*/
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isar.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+
+extern const char *CardType[];
+
+const char *Sedlbauer_revision = "$Revision: 1.34.2.6 $";
+
+const char *Sedlbauer_Types[] =
+	{"None", "speed card/win", "speed star", "speed fax+",
+	"speed win II / ISDN PC/104", "speed star II", "speed pci",
+	"speed fax+ pyramid", "speed fax+ pci", "HST Saphir III"};
+
+#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID	0x51
+#define PCI_SUBVENDOR_HST_SAPHIR3	0x52
+#define PCI_SUBVENDOR_SEDLBAUER_PCI	0x53
+#define PCI_SUBVENDOR_SPEEDFAX_PCI	0x54
+#define PCI_SUB_ID_SEDLBAUER		0x01
+
+#define SEDL_SPEED_CARD_WIN	1
+#define SEDL_SPEED_STAR 	2
+#define SEDL_SPEED_FAX		3
+#define SEDL_SPEED_WIN2_PC104 	4
+#define SEDL_SPEED_STAR2 	5
+#define SEDL_SPEED_PCI   	6
+#define SEDL_SPEEDFAX_PYRAMID	7
+#define SEDL_SPEEDFAX_PCI	8
+#define HST_SAPHIR3		9
+
+#define SEDL_CHIP_TEST		0
+#define SEDL_CHIP_ISAC_HSCX	1
+#define SEDL_CHIP_ISAC_ISAR	2
+#define SEDL_CHIP_IPAC		3
+
+#define SEDL_BUS_ISA		1
+#define SEDL_BUS_PCI		2
+#define	SEDL_BUS_PCMCIA		3
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define SEDL_HSCX_ISA_RESET_ON	0
+#define SEDL_HSCX_ISA_RESET_OFF	1
+#define SEDL_HSCX_ISA_ISAC	2
+#define SEDL_HSCX_ISA_HSCX	3
+#define SEDL_HSCX_ISA_ADR	4
+
+#define SEDL_HSCX_PCMCIA_RESET	0
+#define SEDL_HSCX_PCMCIA_ISAC	1
+#define SEDL_HSCX_PCMCIA_HSCX	2
+#define SEDL_HSCX_PCMCIA_ADR	4
+
+#define SEDL_ISAR_ISA_ISAC		4
+#define SEDL_ISAR_ISA_ISAR		6
+#define SEDL_ISAR_ISA_ADR		8
+#define SEDL_ISAR_ISA_ISAR_RESET_ON	10
+#define SEDL_ISAR_ISA_ISAR_RESET_OFF	12
+
+#define SEDL_IPAC_ANY_ADR		0
+#define SEDL_IPAC_ANY_IPAC		2
+
+#define SEDL_IPAC_PCI_BASE		0
+#define SEDL_IPAC_PCI_ADR		0xc0
+#define SEDL_IPAC_PCI_IPAC		0xc8
+#define SEDL_ISAR_PCI_ADR		0xc8
+#define SEDL_ISAR_PCI_ISAC		0xd0
+#define SEDL_ISAR_PCI_ISAR		0xe0
+#define SEDL_ISAR_PCI_ISAR_RESET_ON	0x01
+#define SEDL_ISAR_PCI_ISAR_RESET_OFF	0x18
+#define SEDL_ISAR_PCI_LED1		0x08
+#define SEDL_ISAR_PCI_LED2		0x10
+
+#define SEDL_RESET      0x3	/* same as DOS driver */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.sedl.adr,
+			cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.sedl.adr,
+		 cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/* 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(struct IsdnCardState *cs, int mode, u_char offset)
+{	
+	if (mode == 0)
+		return (readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset));
+	else if (mode == 1)
+		byteout(cs->hw.sedl.adr, offset);
+	return(bytein(cs->hw.sedl.hscx));
+}
+
+static void
+WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value)
+{
+	if (mode == 0)
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset, value);
+	else {
+		if (mode == 1)
+			byteout(cs->hw.sedl.adr, offset);
+		byteout(cs->hw.sedl.hscx, value);
+	}
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if ((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) && (*cs->busy_flag == 1)) {
+		/* The card tends to generate interrupts while being removed
+		   causing us to just crash the kernel. bad. */
+		spin_unlock_irqrestore(&cs->lock, flags);
+		printk(KERN_WARNING "Sedlbauer: card not available!\n");
+		return IRQ_NONE;
+	}
+
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0x0);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+sedlbauer_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista, val, icnt = 5;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	ista = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA | 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Sedlbauer IRQ LOOP");
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+sedlbauer_interrupt_isar(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	int cnt = 5;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT);
+      Start_ISAR:
+	if (val & ISAR_IRQSTA)
+		isar_int_main(cs);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT);
+	if ((val & ISAR_IRQSTA) && --cnt) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "ISAR IntStat after IntRoutine");
+		goto Start_ISAR;
+	}
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+	if (val && --cnt) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (!cnt)
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Sedlbauer IRQ LOOP");
+
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, 0);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, ISAR_IRQMSK);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_sedlbauer(struct IsdnCardState *cs)
+{
+	int bytecnt = 8;
+
+	if (cs->subtyp == SEDL_SPEED_FAX) {
+		bytecnt = 16;
+	} else if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+		bytecnt = 256;
+	}
+	if (cs->hw.sedl.cfg_reg)
+		release_region(cs->hw.sedl.cfg_reg, bytecnt);
+}
+
+static void
+reset_sedlbauer(struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "Sedlbauer: resetting card\n");
+
+	if (!((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) &&
+	   (cs->hw.sedl.chip == SEDL_CHIP_ISAC_HSCX))) {
+		if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x20);
+			mdelay(2);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0);
+			mdelay(10);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_CONF, 0x0);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ACFG, 0xff);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_AOE, 0x0);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xc0);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_PCFG, 0x12);
+		} else if ((cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) &&
+			(cs->hw.sedl.bus == SEDL_BUS_PCI)) {
+			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on);
+			mdelay(2);
+			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+			mdelay(10);
+		} else {		
+			byteout(cs->hw.sedl.reset_on, SEDL_RESET);	/* Reset On */
+			mdelay(2);
+			byteout(cs->hw.sedl.reset_off, 0);	/* Reset Off */
+			mdelay(10);
+		}
+	}
+}
+
+static int
+Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_sedlbauer(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+				spin_lock_irqsave(&cs->lock, flags);
+				writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx,
+					ISAR_IRQBIT, 0);
+				writereg(cs->hw.sedl.adr, cs->hw.sedl.isac,
+					ISAC_MASK, 0xFF);
+				reset_sedlbauer(cs);
+				writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx,
+					ISAR_IRQBIT, 0);
+				writereg(cs->hw.sedl.adr, cs->hw.sedl.isac,
+					ISAC_MASK, 0xFF);
+				spin_unlock_irqrestore(&cs->lock, flags);
+			}
+			release_io_sedlbauer(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_sedlbauer(cs);
+			if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+				clear_pending_isac_ints(cs);
+				writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx,
+					ISAR_IRQBIT, 0);
+				initisac(cs);
+				initisar(cs);
+				/* Reenable all IRQ */
+				cs->writeisac(cs, ISAC_MASK, 0);
+				/* RESET Receiver and Transmitter */
+				cs->writeisac(cs, ISAC_CMDR, 0x41);
+			} else {
+				inithscxisac(cs, 3);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+		case MDL_INFO_CONN:
+			if (cs->subtyp != SEDL_SPEEDFAX_PYRAMID)
+				return(0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if ((long) arg)
+				cs->hw.sedl.reset_off &= ~SEDL_ISAR_PCI_LED2;
+			else
+				cs->hw.sedl.reset_off &= ~SEDL_ISAR_PCI_LED1;
+			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case MDL_INFO_REL:
+			if (cs->subtyp != SEDL_SPEEDFAX_PYRAMID)
+				return(0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if ((long) arg)
+				cs->hw.sedl.reset_off |= SEDL_ISAR_PCI_LED2;
+			else
+				cs->hw.sedl.reset_off |= SEDL_ISAR_PCI_LED1;
+			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+	}
+	return(0);
+}
+
+static struct pci_dev *dev_sedl __devinitdata = NULL;
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id sedl_ids[] __devinitdata = {
+	{ ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x01),
+	  ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x01), 
+	  (unsigned long) "Speed win" },
+	{ ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x02),
+	  ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x02), 
+	  (unsigned long) "Speed Fax+" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &sedl_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit
+setup_sedlbauer(struct IsdnCard *card)
+{
+	int bytecnt, ver, val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	u16 sub_vendor_id, sub_id;
+
+	strcpy(tmp, Sedlbauer_revision);
+	printk(KERN_INFO "HiSax: Sedlbauer driver Rev. %s\n", HiSax_getrev(tmp));
+	
+ 	if (cs->typ == ISDN_CTYPE_SEDLBAUER) {
+ 		cs->subtyp = SEDL_SPEED_CARD_WIN;
+		cs->hw.sedl.bus = SEDL_BUS_ISA;
+		cs->hw.sedl.chip = SEDL_CHIP_TEST;
+ 	} else if (cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) {	
+ 		cs->subtyp = SEDL_SPEED_STAR;
+		cs->hw.sedl.bus = SEDL_BUS_PCMCIA;
+		cs->hw.sedl.chip = SEDL_CHIP_TEST;
+ 	} else if (cs->typ == ISDN_CTYPE_SEDLBAUER_FAX) {	
+ 		cs->subtyp = SEDL_SPEED_FAX;
+		cs->hw.sedl.bus = SEDL_BUS_ISA;
+		cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+ 	} else
+		return (0);
+
+	bytecnt = 8;
+	if (card->para[1]) {
+		cs->hw.sedl.cfg_reg = card->para[1];
+		cs->irq = card->para[0];
+		if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+			bytecnt = 16;
+		}
+	} else {
+#ifdef __ISAPNP__
+		if (isapnp_present()) {
+			struct pnp_dev *pnp_d;
+			while(ipid->card_vendor) {
+				if ((pnp_c = pnp_find_card(ipid->card_vendor,
+					ipid->card_device, pnp_c))) {
+					pnp_d = NULL;
+					if ((pnp_d = pnp_find_dev(pnp_c,
+						ipid->vendor, ipid->function, pnp_d))) {
+						int err;
+
+						printk(KERN_INFO "HiSax: %s detected\n",
+							(char *)ipid->driver_data);
+						pnp_disable_dev(pnp_d);
+						err = pnp_activate_dev(pnp_d);
+						if (err<0) {
+							printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+								__FUNCTION__, err);
+							return(0);
+						}
+						card->para[1] = pnp_port_start(pnp_d, 0);
+						card->para[0] = pnp_irq(pnp_d, 0);
+
+						if (!card->para[0] || !card->para[1]) {
+							printk(KERN_ERR "Sedlbauer PnP:some resources are missing %ld/%lx\n",
+								card->para[0], card->para[1]);
+							pnp_disable_dev(pnp_d);
+							return(0);
+						}
+						cs->hw.sedl.cfg_reg = card->para[1];
+						cs->irq = card->para[0];
+						if (ipid->function == ISAPNP_FUNCTION(0x2)) {
+							cs->subtyp = SEDL_SPEED_FAX;
+							cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+							bytecnt = 16;
+						} else {
+							cs->subtyp = SEDL_SPEED_CARD_WIN;
+							cs->hw.sedl.chip = SEDL_CHIP_TEST;
+						}
+						goto ready;
+					} else {
+						printk(KERN_ERR "Sedlbauer PnP: PnP error card found, no device\n");
+						return(0);
+					}
+				}
+				ipid++;
+				pnp_c = NULL;
+			} 
+			if (!ipid->card_vendor) {
+				printk(KERN_INFO "Sedlbauer PnP: no ISAPnP card found\n");
+			}
+		}
+#endif
+/* Probe for Sedlbauer speed pci */
+#ifdef CONFIG_PCI
+		if ((dev_sedl = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+				PCI_DEVICE_ID_TIGERJET_100, dev_sedl))) {
+			if (pci_enable_device(dev_sedl))
+				return(0);
+			cs->irq = dev_sedl->irq;
+			if (!cs->irq) {
+				printk(KERN_WARNING "Sedlbauer: No IRQ for PCI card found\n");
+				return(0);
+			}
+			cs->hw.sedl.cfg_reg = pci_resource_start(dev_sedl, 0);
+		} else {
+			printk(KERN_WARNING "Sedlbauer: No PCI card found\n");
+			return(0);
+		}
+		cs->irq_flags |= SA_SHIRQ;
+		cs->hw.sedl.bus = SEDL_BUS_PCI;
+		sub_vendor_id = dev_sedl->subsystem_vendor;
+		sub_id = dev_sedl->subsystem_device;
+		printk(KERN_INFO "Sedlbauer: PCI subvendor:%x subid %x\n",
+			sub_vendor_id, sub_id);
+		printk(KERN_INFO "Sedlbauer: PCI base adr %#x\n",
+			cs->hw.sedl.cfg_reg);
+		if (sub_id != PCI_SUB_ID_SEDLBAUER) {
+			printk(KERN_ERR "Sedlbauer: unknown sub id %#x\n", sub_id);
+			return(0);
+		}
+		if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PYRAMID) {
+			cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+			cs->subtyp = SEDL_SPEEDFAX_PYRAMID;
+		} else if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PCI) {
+			cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+			cs->subtyp = SEDL_SPEEDFAX_PCI;
+		} else if (sub_vendor_id == PCI_SUBVENDOR_HST_SAPHIR3) {
+			cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+			cs->subtyp = HST_SAPHIR3;
+		} else if (sub_vendor_id == PCI_SUBVENDOR_SEDLBAUER_PCI) {
+			cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+			cs->subtyp = SEDL_SPEED_PCI;
+		} else {
+			printk(KERN_ERR "Sedlbauer: unknown sub vendor id %#x\n",
+				sub_vendor_id);
+			return(0);
+		}
+		bytecnt = 256;
+		cs->hw.sedl.reset_on = SEDL_ISAR_PCI_ISAR_RESET_ON;
+		cs->hw.sedl.reset_off = SEDL_ISAR_PCI_ISAR_RESET_OFF;
+		byteout(cs->hw.sedl.cfg_reg, 0xff);
+		byteout(cs->hw.sedl.cfg_reg, 0x00);
+		byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd);
+		byteout(cs->hw.sedl.cfg_reg+ 5, 0x02);
+		byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on);
+		mdelay(2);
+		byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+		mdelay(10);
+#else
+		printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n");
+		return (0);
+#endif /* CONFIG_PCI */
+	}	
+ready:	
+	/* In case of the sedlbauer pcmcia card, this region is in use,
+	 * reserved for us by the card manager. So we do not check it
+	 * here, it would fail.
+	 */
+	if (cs->hw.sedl.bus != SEDL_BUS_PCMCIA &&
+		!request_region(cs->hw.sedl.cfg_reg, bytecnt, "sedlbauer isdn")) {
+		printk(KERN_WARNING
+			"HiSax: %s config port %x-%x already in use\n",
+			CardType[card->typ],
+			cs->hw.sedl.cfg_reg,
+			cs->hw.sedl.cfg_reg + bytecnt);
+			return (0);
+	}
+
+	printk(KERN_INFO
+	       "Sedlbauer: defined at 0x%x-0x%x IRQ %d\n",
+	       cs->hw.sedl.cfg_reg,
+	       cs->hw.sedl.cfg_reg + bytecnt,
+	       cs->irq);
+
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Sedl_card_msg;
+
+/*
+ * testing ISA and PCMCIA Cards for IPAC, default is ISAC
+ * do not test for PCI card, because ports are different
+ * and PCI card uses only IPAC (for the moment)
+ */	
+	if (cs->hw.sedl.bus != SEDL_BUS_PCI) {
+		val = readreg(cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR,
+			cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC, IPAC_ID);
+		printk(KERN_DEBUG "Sedlbauer: testing IPAC version %x\n", val);
+	        if ((val == 1) || (val == 2)) {
+			/* IPAC */
+			cs->subtyp = SEDL_SPEED_WIN2_PC104;
+			if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) {
+				cs->subtyp = SEDL_SPEED_STAR2;
+			}
+			cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+		} else {
+			/* ISAC_HSCX oder ISAC_ISAR */
+			if (cs->hw.sedl.chip == SEDL_CHIP_TEST) {
+				cs->hw.sedl.chip = SEDL_CHIP_ISAC_HSCX;
+			}
+		}
+	}
+
+/*
+ * hw.sedl.chip is now properly set
+ */
+	printk(KERN_INFO "Sedlbauer: %s detected\n",
+		Sedlbauer_Types[cs->subtyp]);
+
+	setup_isac(cs);
+	if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+		if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+	                cs->hw.sedl.adr  = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_ADR;
+			cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC;
+			cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC;
+		} else {
+	                cs->hw.sedl.adr  = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR;
+			cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC;
+			cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC;
+		}
+		test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		cs->readisac = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		cs->irq_func = &sedlbauer_interrupt_ipac;
+		val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ID);
+		printk(KERN_INFO "Sedlbauer: IPAC version %x\n", val);
+	} else {
+		/* ISAC_HSCX oder ISAC_ISAR */
+		cs->readisac = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+			if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+				cs->hw.sedl.adr = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_PCI_ADR;
+				cs->hw.sedl.isac = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_PCI_ISAC;
+				cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_PCI_ISAR;
+			} else {
+				cs->hw.sedl.adr = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_ISA_ADR;
+				cs->hw.sedl.isac = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_ISA_ISAC;
+				cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_ISA_ISAR;
+				cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_ISA_ISAR_RESET_ON;
+				cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_ISA_ISAR_RESET_OFF;
+			}
+			cs->bcs[0].hw.isar.reg = &cs->hw.sedl.isar;
+			cs->bcs[1].hw.isar.reg = &cs->hw.sedl.isar;
+			test_and_set_bit(HW_ISAR, &cs->HW_Flags);
+			cs->irq_func = &sedlbauer_interrupt_isar;
+			cs->auxcmd = &isar_auxcmd;
+			ISACVersion(cs, "Sedlbauer:");
+			cs->BC_Read_Reg = &ReadISAR;
+			cs->BC_Write_Reg = &WriteISAR;
+			cs->BC_Send_Data = &isar_fill_fifo;
+			bytecnt = 3;
+			while (bytecnt) {
+				ver = ISARVersion(cs, "Sedlbauer:");
+				if (ver < 0)
+					printk(KERN_WARNING
+						"Sedlbauer: wrong ISAR version (ret = %d)\n", ver);
+				else
+					break;
+				reset_sedlbauer(cs);
+				bytecnt--;
+			}
+			if (!bytecnt) {
+				release_io_sedlbauer(cs);
+				return (0);
+			}
+		} else {
+			if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) {
+				cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ADR;
+				cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ISAC;
+				cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_HSCX;
+				cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET;
+				cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET;
+				cs->irq_flags |= SA_SHIRQ;
+			} else {
+				cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ADR;
+				cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ISAC;
+				cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_HSCX;
+				cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_ON;
+				cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_OFF;
+			}
+			cs->irq_func = &sedlbauer_interrupt;
+			ISACVersion(cs, "Sedlbauer:");
+		
+			if (HscxVersion(cs, "Sedlbauer:")) {
+				printk(KERN_WARNING
+					"Sedlbauer: wrong HSCX versions check IO address\n");
+				release_io_sedlbauer(cs);
+				return (0);
+			}
+		}
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c
new file mode 100644
index 000000000000..449651241477
--- /dev/null
+++ b/drivers/isdn/hisax/sedlbauer_cs.c
@@ -0,0 +1,640 @@
+/*======================================================================
+
+    A Sedlbauer PCMCIA client driver
+
+    This driver is for the Sedlbauer Speed Star and Speed Star II, 
+    which are ISDN PCMCIA Cards.
+    
+    The contents of this file are subject to the Mozilla Public
+    License Version 1.1 (the "License"); you may not use this file
+    except in compliance with the License. You may obtain a copy of
+    the License at http://www.mozilla.org/MPL/
+
+    Software distributed under the License is distributed on an "AS
+    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+    implied. See the License for the specific language governing
+    rights and limitations under the License.
+
+    The initial developer of the original code is David A. Hinds
+    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Modifications from dummy_cs.c are Copyright (C) 1999-2001 Marcus Niemann
+    <maniemann@users.sourceforge.net>. All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU General Public License version 2 (the "GPL"), in
+    which case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the MPL, indicate your decision
+    by deleting the provisions above and replace them with the notice
+    and other provisions required by the GPL.  If you do not delete
+    the provisions above, a recipient may use your version of this
+    file under either the MPL or the GPL.
+    
+======================================================================*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include "hisax_cfg.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Sedlbauer cards");
+MODULE_AUTHOR("Marcus Niemann");
+MODULE_LICENSE("Dual MPL/GPL");
+
+/*
+   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
+   you do not define PCMCIA_DEBUG at all, all the debug code will be
+   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
+   be present but disabled -- but it can then be enabled for specific
+   modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args); 
+static char *version =
+"sedlbauer_cs.c 1.1a 2001/01/28 15:04:04 (M.Niemann)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int protocol = 2;        /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+
+/*====================================================================*/
+
+/*
+   The event() function is this driver's Card Services event handler.
+   It will be called by Card Services when an appropriate card status
+   event is received.  The config() and release() entry points are
+   used to configure or release a socket, in response to card
+   insertion and ejection events.  They are invoked from the sedlbauer
+   event handler. 
+*/
+
+static void sedlbauer_config(dev_link_t *link);
+static void sedlbauer_release(dev_link_t *link);
+static int sedlbauer_event(event_t event, int priority,
+		       event_callback_args_t *args);
+
+/*
+   The attach() and detach() entry points are used to create and destroy
+   "instances" of the driver, where each instance represents everything
+   needed to manage one actual PCMCIA card.
+*/
+
+static dev_link_t *sedlbauer_attach(void);
+static void sedlbauer_detach(dev_link_t *);
+
+/*
+   You'll also need to prototype all the functions that will actually
+   be used to talk to your device.  See 'memory_cs' for a good example
+   of a fully self-sufficient driver; the other drivers rely more or
+   less on other parts of the kernel.
+*/
+
+/*
+   The dev_info variable is the "key" that is used to match up this
+   device driver with appropriate cards, through the card configuration
+   database.
+*/
+
+static dev_info_t dev_info = "sedlbauer_cs";
+
+/*
+   A linked list of "instances" of the sedlbauer device.  Each actual
+   PCMCIA card corresponds to one device instance, and is described
+   by one dev_link_t structure (defined in ds.h).
+
+   You may not want to use a linked list for this -- for example, the
+   memory card driver uses an array of dev_link_t pointers, where minor
+   device numbers are used to derive the corresponding array index.
+*/
+
+static dev_link_t *dev_list = NULL;
+
+/*
+   A dev_link_t structure has fields for most things that are needed
+   to keep track of a socket, but there will usually be some device
+   specific information that also needs to be kept track of.  The
+   'priv' pointer in a dev_link_t structure can be used to point to
+   a device-specific private data structure, like this.
+
+   To simplify the data structure handling, we actually include the
+   dev_link_t structure in the device's private data structure.
+
+   A driver needs to provide a dev_node_t structure for each device
+   on a card.  In some cases, there is only one device per card (for
+   example, ethernet cards, modems).  In other cases, there may be
+   many actual or logical devices (SCSI adapters, memory cards with
+   multiple partitions).  The dev_node_t structures need to be kept
+   in a linked list starting at the 'dev' field of a dev_link_t
+   structure.  We allocate them in the card's private data structure,
+   because they generally shouldn't be allocated dynamically.
+
+   In this case, we also provide a flag to indicate if a device is
+   "stopped" due to a power management event, or card ejection.  The
+   device IO routines can use a flag like this to throttle IO to a
+   card that is not ready to accept it.
+*/
+   
+typedef struct local_info_t {
+    dev_link_t		link;
+    dev_node_t		node;
+    int			stop;
+    int			cardnr;
+} local_info_t;
+
+/*======================================================================
+
+    sedlbauer_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+    The dev_link structure is initialized, but we don't actually
+    configure the card at this point -- we wait until we receive a
+    card insertion event.
+    
+======================================================================*/
+
+static dev_link_t *sedlbauer_attach(void)
+{
+    local_info_t *local;
+    dev_link_t *link;
+    client_reg_t client_reg;
+    int ret;
+    
+    DEBUG(0, "sedlbauer_attach()\n");
+
+    /* Allocate space for private device-specific data */
+    local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+    if (!local) return NULL;
+    memset(local, 0, sizeof(local_info_t));
+    local->cardnr = -1;
+    link = &local->link; link->priv = local;
+    
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    link->irq.Handler = NULL;
+
+    /*
+      General socket configuration defaults can go here.  In this
+      client, we assume very little, and rely on the CIS for almost
+      everything.  In most clients, many details (i.e., number, sizes,
+      and attributes of IO windows) are fixed by the nature of the
+      device, and can be hard-wired here.
+    */
+
+    /* from old sedl_cs 
+    */
+    /* The io structure describes IO port mapping */
+    link->io.NumPorts1 = 8;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+    link->io.IOAddrLines = 3;
+
+
+    link->conf.Attributes = 0;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &sedlbauer_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != CS_SUCCESS) {
+	cs_error(link->handle, RegisterClient, ret);
+	sedlbauer_detach(link);
+	return NULL;
+    }
+
+    return link;
+} /* sedlbauer_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void sedlbauer_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+
+    DEBUG(0, "sedlbauer_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    /*
+       If the device is currently configured and active, we won't
+       actually delete it yet.  Instead, it is marked so that when
+       the release() function is called, that will trigger a proper
+       detach().
+    */
+    if (link->state & DEV_CONFIG) {
+#ifdef PCMCIA_DEBUG
+	printk(KERN_DEBUG "sedlbauer_cs: detach postponed, '%s' "
+	       "still locked\n", link->dev->dev_name);
+#endif
+	link->state |= DEV_STALE_LINK;
+	return;
+    }
+
+    /* Break the link with Card Services */
+    if (link->handle)
+	pcmcia_deregister_client(link->handle);
+    
+    /* Unlink device structure, and free it */
+    *linkp = link->next;
+    /* This points to the parent local_info_t struct */
+    kfree(link->priv);
+} /* sedlbauer_detach */
+
+/*======================================================================
+
+    sedlbauer_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    device available to the system.
+    
+======================================================================*/
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void sedlbauer_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    local_info_t *dev = link->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    int last_fn, last_ret;
+    u8 buf[64];
+    config_info_t conf;
+    win_req_t req;
+    memreq_t map;
+    IsdnCard_t  icard;
+
+    DEBUG(0, "sedlbauer_config(0x%p)\n", link);
+
+    /*
+       This reads the card's CONFIG tuple to find its configuration
+       registers.
+    */
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    tuple.Attributes = 0;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = sizeof(buf);
+    tuple.TupleOffset = 0;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present = parse.config.rmask[0];
+    
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    /* Look up the current Vcc */
+    CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
+    link->conf.Vcc = conf.Vcc;
+
+    /*
+      In this loop, we scan the CIS for configuration table entries,
+      each of which describes a valid card configuration, including
+      voltage, IO window, memory window, and interrupt settings.
+
+      We make no assumptions about the card to be configured: we use
+      just the information available in the CIS.  In an ideal world,
+      this would work for any PCMCIA card, but it requires a complete
+      and accurate CIS.  In practice, a driver usually "knows" most of
+      these things without consulting the CIS, and most client drivers
+      will only use the CIS to fill in implementation-defined details.
+    */
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    while (1) {
+	cistpl_cftable_entry_t dflt = { 0 };
+	cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+	if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+		pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+	    goto next_entry;
+
+	if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
+	if (cfg->index == 0) goto next_entry;
+	link->conf.ConfigIndex = cfg->index;
+	
+	/* Does this card need audio output? */
+	if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+	    link->conf.Attributes |= CONF_ENABLE_SPKR;
+	    link->conf.Status = CCSR_AUDIO_ENA;
+	}
+	
+	/* Use power settings for Vcc and Vpp if present */
+	/*  Note that the CIS values need to be rescaled */
+	if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+	    if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000)
+		goto next_entry;
+	} else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
+	    if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000)
+		goto next_entry;
+	}
+	    
+	if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
+	    link->conf.Vpp1 = link->conf.Vpp2 =
+		cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
+	else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
+	    link->conf.Vpp1 = link->conf.Vpp2 =
+		dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
+	
+	/* Do we need to allocate an interrupt? */
+	if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+	    link->conf.Attributes |= CONF_ENABLE_IRQ;
+	
+	/* IO window settings */
+	link->io.NumPorts1 = link->io.NumPorts2 = 0;
+	if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+	    cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+	    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+	    if (!(io->flags & CISTPL_IO_8BIT))
+		link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+	    if (!(io->flags & CISTPL_IO_16BIT))
+		link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+/* new in dummy.cs 2001/01/28 MN 
+            link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+*/
+	    link->io.BasePort1 = io->win[0].base;
+	    link->io.NumPorts1 = io->win[0].len;
+	    if (io->nwin > 1) {
+		link->io.Attributes2 = link->io.Attributes1;
+		link->io.BasePort2 = io->win[1].base;
+		link->io.NumPorts2 = io->win[1].len;
+	    }
+	    /* This reserves IO space but doesn't actually enable it */
+	    if (pcmcia_request_io(link->handle, &link->io) != 0)
+		goto next_entry;
+	}
+
+	/*
+	  Now set up a common memory window, if needed.  There is room
+	  in the dev_link_t structure for one memory window handle,
+	  but if the base addresses need to be saved, or if multiple
+	  windows are needed, the info should go in the private data
+	  structure for this device.
+
+	  Note that the memory window base is a physical address, and
+	  needs to be mapped to virtual space with ioremap() before it
+	  is used.
+	*/
+	if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
+	    cistpl_mem_t *mem =
+		(cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
+	    req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM;
+	    req.Attributes |= WIN_ENABLE;
+	    req.Base = mem->win[0].host_addr;
+	    req.Size = mem->win[0].len;
+/* new in dummy.cs 2001/01/28 MN 
+            if (req.Size < 0x1000)
+                req.Size = 0x1000;
+*/
+	    req.AccessSpeed = 0;
+	    if (pcmcia_request_window(&link->handle, &req, &link->win) != 0)
+		goto next_entry;
+	    map.Page = 0; map.CardOffset = mem->win[0].card_addr;
+	    if (pcmcia_map_mem_page(link->win, &map) != 0)
+		goto next_entry;
+	}
+	/* If we got this far, we're cool! */
+	break;
+	
+    next_entry:
+/* new in dummy.cs 2001/01/28 MN 
+        if (link->io.NumPorts1)
+           pcmcia_release_io(link->handle, &link->io);
+*/
+	CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+    }
+    
+    /*
+       Allocate an interrupt line.  Note that this does not assign a
+       handler to the interrupt, unless the 'Handler' member of the
+       irq structure is initialized.
+    */
+    if (link->conf.Attributes & CONF_ENABLE_IRQ)
+	CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+	
+    /*
+       This actually configures the PCMCIA socket -- setting up
+       the I/O windows and the interrupt mapping, and putting the
+       card and host interface into "Memory and IO" mode.
+    */
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+
+    /*
+      At this point, the dev_node_t structure(s) need to be
+      initialized and arranged in a linked list at link->dev.
+    */
+    sprintf(dev->node.dev_name, "sedlbauer");
+    dev->node.major = dev->node.minor = 0;
+    link->dev = &dev->node;
+
+    /* Finally, report what we've done */
+    printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
+	   dev->node.dev_name, link->conf.ConfigIndex,
+	   link->conf.Vcc/10, link->conf.Vcc%10);
+    if (link->conf.Vpp1)
+	printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+    if (link->conf.Attributes & CONF_ENABLE_IRQ)
+	printk(", irq %d", link->irq.AssignedIRQ);
+    if (link->io.NumPorts1)
+	printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+	       link->io.BasePort1+link->io.NumPorts1-1);
+    if (link->io.NumPorts2)
+	printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+	       link->io.BasePort2+link->io.NumPorts2-1);
+    if (link->win)
+	printk(", mem 0x%06lx-0x%06lx", req.Base,
+	       req.Base+req.Size-1);
+    printk("\n");
+    
+    link->state &= ~DEV_CONFIG_PENDING;
+
+    icard.para[0] = link->irq.AssignedIRQ;
+    icard.para[1] = link->io.BasePort1;
+    icard.protocol = protocol;
+    icard.typ = ISDN_CTYPE_SEDLBAUER_PCMCIA;
+    
+    last_ret = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->stop), &icard);
+    if (last_ret < 0) {
+    	printk(KERN_ERR "sedlbauer_cs: failed to initialize SEDLBAUER PCMCIA %d at i/o %#x\n",
+    		last_ret, link->io.BasePort1);
+    	sedlbauer_release(link);
+    } else
+    	((local_info_t*)link->priv)->cardnr = last_ret;
+
+    return;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+    sedlbauer_release(link);
+
+} /* sedlbauer_config */
+
+/*======================================================================
+
+    After a card is removed, sedlbauer_release() will unregister the
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+    
+======================================================================*/
+
+static void sedlbauer_release(dev_link_t *link)
+{
+    local_info_t *local = link->priv;
+    DEBUG(0, "sedlbauer_release(0x%p)\n", link);
+
+    if (local) {
+    	if (local->cardnr >= 0) {
+    	    /* no unregister function with hisax */
+	    HiSax_closecard(local->cardnr);
+	}
+    }
+    /* Unlink the device chain */
+    link->dev = NULL;
+
+    /*
+      In a normal driver, additional code may be needed to release
+      other kernel data structures associated with this device. 
+    */
+    
+    /* Don't bother checking to see if these succeed or not */
+    if (link->win)
+	pcmcia_release_window(link->win);
+    pcmcia_release_configuration(link->handle);
+    if (link->io.NumPorts1)
+	pcmcia_release_io(link->handle, &link->io);
+    if (link->irq.AssignedIRQ)
+	pcmcia_release_irq(link->handle, &link->irq);
+    link->state &= ~DEV_CONFIG;
+    
+    if (link->state & DEV_STALE_LINK)
+	sedlbauer_detach(link);
+    
+} /* sedlbauer_release */
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.
+
+    When a CARD_REMOVAL event is received, we immediately set a
+    private flag to block future accesses to this device.  All the
+    functions that actually access the device should check this flag
+    to make sure the card is still present.
+    
+======================================================================*/
+
+static int sedlbauer_event(event_t event, int priority,
+		       event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    local_info_t *dev = link->priv;
+    
+    DEBUG(1, "sedlbauer_event(0x%06x)\n", event);
+    
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG) {
+	    ((local_info_t *)link->priv)->stop = 1;
+	    sedlbauer_release(link);
+	}
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	sedlbauer_config(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	/* Mark the device as stopped, to block IO until later */
+	dev->stop = 1;
+	if (link->state & DEV_CONFIG)
+	    pcmcia_release_configuration(link->handle);
+	break;
+    case CS_EVENT_PM_RESUME:
+	link->state &= ~DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	if (link->state & DEV_CONFIG)
+	    pcmcia_request_configuration(link->handle, &link->conf);
+	dev->stop = 0;
+	/*
+	  In a normal driver, additional code may go here to restore
+	  the device state and restart IO. 
+	*/
+	break;
+    }
+    return 0;
+} /* sedlbauer_event */
+
+static struct pcmcia_driver sedlbauer_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "sedlbauer_cs",
+	},
+	.attach		= sedlbauer_attach,
+	.detach		= sedlbauer_detach,
+};
+
+static int __init init_sedlbauer_cs(void)
+{
+	return pcmcia_register_driver(&sedlbauer_driver);
+}
+
+static void __exit exit_sedlbauer_cs(void)
+{
+	pcmcia_unregister_driver(&sedlbauer_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_sedlbauer_cs);
+module_exit(exit_sedlbauer_cs);
diff --git a/drivers/isdn/hisax/sportster.c b/drivers/isdn/hisax/sportster.c
new file mode 100644
index 000000000000..132840b750ce
--- /dev/null
+++ b/drivers/isdn/hisax/sportster.c
@@ -0,0 +1,270 @@
+/* $Id: sportster.c,v 1.16.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for USR Sportster internal TA
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation
+ *
+ *
+ */
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *sportster_revision = "$Revision: 1.16.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define	 SPORTSTER_ISAC		0xC000
+#define	 SPORTSTER_HSCXA	0x0000
+#define	 SPORTSTER_HSCXB	0x4000
+#define	 SPORTSTER_RES_IRQ	0x8000
+#define	 SPORTSTER_RESET	0x80
+#define	 SPORTSTER_INTE		0x40
+
+static inline int
+calc_off(unsigned int base, unsigned int off)
+{
+	return(base + ((off & 0xfc)<<8) + ((off & 3)<<1));
+}
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+	insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (bytein(calc_off(cs->hw.spt.isac, offset)));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	byteout(calc_off(cs->hw.spt.isac, offset), value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo(cs->hw.spt.isac, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo(cs->hw.spt.isac, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (bytein(calc_off(cs->hw.spt.hscx[hscx], offset)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	byteout(calc_off(cs->hw.spt.hscx[hscx], offset), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) bytein(calc_off(cs->hw.spt.hscx[nr], reg))
+#define WRITEHSCX(cs, nr, reg, data) byteout(calc_off(cs->hw.spt.hscx[nr], reg), data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.spt.hscx[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.spt.hscx[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+sportster_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = READHSCX(cs, 1, HSCX_ISTA);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = ReadISAC(cs, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = READHSCX(cs, 1, HSCX_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = ReadISAC(cs, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	/* get a new irq impulse if there any pending */
+	bytein(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ +1);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_sportster(struct IsdnCardState *cs)
+{
+	int i, adr;
+
+	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, 0);
+	for (i=0; i<64; i++) {
+		adr = cs->hw.spt.cfg_reg + i *1024;
+		release_region(adr, 8);
+	}
+}
+
+void
+reset_sportster(struct IsdnCardState *cs)
+{
+	cs->hw.spt.res_irq |= SPORTSTER_RESET; /* Reset On */
+	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+	mdelay(10);
+	cs->hw.spt.res_irq &= ~SPORTSTER_RESET; /* Reset Off */
+	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+	mdelay(10);
+}
+
+static int
+Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_sportster(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_sportster(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_sportster(cs);
+			inithscxisac(cs, 1);
+			cs->hw.spt.res_irq |= SPORTSTER_INTE; /* IRQ On */
+			byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+			inithscxisac(cs, 2);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static int __init
+get_io_range(struct IsdnCardState *cs)
+{
+	int i, j, adr;
+	
+	for (i=0;i<64;i++) {
+		adr = cs->hw.spt.cfg_reg + i *1024;
+		if (!request_region(adr, 8, "sportster")) {
+			printk(KERN_WARNING
+				"HiSax: %s config port %x-%x already in use\n",
+				CardType[cs->typ], adr, adr + 8);
+			break;
+		} 
+	}
+	if (i==64)
+		return(1);
+	else {
+		for (j=0; j<i; j++) {
+			adr = cs->hw.spt.cfg_reg + j *1024;
+			release_region(adr, 8);
+		}
+		return(0);
+	}
+}
+
+int __init
+setup_sportster(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, sportster_revision);
+	printk(KERN_INFO "HiSax: USR Sportster driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_SPORTSTER)
+		return (0);
+
+	cs->hw.spt.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	if (!get_io_range(cs))
+		return (0);
+	cs->hw.spt.isac = cs->hw.spt.cfg_reg + SPORTSTER_ISAC;
+	cs->hw.spt.hscx[0] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXA;
+	cs->hw.spt.hscx[1] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXB;
+	
+	switch(cs->irq) {
+		case 5:	cs->hw.spt.res_irq = 1;
+			break;
+		case 7:	cs->hw.spt.res_irq = 2;
+			break;
+		case 10:cs->hw.spt.res_irq = 3;
+			break;
+		case 11:cs->hw.spt.res_irq = 4;
+			break;
+		case 12:cs->hw.spt.res_irq = 5;
+			break;
+		case 14:cs->hw.spt.res_irq = 6;
+			break;
+		case 15:cs->hw.spt.res_irq = 7;
+			break;
+		default:release_io_sportster(cs);
+			printk(KERN_WARNING "Sportster: wrong IRQ\n");
+			return(0);
+	}
+	printk(KERN_INFO "HiSax: %s config irq:%d cfg:0x%X\n",
+		CardType[cs->typ], cs->irq, cs->hw.spt.cfg_reg);
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Sportster_card_msg;
+	cs->irq_func = &sportster_interrupt;
+	ISACVersion(cs, "Sportster:");
+	if (HscxVersion(cs, "Sportster:")) {
+		printk(KERN_WARNING
+		       "Sportster: wrong HSCX versions check IO address\n");
+		release_io_sportster(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/st5481.h b/drivers/isdn/hisax/st5481.h
new file mode 100644
index 000000000000..e8177b017b1d
--- /dev/null
+++ b/drivers/isdn/hisax/st5481.h
@@ -0,0 +1,535 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef _ST5481_H_
+#define _ST5481_H_
+
+#include <linux/config.h>
+
+// USB IDs, the Product Id is in the range 0x4810-0x481F
+
+#define ST_VENDOR_ID 0x0483
+#define ST5481_PRODUCT_ID 0x4810
+#define ST5481_PRODUCT_ID_MASK 0xFFF0
+
+// ST5481 endpoints when using alternative setting 3 (2B+D).
+// To get the endpoint address, OR with 0x80 for IN endpoints.
+
+#define EP_CTRL   0x00U /* Control endpoint */
+#define EP_INT    0x01U /* Interrupt endpoint */
+#define EP_B1_OUT 0x02U /* B1 channel out */
+#define EP_B1_IN  0x03U /* B1 channel in */
+#define EP_B2_OUT 0x04U /* B2 channel out */
+#define EP_B2_IN  0x05U /* B2 channel in */
+#define EP_D_OUT  0x06U /* D channel out */
+#define EP_D_IN   0x07U /* D channel in */
+  
+// Number of isochronous packets. With 20 packets we get
+// 50 interrupts/sec for each endpoint.
+
+#define NUM_ISO_PACKETS_D      20
+#define NUM_ISO_PACKETS_B      20
+
+// Size of each isochronous packet.
+// In outgoing direction we need to match ISDN data rates:
+// D:  2 bytes / msec -> 16 kbit / s
+// B: 16 bytes / msec -> 64 kbit / s
+#define SIZE_ISO_PACKETS_D_IN  16
+#define SIZE_ISO_PACKETS_D_OUT 2
+#define SIZE_ISO_PACKETS_B_IN  32
+#define SIZE_ISO_PACKETS_B_OUT 8
+
+// If we overrun/underrun, we send one packet with +/- 2 bytes
+#define B_FLOW_ADJUST 2
+
+// Registers that are written using vendor specific device request
+// on endpoint 0. 
+
+#define LBA			0x02 /* S loopback */
+#define SET_DEFAULT		0x06 /* Soft reset */
+#define LBB			0x1D /* S maintenance loopback */
+#define STT			0x1e /* S force transmission signals */
+#define SDA_MIN			0x20 /* SDA-sin minimal value */
+#define SDA_MAX			0x21 /* SDA-sin maximal value */
+#define SDELAY_VALUE		0x22 /* Delay between Tx and Rx clock */
+#define IN_D_COUNTER		0x36 /* D receive channel fifo counter */
+#define OUT_D_COUNTER		0x37 /* D transmit channel fifo counter */
+#define IN_B1_COUNTER		0x38 /* B1 receive channel fifo counter */
+#define OUT_B1_COUNTER		0x39 /* B1 transmit channel fifo counter */
+#define IN_B2_COUNTER		0x3a /* B2 receive channel fifo counter */
+#define OUT_B2_COUNTER		0x3b /* B2 transmit channel fifo counter */
+#define FFCTRL_IN_D		0x3C /* D receive channel fifo threshold low */
+#define FFCTRH_IN_D		0x3D /* D receive channel fifo threshold high */
+#define FFCTRL_OUT_D		0x3E /* D transmit channel fifo threshold low */
+#define FFCTRH_OUT_D		0x3F /* D transmit channel fifo threshold high */
+#define FFCTRL_IN_B1		0x40 /* B1 receive channel fifo threshold low */
+#define FFCTRH_IN_B1		0x41 /* B1 receive channel fifo threshold high */
+#define FFCTRL_OUT_B1		0x42 /* B1 transmit channel fifo threshold low */
+#define FFCTRH_OUT_B1		0x43 /* B1 transmit channel fifo threshold high */
+#define FFCTRL_IN_B2		0x44 /* B2 receive channel fifo threshold low */
+#define FFCTRH_IN_B2		0x45 /* B2 receive channel fifo threshold high */
+#define FFCTRL_OUT_B2		0x46 /* B2 transmit channel fifo threshold low */
+#define FFCTRH_OUT_B2		0x47 /* B2 transmit channel fifo threshold high */
+#define MPMSK			0x4A /* Multi purpose interrupt MASK register */
+#define	FFMSK_D			0x4c /* D fifo interrupt MASK register */
+#define	FFMSK_B1		0x4e /* B1 fifo interrupt MASK register */
+#define	FFMSK_B2		0x50 /* B2 fifo interrupt MASK register */
+#define GPIO_DIR		0x52 /* GPIO pins direction registers */
+#define GPIO_OUT		0x53 /* GPIO pins output register */
+#define GPIO_IN			0x54 /* GPIO pins input register */ 
+#define TXCI			0x56 /* CI command to be transmitted */
+
+
+// Format of the interrupt packet received on endpoint 1:
+//
+// +--------+--------+--------+--------+--------+--------+
+// !MPINT   !FFINT_D !FFINT_B1!FFINT_B2!CCIST   !GPIO_INT!
+// +--------+--------+--------+--------+--------+--------+
+
+// Offsets in the interrupt packet
+
+#define MPINT			0
+#define FFINT_D			1
+#define FFINT_B1		2
+#define FFINT_B2		3
+#define CCIST			4
+#define GPIO_INT		5
+#define INT_PKT_SIZE            6
+
+// MPINT
+#define LSD_INT                 0x80 /* S line activity detected */
+#define RXCI_INT		0x40 /* Indicate primitive arrived */
+#define	DEN_INT			0x20 /* Signal enabling data out of D Tx fifo */
+#define DCOLL_INT		0x10 /* D channel collision */
+#define AMIVN_INT		0x04 /* AMI violation number reached 2 */
+#define INFOI_INT		0x04 /* INFOi changed */
+#define DRXON_INT               0x02 /* Reception channel active */
+#define GPCHG_INT               0x01 /* GPIO pin value changed */
+
+// FFINT_x
+#define IN_OVERRUN		0x80 /* In fifo overrun */
+#define OUT_UNDERRUN		0x40 /* Out fifo underrun */
+#define IN_UP			0x20 /* In fifo thresholdh up-crossed */
+#define IN_DOWN			0x10 /* In fifo thresholdl down-crossed */
+#define OUT_UP			0x08 /* Out fifo thresholdh up-crossed */
+#define OUT_DOWN		0x04 /* Out fifo thresholdl down-crossed */
+#define IN_COUNTER_ZEROED	0x02 /* In down-counter reached 0 */
+#define OUT_COUNTER_ZEROED	0x01 /* Out down-counter reached 0 */
+
+#define ANY_REC_INT	(IN_OVERRUN+IN_UP+IN_DOWN+IN_COUNTER_ZEROED)
+#define ANY_XMIT_INT	(OUT_UNDERRUN+OUT_UP+OUT_DOWN+OUT_COUNTER_ZEROED)
+
+
+// Level 1 commands that are sent using the TXCI device request
+#define ST5481_CMD_DR		 0x0 /* Deactivation Request */
+#define ST5481_CMD_RES		 0x1 /* state machine RESet */
+#define ST5481_CMD_TM1		 0x2 /* Test Mode 1 */
+#define ST5481_CMD_TM2		 0x3 /* Test Mode 2 */
+#define ST5481_CMD_PUP		 0x7 /* Power UP */
+#define ST5481_CMD_AR8		 0x8 /* Activation Request class 1 */
+#define ST5481_CMD_AR10		 0x9 /* Activation Request class 2 */
+#define ST5481_CMD_ARL		 0xA /* Activation Request Loopback */
+#define ST5481_CMD_PDN		 0xF /* Power DoWn */
+
+// Turn on/off the LEDs using the GPIO device request.
+// To use the B LEDs, number_of_leds must be set to 4
+#define B1_LED		0x10U
+#define B2_LED		0x20U
+#define GREEN_LED	0x40U
+#define RED_LED	        0x80U
+
+// D channel out states
+enum {
+	ST_DOUT_NONE,
+
+	ST_DOUT_SHORT_INIT,
+	ST_DOUT_SHORT_WAIT_DEN,
+
+	ST_DOUT_LONG_INIT,
+	ST_DOUT_LONG_WAIT_DEN,
+	ST_DOUT_NORMAL,
+
+	ST_DOUT_WAIT_FOR_UNDERRUN,
+        ST_DOUT_WAIT_FOR_NOT_BUSY,
+	ST_DOUT_WAIT_FOR_STOP,
+	ST_DOUT_WAIT_FOR_RESET,
+};
+
+#define DOUT_STATE_COUNT (ST_DOUT_WAIT_FOR_RESET + 1)
+
+// D channel out events
+enum {
+	EV_DOUT_START_XMIT,
+	EV_DOUT_COMPLETE,
+	EV_DOUT_DEN,
+	EV_DOUT_RESETED,
+	EV_DOUT_STOPPED,
+	EV_DOUT_COLL,
+	EV_DOUT_UNDERRUN,
+};
+
+#define DOUT_EVENT_COUNT (EV_DOUT_UNDERRUN + 1)
+
+// ----------------------------------------------------------------------
+
+enum {
+	ST_L1_F3,
+	ST_L1_F4,
+	ST_L1_F6,
+	ST_L1_F7,
+	ST_L1_F8,
+};
+
+#define L1_STATE_COUNT (ST_L1_F8+1)
+
+// The first 16 entries match the Level 1 indications that 
+// are found at offset 4 (CCIST) in the interrupt packet
+
+enum {
+	EV_IND_DP,  // 0000 Deactivation Pending
+	EV_IND_1,   // 0001
+	EV_IND_2,   // 0010
+	EV_IND_3,   // 0011
+	EV_IND_RSY, // 0100 ReSYnchronizing
+	EV_IND_5,   // 0101
+	EV_IND_6,   // 0110
+	EV_IND_7,   // 0111
+	EV_IND_AP,  // 1000 Activation Pending
+	EV_IND_9,   // 1001
+	EV_IND_10,  // 1010
+	EV_IND_11,  // 1011
+	EV_IND_AI8, // 1100 Activation Indication class 8
+	EV_IND_AI10,// 1101 Activation Indication class 10
+	EV_IND_AIL, // 1110 Activation Indication Loopback
+	EV_IND_DI,  // 1111 Deactivation Indication
+	EV_PH_ACTIVATE_REQ,
+	EV_PH_DEACTIVATE_REQ,
+	EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+#define ERR(format, arg...) \
+printk(KERN_ERR "%s:%s: " format "\n" , __FILE__,  __FUNCTION__ , ## arg)
+
+#define WARN(format, arg...) \
+printk(KERN_WARNING "%s:%s: " format "\n" , __FILE__,  __FUNCTION__ , ## arg)
+
+#define INFO(format, arg...) \
+printk(KERN_INFO "%s:%s: " format "\n" , __FILE__,  __FUNCTION__ , ## arg)
+
+#include "isdnhdlc.h"
+#include "fsm.h"
+#include "hisax_if.h"
+#include <linux/skbuff.h>
+
+/* ======================================================================
+ * FIFO handling
+ */
+
+/* Generic FIFO structure */
+struct fifo {
+	u_char r,w,count,size;
+	spinlock_t lock;
+};
+
+/*
+ * Init an FIFO
+ */
+static inline void fifo_init(struct fifo *fifo, int size)
+{
+	fifo->r = fifo->w = fifo->count = 0;
+	fifo->size = size;
+	spin_lock_init(&fifo->lock);
+}
+
+/*
+ * Add an entry to the FIFO
+ */
+static inline int fifo_add(struct fifo *fifo)
+{
+	unsigned long flags;
+	int index;
+
+	if (!fifo) {
+		return -1;
+	}
+
+	spin_lock_irqsave(&fifo->lock, flags);
+	if (fifo->count == fifo->size) {
+		// FIFO full
+		index = -1;
+	} else {
+		// Return index where to get the next data to add to the FIFO
+		index = fifo->w++ & (fifo->size-1);
+		fifo->count++;
+	}
+	spin_unlock_irqrestore(&fifo->lock, flags);
+	return index;
+}
+
+/*
+ * Remove an entry from the FIFO with the index returned.
+ */
+static inline int fifo_remove(struct fifo *fifo)
+{
+	unsigned long flags;
+	int index;
+
+	if (!fifo) {
+		return -1;
+	}
+
+	spin_lock_irqsave(&fifo->lock, flags);
+	if (!fifo->count) {
+		// FIFO empty
+		index = -1;
+	} else {
+		// Return index where to get the next data from the FIFO
+		index = fifo->r++ & (fifo->size-1);
+		fifo->count--;
+	}
+	spin_unlock_irqrestore(&fifo->lock, flags);
+
+	return index;
+}
+
+/* ======================================================================
+ * control pipe
+ */
+typedef void (*ctrl_complete_t)(void *);
+
+typedef struct ctrl_msg {
+	struct usb_ctrlrequest dr;
+	ctrl_complete_t complete;
+	void *context;
+} ctrl_msg; 
+
+/* FIFO of ctrl messages waiting to be sent */
+#define MAX_EP0_MSG 16
+struct ctrl_msg_fifo {
+	struct fifo f;
+	struct ctrl_msg data[MAX_EP0_MSG];
+};	
+
+#define MAX_DFRAME_LEN_L1	300
+#define HSCX_BUFMAX	4096
+
+struct st5481_ctrl {
+	struct ctrl_msg_fifo msg_fifo;
+	unsigned long busy;
+	struct urb *urb;
+};
+
+struct st5481_intr {
+  //	struct evt_fifo evt_fifo;
+	struct urb *urb;
+};
+
+struct st5481_d_out {
+	struct isdnhdlc_vars hdlc_state;
+	struct urb *urb[2]; /* double buffering */
+	unsigned long busy;
+	struct sk_buff *tx_skb;
+	struct FsmInst fsm;
+};
+
+struct st5481_b_out {
+	struct isdnhdlc_vars hdlc_state;
+	struct urb *urb[2]; /* double buffering */
+	u_char flow_event;
+	u_long busy;
+	struct sk_buff *tx_skb;
+};
+
+struct st5481_in {
+	struct isdnhdlc_vars hdlc_state;
+	struct urb *urb[2]; /* double buffering */
+	int mode;
+	int bufsize;
+	unsigned int num_packets;
+	unsigned int packet_size;
+	unsigned char ep, counter;
+	unsigned char *rcvbuf;
+	struct st5481_adapter *adapter;
+	struct hisax_if *hisax_if;
+};
+
+int st5481_setup_in(struct st5481_in *in);
+void st5481_release_in(struct st5481_in *in);
+void st5481_in_mode(struct st5481_in *in, int mode);
+
+struct st5481_bcs {
+	struct hisax_b_if b_if;
+	struct st5481_adapter *adapter;
+	struct st5481_in b_in;
+	struct st5481_b_out b_out;
+	int channel;
+	int mode;
+};
+
+struct st5481_adapter {
+	struct list_head list;
+	int number_of_leds;
+	struct usb_device *usb_dev;
+	struct hisax_d_if hisax_d_if;
+
+	struct st5481_ctrl ctrl;
+	struct st5481_intr intr;
+	struct st5481_in d_in;
+	struct st5481_d_out d_out;
+
+	unsigned char leds;
+	unsigned int led_counter;
+
+	unsigned long event;
+
+	struct FsmInst l1m;
+	struct FsmTimer timer;
+
+	struct st5481_bcs bcs[2];
+};
+
+#define TIMER3_VALUE 7000
+
+/* ======================================================================
+ *
+ */
+
+/*
+ * Submit an URB with error reporting. This is a macro so
+ * the __FUNCTION__ returns the caller function name.
+ */
+#define SUBMIT_URB(urb, mem_flags) \
+({ \
+	int status; \
+	if ((status = usb_submit_urb(urb, mem_flags)) < 0) { \
+		WARN("usb_submit_urb failed,status=%d", status); \
+	} \
+        status; \
+})
+
+/*
+ * USB double buffering, return the URB index (0 or 1).
+ */
+static inline int get_buf_nr(struct urb *urbs[], struct urb *urb)
+{
+        return (urbs[0]==urb ? 0 : 1); 
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* B Channel */
+
+int  st5481_setup_b(struct st5481_bcs *bcs);
+void st5481_release_b(struct st5481_bcs *bcs);
+void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg);
+
+/* D Channel */
+
+int  st5481_setup_d(struct st5481_adapter *adapter);
+void st5481_release_d(struct st5481_adapter *adapter);
+void st5481_b_l2l1(struct hisax_if *b_if, int pr, void *arg);
+int  st5481_d_init(void);
+void st5481_d_exit(void);
+
+/* USB */
+void st5481_ph_command(struct st5481_adapter *adapter, unsigned int command);
+int st5481_setup_isocpipes(struct urb* urb[2], struct usb_device *dev, 
+			   unsigned int pipe, int num_packets,
+			   int packet_size, int buf_size,
+			   usb_complete_t complete, void *context);
+void st5481_release_isocpipes(struct urb* urb[2]);
+
+int  st5481_isoc_flatten(struct urb *urb);
+void st5481_usb_pipe_reset(struct st5481_adapter *adapter,
+		    u_char pipe, ctrl_complete_t complete, void *context);
+void st5481_usb_ctrl_msg(struct st5481_adapter *adapter,
+		  u8 request, u8 requesttype, u16 value, u16 index,
+		  ctrl_complete_t complete, void *context);
+void st5481_usb_device_ctrl_msg(struct st5481_adapter *adapter,
+			 u8 request, u16 value,
+			 ctrl_complete_t complete, void *context);
+int  st5481_setup_usb(struct st5481_adapter *adapter);
+void st5481_release_usb(struct st5481_adapter *adapter);
+void st5481_start(struct st5481_adapter *adapter);
+void st5481_stop(struct st5481_adapter *adapter);
+
+// ----------------------------------------------------------------------
+// debugging macros
+
+#define __debug_variable st5481_debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+
+extern int st5481_debug;
+
+#define DBG_ISO_PACKET(level,urb) \
+  if (level & __debug_variable) dump_iso_packet(__FUNCTION__,urb)
+
+static void __attribute__((unused))
+dump_iso_packet(const char *name, struct urb *urb)
+{
+	int i,j;
+	int len,ofs;
+	u_char *data;
+
+	printk(KERN_DEBUG "%s: packets=%d,errors=%d\n",
+	       name,urb->number_of_packets,urb->error_count);
+	for (i = 0; i  < urb->number_of_packets; ++i) {
+		if (urb->pipe & USB_DIR_IN) {
+			len = urb->iso_frame_desc[i].actual_length;
+		} else {
+			len = urb->iso_frame_desc[i].length;
+		}
+		ofs = urb->iso_frame_desc[i].offset;
+		printk(KERN_DEBUG "len=%.2d,ofs=%.3d ",len,ofs);
+		if (len) {
+			data = urb->transfer_buffer+ofs;
+			for (j=0; j < len; j++) {
+				printk ("%.2x", data[j]);
+			}
+		}
+		printk("\n");
+	}
+}
+
+static inline const char *ST5481_CMD_string(int evt)
+{
+	static char s[16];
+
+	switch (evt) {
+	case ST5481_CMD_DR: return "DR";
+	case ST5481_CMD_RES: return "RES";
+	case ST5481_CMD_TM1: return "TM1";
+	case ST5481_CMD_TM2: return "TM2";
+	case ST5481_CMD_PUP: return "PUP";
+	case ST5481_CMD_AR8: return "AR8";
+	case ST5481_CMD_AR10: return "AR10";
+	case ST5481_CMD_ARL: return "ARL";
+	case ST5481_CMD_PDN: return "PDN";
+	};
+	
+	sprintf(s,"0x%x",evt);
+	return s;
+}	
+
+#else
+
+#define DBG_ISO_PACKET(level,urb) do {} while (0)
+
+#endif
+
+
+
+#endif 
diff --git a/drivers/isdn/hisax/st5481_b.c b/drivers/isdn/hisax/st5481_b.c
new file mode 100644
index 000000000000..2fcd093921d8
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_b.c
@@ -0,0 +1,374 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include "st5481.h"
+
+static inline void B_L1L2(struct st5481_bcs *bcs, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if;
+
+	ifc->l1l2(ifc, pr, arg);
+}
+
+/*
+ * Encode and transmit next frame.
+ */
+static void usb_b_out(struct st5481_bcs *bcs,int buf_nr)
+{
+	struct st5481_b_out *b_out = &bcs->b_out;
+	struct st5481_adapter *adapter = bcs->adapter;
+	struct urb *urb;
+	unsigned int packet_size,offset;
+	int len,buf_size,bytes_sent;
+	int i;
+	struct sk_buff *skb;
+	
+	if (test_and_set_bit(buf_nr, &b_out->busy)) {
+		DBG(4,"ep %d urb %d busy",(bcs->channel+1)*2,buf_nr);
+		return;
+	}
+	urb = b_out->urb[buf_nr];
+
+	// Adjust isoc buffer size according to flow state
+	if(b_out->flow_event & (OUT_DOWN | OUT_UNDERRUN)) {
+		buf_size = NUM_ISO_PACKETS_B*SIZE_ISO_PACKETS_B_OUT + B_FLOW_ADJUST;
+		packet_size = SIZE_ISO_PACKETS_B_OUT + B_FLOW_ADJUST;
+		DBG(4,"B%d,adjust flow,add %d bytes",bcs->channel+1,B_FLOW_ADJUST);
+	} else if(b_out->flow_event & OUT_UP){
+		buf_size = NUM_ISO_PACKETS_B*SIZE_ISO_PACKETS_B_OUT - B_FLOW_ADJUST;
+		packet_size = SIZE_ISO_PACKETS_B_OUT - B_FLOW_ADJUST;
+		DBG(4,"B%d,adjust flow,remove %d bytes",bcs->channel+1,B_FLOW_ADJUST);
+	} else {
+		buf_size = NUM_ISO_PACKETS_B*SIZE_ISO_PACKETS_B_OUT;
+		packet_size = 8;
+	}
+	b_out->flow_event = 0;
+
+	len = 0;
+	while (len < buf_size) {
+		if ((skb = b_out->tx_skb)) {
+			DBG_SKB(0x100, skb);
+			DBG(4,"B%d,len=%d",bcs->channel+1,skb->len);
+			
+			if (bcs->mode == L1_MODE_TRANS) {	
+				bytes_sent = buf_size - len;
+				if (skb->len < bytes_sent)
+					bytes_sent = skb->len;
+				{	/* swap tx bytes to get hearable audio data */
+					register unsigned char *src  = skb->data;
+					register unsigned char *dest = urb->transfer_buffer+len;
+					register unsigned int count;
+					for (count = 0; count < bytes_sent; count++)
+						*dest++ = isdnhdlc_bit_rev_tab[*src++];
+				}
+				len += bytes_sent;
+			} else {
+				len += isdnhdlc_encode(&b_out->hdlc_state,
+						       skb->data, skb->len, &bytes_sent,
+						       urb->transfer_buffer+len, buf_size-len);
+			}
+
+			skb_pull(skb, bytes_sent);
+
+			if (!skb->len) {
+				// Frame sent
+				b_out->tx_skb = NULL;
+				B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize);
+				dev_kfree_skb_any(skb);
+
+/* 				if (!(bcs->tx_skb = skb_dequeue(&bcs->sq))) { */
+/* 					st5481B_sched_event(bcs, B_XMTBUFREADY); */
+/* 				} */
+			}
+		} else {
+			if (bcs->mode == L1_MODE_TRANS) {
+				memset(urb->transfer_buffer+len, 0xff, buf_size-len);
+				len = buf_size;
+			} else {
+				// Send flags
+				len += isdnhdlc_encode(&b_out->hdlc_state,
+						       NULL, 0, &bytes_sent,
+						       urb->transfer_buffer+len, buf_size-len);
+			}
+		}	
+	}
+
+	// Prepare the URB
+	for (i = 0, offset = 0; offset < len; i++) {
+		urb->iso_frame_desc[i].offset = offset;
+		urb->iso_frame_desc[i].length = packet_size;
+		offset += packet_size;
+		packet_size = SIZE_ISO_PACKETS_B_OUT;
+	}
+	urb->transfer_buffer_length = len;
+	urb->number_of_packets = i;
+	urb->dev = adapter->usb_dev;
+
+	DBG_ISO_PACKET(0x200,urb);
+
+	SUBMIT_URB(urb, GFP_NOIO);
+}
+
+/*
+ * Start transfering (flags or data) on the B channel, since
+ * FIFO counters has been set to a non-zero value.
+ */
+static void st5481B_start_xfer(void *context)
+{
+	struct st5481_bcs *bcs = context;
+
+	DBG(4,"B%d",bcs->channel+1);
+
+	// Start transmitting (flags or data) on B channel
+
+	usb_b_out(bcs,0);
+	usb_b_out(bcs,1);
+}
+
+/*
+ * If the adapter has only 2 LEDs, the green
+ * LED will blink with a rate depending
+ * on the number of channels opened.
+ */
+static void led_blink(struct st5481_adapter *adapter)
+{
+	u_char leds = adapter->leds;
+
+	// 50 frames/sec for each channel
+	if (++adapter->led_counter % 50) {
+		return;
+	}
+
+	if (adapter->led_counter % 100) {
+		leds |= GREEN_LED;
+	} else {
+		leds &= ~GREEN_LED;
+	}
+	
+	st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, leds, NULL, NULL);
+}
+
+static void usb_b_out_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct st5481_bcs *bcs = urb->context;
+	struct st5481_b_out *b_out = &bcs->b_out;
+	struct st5481_adapter *adapter = bcs->adapter;
+	int buf_nr;
+	
+	buf_nr = get_buf_nr(b_out->urb, urb);
+	test_and_clear_bit(buf_nr, &b_out->busy);
+
+	if (unlikely(urb->status < 0)) {
+		if (urb->status != -ENOENT && urb->status != -ESHUTDOWN) {
+			WARN("urb status %d",urb->status);
+			if (b_out->busy == 0) {
+				st5481_usb_pipe_reset(adapter, (bcs->channel+1)*2 | USB_DIR_OUT, NULL, NULL);
+			}
+		} else {
+			DBG(1,"urb killed"); 
+			return; // Give up
+		}
+	}
+
+	usb_b_out(bcs,buf_nr);
+
+	if (adapter->number_of_leds == 2)
+		led_blink(adapter);
+}
+
+/*
+ * Start or stop the transfer on the B channel.
+ */
+static void st5481B_mode(struct st5481_bcs *bcs, int mode)
+{
+	struct st5481_b_out *b_out = &bcs->b_out;
+	struct st5481_adapter *adapter = bcs->adapter;
+
+	DBG(4,"B%d,mode=%d", bcs->channel + 1, mode);
+
+	if (bcs->mode == mode)
+		return;
+
+	bcs->mode = mode;
+
+	// Cancel all USB transfers on this B channel
+	usb_unlink_urb(b_out->urb[0]);
+	usb_unlink_urb(b_out->urb[1]);
+	b_out->busy = 0;
+
+	st5481_in_mode(&bcs->b_in, mode);
+	if (bcs->mode != L1_MODE_NULL) {
+		// Open the B channel
+		if (bcs->mode != L1_MODE_TRANS) {
+			isdnhdlc_out_init(&b_out->hdlc_state, 0, bcs->mode == L1_MODE_HDLC_56K);
+		}
+		st5481_usb_pipe_reset(adapter, (bcs->channel+1)*2, NULL, NULL);
+	
+		// Enable B channel interrupts
+		st5481_usb_device_ctrl_msg(adapter, FFMSK_B1+(bcs->channel*2), 
+				    OUT_UP+OUT_DOWN+OUT_UNDERRUN, NULL, NULL);
+
+		// Enable B channel FIFOs
+		st5481_usb_device_ctrl_msg(adapter, OUT_B1_COUNTER+(bcs->channel*2), 32, st5481B_start_xfer, bcs);
+		if (adapter->number_of_leds == 4) {
+			if (bcs->channel == 0) {
+				adapter->leds |= B1_LED;
+			} else {
+				adapter->leds |= B2_LED;
+			}
+		}
+	} else {
+		// Disble B channel interrupts
+		st5481_usb_device_ctrl_msg(adapter, FFMSK_B1+(bcs->channel*2), 0, NULL, NULL);
+
+		// Disable B channel FIFOs
+		st5481_usb_device_ctrl_msg(adapter, OUT_B1_COUNTER+(bcs->channel*2), 0, NULL, NULL);
+
+		if (adapter->number_of_leds == 4) {
+			if (bcs->channel == 0) {
+				adapter->leds &= ~B1_LED;
+			} else {
+				adapter->leds &= ~B2_LED;
+			}
+		} else {
+			st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL);
+		}
+		if (b_out->tx_skb) {
+			dev_kfree_skb_any(b_out->tx_skb);
+			b_out->tx_skb = NULL;
+		}
+		
+	}
+}
+
+static int st5481_setup_b_out(struct st5481_bcs *bcs)
+{
+	struct usb_device *dev = bcs->adapter->usb_dev;
+	struct usb_interface *intf;
+	struct usb_host_interface *altsetting = NULL;
+	struct usb_host_endpoint *endpoint;
+  	struct st5481_b_out *b_out = &bcs->b_out;
+
+	DBG(4,"");
+
+	intf = usb_ifnum_to_if(dev, 0);
+	if (intf)
+		altsetting = usb_altnum_to_altsetting(intf, 3);
+	if (!altsetting)
+		return -ENXIO;
+
+	// Allocate URBs and buffers for the B channel out
+	endpoint = &altsetting->endpoint[EP_B1_OUT - 1 + bcs->channel * 2];
+
+	DBG(4,"endpoint address=%02x,packet size=%d",
+	    endpoint->desc.bEndpointAddress, le16_to_cpu(endpoint->desc.wMaxPacketSize));
+
+	// Allocate memory for 8000bytes/sec + extra bytes if underrun
+	return st5481_setup_isocpipes(b_out->urb, dev, 
+				      usb_sndisocpipe(dev, endpoint->desc.bEndpointAddress),
+				      NUM_ISO_PACKETS_B, SIZE_ISO_PACKETS_B_OUT,
+				      NUM_ISO_PACKETS_B * SIZE_ISO_PACKETS_B_OUT + B_FLOW_ADJUST,
+				      usb_b_out_complete, bcs);
+}
+
+static void st5481_release_b_out(struct st5481_bcs *bcs)
+{
+	struct st5481_b_out *b_out = &bcs->b_out;
+
+	DBG(4,"");
+
+	st5481_release_isocpipes(b_out->urb);
+}
+
+int st5481_setup_b(struct st5481_bcs *bcs)
+{
+	int retval;
+
+	DBG(4,"");
+
+	retval = st5481_setup_b_out(bcs);
+	if (retval)
+		goto err;
+	bcs->b_in.bufsize = HSCX_BUFMAX;
+	bcs->b_in.num_packets = NUM_ISO_PACKETS_B;
+	bcs->b_in.packet_size = SIZE_ISO_PACKETS_B_IN;
+	bcs->b_in.ep = (bcs->channel ? EP_B2_IN : EP_B1_IN) | USB_DIR_IN;
+	bcs->b_in.counter = bcs->channel ? IN_B2_COUNTER : IN_B1_COUNTER;
+	bcs->b_in.adapter = bcs->adapter;
+	bcs->b_in.hisax_if = &bcs->b_if.ifc;
+	retval = st5481_setup_in(&bcs->b_in);
+	if (retval)
+		goto err_b_out;
+
+
+	return 0;
+
+ err_b_out:
+	st5481_release_b_out(bcs);
+ err:
+	return retval;
+}
+
+/*
+ * Release buffers and URBs for the B channels
+ */
+void st5481_release_b(struct st5481_bcs *bcs)
+{
+	DBG(4,"");
+
+	st5481_release_in(&bcs->b_in);
+	st5481_release_b_out(bcs);
+}
+
+/*
+ * st5481_b_l2l1 is the entry point for upper layer routines that want to
+ * transmit on the B channel.  PH_DATA | REQUEST is a normal packet that
+ * we either start transmitting (if idle) or queue (if busy).
+ * PH_PULL | REQUEST can be called to request a callback message
+ * (PH_PULL | CONFIRM)
+ * once the link is idle.  After a "pull" callback, the upper layer
+ * routines can use PH_PULL | INDICATION to send data.
+ */
+void st5481_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+	struct st5481_bcs *bcs = ifc->priv;
+	struct sk_buff *skb = arg;
+	int mode;
+
+	DBG(4, "");
+
+	switch (pr) {
+	case PH_DATA | REQUEST:
+		if (bcs->b_out.tx_skb)
+			BUG();
+		
+		bcs->b_out.tx_skb = skb;
+		break;
+	case PH_ACTIVATE | REQUEST:
+		mode = (int) arg;
+		DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode);
+		st5481B_mode(bcs, mode);
+		B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL);
+		break;
+	case PH_DEACTIVATE | REQUEST:
+		DBG(4,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1);
+		st5481B_mode(bcs, L1_MODE_NULL);
+		B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL);
+		break;
+	default:
+		WARN("pr %#x\n", pr);
+	}
+}
diff --git a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c
new file mode 100644
index 000000000000..071b1d31999f
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_d.c
@@ -0,0 +1,776 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include "st5481.h"
+
+static void ph_connect(struct st5481_adapter *adapter);
+static void ph_disconnect(struct st5481_adapter *adapter);
+
+static struct Fsm l1fsm;
+
+static char *strL1State[] =
+{
+	"ST_L1_F3",
+	"ST_L1_F4",
+	"ST_L1_F6",
+	"ST_L1_F7",
+	"ST_L1_F8",
+};
+
+static char *strL1Event[] =
+{
+	"EV_IND_DP",  
+	"EV_IND_1",   
+	"EV_IND_2",   
+	"EV_IND_3",   
+	"EV_IND_RSY", 
+	"EV_IND_5",   
+	"EV_IND_6",   
+	"EV_IND_7",   
+	"EV_IND_AP",  
+	"EV_IND_9",   
+	"EV_IND_10",  
+	"EV_IND_11",  
+	"EV_IND_AI8",
+	"EV_IND_AI10",
+	"EV_IND_AIL",
+	"EV_IND_DI",  
+	"EV_PH_ACTIVATE_REQ",
+	"EV_PH_DEACTIVATE_REQ",
+	"EV_TIMER3",
+};
+
+static inline void D_L1L2(struct st5481_adapter *adapter, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) &adapter->hisax_d_if;
+
+	ifc->l1l2(ifc, pr, arg);
+}
+
+static void
+l1_go_f3(struct FsmInst *fi, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fi->userdata;
+
+	if (fi->state == ST_L1_F7)
+		ph_disconnect(adapter);
+	
+	FsmChangeState(fi, ST_L1_F3);
+	D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void
+l1_go_f6(struct FsmInst *fi, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fi->userdata;
+
+	if (fi->state == ST_L1_F7)
+		ph_disconnect(adapter);
+
+	FsmChangeState(fi, ST_L1_F6);
+}
+
+static void
+l1_go_f7(struct FsmInst *fi, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fi->userdata;
+
+	FsmDelTimer(&adapter->timer, 0);
+	ph_connect(adapter);
+	FsmChangeState(fi, ST_L1_F7);
+	D_L1L2(adapter, PH_ACTIVATE | INDICATION, NULL);
+}
+
+static void
+l1_go_f8(struct FsmInst *fi, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fi->userdata;
+
+	if (fi->state == ST_L1_F7)
+		ph_disconnect(adapter);
+
+	FsmChangeState(fi, ST_L1_F8);
+}
+
+static void
+l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fi->userdata;
+
+	st5481_ph_command(adapter, ST5481_CMD_DR);
+	FsmChangeState(fi, ST_L1_F3);
+	D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void
+l1_ignore(struct FsmInst *fi, int event, void *arg)
+{
+}
+
+static void
+l1_activate(struct FsmInst *fi, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fi->userdata;
+
+	st5481_ph_command(adapter, ST5481_CMD_DR);
+	st5481_ph_command(adapter, ST5481_CMD_PUP);
+	FsmRestartTimer(&adapter->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+	st5481_ph_command(adapter, ST5481_CMD_AR8);
+	FsmChangeState(fi, ST_L1_F4);
+}
+
+static struct FsmNode L1FnList[] __initdata =
+{
+	{ST_L1_F3, EV_IND_DP,            l1_ignore},
+	{ST_L1_F3, EV_IND_AP,            l1_go_f6},
+	{ST_L1_F3, EV_IND_AI8,           l1_go_f7},
+	{ST_L1_F3, EV_IND_AI10,          l1_go_f7},
+	{ST_L1_F3, EV_PH_ACTIVATE_REQ,   l1_activate},
+
+	{ST_L1_F4, EV_TIMER3,            l1_timer3},
+	{ST_L1_F4, EV_IND_DP,            l1_go_f3},
+	{ST_L1_F4, EV_IND_AP,            l1_go_f6},
+	{ST_L1_F4, EV_IND_AI8,           l1_go_f7},
+	{ST_L1_F4, EV_IND_AI10,          l1_go_f7},
+
+	{ST_L1_F6, EV_TIMER3,            l1_timer3},
+	{ST_L1_F6, EV_IND_DP,            l1_go_f3},
+	{ST_L1_F6, EV_IND_AP,            l1_ignore},
+	{ST_L1_F6, EV_IND_AI8,           l1_go_f7},
+	{ST_L1_F6, EV_IND_AI10,          l1_go_f7},
+	{ST_L1_F7, EV_IND_RSY,           l1_go_f8},
+
+	{ST_L1_F7, EV_IND_DP,            l1_go_f3},
+	{ST_L1_F7, EV_IND_AP,            l1_go_f6},
+	{ST_L1_F7, EV_IND_AI8,           l1_ignore},
+	{ST_L1_F7, EV_IND_AI10,          l1_ignore},
+	{ST_L1_F7, EV_IND_RSY,           l1_go_f8},
+
+	{ST_L1_F8, EV_TIMER3,            l1_timer3},
+	{ST_L1_F8, EV_IND_DP,            l1_go_f3},
+	{ST_L1_F8, EV_IND_AP,            l1_go_f6},
+	{ST_L1_F8, EV_IND_AI8,           l1_go_f8},
+	{ST_L1_F8, EV_IND_AI10,          l1_go_f8},
+	{ST_L1_F8, EV_IND_RSY,           l1_ignore},
+};
+
+static void l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	char buf[256];
+	
+	va_start(args, fmt);
+	vsprintf(buf, fmt, args);
+	DBG(8, "%s", buf);
+	va_end(args);
+}
+
+/* ======================================================================
+ * D-Channel out
+ */
+
+/*
+  D OUT state machine:
+  ====================
+
+  Transmit short frame (< 16 bytes of encoded data):
+
+  L1 FRAME    D_OUT_STATE           USB                  D CHANNEL
+  --------    -----------           ---                  ---------
+ 
+              FIXME
+
+ -> [xx..xx]  SHORT_INIT            -> [7Exx..xxC1C27EFF]
+              SHORT_WAIT_DEN        <> OUT_D_COUNTER=16 
+                                                 
+              END_OF_SHORT          <- DEN_EVENT         -> 7Exx
+                                                          xxxx 
+                                                          xxxx
+							  xxxx 
+							  xxxx
+							  xxxx
+							  C1C1 
+							  7EFF 
+              WAIT_FOR_RESET_IDLE   <- D_UNDERRUN        <- (8ms)                        
+              IDLE                  <> Reset pipe
+
+              
+
+  Transmit long frame (>= 16 bytes of encoded data):
+
+  L1 FRAME    D_OUT_STATE           USB                  D CHANNEL
+  --------    -----------           ---                  ---------
+
+ -> [xx...xx] IDLE
+              WAIT_FOR_STOP         <> OUT_D_COUNTER=0
+              WAIT_FOR_RESET        <> Reset pipe
+	      STOP
+	      INIT_LONG_FRAME       -> [7Exx..xx]
+              WAIT_DEN              <> OUT_D_COUNTER=16 
+              OUT_NORMAL            <- DEN_EVENT       -> 7Exx
+              END_OF_FRAME_BUSY     -> [xxxx]             xxxx 
+              END_OF_FRAME_NOT_BUSY -> [xxxx]             xxxx
+				    -> [xxxx]		  xxxx 
+				    -> [C1C2]		  xxxx
+				    -> [7EFF]		  xxxx
+							  xxxx 
+							  xxxx 
+                                                          ....
+							  xxxx
+							  C1C2
+							  7EFF
+	                 	    <- D_UNDERRUN      <- (> 8ms)                        
+              WAIT_FOR_STOP         <> OUT_D_COUNTER=0
+              WAIT_FOR_RESET        <> Reset pipe
+	      STOP
+
+*/          
+
+static struct Fsm dout_fsm;
+
+static char *strDoutState[] =
+{
+	"ST_DOUT_NONE",
+
+	"ST_DOUT_SHORT_INIT",
+	"ST_DOUT_SHORT_WAIT_DEN",
+
+	"ST_DOUT_LONG_INIT",
+	"ST_DOUT_LONG_WAIT_DEN",
+	"ST_DOUT_NORMAL",
+
+	"ST_DOUT_WAIT_FOR_UNDERRUN",
+        "ST_DOUT_WAIT_FOR_NOT_BUSY",
+	"ST_DOUT_WAIT_FOR_STOP",
+	"ST_DOUT_WAIT_FOR_RESET",
+};
+
+static char *strDoutEvent[] =
+{
+	"EV_DOUT_START_XMIT",
+	"EV_DOUT_COMPLETE",
+	"EV_DOUT_DEN",
+	"EV_DOUT_RESETED",
+	"EV_DOUT_STOPPED",
+	"EV_DOUT_COLL",
+	"EV_DOUT_UNDERRUN",
+};
+
+static void dout_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	char buf[256];
+	
+	va_start(args, fmt);
+	vsprintf(buf, fmt, args);
+	DBG(0x2, "%s", buf);
+	va_end(args);
+}
+
+static void dout_stop_event(void *context)
+{
+	struct st5481_adapter *adapter = context;
+
+	FsmEvent(&adapter->d_out.fsm, EV_DOUT_STOPPED, NULL);
+}
+
+/*
+ * Start the transfer of a D channel frame.
+ */
+static void usb_d_out(struct st5481_adapter *adapter, int buf_nr)
+{
+	struct st5481_d_out *d_out = &adapter->d_out;
+	struct urb *urb;
+	unsigned int num_packets, packet_offset;
+	int len, buf_size, bytes_sent;
+	struct sk_buff *skb;
+	struct usb_iso_packet_descriptor *desc;
+
+	if (d_out->fsm.state != ST_DOUT_NORMAL)
+		return;
+
+	if (test_and_set_bit(buf_nr, &d_out->busy)) {
+		DBG(2, "ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy);
+		return;
+	}
+	urb = d_out->urb[buf_nr];
+
+	skb = d_out->tx_skb;
+
+	buf_size = NUM_ISO_PACKETS_D * SIZE_ISO_PACKETS_D_OUT;
+	
+	if (skb) {
+		len = isdnhdlc_encode(&d_out->hdlc_state,
+				      skb->data, skb->len, &bytes_sent,
+				      urb->transfer_buffer, buf_size);
+		skb_pull(skb,bytes_sent);
+	} else {
+		// Send flags or idle
+		len = isdnhdlc_encode(&d_out->hdlc_state,
+				      NULL, 0, &bytes_sent,
+				      urb->transfer_buffer, buf_size);
+	}
+	
+	if (len < buf_size) {
+		FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_UNDERRUN);
+	}
+	if (skb && !skb->len) {
+		d_out->tx_skb = NULL;
+		D_L1L2(adapter, PH_DATA | CONFIRM, NULL);
+		dev_kfree_skb_any(skb);
+	}
+
+	// Prepare the URB
+	urb->transfer_buffer_length = len;
+	num_packets = 0;
+	packet_offset = 0;
+	while (packet_offset < len) {
+		desc = &urb->iso_frame_desc[num_packets];
+		desc->offset = packet_offset;
+		desc->length = SIZE_ISO_PACKETS_D_OUT;
+		if (len - packet_offset < desc->length)
+			desc->length = len - packet_offset;
+		num_packets++;
+		packet_offset += desc->length;
+	}
+	urb->number_of_packets = num_packets;
+
+	// Prepare the URB
+	urb->dev = adapter->usb_dev;
+	// Need to transmit the next buffer 2ms after the DEN_EVENT
+	urb->transfer_flags = 0;
+	urb->start_frame = usb_get_current_frame_number(adapter->usb_dev)+2;
+
+	DBG_ISO_PACKET(0x20,urb);
+
+	if (usb_submit_urb(urb, GFP_KERNEL) < 0) {
+		// There is another URB queued up
+		urb->transfer_flags = URB_ISO_ASAP;
+		SUBMIT_URB(urb, GFP_KERNEL);
+	}	
+}
+
+static void fifo_reseted(void *context)
+{
+	struct st5481_adapter *adapter = context;
+
+	FsmEvent(&adapter->d_out.fsm, EV_DOUT_RESETED, NULL);
+}
+
+static void usb_d_out_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct st5481_adapter *adapter = urb->context;
+	struct st5481_d_out *d_out = &adapter->d_out;
+	int buf_nr;
+	
+	DBG(2, "");
+
+	buf_nr = get_buf_nr(d_out->urb, urb);
+	test_and_clear_bit(buf_nr, &d_out->busy);
+
+	if (unlikely(urb->status < 0)) {
+		if (urb->status != -ENOENT && urb->status != -ESHUTDOWN) {
+			WARN("urb status %d",urb->status);
+			if (d_out->busy == 0) {
+				st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter);
+			}
+			return;
+		} else {
+			DBG(1,"urb killed"); 
+			return; // Give up
+		}
+	}
+
+	FsmEvent(&adapter->d_out.fsm, EV_DOUT_COMPLETE, (void *) buf_nr);
+}
+
+/* ====================================================================== */
+
+static void dout_start_xmit(struct FsmInst *fsm, int event, void *arg)
+{
+	// FIXME unify?
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+	struct urb *urb;
+	int len, bytes_sent;
+	struct sk_buff *skb;
+	int buf_nr = 0;
+
+	skb = d_out->tx_skb;
+
+	DBG(2,"len=%d",skb->len);
+
+	isdnhdlc_out_init(&d_out->hdlc_state, 1, 0);
+
+	if (test_and_set_bit(buf_nr, &d_out->busy)) {
+		WARN("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy);
+		return;
+	}
+	urb = d_out->urb[buf_nr];
+
+	DBG_SKB(0x10, skb);
+	len = isdnhdlc_encode(&d_out->hdlc_state,
+			      skb->data, skb->len, &bytes_sent,
+			      urb->transfer_buffer, 16);
+	skb_pull(skb, bytes_sent);
+
+	if(len < 16)
+		FsmChangeState(&d_out->fsm, ST_DOUT_SHORT_INIT);
+	else
+		FsmChangeState(&d_out->fsm, ST_DOUT_LONG_INIT);
+
+	if (skb->len == 0) {
+		d_out->tx_skb = NULL;
+		D_L1L2(adapter, PH_DATA | CONFIRM, NULL);
+		dev_kfree_skb_any(skb);
+	}
+
+// Prepare the URB
+	urb->transfer_buffer_length = len;
+
+	urb->iso_frame_desc[0].offset = 0;
+	urb->iso_frame_desc[0].length = len;
+	urb->number_of_packets = 1;
+
+	// Prepare the URB
+	urb->dev = adapter->usb_dev;
+	urb->transfer_flags = URB_ISO_ASAP;
+
+	DBG_ISO_PACKET(0x20,urb);
+	SUBMIT_URB(urb, GFP_KERNEL);
+}
+
+static void dout_short_fifo(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	FsmChangeState(&d_out->fsm, ST_DOUT_SHORT_WAIT_DEN);
+	st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 16, NULL, NULL);
+}
+
+static void dout_end_short_frame(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_UNDERRUN);
+}
+
+static void dout_long_enable_fifo(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+    
+	st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 16, NULL, NULL);
+	FsmChangeState(&d_out->fsm, ST_DOUT_LONG_WAIT_DEN);
+}
+
+static void dout_long_den(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	FsmChangeState(&d_out->fsm, ST_DOUT_NORMAL);
+	usb_d_out(adapter, 0);
+	usb_d_out(adapter, 1);
+}
+
+static void dout_reset(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_RESET);
+	st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter);
+}
+
+static void dout_stop(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_STOP);
+	st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 0, dout_stop_event, adapter);
+}
+
+static void dout_underrun(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	if (test_bit(0, &d_out->busy) || test_bit(1, &d_out->busy)) {
+		FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_NOT_BUSY);
+	}  else {
+		dout_stop(fsm, event, arg);
+	}
+}
+
+static void dout_check_busy(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	if (!test_bit(0, &d_out->busy) && !test_bit(1, &d_out->busy))
+		dout_stop(fsm, event, arg);
+}
+
+static void dout_reseted(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	FsmChangeState(&d_out->fsm, ST_DOUT_NONE);
+	// FIXME locking
+	if (d_out->tx_skb)
+		FsmEvent(&d_out->fsm, EV_DOUT_START_XMIT, NULL);
+}
+
+static void dout_complete(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	int buf_nr = (int) arg;
+
+	usb_d_out(adapter, buf_nr);
+}
+
+static void dout_ignore(struct FsmInst *fsm, int event, void *arg)
+{
+}
+
+static struct FsmNode DoutFnList[] __initdata =
+{
+	{ST_DOUT_NONE,                   EV_DOUT_START_XMIT,   dout_start_xmit},
+
+	{ST_DOUT_SHORT_INIT,             EV_DOUT_COMPLETE,     dout_short_fifo},
+
+	{ST_DOUT_SHORT_WAIT_DEN,         EV_DOUT_DEN,          dout_end_short_frame},
+	{ST_DOUT_SHORT_WAIT_DEN,         EV_DOUT_UNDERRUN,     dout_underrun},
+
+	{ST_DOUT_LONG_INIT,              EV_DOUT_COMPLETE,     dout_long_enable_fifo},
+
+	{ST_DOUT_LONG_WAIT_DEN,          EV_DOUT_DEN,          dout_long_den},
+	{ST_DOUT_LONG_WAIT_DEN,          EV_DOUT_UNDERRUN,     dout_underrun},
+
+	{ST_DOUT_NORMAL,                 EV_DOUT_UNDERRUN,     dout_underrun},
+	{ST_DOUT_NORMAL,                 EV_DOUT_COMPLETE,     dout_complete},
+
+	{ST_DOUT_WAIT_FOR_UNDERRUN,      EV_DOUT_UNDERRUN,     dout_underrun},
+	{ST_DOUT_WAIT_FOR_UNDERRUN,      EV_DOUT_COMPLETE,     dout_ignore},
+
+	{ST_DOUT_WAIT_FOR_NOT_BUSY,      EV_DOUT_COMPLETE,     dout_check_busy},
+
+	{ST_DOUT_WAIT_FOR_STOP,          EV_DOUT_STOPPED,      dout_reset},
+
+	{ST_DOUT_WAIT_FOR_RESET,         EV_DOUT_RESETED,      dout_reseted},
+};
+
+void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg)
+{
+	struct st5481_adapter *adapter = hisax_d_if->priv;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+	case PH_ACTIVATE | REQUEST:
+		FsmEvent(&adapter->l1m, EV_PH_ACTIVATE_REQ, NULL);
+		break;
+	case PH_DEACTIVATE | REQUEST:
+		FsmEvent(&adapter->l1m, EV_PH_DEACTIVATE_REQ, NULL);
+		break;
+	case PH_DATA | REQUEST:
+		DBG(2, "PH_DATA REQUEST len %d", skb->len);
+		if (adapter->d_out.tx_skb)
+			BUG();
+
+		adapter->d_out.tx_skb = skb;
+		FsmEvent(&adapter->d_out.fsm, EV_DOUT_START_XMIT, NULL);
+		break;
+	default:
+		WARN("pr %#x\n", pr);
+		break;
+	}
+}
+
+/* ======================================================================
+ */
+
+/*
+ * Start receiving on the D channel since entered state F7.
+ */
+static void ph_connect(struct st5481_adapter *adapter)
+{
+	struct st5481_d_out *d_out = &adapter->d_out;
+	struct st5481_in *d_in = &adapter->d_in;
+
+	DBG(8,"");
+		
+	FsmChangeState(&d_out->fsm, ST_DOUT_NONE);
+
+	//	st5481_usb_device_ctrl_msg(adapter, FFMSK_D, OUT_UNDERRUN, NULL, NULL);
+	st5481_usb_device_ctrl_msg(adapter, FFMSK_D, 0xfc, NULL, NULL);
+	st5481_in_mode(d_in, L1_MODE_HDLC);
+
+#ifdef LOOPBACK
+	// Turn loopback on (data sent on B and D looped back)
+	st5481_usb_device_ctrl_msg(cs, LBB, 0x04, NULL, NULL);
+#endif
+
+	st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, NULL, NULL);
+
+	// Turn on the green LED to tell that we are in state F7
+	adapter->leds |= GREEN_LED;
+	st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL);
+}
+
+/*
+ * Stop receiving on the D channel since not in state F7.
+ */
+static void ph_disconnect(struct st5481_adapter *adapter)
+{
+	DBG(8,"");
+
+	st5481_in_mode(&adapter->d_in, L1_MODE_NULL);
+
+	// Turn off the green LED to tell that we left state F7
+	adapter->leds &= ~GREEN_LED;
+	st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL);
+}
+
+static int st5481_setup_d_out(struct st5481_adapter *adapter)
+{
+	struct usb_device *dev = adapter->usb_dev;
+	struct usb_interface *intf;
+	struct usb_host_interface *altsetting = NULL;
+	struct usb_host_endpoint *endpoint;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	DBG(2,"");
+
+	intf = usb_ifnum_to_if(dev, 0);
+	if (intf)
+		altsetting = usb_altnum_to_altsetting(intf, 3);
+	if (!altsetting)
+		return -ENXIO;
+
+	// Allocate URBs and buffers for the D channel out
+	endpoint = &altsetting->endpoint[EP_D_OUT-1];
+
+	DBG(2,"endpoint address=%02x,packet size=%d",
+	    endpoint->desc.bEndpointAddress, le16_to_cpu(endpoint->desc.wMaxPacketSize));
+
+	return st5481_setup_isocpipes(d_out->urb, dev, 
+				      usb_sndisocpipe(dev, endpoint->desc.bEndpointAddress),
+				      NUM_ISO_PACKETS_D, SIZE_ISO_PACKETS_D_OUT,
+				      NUM_ISO_PACKETS_D * SIZE_ISO_PACKETS_D_OUT,
+				      usb_d_out_complete, adapter);
+}
+
+static void st5481_release_d_out(struct st5481_adapter *adapter)
+{
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	DBG(2,"");
+
+	st5481_release_isocpipes(d_out->urb);
+}
+
+int st5481_setup_d(struct st5481_adapter *adapter)
+{
+	int retval;
+
+	DBG(2,"");
+
+	retval = st5481_setup_d_out(adapter);
+	if (retval)
+		goto err;
+	adapter->d_in.bufsize = MAX_DFRAME_LEN_L1;
+	adapter->d_in.num_packets = NUM_ISO_PACKETS_D;
+	adapter->d_in.packet_size = SIZE_ISO_PACKETS_D_IN;
+	adapter->d_in.ep = EP_D_IN | USB_DIR_IN;
+	adapter->d_in.counter = IN_D_COUNTER;
+	adapter->d_in.adapter = adapter;
+	adapter->d_in.hisax_if = &adapter->hisax_d_if.ifc;
+	retval = st5481_setup_in(&adapter->d_in);
+	if (retval)
+		goto err_d_out;
+
+	adapter->l1m.fsm = &l1fsm;
+	adapter->l1m.state = ST_L1_F3;
+	adapter->l1m.debug = 1;
+	adapter->l1m.userdata = adapter;
+	adapter->l1m.printdebug = l1m_debug;
+	FsmInitTimer(&adapter->l1m, &adapter->timer);
+
+	adapter->d_out.fsm.fsm = &dout_fsm;
+	adapter->d_out.fsm.state = ST_DOUT_NONE;
+	adapter->d_out.fsm.debug = 1;
+	adapter->d_out.fsm.userdata = adapter;
+	adapter->d_out.fsm.printdebug = dout_debug;
+
+	return 0;
+
+ err_d_out:
+	st5481_release_d_out(adapter);
+ err:
+	return retval;
+}
+
+void st5481_release_d(struct st5481_adapter *adapter)
+{
+	DBG(2,"");
+
+	st5481_release_in(&adapter->d_in);
+	st5481_release_d_out(adapter);
+}
+
+/* ======================================================================
+ * init / exit
+ */
+
+int __init st5481_d_init(void)
+{
+	int retval;
+
+	l1fsm.state_count = L1_STATE_COUNT;
+	l1fsm.event_count = L1_EVENT_COUNT;
+	l1fsm.strEvent = strL1Event;
+	l1fsm.strState = strL1State;
+	retval = FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList));
+	if (retval)
+		goto err;
+
+	dout_fsm.state_count = DOUT_STATE_COUNT;
+	dout_fsm.event_count = DOUT_EVENT_COUNT;
+	dout_fsm.strEvent = strDoutEvent;
+	dout_fsm.strState = strDoutState;
+	retval = FsmNew(&dout_fsm, DoutFnList, ARRAY_SIZE(DoutFnList));
+	if (retval)
+		goto err_l1;
+
+	return 0;
+
+ err_l1:
+	FsmFree(&l1fsm);
+ err:
+	return retval;
+}
+
+// can't be __exit
+void st5481_d_exit(void)
+{
+	FsmFree(&l1fsm);
+	FsmFree(&dout_fsm);
+}
diff --git a/drivers/isdn/hisax/st5481_hdlc.c b/drivers/isdn/hisax/st5481_hdlc.c
new file mode 100644
index 000000000000..680f42e9a993
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_hdlc.c
@@ -0,0 +1,580 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/crc-ccitt.h>
+#include "st5481_hdlc.h"
+
+
+enum {
+	HDLC_FAST_IDLE,HDLC_GET_FLAG_B0,HDLC_GETFLAG_B1A6,HDLC_GETFLAG_B7,
+	HDLC_GET_DATA,HDLC_FAST_FLAG
+};
+
+enum {
+	HDLC_SEND_DATA,HDLC_SEND_CRC1,HDLC_SEND_FAST_FLAG,
+	HDLC_SEND_FIRST_FLAG,HDLC_SEND_CRC2,HDLC_SEND_CLOSING_FLAG,
+	HDLC_SEND_IDLE1,HDLC_SEND_FAST_IDLE,HDLC_SENDFLAG_B0,
+	HDLC_SENDFLAG_B1A6,HDLC_SENDFLAG_B7,STOPPED
+};
+
+void 
+hdlc_rcv_init(struct hdlc_vars *hdlc, int do_adapt56)
+{
+   	hdlc->bit_shift = 0;
+	hdlc->hdlc_bits1 = 0;
+	hdlc->data_bits = 0;
+	hdlc->ffbit_shift = 0;
+	hdlc->data_received = 0;
+	hdlc->state = HDLC_GET_DATA;
+	hdlc->do_adapt56 = do_adapt56;
+	hdlc->dchannel = 0;
+	hdlc->crc = 0;
+	hdlc->cbin = 0;
+	hdlc->shift_reg = 0;
+	hdlc->ffvalue = 0;
+	hdlc->dstpos = 0;
+}
+
+void 
+hdlc_out_init(struct hdlc_vars *hdlc, int is_d_channel, int do_adapt56)
+{
+   	hdlc->bit_shift = 0;
+	hdlc->hdlc_bits1 = 0;
+	hdlc->data_bits = 0;
+	hdlc->ffbit_shift = 0;
+	hdlc->data_received = 0;
+	hdlc->do_closing = 0;
+	hdlc->ffvalue = 0;
+	if (is_d_channel) {
+		hdlc->dchannel = 1;
+		hdlc->state = HDLC_SEND_FIRST_FLAG;
+	} else {
+		hdlc->dchannel = 0;
+		hdlc->state = HDLC_SEND_FAST_FLAG;
+		hdlc->ffvalue = 0x7e;
+	} 
+	hdlc->cbin = 0x7e;
+	hdlc->bit_shift = 0;
+	if(do_adapt56){
+		hdlc->do_adapt56 = 1;		
+		hdlc->data_bits = 0;
+		hdlc->state = HDLC_SENDFLAG_B0;
+	} else {
+		hdlc->do_adapt56 = 0;		
+		hdlc->data_bits = 8;
+	}
+	hdlc->shift_reg = 0;
+}
+
+/*
+  hdlc_decode - decodes HDLC frames from a transparent bit stream.
+
+  The source buffer is scanned for valid HDLC frames looking for
+  flags (01111110) to indicate the start of a frame. If the start of
+  the frame is found, the bit stuffing is removed (0 after 5 1's).
+  When a new flag is found, the complete frame has been received
+  and the CRC is checked.
+  If a valid frame is found, the function returns the frame length 
+  excluding the CRC with the bit HDLC_END_OF_FRAME set.
+  If the beginning of a valid frame is found, the function returns
+  the length. 
+  If a framing error is found (too many 1s and not a flag) the function 
+  returns the length with the bit HDLC_FRAMING_ERROR set.
+  If a CRC error is found the function returns the length with the
+  bit HDLC_CRC_ERROR set.
+  If the frame length exceeds the destination buffer size, the function
+  returns the length with the bit HDLC_LENGTH_ERROR set.
+
+  src - source buffer
+  slen - source buffer length
+  count - number of bytes removed (decoded) from the source buffer
+  dst _ destination buffer
+  dsize - destination buffer size
+  returns - number of decoded bytes in the destination buffer and status
+  flag.
+ */
+int hdlc_decode(struct hdlc_vars *hdlc, const unsigned char *src,
+		int slen, int *count, unsigned char *dst, int dsize)
+{
+	int status=0;
+
+	static const unsigned char fast_flag[]={
+		0x00,0x00,0x00,0x20,0x30,0x38,0x3c,0x3e,0x3f
+	};
+
+	static const unsigned char fast_flag_value[]={
+		0x00,0x7e,0xfc,0xf9,0xf3,0xe7,0xcf,0x9f,0x3f
+	};
+
+	static const unsigned char fast_abort[]={
+		0x00,0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff
+	};
+
+	*count = slen;
+
+	while(slen > 0){
+		if(hdlc->bit_shift==0){
+			hdlc->cbin = *src++;
+			slen--;
+			hdlc->bit_shift = 8;
+			if(hdlc->do_adapt56){
+				hdlc->bit_shift --;
+			}
+		}
+
+		switch(hdlc->state){
+		case STOPPED:
+			return 0;
+		case HDLC_FAST_IDLE:
+			if(hdlc->cbin == 0xff){
+				hdlc->bit_shift = 0;
+				break;
+			}
+			hdlc->state = HDLC_GET_FLAG_B0;
+			hdlc->hdlc_bits1 = 0;
+			hdlc->bit_shift = 8;
+			break;
+		case HDLC_GET_FLAG_B0:
+			if(!(hdlc->cbin & 0x80)) {
+				hdlc->state = HDLC_GETFLAG_B1A6;
+				hdlc->hdlc_bits1 = 0;
+			} else {
+				if(!hdlc->do_adapt56){
+					if(++hdlc->hdlc_bits1 >=8 ) if(hdlc->bit_shift==1)
+						hdlc->state = HDLC_FAST_IDLE;
+				}
+			}
+			hdlc->cbin<<=1;
+			hdlc->bit_shift --;
+			break;
+		case HDLC_GETFLAG_B1A6:
+			if(hdlc->cbin & 0x80){
+				hdlc->hdlc_bits1++;
+				if(hdlc->hdlc_bits1==6){
+					hdlc->state = HDLC_GETFLAG_B7;
+				}
+			} else {
+				hdlc->hdlc_bits1 = 0;
+			}
+			hdlc->cbin<<=1;
+			hdlc->bit_shift --;
+			break;
+		case HDLC_GETFLAG_B7:
+			if(hdlc->cbin & 0x80) {
+				hdlc->state = HDLC_GET_FLAG_B0;
+			} else {
+				hdlc->state = HDLC_GET_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->shift_reg = 0;
+				hdlc->hdlc_bits1 = 0;
+				hdlc->data_bits = 0;
+				hdlc->data_received = 0;
+			}
+			hdlc->cbin<<=1;
+			hdlc->bit_shift --;
+			break;
+		case HDLC_GET_DATA:
+			if(hdlc->cbin & 0x80){
+				hdlc->hdlc_bits1++;
+				switch(hdlc->hdlc_bits1){
+				case 6:
+					break;
+				case 7:
+					if(hdlc->data_received) {
+						// bad frame
+						status = -HDLC_FRAMING_ERROR;
+					}
+					if(!hdlc->do_adapt56){
+						if(hdlc->cbin==fast_abort[hdlc->bit_shift+1]){
+							hdlc->state = HDLC_FAST_IDLE;
+							hdlc->bit_shift=1;
+							break;
+						}
+					} else {
+						hdlc->state = HDLC_GET_FLAG_B0;
+					}
+					break;
+				default:
+					hdlc->shift_reg>>=1;
+					hdlc->shift_reg |= 0x80;
+					hdlc->data_bits++;
+					break;
+				}
+			} else {
+				switch(hdlc->hdlc_bits1){
+				case 5:
+					break;
+				case 6:
+					if(hdlc->data_received){
+						if (hdlc->dstpos < 2) {
+							status = -HDLC_FRAMING_ERROR;
+						} else if (hdlc->crc != 0xf0b8){
+							// crc error
+							status = -HDLC_CRC_ERROR;
+						} else {
+							// remove CRC
+							hdlc->dstpos -= 2;
+							// good frame
+							status = hdlc->dstpos;
+						}
+					}
+					hdlc->crc = 0xffff;
+					hdlc->shift_reg = 0;
+					hdlc->data_bits = 0;
+					if(!hdlc->do_adapt56){
+						if(hdlc->cbin==fast_flag[hdlc->bit_shift]){
+							hdlc->ffvalue = fast_flag_value[hdlc->bit_shift];
+							hdlc->state = HDLC_FAST_FLAG;
+							hdlc->ffbit_shift = hdlc->bit_shift;
+							hdlc->bit_shift = 1;
+						} else {
+							hdlc->state = HDLC_GET_DATA;
+							hdlc->data_received = 0;
+						}
+					} else {
+						hdlc->state = HDLC_GET_DATA;
+						hdlc->data_received = 0;
+					}
+					break;
+				default:
+					hdlc->shift_reg>>=1;
+					hdlc->data_bits++;
+					break;
+				}
+				hdlc->hdlc_bits1 = 0;
+			}
+			if (status) {
+				hdlc->dstpos = 0;
+				*count -= slen;
+				hdlc->cbin <<= 1;
+				hdlc->bit_shift--;
+				return status;
+			}
+			if(hdlc->data_bits==8){
+				hdlc->data_bits = 0;
+				hdlc->data_received = 1;
+				hdlc->crc = crc_ccitt_byte(hdlc->crc, hdlc->shift_reg);
+
+				// good byte received
+				if (dsize--) {
+					dst[hdlc->dstpos++] = hdlc->shift_reg;
+				} else {
+					// frame too long
+					status = -HDLC_LENGTH_ERROR;
+					hdlc->dstpos = 0;
+				}
+			}
+			hdlc->cbin <<= 1;
+			hdlc->bit_shift--;
+			break;
+		case HDLC_FAST_FLAG:
+			if(hdlc->cbin==hdlc->ffvalue){
+				hdlc->bit_shift = 0;
+				break;
+			} else {
+				if(hdlc->cbin == 0xff){
+					hdlc->state = HDLC_FAST_IDLE;
+					hdlc->bit_shift=0;
+				} else if(hdlc->ffbit_shift==8){
+					hdlc->state = HDLC_GETFLAG_B7;
+					break;
+				} else {
+					hdlc->shift_reg = fast_abort[hdlc->ffbit_shift-1];
+					hdlc->hdlc_bits1 = hdlc->ffbit_shift-2;
+					if(hdlc->hdlc_bits1<0)hdlc->hdlc_bits1 = 0;
+					hdlc->data_bits = hdlc->ffbit_shift-1;
+					hdlc->state = HDLC_GET_DATA;
+					hdlc->data_received = 0;
+				}
+			}
+			break;
+		default:
+			break;
+		}
+	}
+	*count -= slen;
+	return 0;
+}
+
+/*
+  hdlc_encode - encodes HDLC frames to a transparent bit stream.
+
+  The bit stream starts with a beginning flag (01111110). After
+  that each byte is added to the bit stream with bit stuffing added
+  (0 after 5 1's).
+  When the last byte has been removed from the source buffer, the
+  CRC (2 bytes is added) and the frame terminates with the ending flag.
+  For the dchannel, the idle character (all 1's) is also added at the end.
+  If this function is called with empty source buffer (slen=0), flags or
+  idle character will be generated.
+ 
+  src - source buffer
+  slen - source buffer length
+  count - number of bytes removed (encoded) from source buffer
+  dst _ destination buffer
+  dsize - destination buffer size
+  returns - number of encoded bytes in the destination buffer
+*/
+int hdlc_encode(struct hdlc_vars *hdlc, const unsigned char *src, 
+		unsigned short slen, int *count,
+		unsigned char *dst, int dsize)
+{
+	static const unsigned char xfast_flag_value[] = {
+		0x7e,0x3f,0x9f,0xcf,0xe7,0xf3,0xf9,0xfc,0x7e
+	};
+
+	int len = 0;
+
+	*count = slen;
+
+	while (dsize > 0) {
+		if(hdlc->bit_shift==0){	
+			if(slen && !hdlc->do_closing){
+				hdlc->shift_reg = *src++;
+				slen--;
+				if (slen == 0) 
+					hdlc->do_closing = 1;  /* closing sequence, CRC + flag(s) */
+				hdlc->bit_shift = 8;
+			} else {
+				if(hdlc->state == HDLC_SEND_DATA){
+					if(hdlc->data_received){
+						hdlc->state = HDLC_SEND_CRC1;
+						hdlc->crc ^= 0xffff;
+						hdlc->bit_shift = 8;
+						hdlc->shift_reg = hdlc->crc & 0xff;
+					} else if(!hdlc->do_adapt56){
+						hdlc->state = HDLC_SEND_FAST_FLAG;
+					} else {
+						hdlc->state = HDLC_SENDFLAG_B0;
+					}
+				}
+			  
+			}
+		}
+
+		switch(hdlc->state){
+		case STOPPED:
+			while (dsize--)
+				*dst++ = 0xff;
+		  
+			return dsize;
+		case HDLC_SEND_FAST_FLAG:
+			hdlc->do_closing = 0;
+			if(slen == 0){
+				*dst++ = hdlc->ffvalue;
+				len++;
+				dsize--;
+				break;
+			}
+			if(hdlc->bit_shift==8){
+				hdlc->cbin = hdlc->ffvalue>>(8-hdlc->data_bits);
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+				hdlc->data_received = 1;
+			}
+			break;
+		case HDLC_SENDFLAG_B0:
+			hdlc->do_closing = 0;
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			hdlc->hdlc_bits1 = 0;
+			hdlc->state = HDLC_SENDFLAG_B1A6;
+			break;
+		case HDLC_SENDFLAG_B1A6:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			hdlc->cbin++;
+			if(++hdlc->hdlc_bits1 == 6)
+				hdlc->state = HDLC_SENDFLAG_B7;
+			break;
+		case HDLC_SENDFLAG_B7:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(slen == 0){
+				hdlc->state = HDLC_SENDFLAG_B0;
+				break;
+			}
+			if(hdlc->bit_shift==8){
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+				hdlc->data_received = 1;
+			}
+			break;
+		case HDLC_SEND_FIRST_FLAG:
+			hdlc->data_received = 1;
+			if(hdlc->data_bits==8){
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->shift_reg & 0x01)
+				hdlc->cbin++;
+			hdlc->shift_reg >>= 1;
+			hdlc->bit_shift--;
+			if(hdlc->bit_shift==0){
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+			}
+			break;
+		case HDLC_SEND_DATA:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->bit_shift==8){
+				hdlc->crc = crc_ccitt_byte(hdlc->crc, hdlc->shift_reg);
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->hdlc_bits1++;
+				hdlc->cbin++;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			} else {
+				hdlc->hdlc_bits1 = 0;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			}
+			break;
+		case HDLC_SEND_CRC1:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->hdlc_bits1++;
+				hdlc->cbin++;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			} else {
+				hdlc->hdlc_bits1 = 0;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			}
+			if(hdlc->bit_shift==0){
+				hdlc->shift_reg = (hdlc->crc >> 8);
+				hdlc->state = HDLC_SEND_CRC2;
+				hdlc->bit_shift = 8;
+			}
+			break;
+		case HDLC_SEND_CRC2:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->hdlc_bits1++;
+				hdlc->cbin++;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			} else {
+				hdlc->hdlc_bits1 = 0;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			}
+			if(hdlc->bit_shift==0){
+				hdlc->shift_reg = 0x7e;
+				hdlc->state = HDLC_SEND_CLOSING_FLAG;
+				hdlc->bit_shift = 8;
+			}
+			break;
+		case HDLC_SEND_CLOSING_FLAG:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->cbin++;
+			}
+			hdlc->shift_reg >>= 1;
+			hdlc->bit_shift--;
+			if(hdlc->bit_shift==0){
+				hdlc->ffvalue = xfast_flag_value[hdlc->data_bits];
+				if(hdlc->dchannel){
+					hdlc->ffvalue = 0x7e;
+					hdlc->state = HDLC_SEND_IDLE1;
+					hdlc->bit_shift = 8-hdlc->data_bits;
+					if(hdlc->bit_shift==0)
+						hdlc->state = HDLC_SEND_FAST_IDLE;
+				} else {
+					if(!hdlc->do_adapt56){
+						hdlc->state = HDLC_SEND_FAST_FLAG;
+						hdlc->data_received = 0;
+					} else {
+						hdlc->state = HDLC_SENDFLAG_B0;
+						hdlc->data_received = 0;
+					}
+					// Finished with this frame, send flags
+					if (dsize > 1) dsize = 1; 
+				}
+			}
+			break;
+		case HDLC_SEND_IDLE1:
+			hdlc->do_closing = 0;
+			hdlc->cbin <<= 1;
+			hdlc->cbin++;
+			hdlc->data_bits++;
+			hdlc->bit_shift--;
+			if(hdlc->bit_shift==0){
+				hdlc->state = HDLC_SEND_FAST_IDLE;
+				hdlc->bit_shift = 0;
+			}
+			break;
+		case HDLC_SEND_FAST_IDLE:
+			hdlc->do_closing = 0;
+			hdlc->cbin = 0xff;
+			hdlc->data_bits = 8;
+			if(hdlc->bit_shift == 8){
+				hdlc->cbin = 0x7e;
+				hdlc->state = HDLC_SEND_FIRST_FLAG;
+			} else {
+				*dst++ = hdlc->cbin;
+				hdlc->bit_shift = hdlc->data_bits = 0;
+				len++;
+				dsize = 0;
+			}
+			break;
+		default:
+			break;
+		}
+		if(hdlc->do_adapt56){
+			if(hdlc->data_bits==7){
+				hdlc->cbin <<= 1;
+				hdlc->cbin++;
+				hdlc->data_bits++;
+			}
+		}
+		if(hdlc->data_bits==8){
+			*dst++ = hdlc->cbin;
+			hdlc->data_bits = 0;
+			len++;
+			dsize--;
+		}
+	}
+	*count -= slen;
+
+	return len;
+}
+
diff --git a/drivers/isdn/hisax/st5481_hdlc.h b/drivers/isdn/hisax/st5481_hdlc.h
new file mode 100644
index 000000000000..495432f0f6ba
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_hdlc.h
@@ -0,0 +1,62 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __ST5481_HDLC_H__
+#define __ST5481_HDLC_H__
+
+struct hdlc_vars {
+  	int bit_shift; 
+	int hdlc_bits1;
+	int data_bits;
+	int ffbit_shift; // encoding only
+	int state;
+	int dstpos;
+
+	int data_received:1; // set if transferring data
+	int dchannel:1; // set if D channel (send idle instead of flags)
+	int do_adapt56:1; // set if 56K adaptation
+        int do_closing:1; // set if in closing phase (need to send CRC + flag
+
+	unsigned short crc;
+
+	unsigned char cbin; 
+	unsigned char shift_reg;
+	unsigned char ffvalue;
+	
+};
+
+
+/*
+  The return value from hdlc_decode is
+  the frame length, 0 if no complete frame was decoded,
+  or a negative error number
+*/
+
+#define HDLC_FRAMING_ERROR     1
+#define HDLC_CRC_ERROR         2
+#define HDLC_LENGTH_ERROR      3
+
+void 
+hdlc_rcv_init(struct hdlc_vars *hdlc, int do_adapt56);
+
+int
+hdlc_decode(struct hdlc_vars *hdlc, const unsigned char *src, int slen,int *count, 
+	    unsigned char *dst, int dsize);
+
+void 
+hdlc_out_init(struct hdlc_vars *hdlc,int is_d_channel,int do_adapt56);
+
+int 
+hdlc_encode(struct hdlc_vars *hdlc,const unsigned char *src,unsigned short slen,int *count,
+	    unsigned char *dst,int dsize);
+
+#endif
diff --git a/drivers/isdn/hisax/st5481_init.c b/drivers/isdn/hisax/st5481_init.c
new file mode 100644
index 000000000000..7aa810d5d333
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_init.c
@@ -0,0 +1,224 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* 
+ * TODO:
+ *
+ * b layer1 delay?
+ * hotplug / unregister issues
+ * mod_inc/dec_use_count
+ * unify parts of d/b channel usb handling
+ * file header
+ * avoid copy to isoc buffer?
+ * improve usb delay?
+ * merge l1 state machines?
+ * clean up debug
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include "st5481.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: driver for ST5481 USB ISDN adapter");
+MODULE_AUTHOR("Frode Isaksen");
+MODULE_LICENSE("GPL");
+
+static int protocol = 2;       /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+
+static int number_of_leds = 2;       /* 2 LEDs on the adpater default */
+module_param(number_of_leds, int, 0);
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 0x1;
+module_param(debug, int, 0);
+int st5481_debug;
+#endif
+
+static LIST_HEAD(adapter_list);
+
+/* ======================================================================
+ * registration/deregistration with the USB layer
+ */
+
+/*
+ * This function will be called when the adapter is plugged
+ * into the USB bus.
+ */
+static int probe_st5481(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct st5481_adapter *adapter;
+	struct hisax_b_if *b_if[2];
+	int retval, i;
+
+	printk(KERN_INFO "st541: found adapter VendorId %04x, ProductId %04x, LEDs %d\n",
+	     le16_to_cpu(dev->descriptor.idVendor),
+	     le16_to_cpu(dev->descriptor.idProduct),
+	     number_of_leds);
+
+	adapter = kmalloc(sizeof(struct st5481_adapter), GFP_KERNEL);
+	if (!adapter)
+		return -ENOMEM;
+
+	memset(adapter, 0, sizeof(struct st5481_adapter));
+
+	adapter->number_of_leds = number_of_leds;
+	adapter->usb_dev = dev;
+
+	adapter->hisax_d_if.owner = THIS_MODULE;
+	adapter->hisax_d_if.ifc.priv = adapter;
+	adapter->hisax_d_if.ifc.l2l1 = st5481_d_l2l1;
+
+	for (i = 0; i < 2; i++) {
+		adapter->bcs[i].adapter = adapter;
+		adapter->bcs[i].channel = i;
+		adapter->bcs[i].b_if.ifc.priv = &adapter->bcs[i];
+		adapter->bcs[i].b_if.ifc.l2l1 = st5481_b_l2l1;
+	}
+	list_add(&adapter->list, &adapter_list);
+
+	retval = st5481_setup_usb(adapter);
+	if (retval < 0)
+		goto err;
+
+	retval = st5481_setup_d(adapter);
+	if (retval < 0)
+		goto err_usb;
+
+	retval = st5481_setup_b(&adapter->bcs[0]);
+	if (retval < 0)
+		goto err_d;
+
+	retval = st5481_setup_b(&adapter->bcs[1]);
+	if (retval < 0)
+		goto err_b;
+
+	for (i = 0; i < 2; i++)
+		b_if[i] = &adapter->bcs[i].b_if;
+
+	hisax_register(&adapter->hisax_d_if, b_if, "st5481_usb", protocol);
+	st5481_start(adapter);
+
+	usb_set_intfdata(intf, adapter);
+	return 0;
+
+ err_b:
+	st5481_release_b(&adapter->bcs[0]);
+ err_d:
+	st5481_release_d(adapter);
+ err_usb:
+	st5481_release_usb(adapter);
+ err:
+	return -EIO;
+}
+
+/*
+ * This function will be called when the adapter is removed
+ * from the USB bus.
+ */
+static void disconnect_st5481(struct usb_interface *intf)
+{
+	struct st5481_adapter *adapter = usb_get_intfdata(intf);
+
+	DBG(1,"");
+
+	usb_set_intfdata(intf, NULL);
+	if (!adapter)
+		return;
+	
+	list_del(&adapter->list);
+
+	st5481_stop(adapter);
+	st5481_release_b(&adapter->bcs[1]);
+	st5481_release_b(&adapter->bcs[0]);
+	st5481_release_d(adapter);
+	// we would actually better wait for completion of outstanding urbs
+	mdelay(2);
+	st5481_release_usb(adapter);
+
+	hisax_unregister(&adapter->hisax_d_if);
+
+	kfree(adapter);
+}
+
+/*
+ * The last 4 bits in the Product Id is set with 4 pins on the chip.
+ */
+static struct usb_device_id st5481_ids[] = {
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x0) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x1) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x2) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x3) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x4) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x5) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x6) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x7) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x8) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x9) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xA) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xB) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xC) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xD) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xE) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xF) },
+	{ }
+};
+MODULE_DEVICE_TABLE (usb, st5481_ids);
+
+static struct usb_driver st5481_usb_driver = {
+	.owner =	THIS_MODULE,
+	.name =		"st5481_usb",
+	.probe =	probe_st5481,
+	.disconnect =	disconnect_st5481,
+	.id_table =	st5481_ids,
+};
+
+static int __init st5481_usb_init(void)
+{
+	int retval;
+
+#ifdef CONFIG_HISAX_DEBUG
+	st5481_debug = debug;
+#endif
+
+	printk(KERN_INFO "hisax_st5481: ST5481 USB ISDN driver $Revision: 2.4.2.3 $\n");
+
+	retval = st5481_d_init();
+	if (retval < 0)
+		goto out;
+
+	retval = usb_register(&st5481_usb_driver);
+	if (retval < 0)
+		goto out_d_exit;
+
+	return 0;
+
+ out_d_exit:
+	st5481_d_exit();
+ out:
+	return retval;
+}
+
+static void __exit st5481_usb_exit(void)
+{
+	usb_deregister(&st5481_usb_driver);
+	st5481_d_exit();
+}
+
+module_init(st5481_usb_init);
+module_exit(st5481_usb_exit);
diff --git a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c
new file mode 100644
index 000000000000..2369180b1cb1
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_usb.c
@@ -0,0 +1,650 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include "st5481.h"
+
+/* ======================================================================
+ * control pipe
+ */
+
+/*
+ * Send the next endpoint 0 request stored in the FIFO.
+ * Called either by the completion or by usb_ctrl_msg.
+ */
+static void usb_next_ctrl_msg(struct urb *urb,
+			      struct st5481_adapter *adapter)
+{
+	struct st5481_ctrl *ctrl = &adapter->ctrl;
+	int r_index;
+
+	if (test_and_set_bit(0, &ctrl->busy)) {
+		return;
+	}
+
+	if ((r_index = fifo_remove(&ctrl->msg_fifo.f)) < 0) {
+		test_and_clear_bit(0,&ctrl->busy);
+		return;
+	} 
+	urb->setup_packet = 
+		(unsigned char *)&ctrl->msg_fifo.data[r_index];
+	
+	DBG(1,"request=0x%02x,value=0x%04x,index=%x",
+	    ((struct ctrl_msg *)urb->setup_packet)->dr.bRequest,
+	    ((struct ctrl_msg *)urb->setup_packet)->dr.wValue,
+	    ((struct ctrl_msg *)urb->setup_packet)->dr.wIndex);
+
+	// Prepare the URB
+	urb->dev = adapter->usb_dev;
+
+	SUBMIT_URB(urb, GFP_ATOMIC);
+}
+
+/*
+ * Asynchronous endpoint 0 request (async version of usb_control_msg).
+ * The request will be queued up in a FIFO if the endpoint is busy.
+ */
+void usb_ctrl_msg(struct st5481_adapter *adapter,
+		  u8 request, u8 requesttype, u16 value, u16 index,
+		  ctrl_complete_t complete, void *context)
+{
+	struct st5481_ctrl *ctrl = &adapter->ctrl;
+	int w_index;
+	struct ctrl_msg *ctrl_msg;
+	
+	if ((w_index = fifo_add(&ctrl->msg_fifo.f)) < 0) {
+		WARN("control msg FIFO full");
+		return;
+	}
+	ctrl_msg = &ctrl->msg_fifo.data[w_index]; 
+   
+	ctrl_msg->dr.bRequestType = requesttype;
+	ctrl_msg->dr.bRequest = request;
+	ctrl_msg->dr.wValue = cpu_to_le16p(&value);
+	ctrl_msg->dr.wIndex = cpu_to_le16p(&index);
+	ctrl_msg->dr.wLength = 0;
+	ctrl_msg->complete = complete;
+	ctrl_msg->context = context;
+
+	usb_next_ctrl_msg(ctrl->urb, adapter);
+}
+
+/*
+ * Asynchronous endpoint 0 device request.
+ */
+void st5481_usb_device_ctrl_msg(struct st5481_adapter *adapter,
+			 u8 request, u16 value,
+			 ctrl_complete_t complete, void *context)
+{
+	usb_ctrl_msg(adapter, request, 
+		     USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 
+		     value, 0, complete, context);
+}
+
+/*
+ * Asynchronous pipe reset (async version of usb_clear_halt).
+ */
+void st5481_usb_pipe_reset(struct st5481_adapter *adapter,
+		    u_char pipe,
+		    ctrl_complete_t complete, void *context)
+{
+	DBG(1,"pipe=%02x",pipe);
+
+	usb_ctrl_msg(adapter,
+		     USB_REQ_CLEAR_FEATURE, USB_DIR_OUT | USB_RECIP_ENDPOINT,
+		     0, pipe, complete, context);
+}
+
+
+/*
+  Physical level functions
+*/
+
+void st5481_ph_command(struct st5481_adapter *adapter, unsigned int command)
+{
+	DBG(8,"command=%s", ST5481_CMD_string(command));
+
+	st5481_usb_device_ctrl_msg(adapter, TXCI, command, NULL, NULL);
+}
+
+/*
+ * The request on endpoint 0 has completed.
+ * Call the user provided completion routine and try
+ * to send the next request.
+ */
+static void usb_ctrl_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct st5481_adapter *adapter = urb->context;
+	struct st5481_ctrl *ctrl = &adapter->ctrl;
+	struct ctrl_msg *ctrl_msg;
+	
+	if (unlikely(urb->status < 0)) {
+		if (urb->status != -ENOENT && urb->status != -ESHUTDOWN) {
+			WARN("urb status %d",urb->status);
+		} else {
+			DBG(1,"urb killed");
+			return; // Give up
+		}
+	}
+
+	ctrl_msg = (struct ctrl_msg *)urb->setup_packet;
+	
+	if (ctrl_msg->dr.bRequest == USB_REQ_CLEAR_FEATURE) {
+	        /* Special case handling for pipe reset */
+		le16_to_cpus(&ctrl_msg->dr.wIndex);
+
+		/* toggle is reset on clear */
+		usb_settoggle(adapter->usb_dev, 
+			      ctrl_msg->dr.wIndex & ~USB_DIR_IN, 
+			      (ctrl_msg->dr.wIndex & USB_DIR_IN) == 0,
+			      0);
+
+
+	}
+	
+	if (ctrl_msg->complete)
+		ctrl_msg->complete(ctrl_msg->context);
+
+	clear_bit(0, &ctrl->busy);
+	
+	// Try to send next control message
+	usb_next_ctrl_msg(urb, adapter);
+	return;
+}
+
+/* ======================================================================
+ * interrupt pipe
+ */
+
+/*
+ * The interrupt endpoint will be called when any
+ * of the 6 registers changes state (depending on masks).
+ * Decode the register values and schedule a private event.
+ * Called at interrupt.
+ */
+static void usb_int_complete(struct urb *urb, struct pt_regs *regs)
+{
+	u8 *data = urb->transfer_buffer;
+	u8 irqbyte;
+	struct st5481_adapter *adapter = urb->context;
+	int j;
+	int status;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		DBG(1, "urb shutting down with status: %d", urb->status);
+		return;
+	default:
+		WARN("nonzero urb status received: %d", urb->status);
+		goto exit;
+	}
+
+	
+	DBG_PACKET(1, data, INT_PKT_SIZE);
+		
+	if (urb->actual_length == 0) {
+		goto exit;
+	}
+
+	irqbyte = data[MPINT];
+	if (irqbyte & DEN_INT)
+		FsmEvent(&adapter->d_out.fsm, EV_DOUT_DEN, NULL);
+
+	if (irqbyte & DCOLL_INT)
+		FsmEvent(&adapter->d_out.fsm, EV_DOUT_COLL, NULL);
+
+	irqbyte = data[FFINT_D];
+	if (irqbyte & OUT_UNDERRUN)
+		FsmEvent(&adapter->d_out.fsm, EV_DOUT_UNDERRUN, NULL);
+
+	if (irqbyte & OUT_DOWN)
+;//		printk("OUT_DOWN\n");
+
+	irqbyte = data[MPINT];
+	if (irqbyte & RXCI_INT)
+		FsmEvent(&adapter->l1m, data[CCIST] & 0x0f, NULL);
+
+	for (j = 0; j < 2; j++)
+		adapter->bcs[j].b_out.flow_event |= data[FFINT_B1 + j];
+
+	urb->actual_length = 0;
+
+exit:
+	status = usb_submit_urb (urb, GFP_ATOMIC);
+	if (status)
+		WARN("usb_submit_urb failed with result %d", status);
+}
+
+/* ======================================================================
+ * initialization
+ */
+
+int st5481_setup_usb(struct st5481_adapter *adapter)
+{
+	struct usb_device *dev = adapter->usb_dev;
+	struct st5481_ctrl *ctrl = &adapter->ctrl;
+	struct st5481_intr *intr = &adapter->intr;
+	struct usb_interface *intf;
+	struct usb_host_interface *altsetting = NULL;
+	struct usb_host_endpoint *endpoint;
+	int status;
+	struct urb *urb;
+	u8 *buf;
+	
+	DBG(1,"");
+	
+	if ((status = usb_reset_configuration (dev)) < 0) {
+		WARN("reset_configuration failed,status=%d",status);
+		return status;
+	}
+
+	intf = usb_ifnum_to_if(dev, 0);
+	if (intf)
+		altsetting = usb_altnum_to_altsetting(intf, 3);
+	if (!altsetting)
+		return -ENXIO;
+
+	// Check if the config is sane
+	if ( altsetting->desc.bNumEndpoints != 7 ) {
+		WARN("expecting 7 got %d endpoints!", altsetting->desc.bNumEndpoints);
+		return -EINVAL;
+	}
+
+	// The descriptor is wrong for some early samples of the ST5481 chip
+	altsetting->endpoint[3].desc.wMaxPacketSize = __constant_cpu_to_le16(32);
+	altsetting->endpoint[4].desc.wMaxPacketSize = __constant_cpu_to_le16(32);
+
+	// Use alternative setting 3 on interface 0 to have 2B+D
+	if ((status = usb_set_interface (dev, 0, 3)) < 0) {
+		WARN("usb_set_interface failed,status=%d",status);
+		return status;
+	}
+
+	// Allocate URB for control endpoint
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		return -ENOMEM;
+	}
+	ctrl->urb = urb;
+	
+	// Fill the control URB
+	usb_fill_control_urb (urb, dev, 
+			  usb_sndctrlpipe(dev, 0),
+			  NULL, NULL, 0, usb_ctrl_complete, adapter);
+
+		
+	fifo_init(&ctrl->msg_fifo.f, ARRAY_SIZE(ctrl->msg_fifo.data));
+
+	// Allocate URBs and buffers for interrupt endpoint
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) { 
+		return -ENOMEM;
+	}
+	intr->urb = urb;
+	
+	buf = kmalloc(INT_PKT_SIZE, GFP_KERNEL);
+	if (!buf) {
+		return -ENOMEM;
+	}
+
+	endpoint = &altsetting->endpoint[EP_INT-1];
+				
+	// Fill the interrupt URB
+	usb_fill_int_urb(urb, dev,
+		     usb_rcvintpipe(dev, endpoint->desc.bEndpointAddress),
+		     buf, INT_PKT_SIZE,
+		     usb_int_complete, adapter,
+		     endpoint->desc.bInterval);
+		
+	return 0;
+}
+
+/*
+ * Release buffers and URBs for the interrupt and control
+ * endpoint.
+ */
+void st5481_release_usb(struct st5481_adapter *adapter)
+{
+	struct st5481_intr *intr = &adapter->intr;
+	struct st5481_ctrl *ctrl = &adapter->ctrl;
+
+	DBG(1,"");
+
+	// Stop and free Control and Interrupt URBs
+	usb_unlink_urb(ctrl->urb);
+	if (ctrl->urb->transfer_buffer)
+		kfree(ctrl->urb->transfer_buffer);
+	usb_free_urb(ctrl->urb);
+
+	usb_unlink_urb(intr->urb);
+	if (intr->urb->transfer_buffer)
+		kfree(intr->urb->transfer_buffer);
+	usb_free_urb(intr->urb);
+}
+
+/*
+ *  Initialize the adapter.
+ */
+void st5481_start(struct st5481_adapter *adapter)
+{
+	static const u8 init_cmd_table[]={
+		SET_DEFAULT,0,
+		STT,0,
+		SDA_MIN,0x0d,
+		SDA_MAX,0x29,
+		SDELAY_VALUE,0x14,
+		GPIO_DIR,0x01,		
+		GPIO_OUT,RED_LED,
+//		FFCTRL_OUT_D,4,
+//		FFCTRH_OUT_D,12,
+		FFCTRL_OUT_B1,6,
+		FFCTRH_OUT_B1,20,
+		FFCTRL_OUT_B2,6,
+		FFCTRH_OUT_B2,20,
+		MPMSK,RXCI_INT+DEN_INT+DCOLL_INT,
+		0
+	};	
+	struct st5481_intr *intr = &adapter->intr;
+	int i = 0;
+	u8 request,value;
+
+	DBG(8,"");
+
+	adapter->leds = RED_LED; 
+
+	// Start receiving on the interrupt endpoint
+	SUBMIT_URB(intr->urb, GFP_KERNEL); 
+
+	while ((request = init_cmd_table[i++])) {
+		value = init_cmd_table[i++];
+		st5481_usb_device_ctrl_msg(adapter, request, value, NULL, NULL);
+	}
+	st5481_ph_command(adapter, ST5481_CMD_PUP);
+}
+
+/*
+ * Reset the adapter to default values.
+ */
+void st5481_stop(struct st5481_adapter *adapter)
+{
+	DBG(8,"");
+
+	st5481_usb_device_ctrl_msg(adapter, SET_DEFAULT, 0, NULL, NULL);
+}
+
+/* ======================================================================
+ * isochronous USB  helpers
+ */
+
+static void
+fill_isoc_urb(struct urb *urb, struct usb_device *dev,
+	      unsigned int pipe, void *buf, int num_packets, 
+	      int packet_size, usb_complete_t complete,
+	      void *context) 
+{
+	int k;
+
+	spin_lock_init(&urb->lock);
+	urb->dev=dev;
+	urb->pipe=pipe;
+	urb->transfer_buffer=buf;
+	urb->number_of_packets = num_packets;
+	urb->transfer_buffer_length=num_packets*packet_size;
+	urb->actual_length = 0;
+	urb->complete=complete;
+	urb->context=context;
+	urb->transfer_flags=URB_ISO_ASAP;
+	for (k = 0; k < num_packets; k++) {
+		urb->iso_frame_desc[k].offset = packet_size * k;
+		urb->iso_frame_desc[k].length = packet_size;
+		urb->iso_frame_desc[k].actual_length = 0;
+	}
+}
+
+int
+st5481_setup_isocpipes(struct urb* urb[2], struct usb_device *dev, 
+			   unsigned int pipe, int num_packets,
+			   int packet_size, int buf_size,
+			   usb_complete_t complete, void *context)
+{
+	int j, retval;
+	unsigned char *buf;
+
+	for (j = 0; j < 2; j++) {
+		retval = -ENOMEM;
+		urb[j] = usb_alloc_urb(num_packets, GFP_KERNEL);
+		if (!urb[j])
+			goto err;
+
+		// Allocate memory for 2000bytes/sec (16Kb/s)
+		buf = kmalloc(buf_size, GFP_KERNEL);
+		if (!buf)
+			goto err;
+			
+		// Fill the isochronous URB
+		fill_isoc_urb(urb[j], dev, pipe, buf, 
+			      num_packets, packet_size, complete,
+			      context);
+	}
+	return 0;
+
+ err:
+	for (j = 0; j < 2; j++) {
+		if (urb[j]) {
+			if (urb[j]->transfer_buffer)
+				kfree(urb[j]->transfer_buffer);
+			usb_free_urb(urb[j]);
+		}
+	}
+	return retval;
+}
+
+void st5481_release_isocpipes(struct urb* urb[2])
+{
+	int j;
+
+	for (j = 0; j < 2; j++) {
+		usb_unlink_urb(urb[j]);
+		if (urb[j]->transfer_buffer)
+			kfree(urb[j]->transfer_buffer);			
+		usb_free_urb(urb[j]);
+	}
+}
+
+/*
+ * Decode frames received on the B/D channel.
+ * Note that this function will be called continously
+ * with 64Kbit/s / 16Kbit/s of data and hence it will be 
+ * called 50 times per second with 20 ISOC descriptors. 
+ * Called at interrupt.
+ */
+static void usb_in_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct st5481_in *in = urb->context;
+	unsigned char *ptr;
+	struct sk_buff *skb;
+	int len, count, status;
+
+	if (unlikely(urb->status < 0)) {
+		if (urb->status != -ENOENT && urb->status != -ESHUTDOWN) {
+			WARN("urb status %d",urb->status);
+		} else {
+			DBG(1,"urb killed");
+			return; // Give up
+		}
+	}
+
+	DBG_ISO_PACKET(0x80,urb);
+
+	len = st5481_isoc_flatten(urb);
+	ptr = urb->transfer_buffer;
+	while (len > 0) {
+		if (in->mode == L1_MODE_TRANS) {
+			memcpy(in->rcvbuf, ptr, len);
+			status = len;
+			len = 0;
+		} else {
+			status = isdnhdlc_decode(&in->hdlc_state, ptr, len, &count,
+				in->rcvbuf, in->bufsize);
+			ptr += count;
+			len -= count;
+		}
+		
+		if (status > 0) {
+			// Good frame received
+			DBG(4,"count=%d",status);
+			DBG_PACKET(0x400, in->rcvbuf, status);
+			if (!(skb = dev_alloc_skb(status))) {
+				WARN("receive out of memory\n");
+				break;
+			}
+			memcpy(skb_put(skb, status), in->rcvbuf, status);
+			in->hisax_if->l1l2(in->hisax_if, PH_DATA | INDICATION, skb);
+		} else if (status == -HDLC_CRC_ERROR) {
+			INFO("CRC error");
+		} else if (status == -HDLC_FRAMING_ERROR) {
+			INFO("framing error");
+		} else if (status == -HDLC_LENGTH_ERROR) {
+			INFO("length error");
+		}
+	}
+
+	// Prepare URB for next transfer
+	urb->dev = in->adapter->usb_dev;
+	urb->actual_length = 0;
+
+	SUBMIT_URB(urb, GFP_ATOMIC);
+}
+
+int st5481_setup_in(struct st5481_in *in)
+{
+	struct usb_device *dev = in->adapter->usb_dev;
+	int retval;
+
+	DBG(4,"");
+
+	in->rcvbuf = kmalloc(in->bufsize, GFP_KERNEL);
+	retval = -ENOMEM;
+	if (!in->rcvbuf)
+		goto err;
+
+	retval = st5481_setup_isocpipes(in->urb, dev, 
+					usb_rcvisocpipe(dev, in->ep),
+					in->num_packets,  in->packet_size,
+					in->num_packets * in->packet_size,
+					usb_in_complete, in);
+	if (retval)
+		goto err_free;
+	return 0;
+
+ err_free:
+	kfree(in->rcvbuf);
+ err:
+	return retval;
+}
+
+void st5481_release_in(struct st5481_in *in)
+{
+	DBG(2,"");
+
+	st5481_release_isocpipes(in->urb);
+}
+
+/*
+ * Make the transfer_buffer contiguous by
+ * copying from the iso descriptors if necessary. 
+ */
+int st5481_isoc_flatten(struct urb *urb)
+{
+	struct usb_iso_packet_descriptor *pipd,*pend;
+	unsigned char *src,*dst;
+	unsigned int len;
+	
+	if (urb->status < 0) {
+		return urb->status;
+	}
+	for (pipd = &urb->iso_frame_desc[0],
+		     pend = &urb->iso_frame_desc[urb->number_of_packets],
+		     dst = urb->transfer_buffer; 
+	     pipd < pend; 
+	     pipd++) {
+		
+		if (pipd->status < 0) {
+			return (pipd->status);
+		}
+	
+		len = pipd->actual_length;
+		pipd->actual_length = 0;
+		src = urb->transfer_buffer+pipd->offset;
+
+		if (src != dst) {
+			// Need to copy since isoc buffers not full
+			while (len--) {
+				*dst++ = *src++;
+			}			
+		} else {
+			// No need to copy, just update destination buffer
+			dst += len;
+		}
+	}
+	// Return size of flattened buffer
+	return (dst - (unsigned char *)urb->transfer_buffer);
+}
+
+static void st5481_start_rcv(void *context)
+{
+	struct st5481_in *in = context;
+	struct st5481_adapter *adapter = in->adapter;
+
+	DBG(4,"");
+
+	in->urb[0]->dev = adapter->usb_dev;
+	SUBMIT_URB(in->urb[0], GFP_KERNEL);
+
+	in->urb[1]->dev = adapter->usb_dev;
+	SUBMIT_URB(in->urb[1], GFP_KERNEL);
+}
+
+void st5481_in_mode(struct st5481_in *in, int mode)
+{
+	if (in->mode == mode)
+		return;
+
+	in->mode = mode;
+
+	usb_unlink_urb(in->urb[0]);
+	usb_unlink_urb(in->urb[1]);
+
+	if (in->mode != L1_MODE_NULL) {
+		if (in->mode != L1_MODE_TRANS)
+			isdnhdlc_rcv_init(&in->hdlc_state,
+				in->mode == L1_MODE_HDLC_56K);
+		
+		st5481_usb_pipe_reset(in->adapter, in->ep, NULL, NULL);
+		st5481_usb_device_ctrl_msg(in->adapter, in->counter,
+					   in->packet_size,
+					   NULL, NULL);
+		st5481_start_rcv(in);
+	} else {
+		st5481_usb_device_ctrl_msg(in->adapter, in->counter,
+					   0, NULL, NULL);
+	}
+}
+
diff --git a/drivers/isdn/hisax/tei.c b/drivers/isdn/hisax/tei.c
new file mode 100644
index 000000000000..082726db3985
--- /dev/null
+++ b/drivers/isdn/hisax/tei.c
@@ -0,0 +1,466 @@
+/* $Id: tei.c,v 2.20.2.3 2004/01/13 14:31:26 keil Exp $
+ *
+ * Author       Karsten Keil
+ *              based on the teles driver from Jan den Ouden
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include "hisax.h"
+#include "isdnl2.h"
+#include <linux/init.h>
+#include <linux/random.h>
+
+const char *tei_revision = "$Revision: 2.20.2.3 $";
+
+#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;
+
+void tei_handler(struct PStack *st, u_char pr, struct sk_buff *skb);
+
+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_DENIED,
+	EV_CHKREQ,
+	EV_REMOVE,
+	EV_VERIFY,
+	EV_T202,
+};
+
+#define TEI_EVENT_COUNT (EV_T202+1)
+
+static char *strTeiEvent[] =
+{
+	"EV_IDREQ",
+	"EV_ASSIGN",
+	"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 struct PStack *
+findtei(struct PStack *st, int tei)
+{
+	struct PStack *ptr = *(st->l1.stlistp);
+
+	if (tei == 127)
+		return (NULL);
+
+	while (ptr)
+		if (ptr->l2.tei == tei)
+			return (ptr);
+		else
+			ptr = ptr->next;
+	return (NULL);
+}
+
+static void
+put_tei_msg(struct PStack *st, u_char m_id, unsigned int ri, u_char tei)
+{
+	struct sk_buff *skb;
+	u_char *bp;
+
+	if (!(skb = alloc_skb(8, GFP_ATOMIC))) {
+		printk(KERN_WARNING "HiSax: No skb for TEI manager\n");
+		return;
+	}
+	bp = skb_put(skb, 3);
+	bp[0] = (TEI_SAPI << 2);
+	bp[1] = (GROUP_TEI << 1) | 0x1;
+	bp[2] = UI;
+	bp = skb_put(skb, 5);
+	bp[0] = TEI_ENTITY_ID;
+	bp[1] = ri >> 8;
+	bp[2] = ri & 0xff;
+	bp[3] = m_id;
+	bp[4] = (tei << 1) | 1;
+	st->l2.l2l1(st, PH_DATA | REQUEST, skb);
+}
+
+static void
+tei_id_request(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (st->l2.tei != -1) {
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"assign request for allready asigned tei %d",
+			st->l2.tei);
+		return;
+	}
+	st->ma.ri = random_ri();
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"assign request ri %d", st->ma.ri);
+	put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
+	FsmChangeState(&st->ma.tei_m, ST_TEI_IDREQ);
+	FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 1);
+	st->ma.N202 = 3;
+}
+
+static void
+tei_id_assign(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *ost, *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	struct IsdnCardState *cs;
+	int ri, tei;
+
+	ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity assign ri %d tei %d", ri, tei);
+	if ((ost = findtei(st, tei))) {	/* same tei is in use */
+		if (ri != ost->ma.ri) {
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"possible duplicate assignment tei %d", tei);
+			ost->l2.l2tei(ost, MDL_ERROR | RESPONSE, NULL);
+		}
+	} else if (ri == st->ma.ri) {
+		FsmDelTimer(&st->ma.t202, 1);
+		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+		st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
+	}
+}
+
+static void
+tei_id_test_dup(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *ost, *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int tei, ri;
+
+	ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"foreign identity assign ri %d tei %d", ri, tei);
+	if ((ost = findtei(st, tei))) {	/* same tei is in use */
+		if (ri != ost->ma.ri) {	/* and it wasn't our request */
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"possible duplicate assignment tei %d", tei);
+			FsmEvent(&ost->ma.tei_m, EV_VERIFY, NULL);
+		}
+	} 
+}
+
+static void
+tei_id_denied(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int ri, tei;
+
+	ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity denied ri %d tei %d", ri, tei);
+}
+
+static void
+tei_id_chk_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int tei;
+
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity check req tei %d", tei);
+	if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
+		FsmDelTimer(&st->ma.t202, 4);
+		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+		put_tei_msg(st, ID_CHK_RES, random_ri(), st->l2.tei);
+	}
+}
+
+static void
+tei_id_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	struct IsdnCardState *cs;
+	int tei;
+
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity remove tei %d", tei);
+	if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
+		FsmDelTimer(&st->ma.t202, 5);
+		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+		st->l3.l3l2(st, MDL_REMOVE | REQUEST, NULL);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
+	}
+}
+
+static void
+tei_id_verify(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"id verify request for tei %d", st->l2.tei);
+	put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
+	FsmChangeState(&st->ma.tei_m, ST_TEI_IDVERIFY);
+	FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 2);
+	st->ma.N202 = 2;
+}
+
+static void
+tei_id_req_tout(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs;
+
+	if (--st->ma.N202) {
+		st->ma.ri = random_ri();
+		if (st->ma.debug)
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"assign req(%d) ri %d", 4 - st->ma.N202,
+				st->ma.ri);
+		put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
+		FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 3);
+	} else {
+		st->ma.tei_m.printdebug(&st->ma.tei_m, "assign req failed");
+		st->l3.l3l2(st, MDL_ERROR | RESPONSE, NULL);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
+		FsmChangeState(fi, ST_TEI_NOP);
+	}
+}
+
+static void
+tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs;
+
+	if (--st->ma.N202) {
+		if (st->ma.debug)
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"id verify req(%d) for tei %d",
+				3 - st->ma.N202, st->l2.tei);
+		put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
+		FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 4);
+	} else {
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"verify req for tei %d failed", st->l2.tei);
+		st->l3.l3l2(st, MDL_REMOVE | REQUEST, NULL);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
+		FsmChangeState(fi, ST_TEI_NOP);
+	}
+}
+
+static void
+tei_l1l2(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int mt;
+
+	if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	if (pr == (PH_DATA | INDICATION)) {
+		if (skb->len < 3) {
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"short mgr frame %ld/3", skb->len);
+		} else if ((skb->data[0] != ((TEI_SAPI << 2) | 2)) ||
+			   (skb->data[1] != ((GROUP_TEI << 1) | 1))) {
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"wrong mgr sapi/tei %x/%x",
+				skb->data[0], skb->data[1]);
+		} else if ((skb->data[2] & 0xef) != UI) {
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"mgr frame is not ui %x", skb->data[2]);
+		} else {
+			skb_pull(skb, 3);
+			if (skb->len < 5) {
+				st->ma.tei_m.printdebug(&st->ma.tei_m,
+					"short mgr frame %ld/5", skb->len);
+			} else if (skb->data[0] != TEI_ENTITY_ID) {
+				/* wrong management entity identifier, ignore */
+				st->ma.tei_m.printdebug(&st->ma.tei_m,
+					"tei handler wrong entity id %x",
+					skb->data[0]);
+			} else {
+				mt = skb->data[3];
+				if (mt == ID_ASSIGNED)
+					FsmEvent(&st->ma.tei_m, EV_ASSIGN, skb);
+				else if (mt == ID_DENIED)
+					FsmEvent(&st->ma.tei_m, EV_DENIED, skb);
+				else if (mt == ID_CHK_REQ)
+					FsmEvent(&st->ma.tei_m, EV_CHKREQ, skb);
+				else if (mt == ID_REMOVE)
+					FsmEvent(&st->ma.tei_m, EV_REMOVE, skb);
+				else {
+					st->ma.tei_m.printdebug(&st->ma.tei_m,
+						"tei handler wrong mt %x\n", mt);
+				}
+			}
+		}
+	} else {
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"tei handler wrong pr %x\n", pr);
+	}
+	dev_kfree_skb(skb);
+}
+
+static void
+tei_l2tei(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs;
+
+	if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+		if (pr == (MDL_ASSIGN | INDICATION)) {
+			if (st->ma.debug)
+				st->ma.tei_m.printdebug(&st->ma.tei_m,
+					"fixed assign tei %d", st->l2.tei);
+			st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei);
+			cs = (struct IsdnCardState *) st->l1.hardware;
+			cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
+		}
+		return;
+	}
+	switch (pr) {
+		case (MDL_ASSIGN | INDICATION):
+			FsmEvent(&st->ma.tei_m, EV_IDREQ, arg);
+			break;
+		case (MDL_ERROR | REQUEST):
+			FsmEvent(&st->ma.tei_m, EV_VERIFY, arg);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+tei_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	struct PStack *st = fi->userdata;
+
+	va_start(args, fmt);
+	VHiSax_putstatus(st->l1.hardware, "tei ", fmt, args);
+	va_end(args);
+}
+
+void
+setstack_tei(struct PStack *st)
+{
+	st->l2.l2tei = tei_l2tei;
+	st->ma.T202 = 2000;	/* T202  2000 milliseconds */
+	st->l1.l1tei = tei_l1l2;
+	st->ma.debug = 1;
+	st->ma.tei_m.fsm = &teifsm;
+	st->ma.tei_m.state = ST_TEI_NOP;
+	st->ma.tei_m.debug = 1;
+	st->ma.tei_m.userdata = st;
+	st->ma.tei_m.userint = 0;
+	st->ma.tei_m.printdebug = tei_debug;
+	FsmInitTimer(&st->ma.tei_m, &st->ma.t202);
+}
+
+void
+init_tei(struct IsdnCardState *cs, int protocol)
+{
+}
+
+void
+release_tei(struct IsdnCardState *cs)
+{
+	struct PStack *st = cs->stlist;
+
+	while (st) {
+		FsmDelTimer(&st->ma.t202, 1);
+		st = st->next;
+	}
+}
+
+static struct FsmNode TeiFnList[] __initdata =
+{
+	{ST_TEI_NOP, EV_IDREQ, tei_id_request},
+	{ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup},
+	{ST_TEI_NOP, EV_VERIFY, tei_id_verify},
+	{ST_TEI_NOP, EV_REMOVE, tei_id_remove},
+	{ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req},
+	{ST_TEI_IDREQ, EV_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))
+
+int __init
+TeiNew(void)
+{
+	teifsm.state_count = TEI_STATE_COUNT;
+	teifsm.event_count = TEI_EVENT_COUNT;
+	teifsm.strEvent = strTeiEvent;
+	teifsm.strState = strTeiState;
+	return FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT);
+}
+
+void
+TeiFree(void)
+{
+	FsmFree(&teifsm);
+}
diff --git a/drivers/isdn/hisax/teleint.c b/drivers/isdn/hisax/teleint.c
new file mode 100644
index 000000000000..ef8984c5f1f7
--- /dev/null
+++ b/drivers/isdn/hisax/teleint.c
@@ -0,0 +1,339 @@
+/* $Id: teleint.c,v 1.16.2.5 2004/01/19 15:31:50 keil Exp $
+ *
+ * low level stuff for TeleInt isdn cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hfc_2bs0.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *TeleInt_revision = "$Revision: 1.16.2.5 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	int max_delay = 2000;
+
+	byteout(ale, off);
+	ret = HFC_BUSY & bytein(ale);
+	while (ret && --max_delay)
+		ret = HFC_BUSY & bytein(ale);
+	if (!max_delay) {
+		printk(KERN_WARNING "TeleInt Busy not inactive\n");
+		return (0);
+	}
+	ret = bytein(adr);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	register u_char ret;
+	register int max_delay = 20000;
+	register int i;
+	
+	byteout(ale, off);
+	for (i = 0; i<size; i++) {
+		ret = HFC_BUSY & bytein(ale);
+		while (ret && --max_delay)
+			ret = HFC_BUSY & bytein(ale);
+		if (!max_delay) {
+			printk(KERN_WARNING "TeleInt Busy not inactive\n");
+			return;
+		}
+		data[i] = bytein(adr);
+	}
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	register u_char ret;
+	int max_delay = 2000;
+
+	byteout(ale, off);
+	ret = HFC_BUSY & bytein(ale);
+	while (ret && --max_delay)
+		ret = HFC_BUSY & bytein(ale);
+	if (!max_delay) {
+		printk(KERN_WARNING "TeleInt Busy not inactive\n");
+		return;
+	}
+	byteout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	register u_char ret;
+	register int max_delay = 20000;
+	register int i;
+	
+	byteout(ale, off);
+	for (i = 0; i<size; i++) {
+		ret = HFC_BUSY & bytein(ale);
+		while (ret && --max_delay)
+			ret = HFC_BUSY & bytein(ale);
+		if (!max_delay) {
+			printk(KERN_WARNING "TeleInt Busy not inactive\n");
+			return;
+		}
+		byteout(adr, data[i]);
+	}
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	cs->hw.hfc.cip = offset;
+	return (readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	cs->hw.hfc.cip = offset;
+	writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	cs->hw.hfc.cip = 0;
+	readfifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	cs->hw.hfc.cip = 0;
+	writefifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size);
+}
+
+static u_char
+ReadHFC(struct IsdnCardState *cs, int data, u_char reg)
+{
+	register u_char ret;
+
+	if (data) {
+		cs->hw.hfc.cip = reg;
+		byteout(cs->hw.hfc.addr | 1, reg);
+		ret = bytein(cs->hw.hfc.addr);
+		if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+			debugl1(cs, "hfc RD %02x %02x", reg, ret);
+	} else
+		ret = bytein(cs->hw.hfc.addr | 1);
+	return (ret);
+}
+
+static void
+WriteHFC(struct IsdnCardState *cs, int data, u_char reg, u_char value)
+{
+	byteout(cs->hw.hfc.addr | 1, reg);
+	cs->hw.hfc.cip = reg;
+	if (data)
+		byteout(cs->hw.hfc.addr, value);
+	if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+		debugl1(cs, "hfc W%c %02x %02x", data ? 'D' : 'C', reg, value);
+}
+
+static irqreturn_t
+TeleInt_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0xFF);
+	writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+TeleInt_Timer(struct IsdnCardState *cs)
+{
+	int stat = 0;
+	u_long flags;
+	
+	spin_lock_irqsave(&cs->lock, flags);
+	if (cs->bcs[0].mode) {
+		stat |= 1;
+		main_irq_hfc(&cs->bcs[0]);
+	}
+	if (cs->bcs[1].mode) {
+		stat |= 2;
+		main_irq_hfc(&cs->bcs[1]);
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	stat = HZ/100;
+	if (!stat)
+		stat = 1;
+	cs->hw.hfc.timer.expires = jiffies + stat;
+	add_timer(&cs->hw.hfc.timer);
+}
+
+void
+release_io_TeleInt(struct IsdnCardState *cs)
+{
+	del_timer(&cs->hw.hfc.timer);
+	releasehfc(cs);
+	if (cs->hw.hfc.addr)
+		release_region(cs->hw.hfc.addr, 2);
+}
+
+static void
+reset_TeleInt(struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "TeleInt: resetting card\n");
+	cs->hw.hfc.cirm |= HFC_RESET;
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);	/* Reset On */
+	mdelay(10);
+	cs->hw.hfc.cirm &= ~HFC_RESET;
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);	/* Reset Off */
+	mdelay(10);
+}
+
+static int
+TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+	int delay;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_TeleInt(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_TeleInt(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_TeleInt(cs);
+			inithfc(cs);
+			clear_pending_isac_ints(cs);
+			initisac(cs);
+			/* Reenable all IRQ */
+			cs->writeisac(cs, ISAC_MASK, 0);
+			cs->writeisac(cs, ISAC_CMDR, 0x41);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			delay = HZ/100;
+			if (!delay)
+				delay = 1;
+			cs->hw.hfc.timer.expires = jiffies + delay;
+			add_timer(&cs->hw.hfc.timer);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+int __init
+setup_TeleInt(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, TeleInt_revision);
+	printk(KERN_INFO "HiSax: TeleInt driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_TELEINT)
+		return (0);
+
+	cs->hw.hfc.addr = card->para[1] & 0x3fe;
+	cs->irq = card->para[0];
+	cs->hw.hfc.cirm = HFC_CIRM;
+	cs->hw.hfc.isac_spcr = 0x00;
+	cs->hw.hfc.cip = 0;
+	cs->hw.hfc.ctmt = HFC_CTMT | HFC_CLTIMER;
+	cs->bcs[0].hw.hfc.send = NULL;
+	cs->bcs[1].hw.hfc.send = NULL;
+	cs->hw.hfc.fifosize = 7 * 1024 + 512;
+	cs->hw.hfc.timer.function = (void *) TeleInt_Timer;
+	cs->hw.hfc.timer.data = (long) cs;
+	init_timer(&cs->hw.hfc.timer);
+	if (!request_region(cs->hw.hfc.addr, 2, "TeleInt isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.hfc.addr,
+		       cs->hw.hfc.addr + 2);
+		return (0);
+	}
+	/* HW IO = IO */
+	byteout(cs->hw.hfc.addr, cs->hw.hfc.addr & 0xff);
+	byteout(cs->hw.hfc.addr | 1, ((cs->hw.hfc.addr & 0x300) >> 8) | 0x54);
+	switch (cs->irq) {
+		case 3:
+			cs->hw.hfc.cirm |= HFC_INTA;
+			break;
+		case 4:
+			cs->hw.hfc.cirm |= HFC_INTB;
+			break;
+		case 5:
+			cs->hw.hfc.cirm |= HFC_INTC;
+			break;
+		case 7:
+			cs->hw.hfc.cirm |= HFC_INTD;
+			break;
+		case 10:
+			cs->hw.hfc.cirm |= HFC_INTE;
+			break;
+		case 11:
+			cs->hw.hfc.cirm |= HFC_INTF;
+			break;
+		default:
+			printk(KERN_WARNING "TeleInt: wrong IRQ\n");
+			release_io_TeleInt(cs);
+			return (0);
+	}
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.ctmt);
+
+	printk(KERN_INFO "TeleInt: defined at 0x%x IRQ %d\n",
+		cs->hw.hfc.addr, cs->irq);
+
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHFC;
+	cs->BC_Write_Reg = &WriteHFC;
+	cs->cardmsg = &TeleInt_card_msg;
+	cs->irq_func = &TeleInt_interrupt;
+	ISACVersion(cs, "TeleInt:");
+	return (1);
+}
diff --git a/drivers/isdn/hisax/teles0.c b/drivers/isdn/hisax/teles0.c
new file mode 100644
index 000000000000..5ec5ec3e1eab
--- /dev/null
+++ b/drivers/isdn/hisax/teles0.c
@@ -0,0 +1,367 @@
+/* $Id: teles0.c,v 2.15.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for Teles Memory IO isdn cards
+ *
+ * Author       Karsten Keil
+ *              based on the teles driver from Jan den Ouden
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isdnl1.h"
+#include "isac.h"
+#include "hscx.h"
+
+extern const char *CardType[];
+
+const char *teles0_revision = "$Revision: 2.15.2.4 $";
+
+#define TELES_IOMEM_SIZE	0x400
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readisac(void __iomem *adr, u_char off)
+{
+	return readb(adr + ((off & 1) ? 0x2ff : 0x100) + off);
+}
+
+static inline void
+writeisac(void __iomem *adr, u_char off, u_char data)
+{
+	writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off); mb();
+}
+
+
+static inline u_char
+readhscx(void __iomem *adr, int hscx, u_char off)
+{
+	return readb(adr + (hscx ? 0x1c0 : 0x180) +
+		     ((off & 1) ? 0x1ff : 0) + off);
+}
+
+static inline void
+writehscx(void __iomem *adr, int hscx, u_char off, u_char data)
+{
+	writeb(data, adr + (hscx ? 0x1c0 : 0x180) +
+	       ((off & 1) ? 0x1ff : 0) + off); mb();
+}
+
+static inline void
+read_fifo_isac(void __iomem *adr, u_char * data, int size)
+{
+	register int i;
+	register u_char __iomem *ad = adr + 0x100;
+	for (i = 0; i < size; i++)
+		data[i] = readb(ad);
+}
+
+static inline void
+write_fifo_isac(void __iomem *adr, u_char * data, int size)
+{
+	register int i;
+	register u_char __iomem *ad = adr + 0x100;
+	for (i = 0; i < size; i++) {
+		writeb(data[i], ad); mb();
+	}
+}
+
+static inline void
+read_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size)
+{
+	register int i;
+	register u_char __iomem *ad = adr + (hscx ? 0x1c0 : 0x180);
+	for (i = 0; i < size; i++)
+		data[i] = readb(ad);
+}
+
+static inline void
+write_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size)
+{
+	int i;
+	register u_char __iomem *ad = adr + (hscx ? 0x1c0 : 0x180);
+	for (i = 0; i < size; i++) {
+		writeb(data[i], ad); mb();
+	}
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readisac(cs->hw.teles0.membase, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writeisac(cs->hw.teles0.membase, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readhscx(cs->hw.teles0.membase, hscx, offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writehscx(cs->hw.teles0.membase, hscx, offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+teles0_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+	int count = 0;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	count++;
+	val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+	if (val && count < 5) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+	if (val && count < 5) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF);
+	writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF);
+	writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF);
+	writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0);
+	writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0);
+	writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_teles0(struct IsdnCardState *cs)
+{
+	if (cs->hw.teles0.cfg_reg)
+		release_region(cs->hw.teles0.cfg_reg, 8);
+	iounmap(cs->hw.teles0.membase);
+	release_mem_region(cs->hw.teles0.phymem, TELES_IOMEM_SIZE);
+}
+
+static int
+reset_teles0(struct IsdnCardState *cs)
+{
+	u_char cfval;
+
+	if (cs->hw.teles0.cfg_reg) {
+		switch (cs->irq) {
+			case 2:
+			case 9:
+				cfval = 0x00;
+				break;
+			case 3:
+				cfval = 0x02;
+				break;
+			case 4:
+				cfval = 0x04;
+				break;
+			case 5:
+				cfval = 0x06;
+				break;
+			case 10:
+				cfval = 0x08;
+				break;
+			case 11:
+				cfval = 0x0A;
+				break;
+			case 12:
+				cfval = 0x0C;
+				break;
+			case 15:
+				cfval = 0x0E;
+				break;
+			default:
+				return(1);
+		}
+		cfval |= ((cs->hw.teles0.phymem >> 9) & 0xF0);
+		byteout(cs->hw.teles0.cfg_reg + 4, cfval);
+		HZDELAY(HZ / 10 + 1);
+		byteout(cs->hw.teles0.cfg_reg + 4, cfval | 1);
+		HZDELAY(HZ / 10 + 1);
+	}
+	writeb(0, cs->hw.teles0.membase + 0x80); mb();
+	HZDELAY(HZ / 5 + 1);
+	writeb(1, cs->hw.teles0.membase + 0x80); mb();
+	HZDELAY(HZ / 5 + 1);
+	return(0);
+}
+
+static int
+Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_teles0(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_teles0(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+int __init
+setup_teles0(struct IsdnCard *card)
+{
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, teles0_revision);
+	printk(KERN_INFO "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp));
+	if ((cs->typ != ISDN_CTYPE_16_0) && (cs->typ != ISDN_CTYPE_8_0))
+		return (0);
+
+	if (cs->typ == ISDN_CTYPE_16_0)
+		cs->hw.teles0.cfg_reg = card->para[2];
+	else			/* 8.0 */
+		cs->hw.teles0.cfg_reg = 0;
+
+	if (card->para[1] < 0x10000) {
+		card->para[1] <<= 4;
+		printk(KERN_INFO
+		   "Teles0: membase configured DOSish, assuming 0x%lx\n",
+		       (unsigned long) card->para[1]);
+	}
+	cs->irq = card->para[0];
+	if (cs->hw.teles0.cfg_reg) {
+		if (!request_region(cs->hw.teles0.cfg_reg, 8, "teles cfg")) {
+			printk(KERN_WARNING
+			  "HiSax: %s config port %x-%x already in use\n",
+			       CardType[card->typ],
+			       cs->hw.teles0.cfg_reg,
+			       cs->hw.teles0.cfg_reg + 8);
+			return (0);
+		}
+	}
+	if (cs->hw.teles0.cfg_reg) {
+		if ((val = bytein(cs->hw.teles0.cfg_reg + 0)) != 0x51) {
+			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+			       cs->hw.teles0.cfg_reg + 0, val);
+			release_region(cs->hw.teles0.cfg_reg, 8);
+			return (0);
+		}
+		if ((val = bytein(cs->hw.teles0.cfg_reg + 1)) != 0x93) {
+			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+			       cs->hw.teles0.cfg_reg + 1, val);
+			release_region(cs->hw.teles0.cfg_reg, 8);
+			return (0);
+		}
+		val = bytein(cs->hw.teles0.cfg_reg + 2);	/* 0x1e=without AB
+								   * 0x1f=with AB
+								   * 0x1c 16.3 ???
+								 */
+		if (val != 0x1e && val != 0x1f) {
+			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+			       cs->hw.teles0.cfg_reg + 2, val);
+			release_region(cs->hw.teles0.cfg_reg, 8);
+			return (0);
+		}
+	}
+	/* 16.0 and 8.0 designed for IOM1 */
+	test_and_set_bit(HW_IOM1, &cs->HW_Flags);
+	cs->hw.teles0.phymem = card->para[1];
+	if (!request_mem_region(cs->hw.teles0.phymem, TELES_IOMEM_SIZE, "teles iomem")) {
+		printk(KERN_WARNING
+			"HiSax: %s memory region %lx-%lx already in use\n",
+			CardType[card->typ],
+			cs->hw.teles0.phymem,
+			cs->hw.teles0.phymem + TELES_IOMEM_SIZE);
+		if (cs->hw.teles0.cfg_reg)
+			release_region(cs->hw.teles0.cfg_reg, 8);
+		return (0);
+	}
+	cs->hw.teles0.membase = ioremap(cs->hw.teles0.phymem, TELES_IOMEM_SIZE);
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d mem:%p cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles0.membase, cs->hw.teles0.cfg_reg);
+	if (reset_teles0(cs)) {
+		printk(KERN_WARNING "Teles0: wrong IRQ\n");
+		release_io_teles0(cs);
+		return (0);
+	}
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Teles_card_msg;
+	cs->irq_func = &teles0_interrupt;
+	ISACVersion(cs, "Teles0:");
+	if (HscxVersion(cs, "Teles0:")) {
+		printk(KERN_WARNING
+		 "Teles0: wrong HSCX versions check IO/MEM addresses\n");
+		release_io_teles0(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/teles3.c b/drivers/isdn/hisax/teles3.c
new file mode 100644
index 000000000000..c5b1f65f7275
--- /dev/null
+++ b/drivers/isdn/hisax/teles3.c
@@ -0,0 +1,499 @@
+/* $Id: teles3.c,v 2.19.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for Teles 16.3 & PNP isdn cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ */
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *teles3_revision = "$Revision: 2.19.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int adr, u_char off)
+{
+	return (bytein(adr + off));
+}
+
+static inline void
+writereg(unsigned int adr, u_char off, u_char data)
+{
+	byteout(adr + off, data);
+}
+
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+	insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.teles3.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.teles3.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo(cs->hw.teles3.isacfifo, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo(cs->hw.teles3.isacfifo, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.teles3.hscx[hscx], offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.teles3.hscx[hscx], offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 5
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+	int count = 0;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.teles3.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	count++;
+	val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.teles3.isac, ISAC_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (count >= MAXCOUNT)
+		printk(KERN_WARNING "Teles3: more than %d loops in teles3_interrupt\n", count);
+	writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF);
+	writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF);
+	writereg(cs->hw.teles3.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.teles3.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0x0);
+	writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+inline static void
+release_ioregs(struct IsdnCardState *cs, int mask)
+{
+	if (mask & 1)
+		release_region(cs->hw.teles3.isac + 32, 32);
+	if (mask & 2)
+		release_region(cs->hw.teles3.hscx[0] + 32, 32);
+	if (mask & 4)
+		release_region(cs->hw.teles3.hscx[1] + 32, 32);
+}
+
+void
+release_io_teles3(struct IsdnCardState *cs)
+{
+	if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
+		release_region(cs->hw.teles3.hscx[1], 96);
+	} else {
+		if (cs->hw.teles3.cfg_reg) {
+			if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+				release_region(cs->hw.teles3.cfg_reg, 1);
+			} else {
+				release_region(cs->hw.teles3.cfg_reg, 8);
+			}
+		}
+		release_ioregs(cs, 0x7);
+	}
+}
+
+static int
+reset_teles3(struct IsdnCardState *cs)
+{
+	u_char irqcfg;
+
+	if (cs->typ != ISDN_CTYPE_TELESPCMCIA) {
+		if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) {
+			switch (cs->irq) {
+				case 2:
+				case 9:
+					irqcfg = 0x00;
+					break;
+				case 3:
+					irqcfg = 0x02;
+					break;
+				case 4:
+					irqcfg = 0x04;
+					break;
+				case 5:
+					irqcfg = 0x06;
+					break;
+				case 10:
+					irqcfg = 0x08;
+					break;
+				case 11:
+					irqcfg = 0x0A;
+					break;
+				case 12:
+					irqcfg = 0x0C;
+					break;
+				case 15:
+					irqcfg = 0x0E;
+					break;
+				default:
+					return(1);
+			}
+			byteout(cs->hw.teles3.cfg_reg + 4, irqcfg);
+			HZDELAY(HZ / 10 + 1);
+			byteout(cs->hw.teles3.cfg_reg + 4, irqcfg | 1);
+			HZDELAY(HZ / 10 + 1);
+		} else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+			byteout(cs->hw.teles3.cfg_reg, 0xff);
+			HZDELAY(2);
+			byteout(cs->hw.teles3.cfg_reg, 0x00);
+			HZDELAY(2);
+		} else {
+			/* Reset off for 16.3 PnP , thanks to Georg Acher */
+			byteout(cs->hw.teles3.isac + 0x3c, 0);
+			HZDELAY(2);
+			byteout(cs->hw.teles3.isac + 0x3c, 1);
+			HZDELAY(2);
+		}
+	}
+	return(0);
+}
+
+static int
+Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_teles3(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_teles3(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+#ifdef __ISAPNP__
+
+static struct isapnp_device_id teles_ids[] __devinitdata = {
+	{ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2110),
+	  ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2110), 
+	  (unsigned long) "Teles 16.3 PnP" },
+	{ ISAPNP_VENDOR('C', 'T', 'X'), ISAPNP_FUNCTION(0x0),
+	  ISAPNP_VENDOR('C', 'T', 'X'), ISAPNP_FUNCTION(0x0), 
+	  (unsigned long) "Creatix 16.3 PnP" },
+	{ ISAPNP_VENDOR('C', 'P', 'Q'), ISAPNP_FUNCTION(0x1002),
+	  ISAPNP_VENDOR('C', 'P', 'Q'), ISAPNP_FUNCTION(0x1002), 
+	  (unsigned long) "Compaq ISDN S0" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &teles_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit
+setup_teles3(struct IsdnCard *card)
+{
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, teles3_revision);
+	printk(KERN_INFO "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp));
+	if ((cs->typ != ISDN_CTYPE_16_3) && (cs->typ != ISDN_CTYPE_PNP)
+	    && (cs->typ != ISDN_CTYPE_TELESPCMCIA) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA))
+		return (0);
+
+#ifdef __ISAPNP__
+	if (!card->para[1] && isapnp_present()) {
+		struct pnp_dev *pnp_d;
+		while(ipid->card_vendor) {
+			if ((pnp_c = pnp_find_card(ipid->card_vendor,
+				ipid->card_device, pnp_c))) {
+				pnp_d = NULL;
+				if ((pnp_d = pnp_find_dev(pnp_c,
+					ipid->vendor, ipid->function, pnp_d))) {
+					int err;
+
+					printk(KERN_INFO "HiSax: %s detected\n",
+						(char *)ipid->driver_data);
+					pnp_disable_dev(pnp_d);
+					err = pnp_activate_dev(pnp_d);
+					if (err<0) {
+						printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+							__FUNCTION__, err);
+						return(0);
+					}
+					card->para[3] = pnp_port_start(pnp_d, 2);
+					card->para[2] = pnp_port_start(pnp_d, 1);
+					card->para[1] = pnp_port_start(pnp_d, 0);
+					card->para[0] = pnp_irq(pnp_d, 0);
+					if (!card->para[0] || !card->para[1] || !card->para[2]) {
+						printk(KERN_ERR "Teles PnP:some resources are missing %ld/%lx/%lx\n",
+							card->para[0], card->para[1], card->para[2]);
+						pnp_disable_dev(pnp_d);
+						return(0);
+					}
+					break;
+				} else {
+					printk(KERN_ERR "Teles PnP: PnP error card found, no device\n");
+				}
+			}
+			ipid++;
+			pnp_c = NULL;
+		} 
+		if (!ipid->card_vendor) {
+			printk(KERN_INFO "Teles PnP: no ISAPnP card found\n");
+			return(0);
+		}
+	}
+#endif
+	if (cs->typ == ISDN_CTYPE_16_3) {
+		cs->hw.teles3.cfg_reg = card->para[1];
+		switch (cs->hw.teles3.cfg_reg) {
+			case 0x180:
+			case 0x280:
+			case 0x380:
+				cs->hw.teles3.cfg_reg |= 0xc00;
+				break;
+		}
+		cs->hw.teles3.isac = cs->hw.teles3.cfg_reg - 0x420;
+		cs->hw.teles3.hscx[0] = cs->hw.teles3.cfg_reg - 0xc20;
+		cs->hw.teles3.hscx[1] = cs->hw.teles3.cfg_reg - 0x820;
+	} else if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
+		cs->hw.teles3.cfg_reg = 0;
+		cs->hw.teles3.hscx[0] = card->para[1] - 0x20;
+		cs->hw.teles3.hscx[1] = card->para[1];
+		cs->hw.teles3.isac = card->para[1] + 0x20;
+	} else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+		cs->hw.teles3.cfg_reg = card->para[3];
+		cs->hw.teles3.isac = card->para[2] - 32;
+		cs->hw.teles3.hscx[0] = card->para[1] - 32;
+		cs->hw.teles3.hscx[1] = card->para[1];
+	} else {	/* PNP */
+		cs->hw.teles3.cfg_reg = 0;
+		cs->hw.teles3.isac = card->para[1] - 32;
+		cs->hw.teles3.hscx[0] = card->para[2] - 32;
+		cs->hw.teles3.hscx[1] = card->para[2];
+	}
+	cs->irq = card->para[0];
+	cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e;
+	cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
+	cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
+	if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
+		if (!request_region(cs->hw.teles3.hscx[1], 96, "HiSax Teles PCMCIA")) {
+			printk(KERN_WARNING
+			       "HiSax: %s ports %x-%x already in use\n",
+			       CardType[cs->typ],
+			       cs->hw.teles3.hscx[1],
+			       cs->hw.teles3.hscx[1] + 96);
+			return (0);
+		}
+	} else {
+		if (cs->hw.teles3.cfg_reg) {
+			if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+				if (!request_region(cs->hw.teles3.cfg_reg, 1, "teles3 cfg")) {
+					printk(KERN_WARNING
+						"HiSax: %s config port %x already in use\n",
+						CardType[card->typ],
+						cs->hw.teles3.cfg_reg);
+					return (0);
+				}
+			} else {
+				if (!request_region(cs->hw.teles3.cfg_reg, 8, "teles3 cfg")) {
+					printk(KERN_WARNING
+					       "HiSax: %s config port %x-%x already in use\n",
+					       CardType[card->typ],
+					       cs->hw.teles3.cfg_reg,
+						cs->hw.teles3.cfg_reg + 8);
+					return (0);
+				}
+			}
+		}
+		if (!request_region(cs->hw.teles3.isac + 32, 32, "HiSax isac")) {
+			printk(KERN_WARNING
+			   "HiSax: %s isac ports %x-%x already in use\n",
+			       CardType[cs->typ],
+			       cs->hw.teles3.isac + 32,
+			       cs->hw.teles3.isac + 64);
+			if (cs->hw.teles3.cfg_reg) {
+				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+					release_region(cs->hw.teles3.cfg_reg, 1);
+				} else {
+					release_region(cs->hw.teles3.cfg_reg, 8);
+				}
+			}
+			return (0);
+		}
+		if (!request_region(cs->hw.teles3.hscx[0] + 32, 32, "HiSax hscx A")) {
+			printk(KERN_WARNING
+			 "HiSax: %s hscx A ports %x-%x already in use\n",
+			       CardType[cs->typ],
+			       cs->hw.teles3.hscx[0] + 32,
+			       cs->hw.teles3.hscx[0] + 64);
+			if (cs->hw.teles3.cfg_reg) {
+				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+					release_region(cs->hw.teles3.cfg_reg, 1);
+				} else {
+					release_region(cs->hw.teles3.cfg_reg, 8);
+				}
+			}
+			release_ioregs(cs, 1);
+			return (0);
+		}
+		if (!request_region(cs->hw.teles3.hscx[1] + 32, 32, "HiSax hscx B")) {
+			printk(KERN_WARNING
+			 "HiSax: %s hscx B ports %x-%x already in use\n",
+			       CardType[cs->typ],
+			       cs->hw.teles3.hscx[1] + 32,
+			       cs->hw.teles3.hscx[1] + 64);
+			if (cs->hw.teles3.cfg_reg) {
+				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+					release_region(cs->hw.teles3.cfg_reg, 1);
+				} else {
+					release_region(cs->hw.teles3.cfg_reg, 8);
+				}
+			}
+			release_ioregs(cs, 3);
+			return (0);
+		}
+	}
+	if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) {
+		if ((val = bytein(cs->hw.teles3.cfg_reg + 0)) != 0x51) {
+			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+			       cs->hw.teles3.cfg_reg + 0, val);
+			release_io_teles3(cs);
+			return (0);
+		}
+		if ((val = bytein(cs->hw.teles3.cfg_reg + 1)) != 0x93) {
+			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+			       cs->hw.teles3.cfg_reg + 1, val);
+			release_io_teles3(cs);
+			return (0);
+		}
+		val = bytein(cs->hw.teles3.cfg_reg + 2);/* 0x1e=without AB
+							 * 0x1f=with AB
+							 * 0x1c 16.3 ???
+							 * 0x39 16.3 1.1
+							 * 0x38 16.3 1.3
+							 * 0x46 16.3 with AB + Video (Teles-Vision)
+							 */
+		if (val != 0x46 && val != 0x39 && val != 0x38 && val != 0x1c && val != 0x1e && val != 0x1f) {
+			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+			       cs->hw.teles3.cfg_reg + 2, val);
+			release_io_teles3(cs);
+			return (0);
+		}
+	}
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d isac:0x%X  cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles3.isac + 32, cs->hw.teles3.cfg_reg);
+	printk(KERN_INFO
+	       "HiSax: hscx A:0x%X  hscx B:0x%X\n",
+	       cs->hw.teles3.hscx[0] + 32, cs->hw.teles3.hscx[1] + 32);
+
+	setup_isac(cs);
+	if (reset_teles3(cs)) {
+		printk(KERN_WARNING "Teles3: wrong IRQ\n");
+		release_io_teles3(cs);
+		return (0);
+	}
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Teles_card_msg;
+	cs->irq_func = &teles3_interrupt;
+	ISACVersion(cs, "Teles3:");
+	if (HscxVersion(cs, "Teles3:")) {
+		printk(KERN_WARNING
+		       "Teles3: wrong HSCX versions check IO address\n");
+		release_io_teles3(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c
new file mode 100644
index 000000000000..63e8e20c17a8
--- /dev/null
+++ b/drivers/isdn/hisax/teles_cs.c
@@ -0,0 +1,513 @@
+/* $Id: teles_cs.c,v 1.1.2.2 2004/01/25 15:07:06 keil Exp $ */
+/*======================================================================
+
+    A teles S0 PCMCIA client driver
+
+    Based on skeleton by David Hinds, dhinds@allegro.stanford.edu
+    Written by Christof Petig, christof.petig@wtal.de
+    
+    Also inspired by ELSA PCMCIA driver 
+    by Klaus Lichtenwalder <Lichtenwalder@ACM.org>
+    
+    Extentions to new hisax_pcmcia by Karsten Keil
+
+    minor changes to be compatible with kernel 2.4.x
+    by Jan.Schubert@GMX.li
+
+======================================================================*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include "hisax_cfg.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Teles PCMCIA cards");
+MODULE_AUTHOR("Christof Petig, christof.petig@wtal.de, Karsten Keil, kkeil@suse.de");
+MODULE_LICENSE("GPL");
+
+/*
+   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
+   you do not define PCMCIA_DEBUG at all, all the debug code will be
+   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
+   be present but disabled -- but it can then be enabled for specific
+   modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
+static char *version =
+"teles_cs.c 2.10 2002/07/30 22:23:34 kkeil";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int protocol = 2;        /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+
+/*====================================================================*/
+
+/*
+   The event() function is this driver's Card Services event handler.
+   It will be called by Card Services when an appropriate card status
+   event is received.  The config() and release() entry points are
+   used to configure or release a socket, in response to card insertion
+   and ejection events.  They are invoked from the teles_cs event
+   handler.
+*/
+
+static void teles_cs_config(dev_link_t *link);
+static void teles_cs_release(dev_link_t *link);
+static int teles_cs_event(event_t event, int priority,
+                          event_callback_args_t *args);
+
+/*
+   The attach() and detach() entry points are used to create and destroy
+   "instances" of the driver, where each instance represents everything
+   needed to manage one actual PCMCIA card.
+*/
+
+static dev_link_t *teles_attach(void);
+static void teles_detach(dev_link_t *);
+
+/*
+   The dev_info variable is the "key" that is used to match up this
+   device driver with appropriate cards, through the card configuration
+   database.
+*/
+
+static dev_info_t dev_info = "teles_cs";
+
+/*
+   A linked list of "instances" of the teles_cs device.  Each actual
+   PCMCIA card corresponds to one device instance, and is described
+   by one dev_link_t structure (defined in ds.h).
+
+   You may not want to use a linked list for this -- for example, the
+   memory card driver uses an array of dev_link_t pointers, where minor
+   device numbers are used to derive the corresponding array index.
+*/
+
+static dev_link_t *dev_list = NULL;
+
+/*
+   A dev_link_t structure has fields for most things that are needed
+   to keep track of a socket, but there will usually be some device
+   specific information that also needs to be kept track of.  The
+   'priv' pointer in a dev_link_t structure can be used to point to
+   a device-specific private data structure, like this.
+
+   To simplify the data structure handling, we actually include the
+   dev_link_t structure in the device's private data structure.
+
+   A driver needs to provide a dev_node_t structure for each device
+   on a card.  In some cases, there is only one device per card (for
+   example, ethernet cards, modems).  In other cases, there may be
+   many actual or logical devices (SCSI adapters, memory cards with
+   multiple partitions).  The dev_node_t structures need to be kept
+   in a linked list starting at the 'dev' field of a dev_link_t
+   structure.  We allocate them in the card's private data structure,
+   because they generally shouldn't be allocated dynamically.
+   In this case, we also provide a flag to indicate if a device is
+   "stopped" due to a power management event, or card ejection.  The
+   device IO routines can use a flag like this to throttle IO to a
+   card that is not ready to accept it.
+*/
+
+typedef struct local_info_t {
+    dev_link_t          link;
+    dev_node_t          node;
+    int                 busy;
+    int			cardnr;
+} local_info_t;
+
+/*======================================================================
+
+    teles_attach() creates an "instance" of the driver, allocatingx
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+    The dev_link structure is initialized, but we don't actually
+    configure the card at this point -- we wait until we receive a
+    card insertion event.
+
+======================================================================*/
+
+static dev_link_t *teles_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    local_info_t *local;
+    int ret;
+
+    DEBUG(0, "teles_attach()\n");
+
+    /* Allocate space for private device-specific data */
+    local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+    if (!local) return NULL;
+    memset(local, 0, sizeof(local_info_t));
+    local->cardnr = -1;
+    link = &local->link; link->priv = local;
+
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID|IRQ_SHARE_ID;
+    link->irq.Handler = NULL;
+
+    /*
+      General socket configuration defaults can go here.  In this
+      client, we assume very little, and rely on the CIS for almost
+      everything.  In most clients, many details (i.e., number, sizes,
+      and attributes of IO windows) are fixed by the nature of the
+      device, and can be hard-wired here.
+    */
+    link->io.NumPorts1 = 96;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 5;
+
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+        CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+        CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+        CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &teles_cs_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != CS_SUCCESS) {
+        cs_error(link->handle, RegisterClient, ret);
+        teles_detach(link);
+        return NULL;
+    }
+
+    return link;
+} /* teles_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void teles_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+    local_info_t *info = link->priv;
+    int ret;
+
+    DEBUG(0, "teles_detach(0x%p)\n", link);
+
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+        if (*linkp == link) break;
+    if (*linkp == NULL)
+        return;
+
+    if (link->state & DEV_CONFIG)
+        teles_cs_release(link);
+
+    /* Break the link with Card Services */
+    if (link->handle) {
+        ret = pcmcia_deregister_client(link->handle);
+	if (ret != CS_SUCCESS)
+	    cs_error(link->handle, DeregisterClient, ret);
+    }
+
+    /* Unlink device structure and free it */
+    *linkp = link->next;
+    kfree(info);
+
+} /* teles_detach */
+
+/*======================================================================
+
+    teles_cs_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    device available to the system.
+
+======================================================================*/
+static int get_tuple(client_handle_t handle, tuple_t *tuple,
+                     cisparse_t *parse)
+{
+    int i = pcmcia_get_tuple_data(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return pcmcia_parse_tuple(handle, tuple, parse);
+}
+
+static int first_tuple(client_handle_t handle, tuple_t *tuple,
+                     cisparse_t *parse)
+{
+    int i = pcmcia_get_first_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static int next_tuple(client_handle_t handle, tuple_t *tuple,
+                     cisparse_t *parse)
+{
+    int i = pcmcia_get_next_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static void teles_cs_config(dev_link_t *link)
+{
+    client_handle_t handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    local_info_t *dev;
+    int i, j, last_fn;
+    u_short buf[128];
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+    IsdnCard_t icard;
+
+    DEBUG(0, "teles_config(0x%p)\n", link);
+    handle = link->handle;
+    dev = link->priv;
+
+    /*
+       This reads the card's CONFIG tuple to find its configuration
+       registers.
+    */
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleDataMax = 255;
+    tuple.TupleOffset = 0;
+    tuple.Attributes = 0;
+    i = first_tuple(handle, &tuple, &parse);
+    if (i != CS_SUCCESS) {
+        last_fn = ParseTuple;
+	goto cs_failed;
+    }
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present = parse.config.rmask[0];
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+    tuple.Attributes = 0;
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    i = first_tuple(handle, &tuple, &parse);
+    while (i == CS_SUCCESS) {
+        if ( (cf->io.nwin > 0) && cf->io.win[0].base) {
+            printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
+            link->conf.ConfigIndex = cf->index;
+            link->io.BasePort1 = cf->io.win[0].base;
+            i = pcmcia_request_io(link->handle, &link->io);
+            if (i == CS_SUCCESS) break;
+        } else {
+          printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
+          link->conf.ConfigIndex = cf->index;
+          for (i = 0, j = 0x2f0; j > 0x100; j -= 0x10) {
+            link->io.BasePort1 = j;
+            i = pcmcia_request_io(link->handle, &link->io);
+            if (i == CS_SUCCESS) break;
+          }
+          break;
+        }
+        i = next_tuple(handle, &tuple, &parse);
+    }
+
+    if (i != CS_SUCCESS) {
+	last_fn = RequestIO;
+	goto cs_failed;
+    }
+
+    i = pcmcia_request_irq(link->handle, &link->irq);
+    if (i != CS_SUCCESS) {
+        link->irq.AssignedIRQ = 0;
+	last_fn = RequestIRQ;
+        goto cs_failed;
+    }
+
+    i = pcmcia_request_configuration(link->handle, &link->conf);
+    if (i != CS_SUCCESS) {
+      last_fn = RequestConfiguration;
+      goto cs_failed;
+    }
+
+    /* At this point, the dev_node_t structure(s) should be
+       initialized and arranged in a linked list at link->dev. *//*  */
+    sprintf(dev->node.dev_name, "teles");
+    dev->node.major = dev->node.minor = 0x0;
+
+    link->dev = &dev->node;
+
+    /* Finally, report what we've done */
+    printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
+           dev->node.dev_name, link->conf.ConfigIndex,
+           link->conf.Vcc/10, link->conf.Vcc%10);
+    if (link->conf.Vpp1)
+        printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+    if (link->conf.Attributes & CONF_ENABLE_IRQ)
+        printk(", irq %d", link->irq.AssignedIRQ);
+    if (link->io.NumPorts1)
+        printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+               link->io.BasePort1+link->io.NumPorts1-1);
+    if (link->io.NumPorts2)
+        printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+               link->io.BasePort2+link->io.NumPorts2-1);
+    printk("\n");
+
+    link->state &= ~DEV_CONFIG_PENDING;
+
+    icard.para[0] = link->irq.AssignedIRQ;
+    icard.para[1] = link->io.BasePort1;
+    icard.protocol = protocol;
+    icard.typ = ISDN_CTYPE_TELESPCMCIA;
+    
+    i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
+    if (i < 0) {
+    	printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n",
+    		i, link->io.BasePort1);
+    	teles_cs_release(link);
+    } else
+    	((local_info_t*)link->priv)->cardnr = i;
+
+    return;
+cs_failed:
+    cs_error(link->handle, last_fn, i);
+    teles_cs_release(link);
+} /* teles_cs_config */
+
+/*======================================================================
+
+    After a card is removed, teles_cs_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void teles_cs_release(dev_link_t *link)
+{
+    local_info_t *local = link->priv;
+
+    DEBUG(0, "teles_cs_release(0x%p)\n", link);
+
+    if (local) {
+    	if (local->cardnr >= 0) {
+    	    /* no unregister function with hisax */
+	    HiSax_closecard(local->cardnr);
+	}
+    }
+    /* Unlink the device chain */
+    link->dev = NULL;
+
+    /* Don't bother checking to see if these succeed or not */
+    if (link->win)
+        pcmcia_release_window(link->win);
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+    link->state &= ~DEV_CONFIG;
+} /* teles_cs_release */
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+    When a CARD_REMOVAL event is received, we immediately set a flag
+    to block future accesses to this device.  All the functions that
+    actually access the device should check this flag to make sure
+    the card is still present.
+
+======================================================================*/
+
+static int teles_cs_event(event_t event, int priority,
+                          event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    local_info_t *dev = link->priv;
+
+    DEBUG(1, "teles_cs_event(%d)\n", event);
+
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+        link->state &= ~DEV_PRESENT;
+        if (link->state & DEV_CONFIG) {
+            ((local_info_t*)link->priv)->busy = 1;
+	    teles_cs_release(link);
+        }
+        break;
+    case CS_EVENT_CARD_INSERTION:
+        link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+        teles_cs_config(link);
+        break;
+    case CS_EVENT_PM_SUSPEND:
+        link->state |= DEV_SUSPEND;
+        /* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+        /* Mark the device as stopped, to block IO until later */
+        dev->busy = 1;
+        if (link->state & DEV_CONFIG)
+            pcmcia_release_configuration(link->handle);
+        break;
+    case CS_EVENT_PM_RESUME:
+        link->state &= ~DEV_SUSPEND;
+        /* Fall through... */
+    case CS_EVENT_CARD_RESET:
+        if (link->state & DEV_CONFIG)
+            pcmcia_request_configuration(link->handle, &link->conf);
+        dev->busy = 0;
+        break;
+    }
+    return 0;
+} /* teles_cs_event */
+
+static struct pcmcia_driver teles_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "teles_cs",
+	},
+	.attach		= teles_attach,
+	.detach		= teles_detach,
+};
+
+static int __init init_teles_cs(void)
+{
+	return pcmcia_register_driver(&teles_cs_driver);
+}
+
+static void __exit exit_teles_cs(void)
+{
+	pcmcia_unregister_driver(&teles_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_teles_cs);
+module_exit(exit_teles_cs);
diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c
new file mode 100644
index 000000000000..0661c6c31ad0
--- /dev/null
+++ b/drivers/isdn/hisax/telespci.c
@@ -0,0 +1,359 @@
+/* $Id: telespci.c,v 2.23.2.3 2004/01/13 14:31:26 keil Exp $
+ *
+ * low level stuff for Teles PCI isdn cards
+ *
+ * Author       Ton van Rosmalen
+ *              Karsten Keil
+ * Copyright    by Ton van Rosmalen
+ *              by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+
+extern const char *CardType[];
+const char *telespci_revision = "$Revision: 2.23.2.3 $";
+
+#define ZORAN_PO_RQ_PEN	0x02000000
+#define ZORAN_PO_WR	0x00800000
+#define ZORAN_PO_GID0	0x00000000
+#define ZORAN_PO_GID1	0x00100000
+#define ZORAN_PO_GREG0	0x00000000
+#define ZORAN_PO_GREG1	0x00010000
+#define ZORAN_PO_DMASK	0xFF
+
+#define WRITE_ADDR_ISAC	(ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG0)
+#define READ_DATA_ISAC	(ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_DATA_ISAC	(ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_ADDR_HSCX	(ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG0)
+#define READ_DATA_HSCX	(ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+#define WRITE_DATA_HSCX	(ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+
+#define ZORAN_WAIT_NOBUSY	do { \
+					portdata = readl(adr + 0x200); \
+				} while (portdata & ZORAN_PO_RQ_PEN)
+
+static inline u_char
+readisac(void __iomem *adr, u_char off)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	
+	/* set address for ISAC */
+	writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	
+	/* read data from ISAC */
+	writel(READ_DATA_ISAC, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	return((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writeisac(void __iomem *adr, u_char off, u_char data)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	
+	/* set address for ISAC */
+	writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+
+	/* write data to ISAC */
+	writel(WRITE_DATA_ISAC | data, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+}
+
+static inline u_char
+readhscx(void __iomem *adr, int hscx, u_char off)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	/* set address for HSCX */
+	writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	
+	/* read data from HSCX */
+	writel(READ_DATA_HSCX, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	return ((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writehscx(void __iomem *adr, int hscx, u_char off, u_char data)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	/* set address for HSCX */
+	writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+
+	/* write data to HSCX */
+	writel(WRITE_DATA_HSCX | data, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+}
+
+static inline void
+read_fifo_isac(void __iomem *adr, u_char * data, int size)
+{
+	register unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* read data from ISAC */
+	for (i = 0; i < size; i++) {
+		/* set address for ISAC fifo */
+		writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(READ_DATA_ISAC, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		data[i] = (u_char)(portdata & ZORAN_PO_DMASK);
+	}
+}
+
+static void
+write_fifo_isac(void __iomem *adr, u_char * data, int size)
+{
+	register unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* write data to ISAC */
+	for (i = 0; i < size; i++) {
+		/* set address for ISAC fifo */
+		writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(WRITE_DATA_ISAC | data[i], adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+	}
+}
+
+static inline void
+read_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size)
+{
+	register unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* read data from HSCX */
+	for (i = 0; i < size; i++) {
+		/* set address for HSCX fifo */
+		writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(READ_DATA_HSCX, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		data[i] = (u_char) (portdata & ZORAN_PO_DMASK);
+	}
+}
+
+static inline void
+write_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size)
+{
+	unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* write data to HSCX */
+	for (i = 0; i < size; i++) {
+		/* set address for HSCX fifo */
+		writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(WRITE_DATA_HSCX | data[i], adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		udelay(10);
+	}
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readisac(cs->hw.teles0.membase, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writeisac(cs->hw.teles0.membase, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readhscx(cs->hw.teles0.membase, hscx, offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writehscx(cs->hw.teles0.membase, hscx, offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+telespci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char hval, ival;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	hval = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+	if (hval)
+		hscx_int_main(cs, hval);
+	ival = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+	if ((hval | ival) == 0) {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+	if (ival)
+		isac_interrupt(cs, ival);
+	/* Clear interrupt register for Zoran PCI controller */
+	writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+
+	writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF);
+	writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF);
+	writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF);
+	writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0);
+	writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0);
+	writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_telespci(struct IsdnCardState *cs)
+{
+	iounmap(cs->hw.teles0.membase);
+}
+
+static int
+TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			release_io_telespci(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static struct pci_dev *dev_tel __initdata = NULL;
+
+int __init
+setup_telespci(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+	strcpy(tmp, telespci_revision);
+	printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_TELESPCI)
+		return (0);
+#ifdef CONFIG_PCI
+	if ((dev_tel = pci_find_device (PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_tel))) {
+		if (pci_enable_device(dev_tel))
+			return(0);
+		cs->irq = dev_tel->irq;
+		if (!cs->irq) {
+			printk(KERN_WARNING "Teles: No IRQ for PCI card found\n");
+			return(0);
+		}
+		cs->hw.teles0.membase = ioremap(pci_resource_start(dev_tel, 0),
+			PAGE_SIZE);
+		printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n",
+			pci_resource_start(dev_tel, 0), dev_tel->irq);
+	} else {
+		printk(KERN_WARNING "TelesPCI: No PCI card found\n");
+		return(0);
+	}
+#else
+	printk(KERN_WARNING "HiSax: Teles/PCI and NO_PCI_BIOS\n");
+	printk(KERN_WARNING "HiSax: Teles/PCI unable to config\n");
+	return (0);
+#endif /* CONFIG_PCI */
+
+	/* Initialize Zoran PCI controller */
+	writel(0x00000000, cs->hw.teles0.membase + 0x28);
+	writel(0x01000000, cs->hw.teles0.membase + 0x28);
+	writel(0x01000000, cs->hw.teles0.membase + 0x28);
+	writel(0x7BFFFFFF, cs->hw.teles0.membase + 0x2C);
+	writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+	writel(0x61000000, cs->hw.teles0.membase + 0x40);
+	/* writel(0x00800000, cs->hw.teles0.membase + 0x200); */
+
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d mem:%p\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles0.membase);
+
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &TelesPCI_card_msg;
+	cs->irq_func = &telespci_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+	ISACVersion(cs, "TelesPCI:");
+	if (HscxVersion(cs, "TelesPCI:")) {
+		printk(KERN_WARNING
+		 "TelesPCI: wrong HSCX versions check IO/MEM addresses\n");
+		release_io_telespci(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/w6692.c b/drivers/isdn/hisax/w6692.c
new file mode 100644
index 000000000000..d2b6b8e72980
--- /dev/null
+++ b/drivers/isdn/hisax/w6692.c
@@ -0,0 +1,1096 @@
+/* $Id: w6692.c,v 1.18.2.4 2004/02/11 13:21:34 keil Exp $
+ *
+ * Winbond W6692 specific routines
+ *
+ * Author       Petr Novak
+ * Copyright    by 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.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include "w6692.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+
+/* 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_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, "Winbond", "W6692"},
+	{PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, "Dynalink/AsusCom", "IS64PH"},
+	{0, 0, "U.S.Robotics", "ISDN PCI Card TA"}
+};
+
+#define W6692_SV_USR   0x16ec
+#define W6692_SD_USR   0x3409
+#define W6692_WINBOND  0
+#define W6692_DYNALINK 1
+#define W6692_USR      2
+
+extern const char *CardType[];
+
+const char *w6692_revision = "$Revision: 1.18.2.4 $";
+
+#define DBUSY_TIMER_VALUE 80
+
+static char *W6692Ver[] __initdata =
+{"W6692 V00", "W6692 V01", "W6692 V10",
+ "W6692 V11"};
+
+static void __init
+W6692Version(struct IsdnCardState *cs, char *s)
+{
+	int val;
+
+	val = cs->readW6692(cs, W_D_RBCH);
+	printk(KERN_INFO "%s Winbond W6692 version (%x): %s\n", s, val, W6692Ver[(val >> 6) & 3]);
+}
+
+static void
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ph_command %x", command);
+	cs->writeisac(cs, W_CIX, command);
+}
+
+
+static void
+W6692_new_ph(struct IsdnCardState *cs)
+{
+	switch (cs->dc.w6692.ph_state) {
+		case (W_L1CMD_RST):
+			ph_command(cs, W_L1CMD_DRC);
+			l1_msg(cs, HW_RESET | INDICATION, NULL);
+			/* fallthru */
+		case (W_L1IND_CD):
+			l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+			break;
+		case (W_L1IND_DRD):
+			l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+			break;
+		case (W_L1IND_CE):
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (W_L1IND_LD):
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			break;
+		case (W_L1IND_ARD):
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+			break;
+		case (W_L1IND_AI8):
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+			break;
+		case (W_L1IND_AI10):
+			l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+W6692_bh(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy cleared");
+		stptr = cs->stlist;
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			stptr = stptr->next;
+		}
+	}
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
+		W6692_new_ph(cs);
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+/*
+   if (test_and_clear_bit(D_RX_MON1, &cs->event))
+   arcofi_fsm(cs, ARCOFI_RX_END, NULL);
+   if (test_and_clear_bit(D_TX_MON1, &cs->event))
+   arcofi_fsm(cs, ARCOFI_TX_END, NULL);
+ */
+}
+
+static void
+W6692_empty_fifo(struct IsdnCardState *cs, int count)
+{
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "W6692_empty_fifo");
+
+	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "W6692_empty_fifo overrun %d",
+				cs->rcvidx + count);
+		cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK);
+		cs->rcvidx = 0;
+		return;
+	}
+	ptr = cs->rcvbuf + cs->rcvidx;
+	cs->rcvidx += count;
+	cs->readW6692fifo(cs, ptr, count);
+	cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "W6692_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+static void
+W6692_fill_fifo(struct IsdnCardState *cs)
+{
+	int count, more;
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "W6692_fill_fifo");
+
+	if (!cs->tx_skb)
+		return;
+
+	count = cs->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > W_D_FIFO_THRESH) {
+		more = !0;
+		count = W_D_FIFO_THRESH;
+	}
+	ptr = cs->tx_skb->data;
+	skb_pull(cs->tx_skb, count);
+	cs->tx_cnt += count;
+	cs->writeW6692fifo(cs, ptr, count);
+	cs->writeW6692(cs, W_D_CMDR, more ? W_D_CMDR_XMS : (W_D_CMDR_XMS | W_D_CMDR_XME));
+	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		debugl1(cs, "W6692_fill_fifo dbusytimer running");
+		del_timer(&cs->dbusytimer);
+	}
+	init_timer(&cs->dbusytimer);
+	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000);
+	add_timer(&cs->dbusytimer);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "W6692_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+static void
+W6692B_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *cs = bcs->cs;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "W6692B_empty_fifo");
+
+	if (bcs->hw.w6692.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "W6692B_empty_fifo: incoming packet too large");
+		cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+		bcs->hw.w6692.rcvidx = 0;
+		return;
+	}
+	ptr = bcs->hw.w6692.rcvbuf + bcs->hw.w6692.rcvidx;
+	bcs->hw.w6692.rcvidx += count;
+	READW6692BFIFO(cs, bcs->channel, ptr, count);
+	cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "W6692B_empty_fifo %c cnt %d",
+			     bcs->channel + '1', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static void
+W6692B_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int more, count;
+	u_char *ptr;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+	if (bcs->tx_skb->len > W_B_FIFO_THRESH) {
+		more = 1;
+		count = W_B_FIFO_THRESH;
+	} else
+		count = bcs->tx_skb->len;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "W6692B_fill_fifo%s%d", (more ? " ": " last "), count);
+
+	ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.w6692.count += count;
+	WRITEW6692BFIFO(cs, bcs->channel, ptr, count);
+	cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACT | W_B_CMDR_XMS | (more ? 0 : W_B_CMDR_XME));
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "W6692B_fill_fifo %c cnt %d",
+			     bcs->channel + '1', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static void
+W6692B_interrupt(struct IsdnCardState *cs, u_char bchan)
+{
+	u_char val;
+	u_char r;
+	struct BCState *bcs;
+	struct sk_buff *skb;
+	int count;
+
+	bcs = (cs->bcs->channel == bchan) ? cs->bcs : (cs->bcs+1);
+	val = cs->BC_Read_Reg(cs, bchan, W_B_EXIR);
+	debugl1(cs, "W6692B chan %d B_EXIR 0x%02X", bchan, val);
+
+	if (!test_bit(BC_FLG_INIT, &bcs->Flag)) {
+		debugl1(cs, "W6692B not INIT yet");
+		return;
+	}
+	if (val & W_B_EXI_RME) {	/* RME */
+		r = cs->BC_Read_Reg(cs, bchan, W_B_STAR);
+		if (r & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "W6692 B STAR %x", r);
+			if ((r & W_B_STAR_RDOV) && bcs->mode)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "W6692 B RDOV mode=%d",
+						bcs->mode);
+			if (r & W_B_STAR_CRCE)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "W6692 B CRC error");
+			cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);
+		} else {
+			count = cs->BC_Read_Reg(cs, bchan, W_B_RBCL) & (W_B_FIFO_THRESH - 1);
+			if (count == 0)
+				count = W_B_FIFO_THRESH;
+			W6692B_empty_fifo(bcs, count);
+			if ((count = bcs->hw.w6692.rcvidx) > 0) {
+				if (cs->debug & L1_DEB_HSCX_FIFO)
+					debugl1(cs, "W6692 Bchan Frame %d", count);
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "W6692: Bchan receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), bcs->hw.w6692.rcvbuf, count);
+					skb_queue_tail(&bcs->rqueue, skb);
+				}
+			}
+		}
+		bcs->hw.w6692.rcvidx = 0;
+		schedule_event(bcs, B_RCVBUFREADY);
+	}
+	if (val & W_B_EXI_RMR) {	/* RMR */
+		W6692B_empty_fifo(bcs, W_B_FIFO_THRESH);
+		r = cs->BC_Read_Reg(cs, bchan, W_B_STAR);
+		if (r & W_B_STAR_RDOV) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "W6692 B RDOV(RMR) mode=%d",bcs->mode);
+			cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);
+			if (bcs->mode != L1_MODE_TRANS)
+				bcs->hw.w6692.rcvidx = 0;
+		}
+		if (bcs->mode == L1_MODE_TRANS) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(W_B_FIFO_THRESH)))
+				printk(KERN_WARNING "HiSax: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, W_B_FIFO_THRESH), bcs->hw.w6692.rcvbuf, W_B_FIFO_THRESH);
+				skb_queue_tail(&bcs->rqueue, skb);
+			}
+			bcs->hw.w6692.rcvidx = 0;
+			schedule_event(bcs, B_RCVBUFREADY);
+		}
+	}
+	if (val & W_B_EXI_XDUN) {	/* XDUN */
+		cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "W6692 B EXIR %x Lost TX", val);
+		if (bcs->mode == 1)
+			W6692B_fill_fifo(bcs);
+		else {
+			/* Here we lost an TX interrupt, so
+			   * restart transmitting the whole frame.
+			 */
+			if (bcs->tx_skb) {
+				skb_push(bcs->tx_skb, bcs->hw.w6692.count);
+				bcs->tx_cnt += bcs->hw.w6692.count;
+				bcs->hw.w6692.count = 0;
+			}
+		}
+		return;
+	}
+	if (val & W_B_EXI_XFR) {	/* XFR */
+		r = cs->BC_Read_Reg(cs, bchan, W_B_STAR);
+		if (r & W_B_STAR_XDOW) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "W6692 B STAR %x XDOW", r);
+			cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
+			if (bcs->tx_skb && (bcs->mode != 1)) {
+				skb_push(bcs->tx_skb, bcs->hw.w6692.count);
+				bcs->tx_cnt += bcs->hw.w6692.count;
+				bcs->hw.w6692.count = 0;
+			}
+		}
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				W6692B_fill_fifo(bcs);
+				return;
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->hw.w6692.count;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+				dev_kfree_skb_irq(bcs->tx_skb);
+				bcs->hw.w6692.count = 0;
+				bcs->tx_skb = NULL;
+			}
+		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.w6692.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			W6692B_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+static irqreturn_t
+W6692_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState	*cs = dev_id;
+	u_char			val, exval, v1;
+	struct sk_buff		*skb;
+	u_int			count;
+	u_long			flags;
+	int			icnt = 5;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = cs->readW6692(cs, W_ISTA);
+	if (!val) {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+      StartW6692:
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "W6692 ISTA %x", val);
+
+	if (val & W_INT_D_RME) {	/* RME */
+		exval = cs->readW6692(cs, W_D_RSTA);
+		if (exval & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) {
+			if (exval & W_D_RSTA_RDOV)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "W6692 RDOV");
+			if (exval & W_D_RSTA_CRCE)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "W6692 D-channel CRC error");
+			if (exval & W_D_RSTA_RMB)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "W6692 D-channel ABORT");
+			cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST);
+		} else {
+			count = cs->readW6692(cs, W_D_RBCL) & (W_D_FIFO_THRESH - 1);
+			if (count == 0)
+				count = W_D_FIFO_THRESH;
+			W6692_empty_fifo(cs, count);
+			if ((count = cs->rcvidx) > 0) {
+				cs->rcvidx = 0;
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "HiSax: D receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), cs->rcvbuf, count);
+					skb_queue_tail(&cs->rq, skb);
+				}
+			}
+		}
+		cs->rcvidx = 0;
+		schedule_event(cs, D_RCVBUFREADY);
+	}
+	if (val & W_INT_D_RMR) {	/* RMR */
+		W6692_empty_fifo(cs, W_D_FIFO_THRESH);
+	}
+	if (val & W_INT_D_XFR) {	/* XFR */
+		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+		if (cs->tx_skb) {
+			if (cs->tx_skb->len) {
+				W6692_fill_fifo(cs);
+				goto afterXFR;
+			} else {
+				dev_kfree_skb_irq(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			}
+		}
+		if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+			cs->tx_cnt = 0;
+			W6692_fill_fifo(cs);
+		} else
+			schedule_event(cs, D_XMTBUFREADY);
+	}
+      afterXFR:
+	if (val & (W_INT_XINT0 | W_INT_XINT1)) {	/* XINT0/1 - never */
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "W6692 spurious XINT!");
+	}
+	if (val & W_INT_D_EXI) {	/* EXI */
+		exval = cs->readW6692(cs, W_D_EXIR);
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "W6692 D_EXIR %02x", exval);
+		if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) {	/* Transmit underrun/collision */
+			debugl1(cs, "W6692 D-chan underrun/collision");
+			printk(KERN_WARNING "HiSax: W6692 XDUN/XCOL\n");
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			if (cs->tx_skb) {	/* Restart frame */
+				skb_push(cs->tx_skb, cs->tx_cnt);
+				cs->tx_cnt = 0;
+				W6692_fill_fifo(cs);
+			} else {
+				printk(KERN_WARNING "HiSax: W6692 XDUN/XCOL no skb\n");
+				debugl1(cs, "W6692 XDUN/XCOL no skb");
+				cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_XRST);
+			}
+		}
+		if (exval & W_D_EXI_RDOV) {	/* RDOV */
+			debugl1(cs, "W6692 D-channel RDOV");
+			printk(KERN_WARNING "HiSax: W6692 D-RDOV\n");
+			cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RRST);
+		}
+		if (exval & W_D_EXI_TIN2) {	/* TIN2 - never */
+			debugl1(cs, "W6692 spurious TIN2 interrupt");
+		}
+		if (exval & W_D_EXI_MOC) {	/* MOC - not supported */
+			debugl1(cs, "W6692 spurious MOC interrupt");
+			v1 = cs->readW6692(cs, W_MOSR);
+			debugl1(cs, "W6692 MOSR %02x", v1);
+		}
+		if (exval & W_D_EXI_ISC) {	/* ISC - Level1 change */
+			v1 = cs->readW6692(cs, W_CIR);
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "W6692 ISC CIR=0x%02X", v1);
+			if (v1 & W_CIR_ICC) {
+				cs->dc.w6692.ph_state = v1 & W_CIR_COD_MASK;
+				if (cs->debug & L1_DEB_ISAC)
+					debugl1(cs, "ph_state_change %x", cs->dc.w6692.ph_state);
+				schedule_event(cs, D_L1STATECHANGE);
+			}
+			if (v1 & W_CIR_SCC) {
+				v1 = cs->readW6692(cs, W_SQR);
+				debugl1(cs, "W6692 SCC SQR=0x%02X", v1);
+			}
+		}
+		if (exval & W_D_EXI_WEXP) {
+			debugl1(cs, "W6692 spurious WEXP interrupt!");
+		}
+		if (exval & W_D_EXI_TEXP) {
+			debugl1(cs, "W6692 spurious TEXP interrupt!");
+		}
+	}
+	if (val & W_INT_B1_EXI) {
+		debugl1(cs, "W6692 B channel 1 interrupt");
+		W6692B_interrupt(cs, 0);
+	}
+	if (val & W_INT_B2_EXI) {
+		debugl1(cs, "W6692 B channel 2 interrupt");
+		W6692B_interrupt(cs, 1);
+	}
+	val = cs->readW6692(cs, W_ISTA);
+	if (val && icnt) {
+		icnt--;
+		goto StartW6692;
+	}
+	if (!icnt) {
+		printk(KERN_WARNING "W6692 IRQ LOOP\n");
+		cs->writeW6692(cs, W_IMASK, 0xff);
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+W6692_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	u_long flags;
+	int val;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				W6692_fill_fifo(cs);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				spin_unlock_irqrestore(&cs->lock, flags);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			W6692_fill_fifo(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			if ((cs->dc.w6692.ph_state == W_L1IND_DRD)) {
+				ph_command(cs, W_L1CMD_ECK);
+				spin_unlock_irqrestore(&cs->lock, flags);
+			} else {
+				ph_command(cs, W_L1CMD_RST);
+				cs->dc.w6692.ph_state = W_L1CMD_RST;
+				spin_unlock_irqrestore(&cs->lock, flags);
+				W6692_new_ph(cs);
+			}
+			break;
+		case (HW_ENABLE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, W_L1CMD_ECK);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO3 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, W_L1CMD_AR8);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			val = 0;
+			if (1 & (long) arg)
+				val |= 0x0c;
+			if (2 & (long) arg)
+				val |= 0x3;
+			/* !!! not implemented yet */
+			break;
+		case (HW_DEACTIVATE | RESPONSE):
+			skb_queue_purge(&cs->rq);
+			skb_queue_purge(&cs->sq);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_skb = NULL;
+			}
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "W6692_l1hw unknown %04x", pr);
+			break;
+	}
+}
+
+static void
+setstack_W6692(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = W6692_l1hw;
+}
+
+static void
+DC_Close_W6692(struct IsdnCardState *cs)
+{
+}
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	int rbch, star;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		rbch = cs->readW6692(cs, W_D_RBCH);
+		star = cs->readW6692(cs, W_D_STAR);
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy D_RBCH %02x D_STAR %02x",
+				rbch, star);
+		if (star & W_D_STAR_XBZ) {	/* D-Channel Busy */
+			test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+			stptr = cs->stlist;
+			while (stptr != NULL) {
+				stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+				stptr = stptr->next;
+			}
+		} else {
+			/* discard frame; reset transceiver */
+			test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			} else {
+				printk(KERN_WARNING "HiSax: W6692 D-Channel Busy no skb\n");
+				debugl1(cs, "D-Channel Busy no skb");
+			}
+			cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_XRST);	/* Transmitter reset */
+			spin_unlock_irqrestore(&cs->lock, flags);
+			cs->irq_func(cs->irq, cs, NULL);
+			return;
+		}
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+static void
+W6692Bmode(struct BCState *bcs, int mode, int bchan)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "w6692 %c mode %d ichan %d",
+			'1' + bchan, mode, bchan);
+	bcs->mode = mode;
+	bcs->channel = bchan;
+	bcs->hw.w6692.bchan = bchan;
+
+	switch (mode) {
+		case (L1_MODE_NULL):
+			cs->BC_Write_Reg(cs, bchan, W_B_MODE, 0);
+			break;
+		case (L1_MODE_TRANS):
+			cs->BC_Write_Reg(cs, bchan, W_B_MODE, W_B_MODE_MMS);
+			break;
+		case (L1_MODE_HDLC):
+			cs->BC_Write_Reg(cs, bchan, W_B_MODE, W_B_MODE_ITF);
+			cs->BC_Write_Reg(cs, bchan, W_B_ADM1, 0xff);
+			cs->BC_Write_Reg(cs, bchan, W_B_ADM2, 0xff);
+			break;
+	}
+	if (mode)
+		cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_RRST |
+				 W_B_CMDR_RACT | W_B_CMDR_XRST);
+	cs->BC_Write_Reg(cs, bchan, W_B_EXIM, 0x00);
+}
+
+static void
+W6692_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct BCState *bcs = st->l1.bcs; 
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->hw.w6692.count = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "W6692_l2l1: this shouldn't happen\n");
+				break;
+			}
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->tx_skb = skb;
+			bcs->hw.w6692.count = 0;
+			bcs->cs->BC_Send_Data(bcs);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			W6692Bmode(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			W6692Bmode(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+static void
+close_w6692state(struct BCState *bcs)
+{
+	W6692Bmode(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.w6692.rcvbuf) {
+			kfree(bcs->hw.w6692.rcvbuf);
+			bcs->hw.w6692.rcvbuf = NULL;
+		}
+		if (bcs->blog) {
+			kfree(bcs->blog);
+			bcs->blog = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+static int
+open_w6692state(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.w6692.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for w6692.rcvbuf\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			return (1);
+		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for bcs->blog\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.w6692.rcvbuf);
+			bcs->hw.w6692.rcvbuf = NULL;
+			return (2);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.w6692.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+static int
+setstack_w6692(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_w6692state(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = W6692_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void resetW6692(struct IsdnCardState *cs)
+{
+	cs->writeW6692(cs, W_D_CTL, W_D_CTL_SRST);
+	mdelay(10);
+	cs->writeW6692(cs, W_D_CTL, 0x00);
+	mdelay(10);
+	cs->writeW6692(cs, W_IMASK, 0xff);
+	cs->writeW6692(cs, W_D_SAM, 0xff);
+	cs->writeW6692(cs, W_D_TAM, 0xff);
+	cs->writeW6692(cs, W_D_EXIM, 0x00);
+	cs->writeW6692(cs, W_D_MODE, W_D_MODE_RACT);
+	cs->writeW6692(cs, W_IMASK, 0x18);
+	if (cs->subtyp == W6692_USR) {
+		/* seems that USR implemented some power control features
+		 * Pin 79 is connected to the oscilator circuit so we
+		 * have to handle it here
+		 */
+		cs->writeW6692(cs, W_PCTL, 0x80);
+		cs->writeW6692(cs, W_XDATA, 0x00);
+	}
+}
+
+void __init initW6692(struct IsdnCardState *cs, int part)
+{
+	if (part & 1) {
+		cs->setstack_d = setstack_W6692;
+		cs->DC_Close = DC_Close_W6692;
+		cs->dbusytimer.function = (void *) dbusy_timer_handler;
+		cs->dbusytimer.data = (long) cs;
+		init_timer(&cs->dbusytimer);
+		resetW6692(cs);
+		ph_command(cs, W_L1CMD_RST);
+		cs->dc.w6692.ph_state = W_L1CMD_RST;
+		W6692_new_ph(cs);
+		ph_command(cs, W_L1CMD_ECK);
+
+		cs->bcs[0].BC_SetStack = setstack_w6692;
+		cs->bcs[1].BC_SetStack = setstack_w6692;
+		cs->bcs[0].BC_Close = close_w6692state;
+		cs->bcs[1].BC_Close = close_w6692state;
+		W6692Bmode(cs->bcs, 0, 0);
+		W6692Bmode(cs->bcs + 1, 0, 0);
+	}
+	if (part & 2) {
+		/* Reenable all IRQ */
+		cs->writeW6692(cs, W_IMASK, 0x18);
+		cs->writeW6692(cs, W_D_EXIM, 0x00);
+		cs->BC_Write_Reg(cs, 0, W_B_EXIM, 0x00);
+		cs->BC_Write_Reg(cs, 1, W_B_EXIM, 0x00);
+		/* Reset D-chan receiver and transmitter */
+		cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST);
+	}
+}
+
+/* Interface functions */
+
+static u_char
+ReadW6692(struct IsdnCardState *cs, u_char offset)
+{
+	return (inb(cs->hw.w6692.iobase + offset));
+}
+
+static void
+WriteW6692(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	outb(value, cs->hw.w6692.iobase + offset);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	insb(cs->hw.w6692.iobase + W_D_RFIFO, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	outsb(cs->hw.w6692.iobase + W_D_XFIFO, data, size);
+}
+
+static u_char
+ReadW6692B(struct IsdnCardState *cs, int bchan, u_char offset)
+{
+	return (inb(cs->hw.w6692.iobase + (bchan ? 0x40 : 0) + offset));
+}
+
+static void
+WriteW6692B(struct IsdnCardState *cs, int bchan, u_char offset, u_char value)
+{
+	outb(value, cs->hw.w6692.iobase + (bchan ? 0x40 : 0) + offset);
+}
+
+static int
+w6692_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			resetW6692(cs);
+			return (0);
+		case CARD_RELEASE:
+			cs->writeW6692(cs, W_IMASK, 0xff);
+			release_region(cs->hw.w6692.iobase, 256);
+			if (cs->subtyp == W6692_USR) {
+				cs->writeW6692(cs, W_XDATA, 0x04);
+			}
+			return (0);
+		case CARD_INIT:
+			initW6692(cs, 3);
+			return (0);
+		case CARD_TEST:
+			return (0);
+	}
+	return (0);
+}
+
+static int id_idx ;
+
+static struct pci_dev *dev_w6692 __initdata = NULL;
+
+int __init 
+setup_w6692(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	u_char found = 0;
+	u_char pci_irq = 0;
+	u_int pci_ioaddr = 0;
+
+	strcpy(tmp, w6692_revision);
+	printk(KERN_INFO "HiSax: W6692 driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_W6692)
+		return (0);
+#ifdef CONFIG_PCI
+	while (id_list[id_idx].vendor_id) {
+		dev_w6692 = pci_find_device(id_list[id_idx].vendor_id,
+					    id_list[id_idx].device_id,
+					    dev_w6692);
+		if (dev_w6692) {
+			if (pci_enable_device(dev_w6692))
+				continue;
+			cs->subtyp = id_idx;
+			break;
+		}
+		id_idx++;
+	}
+	if (dev_w6692) {
+		found = 1;
+		pci_irq = dev_w6692->irq;
+		/* I think address 0 is allways the configuration area */
+		/* and address 1 is the real IO space KKe 03.09.99 */
+		pci_ioaddr = pci_resource_start(dev_w6692, 1);
+		/* USR ISDN PCI card TA need some special handling */
+		if (cs->subtyp == W6692_WINBOND) {
+			if ((W6692_SV_USR == dev_w6692->subsystem_vendor) &&
+			    (W6692_SD_USR == dev_w6692->subsystem_device)) {
+				cs->subtyp = W6692_USR;
+			}
+		}
+	}
+	if (!found) {
+		printk(KERN_WARNING "W6692: No PCI card found\n");
+		return (0);
+	}
+	cs->irq = pci_irq;
+	if (!cs->irq) {
+		printk(KERN_WARNING "W6692: No IRQ for PCI card found\n");
+		return (0);
+	}
+	if (!pci_ioaddr) {
+		printk(KERN_WARNING "W6692: NO I/O Base Address found\n");
+		return (0);
+	}
+	cs->hw.w6692.iobase = pci_ioaddr;
+	printk(KERN_INFO "Found: %s %s, I/O base: 0x%x, irq: %d\n",
+	       id_list[cs->subtyp].vendor_name, id_list[cs->subtyp].card_name,
+	       pci_ioaddr, pci_irq);
+	if (!request_region(cs->hw.w6692.iobase, 256, id_list[cs->subtyp].card_name)) {
+		printk(KERN_WARNING
+		       "HiSax: %s I/O ports %x-%x already in use\n",
+		       id_list[cs->subtyp].card_name,
+		       cs->hw.w6692.iobase,
+		       cs->hw.w6692.iobase + 255);
+		return (0);
+	}
+#else
+	printk(KERN_WARNING "HiSax: W6692 and NO_PCI_BIOS\n");
+	printk(KERN_WARNING "HiSax: W6692 unable to config\n");
+	return (0);
+#endif				/* CONFIG_PCI */
+
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d I/O:%x\n",
+	       id_list[cs->subtyp].card_name, cs->irq,
+	       cs->hw.w6692.iobase);
+
+	INIT_WORK(&cs->tqueue, (void *)(void *) W6692_bh, cs);
+	cs->readW6692 = &ReadW6692;
+	cs->writeW6692 = &WriteW6692;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadW6692B;
+	cs->BC_Write_Reg = &WriteW6692B;
+	cs->BC_Send_Data = &W6692B_fill_fifo;
+	cs->cardmsg = &w6692_card_msg;
+	cs->irq_func = &W6692_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+	W6692Version(cs, "W6692:");
+	printk(KERN_INFO "W6692 ISTA=0x%X\n", ReadW6692(cs, W_ISTA));
+	printk(KERN_INFO "W6692 IMASK=0x%X\n", ReadW6692(cs, W_IMASK));
+	printk(KERN_INFO "W6692 D_EXIR=0x%X\n", ReadW6692(cs, W_D_EXIR));
+	printk(KERN_INFO "W6692 D_EXIM=0x%X\n", ReadW6692(cs, W_D_EXIM));
+	printk(KERN_INFO "W6692 D_RSTA=0x%X\n", ReadW6692(cs, W_D_RSTA));
+	return (1);
+}
diff --git a/drivers/isdn/hisax/w6692.h b/drivers/isdn/hisax/w6692.h
new file mode 100644
index 000000000000..c79c81e0401f
--- /dev/null
+++ b/drivers/isdn/hisax/w6692.h
@@ -0,0 +1,184 @@
+/* $Id: w6692.h,v 1.4.2.2 2004/01/12 22:52:29 keil Exp $
+ *
+ * Winbond W6692 specific defines
+ *
+ * Author       Petr Novak
+ * Copyright    by 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.
+ *
+ */
+
+/* map W6692 functions to ISAC functions */
+#define readW6692	readisac
+#define writeW6692	writeisac
+#define	readW6692fifo	readisacfifo
+#define	writeW6692fifo	writeisacfifo
+
+/* B-channel FIFO read/write routines */
+
+#define READW6692BFIFO(cs,bchan,ptr,count) \
+	insb(cs->hw.w6692.iobase+W_B_RFIFO+(bchan?0x40:0),ptr,count)
+
+#define WRITEW6692BFIFO(cs,bchan,ptr,count) \
+	outsb(cs->hw.w6692.iobase+W_B_XFIFO+(bchan?0x40:0),ptr,count)
+
+/* 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_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