]> git.neil.brown.name Git - history.git/commitdiff
Import 2.2.12pre4 2.2.12pre4
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:19:27 +0000 (15:19 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:19:27 +0000 (15:19 -0500)
97 files changed:
Documentation/Configure.help
Documentation/isdn/HiSax.cert
Documentation/isdn/INTERFACE
Documentation/isdn/INTERFACE.fax [new file with mode: 0644]
Documentation/isdn/README
Documentation/isdn/README.diversion [new file with mode: 0644]
Documentation/isdn/README.fax [new file with mode: 0644]
Documentation/isdn/README.hfc-pci [new file with mode: 0644]
Documentation/sound/vwsnd [new file with mode: 0644]
arch/alpha/kernel/alpha_ksyms.c
arch/sparc/defconfig
arch/sparc/mm/iommu.c
arch/sparc64/defconfig
arch/sparc64/kernel/ioctl32.c
arch/sparc64/kernel/process.c
drivers/block/Config.in
drivers/isdn/Config.in
drivers/isdn/Makefile
drivers/isdn/avmb1/Makefile
drivers/isdn/avmb1/avmcard.h
drivers/isdn/avmb1/b1.c
drivers/isdn/avmb1/b1pci.c
drivers/isdn/avmb1/capidrv.c
drivers/isdn/avmb1/capilli.h
drivers/isdn/avmb1/kcapi.c
drivers/isdn/divert/divert_procfs.c
drivers/isdn/eicon/eicon.h
drivers/isdn/eicon/eicon_dsp.h
drivers/isdn/eicon/eicon_idi.c
drivers/isdn/eicon/eicon_idi.h
drivers/isdn/eicon/eicon_io.c
drivers/isdn/eicon/eicon_isa.c
drivers/isdn/eicon/eicon_mod.c
drivers/isdn/eicon/eicon_pci.c
drivers/isdn/hisax/Makefile
drivers/isdn/hisax/avm_pci.c
drivers/isdn/hisax/bkm_a4t.c
drivers/isdn/hisax/bkm_a8.c
drivers/isdn/hisax/callc.c
drivers/isdn/hisax/cert.c
drivers/isdn/hisax/config.c
drivers/isdn/hisax/diva.c
drivers/isdn/hisax/elsa.c
drivers/isdn/hisax/gazel.c
drivers/isdn/hisax/hfc_pci.c
drivers/isdn/hisax/hfc_pci.h
drivers/isdn/hisax/hfcscard.c
drivers/isdn/hisax/hisax.h
drivers/isdn/hisax/isac.c
drivers/isdn/hisax/isar.c
drivers/isdn/hisax/isar.h
drivers/isdn/hisax/isdnl2.c
drivers/isdn/hisax/isdnl3.c
drivers/isdn/hisax/isdnl3.h
drivers/isdn/hisax/l3dss1.c
drivers/isdn/hisax/md5sums.asc
drivers/isdn/hisax/netjet.c
drivers/isdn/hisax/niccy.c
drivers/isdn/hisax/sedlbauer.c
drivers/isdn/hisax/tei.c
drivers/isdn/hisax/telespci.c
drivers/isdn/isdn_audio.c
drivers/isdn/isdn_cards.c
drivers/isdn/isdn_common.c
drivers/isdn/isdn_tty.c
drivers/isdn/isdn_tty.h
drivers/isdn/isdn_ttyfax.c [new file with mode: 0644]
drivers/isdn/isdn_ttyfax.h [new file with mode: 0644]
drivers/net/sis900.c
drivers/sound/Config.in
drivers/sound/Makefile
drivers/sound/sound_core.c
drivers/sound/vwsnd.c [new file with mode: 0644]
include/asm-alpha/fpu.h
include/asm-sparc/resource.h
include/asm-sparc64/floppy.h
include/asm-sparc64/resource.h
include/linux/ip_masq.h
include/linux/isdn.h
include/linux/isdn_compat.h
include/linux/isdnif.h
net/ipv4/arp.c
net/ipv4/ip_input.c
net/ipv4/ip_masq.c
net/ipv4/ip_masq_autofw.c
net/ipv4/ip_masq_mfw.c
net/ipv4/ip_masq_portfw.c
net/ipv4/ip_masq_user.c
net/ipv4/ip_vs.c
net/ipv4/ip_vs_pcc.c
net/ipv4/ip_vs_rr.c
net/ipv4/ip_vs_wlc.c
net/ipv4/ip_vs_wrr.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv6/ip6_input.c
net/ipv6/tcp_ipv6.c

index 2666406d96261bd5e579f0e3b6b43fba6d3938b9..d2ae9cacb1e5f5096cf248e186d091972465ec69 100644 (file)
@@ -9873,6 +9873,12 @@ CONFIG_AEDSP16_MPU_IRQ
   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 @@ CONFIG_ISDN_DRV_SC
   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
index b1f8aaa5333861ebeb87e301c3fbde09280840cf..de440068f9cee51bd66cb4fc7a7659de393651d9 100644 (file)
@@ -14,7 +14,8 @@ First:
 
 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/l3dss1.c
 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 @@ keil@isdn4linux.de
 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-----
index 021e00858f4edee0ff2e9d0cf6e2b983f004f2a0..d8bf08ccc63c23b2839be332ec4b6fc1f0f02a1a 100644 (file)
@@ -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 @@ Description of the Interface between Linklevel and Hardwarelevel
 
    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 @@ Description of the Interface between Linklevel and Hardwarelevel
        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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       driver      = driver-Id.
       command     = ISDN_CMD_LOCK
       arg         = unused.
-      para        = unused.
+      parm        = unused.
 
   ISDN_CMD_UNLOCK:
 
@@ -434,7 +434,19 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       driver      = driver-Id
       command     = ISDN_STAT_STAVAIL
       arg         = length of available data.
-      para        = unused.
+      parm        = unused.
 
   ISDN_STAT_ICALL:
 
@@ -466,12 +478,12 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       driver      = driver-Id
       command     = ISDN_STAT_RUN
       arg         = unused.
-      para        = unused.
+      parm        = unused.
 
   ISDN_STAT_STOP:
 
@@ -508,7 +520,7 @@ Description of the Interface between Linklevel and Hardwarelevel
       driver      = driver-Id
       command     = ISDN_STAT_STOP
       arg         = unused.
-      para        = unused.
+      parm        = unused.
 
   ISDN_STAT_DCONN:
 
@@ -519,7 +531,7 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       driver      = driver-Id
       command     = ISDN_STAT_UNLOAD
       arg         = unused.
-      para        = unused.
+      parm        = unused.
 
   ISDN_STAT_BSENT:
 
@@ -600,7 +612,7 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 @@ Description of the Interface between Linklevel and Hardwarelevel
       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 --git a/Documentation/isdn/INTERFACE.fax b/Documentation/isdn/INTERFACE.fax
new file mode 100644 (file)
index 0000000..2c9659f
--- /dev/null
@@ -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
+
index 3dab12ede4efb703e5bac69eb7f0ddc31638d28b..dcd14151eb9bcb49bce3040b9545bd968fb65589 100644 (file)
@@ -149,6 +149,8 @@ README for the ISDN-subsystem
                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 --git a/Documentation/isdn/README.diversion b/Documentation/isdn/README.diversion
new file mode 100644 (file)
index 0000000..8e1d7a0
--- /dev/null
@@ -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 --git a/Documentation/isdn/README.fax b/Documentation/isdn/README.fax
new file mode 100644 (file)
index 0000000..897abd0
--- /dev/null
@@ -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 --git a/Documentation/isdn/README.hfc-pci b/Documentation/isdn/README.hfc-pci
new file mode 100644 (file)
index 0000000..94e0d43
--- /dev/null
@@ -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 --git a/Documentation/sound/vwsnd b/Documentation/sound/vwsnd
new file mode 100644 (file)
index 0000000..a6ea0a1
--- /dev/null
@@ -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);
+       }
+
index c220ccdc667125fcb4095bda9e60cda8798b0ad7..a2e4db1c681983ea441ba4beab310a16c59bcf9b 100644 (file)
@@ -91,6 +91,8 @@ EXPORT_SYMBOL(wrusp);
 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);
