## Automatically generated incremental diff
## From:   linux-2.2.12-pre3
## To:     linux-2.2.12-pre4
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp hpa $

diff -urN linux-2.2.12-pre3/arch/alpha/kernel/alpha_ksyms.c linux-2.2.12-pre4/arch/alpha/kernel/alpha_ksyms.c
--- linux-2.2.12-pre3/arch/alpha/kernel/alpha_ksyms.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/arch/alpha/kernel/alpha_ksyms.c	2003-08-15 15:06:54.000000000 -0700
@@ -91,6 +91,8 @@
 EXPORT_SYMBOL(start_thread);
 EXPORT_SYMBOL(alpha_read_fp_reg);
 EXPORT_SYMBOL(alpha_write_fp_reg);
+EXPORT_SYMBOL(alpha_read_fp_reg_s);
+EXPORT_SYMBOL(alpha_write_fp_reg_s);
 
 /* In-kernel system calls.  */
 EXPORT_SYMBOL(__kernel_thread);
diff -urN linux-2.2.12-pre3/arch/sparc/defconfig linux-2.2.12-pre4/arch/sparc/defconfig
--- linux-2.2.12-pre3/arch/sparc/defconfig	1999-08-09 12:05:09.000000000 -0700
+++ linux-2.2.12-pre4/arch/sparc/defconfig	2003-08-15 15:06:54.000000000 -0700
@@ -305,6 +305,7 @@
 # CONFIG_NLS_ISO8859_7 is not set
 # CONFIG_NLS_ISO8859_8 is not set
 # CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_14 is not set
 # CONFIG_NLS_ISO8859_15 is not set
 # CONFIG_NLS_KOI8_R is not set
 
diff -urN linux-2.2.12-pre3/arch/sparc/mm/iommu.c linux-2.2.12-pre4/arch/sparc/mm/iommu.c
--- linux-2.2.12-pre3/arch/sparc/mm/iommu.c	1999-05-11 08:24:31.000000000 -0700
+++ linux-2.2.12-pre4/arch/sparc/mm/iommu.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: iommu.c,v 1.10 1999/05/07 17:03:34 jj Exp $
+/* $Id: iommu.c,v 1.10.2.1 1999/08/13 12:35:35 davem Exp $
  * iommu.c:  IOMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
@@ -225,6 +225,11 @@
 			pmd_t *pmdp;
 			pte_t *ptep;
 
+			if (viking_mxcc_present)
+				viking_mxcc_flush_page(page);
+			else if (viking_flush)
+				viking_flush_page(page);
+
 			pgdp = pgd_offset(init_task.mm, addr);
 			pmdp = pmd_offset(pgdp, addr);
 			ptep = pte_offset(pmdp, addr);
diff -urN linux-2.2.12-pre3/arch/sparc64/defconfig linux-2.2.12-pre4/arch/sparc64/defconfig
--- linux-2.2.12-pre3/arch/sparc64/defconfig	1999-08-09 12:05:09.000000000 -0700
+++ linux-2.2.12-pre4/arch/sparc64/defconfig	2003-08-15 15:06:54.000000000 -0700
@@ -349,6 +349,7 @@
 # CONFIG_NLS_ISO8859_7 is not set
 # CONFIG_NLS_ISO8859_8 is not set
 # CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_14 is not set
 # CONFIG_NLS_ISO8859_15 is not set
 # CONFIG_NLS_KOI8_R is not set
 
diff -urN linux-2.2.12-pre3/arch/sparc64/kernel/ioctl32.c linux-2.2.12-pre4/arch/sparc64/kernel/ioctl32.c
--- linux-2.2.12-pre3/arch/sparc64/kernel/ioctl32.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/arch/sparc64/kernel/ioctl32.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.62.2.1 1999/06/09 04:53:03 davem Exp $
+/* $Id: ioctl32.c,v 1.62.2.2 1999/08/13 18:28:25 davem Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
diff -urN linux-2.2.12-pre3/arch/sparc64/kernel/process.c linux-2.2.12-pre4/arch/sparc64/kernel/process.c
--- linux-2.2.12-pre3/arch/sparc64/kernel/process.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/arch/sparc64/kernel/process.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.92 1999/05/08 23:04:48 davem Exp $
+/*  $Id: process.c,v 1.92.2.1 1999/08/12 11:30:31 davem Exp $
  *  arch/sparc64/kernel/process.c
  *
  *  Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu)
diff -urN linux-2.2.12-pre3/Documentation/Configure.help linux-2.2.12-pre4/Documentation/Configure.help
--- linux-2.2.12-pre3/Documentation/Configure.help	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/Documentation/Configure.help	2003-08-15 15:06:54.000000000 -0700
@@ -9873,6 +9873,12 @@
   you compiled aedsp16.o as a module you can specify this parameter as
   'mpu_irq=NN'.
 
+SGI Visual Workstation on-board audio
+CONFIG_SOUND_VWSND
+  Say Y or M if you have an SGI Visual Workstation and you want to
+  be able to use its on-board audio.  Read Documentation/sound/visws
+  for more info on this driver's capabilities.
+
 Ensoniq ES1370 based PCI sound cards
 CONFIG_SOUND_ES1370
   Say Y or M if you have a PCI sound card utilizing the Ensoniq
@@ -10301,6 +10307,30 @@
   you need to have access to a machine on the Internet that has a
   program like lynx or netscape).
 
+Eicon.Diehl active card support
+CONFIG_ISDN_DRV_EICON
+  Say Y here if you have an Eicon active ISDN card. In order to use
+  this card, additional firmware is necessary, which has to be loaded
+  into the card using the eiconctrl utility which is part of the latest
+  isdn4k-utils package. Please read the file
+  Documentation/isdn/README.eicon for more information.
+  
+Eicon old-type card support
+CONFIG_ISDN_DRV_EICON_ISA
+  Say Y here if you have an old-type Eicon active ISDN card. In order to
+  use this card, additional firmware is necessary, which has to be loaded
+  into the card using the eiconctrl utility which is part of the latest
+  isdn4k-utils package. Please read the file
+  Documentation/isdn/README.eicon for more information.
+
+Support AT-Fax Class 2 commands
+CONFIG_ISDN_TTY_FAX
+  If you say Y here, the modem-emulator will support a subset of the
+  Fax Class 2 commands. Using a getty with fax-support
+  (mgetty+sendfax, hylafax), you will be able to use your Linux box 
+  as an ISDN-fax-machine. This must be supported by the lowlevel driver
+  also. See Documentation/isdn/README.fax for more information.
+
 AVM-B1 with CAPI2.0 support
 CONFIG_ISDN_DRV_AVMB1
   This enables support for the AVM B1 ISDN networking cards. In
diff -urN linux-2.2.12-pre3/Documentation/isdn/HiSax.cert linux-2.2.12-pre4/Documentation/isdn/HiSax.cert
--- linux-2.2.12-pre3/Documentation/isdn/HiSax.cert	1999-08-09 12:04:38.000000000 -0700
+++ linux-2.2.12-pre4/Documentation/isdn/HiSax.cert	2003-08-15 15:06:54.000000000 -0700
@@ -14,7 +14,8 @@
 
 However, if you wish to modify the HiSax sources, please note the following:
 
-HiSax has passed the ITU approval test suite with ELSA Quickstep ISDN cards.
+HiSax has passed the ITU approval test suite with ELSA Quickstep ISDN cards
+and Eicon Technology Diva 2.01 PCI card.
 The certification is only valid for the combination of the tested software
 version and the tested hardware. Any changes to the HiSax source code may
 therefore affect the certification.
@@ -48,13 +49,14 @@
 drivers/isdn/hisax/l3_1tr6.c
 drivers/isdn/hisax/cert.c
 drivers/isdn/hisax/elsa.c
+drivers/isdn/hisax/diva.c
 
 Please send any changes, bugfixes and patches to me rather than implementing
 them directly into the HiSax sources.
 
 This does not reduce your rights granted by the GNU General Public License.
 If you wish to change the sources, go ahead; but note that then the
-certification is invalid even if you use ELSA Quickstep cards.
+certification is invalid even if you use one of the approved cards.
 
 Here are the certification registration numbers for ELSA Quickstep cards:
 German   D133361J CETECOM ICT Services GmbH 0682
@@ -68,9 +70,9 @@
 Version: 2.6.3i
 Charset: noconv
 
-iQCVAwUBNj5OKDpxHvX/mS9tAQFHuQP/WeImlqCcDZ2d132yAvRBWFULlJoSf1P/
-c1lVTeaWvsSaY5Cu9hrKhXXhPzeEaitUbcUBPXdpzFWCA5CE902lnz7AhgRC+HF1
-0qiKgkZZyc/5HKasFymR1+IWSLw30GesP3Di/ZMR3NJi8SlY9PIjx7hnEMunGSRO
-1ufPvfWWuu8=
-=nGJk
+iQCVAwUBN6xoKTpxHvX/mS9tAQF4DAP/efRWym6jvNOND1O9eaEFdP5fd2xKB3XD
+Ifh6Iv0DvARcIuxXtEjT+z3FjjQk35eo/wX4C4tpRhYQYdgCxl+iv+5DzhVDpB95
+3QS9E5m0E1eIK3t8XiQTRgb+1JPCMYUThCrakYsX25o3ndGKyDipsCTfkyR38XwC
+bUyTfcOYKAk=
+=VKyL
 -----END PGP SIGNATURE-----
diff -urN linux-2.2.12-pre3/Documentation/isdn/INTERFACE linux-2.2.12-pre4/Documentation/isdn/INTERFACE
--- linux-2.2.12-pre3/Documentation/isdn/INTERFACE	1999-08-09 12:04:38.000000000 -0700
+++ linux-2.2.12-pre4/Documentation/isdn/INTERFACE	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-$Id: INTERFACE,v 1.12 1999/07/13 20:59:59 werner Exp $
+$Id: INTERFACE,v 1.13 1999/08/11 20:30:26 armin Exp $
 
 Description of the Interface between Linklevel and Hardwarelevel
   of isdn4linux:
@@ -216,7 +216,7 @@
 
    Until now, the following commands are defined:
 
-***CHANGEI1.34: The parameter "num" has been replaced by a union "para" containing
+***CHANGEI1.34: The parameter "num" has been replaced by a union "parm" containing
                 the old "num" and a new setup_type struct used for ISDN_CMD_DIAL
                 and ISDN_STAT_ICALL callback.
 
@@ -235,7 +235,7 @@
        driver   = driver-Id.
        command  = ISDN_CMD_IOCTL
        arg      = Original ioctl-cmd - IIOCDRVCTL
-       para.num = first bytes filled with (unsigned long)arg
+       parm.num = first bytes filled with (unsigned long)arg
    
      Returnvalue:
        Depending on driver.
@@ -251,10 +251,10 @@
       command     = ISDN_CMD_DIAL
       arg         = channel-number locally to the driver. (starting with 0)
       
-      para.setup.phone  = An ASCII-String containing the number to dial.
-      para.setup.eazmsn = An ASCII-Sting containing the own EAZ or MSN.
-      para.setup.si1    = The Service-Indicator.
-      para.setup.si2    = Additional Service-Indicator.
+      parm.setup.phone  = An ASCII-String containing the number to dial.
+      parm.setup.eazmsn = An ASCII-Sting containing the own EAZ or MSN.
+      parm.setup.si1    = The Service-Indicator.
+      parm.setup.si2    = Additional Service-Indicator.
 
                     If the Line has been designed as SPV (a special german
                     feature, meaning semi-leased-line) the phone has to
@@ -272,7 +272,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_ACCEPTD
       arg         = channel-number locally to the driver. (starting with 0)
-      para        = unused.
+      parm        = unused.
 
   ISDN_CMD_ACCEPTB:
 
@@ -283,7 +283,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_ACCEPTB
       arg         = channel-number locally to the driver. (starting with 0)
-      para        = unused.
+      parm        = unused.
 
   ISDN_CMD_HANGUP:
 
@@ -295,7 +295,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_HANGUP
       arg         = channel-number locally to the driver. (starting with 0)
-      para        = unused.
+      parm        = unused.
 
   ISDN_CMD_CLREAZ:
 
@@ -306,7 +306,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_CLREAZ
       arg         = channel-number locally to the driver. (starting with 0)
-      para        = unused.
+      parm        = unused.
 
   ISDN_CMD_SETEAZ:
 
@@ -317,7 +317,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_SETEAZ
       arg         = channel-number locally to the driver. (starting with 0)
-      para.num    = ASCII-String, containing the desired EAZ's/MSN's
+      parm.num    = ASCII-String, containing the desired EAZ's/MSN's
                     (comma-separated). If an empty String is given, the
                     HL-driver should respond to ALL incoming calls,
                     regardless of the destination-address.
@@ -332,7 +332,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_GETEAZ
       arg         = channel-number locally to the driver. (starting with 0)
-      para.num    = ASCII-String, containing the current EAZ's/MSN's
+      parm.num    = ASCII-String, containing the current EAZ's/MSN's
 
   ISDN_CMD_SETSIL: (currently unused)
 
@@ -343,7 +343,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_SETSIL
       arg         = channel-number locally to the driver. (starting with 0)
-      para.num    = ASCII-String, containing the desired Service-Indicators.
+      parm.num    = ASCII-String, containing the desired Service-Indicators.
 
   ISDN_CMD_GETSIL: (currently unused)
 
@@ -354,7 +354,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_SETSIL
       arg         = channel-number locally to the driver. (starting with 0)
-      para.num    = ASCII-String, containing the current Service-Indicators.
+      parm.num    = ASCII-String, containing the current Service-Indicators.
 
   ISDN_CMD_SETL2:
 
@@ -369,7 +369,7 @@
       arg         = channel-number locally to the driver. (starting with 0)
                     logical or'ed with (protocol-Id << 8)
                     protocol-Id is one of the constants ISDN_PROTO_L2...
-      para        = unused.
+      parm        = unused.
 
   ISDN_CMD_GETL2: (currently unused)
 
@@ -380,7 +380,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_GETL2
       arg         = channel-number locally to the driver. (starting with 0)
-      para        = unused.
+      parm        = unused.
     Returnvalue:
       current protocol-Id (one of the constants ISDN_L2_PROTO)
 
@@ -397,7 +397,7 @@
       arg         = channel-number locally to the driver. (starting with 0)
                     logical or'ed with (protocol-Id << 8)
                     protocol-Id is one of the constants ISDN_PROTO_L3...
-      para        = unused.
+      parm.fax    = Pointer to T30_s fax struct. (fax usage only)
 
   ISDN_CMD_GETL2: (currently unused)
 
@@ -408,7 +408,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_GETL3
       arg         = channel-number locally to the driver. (starting with 0)
-      para        = unused.
+      parm        = unused.
     Returnvalue:
       current protocol-Id (one of the constants ISDN_L3_PROTO)
 
@@ -422,7 +422,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_LOCK
       arg         = unused.
-      para        = unused.
+      parm        = unused.
 
   ISDN_CMD_UNLOCK:
 
@@ -434,7 +434,19 @@
       driver      = driver-Id.
       command     = ISDN_CMD_UNLOCK
       arg         = unused.
-      para        = unused.
+      parm        = unused.
+
+  ISDN_CMD_FAXCMD:
+
+    With this command the HL-driver receives a fax sub-command.
+    For details refer to INTERFACE.fax
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_FAXCMD
+      arg         = channel-number locally to the driver. (starting with 0)
+      parm        = unused.
+
 
 3. Description of the events to be signaled by the HL-driver to the LL.
 
@@ -456,7 +468,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_STAVAIL
       arg         = length of available data.
-      para        = unused.
+      parm        = unused.
 
   ISDN_STAT_ICALL:
 
@@ -466,12 +478,12 @@
       driver            = driver-Id
       command           = ISDN_STAT_ICALL
       arg               = channel-number, locally to the driver. (starting with 0)
-      para.setup.phone  = Callernumber.
-      para.setup.eazmsn = CalledNumber.
-      para.setup.si1    = Service Indicator.
-      para.setup.si2    = Additional Service Indicator.
-      para.setup.plan   = octet 3 from Calling party number Information Element.
-      para.setup.screen = octet 3a from Calling party number Information Element.
+      parm.setup.phone  = Callernumber.
+      parm.setup.eazmsn = CalledNumber.
+      parm.setup.si1    = Service Indicator.
+      parm.setup.si2    = Additional Service Indicator.
+      parm.setup.plan   = octet 3 from Calling party number Information Element.
+      parm.setup.screen = octet 3a from Calling party number Information Element.
 
     Return:
       0           = No device matching this call.
@@ -497,7 +509,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_RUN
       arg         = unused.
-      para        = unused.
+      parm        = unused.
 
   ISDN_STAT_STOP:
 
@@ -508,7 +520,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_STOP
       arg         = unused.
-      para        = unused.
+      parm        = unused.
 
   ISDN_STAT_DCONN:
 
@@ -519,7 +531,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_DCONN
       arg         = channel-number, locally to the driver. (starting with 0)
-      para        = unused.
+      parm        = unused.
 
   ISDN_STAT_BCONN:
 
@@ -534,7 +546,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_BCONN
       arg         = channel-number, locally to the driver. (starting with 0)
-      para.num    = ASCII-String, containing type of connection (for analog
+      parm.num    = ASCII-String, containing type of connection (for analog
 		    modem only). This will be appended to the CONNECT message
 		    e.g. 14400/V.32bis
 
@@ -549,7 +561,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_DHUP
       arg         = channel-number, locally to the driver. (starting with 0)
-      para        = unused.
+      parm        = unused.
 
   ISDN_STAT_BHUP:
 
@@ -564,7 +576,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_BHUP
       arg         = channel-number, locally to the driver. (starting with 0)
-      para        = unused.
+      parm        = unused.
 
   ISDN_STAT_CINF:
 
@@ -575,7 +587,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_CINF
       arg         = channel-number, locally to the driver. (starting with 0)
-      para.num    = ASCII string containing charge-units (digits only).
+      parm.num    = ASCII string containing charge-units (digits only).
 
   ISDN_STAT_LOAD: (currently unused)
 
@@ -588,7 +600,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_UNLOAD
       arg         = unused.
-      para        = unused.
+      parm        = unused.
 
   ISDN_STAT_BSENT:
 
@@ -600,7 +612,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_BSENT
       arg         = channel-number, locally to the driver. (starting with 0)
-      para.length = ***CHANGEI.1.21: New field.
+      parm.length = ***CHANGEI.1.21: New field.
 		    the driver has to set this to the original length
 		    of the skb at the time of receiving it from the linklevel.
 
@@ -613,21 +625,21 @@
       driver      = driver-Id
       command     = ISDN_STAT_NODCH
       arg         = channel-number, locally to the driver. (starting with 0)
-      para        = unused.
+      parm        = unused.
 
-  ISDN_STAT_ADDCH: (currently unused)
+  ISDN_STAT_ADDCH: 
 
-    This call is planned for HL-drivers, which are unable to check card-type
+    This call is for HL-drivers, which are unable to check card-type
     or numbers of supported channels before they have loaded any firmware
-    using ioctl. Those HL-driver simply set the channel-parameter to zero
-    or a minimum channel-number when registering, and later if they know
+    using ioctl. Those HL-driver simply set the channel-parameter to a
+    minimum channel-number when registering, and later if they know
     the real amount, perform this call, allocating additional channels.
 
     Parameter:
       driver      = driver-Id
       command     = ISDN_STAT_ADDCH
-      arg         = to be defined.
-      para        = to be defined.
+      arg         = number of channels to be added.
+      parm        = unused.
 
   ISDN_STAT_CAUSE:
 
@@ -640,7 +652,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_NODCH
       arg         = channel-number, locally to the driver. (starting with 0)
-      para.num    = ASCII string containing CAUSE-message.
+      parm.num    = ASCII string containing CAUSE-message.
 
   ISDN_STAT_L1ERR:
 
@@ -653,7 +665,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_L1ERR
       arg         = channel-number, locally to the driver. (starting with 0)
-      para.errcode= ISDN_STAT_L1ERR_SEND:     Packet lost while sending.
+      parm.errcode= ISDN_STAT_L1ERR_SEND:     Packet lost while sending.
 		    ISDN_STAT_L1ERR_RECV:     Packet lost while receiving.
   ISDN_STAT_DISCH:
 
@@ -672,4 +684,14 @@
       arg         = channel-number, locally to the driver. (starting with 0)
       parm.num[0] = 0 if channel shall be disabled, else enabled.
     
+  ISDN_STAT_FAXIND:
+
+    With this call the HL-driver signals a fax sub-command to the LL.
+    For details refer to INTERFACE.fax
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_STAT_FAXIND
+      arg         = channel-number, locally to the driver. (starting with 0)
+      parm        = unused.
 
diff -urN linux-2.2.12-pre3/Documentation/isdn/INTERFACE.fax linux-2.2.12-pre4/Documentation/isdn/INTERFACE.fax
--- linux-2.2.12-pre3/Documentation/isdn/INTERFACE.fax	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.12-pre4/Documentation/isdn/INTERFACE.fax	2003-08-15 15:06:54.000000000 -0700
@@ -0,0 +1,163 @@
+$Id: INTERFACE.fax,v 1.1 1999/08/11 20:30:28 armin Exp $
+
+
+Description of the fax-subinterface between linklevel and hardwarelevel of 
+  isdn4linux. 
+
+  The communication between linklevel (LL) and harwarelevel (HL) for fax
+  is based on the struct T30_s (defined in isdnif.h).
+  This struct is allocated in the LL.  
+  In order to use fax, the LL provides the pointer to this struct with the 
+  command ISDN_CMD_SETL3 (parm.fax). This pointer expires in case of hangup 
+  and when a new channel to a new connection is assigned. 
+
+
+Data handling:
+  In send-mode the HL-driver has to handle the <DLE> codes and the bit-order 
+  conversion by itself. 
+  In receive-mode the LL-driver takes care of the bit-order conversion
+  (specified by +FBOR)
+
+Structure T30_s description:
+
+  This structure stores the values (set by AT-commands), the remote-
+  capability-values and the command-codes between LL and HL.
+
+  If the HL-driver receives ISDN_CMD_FAXCMD, all needed information
+  is in this struct set by the LL.
+  To signal information to the LL, the HL-driver has to set the 
+  the parameters and use ISDN_STAT_FAXIND.
+  (Please refer to INTERFACE)
+
+Structure T30_s:
+
+  All members are 8-bit unsigned (__u8)
+
+  -  resolution     
+  -  rate
+  -  width
+  -  length
+  -  compression
+  -  ecm
+  -  binary
+  -  scantime
+  -  id[]
+  Local faxmachine's parameters, set by +FDIS, +FDCS, +FLID, ...
+
+  -  r_resolution
+  -  r_rate
+  -  r_width
+  -  r_length
+  -  r_compression
+  -  r_ecm
+  -  r_binary
+  -  r_scantime
+  -  r_id[]
+  Remote faxmachine's parameters. To be set by HL-driver.
+
+  -  phase      
+  Defines the actual state of fax connection. Set by HL or LL
+  depending on progress and type of connection.
+  If the phase changes because of an AT command, the LL driver
+  changes this value. Otherwise the HL-driver takes care of it, but
+  only neccessary on call establishment (from IDLE to PHASE_A).
+  (one of the constants ISDN_FAX_PHASE_[IDLE,A,B,C,D,E])
+
+  -  direction
+  Defines outgoing/send or incoming/receive connection.
+  (ISDN_TTY_FAX_CONN_[IN,OUT])
+
+  -  code
+  Commands from LL to HL; possible constants : 
+      ISDN_TTY_FAX_DR        signals +FDR command to HL
+
+      ISDN_TTY_FAX_DT        signals +FDT command to HL 
+
+      ISDN_TTY_FAX_ET        signals +FET command to HL
+
+
+  Other than that the "code" is set with the hangup-code value at
+  the end of connection for the +FHNG message.
+        
+  -  r_code 
+  Commands from HL to LL; possible constants :
+      ISDN_TTY_FAX_CFR       output of +FCFR message. 
+
+      ISDN_TTY_FAX_RID       output of remote ID set in r_id[]
+                             (+FCSI/+FTSI on send/receive)
+
+      ISDN_TTY_FAX_DCS       output of +FDCS and CONNECT message,
+                             switching to phase C.
+
+      ISDN_TTY_FAX_ET        signals end of data,
+                             switching to phase D.
+
+      ISDN_TTY_FAX_FCON      signals the established, outgoing connection,
+                             switching to phase B.
+
+      ISDN_TTY_FAX_FCON_I    signals the established, incoming connection,
+                             switching to phase B.
+
+      ISDN_TTY_FAX_DIS       output of +FDIS message and values.
+
+      ISDN_TTY_FAX_SENT      signals that all data has been sent 
+                             and <DLE><ETX> is acknowledged,
+                             OK message will be sent.
+
+      ISDN_TTY_FAX_PTS       signals a msg-confirmation (page sent successful),
+                             depending on fet value:
+                             0: output OK message (more pages follow)
+                             1: switching to phase B (next document)
+
+      ISDN_TTY_FAX_TRAIN_OK  output of +FDCS and OK message (for receive mode).
+
+      ISDN_TTY_FAX_EOP       signals end of data in receive mode,
+                             switching to phase D.
+
+      ISDN_TTY_FAX_HNG       output of the +FHNG and value set by code and
+                             OK message, switching to phase E.
+
+
+  -  badlin
+  Value of +FBADLIN  
+
+  -  badmul
+  Value of +FBADMUL
+
+  -  bor
+  Value of +FBOR
+
+  -  fet
+  Value of +FET command in send-mode.
+  Set by HL in receive-mode for +FET message.
+
+  -  pollid[]  
+  ID-string, set by +FCIG
+
+  -  cq
+  Value of +FCQ
+
+  -  cr
+  Value of +FCR
+
+  -  ctcrty
+  Value of +FCTCRTY
+
+  -  minsp
+  Value of +FMINSP
+
+  -  phcto
+  Value of +FPHCTO
+
+  -  rel
+  Value of +FREL
+
+  -  nbc
+  Value of +FNBC (0,1)
+  (+FNBC is not a known class 2 fax command, I added this to change the
+   automatic "best capabilities" connection in the eicon HL-driver)
+
+  
+Armin
+mac@melware.de
+
diff -urN linux-2.2.12-pre3/Documentation/isdn/README linux-2.2.12-pre4/Documentation/isdn/README
--- linux-2.2.12-pre3/Documentation/isdn/README	1999-08-09 12:04:38.000000000 -0700
+++ linux-2.2.12-pre4/Documentation/isdn/README	2003-08-15 15:06:54.000000000 -0700
@@ -149,6 +149,8 @@
                AT&X0    BTX-mode and T.70-mode off (default)
                AT&X1    BTX-mode on. (S13.1=1, S13.5=0 S14=0, S16=7, S18=7, S19=0)
                AT&X2    T.70-mode on. (S13.1=1, S13.5=1, S14=0, S16=7, S18=7, S19=0)
+               AT+Rx    Resume a suspended call with CallID x (x = 1,2,3...)
+               AT+Sx    Suspend a call with CallID x (x = 1,2,3...)
 
            For voice-mode commands refer to README.audio
 
diff -urN linux-2.2.12-pre3/Documentation/isdn/README.diversion linux-2.2.12-pre4/Documentation/isdn/README.diversion
--- linux-2.2.12-pre3/Documentation/isdn/README.diversion	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.12-pre4/Documentation/isdn/README.diversion	2003-08-15 15:06:54.000000000 -0700
@@ -0,0 +1,127 @@
+The isdn diversion services are a supporting module working together with
+the isdn4linux and the HiSax module for passive cards. 
+Active cards, TAs and cards using a own or other driver than the HiSax 
+module need to be adapted to the HL<->LL interface described in a separate 
+document. The diversion services may be used with all cards supported by 
+the HiSax driver.
+The diversion kernel interface and controlling tool divertctrl were written
+by Werner Cornelius (werner@isdn4linux.de or werner@titro.de) under the
+GNU Public License.
+
+    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.
+
+Table of contents
+=================
+
+1. Features of the i4l diversion services 
+   (Or what can the i4l diversion services do for me)
+
+2. Required hard- and software
+
+3. Compiling, installing and loading/unloading the module  
+   Tracing calling and diversion information 
+
+4. Tracing calling and diversion information
+ 
+5. Format of the divert device ASCII output
+ 
+
+1. Features of the i4l diversion services 
+   (Or what can the i4l diversion services do for me)
+
+   The i4l diversion services offers call forwarding and logging normally 
+   only supported by isdn phones. Incoming calls may be diverted 
+   unconditionally (CFU), when not reachable (CFNR) or on busy condition 
+   (CFB). 
+   The diversions may be invoked statically in the providers exchange
+   as normally done by isdn phones. In this case all incoming calls
+   with a special (or all) service identifiers are forwarded if the 
+   forwarding reason is met. Activated static services may also be 
+   interrogated (queried).
+   The i4l diversion services additionally offers a dynamic version of
+   call forwarding which is not preprogrammed inside the providers exchange
+   but dynamically activated by i4l.
+   In this case all incoming calls are checked by rules that may be
+   compared to the mechanism of ipfwadm or ipchains. If a given rule matches
+   the checking process is finished and the rule matching will be applied
+   to the call.
+   The rules include primary and secondary service indentifiers, called 
+   number and subaddress, callers number and subaddress and whether the rule
+   matches to all filtered calls or only those when all B-channel resources
+   are exhausted.
+   Actions that may be invoked by a rule are ignore, proceed, reject, 
+   direct divert or delayed divert of a call.
+   All incoming calls matching a rule except the ignore rule a reported and
+   logged as ASCII via the proc filesystem (/proc/net/isdn/divert). If proceed
+   is selected the call will be held in a proceeding state (without ringing)
+   for a certain amount of time to let an external program or client decide
+   how to handle the call. 
+            
+
+2. Required hard- and software
+   
+   For using the i4l diversion services the isdn line must be of a EURO/DSS1
+   type. Additionally the i4l services only work together with the HiSax 
+   driver for passive isdn cards. All HiSax supported cards may be used for
+   the diversion purposes.
+   The static diversion services require the provider having static services
+   CFU, CFNR, CFB activated on an MSN-line. The static services may not be 
+   used on a point-to-point connection. Further the static services are only
+   available in some countries (for example germany). Countries requiring the 
+   keypad protocol for activating static diversions (like the netherlands) are
+   not supported but may use the tty devices for this purpose.
+   The dynamic diversion servives may be used in all countries if the provider
+   enables the feature CF (call forwarding). This should work on both MSN- and
+   point-to-point lines.
+   To add and delete rules the additional divertctrl program is needed. This
+   program is part of the isdn4kutils package.   
+
+3. Compiling, installing and loading/unloading the module  
+   Tracing calling and diversion information 
+
+
+   To compile the i4l code with diversion support you need to say yes to the 
+   DSS1 diversion services when selecting the i4l options in the kernel 
+   config (menuconfig or config).
+   After having properly activated a make modules and make modules_install all
+   required modules will be correctly installed in the needed modules dirs.
+   As the diversion services are currently not included in the scripts of most
+   standard distributions you will have to add a "insmod dss1_divert" after
+   having loaded the global isdn module.
+   The module can be loaded without any command line parameters.
+   If the module is actually loaded and active may be checked with a 
+   "cat /proc/modules" or "ls /proc/net/isdn/divert". The divert file is 
+   dynamically created by the diversion module and removed when the module is
+   unloaded.
+
+
+4. Tracing calling and diversion information
+ 
+   You also may put a "cat /proc/net/isdn/divert" in the background with the
+   output redirected to a file. Then all actions of the module are logged.
+   The divert file in the proc system may be opened more than once, so in 
+   conjunction with inetd and a small remote client on other machines inside
+   your network incoming calls and reactions by the module may be shown on 
+   every listening machine. 
+   If a call is reported as proceeding an external program or client may 
+   specify during a certain amount of time (normally 4 to 10 seconds) what
+   to do with that call.      
+   To unload the module all open files to the device in the proc system must
+   be closed. Otherwise the module (and isdn.o) may not be unloaded. 
+
+5. Format of the divert device ASCII output
+ 
+   To be done later
+
diff -urN linux-2.2.12-pre3/Documentation/isdn/README.fax linux-2.2.12-pre4/Documentation/isdn/README.fax
--- linux-2.2.12-pre3/Documentation/isdn/README.fax	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.12-pre4/Documentation/isdn/README.fax	2003-08-15 15:06:54.000000000 -0700
@@ -0,0 +1,38 @@
+
+Fax with isdn4linux
+===================
+
+When enabled during kernel configuration, the tty emulator
+of the ISDN subsystem is capable of the Fax Class 2 commands.
+
+This only makes sense under the following conditions :
+
+- You need the commands as dummy, because you are using
+  hylafax (with patch) for AVM capi.
+- You want to use the fax capabillities of your isdn-card.
+  (supported cards are listed below)
+
+
+NOTE: This implementation does *not* support fax with passive
+      ISDN-cards (known as softfax). The low-level driver of
+      the ISDN-card and/or the card itself must support this.
+
+
+Supported ISDN-Cards
+--------------------
+
+Eicon DIVA Server BRI/PCI (will be ready soon)
+Eicon DIVA Server PRI/PCI (will be ready soon)
+
+
+
+The command set is known as Class 2 (not Class 2.0) and
+can be activated by AT+FCLASS=2
+
+
+The interface between the link-level-module and the hardware-level driver
+is described in the files INTERFACE.fax and INTERFACE.
+
+Armin
+mac@melware.de
+
diff -urN linux-2.2.12-pre3/Documentation/isdn/README.hfc-pci linux-2.2.12-pre4/Documentation/isdn/README.hfc-pci
--- linux-2.2.12-pre3/Documentation/isdn/README.hfc-pci	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.12-pre4/Documentation/isdn/README.hfc-pci	2003-08-15 15:06:54.000000000 -0700
@@ -0,0 +1,26 @@
+The driver for the HFC-PCI and HFC-PCI-A chips from CCD may be used
+for many OEM cards using this chips.
+Additionally the driver has a special feature which makes it possible
+to read the echo-channel of the isdn bus. So all frames in both directions
+may be logged.
+When the echo logging feature is used the number of available B-channels
+for a HFC-PCI card is reduced to 1. Of course this is only relevant to
+the card, not to the isdn line.
+To activate the echo mode the following ioctls must be entered:
+
+hisaxctrl <driver/cardname> 10 1
+
+This reduces the available channels to 1. There must not be open connections
+through this card when entering the command.
+And then:
+
+hisaxctrl <driver/cardname> 12 1
+
+This enables the echo mode. If Hex logging is activated the isdnctrlx 
+devices show a output with a line beginning of HEX: for the providers
+exchange and ECHO: for isdn devices sending to the provider.
+
+Comments and reports to werner@isdn4linux.de or werner@titro.de .
+
+
+
diff -urN linux-2.2.12-pre3/Documentation/sound/vwsnd linux-2.2.12-pre4/Documentation/sound/vwsnd
--- linux-2.2.12-pre3/Documentation/sound/vwsnd	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.12-pre4/Documentation/sound/vwsnd	2003-08-15 15:06:54.000000000 -0700
@@ -0,0 +1,293 @@
+vwsnd - Sound driver for the Silicon Graphics 320 and 540 Visual
+Workstations' onboard audio.
+
+Copyright 1999 Silicon Graphics, Inc.  All rights reserved.
+
+
+At the time of this writing, March 1999, there are two models of
+Visual Workstation, the 320 and the 540.  This document only describes
+those models.  Future Visual Workstation models may have different
+sound capabilities, and this driver will probably not work on those
+boxes.
+
+The Visual Workstation has an Analog Devices AD1843 "SoundComm" audio
+codec chip.  The AD1843 is accessed through the Cobalt I/O ASIC, also
+known as Lithium.  This driver programs both both chips.
+
+==============================================================================
+QUICK CONFIGURATION
+
+	# insmod soundcore
+	# insmod vwsnd
+
+==============================================================================
+I/O CONNECTIONS
+
+On the Visual Workstation, only three of the AD1843 inputs are hooked
+up.  The analog line in jacks are connected to the AD1843's AUX1
+input.  The CD audio lines are connected to the AD1843's AUX2 input.
+The microphone jack is connected to the AD1843's MIC input.  The mic
+jack is mono, but the signal is delivered to both the left and right
+MIC inputs.  You can record in stereo from the mic input, but you will
+get the same signal on both channels (within the limits of A/D
+accuracy).  Full scale on the Line input is +/- 2.0 V.  Full scale on
+the MIC input is 20 dB less, or +/- 0.2 V.
+
+The AD1843's LOUT1 outputs are connected to the Line Out jacks.  The
+AD1843's HPOUT outputs are connected to the speaker/headphone jack.
+LOUT2 is not connected.  Line out's maximum level is +/- 2.0 V peak to
+peak.  The speaker/headphone out's maximum is +/- 4.0 V peak to peak.
+
+The AD1843's PCM input channel and one of its output channels (DAC1)
+are connected to Lithium.  The other output channel (DAC2) is not
+connected.
+
+==============================================================================
+CAPABILITIES
+
+The AD1843 has PCM input and output (Pulse Code Modulation, also known
+as wavetable).  PCM input and output can be mono or stereo in any of
+four formats.  The formats are 16 bit signed and 8 bit unsigned,
+u-Law, and A-Law format.  Any sample rate from 4 KHz to 49 KHz is
+available, in 1 Hz increments.
+
+The AD1843 includes an analog mixer that can mix all three input
+signals (line, mic and CD) into the analog outputs.  The mixer has a
+separate gain control and mute switch for each input.
+
+There are two outputs, line out and speaker/headphone out.  They
+always produce the same signal, and the speaker always has 3 dB more
+gain than the line out.  The speaker/headphone output can be muted,
+but this driver does not export that function.
+
+The hardware can sync audio to the video clock, but this driver does
+not have a way to specify syncing to video.
+
+==============================================================================
+PROGRAMMING
+
+This section explains the API supported by the driver.  Also see the
+Open Sound Programming Guide at http://www.opensound.com/pguide/ .
+This section assumes familiarity with that document.
+
+The driver has two interfaces, an I/O interface and a mixer interface.
+There is no MIDI or sequencer capability.
+
+==============================================================================
+PROGRAMMING PCM I/O
+
+The I/O interface is usually accessed as /dev/audio or /dev/dsp.
+Using the standard Open Sound System (OSS) ioctl calls, the sample
+rate, number of channels, and sample format may be set within the
+limitations described above.  The driver supports triggering.  It also
+supports getting the input and output pointers with one-sample
+accuracy.
+
+The SNDCTL_DSP_GETCAP ioctl returns these capabilities.
+
+	DSP_CAP_DUPLEX - driver supports full duplex.
+
+	DSP_CAP_TRIGGER - driver supports triggering.
+
+	DSP_CAP_REALTIME - values returned by SNDCTL_DSP_GETIPTR
+	and SNDCTL_DSP_GETOPTR are accurate to a few samples.
+
+Memory mapping (mmap) is not implemented.
+
+The driver permits subdivided fragment sizes from 64 to 4096 bytes.
+The number of fragments can be anything from 3 fragments to however
+many fragments fit into 124 kilobytes.  It is up to the user to
+determine how few/small fragments can be used without introducing
+glitches with a given workload.  Linux is not realtime, so we can't
+promise anything.  (sigh...)
+
+When this driver is switched into or out of mu-Law or A-Law mode on
+output, it may produce an audible click.  This is unavoidable.  To
+prevent clicking, use signed 16-bit mode instead, and convert from
+mu-Law or A-Law format in software.
+
+==============================================================================
+PROGRAMMING THE MIXER INTERFACE
+
+The mixer interface is usually accessed as /dev/mixer.  It is accessed
+through ioctls.  The mixer allows the application to control gain or
+mute several audio signal paths, and also allows selection of the
+recording source.
+
+Each of the constants described here can be read using the
+MIXER_READ(SOUND_MIXER_xxx) ioctl.  Those that are not read-only can
+also be written using the MIXER_WRITE(SOUND_MIXER_xxx) ioctl.  In most
+cases, <sys/soundcard.h> defines constants SOUND_MIXER_READ_xxx and
+SOUND_MIXER_WRITE_xxx which work just as well.
+
+SOUND_MIXER_CAPS	Read-only
+
+This is a mask of optional driver capabilities that are implemented.
+This driver's only capability is SOUND_CAP_EXCL_INPUT, which means
+that only one recording source can be active at a time.
+
+SOUND_MIXER_DEVMASK	Read-only
+
+This is a mask of the sound channels.  This driver's channels are PCM,
+LINE, MIC, CD, and RECLEV.
+
+SOUND_MIXER_STEREODEVS	Read-only
+
+This is a mask of which sound channels are capable of stereo.  All
+channels are capable of stereo.  (But see caveat on MIC input in I/O
+CONNECTIONS section above).
+
+SOUND_MIXER_OUTMASK	Read-only
+
+This is a mask of channels that route inputs through to outputs.
+Those are LINE, MIC, and CD.
+
+SOUND_MIXER_RECMASK	Read-only
+
+This is a mask of channels that can be recording sources.  Those are
+PCM, LINE, MIC, CD.
+
+SOUND_MIXER_PCM		Default: 0x5757 (0 dB)
+
+This is the gain control for PCM output.  The left and right channel
+gain are controlled independently.  This gain control has 64 levels,
+which range from -82.5 dB to +12.0 dB in 1.5 dB steps.  Those 64
+levels are mapped onto 100 levels at the ioctl, see below.
+
+SOUND_MIXER_LINE	Default: 0x4a4a (0 dB)
+
+This is the gain control for mixing the Line In source into the
+outputs.  The left and right channel gain are controlled
+independently.  This gain control has 32 levels, which range from
+-34.5 dB to +12.0 dB in 1.5 dB steps.  Those 32 levels are mapped onto
+100 levels at the ioctl, see below.
+
+SOUND_MIXER_MIC		Default: 0x4a4a (0 dB)
+
+This is the gain control for mixing the MIC source into the outputs.
+The left and right channel gain are controlled independently.  This
+gain control has 32 levels, which range from -34.5 dB to +12.0 dB in
+1.5 dB steps.  Those 32 levels are mapped onto 100 levels at the
+ioctl, see below.
+
+SOUND_MIXER_CD		Default: 0x4a4a (0 dB)
+
+This is the gain control for mixing the CD audio source into the
+outputs.  The left and right channel gain are controlled
+independently.  This gain control has 32 levels, which range from
+-34.5 dB to +12.0 dB in 1.5 dB steps.  Those 32 levels are mapped onto
+100 levels at the ioctl, see below.
+
+SOUND_MIXER_RECLEV	 Default: 0 (0 dB)
+
+This is the gain control for PCM input (RECording LEVel).  The left
+and right channel gain are controlled independently.  This gain
+control has 16 levels, which range from 0 dB to +22.5 dB in 1.5 dB
+steps.  Those 16 levels are mapped onto 100 levels at the ioctl, see
+below.
+
+SOUND_MIXER_RECSRC	 Default: SOUND_MASK_LINE
+
+This is a mask of currently selected PCM input sources (RECording
+SouRCes).  Because the AD1843 can only have a single recording source
+at a time, only one bit at a time can be set in this mask.  The
+allowable values are SOUND_MASK_PCM, SOUND_MASK_LINE, SOUND_MASK_MIC,
+or SOUND_MASK_CD.  Selecting SOUND_MASK_PCM sets up internal
+resampling which is useful for loopback testing and for hardware
+sample rate conversion.  But software sample rate conversion is
+probably faster, so I don't know how useful that is.
+
+SOUND_MIXER_OUTSRC	DEFAULT: SOUND_MASK_LINE|SOUND_MASK_MIC|SOUND_MASK_CD
+
+This is a mask of sources that are currently passed through to the
+outputs.  Those sources whose bits are not set are muted.
+
+==============================================================================
+GAIN CONTROL
+
+There are five gain controls listed above.  Each has 16, 32, or 64
+steps.  Each control has 1.5 dB of gain per step.  Each control is
+stereo.
+
+The OSS defines the argument to a channel gain ioctl as having two
+components, left and right, each of which ranges from 0 to 100.  The
+two components are packed into the same word, with the left side gain
+in the least significant byte, and the right side gain in the second
+least significant byte.  In C, we would say this.
+
+	#include <assert.h>
+
+	...
+
+	 	assert(leftgain >= 0 && leftgain <= 100);
+		assert(rightgain >= 0 && rightgain <= 100);
+		arg = leftgain | rightgain << 8;
+
+So each OSS gain control has 101 steps.  But the hardware has 16, 32,
+or 64 steps.  The hardware steps are spread across the 101 OSS steps
+nearly evenly.  The conversion formulas are like this, given N equals
+16, 32, or 64.
+
+	int round = N/2 - 1;
+	OSS_gain_steps = (hw_gain_steps * 100 + round) / (N - 1);
+	hw_gain_steps = (OSS_gain_steps * (N - 1) + round) / 100;
+
+Here is a snippet of C code that will return the left and right gain
+of any channel in dB.  Pass it one of the predefined gain_desc_t
+structures to access any of the five channels' gains.
+
+	typedef struct gain_desc {
+		float min_gain;
+		float gain_step;
+		int nbits;
+		int chan;
+	} gain_desc_t;
+
+	const gain_desc_t gain_pcm    = { -82.5, 1.5, 6, SOUND_MIXER_PCM    };
+	const gain_desc_t gain_line   = { -34.5, 1.5, 5, SOUND_MIXER_LINE   };
+	const gain_desc_t gain_mic    = { -34.5, 1.5, 5, SOUND_MIXER_MIC    };
+	const gain_desc_t gain_cd     = { -34.5, 1.5, 5, SOUND_MIXER_CD     };
+	const gain_desc_t gain_reclev = {   0.0, 1.5, 4, SOUND_MIXER_RECLEV };
+
+	int get_gain_dB(int fd, const gain_desc_t *gp,
+			float *left, float *right)
+	{
+		int word;
+		int lg, rg;
+		int mask = (1 << gp->nbits) - 1;
+
+		if (ioctl(fd, MIXER_READ(gp->chan), &word) != 0)
+			return -1;	/* fail */
+		lg = word & 0xFF;
+		rg = word >> 8 & 0xFF;
+		lg = (lg * mask + mask / 2) / 100;
+		rg = (rg * mask + mask / 2) / 100;
+		*left = gp->min_gain + gp->gain_step * lg;
+		*right = gp->min_gain + gp->gain_step * rg;
+		return 0;
+	}	
+
+And here is the corresponding routine to set a channel's gain in dB.
+
+	int set_gain_dB(int fd, const gain_desc_t *gp, float left, float right)
+	{
+		float max_gain =
+			gp->min_gain + (1 << gp->nbits) * gp->gain_step;
+		float round = gp->gain_step / 2;
+		int mask = (1 << gp->nbits) - 1;
+		int word;
+		int lg, rg;
+
+		if (left < gp->min_gain || right < gp->min_gain)
+			return EINVAL;
+		lg = (left - gp->min_gain + round) / gp->gain_step;
+		rg = (right - gp->min_gain + round) / gp->gain_step;
+		if (lg >= (1 << gp->nbits) || rg >= (1 << gp->nbits))
+			return EINVAL;
+		lg = (100 * lg + mask / 2) / mask;
+		rg = (100 * rg + mask / 2) / mask;
+		word = lg | rg << 8;
+
+		return ioctl(fd, MIXER_WRITE(gp->chan), &word);
+	}
+
diff -urN linux-2.2.12-pre3/drivers/block/Config.in linux-2.2.12-pre4/drivers/block/Config.in
--- linux-2.2.12-pre3/drivers/block/Config.in	1999-08-09 12:04:38.000000000 -0700
+++ linux-2.2.12-pre4/drivers/block/Config.in	2003-08-15 15:06:54.000000000 -0700
@@ -101,13 +101,16 @@
 fi
 bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
 if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
+  bool 'Autodetect RAID partitions' CONFIG_AUTODETECT_RAID
   tristate '   Linear (append) mode' CONFIG_MD_LINEAR
   tristate '   RAID-0 (striping) mode' CONFIG_MD_STRIPED
   tristate '   RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
   tristate '   RAID-4/RAID-5 mode' CONFIG_MD_RAID5
-fi
-if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then
-  bool '      Boot support (linear, striped)' CONFIG_MD_BOOT
+  tristate '   Translucent mode' CONFIG_MD_TRANSLUCENT
+  tristate '   Logical Volume Manager support' CONFIG_MD_LVM
+  if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then
+    bool '      Boot support (linear, striped)' CONFIG_MD_BOOT
+  fi
 fi
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
 if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
diff -urN linux-2.2.12-pre3/drivers/isdn/avmb1/avmcard.h linux-2.2.12-pre4/drivers/isdn/avmb1/avmcard.h
--- linux-2.2.12-pre3/drivers/isdn/avmb1/avmcard.h	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/avmb1/avmcard.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,9 +1,15 @@
 /*
- * $Id: avmcard.h,v 1.2 1999/07/05 15:09:45 calle Exp $
+ * $Id: avmcard.h,v 1.4 1999/08/04 10:10:08 calle Exp $
  *
  * Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: avmcard.h,v $
+ * Revision 1.4  1999/08/04 10:10:08  calle
+ * Bugfix: corrected /proc functions, added structure for new AVM cards.
+ *
+ * Revision 1.3  1999/07/23 08:41:47  calle
+ * prepared for new AVM cards.
+ *
  * Revision 1.2  1999/07/05 15:09:45  calle
  * - renamed "appl_release" to "appl_released".
  * - version und profile data now cleared on controller reset
@@ -56,13 +62,22 @@
 	avm_m1,
 	avm_m2,
 	avm_t1isa,
-	avm_t1pci
+	avm_t1pci,
+	avm_c4
 };
 
+typedef struct avmcard_dmainfo {
+	__u32 recvlen;   
+	__u8  recvbuf[128+2048];
+	struct sk_buff_head send_queue;
+	__u8  sendbuf[128+2048];
+} avmcard_dmainfo;
+
 typedef struct avmcard {
 	char name[32];
 	unsigned int port;
 	unsigned irq;
+	unsigned long membase;
 	enum avmcardtype cardtype;
 	int cardnr; /* for t1isa */
 
@@ -78,6 +93,10 @@
 
 	int interrupt;
 
+	void *mbase;
+	__u32 csr;
+	avmcard_dmainfo *dma;
+
 	struct capi_ctr *ctrl;
 } avmcard;
 
@@ -174,6 +193,7 @@
 					   * int32 Length message
 					   * 
 					 */
+#define RECEIVE_POLLDWORD	0x75	/* t1pci in dword mode */
 
 #define WRITE_REGISTER		0x00
 #define READ_REGISTER		0x01
@@ -512,6 +532,10 @@
 	      b1outp(base, B1_RESET, 0xf0);
 	      b1outp(base, B1_INSTAT, 0x02);
 	      break;
+	   case avm_c4:
+	   case avm_t1pci:
+	      b1outp(base, B1_RESET, 0xf0);
+	      break;
 	 }
 }
 
diff -urN linux-2.2.12-pre3/drivers/isdn/avmb1/b1.c linux-2.2.12-pre4/drivers/isdn/avmb1/b1.c
--- linux-2.2.12-pre3/drivers/isdn/avmb1/b1.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/avmb1/b1.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,11 +1,20 @@
 /*
- * $Id: b1.c,v 1.4 1999/07/09 15:05:38 keil Exp $
+ * $Id: b1.c,v 1.7 1999/08/04 10:10:09 calle Exp $
  * 
  * Common module for AVM B1 cards.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1.c,v $
+ * Revision 1.7  1999/08/04 10:10:09  calle
+ * Bugfix: corrected /proc functions, added structure for new AVM cards.
+ *
+ * Revision 1.6  1999/07/23 08:51:04  calle
+ * small fix and typo in checkin before.
+ *
+ * Revision 1.5  1999/07/23 08:41:48  calle
+ * prepared for new AVM cards.
+ *
  * Revision 1.4  1999/07/09 15:05:38  keil
  * compat.h is now isdn_compat.h
  *
@@ -54,7 +63,7 @@
 #include "capicmd.h"
 #include "capiutil.h"
 
-static char *revision = "$Revision: 1.4 $";
+static char *revision = "$Revision: 1.7 $";
 
 /* ------------------------------------------------------------- */
 
@@ -401,7 +410,10 @@
 
 	flag = ((__u8 *)(profp->manu))[1];
 	switch (flag) {
-	case 0: strcpy(card->cardname, "B1"); break;
+	case 0: if (card->version[VER_CARDTYPE])
+	           strcpy(card->cardname, card->version[VER_CARDTYPE]);
+	        else strcpy(card->cardname, "B1");
+		break;
 	case 3: strcpy(card->cardname,"PCMCIA B"); break;
 	case 4: strcpy(card->cardname,"PCMCIA M1"); break;
 	case 5: strcpy(card->cardname,"PCMCIA M2"); break;
@@ -580,6 +592,7 @@
 	case avm_m2: s = "M2"; break;
 	case avm_t1isa: s = "T1 ISA (HEMA)"; break;
 	case avm_t1pci: s = "T1 PCI"; break;
+	case avm_c4: s = "C4"; break;
 	default: s = "???"; break;
 	}
 	len += sprintf(page+len, "%-16s %s\n", "type", s);
@@ -619,15 +632,11 @@
 	}
 	len += sprintf(page+len, "%-16s %s\n", "cardname", card->cardname);
 
-	if (len < off) 
-           return 0;
-	*eof = 1;
-	*start = page - off;
-	return ((count < len-off) ? count : len-off);
-	if (len < off) 
+	if (off+count >= len)
+	   *eof = 1;
+	if (len < off)
            return 0;
-	*eof = 1;
-	*start = page - off;
+	*start = page + off;
 	return ((count < len-off) ? count : len-off);
 }
 
diff -urN linux-2.2.12-pre3/drivers/isdn/avmb1/b1pci.c linux-2.2.12-pre4/drivers/isdn/avmb1/b1pci.c
--- linux-2.2.12-pre3/drivers/isdn/avmb1/b1pci.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/avmb1/b1pci.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,11 +1,17 @@
 /*
- * $Id: b1pci.c,v 1.14 1999/07/09 15:05:41 keil Exp $
+ * $Id: b1pci.c,v 1.16 1999/08/11 21:01:07 keil Exp $
  * 
  * Module for AVM B1 PCI-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1pci.c,v $
+ * Revision 1.16  1999/08/11 21:01:07  keil
+ * new PCI codefix
+ *
+ * Revision 1.15  1999/08/10 16:02:27  calle
+ * struct pci_dev changed in 2.3.13. Made the necessary changes.
+ *
  * Revision 1.14  1999/07/09 15:05:41  keil
  * compat.h is now isdn_compat.h
  *
@@ -51,7 +57,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.14 $";
+static char *revision = "$Revision: 1.16 $";
 
 /* ------------------------------------------------------------- */
 
@@ -246,7 +252,7 @@
 	while ((dev = pci_find_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, dev))) {
 		struct capicardparams param;
 
-		param.port = dev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
+		param.port = get_pcibase(dev, 1) & PCI_BASE_ADDRESS_IO_MASK;
 		param.irq = dev->irq;
 		printk(KERN_INFO
 			"%s: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
diff -urN linux-2.2.12-pre3/drivers/isdn/avmb1/capidrv.c linux-2.2.12-pre4/drivers/isdn/avmb1/capidrv.c
--- linux-2.2.12-pre3/drivers/isdn/avmb1/capidrv.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/avmb1/capidrv.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,11 +1,20 @@
 /*
- * $Id: capidrv.c,v 1.23 1999/07/09 15:05:44 keil Exp $
+ * $Id: capidrv.c,v 1.26 1999/08/06 07:41:16 calle Exp $
  *
  * ISDN4Linux Driver, using capi20 interface (kernelcapi)
  *
  * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capidrv.c,v $
+ * Revision 1.26  1999/08/06 07:41:16  calle
+ * Added the "vbox patch". if (si1 == 1) si2 = 0;
+ *
+ * Revision 1.25  1999/08/04 10:10:11  calle
+ * Bugfix: corrected /proc functions, added structure for new AVM cards.
+ *
+ * Revision 1.24  1999/07/20 06:48:02  calle
+ * Bugfix: firmware version check for D2 trace was too restrictiv.
+ *
  * Revision 1.23  1999/07/09 15:05:44  keil
  * compat.h is now isdn_compat.h
  *
@@ -154,7 +163,7 @@
 #include "capicmd.h"
 #include "capidrv.h"
 
-static char *revision = "$Revision: 1.23 $";
+static char *revision = "$Revision: 1.26 $";
 int debugmode = 0;
 
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
@@ -285,6 +294,8 @@
         case ISDN_PROTO_L2_V11019:
         case ISDN_PROTO_L2_V11038:
 		return 2;
+        case ISDN_PROTO_L2_FAX:
+		return 4;
 	}
 }
 
@@ -302,6 +313,8 @@
         case ISDN_PROTO_L2_V11019:
         case ISDN_PROTO_L2_V11038:
 		return 1;
+        case ISDN_PROTO_L2_FAX:
+		return 4;
 	}
 }
 
@@ -318,6 +331,8 @@
         case ISDN_PROTO_L2_V11038:
 	default:
 		return 0;
+        case ISDN_PROTO_L2_FAX:
+		return 4;
 	}
 }
 
@@ -1017,6 +1032,13 @@
 			cmd.parm.setup.si2,
 			cmd.parm.setup.eazmsn);
 
+	if (cmd.parm.setup.si1 == 1 && cmd.parm.setup.si2 != 0) {
+		printk(KERN_INFO "capidrv-%d: patching si2=%d to 0 for VBOX\n", 
+			card->contrnr,
+			cmd.parm.setup.si2);
+		cmd.parm.setup.si2 = 0;
+	}
+
 	switch (card->interface.statcallb(&cmd)) {
 	case 0:
 	case 3:
@@ -2083,7 +2105,7 @@
 	avmversion[1] |= (version.minormanuversion >> 4) & 0x0f;
 	avmversion[2] |= version.minormanuversion & 0x0f;
 
-        if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 6)) {
+        if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) {
 		printk(KERN_INFO "%s: D2 trace enabled\n", card->name);
 		capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid,
 					   card->msgid++,
@@ -2135,7 +2157,7 @@
 	avmversion[1] |= (version.minormanuversion >> 4) & 0x0f;
 	avmversion[2] |= version.minormanuversion & 0x0f;
 
-        if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 6)) {
+        if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) {
 		printk(KERN_INFO "%s: D2 trace disabled\n", card->name);
 	} else {
 		printk(KERN_INFO "%s: D3 trace disabled\n", card->name);
@@ -2189,6 +2211,10 @@
 	    ISDN_FEATURE_L2_V11096 |
 	    ISDN_FEATURE_L2_V11019 |
 	    ISDN_FEATURE_L2_V11038 |
+#if 0
+	    ISDN_FEATURE_L2_FAX |
+	    ISDN_FEATURE_L3_FAX |
+#endif
 	    ISDN_FEATURE_P_UNKNOWN;
 	card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */
 	strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
@@ -2312,10 +2338,11 @@
 			global.nrecvdatapkt,
 			global.nsentctlpkt,
 			global.nsentdatapkt);
-	if (len < off) 
+	if (off+count >= len)
+	   *eof = 1;
+	if (len < off)
            return 0;
-	*eof = 1;
-	*start = page -off;
+	*start = page + off;
 	return ((count < len-off) ? count : len-off);
 }
 
diff -urN linux-2.2.12-pre3/drivers/isdn/avmb1/capilli.h linux-2.2.12-pre4/drivers/isdn/avmb1/capilli.h
--- linux-2.2.12-pre3/drivers/isdn/avmb1/capilli.h	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/avmb1/capilli.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,5 +1,5 @@
 /*
- * $Id: capilli.h,v 1.2 1999/07/05 15:09:52 calle Exp $
+ * $Id: capilli.h,v 1.4 1999/07/23 08:51:05 calle Exp $
  * 
  * Kernel CAPI 2.0 Driver Interface for Linux
  * 
@@ -25,6 +25,7 @@
 	unsigned irq;
 	int cardtype;
 	int cardnr;
+	unsigned int membase;
 } capicardparams;
 
 struct capi_driver;
diff -urN linux-2.2.12-pre3/drivers/isdn/avmb1/kcapi.c linux-2.2.12-pre4/drivers/isdn/avmb1/kcapi.c
--- linux-2.2.12-pre3/drivers/isdn/avmb1/kcapi.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/avmb1/kcapi.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,11 +1,15 @@
 /*
- * $Id: kcapi.c,v 1.5 1999/07/09 15:05:48 keil Exp $
+ * $Id: kcapi.c,v 1.6 1999/07/20 06:41:49 calle Exp $
  * 
  * Kernel CAPI 2.0 Module
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: kcapi.c,v $
+ * Revision 1.6  1999/07/20 06:41:49  calle
+ * Bugfix: After the redesign of the AVM B1 driver, the driver didn't even
+ *         compile, if not selected as modules.
+ *
  * Revision 1.5  1999/07/09 15:05:48  keil
  * compat.h is now isdn_compat.h
  *
@@ -61,7 +65,7 @@
 #include <linux/b1lli.h>
 #endif
 
-static char *revision = "$Revision: 1.5 $";
+static char *revision = "$Revision: 1.6 $";
 
 /* ------------------------------------------------------------- */
 
@@ -1462,6 +1466,21 @@
 EXPORT_SYMBOL(attach_capi_driver);
 EXPORT_SYMBOL(detach_capi_driver);
 
+#ifndef MODULE
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA
+extern int b1isa_init(void);
+#endif
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI
+extern int b1pci_init(void);
+#endif
+#ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA
+extern int t1isa_init(void);
+#endif
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA
+extern int b1pcmcia_init(void);
+#endif
+#endif
+
 /*
  * init / exit functions
  */
@@ -1497,6 +1516,18 @@
         printk(KERN_NOTICE "CAPI-driver Rev%s: loaded\n", rev);
 #else
 	printk(KERN_NOTICE "CAPI-driver Rev%s: started\n", rev);
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA
+	(void)b1isa_init();
+#endif
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI
+	(void)b1pci_init();
+#endif
+#ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA
+	(void)t1isa_init();
+#endif
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA
+	(void)b1pcmcia_init();
+#endif
 #endif
 	return 0;
 }
diff -urN linux-2.2.12-pre3/drivers/isdn/avmb1/Makefile linux-2.2.12-pre4/drivers/isdn/avmb1/Makefile
--- linux-2.2.12-pre3/drivers/isdn/avmb1/Makefile	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/avmb1/Makefile	2003-08-15 15:06:54.000000000 -0700
@@ -1,5 +1,5 @@
 #
-# $Id: Makefile,v 1.5 1999/07/01 15:26:20 calle Exp $
+# $Id: Makefile,v 1.6 1999/07/20 06:41:44 calle Exp $
 #
 # Makefile for the CAPI and AVM-B1 device drivers.
 #
@@ -11,6 +11,10 @@
 # parent makes..
 #
 # $Log: Makefile,v $
+# Revision 1.6  1999/07/20 06:41:44  calle
+# Bugfix: After the redesign of the AVM B1 driver, the driver didn't even
+#         compile, if not selected as modules.
+#
 # Revision 1.5  1999/07/01 15:26:20  calle
 # complete new version (I love it):
 # + new hardware independed "capi_driver" interface that will make it easy to:
@@ -73,7 +77,7 @@
 ifeq ($(CONFIG_ISDN_DRV_AVMB1),y)
   O_TARGET += avmb1.o
   OX_OBJS  += kcapi.o
-  O_OBJS   += capi.o kernelcapi.o
+  O_OBJS   += capi.o
   ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA
   O_OBJS   += b1isa.o
   endif
diff -urN linux-2.2.12-pre3/drivers/isdn/Config.in linux-2.2.12-pre4/drivers/isdn/Config.in
--- linux-2.2.12-pre3/drivers/isdn/Config.in	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/Config.in	2003-08-15 15:06:54.000000000 -0700
@@ -9,6 +9,9 @@
   fi
 fi
 bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO
+if [ "$CONFIG_ISDN_AUDIO" != "n" ]; then
+  bool 'Support AT-Fax Class 2 commands' CONFIG_ISDN_TTY_FAX
+fi
 bool 'Support isdn diversion services' CONFIG_ISDN_DIVERSION
 if [ "$CONFIG_X25" != "n" ]; then
   bool 'X.25 PLP on top of ISDN (EXPERIMENTAL)' CONFIG_ISDN_X25
@@ -61,6 +64,9 @@
 	dep_tristate 'IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN
 fi
 dep_tristate 'Eicon.Diehl active card support' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN
+if [ "$CONFIG_ISDN_DRV_EICON" != "n" ]; then
+    bool 'Eicon S,SX,SCOM,Quadro,S2M support' CONFIG_ISDN_DRV_EICON_ISA
+fi
 dep_tristate 'AVM CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN
 if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then
     bool 'AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA
diff -urN linux-2.2.12-pre3/drivers/isdn/divert/divert_procfs.c linux-2.2.12-pre4/drivers/isdn/divert/divert_procfs.c
--- linux-2.2.12-pre3/drivers/isdn/divert/divert_procfs.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/divert/divert_procfs.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,5 +1,5 @@
 /* 
- * $Id: divert_procfs.c,v 1.3 1999/07/05 20:21:41 werner Exp $
+ * $Id: divert_procfs.c,v 1.4 1999/08/06 07:42:48 calle Exp $
  *
  * Filesystem handling for the diversion supplementary services.
  *
@@ -20,6 +20,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: divert_procfs.c,v $
+ * Revision 1.4  1999/08/06 07:42:48  calle
+ * Added COMPAT_HAS_NEW_WAITQ for rd_queue for newer kernels.
+ *
  * Revision 1.3  1999/07/05 20:21:41  werner
  * changes to use diversion sources for all kernel versions.
  * removed static device, only proc filesystem used
@@ -44,6 +47,7 @@
   #include <linux/fs.h>
 #endif
 #include <linux/isdnif.h>
+#include <linux/isdn_compat.h>
 #include "isdn_divert.h"
 
 /*********************************/
@@ -52,7 +56,11 @@
 ulong if_used = 0; /* number of interface users */
 static struct divert_info *divert_info_head = NULL; /* head of queue */
 static struct divert_info *divert_info_tail = NULL; /* pointer to last entry */
+#ifdef COMPAT_HAS_NEW_WAITQ
+static wait_queue_head_t rd_queue;
+#else
 static struct wait_queue *rd_queue = 0; /* Queue IO */
+#endif
 
 /*********************************/
 /* put an info buffer into queue */
@@ -388,6 +396,10 @@
 int divert_dev_init(void)
 { int i;
 
+#ifdef COMPAT_HAS_NEW_WAITQ
+	init_waitqueue_head(&rd_queue);
+#endif
+
 #ifdef CONFIG_PROC_FS
 #if (LINUX_VERSION_CODE < 0x020117)
   (void *) proc_reg_dynamic = get_module_symbol("","proc_register_dynamic");
diff -urN linux-2.2.12-pre3/drivers/isdn/eicon/eicon_dsp.h linux-2.2.12-pre4/drivers/isdn/eicon/eicon_dsp.h
--- linux-2.2.12-pre3/drivers/isdn/eicon/eicon_dsp.h	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/eicon/eicon_dsp.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: eicon_dsp.h,v 1.3 1999/07/11 17:16:24 armin Exp $
+/* $Id: eicon_dsp.h,v 1.4 1999/07/25 15:12:02 armin Exp $
  *
  * ISDN lowlevel-module for Eicon.Diehl active cards.
  *        DSP definitions
@@ -21,6 +21,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: eicon_dsp.h,v $
+ * Revision 1.4  1999/07/25 15:12:02  armin
+ * fix of some debug logs.
+ * enabled ISA-cards option.
+ *
  * Revision 1.3  1999/07/11 17:16:24  armin
  * Bugfixes in queue handling.
  * Added DSP-DTMF decoder functions.
@@ -309,5 +313,107 @@
   - none -
 */
 
+/* ============= FAX ================ */
+
+#define EICON_FAXID_LEN 20
+
+typedef struct eicon_t30_s {
+  __u8          code;
+  __u8          rate;
+  __u8          resolution;
+  __u8          format;
+  __u8          pages_low;
+  __u8          pages_high;
+  __u8          atf;
+  __u8          control_bits_low;
+  __u8          control_bits_high;
+  __u8          feature_bits_low;
+  __u8          feature_bits_high;
+  __u8          universal_5;
+  __u8          universal_6;
+  __u8          universal_7;
+  __u8          station_id_len;
+  __u8          head_line_len;
+  __u8          station_id[EICON_FAXID_LEN];
+/* __u8          head_line[]; */
+} eicon_t30_s;
+
+        /* EDATA transmit messages */
+#define EDATA_T30_DIS       0x01
+#define EDATA_T30_FTT       0x02
+#define EDATA_T30_MCF       0x03
+
+        /* EDATA receive messages */
+#define EDATA_T30_DCS       0x81
+#define EDATA_T30_TRAIN_OK  0x82
+#define EDATA_T30_EOP       0x83
+#define EDATA_T30_MPS       0x84
+#define EDATA_T30_EOM       0x85
+#define EDATA_T30_DTC       0x86
+
+#define T30_FORMAT_SFF            0
+#define T30_FORMAT_ASCII          1
+#define T30_FORMAT_COUNT          2
+
+#define T30_CONTROL_BIT_DISABLE_FINE      0x0001
+#define T30_CONTROL_BIT_ENABLE_ECM        0x0002
+#define T30_CONTROL_BIT_ECM_64_BYTES      0x0004
+#define T30_CONTROL_BIT_ENABLE_2D_CODING  0x0008
+#define T30_CONTROL_BIT_ENABLE_T6_CODING  0x0010
+#define T30_CONTROL_BIT_ENABLE_UNCOMPR    0x0020
+#define T30_CONTROL_BIT_ACCEPT_POLLING    0x0040
+#define T30_CONTROL_BIT_REQUEST_POLLING   0x0080
+#define T30_CONTROL_BIT_MORE_DOCUMENTS    0x0100
+
+#define T30_CONTROL_BIT_ALL_FEATURES\
+  (T30_CONTROL_BIT_ENABLE_ECM | T30_CONTROL_BIT_ENABLE_2D_CODING |\
+   T30_CONTROL_BIT_ENABLE_T6_CODING | T30_CONTROL_BIT_ENABLE_UNCOMPR)
+
+#define T30_FEATURE_BIT_FINE              0x0001
+#define T30_FEATURE_BIT_ECM               0x0002
+#define T30_FEATURE_BIT_ECM_64_BYTES      0x0004
+#define T30_FEATURE_BIT_2D_CODING         0x0008
+#define T30_FEATURE_BIT_T6_CODING         0x0010
+#define T30_FEATURE_BIT_UNCOMPR_ENABLED   0x0020
+#define T30_FEATURE_BIT_POLLING           0x0040
+
+#define FAX_OBJECT_DOCU		1
+#define FAX_OBJECT_PAGE		2
+#define FAX_OBJECT_LINE		3
+
+#define T4_EOL			0x800
+#define T4_EOL_BITSIZE		12
+#define T4_EOL_DWORD		(T4_EOL << (32 - T4_EOL_BITSIZE))
+#define T4_EOL_MASK_DWORD	((__u32) -1 << (32 - T4_EOL_BITSIZE))
+
+#define SFF_LEN_FLD_SIZE	3
+
+#define _DLE_	0x10
+#define _ETX_	0x03
+
+typedef struct eicon_sff_dochead {
+	__u32	id		__attribute__ ((packed));
+	__u8	version		__attribute__ ((packed));
+	__u8	reserved1	__attribute__ ((packed));
+	__u16	userinfo	__attribute__ ((packed));
+	__u16	pagecount	__attribute__ ((packed));
+	__u16	off1pagehead	__attribute__ ((packed));
+	__u32	offnpagehead	__attribute__ ((packed));
+	__u32	offdocend	__attribute__ ((packed));
+} eicon_sff_dochead;
+
+typedef struct eicon_sff_pagehead {
+	__u8	pageheadid	__attribute__ ((packed));
+	__u8	pageheadlen	__attribute__ ((packed));
+	__u8	resvert		__attribute__ ((packed));
+	__u8	reshoriz	__attribute__ ((packed));
+	__u8	coding		__attribute__ ((packed));
+	__u8	reserved2	__attribute__ ((packed));
+	__u16	linelength	__attribute__ ((packed));
+	__u16	pagelength	__attribute__ ((packed));
+	__u32	offprevpage	__attribute__ ((packed));
+	__u32	offnextpage	__attribute__ ((packed));
+} eicon_sff_pagehead;
+
 #endif	/* DSP_H */
 
diff -urN linux-2.2.12-pre3/drivers/isdn/eicon/eicon.h linux-2.2.12-pre4/drivers/isdn/eicon/eicon.h
--- linux-2.2.12-pre3/drivers/isdn/eicon/eicon.h	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/eicon/eicon.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: eicon.h,v 1.7 1999/07/11 17:16:23 armin Exp $
+/* $Id: eicon.h,v 1.8 1999/07/25 15:12:01 armin Exp $
  *
  * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
  *
@@ -21,6 +21,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon.h,v $
+ * Revision 1.8  1999/07/25 15:12:01  armin
+ * fix of some debug logs.
+ * enabled ISA-cards option.
+ *
  * Revision 1.7  1999/07/11 17:16:23  armin
  * Bugfixes in queue handling.
  * Added DSP-DTMF decoder functions.
@@ -344,6 +348,22 @@
   __u16                 ref;            /* saved reference          */
 } entity;
 
+#define FAX_MAX_SCANLINE 256
+
+typedef struct {
+	__u8		PrevObject;
+	__u8		NextObject;
+	__u8		abLine[FAX_MAX_SCANLINE];
+	__u8		abFrame[FAX_MAX_SCANLINE];
+	unsigned int	LineLen;
+	unsigned int	LineDataLen;
+	__u32		LineData;
+	unsigned int	NullBytesPos;
+	__u8		NullByteExist;
+	int		PageCount;
+	__u8		Dle;
+	__u8		Eop;
+} eicon_ch_fax_buf;
 
 typedef struct {
 	int	       No;		 /* Channel Number	        */
@@ -357,6 +377,10 @@
 	unsigned short ncci;
 	unsigned char  l2prot;           /* Layer 2 protocol            */
 	unsigned char  l3prot;           /* Layer 3 protocol            */
+#ifdef CONFIG_ISDN_TTY_FAX
+	T30_s		*fax;		 /* pointer to fax data in LL	*/
+	eicon_ch_fax_buf fax2;		 /* fax related struct		*/
+#endif
 	entity		e;		 /* Entity  			*/
 	char		cpn[32];	 /* remember cpn		*/
 	char		oad[32];	 /* remember oad		*/
diff -urN linux-2.2.12-pre3/drivers/isdn/eicon/eicon_idi.c linux-2.2.12-pre4/drivers/isdn/eicon/eicon_idi.c
--- linux-2.2.12-pre3/drivers/isdn/eicon/eicon_idi.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/eicon/eicon_idi.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: eicon_idi.c,v 1.10 1999/07/11 17:16:24 armin Exp $
+/* $Id: eicon_idi.c,v 1.11 1999/07/25 15:12:03 armin Exp $
  *
  * ISDN lowlevel-module for Eicon.Diehl active cards.
  *        IDI interface 
@@ -21,6 +21,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_idi.c,v $
+ * Revision 1.11  1999/07/25 15:12:03  armin
+ * fix of some debug logs.
+ * enabled ISA-cards option.
+ *
  * Revision 1.10  1999/07/11 17:16:24  armin
  * Bugfixes in queue handling.
  * Added DSP-DTMF decoder functions.
@@ -73,7 +77,7 @@
 
 #undef EICON_FULL_SERVICE_OKTETT
 
-char *eicon_idi_revision = "$Revision: 1.10 $";
+char *eicon_idi_revision = "$Revision: 1.11 $";
 
 eicon_manifbuf *manbuf;
 
@@ -91,6 +95,7 @@
 
 int eicon_idi_manage_assign(eicon_card *card);
 int eicon_idi_manage_remove(eicon_card *card);
+int idi_fill_in_T30(eicon_chan *chan, unsigned char *buffer);
 
 int
 idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan)
@@ -141,10 +146,25 @@
 		case ISDN_PROTO_L2_MODEM:
 			reqbuf->XBuffer.P[l++] = 2;
 			break;
+		case ISDN_PROTO_L2_FAX:
+  			if (chan->fsm_state == EICON_STATE_IWAIT)
+				reqbuf->XBuffer.P[l++] = 3; /* autoconnect on incoming */
+			else
+				reqbuf->XBuffer.P[l++] = 2;
+			break;
 		default:
 			reqbuf->XBuffer.P[l++] = 1;
 	}
 	switch(chan->l3prot) {
+		case ISDN_PROTO_L3_FAX:
+#ifdef CONFIG_ISDN_TTY_FAX
+			reqbuf->XBuffer.P[l++] = 6;
+			reqbuf->XBuffer.P[l++] = NLC;
+			tmp = idi_fill_in_T30(chan, &reqbuf->XBuffer.P[l+1]);
+			reqbuf->XBuffer.P[l++] = tmp; 
+			l += tmp;
+			break;
+#endif
 		case ISDN_PROTO_L3_TRANS:
 		default:
 			reqbuf->XBuffer.P[l++] = 4;
@@ -218,6 +238,14 @@
 			reqbuf->XBuffer.P[6] = 128;
 			reqbuf->XBuffer.P[7] = 0;
 			break;
+		case ISDN_PROTO_L2_FAX:
+			reqbuf->XBuffer.P[2] = 0x10;
+			reqbuf->XBuffer.P[3] = 0;
+			reqbuf->XBuffer.P[4] = 0;
+			reqbuf->XBuffer.P[5] = 0;
+			reqbuf->XBuffer.P[6] = 128;
+			reqbuf->XBuffer.P[7] = 0;
+			break;
 		case ISDN_PROTO_L2_TRANS:
 			switch(chan->l3prot) {
 				case ISDN_PROTO_L3_TRANSDSP:
@@ -236,8 +264,8 @@
 int
 idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer)
 {
-        struct sk_buff *skb;
-        struct sk_buff *skb2;
+        struct sk_buff *skb = 0;
+        struct sk_buff *skb2 = 0;
 	eicon_REQ *reqbuf;
 	eicon_chan_ptr *chan2;
 
@@ -246,7 +274,11 @@
 
         if ((!skb) || (!skb2)) {
 		if (DebugVar & 1)
-                	printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No);
+                	printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in do_req()\n", chan->No);
+		if (skb) 
+			dev_kfree_skb(skb);
+		if (skb2) 
+			dev_kfree_skb(skb2);
                 return -ENOMEM; 
 	}
 
@@ -255,7 +287,7 @@
 
 	reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
 	if (DebugVar & 8)
-		printk(KERN_DEBUG "idi_req: Ch%d: 0x%02x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig");
+		printk(KERN_DEBUG "idi_req: Ch%d: req %x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig");
 	if (layer) cmd |= 0x700;
 	switch(cmd) {
 		case ASSIGN:
@@ -296,6 +328,8 @@
 		default:
 			if (DebugVar & 1)
 				printk(KERN_ERR "idi_req: Ch%d: Unknown request\n", chan->No);
+			dev_kfree_skb(skb);
+			dev_kfree_skb(skb2);
 			return(-1);
 	}
 
@@ -372,6 +406,9 @@
 	chan->fsm_state = EICON_STATE_NULL;
 	if (DebugVar & 8)
 		printk(KERN_DEBUG"idi_req: Ch%d: Hangup\n", chan->No);
+#ifdef CONFIG_ISDN_TTY_FAX
+	chan->fax = 0;
+#endif
   return(0);
 }
 
@@ -403,7 +440,11 @@
 
         if ((!skb) || (!skb2)) {
 		if (DebugVar & 1)
-                	printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No);
+                	printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in connect_req()\n", chan->No);
+		if (skb) 
+			dev_kfree_skb(skb);
+		if (skb2) 
+			dev_kfree_skb(skb2);
                 return -ENOMEM; 
 	}
 
@@ -486,6 +527,14 @@
 			reqbuf->XBuffer.P[l-2] = 128;
 			reqbuf->XBuffer.P[l-1] = 0;
                         break;
+                case ISDN_PROTO_L2_FAX:
+			reqbuf->XBuffer.P[l-6] = 0x10;
+			reqbuf->XBuffer.P[l-5] = 0;
+			reqbuf->XBuffer.P[l-4] = 0;
+			reqbuf->XBuffer.P[l-3] = 0;
+			reqbuf->XBuffer.P[l-2] = 128;
+			reqbuf->XBuffer.P[l-1] = 0;
+                        break;
 		case ISDN_PROTO_L2_TRANS:
 			switch(chan->l3prot) {
 				case ISDN_PROTO_L3_TRANSDSP:
@@ -833,6 +882,7 @@
   }
 }
 
+
 int
 idi_send_udata(eicon_card *card, eicon_chan *chan, int UReq, u_char *buffer, int len)
 {
@@ -855,8 +905,12 @@
 
 	if ((!skb) || (!skb2)) {
 		if (DebugVar & 1)
-			printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No);
-			return -ENOMEM;
+			printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in send_udata()\n", chan->No);
+		if (skb) 
+			dev_kfree_skb(skb);
+		if (skb2) 
+			dev_kfree_skb(skb2);
+		return -ENOMEM;
 	}
 
 	chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
@@ -991,7 +1045,7 @@
 	
 	if ((DebugVar & 128) || 
 	   ((DebugVar & 16) && (ind->Ind != 8))) {
-        	printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No,
+        	printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No,
 		        ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);
 	}
 
@@ -1025,6 +1079,9 @@
 				cmd.command = ISDN_STAT_DHUP;
 				ccard->interface.statcallb(&cmd);
 				eicon_idi_listen_req(ccard, chan);
+#ifdef CONFIG_ISDN_TTY_FAX
+				chan->fax = 0;
+#endif
 				break;
 			case INDICATE_IND:
 				if (DebugVar & 8)
@@ -1098,7 +1155,15 @@
 					cmd.command = ISDN_STAT_DCONN;
 					cmd.arg = chan->No;
 					ccard->interface.statcallb(&cmd);
-					idi_do_req(ccard, chan, IDI_N_CONNECT, 1);
+					if (chan->l2prot != ISDN_PROTO_L2_FAX) {
+						idi_do_req(ccard, chan, IDI_N_CONNECT, 1);
+					}
+#ifdef CONFIG_ISDN_TTY_FAX
+					else {
+						if (chan->fax)
+							chan->fax->phase = ISDN_FAX_PHASE_A;
+					}
+#endif
 				} else
 					idi_hangup(ccard, chan);
 				break;
@@ -1113,6 +1178,12 @@
 					ccard->interface.statcallb(&cmd);
 					idi_do_req(ccard, chan, ASSIGN, 1); 
 					idi_do_req(ccard, chan, IDI_N_CONNECT, 1);
+#ifdef CONFIG_ISDN_TTY_FAX
+					if (chan->l2prot == ISDN_PROTO_L2_FAX) {
+						if (chan->fax)
+							chan->fax->phase = ISDN_FAX_PHASE_A;
+					}
+#endif
 				} else
 				idi_hangup(ccard, chan);
 				break;
@@ -1142,6 +1213,27 @@
 					chan->fsm_state = EICON_STATE_WMCONN;
 					break;
 				}
+				if (chan->l2prot == ISDN_PROTO_L2_FAX) {
+#ifdef CONFIG_ISDN_TTY_FAX
+					chan->fsm_state = EICON_STATE_ACTIVE;
+					idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
+					if (chan->fax) {
+						if (chan->fax->phase == ISDN_FAX_PHASE_B) {
+							idi_fax_send_header(ccard, chan, 2);
+							cmd.driver = ccard->myid;
+							cmd.command = ISDN_STAT_FAXIND;
+							cmd.arg = chan->No;
+							chan->fax->r_code = ISDN_TTY_FAX_DCS;
+							ccard->interface.statcallb(&cmd);
+						}
+					}
+					else {
+						if (DebugVar & 1)
+							printk(KERN_DEBUG "idi_ind: N_CONNECT_ACK with NULL fax struct, ERROR\n");
+					}
+#endif
+					break;
+				}
 				chan->fsm_state = EICON_STATE_ACTIVE;
 				cmd.driver = ccard->myid;
 				cmd.command = ISDN_STAT_BCONN;
@@ -1152,6 +1244,9 @@
 				if (DebugVar & 16)
   					printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect\n", chan->No);
 				if (chan->e.B2Id) idi_do_req(ccard, chan, IDI_N_CONNECT_ACK, 1);
+				if (chan->l2prot == ISDN_PROTO_L2_FAX) {
+					break;
+				}
 				if (chan->l2prot == ISDN_PROTO_L2_MODEM) {
 					chan->fsm_state = EICON_STATE_WMCONN;
 					break;
@@ -1169,6 +1264,12 @@
 					idi_do_req(ccard, chan, IDI_N_DISC_ACK, 1);
 					idi_do_req(ccard, chan, REMOVE, 1);
 				}
+#ifdef CONFIG_ISDN_TTY_FAX
+				if (chan->l2prot == ISDN_PROTO_L2_FAX) {
+					idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
+					idi_fax_hangup(ccard, chan);
+				}
+#endif
 				chan->queued = 0;
 				chan->waitq = 0;
 				chan->waitpq = 0;
@@ -1182,6 +1283,12 @@
 			case IDI_N_DISC_ACK:
 				if (DebugVar & 16)
   					printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC_ACK\n", chan->No);
+#ifdef CONFIG_ISDN_TTY_FAX
+				if (chan->l2prot == ISDN_PROTO_L2_FAX) {
+					idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
+					idi_fax_hangup(ccard, chan);
+				}
+#endif
 				break; 
 			case IDI_N_DATA_ACK:
 				if (DebugVar & 16)
@@ -1191,12 +1298,23 @@
 				skb_pull(skb, sizeof(eicon_IND) - 1);
 				if (DebugVar & 128)
 					printk(KERN_DEBUG"idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len);
-				ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb);
-				free_buff = 0; 
+				if (chan->l2prot == ISDN_PROTO_L2_FAX) {
+#ifdef CONFIG_ISDN_TTY_FAX
+					idi_faxdata_rcv(ccard, chan, skb);
+#endif
+				} else {
+					ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb);
+					free_buff = 0; 
+				}
 				break; 
 			case IDI_N_UDATA:
 				idi_parse_udata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
 				break; 
+#ifdef CONFIG_ISDN_TTY_FAX
+			case IDI_N_EDATA:
+				idi_edata_action(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
+				break; 
+#endif
 			default:
 				if (DebugVar & 8)
 					printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind);
@@ -1231,21 +1349,19 @@
 
 	/* Remove an Id */
 	if (chan->e.Req == REMOVE) {
-		if (ack->Reference == chan->e.ref) {
-			ccard->IdTable[ack->RcId] = NULL;
-			if (DebugVar & 16)
-				printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%d Ch=%d (%s)\n", chan->No,
-					ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig");
-			if (!chan->e.ReqCh) 
-				chan->e.D3Id = 0;
-			else
-				chan->e.B2Id = 0;
-		}
-		else {
+		if (ack->Reference != chan->e.ref) {
 			if (DebugVar & 1)
 				printk(KERN_DEBUG "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No,
 					ack->Reference, chan->e.ref);
 		}
+		ccard->IdTable[ack->RcId] = NULL;
+		if (DebugVar & 16)
+			printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%d Ch=%d (%s)\n", chan->No,
+				ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig");
+		if (!chan->e.ReqCh) 
+			chan->e.D3Id = 0;
+		else
+			chan->e.B2Id = 0;
 		return;
 	}
 
@@ -1268,6 +1384,25 @@
 						ccard->interface.statcallb(&cmd);
 					}
 					chan->waitpq = 0;
+#ifdef CONFIG_ISDN_TTY_FAX
+					if (chan->l2prot == ISDN_PROTO_L2_FAX) {
+						if (((chan->queued - chan->waitq) < 1) &&
+						    (chan->fax2.Eop)) {
+							chan->fax2.Eop = 0;
+							if (chan->fax) {
+								cmd.driver = ccard->myid;
+								cmd.command = ISDN_STAT_FAXIND;
+								cmd.arg = chan->No;
+								chan->fax->r_code = ISDN_TTY_FAX_SENT;
+								ccard->interface.statcallb(&cmd);
+							}
+							else {
+								if (DebugVar & 1)
+									printk(KERN_DEBUG "idi_ack: Sent with NULL fax struct, ERROR\n");
+							}
+						}
+					}
+#endif
 				}
 				chan->queued -= chan->waitq;
 				if (chan->queued < 0) chan->queued = 0;
@@ -1300,6 +1435,7 @@
 			if (DebugVar & 1)
 				printk(KERN_ERR "idi_ack: Ch%d: unhandled RC 0x%x\n",
 					dCh, ack->Rc);
+			break;
 		case READY_INT:
 		case TIMER_INT:
 			/* we do nothing here */
@@ -1317,7 +1453,7 @@
 		case ASSIGN_OK:
 			if (chan) {
 				if (DebugVar & 1)
-					printk(KERN_ERR "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%d,%d)\n",
+					printk(KERN_ERR "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%x,%x)\n",
 						chan->No, chan->e.D3Id, chan->e.B2Id);
 			}
 			for(j = 0; j < ccard->nchannels + 1; j++) {
@@ -1330,7 +1466,7 @@
 					ccard->bch[j].e.busy = 0;
 					ccard->bch[j].e.ref = 0;
 					if (DebugVar & 16)
-						printk(KERN_DEBUG"idi_ack: Ch%d: Id %d assigned (%s)\n", j, 
+						printk(KERN_DEBUG"idi_ack: Ch%d: Id %x assigned (%s)\n", j, 
 							ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig");
 					break;
 				}
@@ -1407,10 +1543,14 @@
 	        xmit_skb = alloc_skb(plen + sizeof(eicon_REQ), GFP_ATOMIC);
         	skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
 
-	        if ((!skb) || (!skb2)) {
+	        if ((!xmit_skb) || (!skb2)) {
 			restore_flags(flags);
 			if (DebugVar & 1)
-	        	        printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No);
+	        	        printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in send_data()\n", chan->No);
+			if (xmit_skb) 
+				dev_kfree_skb(skb);
+			if (skb2) 
+				dev_kfree_skb(skb2);
                 	return -ENOMEM;
 	        }
 
@@ -1462,7 +1602,11 @@
 
         if ((!skb) || (!skb2)) {
 		if (DebugVar & 1)
-			printk(KERN_WARNING "idi_err: alloc_skb failed\n");
+			printk(KERN_WARNING "idi_err: alloc_skb failed in manage_assign()\n");
+		if (skb) 
+			dev_kfree_skb(skb);
+		if (skb2) 
+			dev_kfree_skb(skb2);
                 return -ENOMEM;
         }
 
@@ -1501,7 +1645,11 @@
 
         if ((!skb) || (!skb2)) {
 		if (DebugVar & 1)
-                	printk(KERN_WARNING "idi_err: alloc_skb failed\n");
+                	printk(KERN_WARNING "idi_err: alloc_skb failed in manage_remove()\n");
+		if (skb) 
+			dev_kfree_skb(skb);
+		if (skb2) 
+			dev_kfree_skb(skb2);
                 return -ENOMEM;
         }
 
@@ -1538,7 +1686,8 @@
 
         chan = &(card->bch[card->nchannels]);
 
-	if (chan->e.D3Id) return -EBUSY;
+	if (chan->e.D3Id)
+		return -EBUSY;
 	chan->e.D3Id = 1;
 	while((skb2 = skb_dequeue(&chan->e.X)))
 		dev_kfree_skb(skb2);
@@ -1568,6 +1717,7 @@
 		return -ENOMEM;
 	}
 	if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) {
+		kfree(manbuf);
 		chan->e.D3Id = 0;
 		return -EFAULT;
 	}
@@ -1577,7 +1727,11 @@
 
         if ((!skb) || (!skb2)) {
 		if (DebugVar & 1)
-                	printk(KERN_WARNING "idi_err_manif: alloc_skb failed\n");
+                	printk(KERN_WARNING "idi_err_manif: alloc_skb failed in manage()\n");
+		if (skb) 
+			dev_kfree_skb(skb);
+		if (skb2) 
+			dev_kfree_skb(skb2);
 		kfree(manbuf);
 		chan->e.D3Id = 0;
                 return -ENOMEM;
@@ -1623,11 +1777,13 @@
 	}
 
 	if ((ret = eicon_idi_manage_remove(card))) {
+		kfree(manbuf);
 		chan->e.D3Id = 0;
 		return(ret);
 	}
 
 	if (copy_to_user(mb, manbuf, sizeof(eicon_manifbuf))) {
+		kfree(manbuf);
 		chan->e.D3Id = 0;
 		return -EFAULT;
 	}
diff -urN linux-2.2.12-pre3/drivers/isdn/eicon/eicon_idi.h linux-2.2.12-pre4/drivers/isdn/eicon/eicon_idi.h
--- linux-2.2.12-pre3/drivers/isdn/eicon/eicon_idi.h	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/eicon/eicon_idi.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: eicon_idi.h,v 1.5 1999/07/11 17:16:26 armin Exp $
+/* $Id: eicon_idi.h,v 1.6 1999/07/25 15:12:04 armin Exp $
  *
  * ISDN lowlevel-module for the Eicon.Diehl active cards.
  * IDI-Interface
@@ -21,6 +21,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_idi.h,v $
+ * Revision 1.6  1999/07/25 15:12:04  armin
+ * fix of some debug logs.
+ * enabled ISA-cards option.
+ *
  * Revision 1.5  1999/07/11 17:16:26  armin
  * Bugfixes in queue handling.
  * Added DSP-DTMF decoder functions.
@@ -256,5 +260,9 @@
 extern int eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb);
 extern int idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb, int que);
 extern void idi_audio_cmd(eicon_card *ccard, eicon_chan *chan, int cmd, u_char *value);
+#ifdef CONFIG_ISDN_TTY_FAX
+extern void idi_fax_cmd(eicon_card *card, eicon_chan *chan);
+extern int idi_faxdata_send(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb);
+#endif
 
 #endif
diff -urN linux-2.2.12-pre3/drivers/isdn/eicon/eicon_io.c linux-2.2.12-pre4/drivers/isdn/eicon/eicon_io.c
--- linux-2.2.12-pre3/drivers/isdn/eicon/eicon_io.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/eicon/eicon_io.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: eicon_io.c,v 1.1 1999/03/29 11:19:45 armin Exp $
+/* $Id: eicon_io.c,v 1.2 1999/07/25 15:12:05 armin Exp $
  *
  * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
  * Code for communicating with hardware.
@@ -24,6 +24,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_io.c,v $
+ * Revision 1.2  1999/07/25 15:12:05  armin
+ * fix of some debug logs.
+ * enabled ISA-cards option.
+ *
  * Revision 1.1  1999/03/29 11:19:45  armin
  * I/O stuff now in seperate file (eicon_io.c)
  * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
@@ -57,7 +61,7 @@
 						break;
 					default: 
 						printk(KERN_ERR "idi: Indication for unknown channel Ind=%d Id=%d\n", ind->Ind, ind->IndId);
-						printk(KERN_DEBUG "idi_hdl: Ch??: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n",
+						printk(KERN_DEBUG "idi_hdl: Ch??: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n",
 							ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);
 				}
 			}
@@ -303,6 +307,7 @@
         }
 
 	switch(ccard->type) {
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 		case EICON_CTYPE_S:
 		case EICON_CTYPE_SX:
 		case EICON_CTYPE_SCOM:
@@ -314,6 +319,7 @@
 			scom = 0;
 			prram = (eicon_pr_ram *)isa_card->shmem;
 			break;
+#endif
 		case EICON_CTYPE_MAESTRAP:
 			scom = 0;
         		ram = (char *)pci_card->PCIram;
@@ -434,7 +440,7 @@
 			chan->e.busy = 1; 
 			restore_flags(flags);
 			if (DebugVar & 32)
-	                	printk(KERN_DEBUG "eicon: Req=%x Id=%x Ch=%x Len=%x Ref=%d\n", 
+	                	printk(KERN_DEBUG "eicon: Req=%d Id=%x Ch=%d Len=%d Ref=%d\n", 
 							reqbuf->Req, 
 							ram_inb(ccard, &ReqOut->ReqId),
 							reqbuf->ReqCh, reqbuf->XBuffer.length,
@@ -510,6 +516,7 @@
 	isa_card = &ccard->hwif.isa;
 
 	switch(ccard->type) {
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 		case EICON_CTYPE_S:
 		case EICON_CTYPE_SX:
 		case EICON_CTYPE_SCOM:
@@ -523,6 +530,7 @@
 			prram = (eicon_pr_ram *)isa_card->shmem;
 			irqprobe = &isa_card->irqprobe;
 			break;
+#endif
 		case EICON_CTYPE_MAESTRAP:
 			scom = 0;
 			ram = (char *)pci_card->PCIram;
@@ -546,6 +554,7 @@
 
 	if (*irqprobe) {
 		switch(ccard->type) {
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 			case EICON_CTYPE_S:
 			case EICON_CTYPE_SX:
 			case EICON_CTYPE_SCOM:
@@ -563,6 +572,7 @@
 				}
 				(*irqprobe)++;
 				break;
+#endif
 			case EICON_CTYPE_MAESTRAP:
 	        		if (readb(&ram[0x3fe])) { 
         		               	writeb(0, &prram->RcOutput);
@@ -581,6 +591,7 @@
 	}
 
 	switch(ccard->type) {
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 		case EICON_CTYPE_S:
 		case EICON_CTYPE_SX:
 		case EICON_CTYPE_SCOM:
@@ -592,6 +603,7 @@
 				return;
 			} 
 			break;
+#endif
 		case EICON_CTYPE_MAESTRAP:
 			if (!(readb(&ram[0x3fe]))) { /* card did not interrupt */
 				if (DebugVar & 1)
@@ -629,7 +641,7 @@
 			ack->RcCh = ram_inb(ccard, &com->RcCh);
 			ack->Reference = ccard->ref_in++;
 			if (DebugVar & 64)
-                        	printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n",
+                        	printk(KERN_INFO "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n",
 					tmp,ack->RcId,ack->RcCh,ack->Reference);
 			skb_queue_tail(&ccard->rackq, skb);
 			eicon_schedule_ack(ccard);
@@ -652,7 +664,7 @@
 			ind->MLength = ram_inw(ccard, &com->MLength);
 			ind->RBuffer.length = len; 
 			if (DebugVar & 64)
-                        	printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n",
+                        	printk(KERN_INFO "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n",
 				tmp,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len);
 			ram_copyfromcard(ccard, &ind->RBuffer.P, &com->RBuffer.P, len);
 			skb_queue_tail(&ccard->rcvq, skb);
@@ -679,7 +691,7 @@
 				ack->RcCh = ram_inb(ccard, &RcIn->RcCh);
 				ack->Reference = ram_inw(ccard, &RcIn->Reference);
 				if (DebugVar & 64)
-	                        	printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n",
+	                        	printk(KERN_INFO "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n",
 						Rc,ack->RcId,ack->RcCh,ack->Reference);
                         	ram_outb(ccard, &RcIn->Rc, 0);
 				 skb_queue_tail(&ccard->rackq, skb);
@@ -711,7 +723,7 @@
 				ind->MLength = ram_inw(ccard, &IndIn->MLength);
 				ind->RBuffer.length = len;
 				if (DebugVar & 64)
-	                        	printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n",
+	                        	printk(KERN_INFO "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n",
 					Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len);
                                 ram_copyfromcard(ccard, &ind->RBuffer.P, &IndIn->RBuffer.P, len);
 				skb_queue_tail(&ccard->rcvq, skb);
@@ -728,6 +740,7 @@
 
 	/* clear interrupt */
 	switch(ccard->type) {
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 		case EICON_CTYPE_QUADRO:
 			writeb(0, isa_card->intack);
 			writeb(0, &com[0x401]);
@@ -738,6 +751,7 @@
 		case EICON_CTYPE_S2M:
 			writeb(0, isa_card->intack);
 			break;
+#endif
 		case EICON_CTYPE_MAESTRAP:
 			writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]);
 			writew(0, &cfg[MP_IRQ_RESET + 2]); 
diff -urN linux-2.2.12-pre3/drivers/isdn/eicon/eicon_isa.c linux-2.2.12-pre4/drivers/isdn/eicon/eicon_isa.c
--- linux-2.2.12-pre3/drivers/isdn/eicon/eicon_isa.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/eicon/eicon_isa.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: eicon_isa.c,v 1.5 1999/04/01 12:48:33 armin Exp $
+/* $Id: eicon_isa.c,v 1.6 1999/07/25 15:12:06 armin Exp $
  *
  * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
  * Hardware-specific code for old ISA cards.
@@ -22,6 +22,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_isa.c,v $
+ * Revision 1.6  1999/07/25 15:12:06  armin
+ * fix of some debug logs.
+ * enabled ISA-cards option.
+ *
  * Revision 1.5  1999/04/01 12:48:33  armin
  * Changed some log outputs.
  *
@@ -53,7 +57,9 @@
 #define release_shmem release_region
 #define request_shmem request_region
 
-char *eicon_isa_revision = "$Revision: 1.5 $";
+char *eicon_isa_revision = "$Revision: 1.6 $";
+
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 
 /* Mask for detecting invalid IRQ parameter */
 static int eicon_isa_valid_irq[] = {
@@ -430,3 +436,5 @@
 	card->irqprobe = 0;
 	return 0;
 }
+
+#endif /* CONFIG_ISDN_DRV_EICON_ISA */
diff -urN linux-2.2.12-pre3/drivers/isdn/eicon/eicon_mod.c linux-2.2.12-pre4/drivers/isdn/eicon/eicon_mod.c
--- linux-2.2.12-pre3/drivers/isdn/eicon/eicon_mod.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/eicon/eicon_mod.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: eicon_mod.c,v 1.7 1999/07/11 17:16:27 armin Exp $
+/* $Id: eicon_mod.c,v 1.8 1999/07/25 15:12:08 armin Exp $
  *
  * ISDN lowlevel-module for Eicon.Diehl active cards.
  * 
@@ -26,6 +26,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_mod.c,v $
+ * Revision 1.8  1999/07/25 15:12:08  armin
+ * fix of some debug logs.
+ * enabled ISA-cards option.
+ *
  * Revision 1.7  1999/07/11 17:16:27  armin
  * Bugfixes in queue handling.
  * Added DSP-DTMF decoder functions.
@@ -73,7 +77,7 @@
 static eicon_card *cards = (eicon_card *) NULL;   /* glob. var , contains
                                                      start of card-list   */
 
-static char *eicon_revision = "$Revision: 1.7 $";
+static char *eicon_revision = "$Revision: 1.8 $";
 
 extern char *eicon_pci_revision;
 extern char *eicon_isa_revision;
@@ -88,19 +92,23 @@
 ulong DebugVar;
 
 /* Parameters to be set by insmod */
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 static int   membase      = -1;
 static int   irq          = -1;
+#endif
 static char *id           = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
 
 MODULE_DESCRIPTION(             "Driver for Eicon.Diehl active ISDN cards");
 MODULE_AUTHOR(                  "Armin Schindler");
 MODULE_SUPPORTED_DEVICE(        "ISDN subsystem");
+MODULE_PARM_DESC(id,   		"ID-String of first card");
+MODULE_PARM(id,           	"s");
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 MODULE_PARM_DESC(membase,	"Base address of first ISA card");
 MODULE_PARM_DESC(irq,    	"IRQ of first card");
-MODULE_PARM_DESC(id,   		"ID-String of first card");
 MODULE_PARM(membase,    	"i");
 MODULE_PARM(irq,          	"i");
-MODULE_PARM(id,           	"s");
+#endif
 
 char *eicon_ctype_name[] = {
         "ISDN-S",
@@ -382,6 +390,7 @@
 							       card->bus);
 							ret = -ENODEV;
 					}
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 				case EICON_IOCTL_SETMMIO:
 					if (card->flags & EICON_FLAGS_LOADED)
 						return -EBUSY;
@@ -401,6 +410,7 @@
 							       card->bus);
 							ret = -ENODEV;
 					}					
+#endif
 				case EICON_IOCTL_GETIRQ:
 					switch (card->bus) {
 						case EICON_BUS_ISA:
@@ -434,6 +444,7 @@
 							       card->bus);
 							ret = -ENODEV;
 					}					
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 				case EICON_IOCTL_LOADBOOT:
 					if (card->flags & EICON_FLAGS_RUNNING)
 						return -EBUSY;  
@@ -452,6 +463,8 @@
 							ret = -ENODEV;
 					}
 					return ret;
+#endif
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 				case EICON_IOCTL_LOADISA:
 					if (card->flags & EICON_FLAGS_RUNNING)
 						return -EBUSY;  
@@ -484,7 +497,7 @@
 							ret = -ENODEV;
 					}
 					return ret;
-
+#endif
 				case EICON_IOCTL_MANIF:
 					if (!card->flags & EICON_FLAGS_RUNNING)
 						return -ENODEV;
@@ -813,7 +826,9 @@
 	int i;
 	int j;
 	int qloop;
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 	char qid[5];
+#endif
         eicon_card *card;
 #if CONFIG_PCI
 	eicon_pci_card *pcic;
@@ -854,6 +869,7 @@
 		card->myid = -1;
 		card->type = Type;
 		switch (Type) {
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 #if CONFIG_MCA /* only needed for MCA */
                         case EICON_CTYPE_S:
                         case EICON_CTYPE_SX:
@@ -913,6 +929,7 @@
 				card->nchannels = 2;
 				card->interface.channels = 1;
 				break;
+#endif
 #if CONFIG_PCI
 			case EICON_CTYPE_MAESTRA:
 				(eicon_pci_card *)pcic = (eicon_pci_card *)membase;
@@ -964,6 +981,7 @@
 				card->interface.channels = 1;
 				break;
 #endif
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 			case EICON_CTYPE_ISABRI:
 				if (membase == -1)
 					membase = EICON_ISA_MEMBASE;
@@ -992,6 +1010,7 @@
 				card->nchannels = 30;
 				card->interface.channels = 1;
 				break;
+#endif
 			default:
 				printk(KERN_WARNING "eicon_alloccard: Invalid type %d\n", Type);
 				kfree(card);
@@ -1029,6 +1048,7 @@
 eicon_registercard(eicon_card * card)
 {
         switch (card->bus) {
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 		case EICON_BUS_ISA:
 			/* TODO something to print */
 			break;
@@ -1037,6 +1057,7 @@
 			eicon_isa_printpar(&card->hwif.isa);
 			break;
 #endif
+#endif
 		case EICON_BUS_PCI:
 #if CONFIG_PCI
 			eicon_pci_printpar(&card->hwif.pci); 
@@ -1070,12 +1091,14 @@
         cmd.driver = card->myid;
         card->interface.statcallb(&cmd);
         switch (card->bus) {
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 		case EICON_BUS_ISA:
 #ifdef CONFIG_MCA
 		case EICON_BUS_MCA:
 #endif
 			eicon_isa_release(&card->hwif.isa);
 			break;
+#endif
 		case EICON_BUS_PCI:
 #if CONFIG_PCI
 			eicon_pci_release(&card->hwif.pci);
@@ -1107,9 +1130,11 @@
 	int added = 0;
 	int failed = 0;
 
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 	if (!Type) /* ISA */
 		if ((Type = eicon_isa_find_card(membase, irq, id)) < 0)
 			return 0;
+#endif
 	eicon_alloccard(Type, membase, irq, id);
         p = cards;
         while (p) {
@@ -1120,12 +1145,14 @@
 			 */
 			added++;
 			switch (p->bus) {
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 				case EICON_BUS_ISA:
 				case EICON_BUS_MCA:
 					if (eicon_registercard(p))
 						break;
 					registered = 1;
 					break;
+#endif
 				case EICON_BUS_PCI:
 #if CONFIG_PCI
 					if (eicon_registercard(p))
@@ -1187,10 +1214,18 @@
 	printk("%s/", eicon_getrev(tmprev));
 	release += getrel(tmprev);
 	strcpy(tmprev, eicon_pci_revision);
+#if CONFIG_PCI
 	printk("%s/", eicon_getrev(tmprev));
+#else
+	printk("---/");
+#endif
 	release += getrel(tmprev);
 	strcpy(tmprev, eicon_isa_revision);
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 	printk("%s/", eicon_getrev(tmprev));
+#else
+	printk("---/");
+#endif
 	release += getrel(tmprev);
 	strcpy(tmprev, eicon_idi_revision);
 	printk("%s\n", eicon_getrev(tmprev));
@@ -1199,6 +1234,7 @@
         printk(KERN_INFO "%s Release: %s.%s\n", DRIVERNAME,
 		DRIVERRELEASE, tmprev);
 
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 #ifdef CONFIG_MCA
 	/* Check if we have MCA-bus */
         if (!MCA_bus)
@@ -1216,14 +1252,23 @@
 #else
 	card_count = eicon_addcard(0, membase, irq, id);
 #endif /* CONFIG_MCA */
+#endif /* CONFIG_ISDN_DRV_EICON_ISA */
  
 #if CONFIG_PCI
 	card_count += eicon_pci_find_card(id);
 #endif
         if (!cards) {
 #ifdef MODULE
+#ifndef CONFIG_PCI
+#ifndef CONFIG_ISDN_DRV_EICON_ISA
+                printk(KERN_INFO "Eicon: Driver is neither ISA nor PCI compiled !\n");
+#else
                 printk(KERN_INFO "Eicon: No cards defined, driver not loaded !\n");
 #endif
+#else
+                printk(KERN_INFO "Eicon: No PCI-cards found, driver not loaded !\n");
+#endif
+#endif
 		return -ENODEV;
 
 	} else
@@ -1291,7 +1336,7 @@
 }
 #endif /* MODULE */
 
-
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 #ifdef CONFIG_MCA
 
 struct eicon_mca_adapters_struct {
@@ -1457,4 +1502,5 @@
 	};
 };
 #endif /* CONFIG_MCA */
+#endif /* CONFIG_ISDN_DRV_EICON_ISA */
 
diff -urN linux-2.2.12-pre3/drivers/isdn/eicon/eicon_pci.c linux-2.2.12-pre4/drivers/isdn/eicon/eicon_pci.c
--- linux-2.2.12-pre3/drivers/isdn/eicon/eicon_pci.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/eicon/eicon_pci.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: eicon_pci.c,v 1.7 1999/06/09 19:31:29 armin Exp $
+/* $Id: eicon_pci.c,v 1.9 1999/08/11 21:01:11 keil Exp $
  *
  * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
  * Hardware-specific code for PCI cards.
@@ -26,6 +26,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_pci.c,v $
+ * Revision 1.9  1999/08/11 21:01:11  keil
+ * new PCI codefix
+ *
+ * Revision 1.8  1999/08/10 16:02:20  calle
+ * struct pci_dev changed in 2.3.13. Made the necessary changes.
+ *
  * Revision 1.7  1999/06/09 19:31:29  armin
  * Wrong PLX size for request_region() corrected.
  * Added first MCA code from Erik Weber.
@@ -64,7 +70,7 @@
 #include "eicon_pci.h"
 
 
-char *eicon_pci_revision = "$Revision: 1.7 $";
+char *eicon_pci_revision = "$Revision: 1.9 $";
 
 #if CONFIG_PCI	         /* intire stuff is only for PCI */
 
@@ -139,8 +145,8 @@
           aparms->type = EICON_CTYPE_MAESTRA;
 
           aparms->irq = pdev->irq;
-          preg = pdev->base_address[2] & 0xfffffffc;
-          pcfg = pdev->base_address[1] & 0xffffff80;
+          preg = get_pcibase(pdev, 2) & 0xfffffffc;
+          pcfg = get_pcibase(pdev, 1) & 0xffffff80;
 
 #ifdef EICON_PCI_DEBUG
           printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq);
@@ -161,9 +167,9 @@
          printk(KERN_INFO "Eicon: DIVA Server PRI/PCI detected !\n");
           aparms->type = EICON_CTYPE_MAESTRAP; /*includes 9M,30M*/
           aparms->irq = pdev->irq;
-          pram = pdev->base_address[0] & 0xfffff000;
-          preg = pdev->base_address[2] & 0xfffff000;
-          pcfg = pdev->base_address[4] & 0xfffff000;
+          pram = get_pcibase(pdev, 0) & 0xfffff000;
+          preg = get_pcibase(pdev, 2) & 0xfffff000;
+          pcfg = get_pcibase(pdev, 4) & 0xfffff000;
 
 #ifdef EICON_PCI_DEBUG
           printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq);
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/avm_pci.c linux-2.2.12-pre4/drivers/isdn/hisax/avm_pci.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/avm_pci.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/avm_pci.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: avm_pci.c,v 1.9 1999/07/12 21:04:57 keil Exp $
+/* $Id: avm_pci.c,v 1.11 1999/08/11 21:01:18 keil Exp $
 
  * avm_pci.c    low level stuff for AVM Fritz!PCI and ISA PnP isdn cards
  *              Thanks to AVM, Berlin for informations
@@ -7,6 +7,12 @@
  *
  *
  * $Log: avm_pci.c,v $
+ * Revision 1.11  1999/08/11 21:01:18  keil
+ * new PCI codefix
+ *
+ * Revision 1.10  1999/08/10 16:01:44  calle
+ * struct pci_dev changed in 2.3.13. Made the necessary changes.
+ *
  * Revision 1.9  1999/07/12 21:04:57  keil
  * fix race in IRQ handling
  * added watchdog for lost IRQs
@@ -50,7 +56,7 @@
 #include <linux/interrupt.h>
 
 extern const char *CardType[];
-static const char *avm_pci_rev = "$Revision: 1.9 $";
+static const char *avm_pci_rev = "$Revision: 1.11 $";
 
 #define  AVM_FRITZ_PCI		1
 #define  AVM_FRITZ_PNP		2
@@ -802,7 +808,7 @@
 				printk(KERN_WARNING "FritzPCI: No IRQ for PCI card found\n");
 				return(0);
 			}
-			cs->hw.avm.cfg_reg = dev_avm->base_address[1] &
+			cs->hw.avm.cfg_reg = get_pcibase(dev_avm, 1) &
 				PCI_BASE_ADDRESS_IO_MASK; 
 			if (!cs->hw.avm.cfg_reg) {
 				printk(KERN_WARNING "FritzPCI: No IO-Adr for PCI card found\n");
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/bkm_a4t.c linux-2.2.12-pre4/drivers/isdn/hisax/bkm_a4t.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/bkm_a4t.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/bkm_a4t.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: bkm_a4t.c,v 1.4 1999/07/14 11:43:14 keil Exp $
+/* $Id: bkm_a4t.c,v 1.6 1999/08/11 21:01:22 keil Exp $
  * bkm_a4t.c    low level stuff for T-Berkom A4T
  *              derived from the original file sedlbauer.c
  *              derived from the original file niccy.c
@@ -7,6 +7,12 @@
  * Author       Roland Klabunde (R.Klabunde@Berkom.de)
  *
  * $Log: bkm_a4t.c,v $
+ * Revision 1.6  1999/08/11 21:01:22  keil
+ * new PCI codefix
+ *
+ * Revision 1.5  1999/08/10 16:01:46  calle
+ * struct pci_dev changed in 2.3.13. Made the necessary changes.
+ *
  * Revision 1.4  1999/07/14 11:43:14  keil
  * correct PCI_SUBSYSTEM_VENDOR_ID
  *
@@ -35,7 +41,7 @@
 
 extern const char *CardType[];
 
-const char *bkm_a4t_revision = "$Revision: 1.4 $";
+const char *bkm_a4t_revision = "$Revision: 1.6 $";
 
 
 static inline u_char
@@ -314,7 +320,7 @@
 			&sub_sys_id);
 		if (sub_sys_id == ((A4T_SUBSYS_ID << 16) | A4T_SUBVEN_ID)) {
 			found = 1;
-			pci_memaddr = dev_a4t->base_address[0];
+			pci_memaddr = get_pcibase(dev_a4t, 0);
 			cs->irq = dev_a4t->irq;
 		}
 	}
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/bkm_a8.c linux-2.2.12-pre4/drivers/isdn/hisax/bkm_a8.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/bkm_a8.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/bkm_a8.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: bkm_a8.c,v 1.4 1999/07/14 11:43:15 keil Exp $
+/* $Id: bkm_a8.c,v 1.6 1999/08/11 21:01:24 keil Exp $
  * bkm_a8.c     low level stuff for Scitel Quadro (4*S0, passive)
  *              derived from the original file sedlbauer.c
  *              derived from the original file niccy.c
@@ -7,6 +7,12 @@
  * Author       Roland Klabunde (R.Klabunde@Berkom.de)
  *
  * $Log: bkm_a8.c,v $
+ * Revision 1.6  1999/08/11 21:01:24  keil
+ * new PCI codefix
+ *
+ * Revision 1.5  1999/08/10 16:01:48  calle
+ * struct pci_dev changed in 2.3.13. Made the necessary changes.
+ *
  * Revision 1.4  1999/07/14 11:43:15  keil
  * correct PCI_SUBSYSTEM_VENDOR_ID
  *
@@ -36,7 +42,7 @@
 
 extern const char *CardType[];
 
-const char sct_quadro_revision[] = "$Revision: 1.4 $";
+const char sct_quadro_revision[] = "$Revision: 1.6 $";
 
 /* To survive the startup phase */
 typedef struct {
@@ -371,7 +377,7 @@
 			&sub_sys_id);
 		if (sub_sys_id == ((SCT_SUBSYS_ID << 16) | SCT_SUBVEN_ID)) {
 			found = 1;
-			pci_ioaddr1 = dev_a8->base_address[1];
+			pci_ioaddr1 = get_pcibase(dev_a8, 1);
 			pci_irq = dev_a8->irq;
 			pci_bus = dev_a8->bus->number;
 			pci_device_fn = dev_a8->devfn;
@@ -433,7 +439,7 @@
 		pcibios_write_config_dword(pci_bus, pci_device_fn,
 			PCI_BASE_ADDRESS_1, pci_ioaddr1);
 #ifdef COMPAT_HAS_NEW_PCI
-		dev_a8->base_address[1] = pci_ioaddr1;
+		get_pcibase(dev_a8, 1) = pci_ioaddr1;
 #endif /* COMPAT_HAS_NEW_PCI */
 	}
 /* End HACK */
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/callc.c linux-2.2.12-pre4/drivers/isdn/hisax/callc.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/callc.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/callc.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: callc.c,v 2.29 1999/07/13 21:05:41 werner Exp $
+/* $Id: callc.c,v 2.31 1999/08/05 20:43:10 keil Exp $
 
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
@@ -11,6 +11,12 @@
  *              Fritz Elfert
  *
  * $Log: callc.c,v $
+ * Revision 2.31  1999/08/05 20:43:10  keil
+ * ISAR analog modem support
+ *
+ * Revision 2.30  1999/07/25 16:24:04  keil
+ * Fixed TEI now working again
+ *
  * Revision 2.29  1999/07/13 21:05:41  werner
  * Modified set_channel_limit to use new callback ISDN_STAT_DISCH.
  *
@@ -133,7 +139,7 @@
 #endif /* COMPAT_HAS_NEW_SYMTAB */
 #endif	/* MODULE */
 
-const char *lli_revision = "$Revision: 2.29 $";
+const char *lli_revision = "$Revision: 2.31 $";
 
 extern struct IsdnCard cards[];
 extern int nrcards;
@@ -304,7 +310,7 @@
 HL_LL(struct Channel *chanp, int command)
 {
         isdn_ctrl ic;
- 
+
         ic.driver = chanp->cs->myid;
         ic.command = command;
         ic.arg = chanp->chan;
@@ -430,12 +436,21 @@
 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");
-	HL_LL(chanp, ISDN_STAT_BCONN);
+		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);
 }
 
@@ -1341,9 +1356,10 @@
 			st->l1.mode = L1_MODE_TRANS;
 			break;
 		case (ISDN_PROTO_L2_MODEM):
-			st->l1.mode = L1_MODE_MODEM;
+			st->l1.mode = L1_MODE_V32;
 			break;
 	}
+	chanp->bcs->conmsg = NULL;
 	if (chanp->bcs->BC_SetStack(st, chanp->bcs))
 		return (-1);
 	st->l2.flag = 0;
@@ -1747,6 +1763,8 @@
 					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 (9): /* load firmware */
 					memcpy(&adr, ic->parm.num, sizeof(ulong));
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/cert.c linux-2.2.12-pre4/drivers/isdn/hisax/cert.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/cert.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/cert.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: cert.c,v 2.1 1998/11/15 23:51:15 keil Exp $
+/* $Id: cert.c,v 2.2 1999/08/07 17:35:05 keil Exp $
 
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
@@ -7,6 +7,9 @@
  *		../../../Documentation/isdn/HiSax.cert
  *
  * $Log: cert.c,v $
+ * Revision 2.2  1999/08/07 17:35:05  keil
+ * approval for Eicon Technology Diva 2.01 PCI
+ *
  * Revision 2.1  1998/11/15 23:51:15  keil
  * certification stuff
  *
@@ -29,6 +32,7 @@
 		printk(KERN_INFO "HiSax: Approval registration numbers:\n");
 		printk(KERN_INFO "HiSax: German D133361J CETECOM ICT Services GmbH\n");
 		printk(KERN_INFO "HiSax: EU (D133362J) CETECOM ICT Services GmbH\n");
+		printk(KERN_INFO "HiSax: Approved with Eicon Technology Diva 2.01 PCI cards\n");
 	}
 	return(0);
 #endif
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/config.c linux-2.2.12-pre4/drivers/isdn/hisax/config.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/config.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/config.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,10 +1,16 @@
-/* $Id: config.c,v 2.28 1999/07/14 12:38:36 werner Exp $
+/* $Id: config.c,v 2.30 1999/08/05 20:43:14 keil Exp $
 
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
  *
  * $Log: config.c,v $
+ * Revision 2.30  1999/08/05 20:43:14  keil
+ * ISAR analog modem support
+ *
+ * Revision 2.29  1999/07/21 14:46:00  keil
+ * changes from EICON certification
+ *
  * Revision 2.28  1999/07/14 12:38:36  werner
  * Added changes for echo channel handling
  *
@@ -508,9 +514,9 @@
 
 	printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n");
 #ifdef MODULE
-	printk(KERN_INFO "HiSax: Version 3.2a (module)\n");
+	printk(KERN_INFO "HiSax: Version 3.3 (module)\n");
 #else
-	printk(KERN_INFO "HiSax: Version 3.2a (kernel)\n");
+	printk(KERN_INFO "HiSax: Version 3.3 (kernel)\n");
 #endif
 	strcpy(tmp, l1_revision);
 	printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp));
@@ -1047,7 +1053,7 @@
 		cs->iif.features =
 			ISDN_FEATURE_L2_X75I |
 			ISDN_FEATURE_L2_HDLC |
-//			ISDN_FEATURE_L2_MODEM |
+			ISDN_FEATURE_L2_MODEM |
 			ISDN_FEATURE_L2_TRANS |
 			ISDN_FEATURE_L3_TRANS |
 #ifdef	CONFIG_HISAX_1TR6
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/diva.c linux-2.2.12-pre4/drivers/isdn/hisax/diva.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/diva.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/diva.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,13 +1,29 @@
-/* $Id: diva.c,v 1.12 1999/07/12 21:05:04 keil Exp $
+/* $Id: diva.c,v 1.16 1999/08/11 21:01:25 keil Exp $
 
  * diva.c     low level stuff for Eicon.Diehl Diva Family ISDN cards
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
  *
- * Thanks to Eicon Technology Diehl GmbH & Co. oHG for documents and informations
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
+ * Thanks to Eicon Technology for documents and informations
  *
  *
  * $Log: diva.c,v $
+ * Revision 1.16  1999/08/11 21:01:25  keil
+ * new PCI codefix
+ *
+ * Revision 1.15  1999/08/10 16:01:49  calle
+ * struct pci_dev changed in 2.3.13. Made the necessary changes.
+ *
+ * Revision 1.14  1999/08/07 17:35:08  keil
+ * approval for Eicon Technology Diva 2.01 PCI
+ *
+ * Revision 1.13  1999/07/21 14:46:07  keil
+ * changes from EICON certification
+ *
  * Revision 1.12  1999/07/12 21:05:04  keil
  * fix race in IRQ handling
  * added watchdog for lost IRQs
@@ -64,7 +80,7 @@
 
 extern const char *CardType[];
 
-const char *Diva_revision = "$Revision: 1.12 $";
+const char *Diva_revision = "$Revision: 1.16 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -891,7 +907,7 @@
 		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) {
+		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;
@@ -922,23 +938,23 @@
 			PCI_DIVA20_ID, dev_diva))) {
 			cs->subtyp = DIVA_PCI;
 			cs->irq = dev_diva->irq;
-			cs->hw.diva.cfg_reg = dev_diva->base_address[2]
+			cs->hw.diva.cfg_reg = get_pcibase(dev_diva, 2)
 				& PCI_BASE_ADDRESS_IO_MASK;
 		} else if ((dev_diva_u = pci_find_device(PCI_VENDOR_EICON_DIEHL,
 			PCI_DIVA20_U_ID, dev_diva_u))) {
 			cs->subtyp = DIVA_PCI;
 			cs->irq = dev_diva_u->irq;
-			cs->hw.diva.cfg_reg = dev_diva_u->base_address[2]
+			cs->hw.diva.cfg_reg = get_pcibase(dev_diva_u, 2)
 				& PCI_BASE_ADDRESS_IO_MASK;
 		} else if ((dev_diva201 = pci_find_device(PCI_VENDOR_EICON_DIEHL,
 			PCI_DIVA_201, dev_diva201))) {
 			cs->subtyp = DIVA_IPAC_PCI;
 			cs->irq = dev_diva201->irq;
 			cs->hw.diva.pci_cfg =
-				(ulong) ioremap((dev_diva201->base_address[0]
+				(ulong) ioremap((get_pcibase(dev_diva201, 0)
 					& PCI_BASE_ADDRESS_IO_MASK), 4096);
 			cs->hw.diva.cfg_reg =
-				(ulong) ioremap((dev_diva201->base_address[1]
+				(ulong) ioremap((get_pcibase(dev_diva201, 1)
 					& PCI_BASE_ADDRESS_IO_MASK), 4096);
 		} else {
 			printk(KERN_WARNING "Diva: No PCI card found\n");
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/elsa.c linux-2.2.12-pre4/drivers/isdn/hisax/elsa.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/elsa.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/elsa.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: elsa.c,v 2.14 1999/07/12 21:05:07 keil Exp $
+/* $Id: elsa.c,v 2.17 1999/08/11 20:57:40 keil Exp $
 
  * elsa.c     low level stuff for Elsa isdn cards
  *
@@ -13,8 +13,19 @@
  *              Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE)
  *              for ELSA PCMCIA support
  *
- *
  * $Log: elsa.c,v $
+ * Revision 2.17  1999/08/11 20:57:40  keil
+ * bugfix IPAC version 1.1
+ * new PCI codefix
+ *
+ * Revision 2.16  1999/08/10 16:01:51  calle
+ * struct pci_dev changed in 2.3.13. Made the necessary changes.
+ *
+ * Revision 2.15  1999/08/09 19:25:21  keil
+ * Support (alpha version) for the '98 model of ELSA Microlink ISDN/MC
+ * by Christer Weinigel, Cendio Systems AB <wingel@cendio.se>
+ * Add support for IPAC 1.2
+ *
  * Revision 2.14  1999/07/12 21:05:07  keil
  * fix race in IRQ handling
  * added watchdog for lost IRQs
@@ -85,10 +96,11 @@
 
 extern const char *CardType[];
 
-const char *Elsa_revision = "$Revision: 2.14 $";
+const char *Elsa_revision = "$Revision: 2.17 $";
 const char *Elsa_Types[] =
 {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro",
- "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI"};
+ "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI", 
+ "PCMCIA-IPAC" };
 
 const char *ITACVer[] =
 {"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2",
@@ -118,6 +130,7 @@
 #define ELSA_QS3000  8
 #define ELSA_QS1000PCI 9
 #define ELSA_QS3000PCI 10
+#define ELSA_PCMCIA_IPAC 11
 
 /* PCI stuff */
 #define PCI_VENDOR_ELSA	0x1048
@@ -445,9 +458,11 @@
 		printk(KERN_WARNING "Elsa: Spurious interrupt!\n");
 		return;
 	}
-	val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */
-	if (!(val & ELSA_PCI_IRQ_MASK))
-		return;
+	if (cs->subtyp == ELSA_QS1000PCI || cs->subtyp == ELSA_QS3000PCI) {
+		val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */
+		if (!(val & ELSA_PCI_IRQ_MASK))
+			return;
+	}
 #if ARCOFI_USE
 	if (cs->hw.elsa.MFlag) {
 		val = serial_inp(cs, UART_IIR);
@@ -513,6 +528,9 @@
 		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) ||
@@ -547,7 +565,7 @@
 		if (cs->hw.elsa.trig)
 			byteout(cs->hw.elsa.trig, 0xff);
 	}
-	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
+	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI) || (cs->subtyp == ELSA_PCMCIA_IPAC)) {
 		save_flags(flags);
 		sti();
 		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20);
@@ -558,8 +576,14 @@
 		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0);
 		schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
 		restore_flags(flags);
-		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x0);
-		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0x3c);
+		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 */
@@ -675,7 +699,7 @@
 {
 	int blink = 0;
 
-	if (cs->subtyp == ELSA_PCMCIA)
+	if (cs->subtyp == ELSA_PCMCIA || cs->subtyp == ELSA_PCMCIA_IPAC)
 		return;
 	del_timer(&cs->hw.elsa.tl);
 	if (cs->hw.elsa.status & ELSA_ASSIGN)
@@ -739,6 +763,7 @@
 			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) {
@@ -989,10 +1014,19 @@
 	} else if (cs->typ == ISDN_CTYPE_ELSA_PCMCIA) {
 		cs->hw.elsa.base = card->para[1];
 		cs->irq = card->para[0];
-		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;
+		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;
@@ -1013,17 +1047,17 @@
 			 dev_qs1000))) {
 				cs->subtyp = ELSA_QS1000PCI;
 			cs->irq = dev_qs1000->irq;
-			cs->hw.elsa.cfg = dev_qs1000->base_address[1] & 
+			cs->hw.elsa.cfg = get_pcibase(dev_qs1000, 1) & 
 				PCI_BASE_ADDRESS_IO_MASK;
-			cs->hw.elsa.base = dev_qs1000->base_address[3] & 
+			cs->hw.elsa.base = get_pcibase(dev_qs1000, 3) & 
 				PCI_BASE_ADDRESS_IO_MASK;
 		} else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ELSA,
 			PCI_QS3000_ID, dev_qs3000))) {
 			cs->subtyp = ELSA_QS3000PCI;
 			cs->irq = dev_qs3000->irq;
-			cs->hw.elsa.cfg = dev_qs3000->base_address[1] & 
+			cs->hw.elsa.cfg = get_pcibase(dev_qs3000, 1) & 
 				PCI_BASE_ADDRESS_IO_MASK;
-			cs->hw.elsa.base = dev_qs3000->base_address[3] & 
+			cs->hw.elsa.base = get_pcibase(dev_qs3000, 3) & 
 				PCI_BASE_ADDRESS_IO_MASK;
 		} else {
 			printk(KERN_WARNING "Elsa: No PCI card found\n");
@@ -1123,6 +1157,7 @@
 		case ELSA_PCC16:
 		case ELSA_QS1000:
 		case ELSA_PCMCIA:
+		case ELSA_PCMCIA_IPAC:
 			bytecnt = 8;
 			break;
 		case ELSA_PCFPRO:
@@ -1197,7 +1232,7 @@
 	cs->BC_Send_Data = &hscx_fill_fifo;
 	cs->cardmsg = &Elsa_card_msg;
 	reset_elsa(cs);
-	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
+	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;
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/gazel.c linux-2.2.12-pre4/drivers/isdn/hisax/gazel.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/gazel.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/gazel.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: gazel.c,v 2.3 1999/07/12 21:05:09 keil Exp $
+/* $Id: gazel.c,v 2.5 1999/08/11 21:01:26 keil Exp $
 
  * gazel.c     low level stuff for Gazel isdn cards
  *
@@ -6,6 +6,12 @@
  *              based on source code from Karsten Keil
  *
  * $Log: gazel.c,v $
+ * Revision 2.5  1999/08/11 21:01:26  keil
+ * new PCI codefix
+ *
+ * Revision 2.4  1999/08/10 16:01:54  calle
+ * struct pci_dev changed in 2.3.13. Made the necessary changes.
+ *
  * Revision 2.3  1999/07/12 21:05:09  keil
  * fix race in IRQ handling
  * added watchdog for lost IRQs
@@ -29,7 +35,7 @@
 #endif
 
 extern const char *CardType[];
-const char *gazel_revision = "$Revision: 2.3 $";
+const char *gazel_revision = "$Revision: 2.5 $";
 
 #define R647      1
 #define R685      2
@@ -588,8 +594,8 @@
 		if ((dev_tel = pci_find_device(GAZEL_MANUFACTURER, seekcard, dev_tel))) {
 
 			pci_irq = dev_tel->irq;
-			pci_ioaddr0 = dev_tel->base_address[1];
-			pci_ioaddr1 = dev_tel->base_address[2];
+			pci_ioaddr0 = get_pcibase(dev_tel, 1);
+			pci_ioaddr1 = get_pcibase(dev_tel, 2);
 			found = 1;
 		}
 #else
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/hfc_pci.c linux-2.2.12-pre4/drivers/isdn/hisax/hfc_pci.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/hfc_pci.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/hfc_pci.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: hfc_pci.c,v 1.7 1999/07/14 21:24:20 werner Exp $
+/* $Id: hfc_pci.c,v 1.13 1999/08/11 21:01:28 keil Exp $
 
  * hfc_pci.c     low level driver for CCD´s hfc-pci based cards
  *
@@ -23,6 +23,25 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: hfc_pci.c,v $
+ * Revision 1.13  1999/08/11 21:01:28  keil
+ * new PCI codefix
+ *
+ * Revision 1.12  1999/08/10 16:01:58  calle
+ * struct pci_dev changed in 2.3.13. Made the necessary changes.
+ *
+ * Revision 1.11  1999/08/09 19:13:32  werner
+ * moved constant pci ids to pci id table
+ *
+ * Revision 1.10  1999/08/08 10:17:34  werner
+ * added new PCI vendor and card ids for Manufacturer 0x1043
+ *
+ * Revision 1.9  1999/08/07 21:09:10  werner
+ * Fixed another memcpy problem in fifo handling.
+ * Thanks for debugging aid by Olaf Kordwittenborg.
+ *
+ * Revision 1.8  1999/07/23 14:25:15  werner
+ * Some smaller bug fixes and prepared support for GCI/IOM bus
+ *
  * Revision 1.7  1999/07/14 21:24:20  werner
  * fixed memcpy problem when using E-channel feature
  *
@@ -60,7 +79,22 @@
 
 extern const char *CardType[];
 
-static const char *hfcpci_revision = "$Revision: 1.7 $";
+static const char *hfcpci_revision = "$Revision: 1.13 $";
+
+static const int CCD_VENDOR_IDS[] = { 
+	0x1043,   /* Asuscom  */
+	0x1051,   /* Motorola MC145575 */
+        0x1397,   /* CCD and Billion */
+	0,
+};
+
+static const int CCD_DEVICE_IDS[] = { 
+	0x675,    /* Asuscom  */
+	0x100,    /* Motorola MC145575 */
+        0x2BD0,   /* CCD and Billion */
+	0,
+};
+
 
 #if CONFIG_PCI
 /*****************************/
@@ -151,6 +185,20 @@
 	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 */
 	restore_flags(flags);
 }
 
@@ -242,7 +290,7 @@
 		count -= 3;
 		ptr = skb_put(skb, count);
 
-		if (zp->z1 >= zp->z2)
+ 		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 */
@@ -306,7 +354,7 @@
 			rcnt -= 3;
 			ptr = skb_put(skb, rcnt);
 
-			if (zp->z1 >= zp->z2)
+			if (zp->z2 + rcnt <= D_FIFO_SIZE)
 				maxlen = rcnt;	/* complete transfer */
 			else
 				maxlen = D_FIFO_SIZE - zp->z2;	/* maximum */
@@ -570,9 +618,13 @@
 /* set/reset echo mode */
 /***********************/ 
 int hfcpci_set_echo(struct IsdnCardState *cs, int i)
-{
+{ int flags;
+
   if (cs->chanlimit > 1)
     return(-EINVAL);
+
+  save_flags(flags);
+  cli();
   if (i) {
     cs->logecho = 1;
     cs->hw.hfcpci.trm |= 0x20; /* enable echo chan */
@@ -587,7 +639,7 @@
   }
     cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA;
     cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA;
-    cs->hw.hfcpci.conn &= ~0x18;
+    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);
@@ -596,6 +648,7 @@
   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);
+  restore_flags(flags);
   return(0);
 } /* hfcpci_set_echo */ 
 
@@ -654,7 +707,7 @@
 		    rcnt -= 3;
 		    ptr = e_buffer;
 
-		    if (zp->z1 >= zp->z2)
+		    if (zp->z2 <= B_FIFO_SIZE + B_SUB_VAL)
 			maxlen = rcnt;		/* complete transfer */
 		    else
 			maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2;	/* maximum */
@@ -1091,7 +1144,7 @@
 				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC);
 			} else {
 				cs->hw.hfcpci.ctmt |= 1;
-				cs->hw.hfcpci.conn &= ~0x3;
+				cs->hw.hfcpci.conn &= ~0x03;
 				cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA;
 				cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA;
 				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1;
@@ -1115,6 +1168,21 @@
 				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC);
 			}
 			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_INT_M1, cs->hw.hfcpci.int_m1);
 	restore_flags(flags);
@@ -1384,6 +1452,10 @@
 {
 	struct IsdnCardState *cs = card->cs;
 	char tmp[64];
+	int i;
+#ifdef COMPAT_HAS_NEW_PCI
+        struct pci_dev *tmp_hfcpci = NULL;
+#endif
 
 	strcpy(tmp, hfcpci_revision);
 	printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp));
@@ -1402,8 +1474,17 @@
 			printk(KERN_ERR "HFC-PCI: no PCI bus present\n");
 			return (0);
 		}
-		if ((dev_hfcpci = pci_find_device(PCI_VENDOR_CCD,
-					  PCI_CCD_PCI_ID, dev_hfcpci))) {
+		i = 0;
+                while (CCD_VENDOR_IDS[i]) {
+		  tmp_hfcpci = pci_find_device(CCD_VENDOR_IDS[i],
+					       CCD_DEVICE_IDS[i],
+					       dev_hfcpci);
+		  if (tmp_hfcpci) break;
+		  i++;
+		}  
+					      
+		if (tmp_hfcpci) {
+		        dev_hfcpci = tmp_hfcpci; /* old device */
 			cs->hw.hfcpci.pci_bus = dev_hfcpci->bus->number;
 			cs->hw.hfcpci.pci_device_fn = dev_hfcpci->devfn;
 			cs->irq = dev_hfcpci->irq;
@@ -1411,8 +1492,7 @@
 				printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n");
 				return (0);
 			}
-			cs->hw.hfcpci.pci_io = (char *)
-			    dev_hfcpci->base_address[1];
+			cs->hw.hfcpci.pci_io = (char *) get_pcibase(dev_hfcpci, 1);
 		} else {
 			printk(KERN_WARNING "HFC-PCI: No PCI card found\n");
 			return (0);
@@ -1421,11 +1501,17 @@
 		for (; pci_index < 255; pci_index++) {
 			unsigned char irq;
 
-			if (pcibios_find_device(PCI_VENDOR_CCD,
-						PCI_CCD_PCI_ID, pci_index,
-						&cs->hw.hfcpci.pci_bus, &cs->hw.hfcpci.pci_device_fn) != 0) {
-				continue;
+			i = 0;
+                        while (CCD_VENDOR_IDS[i]) {
+			  if (pcibios_find_device(CCD_VENDOR_IDS[i],
+						  CCD_DEVICE_IDS[i], pci_index,
+						  &cs->hw.hfcpci.pci_bus, &cs->hw.hfcpci.pci_device_fn) == 0) 
+			    break;
+			  i++;
 			}
+			if (!CCD_VENDOR_IDS[i]) 
+			  continue;
+
 			pcibios_read_config_byte(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn,
 					       PCI_INTERRUPT_LINE, &irq);
 			cs->irq = irq;
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/hfc_pci.h linux-2.2.12-pre4/drivers/isdn/hisax/hfc_pci.h
--- linux-2.2.12-pre3/drivers/isdn/hisax/hfc_pci.h	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/hfc_pci.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: hfc_pci.h,v 1.3 1999/07/14 12:39:34 werner Exp $
+/* $Id: hfc_pci.h,v 1.5 1999/08/09 19:13:34 werner Exp $
 
  *  specific defines for CCD's HFC 2BDS0 PCI chips
  *
@@ -21,6 +21,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: hfc_pci.h,v $
+ * Revision 1.5  1999/08/09 19:13:34  werner
+ * moved constant pci ids to pci id table
+ *
+ * Revision 1.4  1999/08/08 10:17:33  werner
+ * added new PCI vendor and card ids for Manufacturer 0x1043
+ *
  * Revision 1.3  1999/07/14 12:39:34  werner
  * Added changes for echo handling.
  *
@@ -34,10 +40,6 @@
 
 /* defines for PCI config */
 
-#define PCI_VENDOR_CCD	 0x1397
-#define PCI_CCD_PCI_ID	 0x2BD0
-// #define PCI_VENDOR_CCD	 0x1043
-// #define PCI_CCD_PCI_ID	 0x675
 #define PCI_ENA_MEMIO    0x02
 #define PCI_ENA_MASTER   0x04
 
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/hfcscard.c linux-2.2.12-pre4/drivers/isdn/hisax/hfcscard.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/hfcscard.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/hfcscard.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: hfcscard.c,v 1.3 1999/07/12 21:05:12 keil Exp $
+/* $Id: hfcscard.c,v 1.4 1999/08/09 18:59:59 keil Exp $
 
  * hfcscard.c     low level stuff for hfcs based cards (Teles3c, ACER P10)
  *
@@ -6,6 +6,9 @@
  *
  *
  * $Log: hfcscard.c,v $
+ * Revision 1.4  1999/08/09 18:59:59  keil
+ * Fix S0 init - Thanks to Stefan Gybas
+ *
  * Revision 1.3  1999/07/12 21:05:12  keil
  * fix race in IRQ handling
  * added watchdog for lost IRQs
@@ -24,7 +27,7 @@
 
 extern const char *CardType[];
 
-static const char *hfcs_revision = "$Revision: 1.3 $";
+static const char *hfcs_revision = "$Revision: 1.4 $";
 
 static void
 hfcs_interrupt(int intno, void *dev_id, struct pt_regs *regs)
@@ -105,8 +108,8 @@
 	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 = 0;
-	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, HFCD_MASTER); /* HFC Master */
+	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);
 	restore_flags(flags);
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/hisax.h linux-2.2.12-pre4/drivers/isdn/hisax/hisax.h
--- linux-2.2.12-pre3/drivers/isdn/hisax/hisax.h	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/hisax.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,8 +1,14 @@
-/* $Id: hisax.h,v 2.30 1999/07/14 12:38:38 werner Exp $
+/* $Id: hisax.h,v 2.33 1999/08/05 20:43:16 keil Exp $
 
  *   Basic declarations, defines and prototypes
  *
  * $Log: hisax.h,v $
+ * Revision 2.33  1999/08/05 20:43:16  keil
+ * ISAR analog modem support
+ *
+ * Revision 2.31  1999/07/21 14:46:11  keil
+ * changes from EICON certification
+ *
  * Revision 2.30  1999/07/14 12:38:38  werner
  * Added changes for echo channel handling
  *
@@ -321,6 +327,7 @@
 #define FLG_ESTAB_PEND	13
 #define FLG_PTP		14
 #define FLG_FIXED_TEI	15
+#define FLG_L2BLOCK	16
 
 struct Layer2 {
 	int tei;
@@ -452,6 +459,7 @@
 	int txcnt;
 	int mml;
 	u_char *rcvbuf;         /* B-Channel receive Buffer */
+	u_char conmsg[16];
 	struct isar_reg *reg;
 };
 
@@ -519,11 +527,15 @@
 #define BC_FLG_NOFRAME	4
 #define BC_FLG_HALF	5
 #define BC_FLG_EMPTY	6
+#define BC_FLG_ORIG	7
 
 #define L1_MODE_NULL	0
 #define L1_MODE_TRANS	1
 #define L1_MODE_HDLC	2
+#define L1_MODE_EXTRN	3
 #define L1_MODE_MODEM	7
+#define L1_MODE_V32	8
+#define L1_MODE_FAX	9
 
 struct BCState {
 	int channel;
@@ -536,6 +548,7 @@
 	struct sk_buff_head squeue;	/* B-Channel send Queue */
 	struct PStack *st;
 	u_char *blog;
+	u_char *conmsg;
 	struct timer_list transbusy;
 	struct tq_struct tqueue;
 	int event;
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/isac.c linux-2.2.12-pre4/drivers/isdn/hisax/isac.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/isac.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/isac.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: isac.c,v 1.21 1999/07/12 21:05:17 keil Exp $
+/* $Id: isac.c,v 1.22 1999/08/09 19:04:40 keil Exp $
 
  * isac.c   ISAC specific routines
  *
@@ -9,6 +9,9 @@
  *		../../../Documentation/isdn/HiSax.cert
  *
  * $Log: isac.c,v $
+ * Revision 1.22  1999/08/09 19:04:40  keil
+ * Fix race condition - Thanks to Christer Weinigel
+ *
  * Revision 1.21  1999/07/12 21:05:17  keil
  * fix race in IRQ handling
  * added watchdog for lost IRQs
@@ -233,7 +236,6 @@
 	cs->tx_cnt += count;
 	cs->writeisacfifo(cs, ptr, count);
 	cs->writeisac(cs, ISAC_CMDR, more ? 0x8 : 0xa);
-	restore_flags(flags);
 	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
 		debugl1(cs, "isac_fill_fifo dbusytimer running");
 		del_timer(&cs->dbusytimer);
@@ -241,6 +243,7 @@
 	init_timer(&cs->dbusytimer);
 	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
 	add_timer(&cs->dbusytimer);
+	restore_flags(flags);
 	if (cs->debug & L1_DEB_ISAC_FIFO) {
 		char *t = cs->dlog;
 
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/isar.c linux-2.2.12-pre4/drivers/isdn/hisax/isar.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/isar.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/isar.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: isar.c,v 1.3 1999/07/01 08:11:45 keil Exp $
+/* $Id: isar.c,v 1.4 1999/08/05 20:43:18 keil Exp $
 
  * isar.c   ISAR (Siemens PSB 7110) specific routines
  *
@@ -6,6 +6,9 @@
  *
  *
  * $Log: isar.c,v $
+ * Revision 1.4  1999/08/05 20:43:18  keil
+ * ISAR analog modem support
+ *
  * Revision 1.3  1999/07/01 08:11:45  keil
  * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
  *
@@ -428,6 +431,7 @@
 		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))) {
 			SET_SKB_FREE(skb);
 			rcv_mbox(cs, ireg, (u_char *)skb_put(skb, ireg->clsb));
@@ -517,6 +521,7 @@
 		printk(KERN_ERR"isar_fill_fifo wrong mode 0\n");
 		break;
 	case L1_MODE_TRANS:
+	case L1_MODE_V32:
 		if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
 			0, count, ptr)) {
 			if (cs->debug)
@@ -598,6 +603,136 @@
 	}
 	
 }
+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_status_ev(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");
+//			sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, 0xCF, 0, NULL);
+			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 char debbuf[64];
 
@@ -633,10 +768,31 @@
 			if (cs->debug & L1_DEB_WARN)
 				debugl1(cs, "Buffer STEV dpath%d msb(%x)",
 					ireg->iis>>6, ireg->cmsb);
+		case ISAR_IIS_PSTEV:
+			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+				rcv_mbox(cs, ireg, (u_char *)ireg->par);
+				isar_pump_status_ev(bcs, ireg->cmsb);
+			} else {
+				debugl1(cs, "isar spurious IIS_PSTEV %x/%x/%x",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+				printk(KERN_WARNING"isar spurious IIS_PSTEV %x/%x/%x\n",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			}
 			break;
-		case ISAR_IIS_DIAG:
 		case ISAR_IIS_PSTRSP:
-		case ISAR_IIS_PSTEV:
+			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);
+				printk(KERN_WARNING"isar spurious IIS_PSTRSP %x/%x/%x\n",
+					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);
@@ -664,7 +820,8 @@
 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:
@@ -675,6 +832,44 @@
 						bcs->hw.isar.dpath);
 			}
 			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] = 11; /* 11 db */
+//			param[1] = PV32P2_V22A | PV32P2_V22B | PV32P2_V21; 
+			param[1] = PV32P2_V22A; 
+//			param[2] = PV32P3_AMOD | PV32P3_V32B;
+			param[2] = PV32P3_AMOD;
+			param[3] = PV32P4_48;
+			param[4] = PV32P5_48;
+//			param[3] = PV32P4_UT144;
+//			param[4] = PV32P5_UT144;
+			if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param)) {
+				if (cs->debug)
+					debugl1(cs, "isar pump datamodem cfg dp%d failed",
+						bcs->hw.isar.dpath);
+			}
+			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] = 8; /* 8 db */
+			if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param)) {
+				if (cs->debug)
+					debugl1(cs, "isar pump faxmodem cfg dp%d failed",
+						bcs->hw.isar.dpath);
+			}
+			break;
 	}
 	if (!sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL)) {
 		if (cs->debug)
@@ -687,6 +882,7 @@
 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:
@@ -706,7 +902,17 @@
 		case L1_MODE_HDLC:
 			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, "\0")) {
 				if (cs->debug)
-					debugl1(cs, "isar sart binary dp%d failed",
+					debugl1(cs, "isar sart hdlc dp%d failed",
+						bcs->hw.isar.dpath);
+			}
+			break;
+		case L1_MODE_V32:
+			ctrl = SMODE_V14 | SCTRL_HDMC_BOTH;
+			param[0] = S_P1_CHS_8;
+			param[1] = S_P2_BFT_DEF;
+			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, ctrl, 2, param)) {
+				if (cs->debug)
+					debugl1(cs, "isar sart v14 dp%d failed",
 						bcs->hw.isar.dpath);
 			}
 			break;
@@ -722,18 +928,22 @@
 setup_iom2(struct BCState *bcs) {
 	struct IsdnCardState *cs = bcs->cs;
 	u_char dps = SET_DPS(bcs->hw.isar.dpath);
-	u_char cmsb = 0, msg[5] = {0x10,0,0,0,0};
+	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:
-			cmsb = 0x80;
-			if (bcs->channel)
-				msg[1] = msg[3] = 1;
+			break;
+		case L1_MODE_V32:
+		case L1_MODE_FAX:
+			cmsb |= IOM_CTRL_ALAW | IOM_CTRL_RCV;
 			break;
 	}
 	if (!sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg)) {
@@ -768,7 +978,18 @@
 					&bcs->hw.isar.reg->Flags))
 					bcs->hw.isar.dpath = 1;
 				else {
-					printk(KERN_ERR"isar modeisar both pathes in use\n");
+					printk(KERN_WARNING"isar modeisar both pathes in use\n");
+					return(1);
+				}
+				break;
+			case L1_MODE_V32:
+				/* 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;
@@ -779,8 +1000,8 @@
 			bcs->hw.isar.dpath, bcs->mode, mode, bc);
 	bcs->mode = mode;
 	setup_pump(bcs);
-	setup_sart(bcs);
 	setup_iom2(bcs);
+	setup_sart(bcs);
 	if (bcs->mode == L1_MODE_NULL) {
 		/* Clear resources */
 		if (bcs->hw.isar.dpath == 1)
@@ -858,8 +1079,24 @@
 			break;
 		case (PH_ACTIVATE | REQUEST):
 			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
-			modeisar(st->l1.bcs, st->l1.mode, st->l1.bc);
-			l1_msg_b(st, pr, arg);
+			st->l1.bcs->hw.isar.conmsg[0] = 0;
+			if (test_bit(FLG_ORIG, &st->l2.flag))
+				test_and_set_bit(BC_FLG_ORIG, &st->l1.bcs->Flag);
+			else
+				test_and_clear_bit(BC_FLG_ORIG, &st->l1.bcs->Flag);
+			switch(st->l1.mode) {
+				case L1_MODE_TRANS:
+				case L1_MODE_HDLC:
+					if (modeisar(st->l1.bcs, st->l1.mode, st->l1.bc))
+						l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg);
+					else
+						l1_msg_b(st, PH_ACTIVATE | REQUEST, arg);
+					break;
+				case L1_MODE_V32:
+					if (modeisar(st->l1.bcs, st->l1.mode, st->l1.bc))
+						l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg);
+					break;
+			}
 			break;
 		case (PH_DEACTIVATE | REQUEST):
 			l1_msg_b(st, pr, arg);
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/isar.h linux-2.2.12-pre4/drivers/isdn/hisax/isar.h
--- linux-2.2.12-pre3/drivers/isdn/hisax/isar.h	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/isar.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,10 +1,13 @@
-/* $Id: isar.h,v 1.3 1999/07/01 08:11:46 keil Exp $
+/* $Id: isar.h,v 1.4 1999/08/05 20:43:20 keil Exp $
  * isar.h   ISAR (Siemens PSB 7110) specific defines
  *
  * Author Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: isar.h,v $
+ * Revision 1.4  1999/08/05 20:43:20  keil
+ * ISAR analog modem support
+ *
  * Revision 1.3  1999/07/01 08:11:46  keil
  * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
  *
@@ -39,6 +42,7 @@
 #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
@@ -70,12 +74,97 @@
 
 #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
+
+#define PV32P3_AMOD	0x80
+#define PV32P3_V32B	0x02
+#define PV32P4_48	0x05
+#define PV32P5_48	0x11
+#define PV32P4_UT48	0x0d
+#define PV32P5_UT48	0x11
+#define PV32P4_96	0x03
+#define PV32P5_96	0x11
+#define PV32P4_UT96	0x0f
+#define PV32P5_UT96	0x11
+#define PV32P4_B96	0x0b
+#define PV32P5_B96	0x91
+#define PV32P4_UTB96	0x0f
+#define PV32P5_UTB96	0xd1
+#define PV32P4_120	0x09
+#define PV32P5_120	0xb1
+#define PV32P4_UT120	0x0f
+#define PV32P5_UT120	0xf1
+#define PV32P4_144	0x09
+#define PV32P5_144	0x99
+#define PV32P4_UT144	0x0f
+#define PV32P5_UT144	0xf9
+#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 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	30
+
+#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
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/isdnl2.c linux-2.2.12-pre4/drivers/isdn/hisax/isdnl2.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/isdnl2.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/isdnl2.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: isdnl2.c,v 2.17 1999/07/01 08:11:50 keil Exp $
+/* $Id: isdnl2.c,v 2.19 1999/08/05 20:40:26 keil Exp $
 
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
@@ -11,6 +11,12 @@
  *              Fritz Elfert
  *
  * $Log: isdnl2.c,v $
+ * Revision 2.19  1999/08/05 20:40:26  keil
+ * Fix interlayer communication
+ *
+ * Revision 2.18  1999/07/21 14:46:16  keil
+ * changes from EICON certification
+ *
  * Revision 2.17  1999/07/01 08:11:50  keil
  * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
  *
@@ -74,7 +80,7 @@
 #include "hisax.h"
 #include "isdnl2.h"
 
-const char *l2_revision = "$Revision: 2.17 $";
+const char *l2_revision = "$Revision: 2.19 $";
 
 static void l2m_debug(struct FsmInst *fi, char *fmt, ...);
 
@@ -163,6 +169,19 @@
 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;
@@ -219,7 +238,7 @@
 	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);
-	test_and_clear_bit(FLG_PEER_BUSY, &l2->flag);
+	clear_peer_busy(l2);
 }
 
 inline int
@@ -1026,10 +1045,10 @@
 
 	skb_pull(skb, l2addrsize(l2));
 	if (IsRNR(skb->data, st)) {
-		test_and_set_bit(FLG_PEER_BUSY, &l2->flag);
+		set_peer_busy(l2);
 		typ = RNR;
 	} else
-		test_and_clear_bit(FLG_PEER_BUSY, &l2->flag);
+		clear_peer_busy(l2);
 	if (IsREJ(skb->data, st))
 		typ = REJ;
 
@@ -1376,10 +1395,10 @@
 	skb_pull(skb, l2addrsize(l2));
 
 	if (IsRNR(skb->data, st)) {
-		test_and_set_bit(FLG_PEER_BUSY, &l2->flag);
+		set_peer_busy(l2);
 		rnr = 1;
 	} else
-		test_and_clear_bit(FLG_PEER_BUSY, &l2->flag);
+		clear_peer_busy(l2);
 
 	if (test_bit(FLG_MOD128, &l2->flag)) {
 		PollFlag = (skb->data[1] & 0x1) == 0x1;
@@ -1496,11 +1515,14 @@
 }
 
 static void
-l2_discard_i(struct FsmInst *fi, int event, void *arg)
+l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
 	
 	discard_queue(&st->l2.i_queue);
+	discard_queue(&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
@@ -1663,9 +1685,10 @@
 	{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_discard_i},
+	{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},
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/isdnl3.c linux-2.2.12-pre4/drivers/isdn/hisax/isdnl3.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/isdnl3.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/isdnl3.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: isdnl3.c,v 2.9 1999/07/01 08:11:53 keil Exp $
+/* $Id: isdnl3.c,v 2.10 1999/07/21 14:46:19 keil Exp $
 
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
@@ -11,6 +11,9 @@
  *              Fritz Elfert
  *
  * $Log: isdnl3.c,v $
+ * Revision 2.10  1999/07/21 14:46:19  keil
+ * changes from EICON certification
+ *
  * Revision 2.9  1999/07/01 08:11:53  keil
  * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
  *
@@ -65,7 +68,7 @@
 #include "isdnl3.h"
 #include <linux/config.h>
 
-const char *l3_revision = "$Revision: 2.9 $";
+const char *l3_revision = "$Revision: 2.10 $";
 
 static
 struct Fsm l3fsm =
@@ -347,8 +350,18 @@
 			if (pp)
 				pp->next = np->next;
 			else if (!(p->st->l3.proc = np->next) &&
-				 !test_bit(FLG_PTP, &p->st->l2.flag))
-			         FsmEvent(&p->st->l3.l3m, EV_RELEASE_REQ, NULL);
+				!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");
+					FsmEvent(&p->st->l3.l3m, EV_RELEASE_REQ, NULL);
+				} else {
+					if (p->debug)
+						l3_debug(p->st, "release_l3_process: not release link");
+				}
+			} 
 			kfree(p);
 			return;
 		}
@@ -362,12 +375,12 @@
 static void
 l3ml3p(struct PStack *st, int pr)
 {
-       struct l3_process *p = st->l3.proc;
+	struct l3_process *p = st->l3.proc;
 
-       while (p) {
-               st->l3.l3ml3(st, pr, p);
-               p = p->next;
-       }
+	while (p) {
+		st->l3.l3ml3(st, pr, p);
+		p = p->next;
+	}
 }
 
 void
@@ -474,12 +487,19 @@
 {
 	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++;
 	}
-	l3ml3p(st, DL_ESTABLISH | INDICATION);
+	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
@@ -487,13 +507,20 @@
 {
 	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++;
 	}
-	l3ml3p(st, DL_ESTABLISH | CONFIRM);
+	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
@@ -510,8 +537,15 @@
 {
 	struct PStack *st = fi->userdata;
 
-	FsmChangeState(fi, ST_L3_LC_REL_WAIT);
-	st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL);
+	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
@@ -528,11 +562,11 @@
 static void
 lc_release_cnf(struct FsmInst *fi, int event, void *arg)
 {
-       struct PStack *st = fi->userdata;
+	struct PStack *st = fi->userdata;
 
-        FsmChangeState(fi, ST_L3_LC_REL);
-        discard_queue(&st->l3.squeue);
-       l3ml3p(st, DL_RELEASE | CONFIRM);
+	FsmChangeState(fi, ST_L3_LC_REL);
+	discard_queue(&st->l3.squeue);
+	l3ml3p(st, DL_RELEASE | CONFIRM);
 }
 
 
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/isdnl3.h linux-2.2.12-pre4/drivers/isdn/hisax/isdnl3.h
--- linux-2.2.12-pre3/drivers/isdn/hisax/isdnl3.h	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/isdnl3.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,6 +1,9 @@
-/* $Id: isdnl3.h,v 2.4 1999/07/01 08:11:54 keil Exp $
+/* $Id: isdnl3.h,v 2.5 1999/07/25 16:18:32 keil Exp $
 
  * $Log: isdnl3.h,v $
+ * Revision 2.5  1999/07/25 16:18:32  keil
+ * Fix Suspend/Resume
+ *
  * Revision 2.4  1999/07/01 08:11:54  keil
  * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
  *
@@ -34,7 +37,7 @@
  */
 
 #define SBIT(state) (1<<state)
-#define ALL_STATES  0x00ffffff
+#define ALL_STATES  0x03ffffff
 
 #define PROTO_DIS_EURO	0x08
 
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/l3dss1.c linux-2.2.12-pre4/drivers/isdn/hisax/l3dss1.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/l3dss1.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/l3dss1.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: l3dss1.c,v 2.14 1999/07/09 08:30:08 keil Exp $
+/* $Id: l3dss1.c,v 2.18 1999/08/11 20:54:39 keil Exp $
 
  * EURO/DSS1 D-channel protocol
  *
@@ -13,6 +13,15 @@
  *              Fritz Elfert
  *
  * $Log: l3dss1.c,v $
+ * Revision 2.18  1999/08/11 20:54:39  keil
+ * High layer compatibility is valid in SETUP
+ *
+ * Revision 2.17  1999/07/25 16:18:25  keil
+ * Fix Suspend/Resume
+ *
+ * Revision 2.16  1999/07/21 14:46:23  keil
+ * changes from EICON certification
+ *
  * Revision 2.14  1999/07/09 08:30:08  keil
  * cosmetics
  *
@@ -78,7 +87,7 @@
 #include <linux/ctype.h>
 
 extern char *HiSax_getrev(const char *revision);
-const char *dss1_revision = "$Revision: 2.14 $";
+const char *dss1_revision = "$Revision: 2.18 $";
 
 #define EXT_BEARER_CAPS 1
 
@@ -662,10 +671,8 @@
 static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, -1};
 static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY,
 		IE_PROGRESS, IE_DISPLAY, IE_USER_USER, -1};
-/* not used
- * static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD,
- *		IE_CALLED_PN, -1};
- */
+static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD,
+		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};
@@ -679,7 +686,8 @@
 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_CALLING_PN,
-		IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_LLC, IE_USER_USER, -1};
+		IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_LLC, IE_HLC,
+		IE_USER_USER, -1};
 static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY,
 		IE_PROGRESS, IE_DISPLAY, -1};
 static int ie_STATUS[] = {IE_CAUSE | IE_MANDATORY, IE_CALL_STATE |
@@ -805,12 +813,13 @@
 				err_ureg++;
 		}
 		ie = *p++;
-		if (newpos >= 0) {
+		if (ie & 0x80) {
+			l = 1;
+		} else {
 			l = *p++;
 			p += l;
 			l += 2;
-		} else
-			l = 1;
+		}
 		if (l > getmax_ie_len(ie))
 			err_len++;
 	}
@@ -1243,9 +1252,8 @@
 
 				break;
 			case 0x08:	// if (p[5] == 0x02) // sync. Bitratenadaption
-
-				return DecodeSyncParams(176, p[5]);	// V.120
-
+				if (p[1] > 3) 
+					return DecodeSyncParams(176, p[5]);	// V.120
 				break;
 		}
 	}
@@ -1479,8 +1487,11 @@
 		pc->para.bchannel = id;
 	} else if (1 == pc->state) {
 		if (pc->debug & L3_DEB_WARN)
-			l3_debug(pc->st, "setup answer without chid (ret %d)", id);
-		pc->para.cause = 96;
+			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;
 	}
@@ -1515,8 +1526,11 @@
 		pc->para.bchannel = id;
 	} else {
 		if (pc->debug & L3_DEB_WARN)
-			l3_debug(pc->st, "setup answer without chid (ret %d)", id);
-		pc->para.cause = 96;
+			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;
 	}
@@ -1640,10 +1654,6 @@
 /* JIM, 05.11.97 I wanna set service indicator 2 */
 #if EXT_BEARER_CAPS
 					pc->para.setup.si2 = DecodeSI2(skb);
-					if (pc->debug & L3_DEB_SI)
-						l3_debug(pc->st, "SI=%d, AI=%d",
-							pc->para.setup.si1,
-							pc->para.setup.si2);
 #endif
 					break;
 				case 0x09: /* Restricted digital information */
@@ -1664,6 +1674,7 @@
 			}
 			switch (p[3] & 0x7f) {
 				case 0x40: /* packed mode */
+					pc->para.setup.si1 = 8;
 					break;
 				case 0x10: /* 64 kbit */
 				case 0x11: /* 2*64 kbit */
@@ -1677,6 +1688,9 @@
 					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)",
@@ -1715,7 +1729,10 @@
 	} else {
 		if (pc->debug & L3_DEB_WARN)
 			l3_debug(pc->st, "setup with wrong chid ret %d", id);
-		pc->para.cause = 96;
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
 		l3dss1_msg_without_setup(pc, pr, NULL);
 		return;
 	}
@@ -1991,7 +2008,7 @@
 	u_char *p;
 
 	if ((p = findie(skb->data, skb->len, IE_PROGRESS, 0))) {
-		if (p[1] != 4) {
+		if (p[1] != 2) {
 			err = 1;
 			pc->para.cause = 100;
 		} else if (p[2] & 0x60) {
@@ -2089,10 +2106,20 @@
 
 	ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY);
 	l3dss1_std_ie_err(pc, ret);
+// KKe 19.7.99 test eicon
+//        idev_kfree_skb(skb, FREE_READ);
+	pc->para.cause = 30; /* response to STATUS_ENQUIRY */
+        l3dss1_status_send(pc, pr, NULL);
+}
 
-        idev_kfree_skb(skb, FREE_READ);
+static void
+l3dss1_information(struct l3_process *pc, u_char pr, void *arg)
+{
+	int ret;
+	struct sk_buff *skb = arg;
 
-        l3dss1_status_send(pc, 0x1E, NULL);   /* answer status enquire */
+	ret = check_infoelements(pc, skb, ie_INFORMATION);
+	l3dss1_std_ie_err(pc, ret);
 }
 
 /******************************/
@@ -2326,6 +2353,7 @@
 		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);
 	}
@@ -2610,7 +2638,7 @@
 	memcpy(skb_put(skb, l), tmp, l);
 	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 	newl3state(pc, 17);
-	L3AddTimer(&pc->timer, T319, CC_T319);
+	L3AddTimer(&pc->timer, T318, CC_T318);
 }
 
 static void
@@ -2758,9 +2786,10 @@
 static void
 l3dss1_dl_reest_status(struct l3_process *pc, u_char pr, void *arg)
 {
-        L3DelTimer(&pc->timer);
+	L3DelTimer(&pc->timer);
  
-        l3dss1_status_send(pc, 0x1F, NULL);   /* normal, unspecified */
+ 	pc->para.cause = 0x1F; /* normal, unspecified */
+	l3dss1_status_send(pc, 0, NULL);
 }
 
 /* *INDENT-OFF* */
@@ -2831,8 +2860,10 @@
 	 MT_STATUS, l3dss1_release_ind},
 	{ALL_STATES,
 	 MT_STATUS, l3dss1_status},
-	{SBIT(0) | SBIT(6),
+	{SBIT(0),
 	 MT_SETUP, l3dss1_setup},
+	{SBIT(6) | SBIT(7),
+	 MT_SETUP, l3dss1_dummy},
 	{SBIT(1) | SBIT(2),
 	 MT_CALL_PROCEEDING, l3dss1_call_proc},
 	{SBIT(1),
@@ -2841,15 +2872,18 @@
 	 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(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(19)*/,
+	{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(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(11),
 //	 MT_DISCONNECT, l3dss1_release_req},
@@ -3021,6 +3055,13 @@
 		 */
 		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");
+				idev_kfree_skb(skb, FREE_READ);
+				return;
+			}
 			if (!(proc = dss1_new_l3_process(st, cr))) {
 				/* May be to answer with RELEASE_COMPLETE and
 				 * CAUSE 0x2f "Resource unavailable", but this
@@ -3222,8 +3263,3 @@
 	strcpy(tmp, dss1_revision);
 	printk(KERN_INFO "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp));
 }
-
-
-
-
-
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/Makefile linux-2.2.12-pre4/drivers/isdn/hisax/Makefile
--- linux-2.2.12-pre3/drivers/isdn/hisax/Makefile	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/Makefile	2003-08-15 15:06:54.000000000 -0700
@@ -197,7 +197,8 @@
 include $(TOPDIR)/Rules.make
 
 MD5FILES += isac.c isdnl1.c isdnl2.c isdnl3.c \
-	    tei.c callc.c cert.c l3dss1.c l3_1tr6.c elsa.c
+	    tei.c callc.c cert.c l3dss1.c l3_1tr6.c \
+	    elsa.c diva.c
 
 CERT = $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?)
 
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/md5sums.asc linux-2.2.12-pre4/drivers/isdn/hisax/md5sums.asc
--- linux-2.2.12-pre3/drivers/isdn/hisax/md5sums.asc	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/md5sums.asc	2003-08-15 15:06:54.000000000 -0700
@@ -2,28 +2,30 @@
 
 # This are valid md5sums for certificated HiSax driver.
 # The certification is valid only if the md5sums of all files match.
-# The certification is valid only for ELSA QuickStep cards in the moment.
+# The certification is valid only for ELSA QuickStep cards and
+# Eicon Technology Diva 2.01 PCI cards in the moment.
 # Read ../../../Documentation/isdn/HiSax.cert for more informations.
 # 
-7b86f7c9709d1a96f2591b76db297116  isac.c
-773fb8b13e20ef92c0bc991fea23c673  isdnl1.c
-f2ef2bc94883f818d0beb184688786bc  isdnl2.c
-c7b9992f966c645e0eea548b0d3655a0  isdnl3.c
-df3c2d2bc312af0689ed97a1fe7ad2df  tei.c
-94facb2403188ddfb6b83a890a5e7fa0  callc.c
-f3ec2a634f06074d16167aaba02b6dc1  cert.c
-e69680e894417b00949473ce38259e2a  l3dss1.c
-04082bae9726b7c34adb008d3752ac73  l3_1tr6.c
-6705bf924beeb147d80dc91fa0aa25ee  elsa.c
+d93f31e02c1b153ec04d16f69e5688b3  isac.c
+e2a78c07f32c8ca7c88fc9f92a87dfab  isdnl1.c
+54490c4f46a998ff4ef34287bc262185  isdnl2.c
+f4184a50e35e5b568608e6cb7a693319  isdnl3.c
+ef70f4269fdc2ca15100f9b776afaa0d  tei.c
+cf3923304983e9d64cf35bc5a3533f6c  callc.c
+bf9605b36429898f7be6630034e83230  cert.c
+309261e4c36d950db6978440e8bc8342  l3dss1.c
+b674eee9314a7cc413971c84003cf1d2  l3_1tr6.c
+8f86d92f43ecc42f6457773168cfc114  elsa.c
+24cda374da44b57f6a1bb215424267b5  diva.c
 # end of md5sums
 
 -----BEGIN PGP SIGNATURE-----
 Version: 2.6.3i
 Charset: noconv
 
-iQCVAwUBN3FBezpxHvX/mS9tAQGs9wQAk4pSCWvx5CMheSRedQ7nTtIoVQKPiAEx
-6/0DWE5hu5IsSOG4ZbLG/ISdad4OOZjWfMpeeIwsHVGVSspGvo9lpIMOS9EqEvr8
-I+kCrPQwKaEN675U2m0ofsELAPOyH2JICjKdbW+iipWI+6oqGta7aw/tbgDqykVr
-vz2L4uxmgUY=
-=r0Yz
+iQCVAwUBN7HnvDpxHvX/mS9tAQFANgP+LGuG98lvCv97vN2dc6T/6hZTxFW+WirJ
+XMhU5NHoZ+8MISMOVKB7ugsGO9cJI0lUA0sOe8jtPCo5070nF1ZkNsxV/x7WK2dS
+RwXfHy6+TAH7qIiBnkP9odB2lib+VFl/nnkkTwsXfVwRCD8bLaagMPv+nAveDoNE
+uff0xxXEnJw=
+=Wu2M
 -----END PGP SIGNATURE-----
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/netjet.c linux-2.2.12-pre4/drivers/isdn/hisax/netjet.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/netjet.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/netjet.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: netjet.c,v 1.10 1999/07/12 21:05:22 keil Exp $
+/* $Id: netjet.c,v 1.13 1999/08/11 21:01:31 keil Exp $
 
  * netjet.c     low level stuff for Traverse Technologie NETJet ISDN cards
  *
@@ -6,8 +6,18 @@
  *
  * Thanks to Traverse Technologie Australia for documents and informations
  *
- *
  * $Log: netjet.c,v $
+ * Revision 1.13  1999/08/11 21:01:31  keil
+ * new PCI codefix
+ *
+ * Revision 1.12  1999/08/10 16:02:00  calle
+ * struct pci_dev changed in 2.3.13. Made the necessary changes.
+ *
+ * Revision 1.11  1999/08/07 17:32:00  keil
+ * Asymetric buffers for improved ping times.  Interframe spacing
+ * fix for NJ<->NJ thoughput.  Matt Henderson - www.traverse.com.au
+ *
+ *
  * Revision 1.10  1999/07/12 21:05:22  keil
  * fix race in IRQ handling
  * added watchdog for lost IRQs
@@ -65,7 +75,7 @@
 
 extern const char *CardType[];
 
-const char *NETjet_revision = "$Revision: 1.10 $";
+const char *NETjet_revision = "$Revision: 1.13 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -101,7 +111,8 @@
 #define NETJET_IRQM0_WRITE_1	0x01
 #define NETJET_IRQM0_WRITE_2	0x02
 
-#define NETJET_DMA_SIZE 512
+#define NETJET_DMA_TXSIZE 512
+#define NETJET_DMA_RXSIZE 128
 
 #define HDLC_ZERO_SEARCH 0
 #define HDLC_FLAG_SEARCH 1
@@ -229,7 +240,7 @@
 	switch (mode) {
 		case (L1_MODE_NULL):
 			fill_mem(bcs, bcs->hw.tiger.send,
-				NETJET_DMA_SIZE, bc, 0xff);
+				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,
@@ -246,7 +257,7 @@
 			break;
 		case (L1_MODE_HDLC): 
 			fill_mem(bcs, bcs->hw.tiger.send,
-				NETJET_DMA_SIZE, bc, 0xff);
+				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;
@@ -255,14 +266,14 @@
 			bcs->hw.tiger.s_tot = 0;
 			if (! cs->hw.njet.dmactrl) {
 				fill_mem(bcs, bcs->hw.tiger.send,
-					NETJET_DMA_SIZE, !bc, 0xff);
+					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, 0x3f);
 			}
 			bcs->hw.tiger.sendp = bcs->hw.tiger.send;
-			bcs->hw.tiger.free = NETJET_DMA_SIZE;
+			bcs->hw.tiger.free = NETJET_DMA_TXSIZE;
 			test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
 			break;
 	}
@@ -381,6 +392,7 @@
 			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;
@@ -412,7 +424,7 @@
 	int i;
 	register u_char j;
 	register u_char val;
-	u_int  *pend = bcs->hw.tiger.rec +NETJET_DMA_SIZE -1;
+	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;
@@ -567,7 +579,7 @@
 
 static void read_tiger(struct IsdnCardState *cs) {
 	u_int *p;
-	int cnt = NETJET_DMA_SIZE/2;
+	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",
@@ -578,7 +590,7 @@
 		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_SIZE - 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)
@@ -635,12 +647,12 @@
 		cnt = bcs->hw.tiger.s_end - p;
 		if (cnt < 2) {
 			p = bcs->hw.tiger.send + 1;
-			cnt = NETJET_DMA_SIZE/2 - 2;
+			cnt = NETJET_DMA_TXSIZE/2 - 2;
 		} else {
 			p++;
 			p++;
-			if (cnt <= (NETJET_DMA_SIZE/2))
-				cnt += NETJET_DMA_SIZE/2;
+			if (cnt <= (NETJET_DMA_TXSIZE/2))
+				cnt += NETJET_DMA_TXSIZE/2;
 			cnt--;
 			cnt--;
 		}
@@ -698,7 +710,7 @@
 			}
 			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
 			bcs->hw.tiger.free = cnt - s_cnt;
-			if (bcs->hw.tiger.free > (NETJET_DMA_SIZE/2))
+			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);
@@ -738,7 +750,7 @@
 }
 
 static void write_tiger(struct IsdnCardState *cs) {
-	u_int *p, cnt = NETJET_DMA_SIZE/2;
+	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",
@@ -749,7 +761,7 @@
 		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_SIZE - 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)
@@ -880,42 +892,42 @@
 __initfunc(void
 inittiger(struct IsdnCardState *cs))
 {
-	if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int),
+	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_SIZE/2 - 1;
-	cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1;
+	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_SIZE * sizeof(unsigned int));
+	memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_TXSIZE * sizeof(unsigned int));
 	debugl1(cs, "tiger: send buf %x - %x", (u_int)cs->bcs[0].hw.tiger.send,
-		(u_int)(cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1));
+		(u_int)(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_SIZE * sizeof(unsigned int),
+	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 %x - %x", (u_int)cs->bcs[0].hw.tiger.rec,
-		(u_int)(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1));
+		(u_int)(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_SIZE * sizeof(unsigned int));
+	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_SIZE/2 - 1),
+	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_SIZE - 1),
+	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),
@@ -1098,7 +1110,7 @@
 			printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n");
 			return(0);
 		}
-		cs->hw.njet.base = dev_netjet->base_address[0]
+		cs->hw.njet.base = get_pcibase(dev_netjet, 0)
 			& PCI_BASE_ADDRESS_IO_MASK; 
 		if (!cs->hw.njet.base) {
 			printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n");
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/niccy.c linux-2.2.12-pre4/drivers/isdn/hisax/niccy.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/niccy.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/niccy.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: niccy.c,v 1.6 1999/07/12 21:05:23 keil Exp $
+/* $Id: niccy.c,v 1.8 1999/08/11 21:01:33 keil Exp $
 
  * niccy.c  low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and
  *          compatible (SAGEM cybermodem)
@@ -8,6 +8,12 @@
  * Thanks to Dr. Neuhaus and SAGEM for informations
  *
  * $Log: niccy.c,v $
+ * Revision 1.8  1999/08/11 21:01:33  keil
+ * new PCI codefix
+ *
+ * Revision 1.7  1999/08/10 16:02:04  calle
+ * struct pci_dev changed in 2.3.13. Made the necessary changes.
+ *
  * Revision 1.6  1999/07/12 21:05:23  keil
  * fix race in IRQ handling
  * added watchdog for lost IRQs
@@ -39,7 +45,7 @@
 #endif
 
 extern const char *CardType[];
-const char *niccy_revision = "$Revision: 1.6 $";
+const char *niccy_revision = "$Revision: 1.8 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -314,16 +320,16 @@
 				return(0);
 			}
 			cs->irq = niccy_dev->irq;
-			if (!niccy_dev->base_address[0]) {
+			if (!get_pcibase(niccy_dev, 0)) {
 				printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n");
 				return(0);
 			}
-			cs->hw.niccy.cfg_reg = niccy_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
-			if (!niccy_dev->base_address[1]) {
+			cs->hw.niccy.cfg_reg = get_pcibase(niccy_dev, 0) & PCI_BASE_ADDRESS_IO_MASK;
+			if (!get_pcibase(niccy_dev, 1)) {
 				printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n");
 				return(0);
 			}
-			pci_ioaddr = niccy_dev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
+			pci_ioaddr = get_pcibase(niccy_dev, 1) & PCI_BASE_ADDRESS_IO_MASK;
 			cs->subtyp = NICCY_PCI;
 		} else {
 			printk(KERN_WARNING "Niccy: No PCI card found\n");
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/sedlbauer.c linux-2.2.12-pre4/drivers/isdn/hisax/sedlbauer.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/sedlbauer.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/sedlbauer.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: sedlbauer.c,v 1.11 1999/07/12 21:05:27 keil Exp $
+/* $Id: sedlbauer.c,v 1.14 1999/08/11 20:59:22 keil Exp $
 
  * sedlbauer.c  low level stuff for Sedlbauer cards
  *              includes support for the Sedlbauer speed star (speed star II),
@@ -17,6 +17,16 @@
  *            Edgar Toernig
  *
  * $Log: sedlbauer.c,v $
+ * Revision 1.14  1999/08/11 20:59:22  keil
+ * new PCI codefix
+ * fix IRQ problem while unload
+ *
+ * Revision 1.13  1999/08/10 16:02:08  calle
+ * struct pci_dev changed in 2.3.13. Made the necessary changes.
+ *
+ * Revision 1.12  1999/08/05 20:43:22  keil
+ * ISAR analog modem support
+ *
  * Revision 1.11  1999/07/12 21:05:27  keil
  * fix race in IRQ handling
  * added watchdog for lost IRQs
@@ -89,7 +99,7 @@
 
 extern const char *CardType[];
 
-const char *Sedlbauer_revision = "$Revision: 1.11 $";
+const char *Sedlbauer_revision = "$Revision: 1.14 $";
 
 const char *Sedlbauer_Types[] =
 	{"None", "speed card/win", "speed star", "speed fax+", 
@@ -433,9 +443,11 @@
 void
 release_io_sedlbauer(struct IsdnCardState *cs)
 {
-	int bytecnt = (cs->subtyp == SEDL_SPEED_FAX) ? 16 : 8;
+	int bytecnt = 8;
 
-	if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+	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)
@@ -488,6 +500,17 @@
 			reset_sedlbauer(cs);
 			return(0);
 		case CARD_RELEASE:
+			if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+				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);
+			}
 			release_io_sedlbauer(cs);
 			return(0);
 		case CARD_INIT:
@@ -576,7 +599,7 @@
 				printk(KERN_WARNING "Sedlbauer: No IRQ for PCI card found\n");
 				return(0);
 			}
-			cs->hw.sedl.cfg_reg = dev_sedl->base_address[0] &
+			cs->hw.sedl.cfg_reg = get_pcibase(dev_sedl, 0) &
 				PCI_BASE_ADDRESS_IO_MASK; 
 		} else {
 			printk(KERN_WARNING "Sedlbauer: No PCI card found\n");
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/tei.c linux-2.2.12-pre4/drivers/isdn/hisax/tei.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/tei.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/tei.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: tei.c,v 2.12 1999/07/01 08:12:11 keil Exp $
+/* $Id: tei.c,v 2.13 1999/07/21 14:46:28 keil Exp $
 
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
@@ -11,6 +11,9 @@
  *              Fritz Elfert
  *
  * $Log: tei.c,v $
+ * Revision 2.13  1999/07/21 14:46:28  keil
+ * changes from EICON certification
+ *
  * Revision 2.12  1999/07/01 08:12:11  keil
  * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
  *
@@ -75,7 +78,7 @@
 #include "isdnl2.h"
 #include <linux/random.h>
 
-const char *tei_revision = "$Revision: 2.12 $";
+const char *tei_revision = "$Revision: 2.13 $";
 
 #define ID_REQUEST	1
 #define ID_ASSIGNED	2
@@ -230,6 +233,25 @@
 }
 
 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 */
+		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;
@@ -472,6 +494,7 @@
 static struct FsmNode TeiFnList[] HISAX_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},
diff -urN linux-2.2.12-pre3/drivers/isdn/hisax/telespci.c linux-2.2.12-pre4/drivers/isdn/hisax/telespci.c
--- linux-2.2.12-pre3/drivers/isdn/hisax/telespci.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/hisax/telespci.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: telespci.c,v 2.7 1999/07/12 21:05:34 keil Exp $
+/* $Id: telespci.c,v 2.9 1999/08/11 21:01:34 keil Exp $
 
  * telespci.c     low level stuff for Teles PCI isdn cards
  *
@@ -7,6 +7,12 @@
  *
  *
  * $Log: telespci.c,v $
+ * Revision 2.9  1999/08/11 21:01:34  keil
+ * new PCI codefix
+ *
+ * Revision 2.8  1999/08/10 16:02:10  calle
+ * struct pci_dev changed in 2.3.13. Made the necessary changes.
+ *
  * Revision 2.7  1999/07/12 21:05:34  keil
  * fix race in IRQ handling
  * added watchdog for lost IRQs
@@ -41,7 +47,7 @@
 #endif
 
 extern const char *CardType[];
-const char *telespci_revision = "$Revision: 2.7 $";
+const char *telespci_revision = "$Revision: 2.9 $";
 
 #define ZORAN_PO_RQ_PEN	0x02000000
 #define ZORAN_PO_WR	0x00800000
@@ -331,10 +337,10 @@
 			printk(KERN_WARNING "Teles: No IRQ for PCI card found\n");
 			return(0);
 		}
-		cs->hw.teles0.membase = (u_int) ioremap(dev_tel->base_address[0],
+		cs->hw.teles0.membase = (u_int) ioremap(get_pcibase(dev_tel, 0),
 			PAGE_SIZE);
 		printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n",
-			dev_tel->base_address[0], dev_tel->irq);
+			get_pcibase(dev_tel, 0), dev_tel->irq);
 	} else {
 		printk(KERN_WARNING "TelesPCI: No PCI card found\n");
 		return(0);
diff -urN linux-2.2.12-pre3/drivers/isdn/isdn_audio.c linux-2.2.12-pre4/drivers/isdn/isdn_audio.c
--- linux-2.2.12-pre3/drivers/isdn/isdn_audio.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/isdn_audio.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: isdn_audio.c,v 1.14 1999/07/11 17:14:06 armin Exp $
+/* $Id: isdn_audio.c,v 1.16 1999/08/06 12:47:35 calle Exp $
 
  * Linux ISDN subsystem, audio conversion and compression (linklevel).
  *
@@ -21,6 +21,16 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_audio.c,v $
+ * Revision 1.16  1999/08/06 12:47:35  calle
+ * Using __GNUC__ == 2 && __GNUC_MINOR__ < 95 how to define
+ *   ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT
+ *
+ * Revision 1.15  1999/08/06 12:02:52  calle
+ * egcs 2.95 complain about invalid asm statement:
+ *    "fixed or forbidden register 2 (cx) was spilled for class CREG."
+ * Using ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT and not
+ * define it at the moment.
+ *
  * Revision 1.14  1999/07/11 17:14:06  armin
  * Added new layer 2 and 3 protocols for Fax and DSP functions.
  * Moved "Add CPN to RING message" to new register S23,
@@ -82,7 +92,7 @@
 #include "isdn_audio.h"
 #include "isdn_common.h"
 
-char *isdn_audio_revision = "$Revision: 1.14 $";
+char *isdn_audio_revision = "$Revision: 1.16 $";
 
 /*
  * Misc. lookup-tables.
@@ -279,7 +289,18 @@
 	{'*', '0', '#', 'D'}
 };
 
-#if ((CPU == 386) || (CPU == 486) || (CPU == 586))
+
+/*
+ * egcs 2.95 complain about invalid asm statement:
+ * "fixed or forbidden register 2 (cx) was spilled for class CREG."
+ */
+#if ((CPU == 386) || (CPU == 486) || (CPU == 586)) || defined(__GNUC__)
+#if __GNUC__ == 2 && __GNUC_MINOR__ < 95
+#define ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT
+#endif
+#endif
+
+#ifdef ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT
 static inline void
 isdn_audio_tlookup(const void *table, void *buff, unsigned long n)
 {
diff -urN linux-2.2.12-pre3/drivers/isdn/isdn_cards.c linux-2.2.12-pre4/drivers/isdn/isdn_cards.c
--- linux-2.2.12-pre3/drivers/isdn/isdn_cards.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/isdn_cards.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: isdn_cards.c,v 1.9 1999/04/12 12:33:11 fritz Exp $
+/* $Id: isdn_cards.c,v 1.10 1999/07/20 06:41:28 calle Exp $
 
  * Linux ISDN subsystem, initialization for non-modularized drivers.
  *
@@ -19,6 +19,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_cards.c,v $
+ * Revision 1.10  1999/07/20 06:41:28  calle
+ * Bugfix: After the redesign of the AVM B1 driver, the driver didn't even
+ *         compile, if not selected as modules.
+ *
  * Revision 1.9  1999/04/12 12:33:11  fritz
  * Changes from 2.0 tree.
  *
@@ -67,12 +71,9 @@
 #endif
 
 #ifdef CONFIG_ISDN_DRV_AVMB1
-extern void avmb1_init(void);
+extern void kcapi_init(void);
 extern void capi_init(void);
 extern void capidrv_init(void);
-#ifdef CONFIG_PCI
-extern int b1pci_init(void);
-#endif
 #endif
 
 void
@@ -88,10 +89,7 @@
 	pcbit_init();
 #endif
 #ifdef CONFIG_ISDN_DRV_AVMB1
-	avmb1_init();
-#ifdef CONFIG_PCI
-	b1pci_init();
-#endif
+	kcapi_init();
 	capi_init();
 	capidrv_init();
 #endif
diff -urN linux-2.2.12-pre3/drivers/isdn/isdn_common.c linux-2.2.12-pre4/drivers/isdn/isdn_common.c
--- linux-2.2.12-pre3/drivers/isdn/isdn_common.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/isdn_common.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.c,v 1.83 1999/07/13 21:02:05 werner Exp $
+/* $Id: isdn_common.c,v 1.86 1999/07/31 12:59:42 armin Exp $
 
  * Linux ISDN subsystem, common used functions (linklevel).
  *
@@ -21,6 +21,15 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.c,v $
+ * Revision 1.86  1999/07/31 12:59:42  armin
+ * Added tty fax capabilities.
+ *
+ * Revision 1.85  1999/07/29 16:58:35  armin
+ * Bugfix: DLE handling in isdn_readbchan()
+ *
+ * Revision 1.84  1999/07/25 16:21:10  keil
+ * fix number matching
+ *
  * Revision 1.83  1999/07/13 21:02:05  werner
  * Added limit possibilty of driver b_channel resources (ISDN_STAT_DISCH)
  *
@@ -372,7 +381,7 @@
 
 isdn_dev *dev = (isdn_dev *) 0;
 
-static char *isdn_revision = "$Revision: 1.83 $";
+static char *isdn_revision = "$Revision: 1.86 $";
 
 extern char *isdn_net_revision;
 extern char *isdn_tty_revision;
@@ -483,6 +492,8 @@
 	register int reverse;
 	register int nostar = 1;
 
+	if (!(*s) && !(*p))
+		return(1);
 	for (; *p; s++, p++)
 		switch (*p) {
 			case '\\':
@@ -1041,6 +1052,11 @@
 			break;
 		case CAPI_PUT_MESSAGE:
 			return(isdn_capi_rec_hl_msg(&c->parm.cmsg));
+#ifdef CONFIG_ISDN_TTY_FAX
+		case ISDN_STAT_FAXIND:
+			isdn_tty_stat_callback(i, c);
+			break;
+#endif
 #ifdef CONFIG_ISDN_AUDIO
 		case ISDN_STAT_AUDIO:
 			isdn_tty_stat_callback(i, c);
@@ -1119,7 +1135,7 @@
 		if (ISDN_AUDIO_SKB_LOCK(skb))
 			break;
 		ISDN_AUDIO_SKB_LOCK(skb) = 1;
-		if (ISDN_AUDIO_SKB_DLECOUNT(skb)) {
+		if ((ISDN_AUDIO_SKB_DLECOUNT(skb)) || (dev->drv[di]->DLEflag & (1 << channel))) {
 			char *p = skb->data;
 			unsigned long DLEmask = (1 << channel);
 
@@ -2605,6 +2621,9 @@
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		isdn_tty_cleanup_xmit(&dev->mdm.info[i]);
 		kfree(dev->mdm.info[i].xmit_buf - 4);
+#ifdef CONFIG_ISDN_TTY_FAX
+		kfree(dev->mdm.info[i].fax);
+#endif
 	}
 	if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) {
 		printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n");
diff -urN linux-2.2.12-pre3/drivers/isdn/isdn_tty.c linux-2.2.12-pre4/drivers/isdn/isdn_tty.c
--- linux-2.2.12-pre3/drivers/isdn/isdn_tty.c	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/isdn_tty.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: isdn_tty.c,v 1.68 1999/07/11 17:51:51 armin Exp $
+/* $Id: isdn_tty.c,v 1.72 1999/07/31 12:59:45 armin Exp $
 
  * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
  *
@@ -20,6 +20,20 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.c,v $
+ * Revision 1.72  1999/07/31 12:59:45  armin
+ * Added tty fax capabilities.
+ *
+ * Revision 1.71  1999/07/27 10:34:34  armin
+ * Fixed last change. Did not compile with AUDIO support off.
+ *
+ * Revision 1.70  1999/07/25 16:17:58  keil
+ * Fix Suspend/Resume
+ *
+ * Revision 1.69  1999/07/25 12:56:15  armin
+ * isdn_tty_at_cout() now queues the message if online and
+ * data is in queue or flip buffer is full.
+ * needed for fax connections.
+ *
  * Revision 1.68  1999/07/11 17:51:51  armin
  * Bugfix, "-" was missing for AT&L settings.
  *
@@ -331,7 +345,7 @@
 static int si2bit[8] =
 {4, 1, 4, 4, 4, 4, 4, 4};
 
-char *isdn_tty_revision = "$Revision: 1.68 $";
+char *isdn_tty_revision = "$Revision: 1.72 $";
 
 
 /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb()
@@ -534,6 +548,15 @@
 		ISDN_AUDIO_SKB_DLECOUNT(skb) =
 			isdn_tty_countDLE(skb->data, skb->len);
 	}
+#ifdef CONFIG_ISDN_TTY_FAX
+	else {
+		if (info->faxonline & 2) {
+			isdn_tty_fax_bitorder(info, skb);
+			ISDN_AUDIO_SKB_DLECOUNT(skb) =
+				isdn_tty_countDLE(skb->data, skb->len);
+		}
+	}
+#endif
 #endif
 	/* Try to deliver directly via tty-flip-buf if queue is empty */
 	save_flags(flags);
@@ -863,6 +886,35 @@
 	}
 }
 
+/*
+ * return the usage calculated by si and layer 2 protocol
+ */
+int
+isdn_calc_usage(int si, int l2)
+{
+	int usg = ISDN_USAGE_MODEM;
+
+#ifdef CONFIG_ISDN_AUDIO
+	if (si == 1) {
+		switch(l2) {
+			case ISDN_PROTO_L2_MODEM: 
+				usg = ISDN_USAGE_MODEM;
+				break;
+#ifdef CONFIG_ISDN_TTY_FAX
+			case ISDN_PROTO_L2_FAX: 
+				usg = ISDN_USAGE_FAX;
+				break;
+#endif
+			case ISDN_PROTO_L2_TRANS: 
+			default:
+				usg = ISDN_USAGE_VOICE;
+				break;
+		}
+	}
+#endif
+	return(usg);
+}
+
 /* isdn_tty_dial() performs dialing of a tty an the necessary
  * setup of the lower levels before that.
  */
@@ -882,8 +934,14 @@
 			si = bit2si[j];
 			break;
 		}
+	usg = isdn_calc_usage(si, l2);
 #ifdef CONFIG_ISDN_AUDIO
-	if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
+	if ((si == 1) && 
+		(l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+		&& (l2 != ISDN_PROTO_L2_FAX)
+#endif
+		) {
 		l2 = ISDN_PROTO_L2_TRANS;
 		usg = ISDN_USAGE_VOICE;
 	}
@@ -921,6 +979,12 @@
 		cmd.driver = info->isdn_driver;
 		cmd.command = ISDN_CMD_SETL3;
 		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+#ifdef CONFIG_ISDN_TTY_FAX
+		if (l2 == ISDN_PROTO_L2_FAX) {
+			cmd.parm.fax = info->fax;
+			info->fax->direction = ISDN_TTY_FAX_CONN_OUT;
+		}
+#endif
 		isdn_command(&cmd);
 		cmd.driver = info->isdn_driver;
 		cmd.arg = info->isdn_channel;
@@ -943,7 +1007,7 @@
  * ISDN-line (hangup). The usage-status is cleared
  * and some cleanup is done also.
  */
-static void
+void
 isdn_tty_modem_hup(modem_info * info, int local)
 {
 	isdn_ctrl cmd;
@@ -964,6 +1028,10 @@
 	}
 #ifdef CONFIG_ISDN_AUDIO
 	info->vonline = 0;
+#ifdef CONFIG_ISDN_TTY_FAX
+	info->faxonline = 0;
+	info->fax->phase = ISDN_FAX_PHASE_IDLE;
+#endif
 	info->emu.vpar[4] = 0;
 	info->emu.vpar[5] = 8;
 	if (info->dtmf_state) {
@@ -997,9 +1065,8 @@
 		}
 		isdn_all_eaz(info->isdn_driver, info->isdn_channel);
 		info->emu.mdmreg[REG_RINGCNT] = 0;
-		usage = ((info->emu.mdmreg[REG_SI1I] != 1) || 
-		    (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ?
-			ISDN_USAGE_MODEM : ISDN_USAGE_VOICE;
+		usage = isdn_calc_usage(info->emu.mdmreg[REG_SI1I],
+					info->emu.mdmreg[REG_L2PROT]);
 		isdn_free_channel(info->isdn_driver, info->isdn_channel,
 				  usage);
 	}
@@ -1086,8 +1153,14 @@
 			si = bit2si[j];
 			break;
 		}
+	usg = isdn_calc_usage(si, l2);
 #ifdef CONFIG_ISDN_AUDIO
-	if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
+	if ((si == 1) && 
+		(l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+		&& (l2 != ISDN_PROTO_L2_FAX)
+#endif
+		) {
 		l2 = ISDN_PROTO_L2_TRANS;
 		usg = ISDN_USAGE_VOICE;
 	}
@@ -1140,11 +1213,11 @@
 		cmd.parm.cmsg.para[5] = l;
 		strncpy(&cmd.parm.cmsg.para[6], id, l);
 		cmd.command =CAPI_PUT_MESSAGE;
-/*		info->dialing = 1;
-		strcpy(dev->num[i], n);
+		info->dialing = 1;
+//		strcpy(dev->num[i], n);
 		isdn_info_update();
-*/
 		isdn_command(&cmd);
+		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
 	}
 }
 
@@ -1174,8 +1247,14 @@
 			si = bit2si[j];
 			break;
 		}
+	usg = isdn_calc_usage(si, l2);
 #ifdef CONFIG_ISDN_AUDIO
-	if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
+	if ((si == 1) && 
+		(l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+		&& (l2 != ISDN_PROTO_L2_FAX)
+#endif
+		) {
 		l2 = ISDN_PROTO_L2_TRANS;
 		usg = ISDN_USAGE_VOICE;
 	}
@@ -2160,8 +2239,42 @@
 	m->vpar[1] = 0;         /* Silence detection level (0 = none      ) */
 	m->vpar[2] = 70;        /* Silence interval        (7 sec.        ) */
 	m->vpar[3] = 2;         /* Compression type        (1 = ADPCM-2   ) */
-	m->vpar[4] = 0;		/* DTMF detection level    (0 = softcode  ) */
-	m->vpar[5] = 8;		/* DTMF interval           (8 * 5 ms.     ) */
+	m->vpar[4] = 0;         /* DTMF detection level    (0 = softcode  ) */
+	m->vpar[5] = 8;         /* DTMF interval           (8 * 5 ms.     ) */
+}
+#endif
+
+#ifdef CONFIG_ISDN_TTY_FAX
+static void
+isdn_tty_modem_reset_faxpar(modem_info * info)
+{
+	T30_s *f = info->fax;
+
+	f->code = 0;
+	f->phase = ISDN_FAX_PHASE_IDLE;
+	f->direction = 0;
+	f->resolution = 1;	/* fine */
+	f->rate = 5;		/* 14400 bit/s */
+	f->width = 0;
+	f->length = 0;
+	f->compression = 0;
+	f->ecm = 0;
+	f->binary = 0;
+	f->scantime = 0;
+	memset(&f->id[0], 32, FAXIDLEN - 1);
+	f->id[FAXIDLEN - 1] = 0;
+	f->badlin = 0;
+	f->badmul = 0;
+	f->bor = 0;
+	f->nbc = 0;
+	f->cq = 0;
+	f->cr = 0;
+	f->ctcrty = 0;
+	f->minsp = 0;
+	f->phcto = 30;
+	f->rel = 0;
+	memset(&f->pollid[0], 32, FAXIDLEN - 1);
+	f->pollid[FAXIDLEN - 1] = 0;
 }
 #endif
 
@@ -2178,6 +2291,9 @@
 #ifdef CONFIG_ISDN_AUDIO
 	isdn_tty_modem_reset_vpar(m);
 #endif
+#ifdef CONFIG_ISDN_TTY_FAX
+	isdn_tty_modem_reset_faxpar(info);
+#endif
 	m->mdmcmdl = 0;
 }
 
@@ -2250,6 +2366,12 @@
 	}
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		info = &m->info[i];
+#ifdef CONFIG_ISDN_TTY_FAX
+		if (!(info->fax = kmalloc(sizeof(T30_s), GFP_KERNEL))) {
+			printk(KERN_ERR "Could not allocate fax t30-buffer\n");
+			return -3;
+		}
+#endif
 #ifdef COMPAT_HAS_NEW_WAITQ
 		init_MUTEX(&info->write_sem);
 #else
@@ -2330,8 +2452,15 @@
 				break;
 		}
 		return ret;
-	} else
-		return isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di));
+	} else {
+		int tmp;
+		tmp = isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di));
+#ifdef ISDN_DEBUG_MODEM_ICALL
+			printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n",
+			       isdn_map_eaz2msn(emu->msn, di), tmp);
+#endif
+		return tmp;
+	}
 }
 
 /*
@@ -2383,7 +2512,7 @@
 		    (info->emu.mdmreg[REG_SI2] == si2))	{         /* SI2 is matching */
 			idx = isdn_dc2minor(di, ch);
 #ifdef ISDN_DEBUG_MODEM_ICALL
-			printk(KERN_DEBUG "m_fi: match1\n");
+			printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret);
 			printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx,
 			       info->flags, info->isdn_driver, info->isdn_channel,
 			       dev->usage[idx]);
@@ -2405,8 +2534,7 @@
 					info->drv_index = idx;
 					dev->m_idx[idx] = info->line;
 					dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
-					dev->usage[idx] |= ((si1 != 1) || (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ? 
-						ISDN_USAGE_MODEM : ISDN_USAGE_VOICE;
+					dev->usage[idx] |= isdn_calc_usage(si1, info->emu.mdmreg[REG_L2PROT]); 
 					strcpy(dev->num[idx], nr);
 					strcpy(info->emu.cpn, eaz);
 					info->emu.mdmreg[REG_SI1I] = si2bit[si1];
@@ -2584,6 +2712,13 @@
 					}
 				}
 				return 1;
+#ifdef CONFIG_ISDN_TTY_FAX
+			case ISDN_STAT_FAXIND:
+				if (TTY_IS_ACTIVE(info)) {
+					isdn_tty_fax_command(info); 
+				}
+				break;
+#endif
 #ifdef CONFIG_ISDN_AUDIO
 			case ISDN_STAT_AUDIO:
 				if (TTY_IS_ACTIVE(info)) {
@@ -2621,6 +2756,8 @@
 	char *p;
 	char c;
 	ulong flags;
+	struct sk_buff *skb = 0;
+	char *sp = 0;
 
 	if (!msg) {
 		printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n");
@@ -2629,6 +2766,34 @@
 	save_flags(flags);
 	cli();
 	tty = info->tty;
+	if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
+		restore_flags(flags);
+		return;
+	}
+
+	/* use queue instead of direct flip, if online and */
+	/* data is in queue or flip buffer is full */
+	if ((info->online) && (((tty->flip.count + strlen(msg)) >= TTY_FLIPBUF_SIZE) ||
+	    (!skb_queue_empty(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel])))) {
+		skb = alloc_skb(strlen(msg)
+#ifdef CONFIG_ISDN_AUDIO
+			+ sizeof(isdn_audio_skb)
+#endif
+			, GFP_ATOMIC);
+		if (!skb) {
+			restore_flags(flags);
+			return;
+		}
+#ifdef CONFIG_ISDN_AUDIO
+		skb_reserve(skb, sizeof(isdn_audio_skb));
+#endif
+		sp = skb_put(skb, strlen(msg));
+#ifdef CONFIG_ISDN_AUDIO
+		ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+		ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+	}
+
 	for (p = msg; *p; p++) {
 		switch (*p) {
 			case '\r':
@@ -2643,16 +2808,26 @@
 			default:
 				c = *p;
 		}
-		if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
-			restore_flags(flags);
-			return;
+		if (skb) {
+			*sp++ = c;
+		} else {
+			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+				break;
+			tty_insert_flip_char(tty, c, 0);
 		}
-		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-			break;
-		tty_insert_flip_char(tty, c, 0);
 	}
-	restore_flags(flags);
-	queue_task(&tty->flip.tqueue, &tq_timer);
+	if (skb) {
+		__skb_queue_tail(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel], skb);
+		dev->drv[info->isdn_driver]->rcvcount[info->isdn_channel] += skb->len;
+		restore_flags(flags);
+		/* Schedule dequeuing */
+		if ((dev->modempoll) && (info->rcvsched))
+			isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+
+	} else {
+		restore_flags(flags);
+		queue_task(&tty->flip.tqueue, &tq_timer);
+	}
 }
 
 /*
@@ -3328,7 +3503,7 @@
 		/* If more than one bit set in reg18, autoselect Layer2 */
 		if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) {
 			if (m->mdmreg[REG_SI1I] == 1) {
-				if (l2 != ISDN_PROTO_L2_MODEM)
+				if ((l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX))
 					l2 = ISDN_PROTO_L2_TRANS;
 			} else
 				l2 = ISDN_PROTO_L2_X75I;
@@ -3342,6 +3517,12 @@
 		cmd.driver = info->isdn_driver;
 		cmd.command = ISDN_CMD_SETL3;
 		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+#ifdef CONFIG_ISDN_TTY_FAX
+		if (l2 == ISDN_PROTO_L2_FAX) {
+			cmd.parm.fax = info->fax;
+			info->fax->direction = ISDN_TTY_FAX_CONN_IN;
+		}
+#endif
 		isdn_command(&cmd);
 		cmd.driver = info->isdn_driver;
 		cmd.arg = info->isdn_channel;
@@ -3362,7 +3543,6 @@
 isdn_tty_cmd_PLUSF(char **p, modem_info * info)
 {
 	atemu *m = &info->emu;
-	int par;
 	char rs[20];
 
 	if (!strncmp(p[0], "CLASS", 5)) {
@@ -3372,6 +3552,10 @@
 				p[0]++;
 				sprintf(rs, "\r\n%d",
 					(m->mdmreg[REG_SI1] & 1) ? 8 : 0);
+#ifdef CONFIG_ISDN_TTY_FAX
+				if (m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX)
+				sprintf(rs, "\r\n2");
+#endif
 				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
@@ -3379,23 +3563,37 @@
 				switch (*p[0]) {
 					case '0':
 						p[0]++;
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
 						m->mdmreg[REG_SI1] = 4;
 						info->xmit_size =
 						    m->mdmreg[REG_PSIZE] * 16;
 						break;
+#ifdef CONFIG_ISDN_TTY_FAX
 					case '2':
-						printk(KERN_DEBUG "isdn_tty: FCLASS=2\n");
 						p[0]++;
+						m->mdmreg[REG_SI1] = 1;
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FAX;
+						info->xmit_size =
+						    m->mdmreg[REG_PSIZE] * 16;
 						break;
+#endif
 					case '8':
 						p[0]++;
+						/* L2 will change on dialout with si=1 */
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
 						m->mdmreg[REG_SI1] = 5;
 						info->xmit_size = VBUF;
 						break;
 					case '?':
 						p[0]++;
-						isdn_tty_at_cout("\r\n0,2,8",
-								 info);
+#ifdef CONFIG_ISDN_TTY_FAX
+						isdn_tty_at_cout("\r\n0,2,8", info);
+#else
+						isdn_tty_at_cout("\r\n0,8", info);
+#endif
 						break;
 					default:
 						PARSE_ERROR1;
@@ -3406,115 +3604,11 @@
 		}
 		return 0;
 	}
-	if (!strncmp(p[0], "AA", 2)) {
-		p[0] += 2;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d",
-					m->mdmreg[REG_RINGATA]);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				par = isdn_getnum(p);
-				if ((par < 0) || (par > 255))
-					PARSE_ERROR1;
-				m->mdmreg[REG_RINGATA] = par;
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-        if (!strncmp(p[0], "TBC=", 4)) { /* UNKLAR !! */
-                p[0] += 4;
-                printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]);
-                switch (*p[0]) {
-                        case '0':
-                                p[0]++;
-                                break;
-                        default:
-                                PARSE_ERROR1;
-                }
-                return 0;
-        }
-        if (!strncmp(p[0], "BOR=", 4)) { /* UNKLAR !! */
-                p[0] += 4;
-                printk(KERN_DEBUG "isdn_tty: Fax FBOR=%c\n", *p[0]);
-                switch (*p[0]) {
-                        case '0':
-                                p[0]++;
-                                break;
-                        default:
-                                PARSE_ERROR1;
-                }
-                return 0;
-        }
-        if (!strncmp(p[0], "DCC=", 4)) { /* SETUP irgendwie !! */
-		int i, val[]={0,0,0,0,0,0,0,0};
-
-                p[0] += 4;
-                if (*p[0] == '?') {
-			isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0,1),(0),(0),(0-7)",info);
-			p[0]++;
-                } else {
-			for (i=0; (*p[0]>='0') && (*p[0]<='9'); i++) {
-				val[i] = *p[0] - 48;
-				p[0]++;
-				if (*p[0] == ',')
-					p[0]++;
-			}
-			printk(KERN_DEBUG "isdn_tty: Fax Setup values=%d,%d,%d,%d,%d,%d,%d,%d\n",
-			       val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
-                }
-                return 0;
-        }
-        if (!strncmp(p[0], "LID=", 4)) { /* set sender ID !! */
-		char senderID[80];
-		int i;
-
-                p[0] += 4;
-                if (*p[0] =='"')
-			p[0]++;
-                for(i=0; (*p[0]) && (*p[0] != '"'); i++)
-			senderID[i] = *p[0]++;
-                senderID[i] = 0;
-                if (*p[0] =='"')
-			p[0]++;
-                printk(KERN_DEBUG "isdn_tty: Fax sender=>%s<\n", senderID);
-                return 0;
-        }
-        if (!strncmp(p[0], "MFR?", 4)) {
-                p[0] += 4;
-                printk(KERN_DEBUG "isdn_tty: FMFR?\n");
-                isdn_tty_at_cout("\r\nISDNfax", info);
-                return 0;
-        }
-        if (!strncmp(p[0], "MDL?", 4)) {
-                p[0] += 4;
-                printk(KERN_DEBUG "isdn_tty: FMDL?\n");
-                isdn_tty_at_cout("\r\nAVM-B1", info);
-                return 0;
-        }
-        if (!strncmp(p[0], "AP=?", 4)) {
-                p[0] += 4;
-                printk(KERN_DEBUG "isdn_tty: FAP=?\n");
-                return 0;
-        }
-        if (!strncmp(p[0], "PHCTO=", 6)) {
-		/* beim trace mit dem zyxel folgt der wert 30 ;*/
-                p[0] += 6;
-                printk(KERN_DEBUG "isdn_tty: FPHCTO=%s\n", p[0]);
-                return 0;
-        }
-        if (!strncmp(p[0], "CR=", 3)) {
-                p[0] += 3;
-                printk(KERN_DEBUG "isdn_tty: FCR=%s\n", p[0]);
-                return 0;
-        }
-        printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]);
+#ifdef CONFIG_ISDN_TTY_FAX
+	return (isdn_tty_cmd_PLUSF_FAX(p, info));
+#else
 	PARSE_ERROR1;
+#endif
 }
 
 /*
@@ -3959,8 +4053,10 @@
 						isdn_tty_suspend(ds, info, m);
 						break;
 					case 'R':	/* RESUME */
+						p++;
 						isdn_tty_get_msnstr(ds, &p);
 						isdn_tty_resume(ds, info, m);
+						break;
 					case 'M':	/* MESSAGE */
 						p++;
 						isdn_tty_send_msg(info, m, p);
diff -urN linux-2.2.12-pre3/drivers/isdn/isdn_ttyfax.c linux-2.2.12-pre4/drivers/isdn/isdn_ttyfax.c
--- linux-2.2.12-pre3/drivers/isdn/isdn_ttyfax.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.12-pre4/drivers/isdn/isdn_ttyfax.c	2003-08-15 15:06:54.000000000 -0700
@@ -0,0 +1,1201 @@
+/* $Id: isdn_ttyfax.c,v 1.2 1999/08/05 10:36:10 armin Exp $
+ * Linux ISDN subsystem, tty_fax AT-command emulator (linklevel).
+ *
+ * Copyright 1999    by Armin Schindler (mac@melware.de)
+ * Copyright 1999    by Ralf Spachmann (mel@melware.de)
+ * Copyright 1999    by Cytronics & Melware 
+ *
+ * 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.
+ *
+ * $Log: isdn_ttyfax.c,v $
+ * Revision 1.2  1999/08/05 10:36:10  armin
+ * Bugfix: kernel oops on getting revision.
+ *
+ * Revision 1.1  1999/07/31 12:59:50  armin
+ * Added tty fax capabilities.
+ *
+ *
+ */
+
+#undef ISDN_TTY_FAX_STAT_DEBUG
+#undef ISDN_TTY_FAX_CMD_DEBUG
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_tty.h"
+#include "isdn_ttyfax.h"
+
+
+static char *isdn_tty_fax_revision = "$Revision: 1.2 $";
+
+#define PARSE_ERROR1 { isdn_tty_fax_modem_result(1, info); return 1; }
+
+static char *
+isdn_getrev(const char *revision)
+{
+        char *rev;
+        char *p;
+
+        if ((p = strchr(revision, ':'))) {
+                rev = p + 2;
+                p = strchr(rev, '$');
+                *--p = 0;
+        } else
+                rev = "???";
+        return rev;
+}
+
+
+/*
+ * Fax Class 2 Modem results
+ *
+ */
+static void isdn_tty_fax_modem_result(int code, modem_info * info)
+{
+	atemu *m = &info->emu;
+	T30_s *f = info->fax;
+	char rs[50];
+	char rss[50];
+	char *rp;
+	int i;
+	static char *msg[] =
+	{"OK", "ERROR", "+FCON", "+FCSI:", "+FDIS:",
+	 "+FHNG:", "+FDCS:", "CONNECT", "+FTSI:",
+	 "+FCFR", "+FPTS:", "+FET:" };
+
+
+	isdn_tty_at_cout("\r\n", info);
+	isdn_tty_at_cout(msg[code], info);
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty: Fax send %s on ttyI%d\n",
+		msg[code], info->line);
+#endif
+	switch (code) {
+		case 0: /* OK */
+			break;
+		case 1: /* ERROR */
+			break;
+		case 2:	/* +FCON */
+			/* Append CPN, if enabled */
+			if ((m->mdmreg[REG_CPN] & BIT_CPN) &&
+				(!(dev->usage[info->isdn_channel] & ISDN_USAGE_OUTGOING))) {
+				sprintf(rs, "/%s", m->cpn);
+				isdn_tty_at_cout(rs, info);
+			}
+			info->online = 1;
+			f->fet = 0;
+			if (f->phase == ISDN_FAX_PHASE_A)
+				f->phase = ISDN_FAX_PHASE_B;
+			break;
+		case 3:	/* +FCSI */
+		case 8:	/* +FTSI */
+			sprintf(rs, "\"%s\"", f->r_id);
+			isdn_tty_at_cout(rs, info); 
+			break;
+		case 4:	/* +FDIS */
+			rs[0] = 0;
+			rp = &f->r_resolution;
+			for(i = 0; i < 8; i++) {
+				sprintf(rss, "%c%s", rp[i] + 48,
+					(i < 7) ? "," : "");
+				strcat(rs, rss);
+			}
+			isdn_tty_at_cout(rs, info);
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+			printk(KERN_DEBUG "isdn_tty: Fax DIS=%s on ttyI%d\n",
+				rs, info->line);
+#endif
+			break;
+		case 5:	/* +FHNG */
+			sprintf(rs, "%d", f->code);
+			isdn_tty_at_cout(rs, info); 
+			info->faxonline = 0;
+			break;
+		case 6:	/* +FDCS */
+			rs[0] = 0;
+			rp = &f->r_resolution;
+			for(i = 0; i < 8; i++) {
+				sprintf(rss, "%c%s", rp[i] + 48,
+					(i < 7) ? "," : "");
+				strcat(rs, rss);
+			}
+			isdn_tty_at_cout(rs, info);
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+			printk(KERN_DEBUG "isdn_tty: Fax DCS=%s on ttyI%d\n",
+				rs, info->line);
+#endif
+			break;
+		case 7:	/* CONNECT */
+			info->faxonline |= 2;
+			break;
+		case 9: /* FCFR */
+			break;
+		case 10: /* FPTS */
+			isdn_tty_at_cout("1", info);
+			break;
+		case 11: /* FET */
+			sprintf(rs, "%d", f->fet);
+			isdn_tty_at_cout(rs, info); 
+			break;
+	}
+
+	isdn_tty_at_cout("\r\n", info);
+
+	switch (code) {
+		case 7: /* CONNECT */
+			info->online = 2;
+			if (info->faxonline & 1) {
+				sprintf(rs, "%c", XON);
+				isdn_tty_at_cout(rs, info); 
+			}
+			break;
+	}
+}
+
+int
+isdn_tty_fax_command(modem_info * info)
+{
+	T30_s *f = info->fax;
+	char rs[10];
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty: Fax cmd %d on ttyI%d\n",
+		f->r_code, info->line);
+#endif
+	switch(f->r_code) {
+		case ISDN_TTY_FAX_FCON:
+			info->faxonline = 1;
+			isdn_tty_fax_modem_result(2, info); /* +FCON */
+			return(0);
+		case ISDN_TTY_FAX_FCON_I:
+			info->faxonline = 16;
+			isdn_tty_fax_modem_result(2, info); /* +FCON */
+			return(0);
+		case ISDN_TTY_FAX_RID:
+			if (info->faxonline & 1)
+				isdn_tty_fax_modem_result(3, info); /* +FCSI */
+			if (info->faxonline & 16)
+				isdn_tty_fax_modem_result(8, info); /* +FTSI */
+			return(0);
+		case ISDN_TTY_FAX_DIS:
+			isdn_tty_fax_modem_result(4, info); /* +FDIS */
+			return(0);
+		case ISDN_TTY_FAX_HNG:
+			if (f->phase == ISDN_FAX_PHASE_C) {
+				if (f->direction == ISDN_TTY_FAX_CONN_IN) {
+					sprintf(rs, "%c%c", DLE, ETX);
+					isdn_tty_at_cout(rs, info); 
+				} else {
+					sprintf(rs, "%c", 0x18);
+					isdn_tty_at_cout(rs, info); 
+				}
+				info->faxonline &= ~2; /* leave data mode */
+				info->online = 1;
+			}
+			f->phase = ISDN_FAX_PHASE_E;
+			isdn_tty_fax_modem_result(5, info); /* +FHNG */
+			isdn_tty_fax_modem_result(0, info); /* OK */
+			return(0);
+		case ISDN_TTY_FAX_DCS:
+			isdn_tty_fax_modem_result(6, info); /* +FDCS */
+			isdn_tty_fax_modem_result(7, info); /* CONNECT */
+			f->phase = ISDN_FAX_PHASE_C;
+			return(0);
+		case ISDN_TTY_FAX_TRAIN_OK:
+			isdn_tty_fax_modem_result(6, info); /* +FDCS */
+			isdn_tty_fax_modem_result(0, info); /* OK */
+			return(0);
+		case ISDN_TTY_FAX_SENT:
+			isdn_tty_fax_modem_result(0, info); /* OK */
+			return(0);
+		case ISDN_TTY_FAX_CFR:
+			isdn_tty_fax_modem_result(9, info); /* +FCFR */
+			return(0);
+		case ISDN_TTY_FAX_ET:
+			sprintf(rs, "%c%c", DLE, ETX);
+			isdn_tty_at_cout(rs, info); 
+			isdn_tty_fax_modem_result(10, info); /* +FPTS */
+			isdn_tty_fax_modem_result(11, info); /* +FET */
+			isdn_tty_fax_modem_result(0, info); /* OK */
+			info->faxonline &= ~2; /* leave data mode */
+			info->online = 1;
+			f->phase = ISDN_FAX_PHASE_D;
+			return(0);
+		case ISDN_TTY_FAX_PTS:
+			isdn_tty_fax_modem_result(10, info); /* +FPTS */
+			if (f->direction == ISDN_TTY_FAX_CONN_OUT) {
+				if (f->fet == 1)
+					f->phase = ISDN_FAX_PHASE_B;
+				if (f->fet == 0)
+					isdn_tty_fax_modem_result(0, info); /* OK */
+			}
+			return(0);
+		case ISDN_TTY_FAX_EOP:
+			info->faxonline &= ~2; /* leave data mode */
+			info->online = 1;
+			f->phase = ISDN_FAX_PHASE_D;
+			return(0);
+
+	}
+	return(-1);
+}
+
+
+void
+isdn_tty_fax_bitorder(modem_info *info, struct sk_buff *skb)
+{
+	__u8 LeftMask;
+	__u8 RightMask;
+	__u8 fBit;
+	__u8 Data;
+	int i;
+
+	if (!info->fax->bor) {
+		for(i = 0; i < skb->len; i++) {
+			Data = skb->data[i];
+			for (
+				LeftMask = 0x80, RightMask = 0x01;
+				LeftMask > RightMask;
+				LeftMask >>= 1, RightMask <<= 1
+			) {
+				fBit = (Data & LeftMask);
+				if (Data & RightMask)
+					Data |= LeftMask;
+				else
+					Data &= ~LeftMask;
+				if (fBit)
+					Data |= RightMask;
+				else
+					Data &= ~RightMask;
+
+			}
+			skb->data[i] = Data;
+		}
+	}
+}
+
+/*
+ * Parse AT+F.. FAX class 2 commands
+ */
+
+int isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info)
+{
+	atemu *m = &info->emu;
+	T30_s *f = info->fax;
+	isdn_ctrl cmd;
+	int par;
+	char rs[50];
+	char rss[50];
+	int maxdccval[]={1,5,2,2,3,2,0,7};
+
+	/* FAA still unchanged */
+        if (!strncmp(p[0], "AA", 2)) { /* TODO */
+		p[0] += 2;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", 0);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				par = isdn_getnum(p);
+				if ((par < 0) || (par > 255))
+					PARSE_ERROR1;
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+       }
+
+	/* BADLIN=value - dummy 0=disable errorchk disabled, 1-255 nr. of lines for making page bad */
+        if (!strncmp(p[0], "BADLIN", 6)) {
+            p[0] += 6;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d",f->badlin);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0-255");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 255))
+                                            PARSE_ERROR1;
+                                        f->badlin = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FBADLIN=%d\n", par);
+#endif
+                                    }
+                                break;
+			default:
+			        PARSE_ERROR1;
+		}
+                return 0;
+        }
+
+	/* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */
+        if (!strncmp(p[0], "BADMUL", 6)){
+            p[0] +=6;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d", f->badmul);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0-255");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 255))
+                                            PARSE_ERROR1;
+                                        f->badmul = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FBADMUL=%d\n", par);
+#endif
+                                    }
+                                    break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+
+	/* BOR=n - Phase C bit order, 0=direct, 1=reverse */
+        if (!strncmp(p[0], "BOR", 3)){
+            p[0] +=3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d", f->bor);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0,1");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 1))
+                                            PARSE_ERROR1;
+                                        f->bor = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FBOR=%d\n", par);
+#endif
+                                    }
+                                break;
+                        default:
+                                PARSE_ERROR1;
+                                }
+                       return 0;
+        }
+
+	/* NBC=n - No Best Capabilities */
+        if (!strncmp(p[0], "NBC", 3)){
+            p[0] +=3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d", f->nbc);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0,1");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 1))
+                                            PARSE_ERROR1;
+                                        f->nbc = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FNBC=%d\n", par);
+#endif
+                                    }
+                                break;
+                        default:
+                                PARSE_ERROR1;
+                                }
+                       return 0;
+        }
+
+	/* BUF? - Readonly buffersize readout  */
+        if (!strncmp(p[0], "BUF?", 4)) {
+                p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                printk(KERN_DEBUG "isdn_tty: Fax FBUF? (%d) \n", (16 * m->mdmreg[REG_PSIZE]));
+#endif
+                                p[0]++;
+                                 sprintf(rs, "\r\n %d ", (16 * m->mdmreg[REG_PSIZE]));
+                                isdn_tty_at_cout(rs, info);
+                return 0;
+        }
+        
+	/* CIG=string - local fax station id string for polling rx */
+        if (!strncmp(p[0], "CIG", 3)) {
+		int i, r;
+                p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n\"%s\"", f->pollid);
+                                isdn_tty_at_cout(rs, info);
+                                break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n\"STRING\"");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        if (*p[0] =='"')
+                                            p[0]++;
+                                        for(i=0; (*p[0]) && i < (FAXIDLEN-1) && (*p[0] != '"'); i++)
+                                        {
+                                            f->pollid[i] = *p[0]++;
+                                        }
+                                        if (*p[0] =='"')
+                                            p[0]++;
+                                        for(r=i; r < FAXIDLEN; r++)
+                                        {
+                                        	f->pollid[r] = 32;
+                                        }
+                                        f->pollid[FAXIDLEN-1] = 0;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                       printk(KERN_DEBUG "isdn_tty: Fax local poll ID rx \"%s\"\n", f->pollid);
+#endif
+                                    }
+                                break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+
+	/* CQ=n - copy qlty chk, 0= no chk, 1=only 1D chk, 2=1D+2D chk */
+        if (!strncmp(p[0], "CQ", 2)) {
+                p[0] += 2;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d", f->cq);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0,1,2");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 2))
+                                            PARSE_ERROR1;
+                                        f->cq = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FCQ=%d\n", par);
+#endif
+                                    }
+                                    break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+
+	/* CR=n - can receive? 0= no data rx or poll remote dev, 1=do receive data or poll remote dev */
+        if (!strncmp(p[0], "CR", 2)) {
+                p[0] += 2;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d", f->cr); /* read actual value from struct and print */
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0,1"); /* display online help */
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 1))
+                                            PARSE_ERROR1;
+                                        f->cr = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FCR=%d\n", par);
+#endif
+                                    }
+                                    break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+
+	/* CTCRTY=value - ECM retry count */
+        if (!strncmp(p[0], "CTCRTY", 6)){
+            p[0] +=6;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d",f->ctcrty);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0-255");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 255))
+                                            PARSE_ERROR1;
+                                        f->ctcrty = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FCTCRTY=%d\n", par);
+#endif
+                                    }
+                                    break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+
+	/* DCC=vr,br,wd,ln,df,ec,bf,st - DCE capabilities parms */
+        if (!strncmp(p[0], "DCC", 3)) {
+		char *rp = &f->resolution;
+		int i;
+
+                p[0] += 3;
+		switch(*p[0]) {
+			case '?':
+				p[0]++;
+	                        strcpy(rs, "\r\n");
+	                        for(i = 0; i < 8; i++) {
+        	                        sprintf(rss, "%c%s", rp[i] + 48,
+                	                        (i < 7) ? "," : "");
+                        	        strcat(rs, rss);
+	                        }
+        	                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+		                if (*p[0] == '?') {
+					isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)",info);
+                                	p[0]++;
+		                } else {
+					for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<8); i++) {
+						if (*p[0] != ',') {
+							if ((*p[0] - 48) > maxdccval[i]) {
+								PARSE_ERROR1;
+							}
+							rp[i] = *p[0] - 48;
+							p[0]++;
+							if (*p[0] == ',') 
+								p[0]++;
+						} else p[0]++;
+					}
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+				printk(KERN_DEBUG "isdn_tty: Fax FDCC capabilities DCE=%d,%d,%d,%d,%d,%d,%d,%d\n",
+				       rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
+#endif
+        		        }
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+       
+	/* DIS=vr,br,wd,ln,df,ec,bf,st - current session parms */
+        if (!strncmp(p[0], "DIS", 3)) {
+		char *rp = &f->resolution;
+		int i;
+
+                p[0] += 3;
+		switch(*p[0]) {
+			case '?':
+				p[0]++;
+	                        strcpy(rs, "\r\n");
+	                        for(i = 0; i < 8; i++) {
+        	                        sprintf(rss, "%c%s", rp[i] + 48,
+                	                        (i < 7) ? "," : "");
+                        	        strcat(rs, rss);
+	                        }
+        	                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+		                if (*p[0] == '?') {
+					isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)",info);
+                                	p[0]++;
+		                } else {
+					for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<8); i++) {
+						if (*p[0] != ',') {
+							if ((*p[0] - 48) > maxdccval[i]) {
+								PARSE_ERROR1;
+							}
+							rp[i] = *p[0] - 48;
+							p[0]++;
+							if (*p[0] == ',') 
+								p[0]++;
+						} else p[0]++;
+					}
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+				printk(KERN_DEBUG "isdn_tty: Fax FDIS session parms=%d,%d,%d,%d,%d,%d,%d,%d\n",
+				       rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
+#endif
+        		        }
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+       
+	/* DR - Receive Phase C data command, initiates document reception */ 
+        if (!strncmp(p[0], "DR", 2)) {
+                p[0] += 2;
+		if ((info->faxonline & 16) &&	/* incoming connection */
+   		   ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D))) {
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+			printk(KERN_DEBUG "isdn_tty: Fax FDR\n");
+#endif
+			f->code = ISDN_TTY_FAX_DR;
+			cmd.driver = info->isdn_driver;
+			cmd.arg = info->isdn_channel;
+			cmd.command = ISDN_CMD_FAXCMD;
+			isdn_command(&cmd);
+			if (f->phase == ISDN_FAX_PHASE_B) {
+				f->phase = ISDN_FAX_PHASE_C;
+			} else if (f->phase == ISDN_FAX_PHASE_D) { 
+				switch(f->fet) {
+					case 0:	/* next page will be received */
+						f->phase = ISDN_FAX_PHASE_C;
+						isdn_tty_fax_modem_result(7, info); /* CONNECT */
+						break;
+					case 1:	/* next doc will be received */
+						f->phase = ISDN_FAX_PHASE_B;
+						break;
+					case 2:	/* fax session is terminating */
+						f->phase = ISDN_FAX_PHASE_E;
+						break;
+					default:	
+						PARSE_ERROR1;
+				}
+			}
+		} else {
+			PARSE_ERROR1;
+		}
+                return 1;
+	}
+ 
+	/* DT=df,vr,wd,ln - TX phase C data command (release DCE to proceed with negotiation) */
+        if (!strncmp(p[0], "DT", 2)) {
+		int i, val[]={4,0,2,3};
+		char *rp = &f->resolution;
+
+                p[0] += 2;
+		if (!info->faxonline & 1) 	/* not outgoing connection */
+			PARSE_ERROR1;
+
+		for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<4); i++) {
+			if (*p[0] != ',') {
+				if ((*p[0] - 48) > maxdccval[val[i]]) {
+					PARSE_ERROR1;
+				}
+				rp[val[i]] = *p[0] - 48;
+				p[0]++;
+				if (*p[0] == ',') 
+					p[0]++;
+			} else p[0]++;
+		}
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+		printk(KERN_DEBUG "isdn_tty: Fax FDT tx data command parms=%d,%d,%d,%d\n",
+		       rp[4], rp[0], rp[2], rp[3]);
+#endif
+		if ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D)) {
+			f->code = ISDN_TTY_FAX_DT;
+			cmd.driver = info->isdn_driver;
+			cmd.arg = info->isdn_channel;
+			cmd.command = ISDN_CMD_FAXCMD;
+			isdn_command(&cmd);
+			if (f->phase == ISDN_FAX_PHASE_D) {
+				f->phase = ISDN_FAX_PHASE_C;
+				isdn_tty_fax_modem_result(7, info); /* CONNECT */
+			}
+		} else {
+			PARSE_ERROR1;
+		}
+                return 1;
+        }
+
+	/* ECM=n - Error mode control 0=disabled, 2=enabled, handled by DCE alone incl. buff of partial pages */
+        if (!strncmp(p[0], "ECM", 3)) {
+                p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d",f->ecm);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0,2");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par != 0) && (par != 2))
+                                            PARSE_ERROR1;
+                                        f->ecm = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FECM=%d\n", par);
+#endif
+                                    }
+                                    break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+
+	/* ET=n - End of page or document */
+	if (!strncmp(p[0], "ET=", 3)) {
+		p[0] += 3;
+		if (*p[0] == '?') {
+			p[0]++;
+			sprintf(rs, "\r\n0-2");
+			isdn_tty_at_cout(rs, info);
+		} else {
+			if ((f->phase != ISDN_FAX_PHASE_D) || (!info->faxonline & 1))
+				PARSE_ERROR1;
+			par = isdn_getnum(p);
+			if ((par < 0) || (par > 2))
+				PARSE_ERROR1;
+			f->fet = par;
+			f->code = ISDN_TTY_FAX_ET;
+			cmd.driver = info->isdn_driver;
+			cmd.arg = info->isdn_channel;
+			cmd.command = ISDN_CMD_FAXCMD;
+			isdn_command(&cmd);
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+			printk(KERN_DEBUG "isdn_tty: Fax FET=%d\n", par);
+#endif
+			return 1;
+		}
+		return 0;
+	}
+
+	/* K - terminate */
+	if (!strncmp(p[0], "K", 1)) {
+		p[0] += 1;
+		if ((f->phase == ISDN_FAX_PHASE_IDLE) || (f->phase == ISDN_FAX_PHASE_E))
+			PARSE_ERROR1;
+		isdn_tty_modem_hup(info, 1);
+		return 1;
+	}
+
+	/* LID=string - local fax ID */
+        if (!strncmp(p[0], "LID", 3)) {
+		int i, r;
+                p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n\"%s\"", f->id);
+                                isdn_tty_at_cout(rs, info);
+                                break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n\"STRING\"");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        if (*p[0] =='"')
+                                            p[0]++;
+                                        for(i=0; (*p[0]) && i < (FAXIDLEN-1) && (*p[0] != '"'); i++)
+                                        {
+                                            f->id[i] = *p[0]++;
+                                        }
+                                        if (*p[0] =='"')
+                                            p[0]++;
+                                        for(r=i; r < FAXIDLEN; r++)
+                                        {
+                                        	f->id[r] = 32;
+                                        }
+                                        f->id[FAXIDLEN-1] = 0;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                       printk(KERN_DEBUG "isdn_tty: Fax local ID \"%s\"\n", f->id);
+#endif
+                                    }
+                                break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+
+#if 0
+	/* LO=n - Flow control opts */
+        if (!strncmp(p[0], "LO", 2)) { /* TODO */
+                p[0] += 2;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d",f->lo);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0,1,2");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 2))
+                                            PARSE_ERROR1;
+                                        f->lo = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FLO=%d\n", par);
+#endif
+                                    }
+                                    break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+#endif
+#if 0
+	/* LPL=n - Doc for polling cmd  */
+        if (!strncmp(p[0], "LPL", 3)) { /* TODO */
+                p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d",f->lpl);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0,1");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 1))
+                                            PARSE_ERROR1;
+                                        f->lpl = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FLPL=%d\n", par);
+#endif
+                                    }
+                                    break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+#endif
+
+	/* MDL? - DCE Model       */
+        if (!strncmp(p[0], "MDL?", 4)) {
+                p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                printk(KERN_DEBUG "isdn_tty: FMDL?\n");
+#endif
+                isdn_tty_at_cout("\r\nisdn4linux", info);
+                return 0;
+        }
+
+	/* MFR? - DCE Manufacturer */
+        if (!strncmp(p[0], "MFR?", 4)) {
+                p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                printk(KERN_DEBUG "isdn_tty: FMFR?\n");
+#endif
+                isdn_tty_at_cout("\r\nisdn4linux", info);
+                return 0;
+        }
+
+	/* MINSP=n - Minimum Speed for Phase C */
+        if (!strncmp(p[0], "MINSP", 5)) {
+                p[0] += 5;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d",f->minsp);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0-5");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 5))
+                                            PARSE_ERROR1;
+                                        f->minsp = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FMINSP=%d\n", par);
+#endif
+                                    }
+                                    break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+
+	/* PHCTO=value - DTE phase C timeout */
+        if (!strncmp(p[0], "PHCTO", 5)){
+            p[0] +=5;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d",f->phcto);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0-255");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 255))
+                                            PARSE_ERROR1;
+                                        f->phcto = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FPHCTO=%d\n", par);
+#endif
+                                    }
+                                    break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+
+#if 0
+	/* PTS=n - Page transfer status       */
+        if (!strncmp(p[0], "PTS", 3)) { /* TODO */
+                p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d",f->pts);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0-5");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 5))
+                                            PARSE_ERROR1;
+                                        f->pts = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FPTS=%d\n", par);
+#endif
+                                    }
+                                    break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+#endif
+
+	/* REL=n - Phase C received EOL alignment */
+        if (!strncmp(p[0], "REL", 3)) {
+                p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d",f->rel);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0,1");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 1))
+                                            PARSE_ERROR1;
+                                        f->rel = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FREL=%d\n", par);
+#endif
+                                    }
+                                    break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+
+	/* REV? - DCE Revision */
+        if (!strncmp(p[0], "REV?", 4)) {
+                p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                printk(KERN_DEBUG "isdn_tty: FREV?\n");
+#endif
+		strcpy(rss, isdn_tty_fax_revision);
+		sprintf(rs, "\r\nRev: %s", isdn_getrev(rss));
+                isdn_tty_at_cout(rs, info);
+                return 0;
+        }
+
+#if 0
+	/* SPL=n - Enable polling */
+        if (!strncmp(p[0], "SPL", 3)) { /* TODO */
+                p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+                                sprintf(rs, "\r\n%d", f->spl);
+                                isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+                                p[0]++;
+                                if (*p[0] == '?')
+                                    {
+                                     p[0]++;
+                                     sprintf(rs, "\r\n0,1");
+                       		     isdn_tty_at_cout(rs, info);
+                                    }
+                                    else
+                                    {
+                                        par = isdn_getnum(p);
+                                        if ((par < 0) || (par > 1))
+                                            PARSE_ERROR1;
+                                        f->spl = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                                        printk(KERN_DEBUG "isdn_tty: Fax FSPL=%d\n", par);
+#endif
+                                    }
+                                    break;
+			default:
+				PARSE_ERROR1;
+		}
+                return 0;
+        }
+#endif
+
+	/* Phase C Transmit Data Block Size */
+	if (!strncmp(p[0], "TBC=", 4)) { /* dummy, not used */
+		p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+                printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]);
+#endif
+                switch (*p[0]) {
+                        case '0':
+                                p[0]++;
+                                break;
+                        default:
+                                PARSE_ERROR1;
+                }
+                return 0;
+	}
+
+        printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]);
+	PARSE_ERROR1;
+}
+
diff -urN linux-2.2.12-pre3/drivers/isdn/isdn_ttyfax.h linux-2.2.12-pre4/drivers/isdn/isdn_ttyfax.h
--- linux-2.2.12-pre3/drivers/isdn/isdn_ttyfax.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.12-pre4/drivers/isdn/isdn_ttyfax.h	2003-08-15 15:06:54.000000000 -0700
@@ -0,0 +1,33 @@
+/* $Id: isdn_ttyfax.h,v 1.1 1999/07/31 12:59:51 armin Exp $
+ * header for Linux ISDN subsystem, tty_fax related functions (linklevel).
+ *
+ * Copyright 1999   by Armin Schindler (mac@melware.de)
+ * Copyright 1999   by Ralf Spachmann (mel@melware.de)
+ * Copyright 1999   by Cytronics & Melware
+ *
+ * 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.
+ *
+ * $Log: isdn_ttyfax.h,v $
+ * Revision 1.1  1999/07/31 12:59:51  armin
+ * Added tty fax capabilities.
+ *
+ *
+ */
+
+
+#define XON	0x11
+#define XOFF	0x13
+#define DC2	0x12
+
diff -urN linux-2.2.12-pre3/drivers/isdn/isdn_tty.h linux-2.2.12-pre4/drivers/isdn/isdn_tty.h
--- linux-2.2.12-pre3/drivers/isdn/isdn_tty.h	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/isdn_tty.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: isdn_tty.h,v 1.14 1999/07/11 17:14:15 armin Exp $
+/* $Id: isdn_tty.h,v 1.15 1999/07/31 12:59:48 armin Exp $
 
  * header for Linux ISDN subsystem, tty related functions (linklevel).
  *
@@ -20,6 +20,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.h,v $
+ * Revision 1.15  1999/07/31 12:59:48  armin
+ * Added tty fax capabilities.
+ *
  * Revision 1.14  1999/07/11 17:14:15  armin
  * Added new layer 2 and 3 protocols for Fax and DSP functions.
  * Moved "Add CPN to RING message" to new register S23,
@@ -157,3 +160,9 @@
 extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *);
 extern int isdn_tty_capi_facility(capi_msg *cm); 
 extern void isdn_tty_at_cout(char *, modem_info *);
+extern void isdn_tty_modem_hup(modem_info *, int);
+#ifdef CONFIG_ISDN_TTY_FAX
+extern int isdn_tty_cmd_PLUSF_FAX(char **, modem_info *);
+extern int isdn_tty_fax_command(modem_info *);
+extern void isdn_tty_fax_bitorder(modem_info *, struct sk_buff *);
+#endif
diff -urN linux-2.2.12-pre3/drivers/isdn/Makefile linux-2.2.12-pre4/drivers/isdn/Makefile
--- linux-2.2.12-pre3/drivers/isdn/Makefile	1999-08-09 12:04:39.000000000 -0700
+++ linux-2.2.12-pre4/drivers/isdn/Makefile	2003-08-15 15:06:54.000000000 -0700
@@ -24,6 +24,9 @@
   endif
   ifdef CONFIG_ISDN_AUDIO
     L_OBJS += isdn_audio.o
+    ifdef CONFIG_ISDN_TTY_FAX
+      L_OBJS += isdn_ttyfax.o
+    endif
   endif
 else
   ifeq ($(CONFIG_ISDN),m)
@@ -41,6 +44,9 @@
     endif  
     ifdef CONFIG_ISDN_AUDIO
       O_OBJS += isdn_audio.o
+      ifdef CONFIG_ISDN_TTY_FAX
+        O_OBJS += isdn_ttyfax.o
+      endif
     endif
   endif
 endif
diff -urN linux-2.2.12-pre3/drivers/net/sis900.c linux-2.2.12-pre4/drivers/net/sis900.c
--- linux-2.2.12-pre3/drivers/net/sis900.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/drivers/net/sis900.c	2003-08-15 15:06:54.000000000 -0700
@@ -558,6 +558,9 @@
                 printk(KERN_INFO "%s", version);
 
         dev = init_etherdev(dev, 0);
+        
+        if(dev==NULL)
+        	return NULL;
 
         printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ",
                    dev->name, pci_tbl[chip_idx].name, ioaddr, irq);
@@ -580,6 +583,11 @@
 
         /* Some data structures must be quadword aligned. */
         tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA);
+        if(tp==NULL)
+        {
+        	free_region(ioaddr, pci_tbl[chip_idx].io_size);
+        	return NULL;
+        }
         memset(tp, 0, sizeof(*tp));
         dev->priv = tp;
 
diff -urN linux-2.2.12-pre3/drivers/sound/Config.in linux-2.2.12-pre4/drivers/sound/Config.in
--- linux-2.2.12-pre3/drivers/sound/Config.in	1999-03-07 15:22:06.000000000 -0800
+++ linux-2.2.12-pre4/drivers/sound/Config.in	2003-08-15 15:06:54.000000000 -0700
@@ -9,6 +9,9 @@
 
 # Prompt user for primary drivers.
 
+if [ "$CONFIG_VISWS" = "y" ]; then
+    dep_tristate 'SGI Visual Workstation Sound' CONFIG_SOUND_VWSND $CONFIG_SOUND
+fi
 dep_tristate 'Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ES1370 $CONFIG_SOUND
 if [ "$CONFIG_SOUND_ES1370" = "y" ]; then
    bool 'Joystick support at boot time' CONFIG_SOUND_ES1370_JOYPORT_BOOT
diff -urN linux-2.2.12-pre3/drivers/sound/Makefile linux-2.2.12-pre4/drivers/sound/Makefile
--- linux-2.2.12-pre3/drivers/sound/Makefile	1999-01-14 22:59:47.000000000 -0800
+++ linux-2.2.12-pre4/drivers/sound/Makefile	2003-08-15 15:06:54.000000000 -0700
@@ -72,6 +72,7 @@
 obj-$(CONFIG_SOUND_VMIDI)	+= v_midi.o
 obj-$(CONFIG_SOUND_YM3812)	+= adlib_card.o opl3.o
 obj-$(CONFIG_SOUND_VIDC)	+= vidc_mod.o
+obj-$(CONFIG_SOUND_VWSND)	+= vwsnd.o
 obj-$(CONFIG_SOUND_WAVEARTIST)	+= waveartist.o
 obj-$(CONFIG_SOUND_WAVEFRONT)   += wavefront.o
 
diff -urN linux-2.2.12-pre3/drivers/sound/sound_core.c linux-2.2.12-pre4/drivers/sound/sound_core.c
--- linux-2.2.12-pre3/drivers/sound/sound_core.c	1999-06-07 11:06:06.000000000 -0700
+++ linux-2.2.12-pre4/drivers/sound/sound_core.c	2003-08-15 15:06:54.000000000 -0700
@@ -395,6 +395,9 @@
 	/*
 	 *	Now init non OSS drivers
 	 */
+#ifdef CONFIG_SOUND_VWSND
+	init_vwsnd();
+#endif
 #ifdef CONFIG_SOUND_SONICVIBES
 	init_sonicvibes();
 #endif
diff -urN linux-2.2.12-pre3/drivers/sound/vwsnd.c linux-2.2.12-pre4/drivers/sound/vwsnd.c
--- linux-2.2.12-pre3/drivers/sound/vwsnd.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.12-pre4/drivers/sound/vwsnd.c	2003-08-15 15:06:54.000000000 -0700
@@ -0,0 +1,3525 @@
+/*
+ * Sound driver for Silicon Graphics 320 and 540 Visual Workstations'
+ * onboard audio.  See notes in ../../Documentation/sound/vwsnd .
+ *
+ * Copyright 1999 Silicon Graphics, Inc.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#undef VWSND_DEBUG			/* define for debugging */
+
+/*
+ * XXX to do -
+ *
+ *	External sync.
+ *	Rename swbuf, hwbuf, u&i, hwptr&swptr to something rational.
+ *	Bug - if select() called before read(), pcm_setup() not called.
+ *	Bug - output doesn't stop soon enough if process killed.
+ */
+
+/*
+ * Things to test -
+ *
+ *	Will readv/writev work?  Write a test.
+ *
+ *	insmod/rmmod 100 million times.
+ *
+ *	Run I/O until int ptrs wrap around (roughly 6.2 hours @ DAT
+ *	rate).
+ *
+ *	Concurrent threads banging on mixer simultaneously, both UP
+ *	and SMP kernels.  Especially, watch for thread A changing
+ *	OUTSRC while thread B changes gain -- both write to the same
+ *	ad1843 register.
+ *
+ *	What happens if a client opens /dev/audio then forks?
+ *	Do two procs have /dev/audio open?  Test.
+ *
+ *	Pump audio through the CD, MIC and line inputs and verify that
+ *	they mix/mute into the output.
+ *
+ *	Apps:
+ *		amp
+ *		mpg123
+ *		x11amp
+ *		mxv
+ *		kmedia
+ *		esound
+ *		need more input apps
+ *
+ *	Run tests while bombarding with signals.  setitimer(2) will do it...  */
+
+/*
+ * This driver is organized in nine sections.
+ * The nine sections are:
+ *
+ *	debug stuff
+ * 	low level lithium access
+ *	high level lithium access
+ *	AD1843 access
+ *	PCM I/O
+ *	audio driver
+ *	mixer driver
+ *	probe/attach/unload
+ *	initialization and loadable kernel module interface
+ *
+ * That is roughly the order of increasing abstraction, so forward
+ * dependencies are minimal.
+ */
+
+/*
+ * Locking Notes
+ *
+ *	INC_USE_COUNT and DEC_USE_COUNT keep track of the number of
+ *	open descriptors to this driver.  When the driver is compiled
+ *	as a module, they call MOD_{INC,DEC}_USE_COUNT; otherwise they
+ *	bump vwsnd_use_count.  The global device list, vwsnd_dev_list,
+ *	is immutable when the IN_USE is true.
+ *
+ *	devc->open_lock is a semaphore that is used to enforce the
+ *	single reader/single writer rule for /dev/audio.  The rule is
+ *	that each device may have at most one reader and one writer.
+ *	Open will block until the previous client has closed the
+ *	device, unless O_NONBLOCK is specified.
+ *
+ *	The semaphore devc->io_sema serializes PCM I/O syscalls.  This
+ *	is unnecessary in Linux 2.2, because the kernel lock
+ *	serializes read, write, and ioctl globally, but it's there,
+ *	ready for the brave, new post-kernel-lock world.
+ *
+ *	Locking between interrupt and baselevel is handled by the
+ *	"lock" spinlock in vwsnd_port (one lock each for read and
+ *	write).  Each half holds the lock just long enough to see what
+ *	area it owns and update its pointers.  See pcm_output() and
+ *	pcm_input() for most of the gory stuff.
+ *
+ *	devc->mix_sema serializes all mixer ioctls.  This is also
+ *	redundant because of the kernel lock.
+ *
+ *	The lowest level lock is lith->lithium_lock.  It is a
+ *	spinlock which is held during the two-register tango of
+ *	reading/writing an AD1843 register.  See
+ *	li_{read,write}_ad1843_reg().
+ */
+
+/*
+ * Sample Format Notes
+ *
+ *	Lithium's DMA engine has two formats: 16-bit 2's complement
+ *	and 8-bit unsigned .  16-bit transfers the data unmodified, 2
+ *	bytes per sample.  8-bit unsigned transfers 1 byte per sample
+ *	and XORs each byte with 0x80.  Lithium can input or output
+ *	either mono or stereo in either format.
+ *
+ *	The AD1843 has four formats: 16-bit 2's complement, 8-bit
+ *	unsigned, 8-bit mu-Law and 8-bit A-Law.
+ *
+ *	This driver supports five formats: AFMT_S8, AFMT_U8,
+ *	AFMT_MU_LAW, AFMT_A_LAW, and AFMT_S16_LE.
+ *
+ *	For AFMT_U8 output, we keep the AD1843 in 16-bit mode, and
+ *	rely on Lithium's XOR to translate between U8 and S8.
+ *
+ *	For AFMT_S8, AFMT_MU_LAW and AFMT_A_LAW output, we have to XOR
+ *	the 0x80 bit in software to compensate for Lithium's XOR.
+ *	This happens in pcm_copy_{in,out}().
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <asm/fixmap.h>
+#include <asm/sgi-cobalt.h>
+#include <asm/spinlock.h>
+
+#include "sound_config.h"
+
+/*****************************************************************************/
+/* debug stuff */
+
+#ifdef VWSND_DEBUG
+
+#include <linux/interrupt.h>		/* for in_interrupt() */
+
+static int shut_up = 1;
+
+/*
+ * dbgassert - called when an assertion fails.
+ */
+
+static void dbgassert(const char *fcn, int line, const char *expr)
+{
+	if (in_interrupt())
+		panic("ASSERTION FAILED IN INTERRUPT, %s:%s:%d %s\n",
+		      __FILE__, fcn, line, expr);
+	else {
+		int x;
+		printk(KERN_ERR "ASSERTION FAILED, %s:%s:%d %s\n",
+		       __FILE__, fcn, line, expr);
+		x = * (volatile int *) 0; /* force proc to exit */
+	}
+}
+
+/*
+ * Bunch of useful debug macros:
+ *
+ *	ASSERT	- print unless e nonzero (panic if in interrupt)
+ *	DBGDO	- include arbitrary code if debugging
+ *	DBGX	- debug print raw (w/o function name)
+ *	DBGP	- debug print w/ function name
+ *	DBGE	- debug print function entry
+ *	DBGC	- debug print function call
+ *	DBGR	- debug print function return
+ *	DBGXV	- debug print raw when verbose
+ *	DBGPV	- debug print when verbose
+ *	DBGEV	- debug print function entry when verbose
+ *	DBGRV	- debug print function return when verbose
+ */
+
+#define ASSERT(e)      ((e) ? (void) 0 : dbgassert(__FUNCTION__, __LINE__, #e))
+#define DBGDO(x)            x
+#define DBGX(fmt, args...)  (in_interrupt() ? 0 : printk(KERN_ERR fmt, ##args))
+#define DBGP(fmt, args...)  (DBGX(__FUNCTION__ ": " fmt, ##args))
+#define DBGE(fmt, args...)  (DBGX(__FUNCTION__ fmt, ##args))
+#define DBGC(rtn)           (DBGP("calling %s\n", rtn))
+#define DBGR()              (DBGP("returning\n"))
+#define DBGXV(fmt, args...) (shut_up ? 0 : DBGX(fmt, ##args))
+#define DBGPV(fmt, args...) (shut_up ? 0 : DBGP(fmt, ##args))
+#define DBGEV(fmt, args...) (shut_up ? 0 : DBGE(fmt, ##args))
+#define DBGCV(rtn)          (shut_up ? 0 : DBGC(rtn))
+#define DBGRV()             (shut_up ? 0 : DBGR())
+
+#else /* !VWSND_DEBUG */
+
+#define ASSERT(e)           ((void) 0)
+#define DBGDO(x)            /* don't */
+#define DBGX(fmt, args...)  ((void) 0)
+#define DBGP(fmt, args...)  ((void) 0)
+#define DBGE(fmt, args...)  ((void) 0)
+#define DBGC(rtn)           ((void) 0)
+#define DBGR()              ((void) 0)
+#define DBGPV(fmt, args...) ((void) 0)
+#define DBGXV(fmt, args...) ((void) 0)
+#define DBGEV(fmt, args...) ((void) 0)
+#define DBGCV(rtn)          ((void) 0)
+#define DBGRV()             ((void) 0)
+
+#endif /* !VWSND_DEBUG */
+
+/*****************************************************************************/
+/* low level lithium access */
+
+/*
+ * We need to talk to Lithium registers on three pages.  Here are
+ * the pages' offsets from the base address (0xFF001000).
+ */
+
+enum {
+	LI_PAGE0_OFFSET = 0x01000 - 0x1000, /* FF001000 */
+	LI_PAGE1_OFFSET = 0x0F000 - 0x1000, /* FF00F000 */
+	LI_PAGE2_OFFSET = 0x10000 - 0x1000, /* FF010000 */
+};
+
+/* low-level lithium data */
+
+typedef struct lithium {
+	caddr_t		page0;		/* virtual addresses */
+	caddr_t		page1;
+	caddr_t		page2;
+	spinlock_t	lock;		/* protects codec and UST/MSC access */
+} lithium_t;
+
+/*
+ * li_create initializes the lithium_t structure and sets up vm mappings
+ * to access the registers.
+ * Returns 0 on success, -errno on failure.
+ */
+
+static int li_create(lithium_t *lith, unsigned long baseaddr)
+{
+	static void li_destroy(lithium_t *);
+
+	lith->lock = SPIN_LOCK_UNLOCKED;
+	lith->page0 = ioremap_nocache(baseaddr + LI_PAGE0_OFFSET, PAGE_SIZE);
+	lith->page1 = ioremap_nocache(baseaddr + LI_PAGE1_OFFSET, PAGE_SIZE);
+	lith->page2 = ioremap_nocache(baseaddr + LI_PAGE2_OFFSET, PAGE_SIZE);
+	if (!lith->page0 || !lith->page1 || !lith->page2) {
+		li_destroy(lith);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+/*
+ * li_destroy destroys the lithium_t structure and vm mappings.
+ */
+
+static void li_destroy(lithium_t *lith)
+{
+	if (lith->page0) {
+		iounmap(lith->page0);
+		lith->page0 = NULL;
+	}
+	if (lith->page1) {
+		iounmap(lith->page1);
+		lith->page1 = NULL;
+	}
+	if (lith->page2) {
+		iounmap(lith->page2);
+		lith->page2 = NULL;
+	}
+}
+
+/*
+ * basic register accessors - read/write long/byte
+ */
+
+static __inline__ unsigned long li_readl(lithium_t *lith, int off)
+{
+	return * (volatile unsigned long *) (lith->page0 + off);
+}
+
+static __inline__ unsigned char li_readb(lithium_t *lith, int off)
+{
+	return * (volatile unsigned char *) (lith->page0 + off);
+}
+
+static __inline__ void li_writel(lithium_t *lith, int off, unsigned long val)
+{
+	* (volatile unsigned long *) (lith->page0 + off) = val;
+}
+
+static __inline__ void li_writeb(lithium_t *lith, int off, unsigned char val)
+{
+	* (volatile unsigned char *) (lith->page0 + off) = val;
+}
+
+/*****************************************************************************/
+/* High Level Lithium Access */
+
+/*
+ * Lithium DMA Notes
+ *
+ * Lithium has two dedicated DMA channels for audio.  They are known
+ * as comm1 and comm2 (communication areas 1 and 2).  Comm1 is for
+ * input, and comm2 is for output.  Each is controlled by three
+ * registers: BASE (base address), CFG (config) and CCTL
+ * (config/control).
+ *
+ * Each DMA channel points to a physically contiguous ring buffer in
+ * main memory of up to 8 Kbytes.  (This driver always uses 8 Kb.)
+ * There are three pointers into the ring buffer: read, write, and
+ * trigger.  The pointers are 8 bits each.  Each pointer points to
+ * 32-byte "chunks" of data.  The DMA engine moves 32 bytes at a time,
+ * so there is no finer-granularity control.
+ *
+ * In comm1, the hardware updates the write ptr, and software updates
+ * the read ptr.  In comm2, it's the opposite: hardware updates the
+ * read ptr, and software updates the write ptr.  I designate the
+ * hardware-updated ptr as the hwptr, and the software-updated ptr as
+ * the swptr.
+ *
+ * The trigger ptr and trigger mask are used to trigger interrupts.
+ * From the Lithium spec, section 5.6.8, revision of 12/15/1998:
+ *
+ *	Trigger Mask Value
+ *
+ *	A three bit wide field that represents a power of two mask
+ *	that is used whenever the trigger pointer is compared to its
+ *	respective read or write pointer.  A value of zero here
+ *	implies a mask of 0xFF and a value of seven implies a mask
+ *	0x01.  This value can be used to sub-divide the ring buffer
+ *	into pie sections so that interrupts monitor the progress of
+ *	hardware from section to section.
+ *
+ * My interpretation of that is, whenever the hw ptr is updated, it is
+ * compared with the trigger ptr, and the result is masked by the
+ * trigger mask.  (Actually, by the complement of the trigger mask.)
+ * If the result is zero, an interrupt is triggered.  I.e., interrupt
+ * if ((hwptr & ~mask) == (trptr & ~mask)).  The mask is formed from
+ * the trigger register value as mask = (1 << (8 - tmreg)) - 1.
+ *
+ * In yet different words, setting tmreg to 0 causes an interrupt after
+ * every 256 DMA chunks (8192 bytes) or once per traversal of the
+ * ring buffer.  Setting it to 7 caues an interrupt every 2 DMA chunks
+ * (64 bytes) or 128 times per traversal of the ring buffer.
+ */
+
+/* Lithium register offsets and bit definitions */
+
+#define LI_HOST_CONTROLLER	0x000
+# define LI_HC_RESET		 0x00008000
+# define LI_HC_LINK_ENABLE	 0x00004000
+# define LI_HC_LINK_FAILURE	 0x00000004
+# define LI_HC_LINK_CODEC	 0x00000002
+# define LI_HC_LINK_READY	 0x00000001
+
+#define LI_INTR_STATUS		0x010
+#define LI_INTR_MASK		0x014
+# define LI_INTR_LINK_ERR	 0x00008000
+# define LI_INTR_COMM2_TRIG	 0x00000008
+# define LI_INTR_COMM2_UNDERFLOW 0x00000004
+# define LI_INTR_COMM1_TRIG	 0x00000002
+# define LI_INTR_COMM1_OVERFLOW  0x00000001
+
+#define LI_CODEC_COMMAND	0x018
+# define LI_CC_BUSY		 0x00008000
+# define LI_CC_DIR		 0x00000080
+#  define LI_CC_DIR_RD		  LI_CC_DIR
+#  define LI_CC_DIR_WR		(!LI_CC_DIR)
+# define LI_CC_ADDR_MASK	 0x0000007F
+
+#define LI_CODEC_DATA		0x01C
+
+#define LI_COMM1_BASE		0x100
+#define LI_COMM1_CTL		0x104
+# define LI_CCTL_RESET		 0x80000000
+# define LI_CCTL_SIZE		 0x70000000
+# define LI_CCTL_DMA_ENABLE	 0x08000000
+# define LI_CCTL_TMASK		 0x07000000 /* trigger mask */
+# define LI_CCTL_TPTR		 0x00FF0000 /* trigger pointer */
+# define LI_CCTL_RPTR		 0x0000FF00
+# define LI_CCTL_WPTR		 0x000000FF
+#define LI_COMM1_CFG		0x108
+# define LI_CCFG_LOCK		 0x00008000
+# define LI_CCFG_SLOT		 0x00000070
+# define LI_CCFG_DIRECTION	 0x00000008
+#  define LI_CCFG_DIR_IN	(!LI_CCFG_DIRECTION)
+#  define LI_CCFG_DIR_OUT	  LI_CCFG_DIRECTION
+# define LI_CCFG_MODE		 0x00000004
+#  define LI_CCFG_MODE_MONO	(!LI_CCFG_MODE)
+#  define LI_CCFG_MODE_STEREO	  LI_CCFG_MODE
+# define LI_CCFG_FORMAT		 0x00000003
+#  define LI_CCFG_FMT_8BIT	  0x00000000
+#  define LI_CCFG_FMT_16BIT	  0x00000001
+#define LI_COMM2_BASE		0x10C
+#define LI_COMM2_CTL		0x110
+ /* bit definitions are the same as LI_COMM1_CTL */
+#define LI_COMM2_CFG		0x114
+ /* bit definitions are the same as LI_COMM1_CFG */
+
+#define LI_UST_LOW		0x200	/* 64-bit Unadjusted System Time is */
+#define LI_UST_HIGH		0x204	/* microseconds since boot */
+
+#define LI_AUDIO1_UST		0x300	/* UST-MSC pairs */
+#define LI_AUDIO1_MSC		0x304	/* MSC (Media Stream Counter) */
+#define LI_AUDIO2_UST		0x308	/* counts samples actually */
+#define LI_AUDIO2_MSC		0x30C	/* processed as of time UST */
+
+/* 
+ * Lithium's DMA engine operates on chunks of 32 bytes.  We call that
+ * a DMACHUNK.
+ */
+
+#define DMACHUNK_SHIFT 5
+#define DMACHUNK_SIZE (1 << DMACHUNK_SHIFT)
+#define BYTES_TO_CHUNKS(bytes) ((bytes) >> DMACHUNK_SHIFT)
+#define CHUNKS_TO_BYTES(chunks) ((chunks) << DMACHUNK_SHIFT)
+
+/*
+ * Two convenient macros to shift bitfields into/out of position.
+ *
+ * Observe that (mask & -mask) is (1 << low_set_bit_of(mask)).
+ * As long as mask is constant, we trust the compiler will change the
+ * multipy and divide into shifts.
+ */
+
+#define SHIFT_FIELD(val, mask) (((val) * ((mask) & -(mask))) & (mask))
+#define UNSHIFT_FIELD(val, mask) (((val) & (mask)) / ((mask) & -(mask)))
+
+/*
+ * dma_chan_desc is invariant information about a Lithium
+ * DMA channel.  There are two instances, li_comm1 and li_comm2.
+ *
+ * Note that the CCTL register fields are write ptr and read ptr, but what
+ * we care about are which pointer is updated by software and which by
+ * hardware.
+ */
+
+typedef struct dma_chan_desc {
+	int basereg;
+	int cfgreg;
+	int ctlreg;
+	int hwptrreg;
+	int swptrreg;
+	int ustreg;
+	int mscreg;
+	unsigned long swptrmask;
+	int ad1843_slot;
+	int direction;			/* LI_CCTL_DIR_IN/OUT */
+} dma_chan_desc_t;
+
+static const dma_chan_desc_t li_comm1 = {
+	LI_COMM1_BASE,			/* base register offset */
+	LI_COMM1_CFG,			/* config register offset */
+	LI_COMM1_CTL,			/* control register offset */
+	LI_COMM1_CTL + 0,		/* hw ptr reg offset (write ptr) */
+	LI_COMM1_CTL + 1,		/* sw ptr reg offset (read ptr) */
+	LI_AUDIO1_UST,			/* ust reg offset */
+	LI_AUDIO1_MSC,			/* msc reg offset */
+	LI_CCTL_RPTR,			/* sw ptr bitmask in ctlval */
+	2,				/* ad1843 serial slot */
+	LI_CCFG_DIR_IN			/* direction */
+};
+
+static const dma_chan_desc_t li_comm2 = {
+	LI_COMM2_BASE,			/* base register offset */
+	LI_COMM2_CFG,			/* config register offset */
+	LI_COMM2_CTL,			/* control register offset */
+	LI_COMM2_CTL + 1,		/* hw ptr reg offset (read ptr) */
+	LI_COMM2_CTL + 0,		/* sw ptr reg offset (writr ptr) */
+	LI_AUDIO2_UST,			/* ust reg offset */
+	LI_AUDIO2_MSC,			/* msc reg offset */
+	LI_CCTL_WPTR,			/* sw ptr bitmask in ctlval */
+	2,				/* ad1843 serial slot */
+	LI_CCFG_DIR_OUT			/* direction */
+};
+
+/*
+ * dma_chan is variable information about a Lithium DMA channel.
+ *
+ * The desc field points to invariant information.
+ * The lith field points to a lithium_t which is passed
+ * to li_read* and li_write* to access the registers.
+ * The *val fields shadow the lithium registers' contents.
+ */
+
+typedef struct dma_chan {
+	const dma_chan_desc_t *desc;
+	lithium_t      *lith;
+	unsigned long   baseval;
+	unsigned long	cfgval;
+	unsigned long	ctlval;
+} dma_chan_t;
+
+/*
+ * ustmsc is a UST/MSC pair (Unadjusted System Time/Media Stream Counter).
+ * UST is time in microseconds since the system booted, and MSC is a
+ * counter that increments with every audio sample.
+ */
+
+typedef struct ustmsc {
+	unsigned long long ust;
+	unsigned long msc;
+} ustmsc_t;
+
+/*
+ * li_ad1843_wait waits until lithium says the AD1843 register
+ * exchange is not busy.  Returns 0 on success, -EBUSY on timeout.
+ *
+ * Locking: must be called with lithium_lock held.
+ */
+
+static int li_ad1843_wait(lithium_t *lith)
+{
+	unsigned long later = jiffies + 2;
+	while (li_readl(lith, LI_CODEC_COMMAND) & LI_CC_BUSY)
+		if (jiffies >= later)
+			return -EBUSY;
+	return 0;
+}
+
+/*
+ * li_read_ad1843_reg returns the current contents of a 16 bit AD1843 register.
+ *
+ * Returns unsigned register value on success, -errno on failure.
+ */
+
+static int li_read_ad1843_reg(lithium_t *lith, int reg)
+{
+	int val;
+
+	ASSERT(!in_interrupt());
+	spin_lock(&lith->lock);
+	{
+		val = li_ad1843_wait(lith);
+		if (val == 0) {
+			li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_RD | reg);
+			val = li_ad1843_wait(lith);
+		}
+		if (val == 0)
+			val = li_readl(lith, LI_CODEC_DATA);
+	}
+	spin_unlock(&lith->lock);
+
+	DBGXV("li_read_ad1843_reg(lith=0x%p, reg=%d) returns 0x%04x\n",
+	      lith, reg, val);
+
+	return val;
+}
+
+/*
+ * li_write_ad1843_reg writes the specified value to a 16 bit AD1843 register.
+ */
+
+static void li_write_ad1843_reg(lithium_t *lith, int reg, int newval)
+{
+	spin_lock(&lith->lock);
+	{
+		if (li_ad1843_wait(lith) == 0) {
+			li_writel(lith, LI_CODEC_DATA, newval);
+			li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_WR | reg);
+		}
+	}
+	spin_unlock(&lith->lock);
+}
+
+/*
+ * li_setup_dma calculates all the register settings for DMA in a particular
+ * mode.  It takes too many arguments.
+ */
+
+static void li_setup_dma(dma_chan_t *chan,
+			 const dma_chan_desc_t *desc,
+			 lithium_t *lith,
+			 unsigned long buffer_paddr,
+			 int bufshift,
+			 int fragshift,
+			 int channels,
+			 int sampsize)
+{
+	unsigned long mode, format;
+	unsigned long size, tmask;
+
+	DBGEV("(chan=0x%p, desc=0x%p, lith=0x%p, buffer_paddr=0x%lx, "
+	     "bufshift=%d, fragshift=%d, channels=%d, sampsize=%d)\n",
+	     chan, desc, lith, buffer_paddr,
+	     bufshift, fragshift, channels, sampsize);
+
+	/* Reset the channel first. */
+
+	li_writel(lith, desc->ctlreg, LI_CCTL_RESET);
+
+	ASSERT(channels == 1 || channels == 2);
+	if (channels == 2)
+		mode = LI_CCFG_MODE_STEREO;
+	else
+		mode = LI_CCFG_MODE_MONO;
+	ASSERT(sampsize == 1 || sampsize == 2);
+	if (sampsize == 2)
+		format = LI_CCFG_FMT_16BIT;
+	else
+		format = LI_CCFG_FMT_8BIT;
+	chan->desc = desc;
+	chan->lith = lith;
+
+	/*
+	 * Lithium DMA address register takes a 40-bit physical
+	 * address, right-shifted by 8 so it fits in 32 bits.  Bit 37
+	 * must be set -- it enables cache coherence.
+	 */
+
+	ASSERT(!(buffer_paddr & 0xFF));
+	chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8);
+
+	chan->cfgval = (!LI_CCFG_LOCK |
+			SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) |
+			desc->direction |
+			mode |
+			format);
+
+	size = bufshift - 6;
+	tmask = 13 - fragshift;		/* See Lithium DMA Notes above. */
+	ASSERT(size >= 2 && size <= 7);
+	ASSERT(tmask >= 1 && tmask <= 7);
+	chan->ctlval = (!LI_CCTL_RESET |
+			SHIFT_FIELD(size, LI_CCTL_SIZE) |
+			!LI_CCTL_DMA_ENABLE |
+			SHIFT_FIELD(tmask, LI_CCTL_TMASK) |
+			SHIFT_FIELD(0, LI_CCTL_TPTR));
+
+	DBGPV("basereg 0x%x = 0x%lx\n", desc->basereg, chan->baseval);
+	DBGPV("cfgreg 0x%x = 0x%lx\n", desc->cfgreg, chan->cfgval);
+	DBGPV("ctlreg 0x%x = 0x%lx\n", desc->ctlreg, chan->ctlval);
+
+	li_writel(lith, desc->basereg, chan->baseval);
+	li_writel(lith, desc->cfgreg, chan->cfgval);
+	li_writel(lith, desc->ctlreg, chan->ctlval);
+
+	DBGRV();
+}
+
+static void li_shutdown_dma(dma_chan_t *chan)
+{
+	lithium_t *lith = chan->lith;
+	caddr_t lith1 = lith->page1;
+
+	DBGEV("(chan=0x%p)\n", chan);
+	
+	chan->ctlval &= ~LI_CCTL_DMA_ENABLE;
+	DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval);
+	li_writel(lith, chan->desc->ctlreg, chan->ctlval);
+
+	/*
+	 * Offset 0x500 on Lithium page 1 is an undocumented,
+	 * unsupported register that holds the zero sample value.
+	 * Lithium is supposed to output zero samples when DMA is
+	 * inactive, and repeat the last sample when DMA underflows.
+	 * But it has a bug, where, after underflow occurs, the zero
+	 * sample is not reset.
+	 *
+	 * I expect this to break in a future rev of Lithium.
+	 */
+
+	if (lith1 && chan->desc->direction == LI_CCFG_DIR_OUT)
+		* (volatile unsigned long *) (lith1 + 0x500) = 0;
+}
+
+/*
+ * li_activate_dma always starts dma at the beginning of the buffer.
+ *
+ * N.B., these may be called from interrupt.
+ */
+
+static __inline__ void li_activate_dma(dma_chan_t *chan)
+{
+	chan->ctlval |= LI_CCTL_DMA_ENABLE;
+	DBGPV("ctlval = 0x%lx\n", chan->ctlval);
+	li_writel(chan->lith, chan->desc->ctlreg, chan->ctlval);
+}
+
+static void li_deactivate_dma(dma_chan_t *chan)
+{
+	lithium_t *lith = chan->lith;
+	caddr_t lith2 = lith->page2;
+
+	chan->ctlval &= ~(LI_CCTL_DMA_ENABLE | LI_CCTL_RPTR | LI_CCTL_WPTR);
+	DBGPV("ctlval = 0x%lx\n", chan->ctlval);
+	DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval);
+	li_writel(lith, chan->desc->ctlreg, chan->ctlval);
+
+	/*
+	 * Offsets 0x98 and 0x9C on Lithium page 2 are undocumented,
+	 * unsupported registers that are internal copies of the DMA
+	 * read and write pointers.  Because of a Lithium bug, these
+	 * registers aren't zeroed correctly when DMA is shut off.  So
+	 * we whack them directly.
+	 *
+	 * I expect this to break in a future rev of Lithium.
+	 */
+
+	if (lith2 && chan->desc->direction == LI_CCFG_DIR_OUT) {
+		* (volatile unsigned long *) (lith2 + 0x98) = 0;
+		* (volatile unsigned long *) (lith2 + 0x9C) = 0;
+	}
+}
+
+/*
+ * read/write the ring buffer pointers.  These routines' arguments and results
+ * are byte offsets from the beginning of the ring buffer.
+ */
+
+static __inline__ int li_read_swptr(dma_chan_t *chan)
+{
+	const unsigned long mask = chan->desc->swptrmask;
+
+	return CHUNKS_TO_BYTES(UNSHIFT_FIELD(chan->ctlval, mask));
+}
+
+static __inline__ int li_read_hwptr(dma_chan_t *chan)
+{
+	return CHUNKS_TO_BYTES(li_readb(chan->lith, chan->desc->hwptrreg));
+}
+
+static __inline__ void li_write_swptr(dma_chan_t *chan, int val)
+{
+	const unsigned long mask = chan->desc->swptrmask;
+
+	ASSERT(!(val & ~CHUNKS_TO_BYTES(0xFF)));
+	val = BYTES_TO_CHUNKS(val);
+	chan->ctlval = (chan->ctlval & ~mask) | SHIFT_FIELD(val, mask);
+	li_writeb(chan->lith, chan->desc->swptrreg, val);
+}
+
+/* li_read_USTMSC() returns a UST/MSC pair for the given channel. */
+
+static void li_read_USTMSC(dma_chan_t *chan, ustmsc_t *ustmsc)
+{
+	lithium_t *lith = chan->lith;
+	const dma_chan_desc_t *desc = chan->desc;
+	unsigned long now_low, now_high0, now_high1, chan_ust;
+
+	spin_lock(&lith->lock);
+	{
+		/*
+		 * retry until we do all five reads without the
+		 * high word changing.  (High word increments
+		 * every 2^32 microseconds, i.e., not often)
+		 */
+		do {
+			now_high0 = li_readl(lith, LI_UST_HIGH);
+			now_low = li_readl(lith, LI_UST_LOW);
+
+			/*
+			 * Lithium guarantees these two reads will be
+			 * atomic -- ust will not increment after msc
+			 * is read.
+			 */
+
+			ustmsc->msc = li_readl(lith, desc->mscreg);
+			chan_ust = li_readl(lith, desc->ustreg);
+
+			now_high1 = li_readl(lith, LI_UST_HIGH);
+		} while (now_high0 != now_high1);
+	}	
+	spin_unlock(&lith->lock);
+	ustmsc->ust = ((unsigned long long) now_high0 << 32 | chan_ust);
+}
+
+static void li_enable_interrupts(lithium_t *lith, unsigned int mask)
+{
+	DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask);
+
+	/* clear any already-pending interrupts. */
+
+	li_writel(lith, LI_INTR_STATUS, mask);
+
+	/* enable the interrupts. */
+
+	mask |= li_readl(lith, LI_INTR_MASK);
+	li_writel(lith, LI_INTR_MASK, mask);
+}
+
+static void li_disable_interrupts(lithium_t *lith, unsigned int mask)
+{
+	unsigned int keepmask;
+
+	DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask);
+
+	/* disable the interrupts */
+
+	keepmask = li_readl(lith, LI_INTR_MASK) & ~mask;
+	li_writel(lith, LI_INTR_MASK, keepmask);
+
+	/* clear any pending interrupts. */
+
+	li_writel(lith, LI_INTR_STATUS, mask);
+}
+
+/* Get the interrupt status and clear all pending interrupts. */
+
+static unsigned int li_get_clear_intr_status(lithium_t *lith)
+{
+	unsigned int status;
+
+	status = li_readl(lith, LI_INTR_STATUS);
+	li_writel(lith, LI_INTR_STATUS, ~0);
+	return status & li_readl(lith, LI_INTR_MASK);
+}
+
+static int li_init(lithium_t *lith)
+{
+	/* 1. System power supplies stabilize. */
+
+	/* 2. Assert the ~RESET signal. */
+
+	li_writel(lith, LI_HOST_CONTROLLER, LI_HC_RESET);
+	udelay(1);
+
+	/* 3. Deassert the ~RESET signal and enter a wait period to allow
+	   the AD1843 internal clocks and the external crystal oscillator
+	   to stabilize. */
+
+	li_writel(lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE);
+	udelay(1);
+
+	return 0;
+}
+
+/*****************************************************************************/
+/* AD1843 access */
+
+/*
+ * AD1843 bitfield definitions.  All are named as in the AD1843 data
+ * sheet, with ad1843_ prepended and individual bit numbers removed.
+ *
+ * E.g., bits LSS0 through LSS2 become ad1843_LSS.
+ *
+ * Only the bitfields we need are defined.
+ */
+
+typedef struct ad1843_bitfield {
+	char reg;
+	char lo_bit;
+	char nbits;
+} ad1843_bitfield_t;
+
+static const ad1843_bitfield_t
+	ad1843_PDNO   = {  0, 14,  1 },	/* Converter Power-Down Flag */
+	ad1843_INIT   = {  0, 15,  1 },	/* Clock Initialization Flag */
+	ad1843_RIG    = {  2,  0,  4 },	/* Right ADC Input Gain */
+	ad1843_RMGE   = {  2,  4,  1 },	/* Right ADC Mic Gain Enable */
+	ad1843_RSS    = {  2,  5,  3 },	/* Right ADC Source Select */
+	ad1843_LIG    = {  2,  8,  4 },	/* Left ADC Input Gain */
+	ad1843_LMGE   = {  2, 12,  1 },	/* Left ADC Mic Gain Enable */
+	ad1843_LSS    = {  2, 13,  3 },	/* Left ADC Source Select */
+	ad1843_RX1M   = {  4,  0,  5 },	/* Right Aux 1 Mix Gain/Atten */
+	ad1843_RX1MM  = {  4,  7,  1 },	/* Right Aux 1 Mix Mute */
+	ad1843_LX1M   = {  4,  8,  5 },	/* Left Aux 1 Mix Gain/Atten */
+	ad1843_LX1MM  = {  4, 15,  1 },	/* Left Aux 1 Mix Mute */
+	ad1843_RX2M   = {  5,  0,  5 },	/* Right Aux 2 Mix Gain/Atten */
+	ad1843_RX2MM  = {  5,  7,  1 },	/* Right Aux 2 Mix Mute */
+	ad1843_LX2M   = {  5,  8,  5 },	/* Left Aux 2 Mix Gain/Atten */
+	ad1843_LX2MM  = {  5, 15,  1 },	/* Left Aux 2 Mix Mute */
+	ad1843_RMCM   = {  7,  0,  5 },	/* Right Mic Mix Gain/Atten */
+	ad1843_RMCMM  = {  7,  7,  1 },	/* Right Mic Mix Mute */
+	ad1843_LMCM   = {  7,  8,  5 },	/* Left Mic Mix Gain/Atten */
+	ad1843_LMCMM  = {  7, 15,  1 },	/* Left Mic Mix Mute */
+	ad1843_HPOS   = {  8,  4,  1 },	/* Headphone Output Voltage Swing */
+	ad1843_HPOM   = {  8,  5,  1 },	/* Headphone Output Mute */
+	ad1843_RDA1G  = {  9,  0,  6 },	/* Right DAC1 Analog/Digital Gain */
+	ad1843_RDA1GM = {  9,  7,  1 },	/* Right DAC1 Analog Mute */
+	ad1843_LDA1G  = {  9,  8,  6 },	/* Left DAC1 Analog/Digital Gain */
+	ad1843_LDA1GM = {  9, 15,  1 },	/* Left DAC1 Analog Mute */
+	ad1843_RDA1AM = { 11,  7,  1 },	/* Right DAC1 Digital Mute */
+	ad1843_LDA1AM = { 11, 15,  1 },	/* Left DAC1 Digital Mute */
+	ad1843_ADLC   = { 15,  0,  2 },	/* ADC Left Sample Rate Source */
+	ad1843_ADRC   = { 15,  2,  2 },	/* ADC Right Sample Rate Source */
+	ad1843_DA1C   = { 15,  8,  2 },	/* DAC1 Sample Rate Source */
+	ad1843_C1C    = { 17,  0, 16 },	/* Clock 1 Sample Rate Select */
+	ad1843_C2C    = { 20,  0, 16 },	/* Clock 1 Sample Rate Select */
+	ad1843_DAADL  = { 25,  4,  2 },	/* Digital ADC Left Source Select */
+	ad1843_DAADR  = { 25,  6,  2 },	/* Digital ADC Right Source Select */
+	ad1843_DRSFLT = { 25, 15,  1 },	/* Digital Reampler Filter Mode */
+	ad1843_ADLF   = { 26,  0,  2 }, /* ADC Left Channel Data Format */
+	ad1843_ADRF   = { 26,  2,  2 }, /* ADC Right Channel Data Format */
+	ad1843_ADTLK  = { 26,  4,  1 },	/* ADC Transmit Lock Mode Select */
+	ad1843_SCF    = { 26,  7,  1 },	/* SCLK Frequency Select */
+	ad1843_DA1F   = { 26,  8,  2 },	/* DAC1 Data Format Select */
+	ad1843_DA1SM  = { 26, 14,  1 },	/* DAC1 Stereo/Mono Mode Select */
+	ad1843_ADLEN  = { 27,  0,  1 },	/* ADC Left Channel Enable */
+	ad1843_ADREN  = { 27,  1,  1 },	/* ADC Right Channel Enable */
+	ad1843_AAMEN  = { 27,  4,  1 },	/* Analog to Analog Mix Enable */
+	ad1843_ANAEN  = { 27,  7,  1 },	/* Analog Channel Enable */
+	ad1843_DA1EN  = { 27,  8,  1 },	/* DAC1 Enable */
+	ad1843_DA2EN  = { 27,  9,  1 },	/* DAC2 Enable */
+	ad1843_C1EN   = { 28, 11,  1 },	/* Clock Generator 1 Enable */
+	ad1843_C2EN   = { 28, 12,  1 },	/* Clock Generator 2 Enable */
+	ad1843_PDNI   = { 28, 15,  1 };	/* Converter Power Down */
+
+/*
+ * The various registers of the AD1843 use three different formats for
+ * specifying gain.  The ad1843_gain structure parameterizes the
+ * formats.
+ */
+
+typedef struct ad1843_gain {
+
+	int	negative;		/* nonzero if gain is negative. */
+	const ad1843_bitfield_t *lfield;
+	const ad1843_bitfield_t *rfield;
+
+} ad1843_gain_t;
+
+static const ad1843_gain_t ad1843_gain_RECLEV
+				= { 0, &ad1843_LIG,   &ad1843_RIG };
+static const ad1843_gain_t ad1843_gain_LINE
+				= { 1, &ad1843_LX1M,  &ad1843_RX1M };
+static const ad1843_gain_t ad1843_gain_CD
+				= { 1, &ad1843_LX2M,  &ad1843_RX2M };
+static const ad1843_gain_t ad1843_gain_MIC
+				= { 1, &ad1843_LMCM,  &ad1843_RMCM };
+static const ad1843_gain_t ad1843_gain_PCM
+				= { 1, &ad1843_LDA1G, &ad1843_RDA1G };
+
+/* read the current value of an AD1843 bitfield. */
+
+static int ad1843_read_bits(lithium_t *lith, const ad1843_bitfield_t *field)
+{
+	int w = li_read_ad1843_reg(lith, field->reg);
+	int val = w >> field->lo_bit & ((1 << field->nbits) - 1);
+
+	DBGXV("ad1843_read_bits(lith=0x%p, field->{%d %d %d}) returns 0x%x\n",
+	      lith, field->reg, field->lo_bit, field->nbits, val);
+
+	return val;
+}
+
+/*
+ * write a new value to an AD1843 bitfield and return the old value.
+ */
+
+static int ad1843_write_bits(lithium_t *lith,
+			     const ad1843_bitfield_t *field,
+			     int newval)
+{
+	int w = li_read_ad1843_reg(lith, field->reg);
+	int mask = ((1 << field->nbits) - 1) << field->lo_bit;
+	int oldval = (w & mask) >> field->lo_bit;
+	int newbits = (newval << field->lo_bit) & mask;
+	w = (w & ~mask) | newbits;
+	(void) li_write_ad1843_reg(lith, field->reg, w);
+
+	DBGXV("ad1843_write_bits(lith=0x%p, field->{%d %d %d}, val=0x%x) "
+	      "returns 0x%x\n",
+	      lith, field->reg, field->lo_bit, field->nbits, newval,
+	      oldval);
+
+	return oldval;
+}
+
+/*
+ * ad1843_read_multi reads multiple bitfields from the same AD1843
+ * register.  It uses a single read cycle to do it.  (Reading the
+ * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20
+ * microseconds.)
+ *
+ * Called ike this.
+ *
+ *  ad1843_read_multi(lith, nfields,
+ *		      &ad1843_FIELD1, &val1,
+ *		      &ad1843_FIELD2, &val2, ...);
+ */
+
+static void ad1843_read_multi(lithium_t *lith, int argcount, ...)
+{
+	va_list ap;
+	const ad1843_bitfield_t *fp;
+	int w = 0, mask, *value, reg = -1;
+
+	va_start(ap, argcount);
+	while (--argcount >= 0) {
+		fp = va_arg(ap, const ad1843_bitfield_t *);
+		value = va_arg(ap, int *);
+		if (reg == -1) {
+			reg = fp->reg;
+			w = li_read_ad1843_reg(lith, reg);
+		}
+		ASSERT(reg == fp->reg);
+		mask = (1 << fp->nbits) - 1;
+		*value = w >> fp->lo_bit & mask;
+	}
+	va_end(ap);
+}
+
+/*
+ * ad1843_write_multi stores multiple bitfields into the same AD1843
+ * register.  It uses one read and one write cycle to do it.
+ *
+ * Called like this.
+ *
+ *  ad1843_write_multi(lith, nfields,
+ *		       &ad1843_FIELD1, val1,
+ *		       &ad1843_FIELF2, val2, ...);
+ */
+
+static void ad1843_write_multi(lithium_t *lith, int argcount, ...)
+{
+	va_list ap;
+	int reg;
+	const ad1843_bitfield_t *fp;
+	int value;
+	int w, m, mask, bits;
+
+	mask = 0;
+	bits = 0;
+	reg = -1;
+
+	va_start(ap, argcount);
+	while (--argcount >= 0) {
+		fp = va_arg(ap, const ad1843_bitfield_t *);
+		value = va_arg(ap, int);
+		if (reg == -1)
+			reg = fp->reg;
+		ASSERT(fp->reg == reg);
+		m = ((1 << fp->nbits) - 1) << fp->lo_bit;
+		mask |= m;
+		bits |= (value << fp->lo_bit) & m;
+	}
+	va_end(ap);
+	ASSERT(!(bits & ~mask));
+	if (~mask & 0xFFFF)
+		w = li_read_ad1843_reg(lith, reg);
+	else
+		w = 0;
+	w = (w & ~mask) | bits;
+	(void) li_write_ad1843_reg(lith, reg, w);
+}
+
+/*
+ * ad1843_get_gain reads the specified register and extracts the gain value
+ * using the supplied gain type.  It returns the gain in OSS format.
+ */
+
+static int ad1843_get_gain(lithium_t *lith, const ad1843_gain_t *gp)
+{
+	int lg, rg;
+	unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+	ad1843_read_multi(lith, 2, gp->lfield, &lg, gp->rfield, &rg);
+	if (gp->negative) {
+		lg = mask - lg;
+		rg = mask - rg;
+	}
+	lg = (lg * 100 + (mask >> 1)) / mask;
+	rg = (rg * 100 + (mask >> 1)) / mask;
+	return lg << 0 | rg << 8;
+}
+
+/*
+ * Set an audio channel's gain. Converts from OSS format to AD1843's
+ * format.
+ *
+ * Returns the new gain, which may be lower than the old gain.
+ */
+
+static int ad1843_set_gain(lithium_t *lith,
+			   const ad1843_gain_t *gp,
+			   int newval)
+{
+	unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+	int lg = newval >> 0 & 0xFF;
+	int rg = newval >> 8;
+	if (lg < 0 || lg > 100 || rg < 0 || rg > 100)
+		return -EINVAL;
+	lg = (lg * mask + (mask >> 1)) / 100;
+	rg = (rg * mask + (mask >> 1)) / 100;
+	if (gp->negative) {
+		lg = mask - lg;
+		rg = mask - rg;
+	}
+	ad1843_write_multi(lith, 2, gp->lfield, lg, gp->rfield, rg);
+	return ad1843_get_gain(lith, gp);
+}
+
+/* Returns the current recording source, in OSS format. */
+
+static int ad1843_get_recsrc(lithium_t *lith)
+{
+	int ls = ad1843_read_bits(lith, &ad1843_LSS);
+
+	switch (ls) {
+	case 1:
+		return SOUND_MASK_MIC;
+	case 2:
+		return SOUND_MASK_LINE;
+	case 3:
+		return SOUND_MASK_CD;
+	case 6:
+		return SOUND_MASK_PCM;
+	default:
+		ASSERT(0);
+		return -1;
+	}
+}
+
+/*
+ * Enable/disable digital resample mode in the AD1843.
+ *
+ * The AD1843 requires that ADL, ADR, DA1 and DA2 be powered down
+ * while switching modes.  So we save DA1's state (DA2's state is not
+ * interesting), power them down, switch into/out of resample mode,
+ * power them up, and restore state.
+ *
+ * This will cause audible glitches if D/A or A/D is going on, so the
+ * driver disallows that (in mixer_write_ioctl()).
+ *
+ * The open question is, is this worth doing?  I'm leaving it in,
+ * because it's written, but...
+ */
+
+static void ad1843_set_resample_mode(lithium_t *lith, int onoff)
+{
+	/* Save DA1 mute and gain (addr 9 is DA1 analog gain/attenuation) */
+	int save_da1 = li_read_ad1843_reg(lith, 9);
+
+	/* Power down A/D and D/A. */
+	ad1843_write_multi(lith, 4,
+			   &ad1843_DA1EN, 0,
+			   &ad1843_DA2EN, 0,
+			   &ad1843_ADLEN, 0,
+			   &ad1843_ADREN, 0);
+
+	/* Switch mode */
+	ASSERT(onoff == 0 || onoff == 1);
+	ad1843_write_bits(lith, &ad1843_DRSFLT, onoff);
+
+ 	/* Power up A/D and D/A. */
+	ad1843_write_multi(lith, 3,
+			   &ad1843_DA1EN, 1,
+			   &ad1843_ADLEN, 1,
+			   &ad1843_ADREN, 1);
+
+	/* Restore DA1 mute and gain. */
+	li_write_ad1843_reg(lith, 9, save_da1);
+}
+
+/*
+ * Set recording source.  Arg newsrc specifies an OSS channel mask.
+ *
+ * The complication is that when we switch into/out of loopback mode
+ * (i.e., src = SOUND_MASK_PCM), we change the AD1843 into/out of
+ * digital resampling mode.
+ *
+ * Returns newsrc on success, -errno on failure.
+ */
+
+static int ad1843_set_recsrc(lithium_t *lith, int newsrc)
+{
+	int bits;
+	int oldbits;
+
+	switch (newsrc) {
+	case SOUND_MASK_PCM:
+		bits = 6;
+		break;
+
+	case SOUND_MASK_MIC:
+		bits = 1;
+		break;
+
+	case SOUND_MASK_LINE:
+		bits = 2;
+		break;
+
+	case SOUND_MASK_CD:
+		bits = 3;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	oldbits = ad1843_read_bits(lith, &ad1843_LSS);
+	if (newsrc == SOUND_MASK_PCM && oldbits != 6) {
+		DBGP("enabling digital resample mode\n");
+		ad1843_set_resample_mode(lith, 1);
+		ad1843_write_multi(lith, 2,
+				   &ad1843_DAADL, 2,
+				   &ad1843_DAADR, 2);
+	} else if (newsrc != SOUND_MASK_PCM && oldbits == 6) {
+		DBGP("disabling digital resample mode\n");
+		ad1843_set_resample_mode(lith, 0);
+		ad1843_write_multi(lith, 2,
+				   &ad1843_DAADL, 0,
+				   &ad1843_DAADR, 0);
+	}
+	ad1843_write_multi(lith, 2, &ad1843_LSS, bits, &ad1843_RSS, bits);
+	return newsrc;
+}
+
+/*
+ * Return current output sources, in OSS format.
+ */
+
+static int ad1843_get_outsrc(lithium_t *lith)
+{
+	int pcm, line, mic, cd;
+
+	pcm  = ad1843_read_bits(lith, &ad1843_LDA1GM) ? 0 : SOUND_MASK_PCM;
+	line = ad1843_read_bits(lith, &ad1843_LX1MM)  ? 0 : SOUND_MASK_LINE;
+	cd   = ad1843_read_bits(lith, &ad1843_LX2MM)  ? 0 : SOUND_MASK_CD;
+	mic  = ad1843_read_bits(lith, &ad1843_LMCMM)  ? 0 : SOUND_MASK_MIC;
+
+	return pcm | line | cd | mic;
+}
+
+/*
+ * Set output sources.  Arg is a mask of active sources in OSS format.
+ *
+ * Returns source mask on success, -errno on failure.
+ */
+
+static int ad1843_set_outsrc(lithium_t *lith, int mask)
+{
+	int pcm, line, mic, cd;
+
+	if (mask & ~(SOUND_MASK_PCM | SOUND_MASK_LINE |
+		     SOUND_MASK_CD | SOUND_MASK_MIC))
+		return -EINVAL;
+	pcm  = (mask & SOUND_MASK_PCM)  ? 0 : 1;
+	line = (mask & SOUND_MASK_LINE) ? 0 : 1;
+	mic  = (mask & SOUND_MASK_MIC)  ? 0 : 1;
+	cd   = (mask & SOUND_MASK_CD)   ? 0 : 1;
+
+	ad1843_write_multi(lith, 2, &ad1843_LDA1GM, pcm, &ad1843_RDA1GM, pcm);
+	ad1843_write_multi(lith, 2, &ad1843_LX1MM, line, &ad1843_RX1MM, line);
+	ad1843_write_multi(lith, 2, &ad1843_LX2MM, cd,   &ad1843_RX2MM, cd);
+	ad1843_write_multi(lith, 2, &ad1843_LMCMM, mic,  &ad1843_RMCMM, mic);
+
+	return mask;
+}
+
+/* Setup ad1843 for D/A conversion. */
+
+static void ad1843_setup_dac(lithium_t *lith,
+			     int framerate,
+			     int fmt,
+			     int channels)
+{
+	int ad_fmt = 0, ad_mode = 0;
+
+	DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n",
+	      lith, framerate, fmt, channels);
+
+	switch (fmt) {
+	case AFMT_S8:		ad_fmt = 1; break;
+	case AFMT_U8:		ad_fmt = 1; break;
+	case AFMT_S16_LE:	ad_fmt = 1; break;
+	case AFMT_MU_LAW:	ad_fmt = 2; break;
+	case AFMT_A_LAW:	ad_fmt = 3; break;
+	default:		ASSERT(0);
+	}
+
+	switch (channels) {
+	case 2:			ad_mode = 0; break;
+	case 1:			ad_mode = 1; break;
+	default:		ASSERT(0);
+	}
+		
+	DBGPV("ad_mode = %d, ad_fmt = %d\n", ad_mode, ad_fmt);
+	ASSERT(framerate >= 4000 && framerate <= 49000);
+	ad1843_write_bits(lith, &ad1843_C1C, framerate);
+	ad1843_write_multi(lith, 2,
+			   &ad1843_DA1SM, ad_mode, &ad1843_DA1F, ad_fmt);
+}
+
+static void ad1843_shutdown_dac(lithium_t *lith)
+{
+	ad1843_write_bits(lith, &ad1843_DA1F, 1);
+}
+
+static void ad1843_setup_adc(lithium_t *lith, int framerate, int fmt, int channels)
+{
+	int da_fmt = 0;
+
+	DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n",
+	      lith, framerate, fmt, channels);
+
+	switch (fmt) {
+	case AFMT_S8:		da_fmt = 1; break;
+	case AFMT_U8:		da_fmt = 1; break;
+	case AFMT_S16_LE:	da_fmt = 1; break;
+	case AFMT_MU_LAW:	da_fmt = 2; break;
+	case AFMT_A_LAW:	da_fmt = 3; break;
+	default:		ASSERT(0);
+	}
+
+	DBGPV("da_fmt = %d\n", da_fmt);
+	ASSERT(framerate >= 4000 && framerate <= 49000);
+	ad1843_write_bits(lith, &ad1843_C2C, framerate);
+	ad1843_write_multi(lith, 2,
+			   &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt);
+}
+
+static void ad1843_shutdown_adc(lithium_t *lith)
+{
+	/* nothing to do */
+}
+
+/*
+ * Fully initialize the ad1843.  As described in the AD1843 data
+ * sheet, section "START-UP SEQUENCE".  The numbered comments are
+ * subsection headings from the data sheet.  See the data sheet, pages
+ * 52-54, for more info.
+ *
+ * return 0 on success, -errno on failure.  */
+
+static int ad1843_init(lithium_t *lith)
+{
+	unsigned long later;
+	int err;
+
+	err = li_init(lith);
+	if (err)
+		return err;
+
+	if (ad1843_read_bits(lith, &ad1843_INIT) != 0) {
+		printk(KERN_ERR "vwsnd sound: AD1843 won't initialize\n");
+		return -EIO;
+	}
+
+	ad1843_write_bits(lith, &ad1843_SCF, 1);
+
+	/* 4. Put the conversion resources into standby. */
+
+	ad1843_write_bits(lith, &ad1843_PDNI, 0);
+	later = jiffies + HZ / 2;	/* roughly half a second */
+	DBGDO(shut_up++);
+	while (ad1843_read_bits(lith, &ad1843_PDNO)) {
+		if (jiffies > later) {
+			printk(KERN_ERR
+			       "vwsnd audio: AD1843 won't power up\n");
+			return -EIO;
+		}
+		schedule();
+	}
+	DBGDO(shut_up--);
+
+	/* 5. Power up the clock generators and enable clock output pins. */
+
+	ad1843_write_multi(lith, 2, &ad1843_C1EN, 1, &ad1843_C2EN, 1);
+
+	/* 6. Configure conversion resources while they are in standby. */
+
+ 	/* DAC1 uses clock 1 as source, ADC uses clock 2.  Always. */
+
+	ad1843_write_multi(lith, 3,
+			   &ad1843_DA1C, 1,
+			   &ad1843_ADLC, 2,
+			   &ad1843_ADRC, 2);
+
+	/* 7. Enable conversion resources. */
+
+	ad1843_write_bits(lith, &ad1843_ADTLK, 1);
+	ad1843_write_multi(lith, 5,
+			   &ad1843_ANAEN, 1,
+			   &ad1843_AAMEN, 1,
+			   &ad1843_DA1EN, 1,
+			   &ad1843_ADLEN, 1,
+			   &ad1843_ADREN, 1);
+
+	/* 8. Configure conversion resources while they are enabled. */
+
+	ad1843_write_bits(lith, &ad1843_DA1C, 1);
+
+	/* Unmute all channels. */
+
+	ad1843_set_outsrc(lith,
+			  (SOUND_MASK_PCM | SOUND_MASK_LINE |
+			   SOUND_MASK_MIC | SOUND_MASK_CD));
+	ad1843_write_multi(lith, 2, &ad1843_LDA1AM, 0, &ad1843_RDA1AM, 0);
+
+	/* Set default recording source to Line In and set
+	 * mic gain to +20 dB.
+	 */
+
+	ad1843_set_recsrc(lith, SOUND_MASK_LINE);
+	ad1843_write_multi(lith, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1);
+
+	/* Set Speaker Out level to +/- 4V and unmute it. */
+
+	ad1843_write_multi(lith, 2, &ad1843_HPOS, 1, &ad1843_HPOM, 0);
+
+	return 0;
+}
+
+/*****************************************************************************/
+/* PCM I/O */
+
+#define READ_INTR_MASK  (LI_INTR_COMM1_TRIG | LI_INTR_COMM1_OVERFLOW)
+#define WRITE_INTR_MASK (LI_INTR_COMM2_TRIG | LI_INTR_COMM2_UNDERFLOW)
+
+typedef enum vwsnd_port_swstate {	/* software state */
+	SW_OFF,
+	SW_INITIAL,
+	SW_RUN,
+	SW_DRAIN,
+} vwsnd_port_swstate_t;
+
+typedef enum vwsnd_port_hwstate {	/* hardware state */
+	HW_STOPPED,
+	HW_RUNNING,
+} vwsnd_port_hwstate_t;
+
+/*
+ * These flags are read by ISR, but only written at baseline.
+ */
+
+typedef enum vwsnd_port_flags {
+	DISABLED = 1 << 0,
+	ERFLOWN  = 1 << 1,		/* overflown or underflown */
+	HW_BUSY  = 1 << 2,
+} vwsnd_port_flags_t;
+
+/*
+ * vwsnd_port is the per-port data structure.  Each device has two
+ * ports, one for input and one for output.
+ *
+ * Locking:
+ *
+ *	port->lock protects: hwstate, flags, swb_[iu]_avail.
+ *
+ *	devc->io_sema protects: swstate, sw_*, swb_[iu]_idx.
+ *
+ *	everything else is only written by open/release or
+ *	pcm_{setup,shutdown}(), which are serialized by a
+ *	combination of devc->open_sema and devc->io_sema.
+ */
+
+typedef struct vwsnd_port {
+
+	spinlock_t	lock;
+	struct wait_queue *queue;
+	vwsnd_port_swstate_t swstate;
+	vwsnd_port_hwstate_t hwstate;
+	vwsnd_port_flags_t flags;
+
+	int		sw_channels;
+	int		sw_samplefmt;
+	int		sw_framerate;
+	int		sample_size;
+	int		frame_size;
+	unsigned int	zero_word;	/* zero for the sample format */
+
+	int		sw_fragshift;
+	int		sw_fragcount;
+	int		sw_subdivshift;
+
+	unsigned int	hw_fragshift;
+	unsigned int	hw_fragsize;
+	unsigned int	hw_fragcount;
+
+	int		hwbuf_size;
+	unsigned long	hwbuf_paddr;
+	unsigned long	hwbuf_vaddr;
+	caddr_t		hwbuf;		/* hwbuf == hwbuf_vaddr */
+	int		hwbuf_max;	/* max bytes to preload */
+
+	caddr_t		swbuf;
+	unsigned int	swbuf_size;	/* size in bytes */
+	unsigned int	swb_u_idx;	/* index of next user byte */
+	unsigned int	swb_i_idx;	/* index of next intr byte */
+	unsigned int	swb_u_avail;	/* # bytes avail to user */
+	unsigned int	swb_i_avail;	/* # bytes avail to intr */
+
+	dma_chan_t	chan;
+
+	/* Accounting */
+
+	int		byte_count;
+	int		frag_count;
+	int		MSC_offset;
+
+} vwsnd_port_t;
+
+/* vwsnd_dev is the per-device data structure. */
+
+typedef struct vwsnd_dev {
+	struct vwsnd_dev *next_dev;
+	int		audio_minor;	/* minor number of audio device */
+	int		mixer_minor;	/* minor number of mixer device */
+
+	struct semaphore open_sema;
+	struct semaphore io_sema;
+	struct semaphore mix_sema;
+	mode_t		open_mode;
+	struct wait_queue *open_wait;
+
+	lithium_t	lith;
+
+	vwsnd_port_t	rport;
+	vwsnd_port_t	wport;
+} vwsnd_dev_t;
+
+static vwsnd_dev_t *vwsnd_dev_list;	/* linked list of all devices */
+
+#ifdef MODULE
+
+# define INC_USE_COUNT MOD_INC_USE_COUNT
+# define DEC_USE_COUNT MOD_DEC_USE_COUNT
+# define IN_USE        MOD_IN_USE
+
+#else
+
+static atomic_t vwsnd_use_count = ATOMIC_INIT(0);
+
+# define INC_USE_COUNT (atomic_inc(&vwsnd_use_count))
+# define DEC_USE_COUNT (atomic_dec(&vwsnd_use_count))
+# define IN_USE        (atomic_read(&vwsnd_use_count) != 0)
+
+#endif
+
+/*
+ * Lithium can only DMA multiples of 32 bytes.  Its DMA buffer may
+ * be up to 8 Kb.  This driver always uses 8 Kb.
+ *
+ * Memory bug workaround -- I'm not sure what's going on here, but
+ * somehow pcm_copy_out() was triggering segv's going on to the next
+ * page of the hw buffer.  So, I make the hw buffer one size bigger
+ * than we actually use.  That way, the following page is allocated
+ * and mapped, and no error.  I suspect that something is broken
+ * in Cobalt, but haven't really investigated.  HBO is the actual
+ * size of the buffer, and HWBUF_ORDER is what we allocate.
+ */
+
+#define HWBUF_SHIFT 13
+#define HWBUF_SIZE (1 << HWBUF_SHIFT)
+# define HBO         (HWBUF_SHIFT > PAGE_SHIFT ? HWBUF_SHIFT - PAGE_SHIFT : 0)
+# define HWBUF_ORDER (HBO + 1)		/* next size bigger */
+#define MIN_SPEED 4000
+#define MAX_SPEED 49000
+
+#define MIN_FRAGSHIFT			(DMACHUNK_SHIFT + 1)
+#define MAX_FRAGSHIFT			(PAGE_SHIFT)
+#define MIN_FRAGSIZE			(1 << MIN_FRAGSHIFT)
+#define MAX_FRAGSIZE			(1 << MAX_FRAGSHIFT)
+#define MIN_FRAGCOUNT(fragsize)		3
+#define MAX_FRAGCOUNT(fragsize)		(32 * PAGE_SIZE / (fragsize))
+#define DEFAULT_FRAGSHIFT		12
+#define DEFAULT_FRAGCOUNT		16
+#define DEFAULT_SUBDIVSHIFT		0
+
+/*
+ * The software buffer (swbuf) is a ring buffer shared between user
+ * level and interrupt level.  Each level owns some of the bytes in
+ * the buffer, and may give bytes away by calling swb_inc_{u,i}().
+ * User level calls _u for user, and interrupt level calls _i for
+ * interrupt.
+ *
+ * port->swb_{u,i}_avail is the number of bytes available to that level.
+ *
+ * port->swb_{u,i}_idx is the index of the first available byte in the
+ * buffer.
+ *
+ * Each level calls swb_inc_{u,i}() to atomically increment its index,
+ * recalculate the number of bytes available for both sides, and
+ * return the number of bytes available.  Since each side can only
+ * give away bytes, the other side can only increase the number of
+ * bytes available to this side.  Each side updates its own index
+ * variable, swb_{u,i}_idx, so no lock is needed to read it.
+ *
+ * To query the number of bytes available, call swb_inc_{u,i} with an
+ * increment of zero.
+ */
+
+static __inline__ unsigned int __swb_inc_u(vwsnd_port_t *port, int inc)
+{
+	if (inc) {
+		port->swb_u_idx += inc;
+		port->swb_u_idx %= port->swbuf_size;
+		port->swb_u_avail -= inc;
+		port->swb_i_avail += inc;
+	}
+	return port->swb_u_avail;
+}
+
+static __inline__ unsigned int swb_inc_u(vwsnd_port_t *port, int inc)
+{
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&port->lock, flags);
+	{
+		ret = __swb_inc_u(port, inc);
+	}
+	spin_unlock_irqrestore(&port->lock, flags);
+	return ret;
+}
+
+static __inline__ unsigned int __swb_inc_i(vwsnd_port_t *port, int inc)
+{
+	if (inc) {
+		port->swb_i_idx += inc;
+		port->swb_i_idx %= port->swbuf_size;
+		port->swb_i_avail -= inc;
+		port->swb_u_avail += inc;
+	}
+	return port->swb_i_avail;
+}
+
+static __inline__ unsigned int swb_inc_i(vwsnd_port_t *port, int inc)
+{
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&port->lock, flags);
+	{
+		ret = __swb_inc_i(port, inc);
+	}
+	spin_unlock_irqrestore(&port->lock, flags);
+	return ret;
+}
+
+/*
+ * pcm_setup - this routine initializes all port state after
+ * mode-setting ioctls have been done, but before the first I/O is
+ * done.
+ *
+ * Locking: called with devc->io_sema held.
+ *
+ * Returns 0 on success, -errno on failure.
+ */
+
+static int pcm_setup(vwsnd_dev_t *devc,
+		     vwsnd_port_t *rport,
+		     vwsnd_port_t *wport)
+{
+	vwsnd_port_t *aport = rport ? rport : wport;
+	int sample_size;
+	unsigned int zero_word;
+
+	DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport);
+
+	ASSERT(aport != NULL);
+	if (aport->swbuf != NULL)
+		return 0;
+	switch (aport->sw_samplefmt) {
+	case AFMT_MU_LAW:
+		sample_size = 1;
+		zero_word = 0xFFFFFFFF ^ 0x80808080;
+		break;
+
+	case AFMT_A_LAW:
+		sample_size = 1;
+		zero_word = 0xD5D5D5D5 ^ 0x80808080;
+		break;
+
+	case AFMT_U8:
+		sample_size = 1;
+		zero_word = 0x80808080;
+		break;
+
+	case AFMT_S8:
+		sample_size = 1;
+		zero_word = 0x00000000;
+		break;
+
+	case AFMT_S16_LE:
+		sample_size = 2;
+		zero_word = 0x00000000;
+		break;
+
+	default:
+		sample_size = 0;	/* prevent compiler warning */
+		zero_word = 0;
+		ASSERT(0);
+	}
+	aport->sample_size  = sample_size;
+	aport->zero_word    = zero_word;
+	aport->frame_size   = aport->sw_channels * aport->sample_size;
+	aport->hw_fragshift = aport->sw_fragshift - aport->sw_subdivshift;
+	aport->hw_fragsize  = 1 << aport->hw_fragshift;
+	aport->hw_fragcount = aport->sw_fragcount << aport->sw_subdivshift;
+	ASSERT(aport->hw_fragsize >= MIN_FRAGSIZE);
+	ASSERT(aport->hw_fragsize <= MAX_FRAGSIZE);
+	ASSERT(aport->hw_fragcount >= MIN_FRAGCOUNT(aport->hw_fragsize));
+	ASSERT(aport->hw_fragcount <= MAX_FRAGCOUNT(aport->hw_fragsize));
+	if (rport) {
+		int hwfrags, swfrags;
+		rport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE;
+		hwfrags = rport->hwbuf_max >> aport->hw_fragshift;
+		swfrags = aport->hw_fragcount - hwfrags;
+		if (swfrags < 2)
+			swfrags = 2;
+		rport->swbuf_size = swfrags * aport->hw_fragsize;
+		DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags);
+		DBGPV("read hwbuf_max = %d, swbuf_size = %d\n",
+		     rport->hwbuf_max, rport->swbuf_size);
+	}
+	if (wport) {
+		int hwfrags, swfrags;
+		int total_bytes = aport->hw_fragcount * aport->hw_fragsize;
+		wport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE;
+		if (wport->hwbuf_max > total_bytes)
+			wport->hwbuf_max = total_bytes;
+		hwfrags = wport->hwbuf_max >> aport->hw_fragshift;
+		DBGPV("hwfrags = %d\n", hwfrags);
+		swfrags = aport->hw_fragcount - hwfrags;
+		if (swfrags < 2)
+			swfrags = 2;
+		wport->swbuf_size = swfrags * aport->hw_fragsize;
+		DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags);
+		DBGPV("write hwbuf_max = %d, swbuf_size = %d\n",
+		     wport->hwbuf_max, wport->swbuf_size);
+	}
+
+	aport->swb_u_idx    = 0;
+	aport->swb_i_idx    = 0;
+	aport->byte_count   = 0;
+
+	/*
+	 * Is this a Cobalt bug?  We need to make this buffer extend
+	 * one page further than we actually use -- somehow memcpy
+	 * causes an exceptoin otherwise.  I suspect there's a bug in
+	 * Cobalt (or somewhere) where it's generating a fault on a
+	 * speculative load or something.  Obviously, I haven't taken
+	 * the time to track it down.
+	 */
+
+	aport->swbuf        = vmalloc(aport->swbuf_size + PAGE_SIZE);
+	if (!aport->swbuf)
+		return -ENOMEM;
+	if (rport && wport) {
+		ASSERT(aport == rport);
+		ASSERT(wport->swbuf == NULL);
+		/* One extra page - see comment above. */
+		wport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE);
+		if (!wport->swbuf) {
+			vfree(aport->swbuf);
+			aport->swbuf = NULL;
+			return -ENOMEM;
+		}
+		wport->sample_size  = rport->sample_size;
+		wport->zero_word    = rport->zero_word;
+		wport->frame_size   = rport->frame_size;
+		wport->hw_fragshift = rport->hw_fragshift;
+		wport->hw_fragsize  = rport->hw_fragsize;
+		wport->hw_fragcount = rport->hw_fragcount;
+		wport->swbuf_size   = rport->swbuf_size;
+		wport->hwbuf_max    = rport->hwbuf_max;
+		wport->swb_u_idx    = rport->swb_u_idx;
+		wport->swb_i_idx    = rport->swb_i_idx;
+		wport->byte_count   = rport->byte_count;
+	}
+	if (rport) {
+		rport->swb_u_avail = 0;
+		rport->swb_i_avail = rport->swbuf_size;
+		rport->swstate = SW_RUN;
+		li_setup_dma(&rport->chan,
+			     &li_comm1,
+			     &devc->lith,
+			     rport->hwbuf_paddr,
+			     HWBUF_SHIFT,
+			     rport->hw_fragshift,
+			     rport->sw_channels,
+			     rport->sample_size);
+		ad1843_setup_adc(&devc->lith,
+				 rport->sw_framerate,
+				 rport->sw_samplefmt,
+				 rport->sw_channels);
+		li_enable_interrupts(&devc->lith, READ_INTR_MASK);
+		if (!(rport->flags & DISABLED)) {
+			ustmsc_t ustmsc;
+			rport->hwstate = HW_RUNNING;
+			li_activate_dma(&rport->chan);
+			li_read_USTMSC(&rport->chan, &ustmsc);
+			rport->MSC_offset = ustmsc.msc;
+		}
+	}
+	if (wport) {
+		if (wport->hwbuf_max > wport->swbuf_size)
+			wport->hwbuf_max = wport->swbuf_size;
+		wport->flags &= ~ERFLOWN;
+		wport->swb_u_avail = wport->swbuf_size;
+		wport->swb_i_avail = 0;
+		wport->swstate = SW_RUN;
+		li_setup_dma(&wport->chan,
+			     &li_comm2,
+			     &devc->lith,
+			     wport->hwbuf_paddr,
+			     HWBUF_SHIFT,
+			     wport->hw_fragshift,
+			     wport->sw_channels,
+			     wport->sample_size);
+		ad1843_setup_dac(&devc->lith,
+				 wport->sw_framerate,
+				 wport->sw_samplefmt,
+				 wport->sw_channels);
+		li_enable_interrupts(&devc->lith, WRITE_INTR_MASK);
+	}
+	DBGRV();
+	return 0;
+}
+
+/*
+ * pcm_shutdown_port - shut down one port (direction) for PCM I/O.
+ * Only called from pcm_shutdown.
+ */
+
+static void pcm_shutdown_port(vwsnd_dev_t *devc,
+			      vwsnd_port_t *aport,
+			      unsigned int mask)
+{
+	unsigned long flags;
+	vwsnd_port_hwstate_t hwstate;
+	struct wait_queue wait = { current, NULL };
+
+	aport->swstate = SW_INITIAL;
+	add_wait_queue(&aport->queue, &wait);
+	current->state = TASK_UNINTERRUPTIBLE;
+	while (1) {
+		spin_lock_irqsave(&aport->lock, flags);
+		{
+			hwstate = aport->hwstate;
+		}		
+		spin_unlock_irqrestore(&aport->lock, flags);
+		if (hwstate == HW_STOPPED)
+			break;
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&aport->queue, &wait);
+	li_disable_interrupts(&devc->lith, mask);
+	if (aport == &devc->rport)
+		ad1843_shutdown_adc(&devc->lith);
+	else /* aport == &devc->wport) */
+		ad1843_shutdown_dac(&devc->lith);
+	li_shutdown_dma(&aport->chan);
+	vfree(aport->swbuf);
+	aport->swbuf = NULL;
+	aport->byte_count = 0;
+}
+
+/*
+ * pcm_shutdown undoes what pcm_setup did.
+ * Also sets the ports' swstate to newstate.
+ */
+
+static void pcm_shutdown(vwsnd_dev_t *devc,
+			 vwsnd_port_t *rport,
+			 vwsnd_port_t *wport)
+{
+	DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport);
+
+	if (rport && rport->swbuf) {
+		DBGPV("shutting down rport\n");
+		pcm_shutdown_port(devc, rport, READ_INTR_MASK);
+	}
+	if (wport && wport->swbuf) {
+		DBGPV("shutting down wport\n");
+		pcm_shutdown_port(devc, wport, WRITE_INTR_MASK);
+	}
+	DBGRV();
+}
+
+static void pcm_copy_in(vwsnd_port_t *rport, int swidx, int hwidx, int nb)
+{
+	char *src = rport->hwbuf + hwidx;
+	char *dst = rport->swbuf + swidx;
+	int fmt = rport->sw_samplefmt;
+
+	DBGPV("swidx = %d, hwidx = %d\n", swidx, hwidx);
+	ASSERT(rport->hwbuf != NULL);
+	ASSERT(rport->swbuf != NULL);
+	ASSERT(nb > 0 && (nb % 32) == 0);
+	ASSERT(swidx % 32 == 0 && hwidx % 32 == 0);
+	ASSERT(swidx >= 0 && swidx + nb <= rport->swbuf_size);
+	ASSERT(hwidx >= 0 && hwidx + nb <= rport->hwbuf_size);
+
+	if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) {
+
+		/* See Sample Format Notes above. */
+
+		char *end = src + nb;
+		while (src < end)
+			*dst++ = *src++ ^ 0x80;
+	} else
+		memcpy(dst, src, nb);
+}
+
+static void pcm_copy_out(vwsnd_port_t *wport, int swidx, int hwidx, int nb)
+{
+	char *src = wport->swbuf + swidx;
+	char *dst = wport->hwbuf + hwidx;
+	int fmt = wport->sw_samplefmt;
+
+	ASSERT(nb > 0 && (nb % 32) == 0);
+	ASSERT(wport->hwbuf != NULL);
+	ASSERT(wport->swbuf != NULL);
+	ASSERT(swidx % 32 == 0 && hwidx % 32 == 0);
+	ASSERT(swidx >= 0 && swidx + nb <= wport->swbuf_size);
+	ASSERT(hwidx >= 0 && hwidx + nb <= wport->hwbuf_size);
+	if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) {
+
+		/* See Sample Format Notes above. */
+
+		char *end = src + nb;
+		while (src < end)
+			*dst++ = *src++ ^ 0x80;
+	} else
+		memcpy(dst, src, nb);
+}
+
+/*
+ * pcm_output() is called both from baselevel and from interrupt level.
+ * This is where audio frames are copied into the hardware-accessible
+ * ring buffer.
+ *
+ * Locking note: The part of this routine that figures out what to do
+ * holds wport->lock.  The longer part releases wport->lock, but sets
+ * wport->flags & HW_BUSY.  Afterward, it reacquires wport->lock, and
+ * checks for more work to do.
+ *
+ * If another thread calls pcm_output() while HW_BUSY is set, it
+ * returns immediately, knowing that the thread that set HW_BUSY will
+ * look for more work to do before returning.
+ *
+ * This has the advantage that port->lock is held for several short
+ * periods instead of one long period.  Also, when pcm_output is
+ * called from base level, it reenables interrupts.
+ */
+
+static void pcm_output(vwsnd_dev_t *devc, int erflown, int nb)
+{
+	vwsnd_port_t *wport = &devc->wport;
+	const int hwmax  = wport->hwbuf_max;
+	const int hwsize = wport->hwbuf_size;
+	const int swsize = wport->swbuf_size;
+	const int fragsize = wport->hw_fragsize;
+	unsigned long iflags;
+
+	DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb);
+	spin_lock_irqsave(&wport->lock, iflags);
+	if (erflown)
+		wport->flags |= ERFLOWN;
+	(void) __swb_inc_u(wport, nb);
+	if (wport->flags & HW_BUSY) {
+		spin_unlock_irqrestore(&wport->lock, iflags);
+		DBGPV("returning: HW BUSY\n");
+		return;
+	}
+	if (wport->flags & DISABLED) {
+		spin_unlock_irqrestore(&wport->lock, iflags);
+		DBGPV("returning: DISABLED\n");
+		return;
+	}
+	wport->flags |= HW_BUSY;
+	while (1) {
+		int swptr, hwptr, hw_avail, sw_avail, swidx;
+		vwsnd_port_hwstate_t hwstate = wport->hwstate;
+		vwsnd_port_swstate_t swstate = wport->swstate;
+		int hw_unavail;
+		ustmsc_t ustmsc;
+
+		hwptr = li_read_hwptr(&wport->chan);
+		swptr = li_read_swptr(&wport->chan);
+		hw_unavail = (swptr - hwptr + hwsize) % hwsize;
+		hw_avail = (hwmax - hw_unavail) & -fragsize;
+		sw_avail = wport->swb_i_avail & -fragsize;
+		if (sw_avail && swstate == SW_RUN) {
+			if (wport->flags & ERFLOWN) {
+				wport->flags &= ~ERFLOWN;
+			}
+		} else if (swstate == SW_INITIAL ||
+			 swstate == SW_OFF ||
+			 (swstate == SW_DRAIN &&
+			  !sw_avail &&
+			  (wport->flags & ERFLOWN))) {
+			DBGP("stopping.  hwstate = %d\n", hwstate);
+			if (hwstate != HW_STOPPED) {
+				li_deactivate_dma(&wport->chan);
+				wport->hwstate = HW_STOPPED;
+			}
+			wake_up(&wport->queue);
+			break;
+		}
+		if (!sw_avail || !hw_avail)
+			break;
+		spin_unlock_irqrestore(&wport->lock, iflags);
+
+		/*
+		 * We gave up the port lock, but we have the HW_BUSY flag.
+		 * Proceed without accessing any nonlocal state.
+		 * Do not exit the loop -- must check for more work.
+		 */
+
+		swidx = wport->swb_i_idx;
+		nb = hw_avail;
+		if (nb > sw_avail)
+			nb = sw_avail;
+		if (nb > hwsize - swptr)
+			nb = hwsize - swptr; /* don't overflow hwbuf */
+		if (nb > swsize - swidx)
+			nb = swsize - swidx; /* don't overflow swbuf */
+		ASSERT(nb > 0);
+		if (nb % fragsize) {
+			DBGP("nb = %d, fragsize = %d\n", nb, fragsize);
+			DBGP("hw_avail = %d\n", hw_avail);
+			DBGP("sw_avail = %d\n", sw_avail);
+			DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr);
+			DBGP("swsize = %d, swidx = %d\n", swsize, swidx);
+		}
+		ASSERT(!(nb % fragsize));
+		DBGPV("copying swb[%d..%d] to hwb[%d..%d]\n",
+		      swidx, swidx + nb, swptr, swptr + nb);
+		pcm_copy_out(wport, swidx, swptr, nb);
+		li_write_swptr(&wport->chan, (swptr + nb) % hwsize);
+		spin_lock_irqsave(&wport->lock, iflags);
+		if (hwstate == HW_STOPPED) {
+			DBGPV("starting\n");
+			li_activate_dma(&wport->chan);
+			wport->hwstate = HW_RUNNING;
+			li_read_USTMSC(&wport->chan, &ustmsc);
+			ASSERT(wport->byte_count % wport->frame_size == 0);
+			wport->MSC_offset = ustmsc.msc - wport->byte_count / wport->frame_size;
+		}
+		__swb_inc_i(wport, nb);
+		wport->byte_count += nb;
+		wport->frag_count += nb / fragsize;
+		ASSERT(nb % fragsize == 0);
+		wake_up(&wport->queue);
+	}
+	wport->flags &= ~HW_BUSY;
+	spin_unlock_irqrestore(&wport->lock, iflags);
+	DBGRV();
+}
+
+/*
+ * pcm_input() is called both from baselevel and from interrupt level.
+ * This is where audio frames are copied out of the hardware-accessible
+ * ring buffer.
+ *
+ * Locking note: The part of this routine that figures out what to do
+ * holds rport->lock.  The longer part releases rport->lock, but sets
+ * rport->flags & HW_BUSY.  Afterward, it reacquires rport->lock, and
+ * checks for more work to do.
+ *
+ * If another thread calls pcm_input() while HW_BUSY is set, it
+ * returns immediately, knowing that the thread that set HW_BUSY will
+ * look for more work to do before returning.
+ *
+ * This has the advantage that port->lock is held for several short
+ * periods instead of one long period.  Also, when pcm_input is
+ * called from base level, it reenables interrupts.
+ */
+
+static void pcm_input(vwsnd_dev_t *devc, int erflown, int nb)
+{
+	vwsnd_port_t *rport = &devc->rport;
+	const int hwmax  = rport->hwbuf_max;
+	const int hwsize = rport->hwbuf_size;
+	const int swsize = rport->swbuf_size;
+	const int fragsize = rport->hw_fragsize;
+	unsigned long iflags;
+
+	DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb);
+
+	spin_lock_irqsave(&rport->lock, iflags);
+	if (erflown)
+		rport->flags |= ERFLOWN;
+	(void) __swb_inc_u(rport, nb);
+	if (rport->flags & HW_BUSY || !rport->swbuf) {
+		spin_unlock_irqrestore(&rport->lock, iflags);
+		DBGPV("returning: HW BUSY or !swbuf\n");
+		return;
+	}
+	if (rport->flags & DISABLED) {
+		spin_unlock_irqrestore(&rport->lock, iflags);
+		DBGPV("returning: DISABLED\n");
+		return;
+	}
+	rport->flags |= HW_BUSY;
+	while (1) {
+		int swptr, hwptr, hw_avail, sw_avail, swidx;
+		vwsnd_port_hwstate_t hwstate = rport->hwstate;
+		vwsnd_port_swstate_t swstate = rport->swstate;
+
+		hwptr = li_read_hwptr(&rport->chan);
+		swptr = li_read_swptr(&rport->chan);
+		hw_avail = (hwptr - swptr + hwsize) % hwsize & -fragsize;
+		if (hw_avail > hwmax)
+			hw_avail = hwmax;
+		sw_avail = rport->swb_i_avail & -fragsize;
+		if (swstate != SW_RUN) {
+			DBGP("stopping.  hwstate = %d\n", hwstate);
+			if (hwstate != HW_STOPPED) {
+				li_deactivate_dma(&rport->chan);
+				rport->hwstate = HW_STOPPED;
+			}
+			wake_up(&rport->queue);
+			break;
+		}
+		if (!sw_avail || !hw_avail)
+			break;
+		spin_unlock_irqrestore(&rport->lock, iflags);
+
+		/*
+		 * We gave up the port lock, but we have the HW_BUSY flag.
+		 * Proceed without accessing any nonlocal state.
+		 * Do not exit the loop -- must check for more work.
+		 */
+
+		swidx = rport->swb_i_idx;
+		nb = hw_avail;
+		if (nb > sw_avail)
+			nb = sw_avail;
+		if (nb > hwsize - swptr)
+			nb = hwsize - swptr; /* don't overflow hwbuf */
+		if (nb > swsize - swidx)
+			nb = swsize - swidx; /* don't overflow swbuf */
+		ASSERT(nb > 0);
+		if (nb % fragsize) {
+			DBGP("nb = %d, fragsize = %d\n", nb, fragsize);
+			DBGP("hw_avail = %d\n", hw_avail);
+			DBGP("sw_avail = %d\n", sw_avail);
+			DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr);
+			DBGP("swsize = %d, swidx = %d\n", swsize, swidx);
+		}
+		ASSERT(!(nb % fragsize));
+		DBGPV("copying hwb[%d..%d] to swb[%d..%d]\n",
+		      swptr, swptr + nb, swidx, swidx + nb);
+		pcm_copy_in(rport, swidx, swptr, nb);
+		li_write_swptr(&rport->chan, (swptr + nb) % hwsize);
+		spin_lock_irqsave(&rport->lock, iflags);
+		__swb_inc_i(rport, nb);
+		rport->byte_count += nb;
+		rport->frag_count += nb / fragsize;
+		ASSERT(nb % fragsize == 0);
+		wake_up(&rport->queue);
+	}
+	rport->flags &= ~HW_BUSY;
+	spin_unlock_irqrestore(&rport->lock, iflags);
+	DBGRV();
+}
+
+/*
+ * pcm_flush_frag() writes zero samples to fill the current fragment,
+ * then flushes it to the hardware.
+ *
+ * It is only meaningful to flush output, not input.
+ */
+
+static void pcm_flush_frag(vwsnd_dev_t *devc)
+{
+	vwsnd_port_t *wport = &devc->wport;
+
+	DBGPV("swstate = %d\n", wport->swstate);
+	if (wport->swstate == SW_RUN) {
+		int idx = wport->swb_u_idx;
+		int end = (idx + wport->hw_fragsize - 1)
+			>> wport->hw_fragshift
+			<< wport->hw_fragshift;
+		int nb = end - idx;
+		DBGPV("clearing %d bytes\n", nb);
+		if (nb)
+			memset(wport->swbuf + idx,
+			       (char) wport->zero_word,
+			       nb);
+		wport->swstate = SW_DRAIN;
+		pcm_output(devc, 0, nb);
+	}
+	DBGRV();
+}
+
+/*
+ * Wait for output to drain.  This sleeps uninterruptibly because
+ * there is nothing intelligent we can do if interrupted.  This
+ * means the process will be delayed in responding to the signal.
+ */
+
+static void pcm_write_sync(vwsnd_dev_t *devc)
+{
+	vwsnd_port_t *wport = &devc->wport;
+	struct wait_queue wait = { current, NULL };
+	unsigned long flags;
+	vwsnd_port_hwstate_t hwstate;
+
+	DBGEV("(devc=0x%p)\n", devc);
+	add_wait_queue(&wport->queue, &wait);
+	current->state = TASK_UNINTERRUPTIBLE;
+	while (1) {
+		spin_lock_irqsave(&wport->lock, flags);
+		{
+			hwstate = wport->hwstate;
+		}
+		spin_unlock_irqrestore(&wport->lock, flags);
+		if (hwstate == HW_STOPPED)
+			break;
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&wport->queue, &wait);
+	DBGPV("swstate = %d, hwstate = %d\n", wport->swstate, wport->hwstate);
+	DBGRV();
+}
+
+/*****************************************************************************/
+/* audio driver */
+
+/*
+ * seek on an audio device always fails.
+ */
+
+static void vwsnd_audio_read_intr(vwsnd_dev_t *devc, unsigned int status)
+{
+	int overflown = status & LI_INTR_COMM1_OVERFLOW;
+
+	if (status & READ_INTR_MASK)
+		pcm_input(devc, overflown, 0);
+}
+
+static void vwsnd_audio_write_intr(vwsnd_dev_t *devc, unsigned int status)
+{
+	int underflown = status & LI_INTR_COMM2_UNDERFLOW;
+
+	if (status & WRITE_INTR_MASK)
+		pcm_output(devc, underflown, 0);
+}
+
+static void vwsnd_audio_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) dev_id;
+	unsigned int status;
+
+	DBGEV("(irq=%d, dev_id=0x%p, regs=0x%p)\n", irq, dev_id, regs);
+
+	status = li_get_clear_intr_status(&devc->lith);
+	vwsnd_audio_read_intr(devc, status);
+	vwsnd_audio_write_intr(devc, status);
+}
+
+static loff_t vwsnd_audio_llseek(struct file *file, loff_t offset, int whence)
+{
+	DBGEV("(file=0x%p, offset=%Ld, whence=%d)\n", file, offset, whence);
+	return -ESPIPE;
+}
+
+static ssize_t vwsnd_audio_do_read(struct file *file,
+				   char *buffer,
+				   size_t count,
+				   loff_t *ppos)
+{
+	vwsnd_dev_t *devc = file->private_data;
+	vwsnd_port_t *rport = ((file->f_mode & FMODE_READ) ?
+			       &devc->rport : NULL);
+	int ret, nb;
+
+	DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n",
+	     file, buffer, count, ppos);
+
+	if (!rport)
+		return -EINVAL;
+
+	if (rport->swbuf == NULL) {
+		vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
+			&devc->wport : NULL;
+		ret = pcm_setup(devc, rport, wport);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count) {
+		struct wait_queue wait = { current, NULL };
+		add_wait_queue(&rport->queue, &wait);
+		current->state = TASK_INTERRUPTIBLE;
+		while ((nb = swb_inc_u(rport, 0)) == 0) {
+			DBGPV("blocking\n");
+			if (rport->flags & DISABLED ||
+			    file->f_flags & O_NONBLOCK) {
+				current->state = TASK_RUNNING;
+				remove_wait_queue(&rport->queue, &wait);
+				return ret ? ret : -EAGAIN;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				current->state = TASK_RUNNING;
+				remove_wait_queue(&rport->queue, &wait);
+				return ret ? ret : -ERESTARTSYS;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&rport->queue, &wait);
+		pcm_input(devc, 0, 0);
+		/* nb bytes are available in userbuf. */
+		if (nb > count)
+			nb = count;
+		DBGPV("nb = %d\n", nb);
+		copy_to_user(buffer, rport->swbuf + rport->swb_u_idx, nb);
+		(void) swb_inc_u(rport, nb);
+		buffer += nb;
+		count -= nb;
+		ret += nb;
+	}
+	DBGPV("returning %d\n", ret);
+	return ret;
+}
+
+static ssize_t vwsnd_audio_read(struct file *file,
+				char *buffer,
+				size_t count,
+				loff_t *ppos)
+{
+	vwsnd_dev_t *devc = file->private_data;
+	ssize_t ret;
+
+	down(&devc->io_sema);
+	ret = vwsnd_audio_do_read(file, buffer, count, ppos);
+	up(&devc->io_sema);
+	return ret;
+}
+
+static ssize_t vwsnd_audio_do_write(struct file *file,
+				    const char *buffer,
+				    size_t count,
+				    loff_t *ppos)
+{
+	vwsnd_dev_t *devc = file->private_data;
+	vwsnd_port_t *wport = ((file->f_mode & FMODE_WRITE) ?
+			       &devc->wport : NULL);
+	int ret, nb;
+
+	DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n",
+	      file, buffer, count, ppos);
+
+	if (!wport)
+		return -EINVAL;
+
+	if (wport->swbuf == NULL) {
+		vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
+			&devc->rport : NULL;
+		ret = pcm_setup(devc, rport, wport);
+		if (ret < 0)
+			return ret;
+	}
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count) {
+		struct wait_queue wait = { current, NULL };
+		add_wait_queue(&wport->queue, &wait);
+		current->state = TASK_INTERRUPTIBLE;
+		while ((nb = swb_inc_u(wport, 0)) == 0) {
+			if (wport->flags & DISABLED ||
+			    file->f_flags & O_NONBLOCK) {
+				current->state = TASK_RUNNING;
+				remove_wait_queue(&wport->queue, &wait);
+				return ret ? ret : -EAGAIN;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				current->state = TASK_RUNNING;
+				remove_wait_queue(&wport->queue, &wait);
+				return ret ? ret : -ERESTARTSYS;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&wport->queue, &wait);
+		/* nb bytes are available in userbuf. */
+		if (nb > count)
+			nb = count;
+		DBGPV("nb = %d\n", nb);
+		copy_from_user(wport->swbuf + wport->swb_u_idx, buffer, nb);
+		pcm_output(devc, 0, nb);
+		buffer += nb;
+		count -= nb;
+		ret += nb;
+	}
+	DBGPV("returning %d\n", ret);
+	return ret;
+}
+
+static ssize_t vwsnd_audio_write(struct file *file,
+				 const char *buffer,
+				 size_t count,
+				 loff_t *ppos)
+{
+	vwsnd_dev_t *devc = file->private_data;
+	ssize_t ret;
+
+	down(&devc->io_sema);
+	ret = vwsnd_audio_do_write(file, buffer, count, ppos);
+	up(&devc->io_sema);
+	return ret;
+}
+
+static unsigned int vwsnd_audio_poll(struct file *file,
+				     struct poll_table_struct *wait)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
+		&devc->rport : NULL;
+	vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
+		&devc->wport : NULL;
+	unsigned int mask = 0;
+
+	DBGEV("(file=0x%p, wait=0x%p)\n", file, wait);
+
+	ASSERT(rport || wport);
+	if (rport) {
+		poll_wait(file, &rport->queue, wait);
+		if (swb_inc_u(rport, 0))
+			mask |= (POLLIN | POLLRDNORM);
+	}
+	if (wport) {
+		poll_wait(file, &wport->queue, wait);
+		if (wport->swbuf == NULL || swb_inc_u(wport, 0))
+			mask |= (POLLOUT | POLLWRNORM);
+	}
+
+	DBGPV("returning 0x%x\n", mask);
+	return mask;
+}
+
+static int vwsnd_audio_do_ioctl(struct inode *inode,
+				struct file *file,
+				unsigned int cmd,
+				unsigned long arg)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
+		&devc->rport : NULL;
+	vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
+		&devc->wport : NULL;
+	vwsnd_port_t *aport = rport ? rport : wport;
+	struct audio_buf_info buf_info;
+	struct count_info info;
+	unsigned long flags;
+	int ival;
+
+	
+	DBGEV("(inode=0x%p, file=0x%p, cmd=0x%x, arg=0x%lx)\n",
+	      inode, file, cmd, arg);
+	switch (cmd) {
+	case OSS_GETVERSION:		/* _SIOR ('M', 118, int) */
+		DBGX("OSS_GETVERSION\n");
+		ival = SOUND_VERSION;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_GETCAPS:	/* _SIOR ('P',15, int) */
+		DBGX("SNDCTL_DSP_GETCAPS\n");
+		ival = DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_GETFMTS:	/* _SIOR ('P',11, int) */
+		DBGX("SNDCTL_DSP_GETFMTS\n");
+		ival = (AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW |
+			AFMT_U8 | AFMT_S8);
+		return put_user(ival, (int *) arg);
+		break;
+
+	case SOUND_PCM_READ_RATE:	/* _SIOR ('P', 2, int) */
+		DBGX("SOUND_PCM_READ_RATE\n");
+		ival = aport->sw_framerate;
+		return put_user(ival, (int *) arg);
+
+	case SOUND_PCM_READ_CHANNELS:	/* _SIOR ('P', 6, int) */
+		DBGX("SOUND_PCM_READ_CHANNELS\n");
+		ival = aport->sw_channels;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_SPEED:		/* _SIOWR('P', 2, int) */
+		get_user_ret(ival, (int *) arg, -EFAULT);
+		DBGX("SNDCTL_DSP_SPEED %d\n", ival);
+		if (ival) {
+			if (aport->swstate != SW_INITIAL) {
+				DBGX("SNDCTL_DSP_SPEED failed: swstate = %d\n",
+				     aport->swstate);
+				return -EINVAL;
+			}
+			if (ival < MIN_SPEED)
+				ival = MIN_SPEED;
+			if (ival > MAX_SPEED)
+				ival = MAX_SPEED;
+			if (rport)
+				rport->sw_framerate = ival;
+			if (wport)
+				wport->sw_framerate = ival;
+		} else
+			ival = aport->sw_framerate;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_STEREO:		/* _SIOWR('P', 3, int) */
+		get_user_ret(ival, (int *) arg, -EFAULT);
+		DBGX("SNDCTL_DSP_STEREO %d\n", ival);
+		if (ival != 0 && ival != 1)
+			return -EINVAL;
+		if (aport->swstate != SW_INITIAL)
+			return -EINVAL;
+		if (rport)
+			rport->sw_channels = ival + 1;
+		if (wport)
+			wport->sw_channels = ival + 1;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_CHANNELS:	/* _SIOWR('P', 6, int) */
+		get_user_ret(ival, (int *) arg, -EFAULT);
+		DBGX("SNDCTL_DSP_CHANNELS %d\n", ival);
+		if (ival != 1 && ival != 2)
+			return -EINVAL;
+		if (aport->swstate != SW_INITIAL)
+			return -EINVAL;
+		if (rport)
+			rport->sw_channels = ival;
+		if (wport)
+			wport->sw_channels = ival;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_GETBLKSIZE:	/* _SIOWR('P', 4, int) */
+		ival = pcm_setup(devc, rport, wport);
+		if (ival < 0) {
+			DBGX("SNDCTL_DSP_GETBLKSIZE failed, errno %d\n", ival);
+			return ival;
+		}
+		ival = 1 << aport->sw_fragshift;
+		DBGX("SNDCTL_DSP_GETBLKSIZE returning %d\n", ival);
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_SETFRAGMENT:	/* _SIOWR('P',10, int) */
+		get_user_ret(ival, (int *) arg, -EFAULT);
+		DBGX("SNDCTL_DSP_SETFRAGMENT %d:%d\n",
+		     ival >> 16, ival & 0xFFFF);
+		if (aport->swstate != SW_INITIAL)
+			return -EINVAL;
+		{
+			int sw_fragshift = ival & 0xFFFF;
+			int sw_subdivshift = aport->sw_subdivshift;
+			int hw_fragshift = sw_fragshift - sw_subdivshift;
+			int sw_fragcount = (ival >> 16) & 0xFFFF;
+			int hw_fragsize;
+			if (hw_fragshift < MIN_FRAGSHIFT)
+				hw_fragshift = MIN_FRAGSHIFT;
+			if (hw_fragshift > MAX_FRAGSHIFT)
+				hw_fragshift = MAX_FRAGSHIFT;
+			sw_fragshift = hw_fragshift + aport->sw_subdivshift;
+			hw_fragsize = 1 << hw_fragshift;
+			if (sw_fragcount < MIN_FRAGCOUNT(hw_fragsize))
+				sw_fragcount = MIN_FRAGCOUNT(hw_fragsize);
+			if (sw_fragcount > MAX_FRAGCOUNT(hw_fragsize))
+				sw_fragcount = MAX_FRAGCOUNT(hw_fragsize);
+			DBGPV("sw_fragshift = %d\n", sw_fragshift);
+			DBGPV("rport = 0x%p, wport = 0x%p\n", rport, wport);
+			if (rport) {
+				rport->sw_fragshift = sw_fragshift;
+				rport->sw_fragcount = sw_fragcount;
+			}
+			if (wport) {
+				wport->sw_fragshift = sw_fragshift;
+				wport->sw_fragcount = sw_fragcount;
+			}
+			ival = sw_fragcount << 16 | sw_fragshift;
+		}
+		DBGX("SNDCTL_DSP_SETFRAGMENT returns %d:%d\n",
+		      ival >> 16, ival & 0xFFFF);
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_SUBDIVIDE:	/* _SIOWR('P', 9, int) */
+                get_user_ret(ival, (int *) arg, -EFAULT);
+		DBGX("SNDCTL_DSP_SUBDIVIDE %d\n", ival);
+		if (aport->swstate != SW_INITIAL)
+			return -EINVAL;
+		{
+			int subdivshift;
+			int hw_fragshift, hw_fragsize, hw_fragcount;
+			switch (ival) {
+			case 1: subdivshift = 0; break;
+			case 2: subdivshift = 1; break;
+			case 4: subdivshift = 2; break;
+			default: return -EINVAL;
+			}
+			hw_fragshift = aport->sw_fragshift - subdivshift;
+			if (hw_fragshift < MIN_FRAGSHIFT ||
+			    hw_fragshift > MAX_FRAGSHIFT)
+				return -EINVAL;
+			hw_fragsize = 1 << hw_fragshift;
+			hw_fragcount = aport->sw_fragcount >> subdivshift;
+			if (hw_fragcount < MIN_FRAGCOUNT(hw_fragsize) ||
+			    hw_fragcount > MAX_FRAGCOUNT(hw_fragsize))
+				return -EINVAL;
+			if (rport)
+				rport->sw_subdivshift = subdivshift;
+			if (wport)
+				wport->sw_subdivshift = subdivshift;
+		}
+		return 0;
+
+	case SNDCTL_DSP_SETFMT:		/* _SIOWR('P',5, int) */
+		get_user_ret(ival, (int *) arg, -EFAULT);
+		DBGX("SNDCTL_DSP_SETFMT %d\n", ival);
+		if (ival != AFMT_QUERY) {
+			if (aport->swstate != SW_INITIAL) {
+				DBGP("SETFMT failed, swstate = %d\n",
+				     aport->swstate);
+				return -EINVAL;
+			}
+			switch (ival) {
+			case AFMT_MU_LAW:
+			case AFMT_A_LAW:
+			case AFMT_U8:
+			case AFMT_S8:
+			case AFMT_S16_LE:
+				if (rport)
+					rport->sw_samplefmt = ival;
+				if (wport)
+					wport->sw_samplefmt = ival;
+				break;
+			default:
+				return -EINVAL;
+			}
+		}
+		ival = aport->sw_samplefmt;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_GETOSPACE:	/* _SIOR ('P',12, audio_buf_info) */
+		DBGXV("SNDCTL_DSP_GETOSPACE\n");
+		if (!wport)
+			return -EINVAL;
+		ival = pcm_setup(devc, rport, wport);
+		if (ival < 0)
+			return ival;
+		ival = swb_inc_u(wport, 0);
+		buf_info.fragments = ival >> wport->sw_fragshift;
+		buf_info.fragstotal = wport->sw_fragcount;
+		buf_info.fragsize = 1 << wport->sw_fragshift;
+		buf_info.bytes = ival;
+		DBGXV("SNDCTL_DSP_GETOSPACE returns { %d %d %d %d }\n",
+		     buf_info.fragments, buf_info.fragstotal,
+		     buf_info.fragsize, buf_info.bytes);
+		return copy_to_user((void *) arg, &buf_info, sizeof buf_info);
+
+	case SNDCTL_DSP_GETISPACE:	/* _SIOR ('P',13, audio_buf_info) */
+		DBGX("SNDCTL_DSP_GETISPACE\n");
+		if (!rport)
+			return -EINVAL;
+		ival = pcm_setup(devc, rport, wport);
+		if (ival < 0)
+			return ival;
+		ival = swb_inc_u(rport, 0);
+		buf_info.fragments = ival >> rport->sw_fragshift;
+		buf_info.fragstotal = rport->sw_fragcount;
+		buf_info.fragsize = 1 << rport->sw_fragshift;
+		buf_info.bytes = ival;
+		DBGX("SNDCTL_DSP_GETISPACE returns { %d %d %d %d }\n",
+		     buf_info.fragments, buf_info.fragstotal,
+		     buf_info.fragsize, buf_info.bytes);
+		return copy_to_user((void *) arg, &buf_info, sizeof buf_info);
+
+	case SNDCTL_DSP_NONBLOCK:	/* _SIO  ('P',14) */
+		DBGX("SNDCTL_DSP_NONBLOCK\n");
+		file->f_flags |= O_NONBLOCK;
+		return 0;
+
+	case SNDCTL_DSP_RESET:		/* _SIO  ('P', 0) */
+		DBGX("SNDCTL_DSP_RESET\n");
+		/*
+		 * Nothing special needs to be done for input.  Input
+		 * samples sit in swbuf, but it will be reinitialized
+		 * to empty when pcm_setup() is called.
+		 */
+		if (wport && wport->swbuf) {
+			wport->swstate = SW_INITIAL;
+			pcm_output(devc, 0, 0);
+			pcm_write_sync(devc);
+		}
+		pcm_shutdown(devc, rport, wport);
+		return 0;
+
+	case SNDCTL_DSP_SYNC:		/* _SIO  ('P', 1) */
+		DBGX("SNDCTL_DSP_SYNC\n");
+		if (wport) {
+			pcm_flush_frag(devc);
+			pcm_write_sync(devc);
+		}
+		pcm_shutdown(devc, rport, wport);
+		return 0;
+
+	case SNDCTL_DSP_POST:		/* _SIO  ('P', 8) */
+		DBGX("SNDCTL_DSP_POST\n");
+		if (!wport)
+			return -EINVAL;
+		pcm_flush_frag(devc);
+		return 0;
+
+	case SNDCTL_DSP_GETIPTR:	/* _SIOR ('P', 17, count_info) */
+		DBGX("SNDCTL_DSP_GETIPTR\n");
+		if (!rport)
+			return -EINVAL;
+		spin_lock_irqsave(&rport->lock, flags);
+		{
+			ustmsc_t ustmsc;
+			if (rport->hwstate == HW_RUNNING) {
+				ASSERT(rport->swstate == SW_RUN);
+				li_read_USTMSC(&rport->chan, &ustmsc);
+				info.bytes = ustmsc.msc - rport->MSC_offset;
+				info.bytes *= rport->frame_size;
+			} else {
+				info.bytes = rport->byte_count;
+			}
+			info.blocks = rport->frag_count;
+			info.ptr = 0;	/* not implemented */
+			rport->frag_count = 0;
+		}
+		spin_unlock_irqrestore(&rport->lock, flags);
+		return copy_to_user((void *) arg, &info, sizeof info);
+
+	case SNDCTL_DSP_GETOPTR:	/* _SIOR ('P',18, count_info) */
+		DBGX("SNDCTL_DSP_GETOPTR\n");
+		if (!wport)
+			return -EINVAL;
+		spin_lock_irqsave(&wport->lock, flags);
+		{
+			ustmsc_t ustmsc;
+			if (wport->hwstate == HW_RUNNING) {
+				ASSERT(wport->swstate == SW_RUN);
+				li_read_USTMSC(&wport->chan, &ustmsc);
+				info.bytes = ustmsc.msc - wport->MSC_offset;
+				info.bytes *= wport->frame_size;
+			} else {
+				info.bytes = wport->byte_count;
+			}
+			info.blocks = wport->frag_count;
+			info.ptr = 0;	/* not implemented */
+			wport->frag_count = 0;
+		}
+		spin_unlock_irqrestore(&wport->lock, flags);
+		return copy_to_user((void *) arg, &info, sizeof info);
+
+	case SNDCTL_DSP_GETODELAY:	/* _SIOR ('P', 23, int) */
+		DBGX("SNDCTL_DSP_GETODELAY\n");
+		if (!wport)
+			return -EINVAL;
+		spin_lock_irqsave(&wport->lock, flags);
+		{
+			int fsize = wport->frame_size;
+			ival = wport->swb_i_avail / fsize;
+			if (wport->hwstate == HW_RUNNING) {
+				int swptr, hwptr, hwframes, hwbytes, hwsize;
+				int totalhwbytes;
+				ustmsc_t ustmsc;
+
+				hwsize = wport->hwbuf_size;
+				swptr = li_read_swptr(&wport->chan);
+				li_read_USTMSC(&wport->chan, &ustmsc);
+				hwframes = ustmsc.msc - wport->MSC_offset;
+				totalhwbytes = hwframes * fsize;
+				hwptr = totalhwbytes % hwsize;
+				hwbytes = (swptr - hwptr + hwsize) % hwsize;
+				ival += hwbytes / fsize;
+			}
+		}
+		spin_unlock_irqrestore(&wport->lock, flags);
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_PROFILE:	/* _SIOW ('P', 23, int) */
+		DBGX("SNDCTL_DSP_PROFILE\n");
+
+		/*
+		 * Thomas Sailer explains SNDCTL_DSP_PROFILE
+		 * (private email, March 24, 1999):
+		 *
+		 *     This gives the sound driver a hint on what it
+		 *     should do with partial fragments
+		 *     (i.e. fragments partially filled with write).
+		 *     This can direct the driver to zero them or
+		 *     leave them alone.  But don't ask me what this
+		 *     is good for, my driver just zeroes the last
+		 *     fragment before the receiver stops, no idea
+		 *     what good for any other behaviour could
+		 *     be. Implementing it as NOP seems safe.
+		 */
+
+		break;
+
+	case SNDCTL_DSP_GETTRIGGER:	/* _SIOR ('P',16, int) */
+		DBGX("SNDCTL_DSP_GETTRIGGER\n");
+		ival = 0;
+		if (rport) {
+			spin_lock_irqsave(&rport->lock, flags);
+			{
+				if (!(rport->flags & DISABLED))
+					ival |= PCM_ENABLE_INPUT;
+			}
+			spin_unlock_irqrestore(&rport->lock, flags);
+		}
+		if (wport) {
+			spin_lock_irqsave(&wport->lock, flags);
+			{
+				if (!(wport->flags & DISABLED))
+					ival |= PCM_ENABLE_OUTPUT;
+			}
+			spin_unlock_irqrestore(&wport->lock, flags);
+		}
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_SETTRIGGER:	/* _SIOW ('P',16, int) */
+		get_user_ret(ival, (int *) arg, -EFAULT);
+		DBGX("SNDCTL_DSP_SETTRIGGER %d\n", ival);
+
+		/*
+		 * If user is disabling I/O and port is not in initial
+		 * state, fail with EINVAL.
+		 */
+
+		if (((rport && !(ival & PCM_ENABLE_INPUT)) ||
+		     (wport && !(ival & PCM_ENABLE_OUTPUT))) &&
+		    aport->swstate != SW_INITIAL)
+			return -EINVAL;
+
+		if (rport) {
+			vwsnd_port_hwstate_t hwstate;
+			spin_lock_irqsave(&rport->lock, flags);
+			{
+				hwstate = rport->hwstate;
+				if (ival & PCM_ENABLE_INPUT)
+					rport->flags &= ~DISABLED;
+				else
+					rport->flags |= DISABLED;
+			}
+			spin_unlock_irqrestore(&rport->lock, flags);
+			if (hwstate != HW_RUNNING && ival & PCM_ENABLE_INPUT) {
+
+				if (rport->swstate == SW_INITIAL)
+					pcm_setup(devc, rport, wport);
+				else
+					li_activate_dma(&rport->chan);
+			}
+		}
+		if (wport) {
+			vwsnd_port_flags_t pflags;
+			spin_lock_irqsave(&wport->lock, flags);
+			{
+				pflags = wport->flags;
+				if (ival & PCM_ENABLE_OUTPUT)
+					wport->flags &= ~DISABLED;
+				else
+					wport->flags |= DISABLED;
+			}
+			spin_unlock_irqrestore(&wport->lock, flags);
+			if (pflags & DISABLED && ival & PCM_ENABLE_OUTPUT) {
+				if (wport->swstate == SW_RUN)
+					pcm_output(devc, 0, 0);
+			}
+		}
+		return 0;
+
+	default:
+		DBGP("unknown ioctl 0x%x\n", cmd);
+		return -EINVAL;
+	}
+	DBGP("unimplemented ioctl 0x%x\n", cmd);
+	return -EINVAL;
+}
+
+static int vwsnd_audio_ioctl(struct inode *inode,
+				struct file *file,
+				unsigned int cmd,
+				unsigned long arg)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	int ret;
+
+	down(&devc->io_sema);
+	ret = vwsnd_audio_do_ioctl(inode, file, cmd, arg);
+	up(&devc->io_sema);
+	return ret;
+}
+
+/* No mmap. */
+
+static int vwsnd_audio_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	DBGE("(file=0x%p, vma=0x%p)\n", file, vma);
+	return -ENODEV;
+}
+
+/*
+ * Open the audio device for read and/or write.
+ *
+ * Returns 0 on success, -errno on failure.
+ */
+
+static int vwsnd_audio_open(struct inode *inode, struct file *file)
+{
+	vwsnd_dev_t *devc;
+	dev_t minor = MINOR(inode->i_rdev);
+	int sw_samplefmt;
+
+	DBGE("(inode=0x%p, file=0x%p)\n", inode, file);
+
+	INC_USE_COUNT;
+	for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
+		if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F))
+			break;
+
+	if (devc == NULL) {
+		DEC_USE_COUNT;
+		return -ENODEV;
+	}
+
+	down(&devc->open_sema);
+	while (devc->open_mode & file->f_mode) {
+		up(&devc->open_sema);
+		if (file->f_flags & O_NONBLOCK) {
+			DEC_USE_COUNT;
+			return -EBUSY;
+		}
+		interruptible_sleep_on(&devc->open_wait);
+		if (signal_pending(current)) {
+			DEC_USE_COUNT;
+			return -ERESTARTSYS;
+		}
+		down(&devc->open_sema);
+	}
+	devc->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	up(&devc->open_sema);
+
+	/* get default sample format from minor number. */
+
+	sw_samplefmt = 0;
+	if ((minor & 0xF) == SND_DEV_DSP)
+		sw_samplefmt = AFMT_U8;
+	else if ((minor & 0xF) == SND_DEV_AUDIO)
+		sw_samplefmt = AFMT_MU_LAW;
+	else if ((minor & 0xF) == SND_DEV_DSP16)
+		sw_samplefmt = AFMT_S16_LE;
+	else
+		ASSERT(0);
+
+	/* Initialize vwsnd_ports. */
+
+	down(&devc->io_sema);
+	{
+		if (file->f_mode & FMODE_READ) {
+			devc->rport.swstate        = SW_INITIAL;
+			devc->rport.flags          = 0;
+			devc->rport.sw_channels    = 1;
+			devc->rport.sw_samplefmt   = sw_samplefmt;
+			devc->rport.sw_framerate   = 8000;
+			devc->rport.sw_fragshift   = DEFAULT_FRAGSHIFT;
+			devc->rport.sw_fragcount   = DEFAULT_FRAGCOUNT;
+			devc->rport.sw_subdivshift = DEFAULT_SUBDIVSHIFT;
+			devc->rport.byte_count     = 0;
+			devc->rport.frag_count     = 0;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			devc->wport.swstate        = SW_INITIAL;
+			devc->wport.flags          = 0;
+			devc->wport.sw_channels    = 1;
+			devc->wport.sw_samplefmt   = sw_samplefmt;
+			devc->wport.sw_framerate   = 8000;
+			devc->wport.sw_fragshift   = DEFAULT_FRAGSHIFT;
+			devc->wport.sw_fragcount   = DEFAULT_FRAGCOUNT;
+			devc->wport.sw_subdivshift = DEFAULT_SUBDIVSHIFT;
+			devc->wport.byte_count     = 0;
+			devc->wport.frag_count     = 0;
+		}
+	}
+	up(&devc->io_sema);
+
+	file->private_data = devc;
+	DBGRV();
+	return 0;
+}
+
+/*
+ * Release (close) the audio device.
+ */
+
+static int vwsnd_audio_release(struct inode *inode, struct file *file)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	vwsnd_port_t *wport = NULL, *rport = NULL;
+	int err = 0;
+
+	down(&devc->io_sema);
+	{
+		DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
+
+		if (file->f_mode & FMODE_READ)
+			rport = &devc->rport;
+		if (file->f_mode & FMODE_WRITE) {
+			wport = &devc->wport;
+			pcm_flush_frag(devc);
+			pcm_write_sync(devc);
+		}
+		pcm_shutdown(devc, rport, wport);
+		if (rport)
+			rport->swstate = SW_OFF;
+		if (wport)
+			wport->swstate = SW_OFF;
+	}
+	up(&devc->io_sema);
+
+	down(&devc->open_sema);
+	{
+		devc->open_mode &= ~file->f_mode;
+	}
+	up(&devc->open_sema);
+	wake_up(&devc->open_wait);
+	DBGDO(if (IN_USE))		/* see hack in vwsnd_mixer_release() */
+		DEC_USE_COUNT;
+	DBGR();
+	return err;
+}
+
+static struct file_operations vwsnd_audio_fops = {
+	&vwsnd_audio_llseek,
+	&vwsnd_audio_read,
+	&vwsnd_audio_write,
+	NULL,				/* readdir */
+	&vwsnd_audio_poll,
+	&vwsnd_audio_ioctl,
+	&vwsnd_audio_mmap,
+	&vwsnd_audio_open,
+	NULL,				/* flush */
+	&vwsnd_audio_release,
+	NULL,				/* fsync */
+	NULL,				/* fasync */
+	NULL,				/* check_media_change */
+	NULL,				/* revalidate */
+	NULL,				/* lock */
+};
+
+/*****************************************************************************/
+/* mixer driver */
+
+/* open the mixer device. */
+
+static int vwsnd_mixer_open(struct inode *inode, struct file *file)
+{
+	vwsnd_dev_t *devc;
+
+	DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
+
+	INC_USE_COUNT;
+	for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
+		if (devc->mixer_minor == MINOR(inode->i_rdev))
+			break;
+
+	if (devc == NULL) {
+		DEC_USE_COUNT;
+		return -ENODEV;
+	}
+	file->private_data = devc;
+	return 0;
+}
+
+/* release (close) the mixer device. */
+
+static int vwsnd_mixer_release(struct inode *inode, struct file *file)
+{
+	DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
+
+	/*
+	 * hack -- opening/closing the mixer device zeroes use count
+	 * so driver can be unloaded.
+	 * Use only while debugging module, and then use it carefully.
+	 */
+
+	DBGDO(while (IN_USE))
+		DEC_USE_COUNT;
+	return 0;
+}
+
+/* seek is illegal on mixer. */
+
+static loff_t vwsnd_mixer_llseek(struct file *file, loff_t offset, int whence)
+{
+	return -ESPIPE;
+}
+
+/* mixer_read_ioctl handles all read ioctls on the mixer device. */
+
+static int mixer_read_ioctl(vwsnd_dev_t *devc, unsigned int nr, caddr_t arg)
+{
+	int val = -1;
+
+	DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg);
+
+	switch (nr) {
+	case SOUND_MIXER_CAPS:
+		val = SOUND_CAP_EXCL_INPUT;
+		break;
+
+	case SOUND_MIXER_DEVMASK:
+		val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
+		       SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV);
+		break;
+
+	case SOUND_MIXER_STEREODEVS:
+		val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
+		       SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV);
+		break;
+
+	case SOUND_MIXER_OUTMASK:
+		val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
+		       SOUND_MASK_MIC | SOUND_MASK_CD);
+		break;
+
+	case SOUND_MIXER_RECMASK:
+		val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
+		       SOUND_MASK_MIC | SOUND_MASK_CD);
+		break;
+
+	case SOUND_MIXER_PCM:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_PCM);
+		break;
+
+	case SOUND_MIXER_LINE:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_LINE);
+		break;
+
+	case SOUND_MIXER_MIC:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_MIC);
+		break;
+
+	case SOUND_MIXER_CD:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_CD);
+		break;
+
+	case SOUND_MIXER_RECLEV:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_RECLEV);
+		break;
+
+	case SOUND_MIXER_RECSRC:
+		val = ad1843_get_recsrc(&devc->lith);
+		break;
+
+	case SOUND_MIXER_OUTSRC:
+		val = ad1843_get_outsrc(&devc->lith);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	return put_user(val, (int *) arg);
+}
+
+/* mixer_write_ioctl handles all write ioctls on the mixer device. */
+
+static int mixer_write_ioctl(vwsnd_dev_t *devc, unsigned int nr, caddr_t arg)
+{
+	int val;
+	int err;
+
+	DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg);
+
+	err = get_user(val, (int *) arg);
+	if (err)
+		return -EFAULT;
+	switch (nr) {
+	case SOUND_MIXER_PCM:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_PCM, val);
+		break;
+
+	case SOUND_MIXER_LINE:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_LINE, val);
+		break;
+
+	case SOUND_MIXER_MIC:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_MIC, val);
+		break;
+
+	case SOUND_MIXER_CD:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_CD, val);
+		break;
+
+	case SOUND_MIXER_RECLEV:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_RECLEV, val);
+		break;
+
+	case SOUND_MIXER_RECSRC:
+		if (devc->rport.swbuf || devc->wport.swbuf)
+			return -EBUSY;	/* can't change recsrc while running */
+		val = ad1843_set_recsrc(&devc->lith, val);
+		break;
+
+	case SOUND_MIXER_OUTSRC:
+		val = ad1843_set_outsrc(&devc->lith, val);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	if (val < 0)
+		return val;
+	return put_user(val, (int *) arg);
+}
+
+/* This is the ioctl entry to the mixer driver. */
+
+static int vwsnd_mixer_ioctl(struct inode *ioctl,
+			      struct file *file,
+			      unsigned int cmd,
+			      unsigned long arg)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	const unsigned int nrmask = _IOC_NRMASK << _IOC_NRSHIFT;
+	const unsigned int nr = (cmd & nrmask) >> _IOC_NRSHIFT;
+	int retval;
+
+	DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg);
+
+	down(&devc->mix_sema);
+	{
+		if ((cmd & ~nrmask) == MIXER_READ(0))
+			retval = mixer_read_ioctl(devc, nr, (caddr_t) arg);
+		else if ((cmd & ~nrmask) == MIXER_WRITE(0))
+			retval = mixer_write_ioctl(devc, nr, (caddr_t) arg);
+		else
+			retval = -EINVAL;
+	}
+	up(&devc->mix_sema);
+	return retval;
+}
+
+static struct file_operations vwsnd_mixer_fops = {
+	&vwsnd_mixer_llseek,
+	NULL,				/* read */
+	NULL,				/* write */
+	NULL,				/* readdir */
+	NULL,				/* poll */
+	&vwsnd_mixer_ioctl,
+	NULL,				/* mmap */
+	&vwsnd_mixer_open,
+	NULL,				/* flush */
+	&vwsnd_mixer_release,
+	NULL,				/* fsync */
+	NULL,				/* fasync */
+	NULL,				/* check_media_change */
+	NULL,				/* revalidate */
+	NULL,				/* lock */
+};
+
+/*****************************************************************************/
+/* probe/attach/unload */
+
+/* driver probe routine.  Return nonzero if hardware is found. */
+
+static int probe_vwsnd(struct address_info *hw_config)
+{
+	lithium_t lith;
+	int w;
+	unsigned long later;
+
+	DBGEV("(hw_config=0x%p)\n", hw_config);
+
+	/* XXX verify lithium present (to prevent crash on non-vw) */
+
+	if (li_create(&lith, hw_config->io_base) != 0) {
+		printk(KERN_WARNING "probe_vwsnd: can't map lithium\n");
+		return 0;
+	}
+	later = jiffies + 2;
+	li_writel(&lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE);
+	do {
+		w = li_readl(&lith, LI_HOST_CONTROLLER);
+	} while (w == LI_HC_LINK_ENABLE && jiffies < later);
+	
+	li_destroy(&lith);
+
+	DBGPV("HC = 0x%04x\n", w);
+
+	if ((w == LI_HC_LINK_ENABLE) || (w & LI_HC_LINK_CODEC)) {
+
+		/* This may indicate a beta machine with no audio,
+		 * or a future machine with different audio.
+		 * On beta-release 320 w/ no audio, HC == 0x4000 */
+
+		printk(KERN_WARNING "probe_vwsnd: audio codec not found\n");
+		return 0;
+	}
+
+	if (w & LI_HC_LINK_FAILURE) {
+		printk(KERN_WARNING "probe_vwsnd: can't init audio codec\n");
+		return 0;
+	}
+
+	printk(KERN_INFO "probe_vwsnd: lithium audio found\n");
+
+	return 1;
+}
+
+/*
+ * driver attach routine.  Initialize driver data structures and
+ * initialize hardware.  A new vwsnd_dev_t is allocated and put
+ * onto the global list, vwsnd_dev_list.
+ *
+ * Return +minor_dev on success, -errno on failure.
+ */
+
+static int attach_vwsnd(struct address_info *hw_config)
+{
+	vwsnd_dev_t *devc = NULL;
+	int err = -ENOMEM;
+
+	DBGEV("(hw_config=0x%p)\n", hw_config);
+
+	devc = kmalloc(sizeof (vwsnd_dev_t), GFP_KERNEL);
+	if (devc == NULL)
+		goto fail0;
+
+	err = li_create(&devc->lith, hw_config->io_base);
+	if (err)
+		goto fail1;
+
+	init_waitqueue(&devc->open_wait);
+
+	devc->rport.hwbuf_size = HWBUF_SIZE;
+	devc->rport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER);
+	if (!devc->rport.hwbuf_vaddr)
+		goto fail2;
+	devc->rport.hwbuf = (caddr_t) devc->rport.hwbuf_vaddr;
+	devc->rport.hwbuf_paddr = virt_to_phys(devc->rport.hwbuf);
+
+	/*
+	 * Quote from the NT driver:
+	 *
+	 * // WARNING!!! HACK to setup output dma!!!
+	 * // This is required because even on output there is some data
+	 * // trickling into the input DMA channel.  This is a bug in the
+	 * // Lithium microcode.
+	 * // --sde
+	 *
+	 * We set the input side's DMA base address here.  It will remain
+	 * valid until the driver is unloaded.
+	 */
+
+	li_writel(&devc->lith, LI_COMM1_BASE,
+		  devc->rport.hwbuf_paddr >> 8 | 1 << (37 - 8));
+
+	devc->wport.hwbuf_size = HWBUF_SIZE;
+	devc->wport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER);
+	if (!devc->wport.hwbuf_vaddr)
+		goto fail3;
+	devc->wport.hwbuf = (caddr_t) devc->wport.hwbuf_vaddr;
+	devc->wport.hwbuf_paddr = virt_to_phys(devc->wport.hwbuf);
+	DBGP("wport hwbuf = 0x%p\n", devc->wport.hwbuf);
+
+	DBGDO(shut_up++);
+	err = ad1843_init(&devc->lith);
+	DBGDO(shut_up--);
+	if (err)
+		goto fail4;
+
+	/* install interrupt handler */
+
+	err = request_irq(hw_config->irq, vwsnd_audio_intr, 0, "vwsnd", devc);
+	if (err)
+		goto fail5;
+
+	/* register this device's drivers. */
+
+	devc->audio_minor = register_sound_dsp(&vwsnd_audio_fops, -1);
+	if ((err = devc->audio_minor) < 0) {
+		DBGDO(printk(KERN_WARNING
+			     "attach_vwsnd: register_sound_dsp error %d\n",
+			     err));
+		goto fail6;
+	}
+	devc->mixer_minor = register_sound_mixer(&vwsnd_mixer_fops,
+						 devc->audio_minor >> 4);
+	if ((err = devc->mixer_minor) < 0) {
+		DBGDO(printk(KERN_WARNING
+			     "attach_vwsnd: register_sound_mixer error %d\n",
+			     err));
+		goto fail7;
+	}
+
+	/* Squirrel away device indices for unload routine. */
+
+	hw_config->slots[0] = devc->audio_minor;
+
+	/* Initialize as much of *devc as possible */
+
+	devc->open_sema = MUTEX;
+	devc->io_sema = MUTEX;
+	devc->mix_sema = MUTEX;
+	devc->open_mode = 0;
+	devc->rport.lock = SPIN_LOCK_UNLOCKED;
+	init_waitqueue(&devc->rport.queue);
+	devc->rport.swstate = SW_OFF;
+	devc->rport.hwstate = HW_STOPPED;
+	devc->rport.flags = 0;
+	devc->rport.swbuf = NULL;
+	devc->wport.lock = SPIN_LOCK_UNLOCKED;
+	init_waitqueue(&devc->wport.queue);
+	devc->wport.swstate = SW_OFF;
+	devc->wport.hwstate = HW_STOPPED;
+	devc->wport.flags = 0;
+	devc->wport.swbuf = NULL;
+
+	/* Success.  Link us onto the local device list. */
+
+	devc->next_dev = vwsnd_dev_list;
+	vwsnd_dev_list = devc;
+	return devc->audio_minor;
+
+	/* So many ways to fail.  Undo what we did. */
+
+ fail7:
+	unregister_sound_dsp(devc->audio_minor);
+ fail6:
+	free_irq(hw_config->irq, devc);
+ fail5:
+ fail4:
+	free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER);
+ fail3:
+	free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER);
+ fail2:
+	li_destroy(&devc->lith);
+ fail1:
+	kfree(devc);
+ fail0:
+	return err;
+}
+
+static int unload_vwsnd(struct address_info *hw_config)
+{
+	vwsnd_dev_t *devc, **devcp;
+
+	DBGE("()\n");
+
+	if (IN_USE)
+		return -EBUSY;
+	devcp = &vwsnd_dev_list;
+	while ((devc = *devcp)) {
+		if (devc->audio_minor == hw_config->slots[0]) {
+			*devcp = devc->next_dev;
+			break;
+		}
+		devcp = &devc->next_dev;
+	}
+
+	if (!devc)
+		return -ENODEV;
+
+	unregister_sound_mixer(devc->mixer_minor);
+	unregister_sound_dsp(devc->audio_minor);
+	free_irq(hw_config->irq, devc);
+	free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER);
+	free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER);
+	li_destroy(&devc->lith);
+	kfree(devc);
+
+	return 0;
+}
+
+/*****************************************************************************/
+/* initialization and loadable kernel module interface */
+
+static struct address_info the_hw_config = {
+	0xFF001000,			/* lithium phys addr  */
+	CO_IRQ(CO_APIC_LI_AUDIO)	/* irq */
+};
+
+#ifdef MODULE
+
+MODULE_DESCRIPTION("SGI Visual Workstation sound module");
+MODULE_AUTHOR("Bob Miller <kbob@sgi.com>");
+
+extern int init_module(void)
+{
+	int err;
+
+	DBGXV("\n");
+	DBGXV("sound::vwsnd::init_module()\n");
+
+	if(!probe_vwsnd(&the_hw_config))
+		return -ENODEV;
+	err = attach_vwsnd(&the_hw_config);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+extern void cleanup_module(void)
+{
+	DBGX("sound::vwsnd::cleanup_module()\n");
+
+	unload_vwsnd(&the_hw_config);
+}
+
+#else
+
+extern void init_vwsnd(void)
+{
+	DBGX("sound::vwsnd::init_vwsnd()\n");
+	if (probe_vwsnd(&the_hw_config))
+		(void) attach_vwsnd(&the_hw_config);
+}
+
+#endif /* !MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "cd ../..; make modules SUBDIRS=drivers/sound"
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN linux-2.2.12-pre3/include/asm-alpha/fpu.h linux-2.2.12-pre4/include/asm-alpha/fpu.h
--- linux-2.2.12-pre3/include/asm-alpha/fpu.h	1999-08-09 12:04:57.000000000 -0700
+++ linux-2.2.12-pre4/include/asm-alpha/fpu.h	2003-08-15 15:06:54.000000000 -0700
@@ -139,6 +139,8 @@
 
 extern unsigned long alpha_read_fp_reg (unsigned long reg);
 extern void alpha_write_fp_reg (unsigned long reg, unsigned long val);
+extern unsigned long alpha_read_fp_reg_s (unsigned long reg);
+extern void alpha_write_fp_reg_s (unsigned long reg, unsigned long val);
 
 #endif /* __KERNEL__ */
 
diff -urN linux-2.2.12-pre3/include/asm-sparc/resource.h linux-2.2.12-pre4/include/asm-sparc/resource.h
--- linux-2.2.12-pre3/include/asm-sparc/resource.h	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/include/asm-sparc/resource.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: resource.h,v 1.7 1998/11/19 20:01:44 davem Exp $
+/* $Id: resource.h,v 1.7.2.1 1999/08/13 18:30:47 davem Exp $
  * resource.h: Resource definitions.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
diff -urN linux-2.2.12-pre3/include/asm-sparc64/floppy.h linux-2.2.12-pre4/include/asm-sparc64/floppy.h
--- linux-2.2.12-pre3/include/asm-sparc64/floppy.h	1999-08-09 12:05:10.000000000 -0700
+++ linux-2.2.12-pre4/include/asm-sparc64/floppy.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: floppy.h,v 1.18.2.1 1999/08/03 08:00:20 davem Exp $
+/* $Id: floppy.h,v 1.18.2.2 1999/08/09 21:07:41 ecd Exp $
  * asm-sparc64/floppy.h: Sparc specific parts of the Floppy driver.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -276,16 +276,19 @@
 
 static unsigned char sun_pci_fd_inb(unsigned long port)
 {
+	udelay(5);
 	return inb(port);
 }
 
 static void sun_pci_fd_outb(unsigned char val, unsigned long port)
 {
+	udelay(5);
 	outb(val, port);
 }
 
 static void sun_pci_fd_broken_outb(unsigned char val, unsigned long port)
 {
+	udelay(5);
 	/*
 	 * XXX: Due to SUN's broken floppy connector on AX and AXi
 	 *      we need to turn on MOTOR_0 also, if the floppy is
@@ -304,6 +307,7 @@
 #ifdef PCI_FDC_SWAP_DRIVES
 static void sun_pci_fd_lde_broken_outb(unsigned char val, unsigned long port)
 {
+	udelay(5);
 	/*
 	 * XXX: Due to SUN's broken floppy connector on AX and AXi
 	 *      we need to turn on MOTOR_0 also, if the floppy is
@@ -326,9 +330,9 @@
 	unsigned int dcsr;
 
 	writel(EBUS_DCSR_RESET, &sun_pci_fd_ebus_dma->dcsr);
-
+	udelay(1);
 	dcsr = EBUS_DCSR_BURST_SZ_16 | EBUS_DCSR_TCI_DIS |
-	       EBUS_DCSR_EN_CNT | EBUS_DCSR_INT_EN;
+	       EBUS_DCSR_EN_CNT;
 	writel(dcsr, (unsigned long)&sun_pci_fd_ebus_dma->dcsr);
 }
 
@@ -346,12 +350,19 @@
 	unsigned int dcsr;
 
 	dcsr = readl(&sun_pci_fd_ebus_dma->dcsr);
-	while (dcsr & EBUS_DCSR_DRAIN)
-		dcsr = readl(&sun_pci_fd_ebus_dma->dcsr);
-	dcsr &= ~(EBUS_DCSR_EN_DMA);
-	if (dcsr & EBUS_DCSR_ERR_PEND)
-		sun_pci_fd_reset_dma();
-	writel(dcsr, &sun_pci_fd_ebus_dma->dcsr);
+	if (dcsr & EBUS_DCSR_EN_DMA) {
+		while (dcsr & EBUS_DCSR_DRAIN) {
+			udelay(1);
+			dcsr = readl(&sun_pci_fd_ebus_dma->dcsr);
+		}
+		dcsr &= ~(EBUS_DCSR_EN_DMA);
+		writel(dcsr, &sun_pci_fd_ebus_dma->dcsr);
+		if (dcsr & EBUS_DCSR_ERR_PEND) {
+			sun_pci_fd_reset_dma();
+			dcsr &= ~(EBUS_DCSR_ERR_PEND);
+			writel(dcsr, &sun_pci_fd_ebus_dma->dcsr);
+		}
+	}
 }
 
 static void sun_pci_fd_set_dma_mode(int mode)
@@ -359,6 +370,11 @@
 	unsigned int dcsr;
 
 	dcsr = readl(&sun_pci_fd_ebus_dma->dcsr);
+	if (readl(&sun_pci_fd_ebus_dma->dbcr)) {
+		sun_pci_fd_reset_dma();
+		writel(dcsr, &sun_pci_fd_ebus_dma->dcsr);
+	}
+
 	dcsr |= EBUS_DCSR_EN_CNT | EBUS_DCSR_TC;
 	/*
 	 * For EBus WRITE means to system memory, which is
@@ -384,7 +400,15 @@
 
 static unsigned int sun_pci_get_dma_residue(void)
 {
-	return readl(&sun_pci_fd_ebus_dma->dbcr);
+	unsigned int dcsr, res;
+
+	res = readl(&sun_pci_fd_ebus_dma->dbcr);
+	if (res != 0) {
+		dcsr = readl(&sun_pci_fd_ebus_dma->dcsr);
+		sun_pci_fd_reset_dma();
+		writel(dcsr, &sun_pci_fd_ebus_dma->dcsr);
+	}
+	return res;
 }
 
 static void sun_pci_fd_enable_irq(void)
@@ -548,7 +572,9 @@
 #ifdef CONFIG_PCI
 		struct linux_ebus *ebus;
 		struct linux_ebus_device *edev = 0;
+		unsigned long config = 0;
 		unsigned long auxio_reg;
+		unsigned char cfg;
 
 		for_each_ebus(ebus) {
 			for_each_ebusdev(edev, ebus) {
@@ -622,57 +648,62 @@
 		if (sun_pci_fd_test_drive((unsigned long)sun_fdc, 1))
 			sun_floppy_types[1] = 4;
 
-#ifdef PCI_FDC_SWAP_DRIVES
 		/*
-		 * If only Floppy 1 is present, swap drives.
+		 * Find NS87303 SuperIO config registers (through ecpp).
 		 */
-		if (!sun_floppy_types[0] && sun_floppy_types[1]) {
-			unsigned long config = 0;
-			unsigned char tmp;
-
-			for_each_ebus(ebus) {
-				for_each_ebusdev(edev, ebus) {
-					if (!strcmp(edev->prom_name, "ecpp")) {
-						config = edev->base_address[1];
-						goto config_done;
-					}
+		for_each_ebus(ebus) {
+			for_each_ebusdev(edev, ebus) {
+				if (!strcmp(edev->prom_name, "ecpp")) {
+					config = edev->base_address[1];
+					goto config_done;
 				}
 			}
-		config_done:
+		}
+	config_done:
 
-			/*
-			 * Sanity check, is this really the NS87303?
-			 */
-			switch (config & 0x3ff) {
-			case 0x02e:
-			case 0x15c:
-			case 0x26e:
-			case 0x398:
-				break;
-			default:
-				config = 0;
-			}
+		/*
+		 * Sanity check, is this really the NS87303?
+		 */
+		switch (config & 0x3ff) {
+		case 0x02e:
+		case 0x15c:
+		case 0x26e:
+		case 0x398:
+			break;
+		default:
+			config = 0;
+		}
 
-			if (!config)
-				return sun_floppy_types[0];
+		if (!config)
+			return sun_floppy_types[0];
 
+		/* Enable PC-AT mode. */
+		cfg = ns87303_readb(config, ASC);
+		cfg |= 0xc0;
+		ns87303_writeb(config, ASC, cfg);
+
+#ifdef PCI_FDC_SWAP_DRIVES
+		/*
+		 * If only Floppy 1 is present, swap drives.
+		 */
+		if (!sun_floppy_types[0] && sun_floppy_types[1]) {
 			/*
 			 * Set the drive exchange bit in FCR on NS87303,
 			 * make shure other bits are sane before doing so.
 			 */
-			tmp = ns87303_readb(config, FER);
-			tmp &= ~(FER_EDM);
-			ns87303_writeb(config, FER, tmp);
-			tmp = ns87303_readb(config, ASC);
-			tmp &= ~(ASC_DRV2_SEL);
-			ns87303_writeb(config, ASC, tmp);
-			tmp = ns87303_readb(config, FCR);
-			tmp |= FCR_LDE;
-			ns87303_writeb(config, FCR, tmp);
+			cfg = ns87303_readb(config, FER);
+			cfg &= ~(FER_EDM);
+			ns87303_writeb(config, FER, cfg);
+			cfg = ns87303_readb(config, ASC);
+			cfg &= ~(ASC_DRV2_SEL);
+			ns87303_writeb(config, ASC, cfg);
+			cfg = ns87303_readb(config, FCR);
+			cfg |= FCR_LDE;
+			ns87303_writeb(config, FCR, cfg);
 
-			tmp = sun_floppy_types[0];
+			cfg = sun_floppy_types[0];
 			sun_floppy_types[0] = sun_floppy_types[1];
-			sun_floppy_types[1] = tmp;
+			sun_floppy_types[1] = cfg;
 
 			if (sun_pci_broken_drive != -1) {
 				sun_pci_broken_drive = 1 - sun_pci_broken_drive;
diff -urN linux-2.2.12-pre3/include/asm-sparc64/resource.h linux-2.2.12-pre4/include/asm-sparc64/resource.h
--- linux-2.2.12-pre3/include/asm-sparc64/resource.h	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/include/asm-sparc64/resource.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: resource.h,v 1.4 1998/11/19 20:01:49 davem Exp $
+/* $Id: resource.h,v 1.4.2.1 1999/08/13 18:30:54 davem Exp $
  * resource.h: Resource definitions.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
diff -urN linux-2.2.12-pre3/include/linux/ip_masq.h linux-2.2.12-pre4/include/linux/ip_masq.h
--- linux-2.2.12-pre3/include/linux/ip_masq.h	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/include/linux/ip_masq.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,6 +1,6 @@
 /*
  *	IP_MASQ user space control interface
- *	$Id: ip_masq.h,v 1.2 1998/12/08 05:41:48 davem Exp $
+ *	$Id: ip_masq.h,v 1.2.2.1 1999/08/13 18:23:03 davem Exp $
  */
 
 #ifndef _LINUX_IP_MASQ_H
diff -urN linux-2.2.12-pre3/include/linux/isdn_compat.h linux-2.2.12-pre4/include/linux/isdn_compat.h
--- linux-2.2.12-pre3/include/linux/isdn_compat.h	1999-08-09 12:04:41.000000000 -0700
+++ linux-2.2.12-pre4/include/linux/isdn_compat.h	2003-08-15 15:06:54.000000000 -0700
@@ -16,6 +16,7 @@
 #include <linux/mm.h>
 
 #define ioremap vremap
+#define ioremap_nocache vremap
 #define iounmap vfree
 
 static inline unsigned long copy_from_user(void *to, const void *from, unsigned long n)
@@ -88,6 +89,11 @@
 
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,91)
 #define COMPAT_HAS_NEW_PCI
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+#define get_pcibase(ps, nr) ps->base_address[nr]
+#else
+#define get_pcibase(ps, nr) ps->resource[nr].start
+#endif
 #endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,127)
diff -urN linux-2.2.12-pre3/include/linux/isdn.h linux-2.2.12-pre4/include/linux/isdn.h
--- linux-2.2.12-pre3/include/linux/isdn.h	1999-08-09 12:04:41.000000000 -0700
+++ linux-2.2.12-pre4/include/linux/isdn.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: isdn.h,v 1.69 1999/07/13 20:47:53 werner Exp $
+/* $Id: isdn.h,v 1.70 1999/07/31 12:59:58 armin Exp $
  *
  * Main header for the Linux ISDN subsystem (linklevel).
  *
@@ -21,6 +21,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdn.h,v $
+ * Revision 1.70  1999/07/31 12:59:58  armin
+ * Added tty fax capabilities.
+ *
  * Revision 1.69  1999/07/13 20:47:53  werner
  * added channel bit ISDN_USAGE_DISABLED for limiting b-channel access.
  *
@@ -732,6 +735,10 @@
   void                  *dtmf_state;     /* state for dtmf decoder         */
   void                  *silence_state;  /* state for silence detection    */
 #endif
+#ifdef CONFIG_ISDN_TTY_FAX
+  struct T30_s		*fax;		 /* T30 Fax Group 3 data/interface */
+  int			faxonline;	 /* Fax-channel status             */
+#endif
   struct tty_struct 	*tty;            /* Pointer to corresponding tty   */
   atemu                 emu;             /* AT-emulator data               */
   struct termios	normal_termios;  /* For saving termios structs     */
diff -urN linux-2.2.12-pre3/include/linux/isdnif.h linux-2.2.12-pre4/include/linux/isdnif.h
--- linux-2.2.12-pre3/include/linux/isdnif.h	1999-08-09 12:04:41.000000000 -0700
+++ linux-2.2.12-pre4/include/linux/isdnif.h	2003-08-15 15:06:54.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: isdnif.h,v 1.28 1999/07/13 20:57:48 werner Exp $
+/* $Id: isdnif.h,v 1.29 1999/07/31 13:00:02 armin Exp $
  *
  * Linux ISDN subsystem
  *
@@ -22,6 +22,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdnif.h,v $
+ * Revision 1.29  1999/07/31 13:00:02  armin
+ * Added tty fax capabilities.
+ *
  * Revision 1.28  1999/07/13 20:57:48  werner
  * added callback ISDN_STAT_DISCH for limiting b-channel resources.
  *
@@ -373,6 +376,84 @@
     unsigned char screen;   /* Screening info      */
 } setup_parm;
 
+
+#ifdef CONFIG_ISDN_TTY_FAX
+/* T.30 Fax G3 */
+
+#define FAXIDLEN 21
+
+typedef struct T30_s {
+	/* session parameters */
+	__u8 resolution		__attribute__ ((packed));
+	__u8 rate		__attribute__ ((packed));
+	__u8 width		__attribute__ ((packed));
+	__u8 length		__attribute__ ((packed));
+	__u8 compression	__attribute__ ((packed));
+	__u8 ecm		__attribute__ ((packed));
+	__u8 binary		__attribute__ ((packed));
+	__u8 scantime		__attribute__ ((packed));
+	__u8 id[FAXIDLEN]	__attribute__ ((packed));
+	/* additional parameters */
+	__u8 phase		__attribute__ ((packed));
+	__u8 direction		__attribute__ ((packed));
+	__u8 code		__attribute__ ((packed));
+	__u8 badlin		__attribute__ ((packed));
+	__u8 badmul		__attribute__ ((packed));
+	__u8 bor		__attribute__ ((packed));
+	__u8 fet		__attribute__ ((packed));
+	__u8 pollid[FAXIDLEN]	__attribute__ ((packed));
+	__u8 cq			__attribute__ ((packed));
+	__u8 cr			__attribute__ ((packed));
+	__u8 ctcrty		__attribute__ ((packed));
+	__u8 minsp		__attribute__ ((packed));
+	__u8 phcto		__attribute__ ((packed));
+	__u8 rel		__attribute__ ((packed));
+	__u8 nbc		__attribute__ ((packed));
+	/* remote station parameters */
+	__u8 r_resolution	__attribute__ ((packed));
+	__u8 r_rate		__attribute__ ((packed));
+	__u8 r_width		__attribute__ ((packed));
+	__u8 r_length		__attribute__ ((packed));
+	__u8 r_compression	__attribute__ ((packed));
+	__u8 r_ecm		__attribute__ ((packed));
+	__u8 r_binary		__attribute__ ((packed));
+	__u8 r_scantime		__attribute__ ((packed));
+	__u8 r_id[FAXIDLEN]	__attribute__ ((packed));
+	__u8 r_code		__attribute__ ((packed));
+} T30_s;
+
+#define ISDN_TTY_FAX_CONN_IN	0
+#define ISDN_TTY_FAX_CONN_OUT	1
+
+#define ISDN_TTY_FAX_FCON	0
+#define ISDN_TTY_FAX_DIS 	1
+#define ISDN_TTY_FAX_FTT 	2
+#define ISDN_TTY_FAX_MCF 	3
+#define ISDN_TTY_FAX_DCS 	4
+#define ISDN_TTY_FAX_TRAIN_OK	5
+#define ISDN_TTY_FAX_EOP 	6
+#define ISDN_TTY_FAX_EOM 	7
+#define ISDN_TTY_FAX_MPS 	8
+#define ISDN_TTY_FAX_DTC 	9
+#define ISDN_TTY_FAX_RID 	10
+#define ISDN_TTY_FAX_HNG 	11
+#define ISDN_TTY_FAX_DT  	12
+#define ISDN_TTY_FAX_FCON_I	13
+#define ISDN_TTY_FAX_DR  	14
+#define ISDN_TTY_FAX_ET  	15
+#define ISDN_TTY_FAX_CFR 	16
+#define ISDN_TTY_FAX_PTS 	17
+#define ISDN_TTY_FAX_SENT	18
+
+#define ISDN_FAX_PHASE_IDLE	0
+#define ISDN_FAX_PHASE_A	1
+#define ISDN_FAX_PHASE_B   	2
+#define ISDN_FAX_PHASE_C   	3
+#define ISDN_FAX_PHASE_D   	4
+#define ISDN_FAX_PHASE_E   	5
+
+#endif /* TTY_FAX */
+
 /* CAPI structs */
 
 /* this is compatible to the old union size */
@@ -411,6 +492,9 @@
 		capi_msg cmsg;	/* For CAPI like messages		*/
 		char display[85];/* display message data          */ 
 		dss1_cmd_stat dss1_io; /* DSS1 IO-parameter/result */
+#ifdef CONFIG_ISDN_TTY_FAX
+		T30_s	*fax;	/* Pointer to ttys fax struct		*/
+#endif
 	} parm;
 } isdn_ctrl;
 
diff -urN linux-2.2.12-pre3/net/ipv4/arp.c linux-2.2.12-pre4/net/ipv4/arp.c
--- linux-2.2.12-pre3/net/ipv4/arp.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/arp.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,6 +1,6 @@
 /* linux/net/inet/arp.c
  *
- * Version:	$Id: arp.c,v 1.77.2.1 1999/06/28 10:39:23 davem Exp $
+ * Version:	$Id: arp.c,v 1.77.2.2 1999/08/13 18:26:03 davem Exp $
  *
  * Copyright (C) 1994 by Florian  La Roche
  *
diff -urN linux-2.2.12-pre3/net/ipv4/ip_input.c linux-2.2.12-pre4/net/ipv4/ip_input.c
--- linux-2.2.12-pre3/net/ipv4/ip_input.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/ip_input.c	2003-08-15 15:06:54.000000000 -0700
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) module.
  *
- * Version:	$Id: ip_input.c,v 1.37 1999/04/22 10:38:36 davem Exp $
+ * Version:	$Id: ip_input.c,v 1.37.2.1 1999/08/13 18:26:08 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
diff -urN linux-2.2.12-pre3/net/ipv4/ip_masq_autofw.c linux-2.2.12-pre4/net/ipv4/ip_masq_autofw.c
--- linux-2.2.12-pre3/net/ipv4/ip_masq_autofw.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/ip_masq_autofw.c	2003-08-15 15:06:54.000000000 -0700
@@ -2,7 +2,7 @@
  *		IP_MASQ_AUTOFW auto forwarding module
  *
  *
- * 	$Id: ip_masq_autofw.c,v 1.3 1998/08/29 23:51:10 davem Exp $
+ * 	$Id: ip_masq_autofw.c,v 1.3.2.1 1999/08/13 18:26:20 davem Exp $
  *
  * Author:	Richard Lynch
  *
diff -urN linux-2.2.12-pre3/net/ipv4/ip_masq.c linux-2.2.12-pre4/net/ipv4/ip_masq.c
--- linux-2.2.12-pre3/net/ipv4/ip_masq.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/ip_masq.c	2003-08-15 15:06:54.000000000 -0700
@@ -4,7 +4,7 @@
  *
  * 	Copyright (c) 1994 Pauline Middelink
  *
- *	$Id: ip_masq.c,v 1.34.2.1 1999/07/02 10:10:00 davem Exp $
+ *	$Id: ip_masq.c,v 1.34.2.3 1999/08/13 18:26:15 davem Exp $
  *
  *
  *	See ip_fw.c for original log
diff -urN linux-2.2.12-pre3/net/ipv4/ip_masq_mfw.c linux-2.2.12-pre4/net/ipv4/ip_masq_mfw.c
--- linux-2.2.12-pre3/net/ipv4/ip_masq_mfw.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/ip_masq_mfw.c	2003-08-15 15:06:54.000000000 -0700
@@ -3,7 +3,7 @@
  *
  *	Does (reverse-masq) forwarding based on skb->fwmark value
  *
- *	$Id: ip_masq_mfw.c,v 1.3.2.1 1999/07/02 10:10:03 davem Exp $
+ *	$Id: ip_masq_mfw.c,v 1.3.2.2 1999/08/13 18:26:26 davem Exp $
  *
  * Author:	Juan Jose Ciarlante   <jjciarla@raiz.uncu.edu.ar>
  *		  based on Steven Clarke's portfw
diff -urN linux-2.2.12-pre3/net/ipv4/ip_masq_portfw.c linux-2.2.12-pre4/net/ipv4/ip_masq_portfw.c
--- linux-2.2.12-pre3/net/ipv4/ip_masq_portfw.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/ip_masq_portfw.c	2003-08-15 15:06:54.000000000 -0700
@@ -2,7 +2,7 @@
  *		IP_MASQ_PORTFW masquerading module
  *
  *
- *	$Id: ip_masq_portfw.c,v 1.3.2.1 1999/07/02 10:10:02 davem Exp $
+ *	$Id: ip_masq_portfw.c,v 1.3.2.2 1999/08/13 18:26:29 davem Exp $
  *
  * Author:	Steven Clarke <steven.clarke@monmouth.demon.co.uk>
  *
diff -urN linux-2.2.12-pre3/net/ipv4/ip_masq_user.c linux-2.2.12-pre4/net/ipv4/ip_masq_user.c
--- linux-2.2.12-pre3/net/ipv4/ip_masq_user.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/ip_masq_user.c	2003-08-15 15:06:54.000000000 -0700
@@ -2,7 +2,7 @@
  *	IP_MASQ_USER user space control module
  *
  *
- *	$Id: ip_masq_user.c,v 1.1 1998/08/29 23:51:08 davem Exp $
+ *	$Id: ip_masq_user.c,v 1.1.2.2 1999/08/13 18:26:33 davem Exp $
  */
 
 #include <linux/config.h>
diff -urN linux-2.2.12-pre3/net/ipv4/ip_vs.c linux-2.2.12-pre4/net/ipv4/ip_vs.c
--- linux-2.2.12-pre3/net/ipv4/ip_vs.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/ip_vs.c	2003-08-15 15:06:54.000000000 -0700
@@ -5,7 +5,7 @@
  *              high-performance and highly available server based on a
  *              cluster of servers.
  *
- * Version:     $Id: ip_vs.c,v 1.2 1999/07/09 12:12:23 wensong Exp $
+ * Version:     $Id: ip_vs.c,v 1.1.2.1 1999/08/13 18:25:27 davem Exp $
  *
  * Authors:     Wensong Zhang <wensong@iinchina.net>
  *              Peter Kese <peter.kese@ijs.si>
diff -urN linux-2.2.12-pre3/net/ipv4/ip_vs_pcc.c linux-2.2.12-pre4/net/ipv4/ip_vs_pcc.c
--- linux-2.2.12-pre3/net/ipv4/ip_vs_pcc.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/ip_vs_pcc.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,7 +1,7 @@
 /*
  * IPVS:        Persistent Client Connection Scheduling module
  *
- * Version:     $Id: ip_vs_pcc.c,v 1.2 1999/07/09 12:12:40 wensong Exp $
+ * Version:     $Id: ip_vs_pcc.c,v 1.1.2.1 1999/08/13 18:25:33 davem Exp $
  *
  * Authors:     Wensong Zhang <wensong@iinchina.net>
  *              Peter Kese <peter.kese@ijs.si>
diff -urN linux-2.2.12-pre3/net/ipv4/ip_vs_rr.c linux-2.2.12-pre4/net/ipv4/ip_vs_rr.c
--- linux-2.2.12-pre3/net/ipv4/ip_vs_rr.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/ip_vs_rr.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,7 +1,7 @@
 /*
  * IPVS:        Round-Robin Scheduling module
  *
- * Version:     $Id: ip_vs_rr.c,v 1.2 1999/07/09 12:13:40 wensong Exp $
+ * Version:     $Id: ip_vs_rr.c,v 1.1.2.1 1999/08/13 18:25:39 davem Exp $
  *
  * Authors:     Wensong Zhang <wensong@iinchina.net>
  *              Peter Kese <peter.kese@ijs.si>
diff -urN linux-2.2.12-pre3/net/ipv4/ip_vs_wlc.c linux-2.2.12-pre4/net/ipv4/ip_vs_wlc.c
--- linux-2.2.12-pre3/net/ipv4/ip_vs_wlc.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/ip_vs_wlc.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,7 +1,7 @@
 /*
  * IPVS:        Weighted Least-Connection Scheduling module
  *
- * Version:     $Id: ip_vs_wlc.c,v 1.2 1999/07/09 12:10:57 wensong Exp $
+ * Version:     $Id: ip_vs_wlc.c,v 1.1.2.1 1999/08/13 18:25:44 davem Exp $
  *
  * Authors:     Wensong Zhang <wensong@iinchina.net>
  *              Peter Kese <peter.kese@ijs.si>
diff -urN linux-2.2.12-pre3/net/ipv4/ip_vs_wrr.c linux-2.2.12-pre4/net/ipv4/ip_vs_wrr.c
--- linux-2.2.12-pre3/net/ipv4/ip_vs_wrr.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/ip_vs_wrr.c	2003-08-15 15:06:54.000000000 -0700
@@ -1,7 +1,7 @@
 /*
  * IPVS:        Weighted Round-Robin Scheduling module
  *
- * Version:     $Id: ip_vs_wrr.c,v 1.2 1999/07/09 12:13:16 wensong Exp $
+ * Version:     $Id: ip_vs_wrr.c,v 1.1.2.1 1999/08/13 18:25:49 davem Exp $
  *
  * Authors:     Wensong Zhang <wensong@iinchina.net>
  *
diff -urN linux-2.2.12-pre3/net/ipv4/tcp_input.c linux-2.2.12-pre4/net/ipv4/tcp_input.c
--- linux-2.2.12-pre3/net/ipv4/tcp_input.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/tcp_input.c	2003-08-15 15:06:54.000000000 -0700
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.164.2.6 1999/08/08 08:43:18 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.164.2.7 1999/08/13 16:14:27 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
diff -urN linux-2.2.12-pre3/net/ipv4/tcp_ipv4.c linux-2.2.12-pre4/net/ipv4/tcp_ipv4.c
--- linux-2.2.12-pre3/net/ipv4/tcp_ipv4.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv4/tcp_ipv4.c	2003-08-15 15:06:54.000000000 -0700
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.175.2.9 1999/08/12 15:34:26 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.175.2.10 1999/08/13 16:14:35 davem Exp $
  *
  *		IPv4 specific functions
  *
diff -urN linux-2.2.12-pre3/net/ipv6/ip6_input.c linux-2.2.12-pre4/net/ipv6/ip6_input.c
--- linux-2.2.12-pre3/net/ipv6/ip6_input.c	1998-08-27 19:33:09.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv6/ip6_input.c	2003-08-15 15:06:54.000000000 -0700
@@ -212,7 +212,7 @@
 
 		if (ipprot->copy || raw_sk)
 			buff = skb_clone(skb, GFP_ATOMIC);
-
+		/* buff == NULL ?????? */
 		ipprot->handler(buff, len);
 		found = 1;
 	}
diff -urN linux-2.2.12-pre3/net/ipv6/tcp_ipv6.c linux-2.2.12-pre4/net/ipv6/tcp_ipv6.c
--- linux-2.2.12-pre3/net/ipv6/tcp_ipv6.c	2003-08-15 15:06:53.000000000 -0700
+++ linux-2.2.12-pre4/net/ipv6/tcp_ipv6.c	2003-08-15 15:06:54.000000000 -0700
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.104.2.7 1999/08/12 15:34:32 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.104.2.9 1999/08/13 18:49:56 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -134,10 +134,13 @@
 					if (!sk_reuse	||
 					    !sk2->reuse	||
 					    sk2->state == TCP_LISTEN) {
+						/* NOTE: IPv6 tw bucket have different format */
 						if (!sk2->rcv_saddr	||
-						    !addr_type == IPV6_ADDR_ANY ||
+						    addr_type == IPV6_ADDR_ANY ||
 						    !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
-								   &sk2->net_pinfo.af_inet6.rcv_saddr))
+								   sk2->state != TCP_TIME_WAIT ?
+								   &sk2->net_pinfo.af_inet6.rcv_saddr :
+								   &((struct tcp_tw_bucket*)sk)->v6_rcv_saddr))
 							break;
 					}
 				}