index 8ee5f11490ceb5510402f5ede4775120c7f2353b..7dcbd88ac9cd3318548ba17396bc4de0516d7fd0 100644 (file)
@@ -305,6 +305,7 @@ CONFIG_NLS=y
 # 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
 
index a0f92ea79599c2669bf83e9a240fe08ccb4abb97..2f233e1b3b87449d44587e34eaf427e2fb6b3cc4 100644 (file)
@@ -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 @@ static void iommu_map_dma_area(unsigned long addr, int len)
                        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);
index 2af69e7c22cfcf7d4118844d944e8fa0f4596eaf..59834c13ed0cc56adbd7b8dfbe6200621995ca45 100644 (file)
@@ -349,6 +349,7 @@ CONFIG_NLS=y
 # 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
 
index e89ade288a4e6898d5d233fef3acd833063d27e8..bf10d2847f3816f353b73e47f27d3fa7a3608bc7 100644 (file)
@@ -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)
index 4cab7590a26acf0379662216dcae6a093eece6ba..0aba9ebee614a3abad2b7f05f6c084ef2c89e8f4 100644 (file)
@@ -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)
index 38d7a475ccf4db905a44d9dd91377b7aa6b00f60..55d85c76cad0444c634352fa52d78ee14d244c2c 100644 (file)
@@ -101,13 +101,16 @@ if [ "$CONFIG_NET" = "y" ]; then
 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
index 01dfad941626aa826ce5764799441d1165468fec..5ad3772819c9ff1b3e5f8a57c80759bf4fb594d9 100644 (file)
@@ -9,6 +9,9 @@ if [ "$CONFIG_INET" != "n" ]; then
   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 @@ if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
        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
index 32ed9d6a300d11f23ae0b36ddcd0e0cbd2a865af..04be19f9cd697db385864f0e38b995ac97802ce7 100644 (file)
@@ -24,6 +24,9 @@ ifeq ($(CONFIG_ISDN),y)
   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 @@ else
     endif  
     ifdef CONFIG_ISDN_AUDIO
       O_OBJS += isdn_audio.o
+      ifdef CONFIG_ISDN_TTY_FAX
+        O_OBJS += isdn_ttyfax.o
+      endif
     endif
   endif
 endif
index 831624ac055a2cbfd5e9d244f5446ecd3faaae37..9f73ea2e3699e358b0bdfa68ea82d7e0ccbf9661 100644 (file)
@@ -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.
 #
 # 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 @@ L_TARGET := # used for .a targets (from L and LX objects)
 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
index 3e62719da3f7c5ac70cd48b148a80059182ac106..e94c5637cdec2f24e0decc4527cb8beeea92a88d 100644 (file)
@@ -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 @@ enum avmcardtype {
        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 @@ typedef struct avmcard {
 
        int interrupt;
 
+       void *mbase;
+       __u32 csr;
+       avmcard_dmainfo *dma;
+
        struct capi_ctr *ctrl;
 } avmcard;
 
@@ -174,6 +193,7 @@ extern int b1_irq_table[16];
                                           * int32 Length message
                                           * 
                                         */
+#define RECEIVE_POLLDWORD      0x75    /* t1pci in dword mode */
 
 #define WRITE_REGISTER         0x00
 #define READ_REGISTER          0x01
@@ -512,6 +532,10 @@ static inline void b1_setinterrupt(unsigned int base, unsigned irq,
              b1outp(base, B1_RESET, 0xf0);
              b1outp(base, B1_INSTAT, 0x02);
              break;
+          case avm_c4:
+          case avm_t1pci:
+             b1outp(base, B1_RESET, 0xf0);
+             break;
         }
 }
 
index 3b2396724defcfb1e835edd6a893b26a938a82bc..620a2aa6e3365b7f3ce3c627a7c3f6da9fba0977 100644 (file)
@@ -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 @@ void b1_parse_version(avmcard *card)
 
        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 @@ int b1ctl_read_proc(char *page, char **start, off_t off,
        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 @@ int b1ctl_read_proc(char *page, char **start, off_t off,
        }
        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);
 }
 
index beeef8d8d1925d5aa8ad08b63bda96d5c51f9c85..8c0006a78bf59f7eb23750cc9db657f2871487d9 100644 (file)
@@ -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 @@ int b1pci_init(void)
        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",
index a9000fe6b7eabb548129e560f2df6b3cd5c7a544..fb83e52a6e066e3a1c37152053b5f0eb675b5f93 100644 (file)
@@ -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
  *
 #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 @@ static inline __u32 b1prot(int l2, int l3)
         case ISDN_PROTO_L2_V11019:
         case ISDN_PROTO_L2_V11038:
                return 2;
+        case ISDN_PROTO_L2_FAX:
+               return 4;
        }
 }
 
@@ -302,6 +313,8 @@ static inline __u32 b2prot(int l2, int l3)
         case ISDN_PROTO_L2_V11019:
         case ISDN_PROTO_L2_V11038:
                return 1;
+        case ISDN_PROTO_L2_FAX:
+               return 4;
        }
 }
 
@@ -318,6 +331,8 @@ static inline __u32 b3prot(int l2, int l3)
         case ISDN_PROTO_L2_V11038:
        default:
                return 0;
+        case ISDN_PROTO_L2_FAX:
+               return 4;
        }
 }
 
@@ -1017,6 +1032,13 @@ static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg)
                        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 @@ static void enable_dchannel_trace(capidrv_contr *card)
        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 @@ static void disable_dchannel_trace(capidrv_contr *card)
        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 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp)
            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 @@ static int proc_capidrv_read_proc(char *page, char **start, off_t off,
                        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);
 }
 
index a867d0defd7b9291038f738e5af5328d11773345..da64a183b6732c1775e19357974c41a4f304e491 100644 (file)
@@ -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 @@ typedef struct capicardparams {
        unsigned irq;
        int cardtype;
        int cardnr;
+       unsigned int membase;
 } capicardparams;
 
 struct capi_driver;
index 6a65f776e930177b11c1b1aed43be11d09dea681..fc58f12415cb0e3e3915c02f55c78fec3ddf43da 100644 (file)
@@ -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(detach_capi_interface);
 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 @@ int kcapi_init(void)
         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;
 }
index 56d81c9db64f5ac72e2fa09daeaf4750136e0482..035aa363613a808e1d9868703edb32e785f4f1c3 100644 (file)
@@ -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"
 
 /*********************************/
 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 @@ static int (*proc_unreg)(struct proc_dir_entry *, int) = NULL;
 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");
index 3210d0b6b6ebb00881f3626faae1db1479d9bef5..88f408cd148f2e717ef83e6a51bde21f81333323 100644 (file)
@@ -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.
  *
  * 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 @@ typedef struct {
   __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 @@ typedef struct {
        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                */
index aafe07e3be826ba5d6f6b3aabedf72dbbaa8c1f9..9ffbd9bdbed376656d50679786b848a2d34cc9b3 100644 (file)
@@ -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
  * 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 @@ returns:
   - 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 */
 
index 1c0fc6c7edc3351c8305e18a4b6766b02184fd6f..412eb46ba9d12f2746ef2aad39498dbb7e0f1877 100644 (file)
@@ -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 
  * 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 @@ static char HLC_faxg3[2] =    { 0x91, 0x84 };
 
 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 @@ idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan)
                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 @@ idi_call_res_req(eicon_REQ *reqbuf, eicon_chan *chan)
                        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 @@ idi_call_res_req(eicon_REQ *reqbuf, eicon_chan *chan)
 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 @@ idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer)
 
         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 @@ idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer)
 
        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 @@ idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer)
                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 @@ idi_hangup(eicon_card *card, eicon_chan *chan)
        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 @@ idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone,
 
         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 @@ idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone,
                        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 @@ idi_bc2si(unsigned char *bc, unsigned char *hlc, unsigned char *si1, unsigned ch
   }
 }
 
+
 int
 idi_send_udata(eicon_card *card, eicon_chan *chan, int UReq, u_char *buffer, int len)
 {
@@ -855,8 +905,12 @@ idi_send_udata(eicon_card *card, eicon_chan *chan, int UReq, u_char *buffer, int
 
        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 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
        
        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 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
                                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 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
                                        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 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
                                        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 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
                                        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 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
                                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_handle_ind(eicon_card *ccard, struct sk_buff *skb)
                                        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 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
                        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 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
                                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 @@ idi_handle_ack_ok(eicon_card *ccard, eicon_chan *chan, eicon_RC *ack)
 
        /* 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 @@ idi_handle_ack_ok(eicon_card *ccard, eicon_chan *chan, eicon_RC *ack)
                                                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 @@ idi_handle_ack(eicon_card *ccard, struct sk_buff *skb)
                        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 @@ idi_handle_ack(eicon_card *ccard, struct sk_buff *skb)
                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 @@ idi_handle_ack(eicon_card *ccard, struct sk_buff *skb)
                                        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 @@ idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb,
                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 @@ eicon_idi_manage_assign(eicon_card *card)
 
         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 @@ eicon_idi_manage_remove(eicon_card *card)
 
         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 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb)
 
         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 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb)
                return -ENOMEM;
        }
        if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) {
+               kfree(manbuf);
                chan->e.D3Id = 0;
                return -EFAULT;
        }
@@ -1577,7 +1727,11 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb)
 
         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 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb)
        }
 
        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;
        }
index d8115bf19449098efa4b796e9217e3506ef84dc7..9a1da1751303f97767e12d66517ec8292689c236 100644 (file)
@@ -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
  * 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 void idi_handle_ind(eicon_card *card, struct sk_buff *skb);
 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
index 1c69d37cd1a29c6e4a988749ccc1f8b0e667db93..79ce41eed87c5b5e7867421b32b8358bf5b62d4f 100644 (file)
@@ -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.
  * 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 @@ eicon_io_rcv_dispatch(eicon_card *ccard) {
                                                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 @@ eicon_io_transmit(eicon_card *ccard) {
         }
 
        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 @@ eicon_io_transmit(eicon_card *ccard) {
                        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 @@ eicon_io_transmit(eicon_card *ccard) {
                        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 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
        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 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
                        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 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
 
        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 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
                                }
                                (*irqprobe)++;
                                break;
+#endif
                        case EICON_CTYPE_MAESTRAP:
                                if (readb(&ram[0x3fe])) { 
                                        writeb(0, &prram->RcOutput);
@@ -581,6 +591,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
        }
 
        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 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
                                return;
                        } 
                        break;
+#endif
                case EICON_CTYPE_MAESTRAP:
                        if (!(readb(&ram[0x3fe]))) { /* card did not interrupt */
                                if (DebugVar & 1)
@@ -629,7 +641,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
                        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 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
                        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 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
                                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 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
                                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 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
 
        /* 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 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
                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]); 
index 184f1c394f515a081031f5ba0c85d85253d3c27b..924d5620f686743b520a72997304a61838da0b26 100644 (file)
@@ -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.
  * 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 @@ eicon_isa_load(eicon_isa_card *card, eicon_isa_codebuf *cb) {
        card->irqprobe = 0;
        return 0;
 }
+
+#endif /* CONFIG_ISDN_DRV_EICON_ISA */
index 92e385944de216283486cb8b52607bee94121f38..9efb770ed1f3c2db1994e432e3fc06510d364e3b 100644 (file)
@@ -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.
  * 
  * 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 @@ extern char *eicon_idi_revision;
 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 @@ eicon_command(eicon_card * card, isdn_ctrl * c)
                                                               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 @@ eicon_command(eicon_card * card, isdn_ctrl * c)
                                                               card->bus);
                                                        ret = -ENODEV;
                                        }                                       
+#endif
                                case EICON_IOCTL_GETIRQ:
                                        switch (card->bus) {
                                                case EICON_BUS_ISA:
@@ -434,6 +444,7 @@ eicon_command(eicon_card * card, isdn_ctrl * c)
                                                               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 @@ eicon_command(eicon_card * card, isdn_ctrl * c)
                                                        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 @@ eicon_command(eicon_card * card, isdn_ctrl * c)
                                                        ret = -ENODEV;
                                        }
                                        return ret;
-
+#endif
                                case EICON_IOCTL_MANIF:
                                        if (!card->flags & EICON_FLAGS_RUNNING)
                                                return -ENODEV;
@@ -813,7 +826,9 @@ eicon_alloccard(int Type, int membase, int irq, char *id)
        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 @@ eicon_alloccard(int Type, int membase, int irq, char *id)
                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 @@ eicon_alloccard(int Type, int membase, int irq, char *id)
                                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 @@ eicon_alloccard(int Type, int membase, int irq, char *id)
                                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 @@ eicon_alloccard(int Type, int membase, int irq, char *id)
                                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 @@ static int
 eicon_registercard(eicon_card * card)
 {
         switch (card->bus) {
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
                case EICON_BUS_ISA:
                        /* TODO something to print */
                        break;
@@ -1036,6 +1056,7 @@ eicon_registercard(eicon_card * card)
                case EICON_BUS_MCA:
                        eicon_isa_printpar(&card->hwif.isa);
                        break;
+#endif
 #endif
                case EICON_BUS_PCI:
 #if CONFIG_PCI
@@ -1070,12 +1091,14 @@ unregister_card(eicon_card * card)
         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 @@ eicon_addcard(int Type, int membase, int irq, char *id)
        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 @@ eicon_addcard(int Type, int membase, int irq, char *id)
                         */
                        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 @@ eicon_init(void))
        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 @@ eicon_init(void))
         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,13 +1252,22 @@ eicon_init(void))
 #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;
 
@@ -1291,7 +1336,7 @@ eicon_setup(char *str, int *ints))
 }
 #endif /* MODULE */
 
-
+#ifdef CONFIG_ISDN_DRV_EICON_ISA
 #ifdef CONFIG_MCA
 
 struct eicon_mca_adapters_struct {
@@ -1457,4 +1502,5 @@ int eicon_mca_probe(int slot,  /* slot-nr where the card was detected         */
        };
 };
 #endif /* CONFIG_MCA */
+#endif /* CONFIG_ISDN_DRV_EICON_ISA */
 
index 9e245288e8daa736138b016e7a816ebc49e3e9e8..a5611394c3aaf2be31313ebae5bb723f9f7d695d 100644 (file)
@@ -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.
  * 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 @@ int eicon_pci_find_card(char *ID)
           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 @@ int eicon_pci_find_card(char *ID)
          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);
index a8dd6b4f194961376a249e733de90c2d04245128..051df618271607af5843551450818571a2982c69 100644 (file)
@@ -197,7 +197,8 @@ endif
 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 $$?)
 
index 768a8ca220fa3499b7d2d31bd73f9f5fdab2032d..ef5ddd45fc4bf9565d48f264a24c260475fc1f48 100644 (file)
@@ -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 @@ setup_avm_pcipnp(struct IsdnCard *card))
                                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");
index cdd6603c9d2f7320b90b003329e11082e95fa6d6..e97c8e9bf01e07aca1c17c7f744df25bf2dd47f5 100644 (file)
@@ -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 @@ __initfunc(int
                        &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;
                }
        }
index 96a85d4d52e1851e66cfb2720048d9a77a1a5196..0e014ebc06e1f757bab17e5bb58fa80935216e7d 100644 (file)
@@ -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 @@ __initfunc(int
                        &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 @@ __initfunc(int
                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 */
index 9e306a0a385c80b010ad261a72960f20443e0b25..6284a4cf38caf9d3e4b3575c47d1d7338f1694c0 100644 (file)
@@ -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
  *              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 @@ extern long mod_use_count_;
 #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 @@ static inline void
 HL_LL(struct Channel *chanp, int command)
 {
         isdn_ctrl ic;
+
         ic.driver = chanp->cs->myid;
         ic.command = command;
         ic.arg = chanp->chan;
@@ -430,12 +436,21 @@ static void
 lli_go_active(struct FsmInst *fi, int event, void *arg)
 {
        struct Channel *chanp = fi->userdata;
+       isdn_ctrl ic;
+
 
        FsmChangeState(fi, ST_ACTIVE);
        chanp->data_open = !0;
+       if (chanp->bcs->conmsg)
+               strcpy(ic.parm.num, chanp->bcs->conmsg);
+       else
+               ic.parm.num[0] = 0;
        if (chanp->debug & 1)
-               link_debug(chanp, 0, "STAT_BCONN");
-       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 @@ init_b_st(struct Channel *chanp, int incoming)
                        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_command(isdn_ctrl * ic)
                                        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));
index a76736b600347bbebff05914c36206d12ee229d5..03f416d5bff3736b70c37123af3881df038ca98d 100644 (file)
@@ -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 @@ certification_check(int output) {
                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
index 707b952b5b0e88e33bbbb2ca1ad6483d39765469..6e54fd1dcfeb04043fb2bdf0503527a3f22791e3 100644 (file)
@@ -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 @@ HiSaxVersion(void))
 
        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 @@ checkcard(int cardnr, char *id, int *busy_flag))
                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
index 264bb3c95b60fe03026a37ffc175ad8485ef9e87..482a90695f87bd0ff7fd050dfd0b183ab99f0e07 100644 (file)
@@ -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 @@ setup_diva(struct IsdnCard *card))
                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 @@ setup_diva(struct IsdnCard *card))
                        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");
index 87935d80b4d0cfe41efb8fa28a8f61b0b7d09d82..8f47869ba8f78ecbe6aeb7a933c77a1a83f4da8a 100644 (file)
@@ -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
  *
  *              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
 
 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 @@ const char *ITACVer[] =
 #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 @@ elsa_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
                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 @@ release_io_elsa(struct IsdnCardState *cs)
                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 @@ reset_elsa(struct IsdnCardState *cs)
                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 @@ reset_elsa(struct IsdnCardState *cs)
                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 @@ elsa_led_handler(struct IsdnCardState *cs)
 {
        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 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg)
                        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 @@ setup_elsa(struct IsdnCard *card)
        } 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 @@ setup_elsa(struct IsdnCard *card)
                         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 @@ setup_elsa(struct IsdnCard *card)
                case ELSA_PCC16:
                case ELSA_QS1000:
                case ELSA_PCMCIA:
+               case ELSA_PCMCIA_IPAC:
                        bytecnt = 8;
                        break;
                case ELSA_PCFPRO:
@@ -1197,7 +1232,7 @@ setup_elsa(struct IsdnCard *card)
        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;
index 815860e629d71e8cabbb6a0b71b82a71b9ada58a..ea27f28b230ff62d1c0a920ada589d1a4cb520dd 100644 (file)
@@ -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 @@ setup_gazelpci(struct IsdnCardState *cs)
                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
index e0c60e24508b2ef5b217f1b534ed1c9f6b9d9578..2d972f3fe6259084ab7fc04947619f747d2ec80a 100644 (file)
@@ -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
  *
  * 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
  *
 
 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 @@ reset_hfcpci(struct IsdnCardState *cs)
        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 @@ hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type * bz, u_char * bdata, int cou
                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 @@ receive_dmsg(struct IsdnCardState *cs)
                        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 @@ hfcpci_fill_fifo(struct BCState *bcs)
 /* 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 @@ int hfcpci_set_echo(struct IsdnCardState *cs, int i)
   }
     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 @@ int hfcpci_set_echo(struct IsdnCardState *cs, int i)
   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 @@ static void receive_emsg(struct IsdnCardState *cs)
                    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 @@ mode_hfcpci(struct BCState *bcs, int mode, int bc)
                                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 @@ mode_hfcpci(struct BCState *bcs, int mode, int bc)
                                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 @@ __initfunc(int
 {
        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 @@ __initfunc(int
                        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 @@ __initfunc(int
                                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 @@ __initfunc(int
                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;
index f5ab125c2d20db4b1d90103f6dfb729d48367744..9f5124523234a61ee073035558eff51a92acb692 100644 (file)
@@ -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
  *
  * 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.
  *
 
 /* 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
 
index aa7c929b5dd5b435b00189ef56ef21c0650f1df9..edd3836431cbd5baa3dfbdde310941e7f29f1cf6 100644 (file)
@@ -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 @@ reset_hfcs(struct IsdnCardState *cs)
        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);
index 693e81c5bd41286223cfe163f9050022db00af4c..14c201386a024f97bc9845452798318d126342e9 100644 (file)
@@ -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 @@ struct Layer1 {
 #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 @@ struct isar_hw {
        int txcnt;
        int mml;
        u_char *rcvbuf;         /* B-Channel receive Buffer */
+       u_char conmsg[16];
        struct isar_reg *reg;
 };
 
@@ -519,11 +527,15 @@ struct amd7930_hw {
 #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 BCState {
        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;
index 6f0efe6897f6b49a4a8bcdf98995daa1f9b16fdb..6a130ed010011595d6e3ef2be5ecd1d538ab9376 100644 (file)
@@ -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 @@ isac_fill_fifo(struct IsdnCardState *cs)
        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 @@ isac_fill_fifo(struct IsdnCardState *cs)
        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;
 
index 4f5ec398c1d483de3b94599ecdf61a02c24183e9..05fb7fe9cb951ba95c5557684a33b534fd963588 100644 (file)
@@ -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 @@ isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs)
                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 @@ isar_fill_fifo(struct BCState *bcs)
                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 @@ check_send(struct IsdnCardState *cs, u_char rdm)
        }
        
 }
+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 @@ isar_int_main(struct IsdnCardState *cs)
                        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 @@ void
 setup_pump(struct BCState *bcs) {
        struct IsdnCardState *cs = bcs->cs;
        u_char dps = SET_DPS(bcs->hw.isar.dpath);
-       
+       u_char ctrl, param[6];
+
        switch (bcs->mode) {
                case L1_MODE_NULL:
                case L1_MODE_TRANS:
@@ -675,6 +832,44 @@ setup_pump(struct BCState *bcs) {
                                                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 @@ void
 setup_sart(struct BCState *bcs) {
        struct IsdnCardState *cs = bcs->cs;
        u_char dps = SET_DPS(bcs->hw.isar.dpath);
+       u_char ctrl, param[2];
        
        switch (bcs->mode) {
                case L1_MODE_NULL:
@@ -706,7 +902,17 @@ setup_sart(struct BCState *bcs) {
                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 @@ void
 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 @@ modeisar(struct BCState *bcs, int mode, int bc)
                                        &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 @@ modeisar(struct BCState *bcs, int mode, int bc)
                        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 @@ isar_l2l1(struct PStack *st, int pr, void *arg)
                        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);
index 7c4865d3dbfe298f67802a5b5c188da585dd9728..c17875263901c19125aab61612f4e59725dd6a36 100644 (file)
@@ -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
 
 #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
index fa8a62e35cee72f0d4b3cd6c2d02e59710a31e16..24d468da252803c8b377f4e1915dea930af6d565 100644 (file)
@@ -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
  *              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, ...);
 
@@ -162,6 +168,19 @@ static char *strL2Event[] =
 
 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)
 {
@@ -219,7 +238,7 @@ clear_exception(struct Layer2 *l2)
        test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
        test_and_clear_bit(FLG_REJEXC, &l2->flag);
        test_and_clear_bit(FLG_OWN_BUSY, &l2->flag);
-       test_and_clear_bit(FLG_PEER_BUSY, &l2->flag);
+       clear_peer_busy(l2);
 }
 
 inline int
@@ -1026,10 +1045,10 @@ l2_st7_got_super(struct FsmInst *fi, int event, void *arg)
 
        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 @@ l2_st8_got_super(struct FsmInst *fi, int event, void *arg)
        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 @@ l2_tei_remove(struct FsmInst *fi, int event, void *arg)
 }
 
 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 @@ static struct FsmNode L2FnList[] HISAX_INITDATA =
        {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},
index d7af34faa54545d946189135371b912dce06ab1d..458afe920984e0b9dd123941e08cd36492558ebc 100644 (file)
@@ -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 @@ release_l3_process(struct l3_process *p)
                        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 @@ release_l3_process(struct l3_process *p)
 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 @@ lc_connect(struct FsmInst *fi, int event, void *arg)
 {
        struct PStack *st = fi->userdata;
        struct sk_buff *skb = arg;
+       int dequeued = 0;
 
        FsmChangeState(fi, ST_L3_LC_ESTAB);
        while ((skb = skb_dequeue(&st->l3.squeue))) {
                st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+               dequeued++;
        }
-       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 @@ lc_connected(struct FsmInst *fi, int event, void *arg)
 {
        struct PStack *st = fi->userdata;
        struct sk_buff *skb = arg;
+       int dequeued = 0;
 
        FsmDelTimer(&st->l3.l3m_timer, 51);
        FsmChangeState(fi, ST_L3_LC_ESTAB);
        while ((skb = skb_dequeue(&st->l3.squeue))) {
                st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+               dequeued++;
        }
-       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 @@ lc_release_req(struct FsmInst *fi, int event, void *arg)
 {
        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 @@ lc_release_ind(struct FsmInst *fi, int event, void *arg)
 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);
 }
 
 
index dbb515f1a02fa2f0e4b6c99115067a0c3f06d7f6..c7050f5f13f902e1ef92ce45928bd863db187563 100644 (file)
@@ -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
 
index c192fa5d4e215307d42d1360bb8bf19956b94d03..1c541033f0fd6ba72fd2526cd07a806a91ec5ee1 100644 (file)
@@ -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
  *
  *              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[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
 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_RESUME_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
 static int ie_SETUP[] = {IE_COMPLETE, IE_BEARER  | IE_MANDATORY,
                IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_PROGRESS,
                IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_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 @@ check_infoelements(struct l3_process *pc, struct sk_buff *skb, int *checklist)
                                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 @@ DecodeSI2(struct sk_buff *skb)
 
                                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 @@ l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3dss1_setup(struct l3_process *pc, u_char pr, void *arg)
 /* 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 @@ l3dss1_setup(struct l3_process *pc, u_char pr, void *arg)
                        }
                        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 @@ l3dss1_setup(struct l3_process *pc, u_char pr, void *arg)
                                        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 @@ l3dss1_setup(struct l3_process *pc, u_char pr, void *arg)
        } 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 @@ l3dss1_progress(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_status_enq(struct l3_process *pc, u_char pr, void *arg)
 
        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_t303(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3dss1_resume_req(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_dl_reestablish(struct l3_process *pc, u_char pr, void *arg)
 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 @@ static struct stateentry datastatelist[] =
         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 @@ static struct stateentry datastatelist[] =
         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 @@ dss1up(struct PStack *st, int pr, void *arg)
                 */
                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 @@ setstack_dss1(struct PStack *st)
        strcpy(tmp, dss1_revision);
        printk(KERN_INFO "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp));
 }
-
-
-
-
-
index 16e33783408312c7e983854d73fa28b6aa186ccb..7fe2bbbae56e9faa050ce2ba5e46415b787ed619 100644 (file)
@@ -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-----
index 24d70a49255f6edca7899c5fddc493dcac1afb54..7043959e9db6a41579af65d4de0f5d292ae5ebeb 100644 (file)
@@ -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 @@ const char *NETjet_revision = "$Revision: 1.10 $";
 #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 @@ mode_tiger(struct BCState *bcs, int mode, int bc)
        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 @@ mode_tiger(struct BCState *bcs, int mode, int bc)
                        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 @@ mode_tiger(struct BCState *bcs, int mode, int bc)
                        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 @@ static int make_raw_data(struct BCState *bcs) {
                        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 @@ static void read_raw(struct BCState *bcs, u_int *buf, int cnt){
        int i;
        register u_char j;
        register u_char val;
-       u_int  *pend = bcs->hw.tiger.rec +NETJET_DMA_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_raw(struct BCState *bcs, u_int *buf, int cnt){
 
 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 @@ static void read_tiger(struct IsdnCardState *cs) {
                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 @@ static void fill_dma(struct BCState *bcs)
                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 @@ static void write_raw(struct BCState *bcs, u_int *buf, int cnt) {
                        }
                        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_raw(struct BCState *bcs, u_int *buf, int cnt) {
 }
 
 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 @@ static void write_tiger(struct IsdnCardState *cs) {
                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 @@ setstack_tiger(struct PStack *st, struct BCState *bcs)
 __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 @@ setup_netjet(struct IsdnCard *card))
                        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");
index 66d04bf3ffe4ba1c70b46fa60dd7c27c9eeaaedc..d83287d23287e880bf331689572aa1c98d299fae 100644 (file)
@@ -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 @@ setup_niccy(struct IsdnCard *card))
                                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");
index c8074dc026344f9f5d70527a704a0975d361150f..25303b6e942bae1ebe3e4f5b0bd79956849a26bc 100644 (file)
@@ -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),
  *            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 @@ sedlbauer_interrupt_isar(int intno, void *dev_id, struct pt_regs *regs)
 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 @@ Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg)
                        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 @@ setup_sedlbauer(struct IsdnCard *card))
                                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");
index d91181d5a923d4dc48e61d816500d8bb097516aa..f21284bece2c2a9a99a0f69af2e2467d38bc2ca2 100644 (file)
@@ -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
@@ -229,6 +232,25 @@ tei_id_assign(struct FsmInst *fi, int event, void *arg)
        }
 }
 
+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)
 {
@@ -472,6 +494,7 @@ release_tei(struct IsdnCardState *cs)
 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},
index b2365a90dd1105454d9db17ae4f22321f72a3ca2..dd9e5fb4a2fe965472394e84babe84a94e25166d 100644 (file)
@@ -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 @@ setup_telespci(struct IsdnCard *card))
                        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);
index df768d2704e8460ed2e9d34a559e28369744eb64..a6f13ef7643203931ff5862cec1f96de92a1e291 100644 (file)
@@ -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).
  *
  * 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 @@ static char dtmf_matrix[4][4] =
        {'*', '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)
 {
index 06fa3e7d7ffca2c69ab2d7e3fe537b873a18b86e..bdbcd419188c9d76f40345fca8c038868d47291c 100644 (file)
@@ -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.
  *
  * 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 @@ extern void eicon_init(void);
 #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 @@ isdn_cards_init(void)
        pcbit_init();
 #endif
 #ifdef CONFIG_ISDN_DRV_AVMB1
-       avmb1_init();
-#ifdef CONFIG_PCI
-       b1pci_init();
-#endif
+       kcapi_init();
        capi_init();
        capidrv_init();
 #endif
index 81f79bec4ee33d45db45e3800852a17d62e289de..cac4be6dcda552dc8b18d993f6cfbb5698cd1eff 100644 (file)
@@ -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).
  *
  * 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)
  *
 
 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 @@ isdn_wildmat(char *s, char *p)
        register int reverse;
        register int nostar = 1;
 
+       if (!(*s) && !(*p))
+               return(1);
        for (; *p; s++, p++)
                switch (*p) {
                        case '\\':
@@ -1041,6 +1052,11 @@ isdn_status_callback(isdn_ctrl * c)
                        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 @@ isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, struct w
                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 @@ cleanup_module(void)
        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");
index 105b5d3c1f2bda9d01aa6f7f82fd9acd64bf7169..d3a36c0e51b7cad8f4bf4d33b6a7252e387528d5 100644 (file)
@@ -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).
  *
  * 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 bit2si[8] =
 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_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb)
                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 @@ isdn_tty_modem_ncarrier(modem_info * info)
        }
 }
 
+/*
+ * 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 @@ isdn_tty_dial(char *n, modem_info * info, atemu * m)
                        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 @@ isdn_tty_dial(char *n, modem_info * info, atemu * m)
                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_tty_dial(char *n, modem_info * info, atemu * m)
  * 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 @@ isdn_tty_modem_hup(modem_info * info, int local)
        }
 #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_tty_modem_hup(modem_info * info, int local)
                }
                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 @@ isdn_tty_resume(char *id, modem_info * info, atemu * m)
                        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 @@ isdn_tty_resume(char *id, modem_info * info, atemu * m)
                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 @@ isdn_tty_send_msg(modem_info * info, atemu * m, char *msg)
                        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 @@ isdn_tty_modem_reset_vpar(atemu * m)
        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
 
@@ -2177,6 +2290,9 @@ isdn_tty_modem_reset_regs(modem_info * info, int force)
        }
 #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 @@ isdn_tty_modem_init(void)
        }
        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 @@ isdn_tty_match_icall(char *cid, atemu *emu, int di)
                                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 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup)
                    (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 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup)
                                        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 @@ isdn_tty_stat_callback(int i, isdn_ctrl * c)
                                        }
                                }
                                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 @@ isdn_tty_at_cout(char *msg, modem_info * info)
        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 @@ isdn_tty_at_cout(char *msg, modem_info * info)
        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 @@ isdn_tty_at_cout(char *msg, modem_info * info)
                        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 @@ isdn_tty_cmd_ATA(modem_info * info)
                /* 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 @@ isdn_tty_cmd_ATA(modem_info * info)
                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 @@ static int
 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 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info)
                                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 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info)
                                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 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info)
                }
                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_parse_at(modem_info * info)
                                                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);
index 33bd6b99a2181800e6f547efba4c9944d8c8f91e..4fe9568ca9e207c1e3092c25b6037b03e5c070c1 100644 (file)
@@ -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_stat_callback(int, isdn_ctrl *);
 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 --git a/drivers/isdn/isdn_ttyfax.c b/drivers/isdn/isdn_ttyfax.c
new file mode 100644 (file)
index 0000000..fc17b5b
--- /dev/null
@@ -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 --git a/drivers/isdn/isdn_ttyfax.h b/drivers/isdn/isdn_ttyfax.h
new file mode 100644 (file)
index 0000000..1ad75d2
--- /dev/null
@@ -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
+
index 2fff7e42b0592cb2a051db74b7251eab61fa4fc4..38e96cca2262d8b7df65872437f5d1c57b9e43ac 100644 (file)
@@ -558,6 +558,9 @@ static struct device * sis900_probe1(   int pci_bus,
                 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 @@ static struct device * sis900_probe1(   int pci_bus,
 
         /* 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;
 
index 634bb1820162de0e4c7d3dc340ad5ad1a64974c8..199b0c8ee25d957b86533bde732323e510e8555c 100644 (file)
@@ -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
index 1d56036e665919c4ed0455d13d8527b79f2b18b5..41505314878cb7b2cba6797dc59c27ff9660ad52 100644 (file)
@@ -72,6 +72,7 @@ obj-$(CONFIG_SOUND_UART6850)  += uart6850.o
 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
 
index 3464a48bac3edc7c55d8a1ae3c589631aab88e75..bc9ca8f35ea845a38740bc4d0e27988a7c1a87d5 100644 (file)
@@ -395,6 +395,9 @@ int soundcore_init(void)
        /*
         *      Now init non OSS drivers
         */
+#ifdef CONFIG_SOUND_VWSND
+       init_vwsnd();
+#endif
 #ifdef CONFIG_SOUND_SONICVIBES
        init_sonicvibes();
 #endif
diff --git a/drivers/sound/vwsnd.c b/drivers/sound/vwsnd.c
new file mode 100644 (file)
index 0000000..36e5052
--- /dev/null
@@ -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:
+ */
index 1d1575c15fa29cda69837222b31e4148cc0a3983..118941b3c86f8560fc20318e70776fab1e7cb247 100644 (file)
@@ -139,6 +139,8 @@ static inline void wrfpcr(unsigned long val)
 
 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__ */
 
index ff73c8e50376b8f686b169b7e4d14d009f64d3d8..b1a241ae504aabc843533a43ad22320a5af4ac9e 100644 (file)
@@ -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)
index 1de70680b1710ae59f4eeac8e594c54f151dd54d..626e557a57d9d360424b577bcfa7589340427866 100644 (file)
@@ -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 @@ extern void floppy_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 
 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 @@ static void sun_pci_fd_broken_outb(unsigned char val, unsigned long port)
 #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 @@ static void sun_pci_fd_reset_dma(void)
        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 @@ static void sun_pci_fd_disable_dma(void)
        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 @@ static void sun_pci_fd_set_dma_mode(int mode)
        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 void sun_pci_fd_set_dma_addr(char *buffer)
 
 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 @@ __initfunc(static unsigned long sun_floppy_init(void))
 #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 @@ __initfunc(static unsigned long sun_floppy_init(void))
                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);
-
-                       tmp = sun_floppy_types[0];
+                       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);
+
+                       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;
index 7e490c729c85d66542434f18c95e9ef5fd311fd7..a27d7a093acc8a6d45ea70ed33eb42d4d6543f58 100644 (file)
@@ -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)
index 66d1a1d68be27eb2dcd7447b212e9a610ae1d9c3..34f9fe4943e3a7e4251beb5bc9de88f6beedc98e 100644 (file)
@@ -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
index 274ed76e156777281dedcd025a86b32500372a8a..853f236f6fef07652b48690b398dfcac88b56385 100644 (file)
@@ -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.
  *
@@ -731,6 +734,10 @@ typedef struct modem_info {
   void                  *adpcmr;         /* state for adpcm compression    */
   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               */
index 3e896a2ce1fcfc9affca1fe39478910dfc66de5d..db002c4153192722414a0a562c7721436f6b0b66 100644 (file)
@@ -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 @@ static inline unsigned long copy_to_user(void *to, const void *from, unsigned lo
 
 #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)
index 49243bff6a95b93f40983240b423792fb5499d24..aeb177ab3956e547f7fde0aa83ebc3d25a30c252 100644 (file)
@@ -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 @@ typedef struct setup_parm {
     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 @@ typedef struct {
                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;
 
index 29a127ccfddd73921cf07193a69001194a792bdd..1646ee78a987ee8ed2e53cd903867c6dcfe1b339 100644 (file)
@@ -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
  *
index c65a691e2108ded09d0632c2392a65c005615a44..92b1078a44dd3e7358bfbe764bad2d66713a0da8 100644 (file)
@@ -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>
index 705c02088ea3e98236047d767e12dde2407eab6c..7c1902488847ada491b408ec75d7d653067b129d 100644 (file)
@@ -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
index dc99d14eacfcfa50e8a5483acb88f87d70885f64..3030144165f5481d29a58e2de58a0d6a11cc9554 100644 (file)
@@ -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
  *
index f1cbb88539821c89411927d92b474dbc36a083ca..473d0c8e615ec23dbedd5bf408a2365526267cb8 100644 (file)
@@ -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
index 898bc5f525a213fce98c067ef48da07d4d01c9f8..c4b1ef4c88e0e0511599ef8b4d25779f3130c791 100644 (file)
@@ -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>
  *
index 134d385c5e4e6bb21159bb589207086e39db5577..f369f03ddee6c7679efe7cf22186822b1cb32fd5 100644 (file)
@@ -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>
index 2df98e1f50eb39e2c9950fa8d57e602be46c2753..efee35a9442e059719aefadddff34ec0b51864c5 100644 (file)
@@ -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>
index 7975336c5130420a11e968307d4bd20c857eb66b..eed47ce7364f104ec3dff8b1ca7ab5685d6709eb 100644 (file)
@@ -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>
index e1622c0bb85b45f3fcdf217821e522008fe0bc84..f7b9d2ee6035bba3e1e133b2c54d790b18ecb719 100644 (file)
@@ -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>
index 6a1df60da1957287213bbca62d5823d291489e21..501a68a155264eebf5229e5105564d2bc14215aa 100644 (file)
@@ -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>
index f44f08ecbe7107fe06081de99972b7e49516f7ea..5bbeaa8304fdc719d7be02b418684b55b4fca24e 100644 (file)
@@ -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>
  *
index 314ec5ea9227f8f0950cdd25b74ba90c4b4a9bcb..a753b128243d9b446c2734722ab3d01a1523bd41 100644 (file)
@@ -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>
index 1decfc5857f9ca5da041ba6a984824e49d7031e0..c2c7836598a08f16c41f34677d6db1e5c8035d2a 100644 (file)
@@ -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
  *
index 6d7359aefafaeeba1f0f7902001d596aaf86c6d9..382071dff5f4186a152077d01974236459785f4b 100644 (file)
@@ -212,7 +212,7 @@ int ip6_input(struct sk_buff *skb)
 
                if (ipprot->copy || raw_sk)
                        buff = skb_clone(skb, GFP_ATOMIC);
-
+               /* buff == NULL ?????? */
                ipprot->handler(buff, len);
                found = 1;
        }
index fe86124688b76ea3a5c5673e3821868fe7962b6b..1bd6181dec4de917a69d64a004aae73085b26abe 100644 (file)
@@ -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 @@ static int tcp_v6_get_port(struct sock *sk, unsigned short snum)
                                        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;
                                        }
                                }