]> git.neil.brown.name Git - history.git/commitdiff
Import 1.3.69 1.3.69
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:37 +0000 (15:10 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:37 +0000 (15:10 -0500)
177 files changed:
CREDITS
Documentation/Configure.help
Documentation/cdrom/optcd
Documentation/filesystems/sysv-fs.txt
Documentation/isdn/CREDITS [new file with mode: 0644]
Documentation/isdn/INTERFACE [new file with mode: 0644]
Documentation/isdn/README [new file with mode: 0644]
Documentation/isdn/README.icn [new file with mode: 0644]
Documentation/isdn/README.syncppp [new file with mode: 0644]
Documentation/isdn/README.teles [new file with mode: 0644]
MAGIC
MAINTAINERS
Makefile
arch/alpha/defconfig
arch/i386/config.in
arch/i386/defconfig
arch/i386/kernel/process.c
arch/i386/kernel/smp.c
drivers/Makefile
drivers/block/Config.in
drivers/block/Makefile
drivers/block/README.md [new file with mode: 0644]
drivers/block/linear.c [new file with mode: 0644]
drivers/block/ll_rw_blk.c
drivers/block/loop.c
drivers/block/loop.h
drivers/block/md.c [new file with mode: 0644]
drivers/block/raid0.c [new file with mode: 0644]
drivers/cdrom/optcd.c
drivers/char/Config.in
drivers/char/Makefile
drivers/char/mem.c
drivers/char/tty_io.c
drivers/isdn/Config.in [new file with mode: 0644]
drivers/isdn/Makefile [new file with mode: 0644]
drivers/isdn/icn/Makefile [new file with mode: 0644]
drivers/isdn/icn/icn.c [new file with mode: 0644]
drivers/isdn/icn/icn.h [new file with mode: 0644]
drivers/isdn/isdn_cards.c [new file with mode: 0644]
drivers/isdn/isdn_cards.h [new file with mode: 0644]
drivers/isdn/isdn_common.c [new file with mode: 0644]
drivers/isdn/isdn_common.h [new file with mode: 0644]
drivers/isdn/isdn_net.c [new file with mode: 0644]
drivers/isdn/isdn_net.h [new file with mode: 0644]
drivers/isdn/isdn_ppp.c [new file with mode: 0644]
drivers/isdn/isdn_ppp.h [new file with mode: 0644]
drivers/isdn/isdn_tty.c [new file with mode: 0644]
drivers/isdn/isdn_tty.h [new file with mode: 0644]
drivers/isdn/teles/Makefile [new file with mode: 0644]
drivers/isdn/teles/buffers.c [new file with mode: 0644]
drivers/isdn/teles/callc.c [new file with mode: 0644]
drivers/isdn/teles/card.c [new file with mode: 0644]
drivers/isdn/teles/config.c [new file with mode: 0644]
drivers/isdn/teles/fsm.c [new file with mode: 0644]
drivers/isdn/teles/isdnl2.c [new file with mode: 0644]
drivers/isdn/teles/isdnl3.c [new file with mode: 0644]
drivers/isdn/teles/l3_1TR6.c [new file with mode: 0644]
drivers/isdn/teles/l3_1TR6.h [new file with mode: 0644]
drivers/isdn/teles/llglue.c [new file with mode: 0644]
drivers/isdn/teles/mod.c [new file with mode: 0644]
drivers/isdn/teles/q931.c [new file with mode: 0644]
drivers/isdn/teles/tei.c [new file with mode: 0644]
drivers/isdn/teles/teles.h [new file with mode: 0644]
drivers/net/3c509.c
drivers/net/Config.in
drivers/net/Makefile
drivers/net/Space.c
drivers/net/new_tunnel.c
drivers/net/ni52.c
drivers/net/ni52.h
drivers/net/ni65.c
drivers/net/ni65.h
drivers/net/slip.c
drivers/scsi/advansys.c
drivers/sound/.blurb
drivers/sound/.version
drivers/sound/CHANGELOG
drivers/sound/Makefile
drivers/sound/Readme
drivers/sound/Readme.cards
drivers/sound/ad1848.c
drivers/sound/audio.c
drivers/sound/configure.c
drivers/sound/cs4232.c
drivers/sound/dev_table.h
drivers/sound/dmabuf.c
drivers/sound/gus_card.c
drivers/sound/gus_wave.c
drivers/sound/hex2hex.h
drivers/sound/mad16.c
drivers/sound/maui.c
drivers/sound/mpu401.c
drivers/sound/opl3.c
drivers/sound/pas2_card.c
drivers/sound/pas2_pcm.c
drivers/sound/pss.c
drivers/sound/sb16_dsp.c
drivers/sound/sb16_midi.c
drivers/sound/sb_dsp.c
drivers/sound/sb_mixer.c
drivers/sound/sequencer.c
drivers/sound/sound_switch.c
drivers/sound/soundcard.c
drivers/sound/soundvers.h
drivers/sound/uart6850.c
fs/binfmt_aout.c
fs/block_dev.c
fs/buffer.c
fs/ext/file.c
fs/ext2/ialloc.c
fs/ext2/super.c
fs/fat/file.c
fs/fat/inode.c
fs/ncpfs/sock.c
fs/nfs/file.c
fs/proc/array.c
fs/super.c
fs/sysv/file.c
fs/sysv/inode.c
fs/umsdos/check.c
fs/umsdos/ioctl.c
fs/umsdos/mangle.c
fs/xiafs/file.c
include/asm-i386/smp.h
include/asm-i386/string-486.h [new file with mode: 0644]
include/asm-i386/string.h
include/linux/blk.h
include/linux/blkdev.h
include/linux/cdrom.h
include/linux/fs.h
include/linux/isdn.h [new file with mode: 0644]
include/linux/isdn_ppp.h [new file with mode: 0644]
include/linux/isdnif.h [new file with mode: 0644]
include/linux/linear.h [new file with mode: 0644]
include/linux/major.h
include/linux/md.h [new file with mode: 0644]
include/linux/mm.h
include/linux/optcd.h
include/linux/pagemap.h
include/linux/pipe_fs_i.h
include/linux/ppp_defs.h
include/linux/proc_fs.h
include/linux/raid0.h [new file with mode: 0644]
include/linux/soundcard.h
include/linux/swap.h
include/linux/sysctl.h
include/linux/sysv_fs.h
include/linux/timer.h
include/linux/ultrasound.h
include/net/sock.h
init/main.c
ipc/msg.c
ipc/shm.c
kernel/ksyms.c
kernel/sysctl.c
mm/filemap.c
mm/kmalloc.c
mm/memory.c
mm/mlock.c
mm/mmap.c
mm/page_io.c
mm/vmscan.c
net/core/dev.c
net/core/sock.c
net/ipv4/af_inet.c
net/ipv4/arp.c
net/ipv4/icmp.c
net/ipv4/packet.c
net/ipv4/tcp_input.c
net/ipx/af_ipx.c
scripts/Menuconfig
scripts/header.tk
scripts/lxdialog/checklist.c
scripts/lxdialog/dialog.h
scripts/lxdialog/lxdialog.c
scripts/patch-kernel
scripts/tkgen.c

diff --git a/CREDITS b/CREDITS
index ea8e77ec07eeb316dc63e79b5b785a2c451dcc79..8c5649256a679fd1e724c583128a62ad3ad50900 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -380,10 +380,10 @@ S: The Netherlands
 
 N: Bruno Haible
 E: haible@ma2s2.mathematik.uni-karlsruhe.de
-D: Unified SysV FS based on Xenix FS (part of standard kernel since 0.99.15)
-S: Augartenstrasse 40
-S: D - 76137 Karlsruhe
-S: Germany
+D: SysV FS, shm swapping, memory management fixes
+S: 17 rue Danton
+S: F - 94270 Le Kremlin-Bicêtre
+S: France
 
 N: Greg Hankins
 E: gregh@cc.gatech.edu
@@ -984,6 +984,13 @@ S: Post Office Box 500
 S: Batavia, Illinois 60510
 S: USA
 
+N: Leo Spiekman
+E: spiekman@et.tudelft.nl
+D: Optics Storage 8000AT cdrom driver
+W: http://dutettk.et.tudelft.nl/~spiekman
+S: Utrecht
+S: The Netherlands
+
 N: Drew Sullivan
 E: drew@lethe.north.net
 D: iBCS2 developer
index 769a190c2cc4dde1c5c6a7ae2c78d659a3f90230..2459f237f68948f80c60916700aeb92d5093e6bb 100644 (file)
@@ -1759,9 +1759,14 @@ NI5210 support
 CONFIG_NI52
   If you have a network (ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than
-  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
-  available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
+  as a module ( = code which can be inserted in and removed from the
+  running kernel whenever you want). If you want to compile it as a
+  module, say M here and read Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt. If you plan to use more
+  than one network card under linux, read the
+  Multiple-Ethernet-mini-HOWTO, available from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 NI6510 support
 CONFIG_NI65
@@ -2447,6 +2452,18 @@ CONFIG_NCP_FS
   running kernel whenever you want), say M here and read
   Documentation/modules.txt.
 
+Standard/generic serial support
+CONFIG_SERIAL
+  This selects whether you want to include the driver for the
+  standard (0x3F8, 0x2F8, etc.) serial ports. Most people will
+  say "y" here, so that they can use serial mice, modems and
+  similar devices. People who might say "n" here are those that
+  are setting up dedicated ethernet WWW/ftp servers, or users
+  that have one of the various bus mice instead of a serial mouse.
+  Note that the Cyclades and Stallion drivers do not need this
+  driver built in for them to work. They are completely independent
+  of each other.
+
 Cyclades async mux support
 CONFIG_CYCLADES
   This is a card which gives you many serial ports. You would need
@@ -2743,6 +2760,51 @@ CONFIG_PROFILE_SHIFT
   enabled "Kernel profiling support", you must be a kernel hacker and
   hence you know what this is about :-)
 
+ISDN subsystem
+CONFIG_ISDN
+  This allows you to use an ISDN-card for networking connections and as
+  dialin/out device. The isdn-tty's have a builtin AT-compatible modem
+  emulator. Network devices support autodial, channel-bundling, callback
+  and caller-authentication without having a daemon running. A reduced T.70
+  protocol is supported with tty's suitable for german BTX. Currently Cards
+  by Teles and compatibles and ICN are supported. On D-Channel, the protocols
+  EDSS1 and 1TR6 are supported. See Documentation/isdn/README for more
+  information.
+
+Support synchronous PPP
+CONFIG_ISDN_PPP
+  This enables synchronous PPP via ISDN. This protocol is used by Cisco
+  or Sun for example. You will need a special version of pppd (called ipppd)
+  for using this feature. See Documentation/isdn/README.syncppp for more
+  information.
+
+Sypport generic MP (RFC 1717)
+CONFIG_ISDN_MPP
+  With synchronous PPP enabled, it is possible to increase throughput by
+  bundling several ISDN-connections, using this protocol. See
+  Documentation/isdn/README.syncppp for more information.
+
+Use VJ-compression with synchronous PPP
+CONFIG_ISDN_PPP_VJ
+  This enables Van Jacobson headercompression for synchronous PPP.
+
+ICN B1 and B2 support
+CONFIG_ISDN_DRV_ICN
+  This enables support for two kinds of ISDN-cards made by a german company
+  called ICN. 1B is the standard version for a single ISDN line with two
+  B-channels, 2B supports two ISDN lines. For running this card, additional
+  firmware is necessary, which has to be downloaded into the card using
+  a utility which is distributed separately.
+  See Documentation/isdn/README and README.icn for more information.
+
+Teles, NICCY1016PC, Creatix support
+CONFIG_ISDN_DRV_TELES
+  This enables support for the Teles ISDN-cards S0-16.0, S0-16.3, S0-8 and
+  many compatibles. By default, the driver is configured to support
+  a 16.0-type using EDSS1-protocol. See Documentation/isdn/README
+  on how to configure it using 16.3, a different D-channel protocol, or
+  non-standard irq/port/shmem settings.
+
 # need an empty line after last entry, for sed script in Configure.
 
 #
index 279684c508d4e2e8f48374513d72d2580984f505..2d66a7301bfc8d15efe622febc4efdddba1b3176 100644 (file)
@@ -1,27 +1,20 @@
 This is the README file for the Optics Storage 8000 AT CDROM device driver.
 
-The driver contains code to enable an ISP16 interface if it finds one.  It
-didn't originally (although this README erroneously said so), because I think
-this kind of code should go into its own module. But having to use a hack all
-the time in order to use a part of the standard kernel started to annoy me, so
-I copied the ISP16 code by Eric van der Maarel (maarel@marin.nl) from Vadim
-Model's Sanyo sjcd driver. I'll remove it again from this driver when we have
-some common way to talk to ISP16 interfaces.
-
-My original configuration code for the ISP-16 card can get found at
+This is the driver for the so-called 'DOLPHIN' drive, with the 34-pin
+Sony-compatible interface. For the IDE-compatible Optics Storage 8001
+drive, you will want the ATAPI CDROM driver. If you have a drive that
+works with this driver, and that doesn't report itself as DOLPHIN,
+please drop me a mail.
+
+The support for multisession CDs is in ALPHA stage. If you use it,
+please mail me your experiences. Multisession support can be disables
+at compile time.
+
+You can find some older versions of the driver at
       dutette.et.tudelft.nl:/pub/linux/
 and at Eberhard's mirror
       ftp.gwdg.de:/pub/linux/cdrom/drivers/optics/
 
-Much more elaborate information can be found at ftp:rbrf.msk.su,
-where Vadim Model (vadim@rbrf.msk.su) has made available an ISP16
-device driver.
-Vadim's directory is
-      rbrf.msk.su:/linux/mediamagic/
-and Eberhard is holding a mirror at
-      ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/
-
-
 Before you can use the driver, you have to create the device file once:
  # mknod /dev/optcd0 b 17 0
 
@@ -36,15 +29,28 @@ or
  # insmod /usr/src/linux/modules/optcd.o optcd=0x340
 with the matching address value of your interface card.
 
-I have tried the module with several 1.2.x kernel versions, and it seems to
-work, as far as I tested. It also seems to work for several 1.3.x versions.
-If you use it, I'd appreciate success/failure reports. If you find a bug,
-try recompiling the driver with some strategically chosen #undef DEBUG_...'s
-changed into #defines (you'll find them in .../include/linux/optcd.h) and
-include the messages generated in your bug report. Good luck.
-
-I have inserted code to support multisession. It works for me, although
-it is very slow during disk detection. At this time multisession support
-is to be considered experimental. Please mail me your experiences.
+The driver employs a number of buffers to do read-ahead and block size
+conversion. The number of buffers is configurable in optcd.h, and has
+influence on the driver performance. For my machine (a P75), 6 buffers
+seems optimal, as can be seen from this table:
+
+#bufs  kb/s    %cpu
+1      97      0.1
+2      191     0.3
+3      188     0.2
+4      246     0.3
+5      189     19
+6      280     0.4
+7      281     7.0
+8      246     2.8
+16     281     3.4
+
+If you get a throughput significantly below 300 kb/s, try tweaking
+N_BUFS, and don't forget to mail me your results!
+
+I'd appreciate success/failure reports. If you find a bug, try
+recompiling the driver with some strategically chosen debug options
+(these can be found in optcd.h) and include the messages generated in
+your bug report. Good luck.
 
 Leo Spiekman (spiekman@dutette.et.tudelft.nl)
index d318eb64b42dca7c0ac110e02ab080468c178202..d6ba74af07ae5a35e8e05eec63e5478440fbe0c3 100644 (file)
@@ -28,7 +28,7 @@ Bugs in the present implementation:
 
 
 Please report any bugs and suggestions to
-  Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhde.de> or
+  Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de> or
   Pascal Haible <haible@izfm.uni-stuttgart.de> .
 
 
diff --git a/Documentation/isdn/CREDITS b/Documentation/isdn/CREDITS
new file mode 100644 (file)
index 0000000..bdc4f3d
--- /dev/null
@@ -0,0 +1,42 @@
+
+I want to thank all who contributed to this project and especially to:
+(in alphabetical order)
+
+Thomas Bogendörfer (tsbogend@bigbug.franken.de)
+  Tester, lots of bugfixes and hints.
+
+Alan Cox ()
+  For help getting into standard-kernel.
+
+Volker Götz (volker@oops.franken.de)
+  For contribution of man-pages, the imontty-tool and a perfect
+  maintaining of the mailing-list at hub-wue.
+
+Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+  For his Sync-PPP-code.
+
+Karsten Keil (isdn4@temic-ech.spacenet.de)
+  For adding 1TR6-support to the Teles-driver.
+
+Michael Knigge (knick@cove.han.de)
+  For contributing the imon-tool
+
+Andreas Kool (akool@Kool.f.EUnet.de)
+  For contribution of the isdnlog/isdnrep-tool
+
+Pedro Roque Marques
+  For lot of new ideas and writing a new driver coming soon.
+
+Jan den Ouden (denouden@groovin.xs4all.nl)
+  For contribution of the teles-driver
+
+Max Riegel (riegel@max.franken.de)
+  For making the ICN hardware-documentation and test-equipment available.
+
+Gerhard 'Fido' Schneider (fido@wuff.franken.de)
+  For heavy-duty-beta-testing with his BBS ;)
+
+Thomas Uhl (uhl@hn-net.de)
+  For distributing the cards.
+  For pushing me to work ;-)
+
diff --git a/Documentation/isdn/INTERFACE b/Documentation/isdn/INTERFACE
new file mode 100644 (file)
index 0000000..44e9b6c
--- /dev/null
@@ -0,0 +1,609 @@
+
+Description of the Interface between Linklevel an Harwarelevel
+  of isdn4linux:
+
+
+  The Communication between Linklevel (LL) and Hardwarelevel (HL)
+  is based on the struct isdn_if (defined in isdnif.h).
+
+  An HL-driver can register itself at LL by calling the function
+  register_isdn() with a pointer to that struct. Prior to that, it has
+  to preset some of the fields of isdn_if. The LL sets the rest of
+  the fields. All further communication is done via callbacks using
+  the funtion-pointers defined in isdn_if.
+
+  ATTENTION, CHANGES since version 0.6 are marked with   "***CHANGE0.6"!
+  ATTENTION, CHANGES since version 0.7 are marked with   "***CHANGE0.7"!
+  ATTENTION, CHANGES since version 0.71 are marked with  "***CHANGE0.7.1"!
+
+1. Description of the fields of isdn_if:
+
+  int channels;
+
+    This field has to be set by the HL-driver to the number of channels
+    supported prior to calling register_isdn(). Upon return of the call,
+    the LL puts an id there, which has to be used by the HL-driver when
+    invoking the other callbacks.
+
+  int maxbufsize;
+
+    ***CHANGE0.6: New since this version.
+
+    Also to be preset by the HL-driver. With this value the HL-driver
+    tells to the LL the maximum size of a data-packet it will accept. 
+
+  unsigned long features;
+
+    To be preset by the HL-driver. Using this field, the HL-driver
+    announces the features supported. At the moment this is limited to
+    report the supported layer2 and layer3-protocols. For setting this
+    field the constants ISDN_FEATURE..., declared in isdnif.h have to be
+    used.
+
+    ***CHANGE0.7.1: The line type (1TR6, EDSS1) has to be set.
+
+  unsigned short hl_hdrlen;
+
+    ***CHANGED.7.4: New field.
+
+    To be preset by the HL-driver, if it supports sk_buff's. The driver
+    should put here the amount of additional space needed in sk-buff's for
+    it's internal purposes. Drivers not supporting sk_buff's should put
+    initialize this field to 0.
+
+  void (*rcvcallb)(int, int, u_char*, int);
+
+    This field will be set by LL. The HL-driver delivers received data-
+    packets by calling this function.
+
+    Parameter:
+      int    driver-Id
+      int    Channel-number locally to the driver. (starting with 0)
+      u_char Pointer to received data. (in kernel-space)
+      int    length of data-packet.
+
+  void (*rcvcallb_skb)(int, int, struct sk_buff *)
+
+    ***CHANGED.7.4: New field.
+
+    This field will be set by LL. The HL-driver delivers received data-
+    packets by calling this function. Upon calling, the HL-driver must
+    already have it's private data pulled off the head of the sk_buff.
+
+    Parameter:
+      int              driver-Id
+      int              Channel-number locally to the driver. (starting with 0)
+      struct sk_buff * Pointer to sk_buff, containing received data.
+
+  int (*statcallb)(isdn_ctrl*);
+
+    This field will be set by LL. This function has to be called by the
+    HL-driver for signaling status-changes or other events to the LL.
+
+    Parameter:
+      isdn_ctrl*
+
+      The struct isdn_ctrl also defined in isdn_if. The exact meaning of it's
+      fields is described together with the descriptions of the possible
+      events. Here is only a short description of the fields:
+
+        driver  = driver Id.
+        command = event-type. (one of the constants ISDN_STAT_...)
+        arg     = depends on event-type.
+        num     = depends on event-type.
+
+    Returnvalue:
+      0 on success, else -1
+
+  int (*command)(isdn_ctrl*);
+
+    This field has to be preset by the HL-driver. It points to a function,
+    to be called by LL to perform functions like dialing, B-channel
+    setup, etc. The exact meaning of the parameters is described with the
+    descriptions of the possible commands.
+
+    Parameter:
+      isdn_ctrl*
+        driver  = driver-Id
+        command = command to perform. (one of the constants ISDN_CMD_...)
+        arg     = depends on command.
+        num     = depends on command.
+    
+    Returnvalue:
+      >=0 on success, else error-code (-ENODEV etc.)
+
+  int (*writebuf)(int, int, u_char*, int, int);
+
+    This field has to be preset by the HL-driver. The given funtion will
+    be called by the LL for delivering data to be send via B-Channel.
+
+    Parameter:
+      int     driver-Id ***CHANGE.7.4: New parameter.
+      int     channel-number locally to the HL-driver. (starts with 0)
+      u_char* pointer to data.
+      int     length of data-packet.
+      int     flag: 0 = call from within kernel-space. (HL-driver must use
+                        memcpy, may NOT use schedule())
+                    1 = call from user-space. (HL-driver must use
+                        memcpy_fromfs, use of schedule() allowed)
+
+    Returnvalue:
+      Length of data accepted on success, else error-code (-EINVAL on
+      oversized packets etc.)
+
+  int (*writebuf_skb)(int, int, struct sk_buff *)
+
+    ***CHANGED.7.4: New field.
+
+    This field has to be preset by the HL-driver. The given function will
+    be called by the LL for delivering data to be send via B-Channel.
+
+    Parameter:
+      int              driver-Id ***CHANGE.7.4: New parameter.
+      int              channel-number locally to the HL-driver. (starts with 0)
+      struct sk_buff * Pointer to sk_buff containing data to be send via
+                       B-channel.
+
+    Returnvalue:
+      Length of data accepted on success, else error-code (-EINVAL on
+      oversized packets etc.)
+
+NOTE on writebuf and writebuf_skb:
+    The HL-driver may initialize one of the field's to NULL, in which case
+    the LL will call the non-NULL function only.
+
+  int (*writecmd)(u_char*, int, int);
+
+    This field has to be preset by the HL-driver. The given function will be
+    called to perform write-requests on /dev/isdnctrl (i.e. sending commands
+    to the card) The data-format is hardware-specific. This function is
+    intended for debugging only. It is not necessary for normal operation
+    and never will be called by the tty-emulation- or network-code. If
+    this functin is not supported, the driver has to set NULL here.
+
+    Parameter:
+      u_char* pointer to data.
+      int     length of data.
+      int     flag: 0 = call from within kernel-space. (HL-driver must use
+                        memcpy, may NOT use schedule())
+                    1 = call from user-space. (HL-driver must use
+                        memcpy_fromfs, use of schedule() allowed)
+
+    Returnvalue:
+      Length of data accepted on success, else error-code (-EINVAL etc.)
+
+  int (*readstat)(u_char*, int, int);
+
+    This field has to be preset by the HL-driver. The given function will be
+    called to perform read-requests on /dev/isdnctrl (i.e. reading repies
+    from the card) The data-format is hardware-specific. This function is
+    intended for debugging only. It is not necessary for normal operation
+    and never will be called by the tty-emulation- or network-code. If
+    this functin is not supported, the driver has to set NULL here.
+
+    Parameter:
+      u_char* pointer to data.
+      int     length of data.
+      int     flag: 0 = call from within kernel-space. (HL-driver must use
+                        memcpy, may NOT use schedule())
+                    1 = call from user-space. (HL-driver must use
+                        memcpy_fromfs, use of schedule() allowed)
+
+    Returnvalue:
+      Length of data on success, else error-code (-EINVAL etc.)
+
+  char id[20];
+       ***CHANGE0.7: New since this version.
+
+   This string has to be preset by the HL-driver. It's purpose is for
+   identification of the driver by the user. Eg.: it is shown in the
+   status-info of /dev/isdninfo. Furthermore it is used as Id for binding
+   net-interfaces to a specific channel. If a string of length zero is
+   given, upon return, isdn4linux will replace it by a generic name. (line0,
+   line1 etc.) It is recommended, to make this string configurabele during
+   module-load-time. (copy a global variable to this string.) For doing that,
+   modules 1.2.8 or newer are necessary.
+
+2. Description of the commands, a HL-driver has to support:
+
+   All commands will be performed by calling the funtion command() described
+   above from within the LL. The field command of the struct-parameter will
+   contain the desired command, the field driver always is set to the
+   apropriate driver-Id.
+
+   Until now, the following commands are defined:
+
+
+   ISDN_CMD_IOCTL:
+
+     This command is intended for performing ioctl-calls for configuring
+     hardware or similar purposes (setting port-adresses, loading firmware
+     etc.) For this purpose, in the LL all ioctl-calls with an argument
+     >= ISDN_IOCTL_DRVIOCTL (0x100) will be handed transparently to this
+     function after substracting 0x100 and placing the result in arg.
+     Example:
+       If a userlevel-program calls ioctl(0x101,...) the function gets
+       called with the field command set to 1.
+
+     Parameter:
+       driver  = driver-Id.
+       command = ISDN_CMD_IOCTL
+       arg     = Original ioctl-cmd - ISDN_IOCTL_DRVIOCTL
+       num     = first bytes filled with (unsigned long)arg
+   
+     Returnvalue:
+       Depending on driver.
+
+  
+  ISDN_CMD_DIAL:
+
+    This command is used to tell the HL-driver it should dial a given
+    number.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_DIAL
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = An ASCII-String containing the number to dial, the own
+                    EAZ or MSN, the Service-Indicator and the Additional
+                    Info. Format:
+                    "%s,%s,%d,%d" RemotePhoneNumber,EazOrMsn,SI,AI
+                    If the Line has been designed as SPV (a special german
+                    feature, meaning semi-leased-line) the number has to
+                    start with an "S".
+      ***CHANGE0.6: In previous versions the EAZ has been given in the
+                    highbyte of arg.
+    ***CHANGE0.7.1: New since this version: ServiceIndicator and AddInfo.
+
+  ISDN_CMD_ACCEPTD:
+
+    With this command, the HL-driver is told to accept a D-Channel-setup.
+    (Response to an incoming call)
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_ACCEPTD
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_CMD_ACCEPTB:
+
+    With this command, the HL-driver is told to perform a B-Channel-setup.
+    (after establishing D-Channel-Connection)
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_ACCEPTB
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_CMD_HANGUP:
+
+    With this command, the HL-driver is told to hangup (B-Channel if
+    established first, then D-Channel). This command is also used for
+    actively rejecting an incoming call.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_HANGUP
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_CMD_CLREAZ:
+
+    With this command, the HL-driver is told not to signal incoming
+    calls to the LL.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_CLREAZ
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_CMD_SETEAZ:
+
+    With this command, the HL-driver is told to signal incoming calls for
+    the given EAZs/MSNs to the LL.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_SETEAZ
+      arg         = channel-number locally to the driver. (starting with 0)
+      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-adress.
+      ***CHANGE0.6: New since this version the "empty-string"-feature.
+
+  ISDN_CMD_GETEAZ: (currently unused)
+
+    With this command, the HL-driver is told to report the current setting
+    given with ISDN_CMD_SETEAZ.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_GETEAZ
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = ASCII-String, containing the current EAZ's/MSN's
+
+  ISDN_CMD_SETSIL: (currently unused)
+
+    With this command, the HL-driver is told to signal only incoming
+    calls with the given Service-Indicators.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_SETSIL
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = ASCII-String, containing the desired Service-Indicators.
+
+  ISDN_CMD_GETSIL: (currently unused)
+
+    With this command, the HL-driver is told to return the current
+    Service-Indicators it will respond to.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_SETSIL
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = ASCII-String, containing the current Service-Indicators.
+
+  ISDN_CMD_SETL2:
+
+    With this command, the HL-driver is told to select the given Layer-2-
+    protocol. This command is issued by the LL prior to ISDN_CMD_DIAL or
+    ISDN_CMD_ACCEPTD.
+
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_SETL2
+      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...
+      num         = unused.
+
+  ISDN_CMD_GETL2: (currently unused)
+
+    With this command, the HL-driver is told to return the current
+    setting of the Layer-2-protocol.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_GETL2
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = unused.
+    Returnvalue:
+      current protocol-Id (one of the constants ISDN_L2_PROTO)
+
+  ISDN_CMD_SETL3:
+
+    With this command, the HL-driver is told to select the given Layer-3-
+    protocol. This command is issued by the LL prior to ISDN_CMD_DIAL or
+    ISDN_CMD_ACCEPTD.
+
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_SETL3
+      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...
+      num         = unused.
+
+  ISDN_CMD_GETL2: (currently unused)
+
+    With this command, the HL-driver is told to return the current
+    setting of the Layer-3-protocol.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_GETL3
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = unused.
+    Returnvalue:
+      current protocol-Id (one of the constants ISDN_L3_PROTO)
+
+  ISDN_CMD_LOCK:
+
+    With this command the HL-driver is told, that it will be used by the
+    LL and therefore may not be unloaded. The HL-driver should use
+    MOD_INC_USE_COUNT to tell that to the kernel.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_LOCK
+      arg         = unused.
+      num         = unused.
+
+  ISDN_CMD_UNLOCK:
+
+    With this command the HL-driver is told, that it is no more used by the
+    LL and therefore may be unloaded. The HL-driver should use
+    DEC_INC_USE_COUNT to tell that to the kernel.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_UNLOCK
+      arg         = unused.
+      num         = unused.
+
+3. Description of the events to be signaled by the HL-driver to th LL.
+
+  All status-changes are signaled via calling the previously described
+  function statcallb(). The field command of the struct isdn_cmd has
+  to be set by the HL-driver with the apropriate Status-Id (event-number).
+  The field arg has to be set to the channel-number (locally to the driver,
+  starting with 0) to which this event applies. (Exception: STAVAIL-event)
+
+  Until now, the following Status-Ids are defined:
+
+  ISDN_STAT_AVAIL:
+
+    With this call, the HL-driver signals the availability of new data
+    for readstat(). Used only for debugging-purposes, see description
+    of readstat().
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_STAVAIL
+      arg         = length of available data.
+      num         = unused.
+
+  ISDN_STAT_ICALL:
+
+    With this call, the HL-driver signals an incoming call to the LL.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_ICALL
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = ASCII-String in the following format:
+                    "%s,%d,%d,%s",CallerNumber,ServiceIndicator,AddInfo,
+                    CalledNumber.
+
+  ISDN_STAT_RUN:
+
+    With this call, the HL-driver signals availability of the ISDN-card.
+    (after initializing, loading firmware)
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_RUN
+      arg         = unused.
+      num         = unused.
+
+  ISDN_STAT_STOP:
+
+    With this call, the HL-driver signals unavailability of the ISDN-card.
+    (before unloading, while resetting/reconfiguring the card)
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_STOP
+      arg         = unused.
+      num         = unused.
+
+  ISDN_STAT_DCONN:
+
+   With this call, the HL-driver signals the successful establishement of
+   a D-Channel-connection. (Response to ISDN_CMD_ACCEPTD or ISDN_CMD_DIAL)
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_DCONN
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_STAT_BCONN:
+
+   With this call, the HL-driver signals the successful establishement of
+   a B-Channel-connection. (Response to ISDN_CMD_ACCEPTB or because the
+   remote-station has initiated establishement)
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_BCONN
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_STAT_DHUP:
+
+   With this call, the HL-driver signals the shutdown of a
+   D-Channel-connection. This could be a response to a prior ISDN_CMD_HANGUP,
+   or caused by a remote-hangup or if the remote-station has actively
+   rejected a call.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_DHUP
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_STAT_BHUP:
+
+   With this call, the HL-driver signals the shutdown of a
+   B-Channel-connection. This could be a response to a prior ISDN_CMD_HANGUP,
+   or caused by a remote-hangup.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_BHUP
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_STAT_CINF:
+
+   With this call, the HL-driver delivers charge-unit information to the
+   LL.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_CINF
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = ASCII string containing charge-units (digits only).
+
+  ISDN_STAT_LOAD: (currently unused)
+
+  ISDN_STAT_UNLOAD:
+
+   With this call, the HL-driver signals that it will be unloaded now. This
+   tells the LL to release all corresponding data-structures.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_UNLOAD
+      arg         = unused.
+      num         = unused.
+
+  ISDN_STAT_BSENT:
+
+    With this call the HL-driver signals the delivery of a data-packet.
+    This callback is used by the network-interfaces only, tty-Emulation
+    does not need this call.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_BSENT
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_STAT_NODCH:
+
+    With this call, the driver has to respond to a prior ISDN_CMD_DIAL, if
+    no D-Channel is available.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_NODCH
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_STAT_ADDCH: (currently unused)
+
+    This call is planned 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
+    the real amount, perform this call, allocating additional channels.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_ADDCH
+      arg         = to be defined.
+      num         = to be defined.
+
+  ISDN_STAT_CAUSE:
+
+    With this call, the HL-driver delivers CAUSE-messages to the LL.
+    Currently the LL does not use this messages. Their contents is simply
+    logged via kernel-messages. Therefore, currently the format of the
+    messages is currently completely free. However they should be printable.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_NODCH
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = ASCII string containing CAUSE-message.
+
diff --git a/Documentation/isdn/README b/Documentation/isdn/README
new file mode 100644 (file)
index 0000000..c37b85f
--- /dev/null
@@ -0,0 +1,590 @@
+README for the ISDN-subsystem
+
+1. Preface
+
+  1.1 Introduction
+
+  This README describes how to set up and how to use the different parts
+  of the ISDN-subsystem.
+
+  For using the ISDN-subsystem, some additional userlevel programs are
+  necessary. Those programs and some contributed utilities are available
+  at
+
+   ftp.franken.de
+
+   /pub/isdn4linux/isdn4k-utils-<VersionNumber>.tar.gz
+
+
+  We also have set up a mailing-list:
+
+   The isdn4linux-project originates in Germany, and therefore by historical
+   reasons, the mailing-list's primary language is german. However mails
+   written in english have been welcome all the time.
+
+   to subscribe: write a email to majordomo@hub-wue.franken.de,
+   Subject irrelevant, in the message body:
+   subscribe isdn4linux <your_email_address>
+
+   To write to the mailing-list, write to isdn4linux@hub-wue.franken.de
+   
+
+  1.1 Technical details
+
+  In the following Text, the terms MSN and EAZ are used.
+
+  MSN is the abbreviation for (M)ultiple(S)ubscriber(N)umber, and applies
+  to Euro(EDSS1)-type lines. Usually it is simply the phone-number.
+
+  EAZ is the abbreviation of (E)ndgeraete(A)uswahl(Z)iffer and
+  applies to German 1TR6-type lines. This is a one-digit string,
+  simply appended to the base phone-number
+
+  The internal handling is nearly identical, so replace the appropriate
+  term to that one, which applies to your local ISDN-environment.
+
+  When the link-level-module isdn.o is loaded, it supports up to 16
+  low-level-modules with up to 16 channels. (The number 16 is arbitrarily
+  chosen and can be configured at compile-time --ISDN_MAX in isdn.h).
+  A low-level-driver can register itself through an interface (which is
+  defined in isdnif.h) and gets assigned a slot.
+  The following char-devices are made available for each channel:
+  
+  A raw-control-device with the following functions:
+     write: raw D-channel-messages (format: depends on driver).
+     read:  raw D-channel-messages (format: depends on driver).
+     ioctl: depends on driver, for the ICN-driver, the base-address of
+            the ports and the shared memory on the card can be set and read
+            also the boot-code an the protocol software can be loaded into 
+            the card.
+
+   O N L Y !!!  for debugging (no locking against other devices):
+   One raw-data-device with the following functions:
+     write: data to B-channel.
+     read:  data from B-channel.
+
+   In addition the following devices are made available:
+
+   32 tty-devices (16 cuix and 16 ttyIx) with integrated modem-emulator:
+   The functionality is almost the same as that of a serial device
+   (the line-discs are handled by the kernel, which lets you run 
+   SLIP, CSLIP and asynchronous PPP through the devices. We have tested 
+   Seyon, minicom, CSLIP (uri-dip) PPP and mgetty (compiled with NO_FAX),
+   XCept.
+
+   The modem-emulation supports the following:
+           1.3.1 Commands:
+
+               ATA     Answer incoming call.
+              ATD<No.> Dial, the number may contain:
+                        [0-9] and [,#.*WPT-S]
+                        the latter are ignored until 'S'.
+                        The 'S' must precede the number, if 
+                        the line is a SPV (German 1TR6).
+               ATE0    Echo off.
+              ATE1     Echo on (default).
+               ATH      Hang-up.
+              ATH1     Off hook (ignored).
+               ATH0     Hang-up.
+              ATI      Return "ISDN for Linux...".
+               ATI0        "
+               ATI1        "
+               ATO      On line (data mode).
+               ATQ0     Enable result codes (default).
+               ATQ1     Disable result codes (default).
+               ATSx=y  Set register x to y.
+              ATSx?    Show contents of register x.
+               ATV0     Numeric responses.
+               ATV1     English responses (default).
+              ATZ      Load registers and EAZ/MSN from Profile.
+              AT&Bx    Set Send-Packet-size to x (max. 4000)
+                        The real packet-size may be limited by the
+                        low-level-driver used. i.e.: the Teles-Module-
+                        limit is 2000. You will get NO Error-Message,
+                        if you set it to higher Values, because at the
+                        time of giving this command the corresponding
+                        driver may not be selected (see "Automatic
+                        Assignment") however the size of outgoing packets
+                        will be limited correctly.
+              AT&D2    DTR-low-edge: Hang up and return to 
+                        command mode (default).
+               AT&D3    Same as AT&D2 but also resets all registers.
+              AT&Ex    Set the EAZ/MSN for this channel to x.
+              AT&F     Reset all registers and profile to "factory-defaults"
+              AT&Sx    Set window-size for Teles-driver (x = 1..8) (not yet
+                        implemented)
+              AT&V     Show all settings.
+               AT&W0    Write registers and EAZ/MSN to profile. See also
+                        iprofd (5.c in this README).
+               AT&X0   BTX-mode off (default)
+              AT&X1    BTX-mode on. (S13.1=1, S14=0, S16=7, S18=7, S19=0)
+
+          1.3.2 Escape sequence:
+               During a connection, the emulation reacts just like
+               a normal modem to the escape sequence <DELAY>+++<DELAY>.
+              (The escape character - default '+' - can be set in the
+               register 2).
+               The DELAY must at least be 1.5 seconds long and delay 
+               between the escape characters must not exceed 0.5 seconds.
+     
+           1.3.3 Registers:
+
+              Nr.  Default  Description
+              0    0        Answer on ring number.
+                            (no auto-answer if S0=0).
+              1    0        Count of rings.
+              2    43       Escape character.
+                            (a value >= 128 disables the escape sequence).
+              3    13       Carriage return character (ASCII).
+              4    10       Line feed character (ASCII).
+              5    8        Backspace character (ASCII).
+              6    3        Delay in seconds before dialing.
+              7    60       Wait for carrier (ignored).
+              8    2        Pause time for comma (ignored)
+              9    6        Carrier detect time (ignored)
+             10    7        Carrier loss to disconnect time (ignored).
+             11    70       Touch tone timing (ignored).
+             12    69       Bit coded register:
+                            Bit 0:    0 = Suppress response messages.
+                                      1 = Show response messages.
+                            Bit 1:    0 = English response messages.
+                                      1 = Numeric response messages.
+                            Bit 2:    0 = Echo off.
+                                      1 = Echo on.
+                            Bit 3     0 = DCD always on.
+                                      1 = DCD follows carrier.
+                            Bit 4     0 = CTS follows RTS
+                                      1 = Ignore RTS, CTS always on.
+                            Bit 5     0 = Low-edge on DTR: Hang up and
+                                          return to command mode.
+                                      1 = Same as 0 but also resets all
+                                          registers.
+                            Bit 6     0 = DSR always on.
+                                      1 = DSR only on if channel is available.
+                            Bit 7     0 = Cisco-PPP-flag-hack off (default).
+                                      1 = Cisco-PPP-flag-hack on.
+             13   0         Bit coded register:
+                            Bit 0:    0 = Use delayed tty-send-algorithm
+                                      1 = Direct tty-send.
+                            Bit 1:    0 = T.70 protocol (Only for BTX!) off
+                                      1 = T.70 protocol (Only for BTX!) on
+            14   0         Layer-2 protocol: (with the ICN-driver 
+                                                currently always 0)
+                                     0 = X75/LAPB with I-frames
+                                     1 = X75/LAPB with UI-frames
+                                      2 = X75/LAPB with BUI-frames
+                                      3 = HDLC
+             15   0         Layer-3 protocol: (at the moment always 0)
+                                      0 = transparent
+            16   250       Send-Packet-size/16
+             17   8         Window-size for Teles-driver (not yet implemented)
+             18   7         Service-Octet-1
+             19   0         Service-Octet-2
+
+  Last but not least a (at the moment fairly primitive) device to request
+  the line-status (/dev/isdninfo) is made available.
+
+  Automatic assignment of devices to lines:
+
+  All inactive physical lines are listening to all EAZs for incoming
+  calls and are NOT assigned to a specific tty or network interface.
+  When an incoming call is detected, the driver looks first for a network 
+  interfaces and then for an opened tty which:
+
+  1. is configured for the same EAZ.
+  2. has the same protocol settings for the B-channel.
+  3. (only for network interfaces if the security flag is set)
+     contains the caller number in its access list.
+  4. Either the channel is not bound exclusively to another Net-interface, or
+     it is bound AND the other checks apply to exact this Interface.
+     (For usage of the bind-features, refer to the isdnctrl-man-page)
+
+  Only when a matching interface or tty is found, the call is accepted
+  and the "connection" between the low-level-layer and the link-level-layer
+  is established and kept until the end of the connection.
+  In all other cases no connection is established. Isdn4linux can be
+  configured to either do NOTHING in this case (which is useful, if
+  other, external devices with the same EAZ/MSN are connected to the bus)
+  or to reject the call actively. (isdnctrl busreject ...)
+
+  For an outgoing call, the inactive physical lines are searched.
+  The call is placed on the first physical line, which supports the
+  requested protocols for the B-channel. If a net-interface, however
+  is pre-bound to a channel, this channel is used directly.
+
+  This makes it possible to configure several network interfaces and ttys
+  for one EAZ, if the network interfaces are set to secure operation.
+  If an incoming call matches one network interface, it gets connected to it.
+  If another incoming call for the same EAZ arrives, which does not match
+  a network interface, the first tty gets a "RING" and so on.
+  As soon as voice gets supported (with the availability of the Diehl-driver),
+  the service-identifier will be evaluated in addition.
+
+2 System prerequisites:
+
+  ATTENTION! The program "insmod" from the Package "modules-1.2.8" (It's
+             on nearly all newer distributions) has a bug, which makes
+             it impossible to set both driver-Id's when loading the
+             icn-module for the Double-ICN-Card. A patch is supplied
+             in the utility-package called "insmod-1.2.8.patch". Change into
+             the source-directory of insmod, and type
+             "patch < insmod-1.2.8.patch". Then recompile it. This will fix
+             the bug.
+             This bug does NOT occur when using insmod with the Teles-driver
+             or a single ICN-card.
+3. Lowlevel-driver configuration.
+
+   Configuration depends on how the drivers are built.
+
+   3.1 Drivers built into the kernel.
+
+     3.1.1 Teles driver.
+
+       The Teles driver can be configured using the commandline-feature
+       while loading the kernel with LILO or LOADLIN. It accepts the
+       following syntax:
+
+       teles=p0,i0,m0,d0[,p1,i1,m1,d1 ... ,pn,in,mn,dn][,idstring]
+
+       where
+
+         p0 = portbase of 1st card.                           (default: 0xd80)
+         i0 = irq of 1st card.                                (default: 15)
+         m0 = shared memory of 1st card.                      (default: 0xd0000)
+         d0 = D-channel protocol of 1st card. 1=1TR6, 2=EDSS1 (default: 2)
+
+         p1,i1,m1,d1 = Parameters of second card (defaults: none)
+         pn,in,mn,d1 = Parameters of n'th card (up to 16 cards are supported)
+
+         idstring = Driver-Id for accessing with utilities and identification
+                    when using a Line-monitor. (default: none)
+                    idstring must start with a character!
+
+         The type of the card is determined by the port, irq and shared memory:
+
+           port == 0, shared memory != 0          -> Teles S0-8
+           port != 0, shared memory != 0          -> Teles S0-16.0
+           port != 0, shared memory == 0          -> Teles S0-16.3
+
+       ATTENTION:
+
+       Due to limited hardware-capabilities, there is no way to check the
+       existence of a card. Therefore you need to be shure your card's setup
+       is correct. Also there are bugs in the printed manual of some newer
+       16.3 cards. The manual tells the port has to be 0x180. THIS IS WRONG!!
+       0xd80 is the correct value! Have a look to the kernel-syslog. With
+       most of the cards, you should see a line "HSCX version A:5 B:5" there.
+       
+     3.1.2 ICN driver.
+
+       The ICN driver can be configured using the commandline-feature while
+       loading the kernel with LILO or LOADLIN. It accepts the following
+       syntax
+
+       icn=p,m[,idstring1[,idstring2]]
+
+       where
+
+         p = portbase      (default: 0x320)
+         m = shared memory (default: 0xd0000)
+
+       When using the ICN double card, you MUST define TWO idstrings.
+       idstring must start with a character!
+
+       The ICN driver supports only one card at the moment. If you like to
+       use more than one card, build the modularized version, loading it
+       more than one time, each module driving a single card.
+
+       Using the "icnctrl"-utility, portbase and shared memory can also be
+       changed during runtime.
+
+       The D-channel protocol is configured by loading different firmware
+       into the card's memory using the "icnctrl"-utility.
+
+
+   3.2 Drivers built as modules.
+
+     3.2.1 Teles driver.
+
+       The module teles.o can be configured during "insmod'ing" it by
+       appending it's parameters to the insmod-commandline. The following
+       syntax is accepted:
+
+       io=m0,i0,p0,d0[,m1,i1,p1,d1 ... ,mn,in,pn,dn] teles_id=idstring
+
+       where
+
+         m0,i0,p0,d0 ... mn,in,pn,dn have the same meanings like the
+                                     parameters described for the kernel-
+                                     version above. Watchout: different
+                                     sequence!
+
+     3.2.2 ICN driver.
+
+       The module icn.o can be configured during "insmod'ing" it by
+       appending it's parameters to the insmod-commandline. The following
+       syntax is accepted:
+
+       portbase=p membase=m icn_id=idstring icn_id2=idstring2
+
+       where p, m, idstring1 and idstring2 have the same meanings like
+                                           parameters described for the kernel-
+                                           version above.
+      
+       When using the ICN double card, you MUST define TWO idstrings.
+       idstring must start with a character!
+
+       The ICN driver supports only one card at the moment. If you like to
+       use more than one card, build the modularized version, loading it
+       more than one time, each module driving a single card.
+
+       Using the "icnctrl"-utility, portbase and shared memory can also be
+       changed during runtime.
+
+       The D-channel protocol is configured by loading different firmware
+       into the card's memory using the "icnctrl"-utility.
+
+4. Device-inodes
+
+   The major and minor-numbers and it's names are described in
+   Documentation/devices.txt. The major-numbers are:
+
+     43 for the ISDN-tty's.
+     44 for the ISDN-callout-tty's.
+     45 for control/info/debug devices.
+
+
+5. Application
+
+   a) (Only for ICN-cards) Load the firmware into the card:
+
+       cd icn
+     For 1TR6:
+       icnctrl [-d IDstring] load download/loadpg.bin download/pc_1t_ca.bin
+     For Euro-ISDN:
+       icnctrl [-d IDstring] load download/loadpg.bin download/pc_eu_ca.bin
+
+     When using the ICN-2B, the protocol-software for the second half of
+     the card must be appended to the command line.
+
+     -> The two LEDs at the back cover of the card (ICN-2B: 4 LEDs) must be
+        blinking intermittingly now. If a connection is up, the corresponding
+        led lits continuously.
+
+   b) If you only intend to use ttys, you are nearly ready now.
+
+   c) If you want to have really permanent "Modem"-settings on disk, you
+      can start the daemon iprofd. Give it a path to a file at the command-
+      line. It will store the profile-settings in this file every time
+      an AT&W0 is performed on any ISDN-tty. If the file already exists,
+      all profiles are initialized from this file. If you want to unload
+      any of the modules, kill iprofd first.
+
+   d) For networking, continue: Create an interface:
+       isdnctrl addif isdn0
+
+   e) Set the EAZ (or MSN for Euro-ISDN):
+       isdnctrl eaz isdn0 2
+
+     (For 1TR6 a single digit is allowed, for Euro-ISDN the number is your
+      real MSN e.g.: Phone-Number)
+
+   f) Set the number for outgoing calls on the interface:
+       isdnctrl addphone isdn0 out 1234567
+       ... (this can be executed more than once, all assigned numbers are
+            tried in order)
+      and the number(s) for incoming calls:
+       isdnctrl addphone isdn0 in 1234567
+
+   g) Set the timeout for hang-up:
+       isdnctrl huptimeout isdn0 <timeout_in_seconds>
+
+   h) additionally you may activate charge-hang-up (= Hang up before 
+      next charge-info, this only works, if your isdn-provider transmits
+      the charge-info during and after the connection, it does NOT work
+      with the Teles on an EDSS1-Line.):
+       isdnctrl chargehup isdn0 on
+
+   i) Setup the interface with ifconfig as usual, and set a route to it.
+
+   j) (optional) If you run X11 and have Tcl/Tk-wish Version4.0, you can use
+     the script tools/tcltk/isdnmon. You can add actions for line-status
+     changes. See the comments at the beginning of the script for how to
+     do that. There are other tty-based tools in the tools-subdirectory
+     contributed by Michael Knigge (imon), Volker Götz (imontty) and
+     Andreas Kool (isdnmon).
+
+   k) For initial testing, you can set the verbose-level to 2 (default: 0).
+      Then all incoming calls are logged, even if they are not addressed
+      to one of the configured net-interfaces:
+      isdnctrl verbose 2
+
+  Now you are ready! A ping to the set address should now result in a
+  dial-out (look at syslog kernel-messages).
+  The phone-numbers and EAZs can be assigned at any time with isdnctrl.
+  You can add as many interfaces as you like with addif following the
+  directions above. Of course, there may be some limitations. But we have 
+  tested as many as 20 interfaces without any problem. However, if you 
+  don't give an interface name to addif, the  kernel will assign a name 
+  which starts with "eth". The number of "eth"-interfaces is limited by
+  the kernel.
+
+5. Additional options for isdnctrl:
+
+   "isdnctrl secure <InterfaceName> on" 
+   Only incoming calls, for which the caller-id is listed in the access
+   list of the interface are accepted. You can add caller-id's With the
+   command "isdnctrl addphone <InterfaceName> in <caller-id>"
+   Euro-ISDN does not transmit the leading '0' of the caller-id for an
+   incoming call, therefore you should configure it accordingly.
+   If the real number for the dialout e.g. is "09311234567" the the number
+   to configure here is "9311234567". The pattern-match function 
+   works similar to the shell mechanism.
+
+     ?     one arbitrary digit
+     *     zero or arbitrary many digits
+     [123] one of the digits in the list
+     [1-5] one digit between '1' and '5'
+           a '^' as the first character in a list inverts the list
+
+
+   "isdnctrl secure <InterfaceName> off" 
+   Switch of secure operation (default).
+
+   "isdnctrl ihup <InterfaceName> [on|off]" 
+   Switch the hang-up-timer for incoming calls on or off.
+
+   "isdnctrl eaz <InterfaceName>" 
+   Returns the EAZ of an interface.
+
+   "isdnctrl delphone <InterfaceName> in|out <number>" 
+   Deletes a number from one of the the access-lists of the interface.
+
+   "isdnctrl delif <InterfaceName>" 
+   Removes the interface (and possible slaves) from the kernel.
+   (You have to unregister it with "ifconfig <InterfaceName> down" before).
+
+   "isdnctrl callback <InterfaceName> [on|off]" 
+   Switches an interface to callback-mode. In this mode, an incoming call
+   will be rejected and after this the remote-station will be called. If
+   you test this feature by using ping, some routers will re-dial very
+   quickly, so that the callback from isdn4linux may not be recognized.
+   In this case use ping with the option -i <sec> to increase the interval
+   between echo-packets.
+
+   "isdnctrl encap <InterfaceName> <EncapType>"
+   Selects the type of packet-encapsulation. The encapsulation can be changed
+   only while an interface is down.
+
+   At the moment th following Values are supported:
+   rawip    (Default) Selects raw-IP-encapsulation. This means, MAC-headers
+            are stripped off.  
+   ip       IP with type-field. Same as IP but the type-field of the MAC-header
+            is preserved.
+   cisco-h  A special-mode for communicating with a Cisco, which is configured
+            to do "hdlc"
+   ethernet No stripping. Packets are sent with full MAC-header.
+            The Ethernet-address of the interface is faked, from it's
+            IP-address: fc:fc:i1:i2:i3:i4, where i1-4 are the IP-addr.-values.
+   syncppp  Synchronous PPP
+
+   Watching packets, using standard-tcpdump will fail for all encapsulations
+   except ethernet because tcpdump does not know how to handle packets
+   without MAC-header. A patch for tcpdump is included in the utility-package
+   mentioned above.
+
+   "isdnctrl l2_prot <InterfaceName> <L2-ProtocolName>" 
+   Selects a layer-2-protocol. 
+   (With the ICN-driver and the Teles-driver, "x75i" and "hdlc" is available.
+   With other drivers, "x75ui", "x75bui" may be possible too.)
+
+   isdnctrl l3_prot <InterfaceName> <L3-ProtocolName> 
+   The same for layer-3. (At the moment only "trans" is allowed)
+
+   "isdnctrl list <InterfaceName>" 
+   Shows all parameters of an interface and the charge-info.
+   Try "all" as the interface name.
+
+   "isdnctrl hangup <InterfaceName>"
+   Forces hangup of an interface.
+
+   "isdnctrl bind <InterfaceName> <DriverId>,<ChannelNumber> [exclusive]"
+   If you are using more than one ISDN-Card, it is sometimes necessary to
+   dial out using a specific Card or even preserve a specific Channel for
+   Dialout of a specific net-interface. This can be done with the above
+   command. Replace <DriverId> by whatever you assigned while loading the
+   module. The <ChannelNumber> is counting from zero. the upper Limit
+   depends on the card used. At the Moment no card supports more than
+   2 Channels, so the upper limit is one.
+
+   "isdnctrl unbind <InterfaceName>"
+   unbinds a previously bound interface.
+
+   "isdnctrl busreject <DriverId> on|off"
+   If switched on, isdn4linux replies a REJECT to incoming calls, it
+   cannot match to any configured interface.
+   If switched off, nothing happens in this case.
+   You normally should NOT enable this feature, if the ISDN-adaptor is not
+   the only device, connected to the S0-bus. Otherwise it could happen, that
+   isdn4linux rejects an incoming call, which belongs to another device on
+   the bus. 
+
+   "isdnctrl addslave <InterfaceName> <SlaveName>
+   Creates a slave interface for channel-bundling. Slave interfaces are
+   not seen by the kernel, but their ISDN-part can be configured with
+   isdnctrl as usual. (Phone numbers, EAZ/MSN, timeouts etc.) If more
+   than two channels are to be bundled, feel free to create as many as you
+   want. InterfaceName must be a real interface, NOT a slave. Slave interfaces
+   start dialing, if the master interface resp. the previous slave interface
+   has a load of more than 7000 cps. They hangup if the load goes under 7000
+   cps, according to their "huptimeout"-parameter.
+
+   "isdnctrl sdelay <InterfaceName> secs."
+   This sets the minimum time an Interface has to be fully loaded, until
+   it sends an dial-request to it's slave.
+
+   "isdnctrl dial <InterfaceName>"
+   Forces an interface to start dialing even if no packets are to be
+   transferred.
+
+   "isdnctrl mapping <DriverId> MSN0,MSN1,MSN2,...MSN9"
+   This installs a mapping table for EAZ<->MSN-mapping for a single line.
+   Missing MSN's have to be given as "-" or can be omitted, if at the end
+   of the commandline.
+   With this command, it's now possible to have an interface listening to
+   mixed 1TR6- and Euro-Type lines. In this case, the interface has to be
+   configured to a 1TR6-type EAZ (one digit). The mapping is also valid
+   for tty-emulation. Seen from the interface/tty-level the mapping
+   CAN be used, however it's possible to use single tty's/interfaces with
+   real MSN's (more digits) also, in which case the mapping will be ignored.
+   Here is an example:
+
+   You have a 1TR6-type line with base-nr. 1234567 and a Euro-line with
+   MSN's 987654, 987655 and 987656. The DriverId for the Euro-line is "EURO".
+
+   isdnctrl mapping EURO -,987654,987655,987656,-,987655
+   ...
+   isdnctrl eaz isdn0 1      # listen on 12345671(1tr6) and 987654(euro)
+   ...
+   isdnctrl eaz isdn1 4      # listen on 12345674(1tr6) only.
+   ...
+   isdnctrl eaz isdn2 987654 # listen on 987654(euro) only.
+   
+   Same scheme is used with AT&E...  at the tty's.
+  
+6. If you want to write a new low-level-driver, you are welcome.
+   The interface to the link-level-module is described in the file INTERFACE.
+   If the interface should be expanded for any reason, don't do it
+   on your own, send me a mail containing the proposed changes and
+   some reasoning about them.
+   If other drivers will not be affected, I will include the changes
+   in the next release.
+   For developers only, there is a second mailing-list. Write to me
+   (fritz@wuemaus.franken.de), if you want to join that list.
+
+Have fun!
+
+ -Fritz
+
diff --git a/Documentation/isdn/README.icn b/Documentation/isdn/README.icn
new file mode 100644 (file)
index 0000000..a5f744c
--- /dev/null
@@ -0,0 +1,61 @@
+You can get the ICN-ISDN-card from:
+
+Thinking Objects Software GmbH
+Obere Heerbergstr. 17
+97078 Würzburg
+Tel: +49 931 2877950
+Fax: +49 931 2877951
+
+email uhl@think.de
+WWW   http:/www.think.de
+
+
+The card communicates with the PC by two interfaces:
+  1. A range of 4 successive port-addresses, whos base address can be 
+     configured with the switches.
+  2. A memory window with 16KB-256KB size, which can be setup in 16k steps
+     over the whole range of 16MB. Isdn4linux only uses a 16k window.
+     The base address of the window can be configured when loading
+     the lowlevel-module (see README).
+
+Setting up the IO-address dipswitches for the ICN-ISDN-card:
+
+  Two types of cards exist, one with dip-switches and one with
+  hook-switches.
+
+  1. Setting for the card with hook-switches:
+
+     (0 = switch closed, 1 = switch open)
+
+     S3 S2 S1  Basie-address
+      0  0  0  0x300
+      0  0  1  0x310
+      0  1  0  0x320 (Default for isdn4linux)
+      0  1  1  0x330
+      1  0  0  0x340
+      1  0  1  0x350
+      1  1  0  0x360
+      1  1  1  NOT ALLOWED!
+    
+  2. Setting for the card with dip-switches:
+
+     (0 = switch closed, 1 = switch open)
+
+     S1 S2 S3 S4  Basis-Adresse
+      0  0  0  0  0x300
+      0  0  0  1  0x310
+      0  0  1  0  0x320 (Default for isdn4linux)
+      0  0  1  1  0x330
+      0  1  0  0  0x340
+      0  1  0  1  0x350
+      0  1  1  0  0x360
+      0  1  1  1  NOT ALLOWED!
+      1  0  0  0  0x308
+      1  0  0  1  0x318
+      1  0  1  0  0x328
+      1  0  1  1  0x338
+      1  1  0  0  0x348
+      1  1  0  1  0x358
+      1  1  1  0  0x368
+      1  1  1  1  NOT ALLOWED!
+
diff --git a/Documentation/isdn/README.syncppp b/Documentation/isdn/README.syncppp
new file mode 100644 (file)
index 0000000..3e400c2
--- /dev/null
@@ -0,0 +1,52 @@
+Some additional information for setting up a syncPPP
+connection using network interfaces.
+---------------------------------------------------------------
+
+You need one thing beside the isdn4linux package:
+
+  a patched pppd .. (I called it ipppd to show the difference)
+
+Compiling isdn4linux with sync PPP:
+-----------------------------------
+To compile isdn4linux with the sync PPP part, you have
+to answer the apropriate question when doing a "make config"
+Don't forget to load the slhc.o
+module before the isdn.o module, if VJ-compression support
+is not compiled into your kernel. 
+
+Using isdn4linux with sync PPP:
+-------------------------------
+Sync PPP is just another encapsulation for isdn4linux. The
+name to enable sync PPP encapsualtion is 'syncppp' .. e.g:
+
+  isdn/isdnctrl encap ippp0 syncppp
+
+The name of the interface is here 'ippp0'. You need 
+one interface with the name 'ippp0' to saturate the
+ipppd, which checks the ppp version via this interface.
+
+To set up a PPP connection you need the ipppd .. You must start 
+the ipppd once after installing the modules. The ipppd 
+communicates with the isdn4linux link-level driver using the
+/dev/ippp0 to /dev/ippp15 devices. One ipppd can handle
+all devices at once. If you want to use two PPP connections
+at the same time, you have to connect the ipppd to two
+devices .. and so on. 
+I've implemented one additonal option for the ipppd:
+ 'useifip' will get (if set to not 0.0.0.0) the IP address 
+ for the negotiation from the attached network-interface. 
+You must disable BSD-compression, this implementation can't
+handle compressed packets.
+
+Check the rc.isdn.syncppp file for an example setup script.
+
+enjoy it,
+    michael
+     
+
+PS: I also implemented generic MP (RFC 1717). But in the 
+current isdn4linux link-level driver there is no 
+(or better say: another) way of doing channel bundling. So,
+don't call the ipppd with the `+mp` option to enable 
+MP negotiation.
+
diff --git a/Documentation/isdn/README.teles b/Documentation/isdn/README.teles
new file mode 100644 (file)
index 0000000..19d8da3
--- /dev/null
@@ -0,0 +1,72 @@
+This is my Linux hardware level driver for Teles compatible ISDN cards. It is 
+meant to be used with isdn4isdn4linux, an ISDN Link-level module for Linux written
+by Fritz Elfert.
+
+Isdn4linux can be obtained from ftp.franken.de:/pub/isdn4linux. The most recent 
+Teles driver can be found on my homepage, http://www.xs4all.nl:/~jdo.
+
+Warning
+-------
+Teles4isdn4linux is a work in progress and may crash your machine. It has not 
+been certified and therefore operation on your PTT's ISDN network is probably 
+illegal.
+
+Limitations
+-----------
+Teles4isdn4linux only works on Euro ISDN lines and german 1TR6-lines.
+
+For the B channel transparent (HDLC) protocol and X.75 have been implemented. 
+
+Running
+-------
+When you insmod isdn.o and teles.o (or with the kernel-version, during boottime)
+a few lines should appear in your syslog. Look for something like:
+
+Oct 11 16:53:30 jedi kernel: channels 2
+Oct 11 16:53:31 jedi kernel: Teles module installed
+
+Remember, that according to the new strategy for accessing Low-level-drivers
+from within isdn4linux you should also define a driver-id while doing
+insmod: Simply append id=<SomeString> to the insmod-commandline. This
+string MUST NOT start with a digit or a small 'x'!
+
+At this point you can run a 'cat /dev/isdnctrl0' and view debugging 
+messages. Debugging messages are enabled with the telesctrl tool: 
+
+       teles/telesctrl <DriverId> 1 <debugging_flags>
+
+where <debugging_flags> is the integer sum of the following debugging
+options you wish enabled:
+
+       1   Link-level <--> Hardware-level communication
+       2   Top state machine 
+       4   D channel Q.931 (call control messages)
+       8   D channel Q.921 
+       16  B channel X.75
+
+For example 'teles/telesctrl MyTeles 1 31' enables full 
+debugging.
+
+Questions
+---------
+Check out the FAQ (ftp.franken.de).
+
+Bugs 
+----
+If you find any please let me know. 
+
+Thanks
+------
+Special thanks to:
+
+       Erik Bos,Beat Doebeli,Fritz Elfert,     
+       Pauline Middelink,Paula de Nie,         
+       Bernd Oerding,Stephan Seidl,Matthias Urlichs, 
+       Rogier Wolff
+
+
+
+Enjoy,
+
+Jan den Ouden   denouden@groovin.xs4all.nl
+
diff --git a/MAGIC b/MAGIC
index 5f912b28686343db3ff192ba9d28e765715f4140..2764ec62a57bd1ca894b41c674199d42241573dc 100644 (file)
--- a/MAGIC
+++ b/MAGIC
@@ -72,11 +72,13 @@ Ioctl       Include File            Comments
 0x03   linux/hdreg.h
 0x04   linux/umsdos_fs.h
 0x06   linux/lp.h
+0x09   linux/md.h
 0x12   linux/fs.h
 0x20   linux/cm206.h
 0x22   linux/scc.h
 'A'    linux/apm_bios.h
 'C'    linux/soundcard.h
+'I'    linux/isdn.h
 'K'    linux/kd.h
 'M'    linux/soundcard.h
 'P'    linux/soundcard.h
index 60b65e15d08d111bc85cad8209ab025e19e61bbb..c3ffb8e7fd13318412abfc5cab57a104757ce9ca 100644 (file)
@@ -102,6 +102,12 @@ M: smp-patches@lxorguk.ukuu.org.uk
 L:     linux-smp@vger.rutgers.edu
 S:     Maintained
 
+SPARC:
+P:     David S. Miller
+M:     davem@caip.rutgers.edu
+L:     sparclinux@vger.rutgers.edu
+S:     Maintained
+
 REST:
 P:     Linus Torvalds
 S:     Buried alive in email
index d2156289de2751b822a09c5f09a9a46bce76f264..d22d9dfc35bdc1547924bb45e3d7025b015724e1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 68
+SUBLEVEL = 69
 
 ARCH = i386
 
@@ -130,6 +130,10 @@ DRIVERS            =drivers/block/block.a \
 LIBS           =$(TOPDIR)/lib/lib.a
 SUBDIRS                =kernel drivers mm fs net ipc lib
 
+ifeq ($(CONFIG_ISDN),y)
+DRIVERS        := $(DRIVERS) drivers/isdn/isdn.a
+endif
+
 ifdef CONFIG_CD_NO_IDESCSI
 DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
 endif
@@ -339,7 +343,7 @@ distclean: mrproper
 
 
 backup: mrproper
-       cd .. && tar cf - linux | gzip -9 > backup.gz
+       cd .. && tar cf - linux/ | gzip -9 > backup.gz
        sync
 
 dep-files: archdep .hdepend include/linux/version.h
index 8f183e3a09d8060439f8dcc16894cf58a45785be..f58ad9f9a8fc2a25fc1617cdc2ee65f48bded751 100644 (file)
@@ -176,6 +176,7 @@ CONFIG_ISO9660_FS=y
 #
 # Character devices
 #
+CONFIG_SERIAL=y
 # CONFIG_CYCLADES is not set
 # CONFIG_STALDRV is not set
 # CONFIG_PRINTER is not set
index 144019f7688122b9e9e3e75be31958acd3c8b80e..d1f8672b874196b9de8ff0a407c73320082a4796 100644 (file)
@@ -63,6 +63,15 @@ if [ "$CONFIG_NET" = "y" ]; then
   endmenu
 fi
 
+mainmenu_option next_comment
+comment 'ISDN subsystem'
+
+tristate 'ISDN support' CONFIG_ISDN
+if [ "$CONFIG_ISDN" != "n" ]; then
+  source drivers/isdn/Config.in
+fi
+endmenu
+
 mainmenu_option next_comment
 comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
 
index 75b4de96a1a911fec71ca214d6da29342ec5acc1..59b25d064da3fafcba3f31a6a4fe239e56ce2ff3 100644 (file)
@@ -45,7 +45,7 @@ CONFIG_BLK_DEV_RZ1000=y
 # CONFIG_BLK_DEV_RAM is not set
 # CONFIG_BLK_DEV_LOOP is not set
 # CONFIG_BLK_DEV_XD is not set
-
+# CONFIG_BLK_DEV_MD is not set
 #
 # Networking options
 #
@@ -103,6 +103,11 @@ CONFIG_EL3=y
 # CONFIG_TR is not set
 # CONFIG_ARCNET is not set
 
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
 #
 # CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
 #
@@ -131,6 +136,7 @@ CONFIG_ISO9660_FS=y
 #
 # Character devices
 #
+CONFIG_SERIAL=y
 # CONFIG_CYCLADES is not set
 # CONFIG_STALDRV is not set
 # CONFIG_PRINTER is not set
index 79655ba93385d5626e34226126bbfe091debb09e..03a12cd381e519a85165dd2277826aa9f5927cdd 100644 (file)
@@ -328,16 +328,15 @@ void dump_thread(struct pt_regs * regs, struct user * dump)
        dump->magic = CMAGIC;
        dump->start_code = 0;
        dump->start_stack = regs->esp & ~(PAGE_SIZE - 1);
-       dump->u_tsize = ((unsigned long) current->mm->end_code) >> 12;
-       dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> 12;
+       dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT;
+       dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT;
        dump->u_dsize -= dump->u_tsize;
        dump->u_ssize = 0;
        for (i = 0; i < 8; i++)
                dump->u_debugreg[i] = current->debugreg[i];  
 
-       if (dump->start_stack < TASK_SIZE) {
-               dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> 12;
-       }
+       if (dump->start_stack < TASK_SIZE)
+               dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
 
        dump->regs = *regs;
 
index b80d6ecf0ef0981f5cec98fdfd084b558be86df0..7754853407d5611dcdfb97a61e71163cba7bff8d 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/mm.h>
 #include <linux/kernel_stat.h>
 #include <linux/delay.h>
+#include <linux/mc146818rtc.h>
 #include <asm/i82489.h>
 #include <linux/smp.h>
 #include <asm/pgtable.h>
@@ -469,7 +470,7 @@ void smp_callin(void)
        /*
         *      Allow the master to continue.
         */     
-       set_bit(cpuid, &cpu_callin_map[0]);
+       set_bit(cpuid, (unsigned long *)&cpu_callin_map[0]);
        /*
         *      Until we are ready for SMP scheduling
         */
@@ -573,21 +574,38 @@ void smp_boot_cpus(void)
                         *      the targeted processor.
                         */
 
-#ifdef EEK
                        SMP_PRINTK(("Setting warm reset code and vector.\n"));
 
+                       /*
+                        *      Needed to boot a 486 board.
+                        */
+                        
                        CMOS_WRITE(0xa, 0xf);
-                       *((volatile unsigned short *) 0x467) = (unsigned short)(stack>>4);
+                       pg0[0]=7;
+                       *((volatile unsigned short *) 0x467) = ((unsigned long)stack)>>4;
                        *((volatile unsigned short *) 0x469) = 0;
-#endif
+                       pg0[0]= pte_val(mk_pte(0, PAGE_READONLY));
+
+                       /*
+                        *      Clean up the errors
+                        */
 
                        apic_write(APIC_ESR, 0);
                        accept_status = (apic_read(APIC_ESR) & 0xEF);
+                       
+                       /*
+                        *      Status is now clean
+                        */
+                        
                        send_status = 0;
                        accept_status = 0;
 
                        SMP_PRINTK(("Asserting INIT.\n"));
 
+                       /*
+                        *      Turn INIT on
+                        */
+                        
                        cfg=apic_read(APIC_ICR2);
                        cfg&=0x00FFFFFF;
                        apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i));                      /* Target chip          */
@@ -610,6 +628,10 @@ void smp_boot_cpus(void)
                        }
 #endif
 
+                       /*
+                        *      And off again
+                        */
+                        
                        if (send_status && !accept_status)
                        {
                                SMP_PRINTK(("Deasserting INIT.\n"));
@@ -706,7 +728,6 @@ void smp_boot_cpus(void)
                                printk("APIC never delivered???\n");
                        else if (accept_status)         /* Send accept error */
                                printk("APIC delivery error (%lx).\n", accept_status);
-                       else
                        {
                                for(timeout=0;timeout<50000;timeout++)
                                {
@@ -1096,9 +1117,9 @@ void smp_message_irq(int cpl, struct pt_regs *regs)
                 */
                 
                case MSG_INVALIDATE_TLB:
-                       if(clear_bit(i,&smp_invalidate_needed))
+                       if(clear_bit(i,(unsigned long *)&smp_invalidate_needed))
                                local_invalidate();
-                       set_bit(i, &cpu_callin_map[0]);
+                       set_bit(i, (unsigned long *)&cpu_callin_map[0]);
                        cpu_callin_map[0]|=1<<smp_processor_id();
                        break;
                        
index 760c63fc15bdf7c3bb6d9ff78d4c7481fbeb4f7d..527715efa3b0d9a0802f9713b29161a0add87259 100644 (file)
@@ -9,7 +9,7 @@
 
 SUB_DIRS     := block char net #streams
 MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sound cdrom
+ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sound cdrom isdn
 
 ifdef CONFIG_PCI
 SUB_DIRS += pci
@@ -39,4 +39,13 @@ SUB_DIRS += cdrom
 MOD_SUB_DIRS += cdrom
 endif
 
+ifeq ($(CONFIG_ISDN),y)
+SUB_DIRS += isdn
+MOD_SUB_DIRS += isdn
+else
+  ifeq ($(CONFIG_ISDN),m)
+  MOD_SUB_DIRS += isdn
+  endif
+endif
+
 include $(TOPDIR)/Rules.make
index 78c216e22ce79a3cc80748e863bbbf009430c711..2aaa1b8f31541b95b4d731bc821fe3548c3c01ad 100644 (file)
@@ -37,4 +37,9 @@ if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then
 fi
 
 bool 'XT harddisk support' CONFIG_BLK_DEV_XD
+bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
+if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
+  tristate '   Linear (append) mode' CONFIG_MD_LINEAR
+  tristate '   RAID-0 (striping) mode' CONFIG_MD_STRIPED
+fi
 endmenu
index 33968fed957493611b230b88159220a7cb0174a5..595ea711e1e1e20e41d805c70c74a25b4b646726 100644 (file)
@@ -19,6 +19,7 @@ L_TARGET := block.a
 L_OBJS   := ll_rw_blk.o genhd.o 
 M_OBJS   :=
 MOD_LIST_NAME := BLOCK_MODULES
+LX_OBJS :=
 
 ifeq ($(CONFIG_BLK_DEV_FD),y)
 L_OBJS += floppy.o
@@ -96,4 +97,45 @@ ifeq ($(CONFIG_BLK_DEV_XD),y)
 L_OBJS += xd.o
 endif
 
+ifeq ($(CONFIG_BLK_DEV_MD),y)
+LX_OBJS += md.o
+
+ifeq ($(CONFIG_MD_LINEAR),y)
+L_OBJS += linear.o
+else
+  ifeq ($(CONFIG_MD_LINEAR),m)
+  M_OBJS += linear.o
+  endif
+endif
+
+ifeq ($(CONFIG_MD_STRIPED),y)
+L_OBJS += raid0.o
+else
+  ifeq ($(CONFIG_MD_STRIPED),m)
+  M_OBJS += raid0.o
+  endif
+endif
+
+#ifeq ($(CONFIG_MD_RAID1),y)
+#L_OBJS += raid1.o
+#else
+#  ifeq ($(CONFIG_MD_SUPPORT_RAID1),y)
+#    ifeq ($(CONFIG_MD_RAID1),m)
+#    M_OBJS += raid1.o
+#    endif
+#  endif
+#endif
+#
+#ifeq ($(CONFIG_MD_RAID5),y)
+#L_OBJS += raid5.o
+#else
+#  ifeq ($(CONFIG_MD_SUPPORT_RAID5),y)
+#    ifeq ($(CONFIG_MD_RAID5),m)
+#    M_OBJS += raid5.o
+#    endif
+#  endif
+#endif
+
+endif
+
 include $(TOPDIR)/Rules.make
diff --git a/drivers/block/README.md b/drivers/block/README.md
new file mode 100644 (file)
index 0000000..1a1d416
--- /dev/null
@@ -0,0 +1,4 @@
+Tools that manage md devices can be found at sweet-smoke.ufr-info-p7.ibp.fr
+in public/Linux/md034.tar.gz.
+
+       Marc ZYNGIER <zyngier@ufr-info-p7.ibp.fr>
diff --git a/drivers/block/linear.c b/drivers/block/linear.c
new file mode 100644 (file)
index 0000000..a73cc5a
--- /dev/null
@@ -0,0 +1,241 @@
+
+/*
+   linear.c : Multiple Devices driver for Linux
+              Copyright (C) 1994-96 Marc ZYNGIER
+             <zyngier@ufr-info-p7.ibp.fr> or
+             <maz@gloups.fdn.fr>
+
+   Linear mode management functions.
+
+   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.
+   
+   You should have received a copy of the GNU General Public License
+   (for example /usr/src/linux/COPYING); if not, write to the Free
+   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+*/
+
+#include <linux/module.h>
+
+#include <linux/md.h>
+#include <linux/linear.h>
+#include <linux/malloc.h>
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+#define MD_PERSONALITY
+
+#include <linux/blk.h>
+
+static int linear_run (int minor, struct md_dev *mddev)
+{
+  int cur=0, i, size, dev0_size, nb_zone;
+  struct linear_data *data;
+
+  MOD_INC_USE_COUNT;
+  
+  mddev->private=kmalloc (sizeof (struct linear_data), GFP_KERNEL);
+  data=(struct linear_data *) mddev->private;
+
+  /*
+     Find out the smallest device. This was previously done
+     at registery time, but since it violates modularity,
+     I moved it here... Any comment ? ;-)
+   */
+
+  data->smallest=devices[minor];
+  for (i=1; i<mddev->nb_dev; i++)
+    if (data->smallest->size > devices[minor][i].size)
+      data->smallest=devices[minor]+i;
+  
+  nb_zone=data->nr_zones=
+    md_size[minor]/data->smallest->size +
+    (md_size[minor]%data->smallest->size ? 1 : 0);
+  
+  data->hash_table=kmalloc (sizeof (struct linear_hash)*nb_zone, GFP_KERNEL);
+
+  size=devices[minor][cur].size;
+
+  i=0;
+  while (cur<mddev->nb_dev)
+  {
+    data->hash_table[i].dev0=devices[minor]+cur;
+
+    if (size>=data->smallest->size) /* If we completly fill the slot */
+    {
+      data->hash_table[i++].dev1=NULL;
+      size-=data->smallest->size;
+
+      if (!size)
+      {
+       if (++cur==mddev->nb_dev) continue;
+       size=devices[minor][cur].size;
+      }
+
+      continue;
+    }
+
+    if (++cur==mddev->nb_dev) /* Last dev, set dev1 as NULL */
+    {
+      data->hash_table[i].dev1=NULL;
+      continue;
+    }
+
+    dev0_size=size;            /* Here, we use a 2nd dev to fill the slot */
+    size=devices[minor][cur].size;
+    data->hash_table[i++].dev1=devices[minor]+cur;
+    size-=(data->smallest->size - dev0_size);
+  }
+
+  return 0;
+}
+
+static int linear_stop (int minor, struct md_dev *mddev)
+{
+  struct linear_data *data=(struct linear_data *) mddev->private;
+  
+  kfree (data->hash_table);
+  kfree (data);
+
+  MOD_DEC_USE_COUNT;
+
+  return 0;
+}
+
+
+static int linear_map (int minor, struct md_dev *mddev, struct request *req)
+{
+  struct linear_data *data=(struct linear_data *) mddev->private;
+  struct linear_hash *hash;
+  struct real_dev *tmp_dev;
+  long block, rblock;
+  struct buffer_head *bh, *bh2;
+  int queue, nblk;
+  static struct request pending[MAX_REAL]={{0, }, };
+
+  while (req->nr_sectors)
+  {
+    block=req->sector >> 1;
+    hash=data->hash_table+(block/data->smallest->size);
+    
+    if (block >= (hash->dev0->size + hash->dev0->offset))
+    {
+      if (!hash->dev1)
+       printk ("linear_map : hash->dev1==NULL for block %ld\n", block);
+      tmp_dev=hash->dev1;
+    }
+    else
+      tmp_dev=hash->dev0;
+    
+    if (block >= (tmp_dev->size + tmp_dev->offset) || block < tmp_dev->offset)
+      printk ("Block %ld out of bounds on dev %04x size %d offset %d\n", block, tmp_dev->dev, tmp_dev->size, tmp_dev->offset);
+    
+    rblock=(block-(tmp_dev->offset));
+    
+    if (req->sem)                              /* This is a paging request */
+    {
+      req->rq_dev=tmp_dev->dev;
+      req->sector=rblock << 1;
+      add_request (blk_dev+MAJOR (tmp_dev->dev), req);
+      
+      return REDIRECTED_REQ;
+    }
+
+    queue=tmp_dev - devices[minor];
+
+    for (nblk=0, bh=bh2=req->bh;
+        bh && rblock + nblk + (bh->b_size >> 10) <= tmp_dev->size;
+        nblk+=bh->b_size >> 10, bh2=bh, bh=bh->b_reqnext)
+    {
+      if (!buffer_locked(bh))
+       printk("md%d: block %ld not locked\n", minor, bh->b_blocknr);
+      
+      bh->b_rdev=tmp_dev->dev;
+    }
+
+    pending[queue].rq_dev=tmp_dev->dev;
+    pending[queue].cmd=req->cmd;
+    pending[queue].sector=rblock << 1;
+    pending[queue].nr_sectors=nblk << 1;
+    pending[queue].current_nr_sectors=req->bh->b_size >> 9;
+    pending[queue].bh=req->bh;
+    pending[queue].bhtail=bh2;
+    bh2->b_reqnext=NULL;
+    
+    req->bh=bh;
+    req->sector+=nblk << 1;
+    req->nr_sectors-=nblk << 1;
+  }
+
+  req->rq_status=RQ_INACTIVE;
+  wake_up (&wait_for_request);
+  make_md_request (pending, mddev->nb_dev);
+  return REDIRECTED_REQ;
+}
+
+
+static int linear_status (char *page, int minor, struct md_dev *mddev)
+{
+  int sz=0;
+
+#undef MD_DEBUG
+#ifdef MD_DEBUG
+  int j;
+  struct linear_data *data=(struct linear_data *) mddev->private;
+  
+  sz+=sprintf (page+sz, "      ");
+  for (j=0; j<data->nr_zones; j++)
+  {
+    sz+=sprintf (page+sz, "[%s",
+                partition_name (data->hash_table[j].dev0->dev));
+
+    if (data->hash_table[j].dev1)
+      sz+=sprintf (page+sz, "/%s] ",
+                  partition_name(data->hash_table[j].dev1->dev));
+    else
+      sz+=sprintf (page+sz, "] ");
+  }
+
+  sz+=sprintf (page+sz, "\n");
+#endif
+  return sz;
+}
+
+
+static struct md_personality linear_personality=
+{
+  "linear",
+  linear_map,
+  linear_run,
+  linear_stop,
+  linear_status,
+  NULL,                                /* no ioctls */
+  0
+};
+
+
+#ifndef MODULE
+
+void linear_init (void)
+{
+  register_md_personality (LINEAR, &linear_personality);
+}
+
+#else
+
+int init_module (void)
+{
+  return (register_md_personality (LINEAR, &linear_personality));
+}
+
+void cleanup_module (void)
+{
+  if (MOD_IN_USE)
+    printk ("md linear : module still busy...\n");
+  else
+    unregister_md_personality (LINEAR);
+}
+
+#endif
index 14250098b13229fa0ad5fc3668b62e4c38358688..46386d23c7cc4f667db998a7730f0d904a6ca711 100644 (file)
 /*
  * The request-struct contains all necessary data
  * to load a nr of sectors into memory
- *
- * NR_REQUEST is the number of entries in the request-queue.
- * NOTE that writes may use only the low 2/3 of these: reads
- * take precedence.
  */
-#define NR_REQUEST     64
 static struct request all_requests[NR_REQUEST];
 
 /*
@@ -232,11 +227,15 @@ static inline void drive_stat_acct(int cmd, unsigned long nr_sectors, short disk
  * By this point, req->cmd is always either READ/WRITE, never READA/WRITEA,
  * which is important for drive_stat_acct() above.
  */
-static void add_request(struct blk_dev_struct * dev, struct request * req)
+
+struct semaphore request_lock = MUTEX;
+
+void add_request(struct blk_dev_struct * dev, struct request * req)
 {
        struct request * tmp;
        short            disk_index;
 
+       down (&request_lock);
        switch (MAJOR(req->rq_dev)) {
                case SCSI_DISK_MAJOR:
                        disk_index = (MINOR(req->rq_dev) & 0x0070) >> 4;
@@ -257,10 +256,11 @@ static void add_request(struct blk_dev_struct * dev, struct request * req)
 
        req->next = NULL;
        cli();
-       if (req->bh)
+       if (req->bh && req->bh->b_dev==req->bh->b_rdev)
                mark_buffer_clean(req->bh);
        if (!(tmp = dev->current_request)) {
                dev->current_request = req;
+               up (&request_lock);
                (dev->request_fn)();
                sti();
                return;
@@ -274,8 +274,9 @@ static void add_request(struct blk_dev_struct * dev, struct request * req)
        req->next = tmp->next;
        tmp->next = req;
 
+       up (&request_lock);
 /* for SCSI devices, call request_fn unconditionally */
-       if (scsi_major(MAJOR(req->rq_dev)))
+       if (scsi_major(MAJOR(req->rq_dev)) && MAJOR(req->rq_dev)!=MD_MAJOR)
                (dev->request_fn)();
 
        sti();
@@ -338,6 +339,7 @@ static void make_request(int major,int rw, struct buffer_head * bh)
 
 /* look for a free request. */
        cli();
+       down (&request_lock);
 
 /* The scsi disk and cdrom drivers completely remove the request
  * from the queue when they start processing an entry.  For this reason
@@ -345,6 +347,7 @@ static void make_request(int major,int rw, struct buffer_head * bh)
  */
        if ((   major == IDE0_MAJOR     /* same as HD_MAJOR */
             || major == IDE1_MAJOR
+            || major == MD_MAJOR
             || major == FLOPPY_MAJOR
             || major == SCSI_DISK_MAJOR
             || major == SCSI_CDROM_MAJOR
@@ -354,6 +357,7 @@ static void make_request(int major,int rw, struct buffer_head * bh)
        {
                if (major != SCSI_DISK_MAJOR && major != SCSI_CDROM_MAJOR)
                        req = req->next;
+
                while (req) {
                        if (req->rq_dev == bh->b_dev &&
                            !req->sem &&
@@ -365,6 +369,7 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                                req->bhtail = bh;
                                req->nr_sectors += count;
                                mark_buffer_clean(bh);
+                               up (&request_lock);
                                sti();
                                return;
                        }
@@ -382,6 +387,7 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                                req->sector = sector;
                                mark_buffer_clean(bh);
                                req->bh = bh;
+                               up (&request_lock);
                                sti();
                                return;
                        }    
@@ -390,6 +396,8 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                }
        }
 
+       up (&request_lock);
+       
 /* find an unused request. */
        req = get_request(max_req, bh->b_dev);
        sti();
@@ -417,6 +425,15 @@ static void make_request(int major,int rw, struct buffer_head * bh)
        add_request(major+blk_dev,req);
 }
 
+#ifdef CONFIG_BLK_DEV_MD
+
+struct request *get_md_request (int max_req, kdev_t dev)
+{
+  return (get_request_wait (max_req, dev));
+}
+
+#endif
+
 /*
  * Swap partitions are now read via brw_page.  ll_rw_page is an
  * asynchronous function now --- we must call wait_on_page afterwards
@@ -515,6 +532,10 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
        for (i = 0; i < nr; i++) {
                if (bh[i]) {
                        set_bit(BH_Req, &bh[i]->b_state);
+
+                       /* Md needs this for error recovery */
+                       bh[i]->b_rdev = bh[i]->b_dev;
+
                        make_request(major, rw, bh[i]);
                }
        }
@@ -658,5 +679,8 @@ int blk_dev_init(void)
 #ifdef CONFIG_SJCD
        sjcd_init();
 #endif CONFIG_SJCD
+#ifdef CONFIG_BLK_DEV_MD
+       md_init();
+#endif CONFIG_BLK_DEV_MD
        return 0;
 }
index cdbb9794d1437a981883b83b055c5771f40857b3..fdec8f5979c16ab071e20489e1f9a74f3f1656d9 100644 (file)
@@ -29,7 +29,7 @@
 #include "des.h"
 #endif
 
-#define DEFAULT_MAJOR_NR 28
+#define DEFAULT_MAJOR_NR 10
 int loop_major = DEFAULT_MAJOR_NR;
 #define MAJOR_NR loop_major    /* not necessarily constant */
 
@@ -225,15 +225,8 @@ repeat:
                        brelse(bh);
                        goto error_out;
                }
-               if (CURRENT->cmd == WRITE) {
-                       set_bit(BH_Dirty, &bh->b_state);
-                       ll_rw_block(WRITE, 1, &bh);
-                       wait_on_buffer(bh);
-                       if (buffer_dirty(bh)) {
-                               brelse(bh);
-                               goto error_out;
-                       }
-               }
+               if (CURRENT->cmd == WRITE)
+                       mark_buffer_dirty(bh, 1);
                brelse(bh);
                dest_addr += size;
                len -= size;
@@ -280,7 +273,7 @@ static int loop_clr_fd(struct loop_device *lo, kdev_t dev)
                return -ENXIO;
        if (lo->lo_refcnt > 1)
                return -EBUSY;
-       lo->lo_inode->i_count--;
+       iput(lo->lo_inode);
        lo->lo_device = 0;
        lo->lo_inode = NULL;
        lo->lo_encrypt_type = 0;
@@ -288,6 +281,7 @@ static int loop_clr_fd(struct loop_device *lo, kdev_t dev)
        lo->lo_encrypt_key_size = 0;
        memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
        memset(lo->lo_name, 0, LO_NAME_SIZE);
+       loop_sizes[lo->lo_number] = 0;
        invalidate_buffers(dev);
        return 0;
 }
index 2c92d5585806b5774516f129c72fa5db1bac1312..3724bb0e980076784b215efe0929b4c0c9473b90 100644 (file)
@@ -62,7 +62,7 @@ struct loop_info {
 #define LO_CRYPT_NONE  0
 #define LO_CRYPT_XOR   1
 #define LO_CRYPT_DES   2
-#define LO_CRYPT_IDEA  5
+#define LO_CRYPT_IDEA  3
 #define MAX_LO_CRYPT   4
 
 /*
diff --git a/drivers/block/md.c b/drivers/block/md.c
new file mode 100644 (file)
index 0000000..a478cb9
--- /dev/null
@@ -0,0 +1,814 @@
+
+/*
+   md.c : Multiple Devices driver for Linux
+          Copyright (C) 1994-96 Marc ZYNGIER
+         <zyngier@ufr-info-p7.ibp.fr> or
+         <maz@gloups.fdn.fr>
+
+   A lot of inspiration came from hd.c ...
+
+   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.
+   
+   You should have received a copy of the GNU General Public License
+   (for example /usr/src/linux/COPYING); if not, write to the Free
+   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/md.h>
+#include <linux/hdreg.h>
+#include <linux/stat.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/blkdev.h>
+#include <errno.h>
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+
+#include <linux/blk.h>
+
+#ifdef CONFIG_MD_SUPPORT_RAID5
+int support_for_raid5; /* So raid-5 module won't be inserted if support
+                         was not set in the kernel */
+#endif
+
+#ifdef CONFIG_MD_SUPPORT_RAID1
+int support_for_raid1; /* So raid-1 module won't be inserted if support
+                         was not set in the kernel */
+#endif
+
+static struct hd_struct md_hd_struct[MAX_MD_DEV];
+static int md_blocksizes[MAX_MD_DEV];
+
+int md_size[MAX_MD_DEV]={0, };
+
+static void md_geninit (struct gendisk *);
+
+static struct gendisk md_gendisk=
+{
+  MD_MAJOR,
+  "md",
+  0,
+  1,
+  MAX_MD_DEV,
+  md_geninit,
+  md_hd_struct,
+  md_size,
+  MAX_MD_DEV,
+  NULL,
+  NULL
+};
+
+static struct md_personality *pers[MAX_PERSONALITY]={NULL, };
+
+struct real_dev devices[MAX_MD_DEV][MAX_REAL];
+struct md_dev md_dev[MAX_MD_DEV];
+
+static struct gendisk *find_gendisk (kdev_t dev)
+{
+  struct gendisk *tmp=gendisk_head;
+
+  while (tmp != NULL)
+  {
+    if (tmp->major==MAJOR(dev))
+      return (tmp);
+    
+    tmp=tmp->next;
+  }
+
+  return (NULL);
+}
+
+
+/* Picked up from genhd.c */
+char *partition_name (kdev_t dev)
+{
+  static char name[10];                /* This should be long
+                                  enough for a device name ! */
+  struct gendisk *hd=find_gendisk (dev);
+  char base_name;
+  int minor=MINOR(dev);
+
+  if (!hd)
+  {
+    printk ("No gendisk entry for dev %04x\n", dev);
+    sprintf (name, "dev %04x", dev);
+    return (name);
+  }
+
+  base_name = (hd->major == IDE1_MAJOR) ? 'c' : 'a';
+  sprintf(name, "%s%c%d",
+         hd->major_name,
+         base_name + (minor >> hd->minor_shift),
+         minor & ((1 << hd->minor_shift) - 1));
+  return (name);
+}
+
+
+static void set_ra (void)
+{
+  int i, j, minra=INT_MAX;
+
+  for (i=0; i<MAX_MD_DEV; i++)
+  {
+    if (!md_dev[i].pers)
+      continue;
+    
+    for (j=0; j<md_dev[i].nb_dev; j++)
+      if (read_ahead[MAJOR(devices[i][j].dev)]<minra)
+       minra=read_ahead[MAJOR(devices[i][j].dev)];
+  }
+  
+  read_ahead[MD_MAJOR]=minra;
+}
+
+
+static int md_ioctl (struct inode *inode, struct file *file,
+                     unsigned int cmd, unsigned long arg)
+{
+  int minor, index, err, current_ra;
+  struct gendisk *gen_real;
+  struct hd_geometry *loc = (struct hd_geometry *) arg;
+  kdev_t dev;
+
+  if (!suser())
+    return -EACCES;
+
+  if (((minor=MINOR(inode->i_rdev)) & 0x80) &&
+      (minor & 0x7f) < MAX_PERSONALITY &&
+      pers[minor & 0x7f] &&
+      pers[minor & 0x7f]->ioctl)
+    return (pers[minor & 0x7f]->ioctl (inode, file, cmd, arg));
+  
+  if (minor >= MAX_MD_DEV)
+    return -EINVAL;
+
+  switch (cmd)
+  {
+    case REGISTER_DEV:
+    dev=to_kdev_t ((dev_t) arg);
+    if (MAJOR(dev)==MD_MAJOR || md_dev[minor].nb_dev==MAX_REAL)
+      return -EINVAL;
+
+    if (!fs_may_mount (dev) || md_dev[minor].pers)
+      return -EBUSY;
+
+    if (!(gen_real=find_gendisk (dev)))
+      return -ENOENT;
+
+    index=md_dev[minor].nb_dev++;
+    devices[minor][index].dev=dev;
+
+    /* Lock the device by inserting a dummy inode. This doesn't
+       smeel very good, but I need to be consistent with the
+       mount stuff, specially with fs_may_mount. If someone have
+       a better idea, please help ! */
+    
+    devices[minor][index].inode=get_empty_inode ();
+    devices[minor][index].inode->i_dev=dev; /* don't care about
+                                              other fields */
+    insert_inode_hash (devices[minor][index].inode);
+    
+    /* Devices sizes are rounded to a multiple of page (needed for
+       paging). This is NOT done by fdisk when partitionning,
+       but that's a DOS thing anyway... */
+    
+    devices[minor][index].size=gen_real->sizes[MINOR(dev)] & (PAGE_MASK>>10);
+    devices[minor][index].offset=index ?
+      (devices[minor][index-1].offset + devices[minor][index-1].size) : 0;
+
+    if (!index)
+      md_size[minor]=devices[minor][index].size;
+    else
+      md_size[minor]+=devices[minor][index].size;
+
+    printk("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor);
+    break;
+
+    case START_MD:
+    if (!md_dev[minor].nb_dev)
+      return -EINVAL;
+
+    if (md_dev[minor].pers)
+      return -EBUSY;
+
+    md_dev[minor].repartition=(int) arg;
+    
+    if ((index=PERSONALITY(md_dev+minor) >> (PERSONALITY_SHIFT))
+       >= MAX_PERSONALITY ||
+       !pers[index])
+      return -EINVAL;
+
+    md_dev[minor].pers=pers[index];
+
+    if ((err=md_dev[minor].pers->run (minor, md_dev+minor)))
+    {
+      md_dev[minor].pers=NULL;
+      return (err);
+    }
+
+    /* FIXME : We assume here we have blocks
+       that are twice as large as sectors.
+       THIS MAY NOT BE TRUE !!! */
+    md_hd_struct[minor].start_sect=0;
+    md_hd_struct[minor].nr_sects=md_size[minor]<<1;
+
+    /* It would be better to have a per-md-dev read_ahead. Currently,
+       we only use the smallest read_ahead among md-attached devices */
+
+    current_ra=read_ahead[MD_MAJOR];
+    
+    for (index=0; index<md_dev[minor].nb_dev; index++)
+    {
+      if (current_ra>read_ahead[MAJOR(devices[minor][index].dev)])
+       current_ra=read_ahead[MAJOR(devices[minor][index].dev)];
+
+      devices[minor][index].fault_count=0;
+      devices[minor][index].invalid=VALID;
+    }
+
+    read_ahead[MD_MAJOR]=current_ra;
+
+    printk ("START_DEV md%x %s\n", minor, md_dev[minor].pers->name);
+    break;
+
+    case STOP_MD:
+    if (inode->i_count>1 || md_dev[minor].busy>1) /* ioctl : one open channel */
+    {
+      printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", minor, inode->i_count, md_dev[minor].busy);
+      return -EBUSY;
+    }
+
+    if (md_dev[minor].pers)
+    {
+      /*  The device won't exist anymore -> flush it now */
+      fsync_dev (inode->i_rdev);
+      invalidate_buffers (inode->i_rdev);
+      md_dev[minor].pers->stop (minor, md_dev+minor);
+    }
+
+    /* Remove locks. */
+    for (index=0; index<md_dev[minor].nb_dev; index++)
+      clear_inode (devices[minor][index].inode);
+
+    md_dev[minor].nb_dev=md_size[minor]=0;
+    md_dev[minor].pers=NULL;
+
+    set_ra ();                 /* calculate new read_ahead */
+    
+    printk ("STOP_DEV md%x\n", minor);
+    break;
+
+#if defined(CONFIG_MD_SUPPORT_RAID1) || defined(CONFIG_MD_SUPPORT_RAID5)
+    case MD_INVALID:
+    dev=to_kdev_t ((dev_t) arg);
+    if (!(err=md_valid_device (minor, dev, INVALID_ALWAYS)))
+      printk ("md%d : %s disabled\n", minor, partition_name (dev));
+
+    return (err);
+
+    case MD_VALID:
+    dev=to_kdev_t ((dev_t) arg);
+    if (!(err=md_valid_device (minor, dev, VALID)))
+      printk ("md%d : %s enabled\n", minor, partition_name (dev));
+
+    return (err);
+#endif
+    
+    case BLKGETSIZE:   /* Return device size */
+    if  (!arg)  return -EINVAL;
+    err=verify_area (VERIFY_WRITE, (long *) arg, sizeof(long));
+    if (err)
+      return err;
+    put_user (md_hd_struct[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
+    break;
+
+    case BLKFLSBUF:
+    fsync_dev (inode->i_rdev);
+    invalidate_buffers (inode->i_rdev);
+    break;
+
+    case BLKRASET:
+    if (arg > 0xff)
+      return -EINVAL;
+    read_ahead[MAJOR(inode->i_rdev)] = arg;
+    return 0;
+    
+    case BLKRAGET:
+    if  (!arg)  return -EINVAL;
+    err=verify_area (VERIFY_WRITE, (long *) arg, sizeof(long));
+    if (err)
+      return err;
+    put_user (read_ahead[MAJOR(inode->i_rdev)], (long *) arg);
+    break;
+
+    case HDIO_GETGEO:
+    if (!loc)  return -EINVAL;
+    err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+    if (err)
+      return err;
+    put_user (2, (char *) &loc->heads);
+    put_user (4, (char *) &loc->sectors);
+    put_user (md_hd_struct[minor].nr_sects/8, (short *) &loc->cylinders);
+    put_user (md_hd_struct[MINOR(inode->i_rdev)].start_sect,
+               (long *) &loc->start);
+    break;
+    
+    RO_IOCTLS(inode->i_rdev,arg);
+    
+    default:
+    printk ("Unknown md_ioctl %d\n", cmd);
+    return -EINVAL;
+  }
+
+  return (0);
+}
+
+
+static int md_open (struct inode *inode, struct file *file)
+{
+  int minor=MINOR(inode->i_rdev);
+
+  md_dev[minor].busy++;
+  return (0);                  /* Always succed */
+}
+
+
+static void md_release (struct inode *inode, struct file *file)
+{
+  int minor=MINOR(inode->i_rdev);
+
+  sync_dev (inode->i_rdev);
+  md_dev[minor].busy--;
+}
+
+
+static struct file_operations md_fops=
+{
+  NULL,
+  block_read,
+  block_write,
+  NULL,
+  NULL,
+  md_ioctl,
+  NULL,
+  md_open,
+  md_release,
+  block_fsync
+};
+
+
+static inline int remap_request (int minor, struct request *req)
+{
+  if (!md_dev[minor].pers)
+  {
+    printk ("Oops ! md%d not running, giving up !\n", minor);
+    return -1;
+  }
+
+  return (md_dev[minor].pers->map(minor, md_dev+minor, req));
+}
+
+static void do_md_request (void)
+{
+  int minor;
+  struct request *req;
+    
+  while (1)
+  {
+#ifdef MD_COUNT_SIZE
+    int reqsize, chunksize;
+#endif
+    
+    cli ();
+    req = blk_dev[MD_MAJOR].current_request;
+    if (!req || (req->rq_status == RQ_INACTIVE))
+    {
+      sti ();
+      return;
+    }
+    
+#ifdef MD_COUNT_SIZE
+    reqsize=req->nr_sectors>>1;
+    chunksize=1 << FACTOR_SHIFT(FACTOR(md_dev+MINOR(req->rq_dev)));
+    if (reqsize==chunksize) (md_dev+MINOR(req->rq_dev))->equal_count++;
+    if (reqsize<chunksize) (md_dev+MINOR(req->rq_dev))->smallest_count++;
+    if (reqsize>chunksize) (md_dev+MINOR(req->rq_dev))->biggest_count++;
+#endif
+    
+    blk_dev[MD_MAJOR].current_request = req->next;
+    sti ();
+
+    minor = MINOR(req->rq_dev);
+    if ((MAJOR(req->rq_dev) != MD_MAJOR) || (minor >= MAX_REAL))
+    {
+      printk("md: bad device number: 0x%04x\n", req->rq_dev);
+      end_request(0, req);
+      continue;
+    }
+
+    switch (remap_request (minor, req))
+    {
+      case REDIRECTED_BHREQ:   /* Allright, redirection was succesful */
+      req->rq_status=RQ_INACTIVE;
+      wake_up (&wait_for_request);
+      break;
+
+      case REDIRECTED_REQ:
+      break;                   /* Redirected whole request (for swaping) */
+      
+      case REDIRECT_FAILED:    /* Swap redirection failed in RAID-[15] */
+      end_request (0, req);
+      break;
+      
+      default:
+      printk ("remap_request returned strange value !\n");
+    }
+  }
+}
+
+extern struct semaphore request_lock;
+
+void make_md_request (struct request *pending, int n)
+{
+  int i, j, max_req, major=0, rw, found;
+  kdev_t dev;
+  struct buffer_head *bh;
+  struct request *req;
+  
+  down (&request_lock);
+
+  for (i=0; i<n; i++)
+  {
+    if (!pending[i].bh)
+      continue;
+
+    cli();
+
+    found=0;
+    rw=pending[i].cmd;
+    bh=pending[i].bh;
+    major=MAJOR(dev=pending[i].rq_dev);
+    
+    max_req = (rw == READ) ? NR_REQUEST : ((NR_REQUEST*2)/3);
+    if ((   major == IDE0_MAJOR        /* same as HD_MAJOR */
+        || major == IDE1_MAJOR
+        || major == SCSI_DISK_MAJOR
+        || major == IDE2_MAJOR
+        || major == IDE3_MAJOR)
+       && (req = blk_dev[major].current_request))
+    {
+#ifdef CONFIG_BLK_DEV_HD
+      if (major == HD_MAJOR)
+       req = req->next;
+#endif CONFIG_BLK_DEV_HD
+
+      while (req && !found)
+      {
+       if (req->rq_status!=RQ_INACTIVE && req->rq_status!=RQ_ACTIVE)
+         printk ("Saw bad status request !\n");
+
+       if (req->rq_dev == dev &&
+           !req->sem &&
+           req->cmd == rw &&
+           req->sector + req->nr_sectors == pending[i].sector &&
+           (req->nr_sectors + pending[i].nr_sectors) < 245)
+       {
+         req->bhtail->b_reqnext = bh;
+         req->bhtail = pending[i].bhtail;
+         req->nr_sectors += pending[i].nr_sectors;
+         found=1;
+         continue;
+       }
+       
+       if (!found &&
+           req->rq_dev == dev &&
+           !req->sem &&
+           req->cmd == rw &&
+           req->sector - pending[i].nr_sectors == pending[i].sector &&
+           (req->nr_sectors + pending[i].nr_sectors) < 245)
+       {
+         req->nr_sectors += pending[i].nr_sectors;
+         bh->b_reqnext = req->bh;
+         req->buffer = bh->b_data;
+         req->current_nr_sectors = bh->b_size >> 9;
+         req->sector = pending[i].sector;
+         req->bh = bh;
+         found=1;
+         continue;
+       }    
+
+       req = req->next;
+      }
+    }
+
+    if (found)
+      continue;
+
+    up (&request_lock);
+    req=get_md_request (max_req, dev);
+    
+    /* Build it up... */
+    req->cmd = rw;
+    req->errors = 0;
+#if defined (CONFIG_MD_SUPPORT_RAID1)
+    req->shared_count = 0;
+#endif
+    req->sector = pending[i].sector;
+    req->nr_sectors = pending[i].nr_sectors;
+    req->current_nr_sectors = bh->b_size >> 9;
+    req->buffer = bh->b_data;
+    req->sem = NULL;
+    req->bh = bh;
+    req->bhtail = pending[i].bhtail;
+    req->next = NULL;
+
+    add_request (blk_dev + MAJOR(dev), req);
+    down (&request_lock);
+  }
+
+  up (&request_lock);
+  for (j=0; j<n; j++)
+  {
+    if (!pending[j].bh)
+      continue;
+    
+    pending[j].bh=NULL;
+  }
+
+  sti ();
+}
+
+
+static struct symbol_table md_symbol_table=
+{
+#include <linux/symtab_begin.h>
+
+  X(devices),
+  X(md_size),
+  X(add_request),
+  X(make_md_request),
+
+#ifdef CONFIG_MD_SUPPORT_RAID1
+  X(support_for_raid1),
+#endif
+
+#ifdef CONFIG_MD_SUPPORT_RAID5
+  X(support_for_raid5),
+#endif
+
+  X(register_md_personality),
+  X(unregister_md_personality),
+  X(partition_name),
+
+#if defined(CONFIG_MD_SUPPORT_RAID1) || defined(CONFIG_MD_SUPPORT_RAID5)
+  X(md_valid_device),
+  X(md_can_reemit),
+#endif
+
+#include <linux/symtab_end.h>
+};
+
+
+static void md_geninit (struct gendisk *gdisk)
+{
+  int i;
+  
+  for(i=0;i<MAX_MD_DEV;i++)
+  {
+    md_blocksizes[i] = 1024;
+    md_gendisk.part[i].start_sect=-1;
+    md_dev[i].pers=NULL;
+#ifdef MD_COUNT_SIZES
+    md_dev[i].smallest_count=md_dev[i].biggest_count=md_dev[i].equal_count=0;
+#endif
+  }
+
+  blksize_size[MAJOR_NR] = md_blocksizes;
+  register_symtab (&md_symbol_table);
+
+  proc_register(&proc_root,
+               &(struct proc_dir_entry)
+             {
+               PROC_MD, 6, "mdstat",
+               S_IFREG | S_IRUGO, 1, 0, 0,
+             });
+}
+
+
+int get_md_status (char *page)
+{
+  int sz=0, i, j;
+
+  sz+=sprintf( page+sz, "Personalities : ");
+  for (i=0; i<MAX_PERSONALITY; i++)
+    if (pers[i])
+      sz+=sprintf (page+sz, "[%d %s] ", i, pers[i]->name);
+
+  page[sz-1]='\n';
+
+  sz+=sprintf (page+sz, "read_ahead ");
+  if (read_ahead[MD_MAJOR]==INT_MAX)
+    sz+=sprintf (page+sz, "not set\n");
+  else
+    sz+=sprintf (page+sz, "%d sectors\n", read_ahead[MD_MAJOR]);
+  
+  for (i=0; i<MAX_MD_DEV; i++)
+  {
+    sz+=sprintf (page+sz, "md%d : %sactive", i, md_dev[i].pers ? "" : "in");
+
+    if (md_dev[i].pers)
+      sz+=sprintf (page+sz, " %s", md_dev[i].pers->name);
+
+    for (j=0; j<md_dev[i].nb_dev; j++)
+      sz+=sprintf (page+sz, " %s%s%s",
+                  (devices[i][j].invalid==VALID) ? "" : "(",
+                  partition_name(devices[i][j].dev),
+                  (devices[i][j].invalid==VALID) ? "" : ")");
+    
+    if (md_dev[i].nb_dev)
+      sz+=sprintf (page+sz, " %d blocks", md_size[i]);
+
+    if (!md_dev[i].pers)
+    {
+      sz+=sprintf (page+sz, "\n");
+      continue;
+    }
+
+    if (md_dev[i].pers->max_invalid_dev)
+      sz+=sprintf (page+sz, " maxfault=%ld", MAX_FAULT(md_dev+i));
+
+    if (md_dev[i].pers != pers[(LINEAR>>PERSONALITY_SHIFT)])
+    {
+      sz+=sprintf (page+sz, " %dk chunks", 1<<FACTOR_SHIFT(FACTOR(md_dev+i)));
+#ifdef MD_COUNT_SIZES
+      sz+=sprintf (page+sz, " (%d/%d/%d)",
+                  md_dev[i].smallest_count,
+                  md_dev[i].equal_count,
+                  md_dev[i].biggest_count);
+#endif
+    }
+    sz+=sprintf (page+sz, "\n");
+    sz+=md_dev[i].pers->status (page+sz, i, md_dev+i);
+  }
+  
+  return (sz);
+}
+
+#if defined(CONFIG_MD_SUPPORT_RAID1) || defined(CONFIG_MD_SUPPORT_RAID5)
+
+int md_valid_device (int minor, kdev_t dev, int mode)
+{
+  int i;
+
+  for (i=0; i<md_dev[minor].nb_dev; i++)
+    if (devices[minor][i].dev==dev)
+      break;
+
+  if (i>md_dev[minor].nb_dev)
+  {
+    printk ("Oops, dev %04x not found in md_valid_device\n", dev);
+    return -EINVAL;
+  }
+
+  switch (mode)
+  {
+    case VALID:
+    /* Don't consider INVALID_NEXT as a real invalidation.
+       Maybe that's not the good way to treat such a thing,
+       we'll see. */
+    if (devices[minor][i].invalid==INVALID_ALWAYS)
+    {
+      devices[minor][i].fault_count=0; /* reset fault count */
+      if (md_dev[minor].invalid_dev_count)
+       md_dev[minor].invalid_dev_count--;
+    }
+    break;
+
+    case INVALID:
+    if (devices[minor][i].invalid != VALID )
+      return 0;                        /* Don't invalidate twice */
+    
+    if (++devices[minor][i].fault_count > MAX_FAULT(md_dev+minor) &&
+       MAX_FAULT(md_dev+minor)!=0xFF)
+    {
+      /* We cannot tolerate this fault.
+        So sing a song, and say GoodBye to this device... */
+      
+      mode=INVALID_ALWAYS;
+      md_dev[minor].invalid_dev_count++;
+    }
+    else
+      /* FIXME :
+        If we reached the max_invalid_dev count, doing one
+        more invalidation will kill the md_dev. So we choose
+        not to invalid the physical dev in such a case. But
+        next access will probably fail... */
+      if (md_dev[minor].invalid_dev_count<=md_dev[minor].pers->max_invalid_dev)
+       mode=INVALID_NEXT;
+      else
+       mode=VALID;
+    break;
+
+    case INVALID_ALWAYS:       /* Only used via MD_INVALID ioctl */
+    md_dev[minor].invalid_dev_count++;
+  }
+  
+  devices[minor][i].invalid=mode;
+  return 0;
+}
+
+
+int md_can_reemit (int minor)
+{
+  /* FIXME :
+     If the device is raid-1 (md_dev[minor].pers->max_invalid_dev=-1),
+     always pretend that we can reemit the request.
+     Problem : if the 2 devices in the pair are dead, will loop
+     forever. Maybe having a per-personality can_reemit function would
+     help. */
+
+  if (!md_dev[minor].pers)
+    return (0);
+  
+  return(md_dev[minor].pers->max_invalid_dev &&
+        ((md_dev[minor].pers->max_invalid_dev==-1) ?
+        1 :
+        md_dev[minor].invalid_dev_count<=md_dev[minor].pers->max_invalid_dev));
+}
+
+#endif
+
+int register_md_personality (int p_num, struct md_personality *p)
+{
+  int i=(p_num >> PERSONALITY_SHIFT);
+
+  if (i >= MAX_PERSONALITY)
+    return -EINVAL;
+
+  if (pers[i])
+    return -EBUSY;
+  
+  pers[i]=p;
+  printk ("%s personality registered\n", p->name);
+  return 0;
+}
+
+int unregister_md_personality (int p_num)
+{
+  int i=(p_num >> PERSONALITY_SHIFT);
+
+  if (i >= MAX_PERSONALITY)
+    return -EINVAL;
+
+  printk ("%s personality unregistered\n", pers[i]->name);
+  pers[i]=NULL;
+  return 0;
+} 
+
+void linear_init (void);
+void raid0_init (void);
+void raid1_init (void);
+void raid5_init (void);
+
+int md_init (void)
+{
+  printk ("md driver %s MAX_MD_DEV=%d, MAX_REAL=%d\n", MD_VERSION, MAX_MD_DEV, MAX_REAL);
+
+  if (register_blkdev (MD_MAJOR, "md", &md_fops))
+  {
+    printk ("Unable to get major %d for md\n", MD_MAJOR);
+    return (-1);
+  }
+
+  blk_dev[MD_MAJOR].request_fn=DEVICE_REQUEST;
+  blk_dev[MD_MAJOR].current_request=NULL;
+  read_ahead[MD_MAJOR]=INT_MAX;
+  md_gendisk.next=gendisk_head;
+
+  gendisk_head=&md_gendisk;
+
+#ifdef CONFIG_MD_LINEAR
+  linear_init ();
+#endif
+#ifdef CONFIG_MD_STRIPED
+  raid0_init ();
+#endif
+#ifdef CONFIG_MD_RAID1
+  raid1_init ();
+#endif
+#ifdef CONFIG_MD_RAID5
+  raid5_init ();
+#endif
+  
+  return (0);
+}
diff --git a/drivers/block/raid0.c b/drivers/block/raid0.c
new file mode 100644 (file)
index 0000000..94d2dc5
--- /dev/null
@@ -0,0 +1,341 @@
+
+/*
+   raid0.c : Multiple Devices driver for Linux
+             Copyright (C) 1994-96 Marc ZYNGIER
+            <zyngier@ufr-info-p7.ibp.fr> or
+            <maz@gloups.fdn.fr>
+
+   RAID-0 management functions.
+
+   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.
+   
+   You should have received a copy of the GNU General Public License
+   (for example /usr/src/linux/COPYING); if not, write to the Free
+   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/md.h>
+#include <linux/raid0.h>
+#include <linux/malloc.h>
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+#define MD_PERSONALITY
+
+#include <linux/blk.h>
+
+static void create_strip_zones (int minor, struct md_dev *mddev)
+{
+  int i, j, c=0;
+  int current_offset=0;
+  struct real_dev *smallest_by_zone;
+  struct raid0_data *data=(struct raid0_data *) mddev->private;
+  
+  data->nr_strip_zones=1;
+  
+  for (i=1; i<mddev->nb_dev; i++)
+  {
+    for (j=0; j<i; j++)
+      if (devices[minor][i].size==devices[minor][j].size)
+      {
+       c=1;
+       break;
+      }
+
+    if (!c)
+      data->nr_strip_zones++;
+
+    c=0;
+  }
+
+  data->strip_zone=kmalloc (sizeof(struct strip_zone)*data->nr_strip_zones,
+                             GFP_KERNEL);
+
+  data->smallest=NULL;
+  
+  for (i=0; i<data->nr_strip_zones; i++)
+  {
+    data->strip_zone[i].dev_offset=current_offset;
+    smallest_by_zone=NULL;
+    c=0;
+
+    for (j=0; j<mddev->nb_dev; j++)
+      if (devices[minor][j].size>current_offset)
+      {
+       data->strip_zone[i].dev[c++]=devices[minor]+j;
+       if (!smallest_by_zone ||
+           smallest_by_zone->size > devices[minor][j].size)
+         smallest_by_zone=devices[minor]+j;
+      }
+
+    data->strip_zone[i].nb_dev=c;
+    data->strip_zone[i].size=(smallest_by_zone->size-current_offset)*c;
+
+    if (!data->smallest ||
+       data->smallest->size > data->strip_zone[i].size)
+      data->smallest=data->strip_zone+i;
+
+    data->strip_zone[i].zone_offset=i ? (data->strip_zone[i-1].zone_offset+
+                                          data->strip_zone[i-1].size) : 0;
+    current_offset=smallest_by_zone->size;
+  }
+}
+
+static int raid0_run (int minor, struct md_dev *mddev)
+{
+  int cur=0, i=0, size, zone0_size, nb_zone, min;
+  struct raid0_data *data;
+
+  min=1 << FACTOR_SHIFT(FACTOR(mddev));
+
+  for (i=0; i<mddev->nb_dev; i++)
+    if (devices[minor][i].size<min)
+    {
+      printk ("Cannot use %dk chunks on dev %s\n", min,
+             partition_name (devices[minor][i].dev));
+      return -EINVAL;
+    }
+  
+  MOD_INC_USE_COUNT;
+  
+  /* Resize devices according to the factor */
+  md_size[minor]=0;
+  
+  for (i=0; i<mddev->nb_dev; i++)
+  {
+    devices[minor][i].size &= ~((1 << FACTOR_SHIFT(FACTOR(mddev))) - 1);
+    md_size[minor] += devices[minor][i].size;
+  }
+
+  mddev->private=kmalloc (sizeof (struct raid0_data), GFP_KERNEL);
+  data=(struct raid0_data *) mddev->private;
+  
+  create_strip_zones (minor, mddev);
+
+  nb_zone=data->nr_zones=
+    md_size[minor]/data->smallest->size +
+    (md_size[minor]%data->smallest->size ? 1 : 0);
+  
+  data->hash_table=kmalloc (sizeof (struct raid0_hash)*nb_zone, GFP_KERNEL);
+
+  size=data->strip_zone[cur].size;
+
+  i=0;
+  while (cur<data->nr_strip_zones)
+  {
+    data->hash_table[i].zone0=data->strip_zone+cur;
+
+    if (size>=data->smallest->size)/* If we completly fill the slot */
+    {
+      data->hash_table[i++].zone1=NULL;
+      size-=data->smallest->size;
+
+      if (!size)
+      {
+       if (++cur==data->nr_strip_zones) continue;
+       size=data->strip_zone[cur].size;
+      }
+
+      continue;
+    }
+
+    if (++cur==data->nr_strip_zones) /* Last dev, set unit1 as NULL */
+    {
+      data->hash_table[i].zone1=NULL;
+      continue;
+    }
+
+    zone0_size=size;           /* Here, we use a 2nd dev to fill the slot */
+    size=data->strip_zone[cur].size;
+    data->hash_table[i++].zone1=data->strip_zone+cur;
+    size-=(data->smallest->size - zone0_size);
+  }
+
+  return (0);
+}
+
+
+static int raid0_stop (int minor, struct md_dev *mddev)
+{
+  struct raid0_data *data=(struct raid0_data *) mddev->private;
+
+  kfree (data->hash_table);
+  kfree (data->strip_zone);
+  kfree (data);
+
+  MOD_DEC_USE_COUNT;
+  return 0;
+}
+
+/*
+ * FIXME - We assume some things here :
+ * - requested buffers NEVER bigger than chunk size,
+ * - requested buffers NEVER cross stripes limits.
+ * Of course, those facts may not be valid anymore (and surely won't...)
+ * Hey guys, there's some work out there ;-)
+ */
+static int raid0_map (int minor, struct md_dev *mddev, struct request *req)
+{
+  struct raid0_data *data=(struct raid0_data *) mddev->private;
+  static struct raid0_hash *hash;
+  struct strip_zone *zone;
+  struct real_dev *tmp_dev;
+  int i, queue, blk_in_chunk, factor, chunk;
+  long block, rblock;
+  struct buffer_head *bh;
+  static struct request pending[MAX_REAL]={{0, }, };
+
+  factor=FACTOR(mddev);
+
+  while (req->bh || req->sem)
+  {
+    block=req->sector >> 1;
+    hash=data->hash_table+(block/data->smallest->size);
+    
+    if (block >= (hash->zone0->size +
+                 hash->zone0->zone_offset))
+    {
+      if (!hash->zone1)
+       printk ("raid0_map : hash->zone1==NULL for block %ld\n", block);
+      zone=hash->zone1;
+    }
+    else
+      zone=hash->zone0;
+    
+    blk_in_chunk=block & ((1UL << FACTOR_SHIFT(factor)) - 1);
+    chunk=(block - zone->zone_offset) / (zone->nb_dev<<FACTOR_SHIFT(factor));
+    tmp_dev=zone->dev[(block >> FACTOR_SHIFT(factor)) % zone->nb_dev];
+    rblock=(chunk << FACTOR_SHIFT(factor)) + blk_in_chunk + zone->dev_offset;
+
+    if (req->sem)              /* This is a paging request */
+    {
+      req->rq_dev=tmp_dev->dev;
+      req->sector=rblock << 1;
+      add_request (blk_dev+MAJOR (tmp_dev->dev), req);
+
+      return REDIRECTED_REQ;
+    }
+
+    queue=tmp_dev - devices[minor];
+    
+                               /* This is a buffer request */
+    for (i=blk_in_chunk;
+        i<(1UL << FACTOR_SHIFT(factor)) && req->bh;
+        i+=bh->b_size >> 10)
+    {
+      bh=req->bh;
+      if (!buffer_locked(bh))
+       printk("md%d: block %ld not locked\n", minor, bh->b_blocknr);
+
+      bh->b_rdev=tmp_dev->dev;
+#if defined (CONFIG_MD_SUPPORT_RAID1)
+      bh->b_reqshared=NULL;
+      bh->b_sister_req=NULL;
+#endif
+      
+      if (!pending[queue].bh)
+      {
+       pending[queue].rq_dev=tmp_dev->dev;
+       pending[queue].bhtail=pending[queue].bh=bh;
+       pending[queue].sector=rblock << 1;
+       pending[queue].cmd=req->cmd;
+       pending[queue].current_nr_sectors=
+         pending[queue].nr_sectors=bh->b_size >> 9;
+      }
+      else
+      {
+       pending[queue].bhtail->b_reqnext=bh;
+       pending[queue].bhtail=bh;
+       pending[queue].nr_sectors+=bh->b_size >> 9;
+      }
+
+      end_redirect (req);      /* Separate bh from the request */
+    }
+  }
+  
+  req->rq_status=RQ_INACTIVE;
+  wake_up (&wait_for_request);
+  make_md_request (pending, mddev->nb_dev);
+  return REDIRECTED_REQ;       /* Since we already set the request free */
+}
+
+
+static int raid0_status (char *page, int minor, struct md_dev *mddev)
+{
+  int sz=0;
+#undef MD_DEBUG
+#ifdef MD_DEBUG
+  int j, k;
+  struct raid0_data *data=(struct raid0_data *) mddev->private;
+  
+  sz+=sprintf (page+sz, "      ");
+  for (j=0; j<data->nr_zones; j++)
+  {
+    sz+=sprintf (page+sz, "[z%d",
+                data->hash_table[j].zone0-data->strip_zone);
+    if (data->hash_table[j].zone1)
+      sz+=sprintf (page+sz, "/z%d] ",
+                  data->hash_table[j].zone1-data->strip_zone);
+    else
+      sz+=sprintf (page+sz, "] ");
+  }
+  
+  sz+=sprintf (page+sz, "\n");
+  
+  for (j=0; j<data->nr_strip_zones; j++)
+  {
+    sz+=sprintf (page+sz, "      z%d=[", j);
+    for (k=0; k<data->strip_zone[j].nb_dev; k++)
+      sz+=sprintf (page+sz, "%s/",
+                  partition_name(data->strip_zone[j].dev[k]->dev));
+    sz--;
+    sz+=sprintf (page+sz, "] zo=%d do=%d s=%d\n",
+                data->strip_zone[j].zone_offset,
+                data->strip_zone[j].dev_offset,
+                data->strip_zone[j].size);
+  }
+#endif
+  return sz;
+}
+
+
+static struct md_personality raid0_personality=
+{
+  "raid0",
+  raid0_map,
+  raid0_run,
+  raid0_stop,
+  raid0_status,
+  NULL,                                /* no ioctls */
+  0
+};
+
+
+#ifndef MODULE
+
+void raid0_init (void)
+{
+  register_md_personality (RAID0, &raid0_personality);
+}
+
+#else
+
+int init_module (void)
+{
+  return (register_md_personality (RAID0, &raid0_personality));
+}
+
+void cleanup_module (void)
+{
+  if (MOD_IN_USE)
+    printk ("md raid0 : module still busy...\n");
+  else
+    unregister_md_personality (RAID0);
+}
+
+#endif
index 3dbba423065032ddf6311ccd4292707c95b27b17..6035b67f7674298cbd0734257d558d18843f46fb 100644 (file)
@@ -1,5 +1,5 @@
 /*     linux/drivers/cdrom/optcd.c - Optics Storage 8000 AT CDROM driver
-       $Id: optcd.c,v 1.20 1996/01/17 19:44:39 root Exp root $
+       $Id: optcd.c,v 1.29 1996/02/22 22:38:30 root Exp $
 
        Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl)
 
@@ -536,13 +536,15 @@ static void bin2bcd(struct cdrom_msf *msf)
 
 
 /* Linear block address to minute, second, frame form */
+#define CD_FPM (CD_SECS * CD_FRAMES)   /* frames per minute */
+
 static void lba2msf(int lba, struct cdrom_msf *msf)
 {
        DEBUG((DEBUG_CONV, "lba2msf %d", lba));
        lba += CD_MSF_OFFSET;
-       msf->cdmsf_min0 = lba / 4500; lba %= 4500;
-       msf->cdmsf_sec0 = lba / 75;
-       msf->cdmsf_frame0 = lba % 75;
+       msf->cdmsf_min0 = lba / CD_FPM; lba %= CD_FPM;
+       msf->cdmsf_sec0 = lba / CD_FRAMES;
+       msf->cdmsf_frame0 = lba % CD_FRAMES;
        msf->cdmsf_min1 = 0;
        msf->cdmsf_sec1 = 0;
        msf->cdmsf_frame1 = 0;
@@ -558,27 +560,17 @@ inline static u_char bcd2bin(u_char bcd)
 }
 
 
-union cd_addr {
-       struct {
-               u_char  minute;
-               u_char  second;
-               u_char  frame;
-       } msf;
-       int     lba;
-};
-
-
-static void msf2lba(union cd_addr *addr)
+static void msf2lba(union cdrom_addr *addr)
 {
-       addr->lba = addr->msf.minute * 4500
-                   + addr->msf.second * 75
+       addr->lba = addr->msf.minute * CD_FPM
+                   + addr->msf.second * CD_FRAMES
                    + addr->msf.frame - CD_MSF_OFFSET;
 }
 
 
 /* Minute, second, frame address BCD to binary or to linear address,
    depending on MODE */
-static void msf_bcd2bin(union cd_addr *addr)
+static void msf_bcd2bin(union cdrom_addr *addr)
 {
        addr->msf.minute = bcd2bin(addr->msf.minute);
        addr->msf.second = bcd2bin(addr->msf.second);
@@ -590,6 +582,7 @@ static void msf_bcd2bin(union cd_addr *addr)
 
 static int audio_status = CDROM_AUDIO_NO_STATUS;
 static char toc_uptodate = 0;
+static char disk_changed = 1;
 
 /* Get drive status, flagging completion of audio play and disk changes. */
 static int drive_status(void)
@@ -610,6 +603,7 @@ static int drive_status(void)
 
        if (status & ST_DSK_CHG) {
                toc_uptodate = 0;
+               disk_changed = 1;
                audio_status = CDROM_AUDIO_NO_STATUS;
        }
 
@@ -687,11 +681,11 @@ static int get_q_channel(struct cdrom_subchnl *qp)
        DEBUG((DEBUG_TOC, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
                d1, d2, d3, d4, d5, d6, d7, d8, d9, d10));
 
-       msf_bcd2bin((union cd_addr *)/*%%*/&qp->cdsc_absaddr);
-       msf_bcd2bin((union cd_addr *)/*%%*/&qp->cdsc_reladdr);
+       msf_bcd2bin(&qp->cdsc_absaddr);
+       msf_bcd2bin(&qp->cdsc_reladdr);
        if (qp->cdsc_format == CDROM_LBA) {
-               msf2lba((union cd_addr *)/*%%*/&qp->cdsc_absaddr);
-               msf2lba((union cd_addr *)/*%%*/&qp->cdsc_reladdr);
+               msf2lba(&qp->cdsc_absaddr);
+               msf2lba(&qp->cdsc_reladdr);
        }
 
        return 0;
@@ -705,24 +699,18 @@ static int get_q_channel(struct cdrom_subchnl *qp)
 #define ERR_TOC_MISSINGENTRY   0x121
 
 
-struct msf {
-       u_char  minute;
-       u_char  second;
-       u_char  frame;
-};
-
 struct cdrom_disk_info {
-       unsigned char   first;
-       unsigned char   last;
-       struct msf      disk_length;
-       struct msf      first_track;
+       unsigned char           first;
+       unsigned char           last;
+       struct cdrom_msf0       disk_length;
+       struct cdrom_msf0       first_track;
        /* Multisession info: */
-       unsigned char   next;
-       struct msf      next_session;
-       struct msf      last_session;
-       unsigned char   multi;
-       unsigned char   xa;
-       unsigned char   audio;
+       unsigned char           next;
+       struct cdrom_msf0       next_session;
+       struct cdrom_msf0       last_session;
+       unsigned char           multi;
+       unsigned char           xa;
+       unsigned char           audio;
 };
 static struct cdrom_disk_info disk_info;
 
@@ -980,15 +968,10 @@ static int update_toc(void)
         && CURRENT -> cmd == READ && CURRENT -> sector != -1)
 
 
-#define BLOCKSIZE      2048
-#define BLOCKSIZE_RAW  2336
-#define BLOCKSIZE_ALL  2646
-#define N_BUFS         16
+/* Buffers for block size conversion. */
 #define NOBUF          -1
 
-
-/* Buffer for block size conversion. */
-static char buf[BLOCKSIZE * N_BUFS];
+static char buf[CD_FRAMESIZE * N_BUFS];
 static volatile int buf_bn[N_BUFS], next_bn;
 static volatile int buf_in = 0, buf_out = NOBUF;
 
@@ -1066,13 +1049,28 @@ static volatile long state_n = 0;
 #endif
 
 
+/* Used as mutex to keep do_optcd_request (and other processes calling
+   ioctl) out while some process is inside a VFS call.
+   Reverse is accomplished by checking if state = S_IDLE upon entry
+   of opt_ioctl and opt_media_change. */
+static int in_vfs = 0;
+
+
 static volatile int transfer_is_active = 0;
 static volatile int error = 0; /* %% do something with this?? */
 static int tries;              /* ibid?? */
+static int timeout = 0;
+
+static struct timer_list req_timer = {NULL, NULL, 0, 0, NULL};
+
+#define SET_REQ_TIMER(func, jifs) \
+       req_timer.expires = jiffies+(jifs); \
+       req_timer.function = (void *) (func); \
+       add_timer(&req_timer);
+#define CLEAR_REQ_TIMER        del_timer(&req_timer)
 
 static void poll(void)
 {
-       static int timeout;
        static volatile int read_count = 1;
        int flags;
        int loop_again = 1;
@@ -1117,8 +1115,14 @@ static void poll(void)
                case S_IDLE:
                        return;
                case S_START:
-                       if (send_cmd(COMDRVST))
+                       if (in_vfs)
+                               break;
+                       if (send_cmd(COMDRVST)) {
+                               state = S_IDLE;
+                               while (CURRENT_VALID)
+                                       end_request(0);
                                return;
+                       }
                        state = S_READ;
                        timeout = READ_TIMEOUT;
                        break;
@@ -1136,14 +1140,10 @@ static void poll(void)
                        skip = 0;
                        if ((status & ST_DOOR_OPEN) || (status & ST_DRVERR)) {
                                toc_uptodate = 0;
+                               opt_invalidate_buffers();
                                printk((status & ST_DOOR_OPEN)
                                       ? "optcd: door open\n"
                                       : "optcd: disk removed\n");
-                               if (transfer_is_active) {
-                                       state = S_START;
-                                       loop_again = 1;
-                                       break;
-                               }
                                state = S_IDLE;
                                while (CURRENT_VALID)
                                        end_request(0);
@@ -1237,8 +1237,8 @@ static void poll(void)
                                                break;
                                        }
                                        fetch_data(buf+
-                                           BLOCKSIZE*buf_in,
-                                           BLOCKSIZE);
+                                           CD_FRAMESIZE*buf_in,
+                                           CD_FRAMESIZE);
                                        read_count--;
 
                                        DEBUG((DEBUG_REQUEST,
@@ -1290,8 +1290,12 @@ static void poll(void)
                                printk("optcd: discard data=%x frames\n",
                                        read_count);
                        flush_data();
-                       if (send_cmd(COMDRVST))
+                       if (send_cmd(COMDRVST)) {
+                               state = S_IDLE;
+                               while (CURRENT_VALID)
+                                       end_request(0);
                                return;
+                       }
                        state = S_STOPPING;
                        timeout = STOP_TIMEOUT;
                        break;
@@ -1327,11 +1331,15 @@ static void poll(void)
        if (!timeout--) {
                printk("optcd: timeout in state %d\n", state);
                state = S_STOP;
-               if (exec_cmd(COMSTOP) < 0)
+               if (exec_cmd(COMSTOP) < 0) {
+                       state = S_IDLE;
+                       while (CURRENT_VALID)
+                               end_request(0);
                        return;
+               }
        }
 
-       SET_TIMER(poll, HZ/100);
+       SET_REQ_TIMER(poll, HZ/100);
 }
 
 
@@ -1366,9 +1374,10 @@ static void do_optcd_request(void)
                                }
                                /* Start state machine */
                                state = S_START;
+                               timeout = READ_TIMEOUT;
                                tries = 5;
                                /* %% why not start right away?? */
-                               SET_TIMER(poll, HZ/100);
+                               SET_REQ_TIMER(poll, HZ/100);
                        }
                        break;
                }
@@ -1536,7 +1545,7 @@ static int cdromreadtocentry(unsigned long arg)
        /* %% What should go into entry.cdte_datamode? */
 
        if (entry.cdte_format == CDROM_LBA)
-               msf2lba((union cd_addr *)/*%%*/&entry.cdte_addr);
+               msf2lba(&entry.cdte_addr);
        else if (entry.cdte_format != CDROM_MSF)
                return -EINVAL;
 
@@ -1604,7 +1613,7 @@ static int cdromread(unsigned long arg, int blocksize, int cmd)
 {
        int status;
        struct cdrom_msf msf;
-       char buf[BLOCKSIZE_ALL];
+       char buf[CD_FRAMESIZE_RAWER];
 
        status = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
        if (status)
@@ -1674,7 +1683,7 @@ static int cdrommultisession(unsigned long arg)
           && ms.addr_format != CDROM_MSF)
                return -EINVAL;
        if (ms.addr_format == CDROM_LBA)
-               msf2lba((union cd_addr *)/*%%*/&ms.addr);
+               msf2lba(&ms.addr);
 
        ms.xa_flag = disk_info.xa;
 
@@ -1710,6 +1719,7 @@ static int cdromreset(void)
        }
 
        toc_uptodate = 0;
+       disk_changed = 1;
        opt_invalidate_buffers();
        audio_status = CDROM_AUDIO_NO_STATUS;
 
@@ -1724,7 +1734,7 @@ static int cdromreset(void)
 static int opt_ioctl(struct inode *ip, struct file *fp,
                      unsigned int cmd, unsigned long arg)
 {
-       int err;
+       int status, err, retval = 0;
 
        DEBUG((DEBUG_VFS, "starting opt_ioctl"));
 
@@ -1734,86 +1744,118 @@ static int opt_ioctl(struct inode *ip, struct file *fp,
        if (cmd == CDROMRESET)
                return cdromreset();
 
-       if (state != S_IDLE)
+       /* is do_optcd_request or another ioctl busy? */
+       if (state != S_IDLE || in_vfs)
                return -EBUSY;
 
-       err = drive_status();
-       if (err < 0) {
-               DEBUG((DEBUG_VFS, "drive_status: %02x", -err));
+       in_vfs = 1;
+
+       status = drive_status();
+       if (status < 0) {
+               DEBUG((DEBUG_VFS, "drive_status: %02x", -status));
+               in_vfs = 0;
                return -EIO;
        }
+
+       if (status & ST_DOOR_OPEN)
+               switch (cmd) {  /* Actions that can be taken with door open */
+               case CDROMCLOSETRAY:
+                       /* We do this before trying to read the toc. */
+                       err = exec_cmd(COMCLOSE);
+                       if (err < 0) {
+                               DEBUG((DEBUG_VFS,
+                                      "exec_cmd COMCLOSE: %02x", -err));
+                               in_vfs = 0;
+                               return -EIO;
+                       }
+                       break;
+               default:        in_vfs = 0;
+                               return -EBUSY;
+               }
+
        err = update_toc();
        if (err < 0) {
                DEBUG((DEBUG_VFS, "update_toc: %02x", -err));
+               in_vfs = 0;
                return -EIO;
        }
 
        DEBUG((DEBUG_VFS, "ioctl cmd 0x%x", cmd));
 
        switch (cmd) {
-       case CDROMPAUSE:        return cdrompause();
-       case CDROMRESUME:       return cdromresume();
-       case CDROMPLAYMSF:      return cdromplaymsf(arg);
-       case CDROMPLAYTRKIND:   return cdromplaytrkind(arg);
-       case CDROMREADTOCHDR:   return cdromreadtochdr(arg);
-       case CDROMREADTOCENTRY: return cdromreadtocentry(arg);
+       case CDROMPAUSE:        retval = cdrompause(); break;
+       case CDROMRESUME:       retval = cdromresume(); break;
+       case CDROMPLAYMSF:      retval = cdromplaymsf(arg); break;
+       case CDROMPLAYTRKIND:   retval = cdromplaytrkind(arg); break;
+       case CDROMREADTOCHDR:   retval = cdromreadtochdr(arg); break;
+       case CDROMREADTOCENTRY: retval = cdromreadtocentry(arg); break;
 
        case CDROMSTOP:         err = exec_cmd(COMSTOP);
                                if (err < 0) {
                                        DEBUG((DEBUG_VFS,
                                                "exec_cmd COMSTOP: %02x",
                                                -err));
-                                       return -EIO;
-                               }
-                               audio_status = CDROM_AUDIO_NO_STATUS;
-                               break;
-       case CDROMSTART:        err = exec_cmd(COMCLOSE);  /* What else? */
-                               if (err < 0) {
-                                       DEBUG((DEBUG_VFS,
-                                               "exec_cmd COMCLOSE: %02x",
-                                               -err));
-                                       return -EIO;
-                               }
+                                       retval = -EIO;
+                               } else
+                                       audio_status = CDROM_AUDIO_NO_STATUS;
                                break;
+       case CDROMSTART:        break;  /* This is a no-op */
        case CDROMEJECT:        err = exec_cmd(COMUNLOCK);
                                if (err < 0) {
                                        DEBUG((DEBUG_VFS,
                                                "exec_cmd COMUNLOCK: %02x",
                                                -err));
-                                       return -EIO;
+                                       retval = -EIO;
+                                       break;
                                }
                                err = exec_cmd(COMOPEN);
                                if (err < 0) {
                                        DEBUG((DEBUG_VFS,
                                                "exec_cmd COMOPEN: %02x",
                                                -err));
-                                       return -EIO;
+                                       retval = -EIO;
                                }
                                break;
 
-       case CDROMVOLCTRL:      return cdromvolctrl(arg);
-       case CDROMSUBCHNL:      return cdromsubchnl(arg);
+       case CDROMVOLCTRL:      retval = cdromvolctrl(arg); break;
+       case CDROMSUBCHNL:      retval = cdromsubchnl(arg); break;
+
+       /* The drive detects the mode and automatically delivers the
+          correct 2048 bytes, so we don't need these IOCTLs */
+       case CDROMREADMODE2:    retval = -EINVAL; break;
+       case CDROMREADMODE1:    retval = -EINVAL; break;
+
+       /* Drive doesn't support reading audio */
+       case CDROMREADAUDIO:    retval = -EINVAL; break;
 
-       case CDROMREADAUDIO:    return -EINVAL; /* not implemented */
        case CDROMEJECT_SW:     auto_eject = (char) arg;
                                break;
 
 #ifdef MULTISESSION
-       case CDROMMULTISESSION: return cdrommultisession(arg);
+       case CDROMMULTISESSION: retval = cdrommultisession(arg); break;
 #endif
 
-       case CDROM_GET_UPC:     return -EINVAL; /* not implemented */
-       case CDROMVOLREAD:      return -EINVAL; /* not implemented */
+       case CDROM_GET_UPC:     retval = -EINVAL; break; /* not implemented */
+       case CDROMVOLREAD:      retval = -EINVAL; break; /* not implemented */
 
        case CDROMREADRAW:
-                       return cdromread(arg, BLOCKSIZE_RAW, COMREADRAW);
+                       /* this drive delivers 2340 bytes in raw mode */
+                       retval = cdromread(arg, CD_FRAMESIZE_RAW1, COMREADRAW);
+                       break;
        case CDROMREADCOOKED:
-                       return cdromread(arg, BLOCKSIZE, COMREAD);
-       case CDROMSEEK:         return cdromseek(arg);
-       case CDROMPLAYBLK:      return -EINVAL; /* not implemented */
-       default:                return -EINVAL;
+                       retval = cdromread(arg, CD_FRAMESIZE, COMREAD);
+                       break;
+       case CDROMREADALL:
+                       retval = cdromread(arg, CD_FRAMESIZE_RAWER, COMREADALL);
+                       break;
+
+       case CDROMSEEK:         retval = cdromseek(arg); break;
+       case CDROMPLAYBLK:      retval = -EINVAL; break; /* not implemented */
+       case CDROMCLOSETRAY:    break;  /* The action was taken earlier */
+       default:                retval = -EINVAL;
        }
-       return 0;
+       in_vfs = 0;
+       return retval;
 }
 
 
@@ -1824,31 +1866,24 @@ static int opt_open(struct inode *ip, struct file *fp)
 {
        DEBUG((DEBUG_VFS, "starting opt_open"));
 
-       if (!open_count++ && state == S_IDLE) {
+       if (!open_count && state == S_IDLE) {
                int status;
 
+               toc_uptodate = 0;
                opt_invalidate_buffers();
+
+               status = exec_cmd(COMCLOSE);    /* close door */
+               if (status < 0) {
+                       DEBUG((DEBUG_VFS, "exec_cmd COMCLOSE: %02x", -status));
+               }
+
                status = drive_status();
                if (status < 0) {
                        DEBUG((DEBUG_VFS, "drive_status: %02x", -status));
                        return -EIO;
                }
                DEBUG((DEBUG_VFS, "status: %02x", status));
-               if (status & ST_DOOR_OPEN) {
-                       status = exec_cmd(COMCLOSE);    /* close door */
-                       if (status < 0) {
-                               DEBUG((DEBUG_VFS,
-                                       "exec_cmd COMCLOSE: %02x", -status));
-                       }
-                       status = drive_status();        /* try again */
-                       if (status < 0) {
-                               DEBUG((DEBUG_VFS,
-                                       "drive_status: %02x", -status));
-                               return -EIO;
-                       }
-                       DEBUG((DEBUG_VFS, "status: %02x", status));
-               }
-               if (status & (ST_DOOR_OPEN|ST_DRVERR)) {
+               if ((status & ST_DOOR_OPEN) || (status & ST_DRVERR)) {
                        printk("optcd: no disk or door open\n");
                        return -EIO;
                }
@@ -1859,8 +1894,14 @@ static int opt_open(struct inode *ip, struct file *fp)
                status = update_toc();  /* Read table of contents */
                if (status < 0) {
                        DEBUG((DEBUG_VFS, "update_toc: %02x", -status));
+                       status = exec_cmd(COMUNLOCK);   /* Unlock door */
+                       if (status < 0) {
+                               DEBUG((DEBUG_VFS,
+                                      "exec_cmd COMUNLOCK: %02x", -status));
+                       }
                        return -EIO;
                }
+               open_count++;
        }
        MOD_INC_USE_COUNT;
 
@@ -1880,6 +1921,7 @@ static void opt_release(struct inode *ip, struct file *fp)
                ip, ip -> i_rdev, fp));
 
        if (!--open_count) {
+               toc_uptodate = 0;
                opt_invalidate_buffers();
                sync_dev(ip -> i_rdev);
                invalidate_buffers(ip -> i_rdev);
@@ -1892,9 +1934,24 @@ static void opt_release(struct inode *ip, struct file *fp)
                        DEBUG((DEBUG_VFS, "exec_cmd COMOPEN: %02x", -status));
                }
                CLEAR_TIMER;
+               CLEAR_REQ_TIMER;
        }
        MOD_DEC_USE_COUNT;
 }
+
+
+/* Check if disk has been changed */
+static int opt_media_change(kdev_t dev)
+{
+       DEBUG((DEBUG_VFS, "executing opt_media_change"));
+       DEBUG((DEBUG_VFS, "dev: 0x%x; disk_changed = %d\n", dev, disk_changed));
+
+       if (disk_changed) {
+               disk_changed = 0;
+               return 1;
+       }
+       return 0;
+}
 \f
 /* Driver initialisation */
 
@@ -1939,19 +1996,19 @@ static int version_ok(void)
 
 
 static struct file_operations opt_fops = {
-       NULL,           /* lseek - default */
-       block_read,     /* read - general block-dev read */
-       block_write,    /* write - general block-dev write */
-       NULL,           /* readdir - bad */
-       NULL,           /* select */
-       opt_ioctl,      /* ioctl */
-       NULL,           /* mmap */
-       opt_open,       /* open */
-       opt_release,    /* release */
-       NULL,           /* fsync */
-       NULL,           /* fasync */
-       NULL,           /* media change */
-       NULL            /* revalidate */
+       NULL,                   /* lseek - default */
+       block_read,             /* read - general block-dev read */
+       block_write,            /* write - general block-dev write */
+       NULL,                   /* readdir - bad */
+       NULL,                   /* select */
+       opt_ioctl,              /* ioctl */
+       NULL,                   /* mmap */
+       opt_open,               /* open */
+       opt_release,            /* release */
+       NULL,                   /* fsync */
+       NULL,                   /* fasync */
+       opt_media_change,       /* media change */
+       NULL                    /* revalidate */
 };
 
 
index b7d3751e2d19422e11f79e19bae1c9951e449ed7..0fc90f2a407010dd7152119a9e2b46a0afe5a280 100644 (file)
@@ -4,6 +4,7 @@
 mainmenu_option next_comment
 comment 'Character devices'
 
+bool 'Standard/generic serial support' CONFIG_SERIAL
 tristate 'Cyclades async mux support' CONFIG_CYCLADES
 bool 'Stallion multiport serial support' CONFIG_STALDRV
 if [ "$CONFIG_STALDRV" = "y" ]; then
index c6245a81c8b73d50b58d98e610f6a57a084cda8b..7d46db138dfa756d7a66031163dc134483a1b170 100644 (file)
@@ -16,10 +16,14 @@ FONTMAPFILE = cp437.uni
 
 L_TARGET := char.a
 M_OBJS   :=
-L_OBJS   := tty_io.o n_tty.o console.o keyboard.o serial.o \
+L_OBJS   := tty_io.o n_tty.o console.o keyboard.o \
        tty_ioctl.o pty.o vt.o mem.o vc_screen.o random.o \
        defkeymap.o consolemap.o selection.o
 
+ifeq ($(CONFIG_SERIAL),y)
+L_OBJS += serial.o
+endif
+
 ifeq ($(CONFIG_CYCLADES),y)
 L_OBJS += cyclades.o
 else
index 066ff76e7ed83a7d50609c43cac1c8f5e0d688bf..6dd57372a427564ab2a584e7a07c99cda7a083a3 100644 (file)
@@ -25,6 +25,9 @@
 #ifdef CONFIG_SOUND
 void soundcard_init(void);
 #endif
+#ifdef CONFIG_ISDN
+void isdn_init(void);
+#endif
 
 static int read_ram(struct inode * inode, struct file * file, char * buf, int count)
 {
@@ -393,6 +396,9 @@ int chr_dev_init(void)
 #endif
 #if CONFIG_QIC02_TAPE
        qic02_tape_init();
+#endif
+#if CONFIG_ISDN
+       isdn_init();
 #endif
        return 0;
 }
index 097623c7fec73a79d53ed315925023a662a527e5..67afdcad441db8ee09b40c479172c0ec84e177a6 100644 (file)
@@ -1823,7 +1823,9 @@ int tty_init(void)
        if (sizeof(struct tty_struct) > PAGE_SIZE)
                panic("size of tty structure > PAGE_SIZE!");
        kbd_init();
+#ifdef CONFIG_SERIAL
        rs_init();
+#endif
 #ifdef CONFIG_SCC
        scc_init();
 #endif
diff --git a/drivers/isdn/Config.in b/drivers/isdn/Config.in
new file mode 100644 (file)
index 0000000..3d35089
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# ISDN device configuration
+#
+if [ "$CONFIG_INET" != "n" ]; then
+  bool 'Support synchronous PPP' CONFIG_ISDN_PPP
+  if [ "$CONFIG_ISDN_PPP" != "n" ]; then
+    bool 'Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ
+    bool 'Support generic MP (RFC 1717)' CONFIG_ISDN_MPP
+  fi
+fi
+dep_tristate 'Teles/NICCY1016PC/Creatix support' CONFIG_ISDN_DRV_TELES $CONFIG_ISDN
+dep_tristate 'ICN B1 and B2 support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile
new file mode 100644 (file)
index 0000000..5d025fa
--- /dev/null
@@ -0,0 +1,54 @@
+SUB_DIRS     :=
+MOD_SUB_DIRS :=
+ALL_SUB_DIRS := icn teles
+
+L_OBJS :=
+LX_OBJS :=
+M_OBJS :=
+MX_OBJS :=
+O_OBJS :=
+OX_OBJS :=
+L_TARGET :=
+O_TARGET :=
+
+ifeq ($(CONFIG_ISDN),y)
+  L_TARGET := isdn.a
+  L_OBJS += isdn_net.o isdn_tty.o isdn_cards.o
+  LX_OBJS += isdn_common.o
+  ifdef CONFIG_ISDN_PPP
+    L_OBJS += isdn_ppp.o
+  endif
+else
+  ifeq ($(CONFIG_ISDN),m)
+    M_OBJS += isdn.o
+    O_TARGET += isdn.o
+    O_OBJS += isdn_net.o isdn_tty.o
+    OX_OBJS += isdn_common.o
+    ifdef CONFIG_ISDN_PPP
+      O_OBJS += isdn_ppp.o
+    endif
+  endif
+endif
+
+ifeq ($(CONFIG_ISDN_DRV_TELES),y)
+  L_OBJS += teles/teles.o
+  SUB_DIRS += teles
+  MOD_SUB_DIRS += teles
+else
+  ifeq ($(CONFIG_ISDN_DRV_TELES),m)
+    MOD_SUB_DIRS += teles
+  endif
+endif
+
+ifeq ($(CONFIG_ISDN_DRV_ICN),y)
+  L_OBJS += icn/icn.o
+  SUB_DIRS += icn
+  MOD_SUB_DIRS += icn
+else
+  ifeq ($(CONFIG_ISDN_DRV_ICN),m)
+    MOD_SUB_DIRS += icn
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/isdn/icn/Makefile b/drivers/isdn/icn/Makefile
new file mode 100644 (file)
index 0000000..2427bf2
--- /dev/null
@@ -0,0 +1,11 @@
+L_OBJS :=
+M_OBJS :=
+
+ifeq ($(CONFIG_ISDN_DRV_ICN),y)
+  L_OBJS += icn.o
+else
+  M_OBJS += icn.o
+endif
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c
new file mode 100644 (file)
index 0000000..0cac1c1
--- /dev/null
@@ -0,0 +1,1503 @@
+/* $Id icn.c,v 1.15 1996/01/10 20:57:39 fritz Exp fritz $
+ *
+ * ISDN low-level module for the ICN active ISDN-Card.
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ * $Log: icn.c,v $
+ * Revision 1.17  1996/02/11 02:39:04  fritz
+ * Increased Buffer for status-messages.
+ * Removed conditionals for HDLC-firmware.
+ *
+ * Revision 1.16  1996/01/22 05:01:55  fritz
+ * Revert to GPL.
+ *
+ * Revision 1.15  1996/01/10 20:57:39  fritz
+ * Bugfix: Loading firmware twice caused the device stop working.
+ *
+ * Revision 1.14  1995/12/18  18:23:37  fritz
+ * Support for ICN-2B Cards.
+ * Change for supporting user-setable service-octet.
+ *
+ * Revision 1.13  1995/10/29  21:41:07  fritz
+ * Added support for DriverId's, added Jan's patches for Kernelversions.
+ *
+ * Revision 1.12  1995/04/29  13:07:35  fritz
+ * Added support for new Euro-ISDN-firmware
+ *
+ * Revision 1.11  1995/04/23  13:40:45  fritz
+ * Added support for SPV's.
+ * Changed Dial-Command to support MSN's on DSS1-Lines.
+ *
+ * Revision 1.10  1995/03/25  23:23:24  fritz
+ * Changed configurable Ports, to allow settings for DIP-Switch Cardversions.
+ *
+ * Revision 1.9  1995/03/25  23:17:30  fritz
+ * Fixed race-condition in pollbchan_send
+ *
+ * Revision 1.8  1995/03/15  12:49:44  fritz
+ * Added support for SPV's
+ * Splitted pollbchan_work ifor calling send-routine directly
+ *
+ * Revision 1.7  1995/02/20  03:48:03  fritz
+ * Added support of new request_region-function.
+ * Minor bugfixes.
+ *
+ * Revision 1.6  1995/01/31  15:48:45  fritz
+ * Added Cause-Messages to be signaled to upper layers.
+ * Added Revision-Info on load.
+ *
+ * Revision 1.5  1995/01/29  23:34:59  fritz
+ * Added stopdriver() and appropriate calls.
+ * Changed printk-statements to support loglevels.
+ *
+ * Revision 1.4  1995/01/09  07:40:46  fritz
+ * Added GPL-Notice
+ *
+ * Revision 1.3  1995/01/04  05:15:18  fritz
+ * Added undocumented "bootload-finished"-command in download-code
+ * to satisfy some brain-damaged icn card-versions.
+ *
+ * Revision 1.2  1995/01/02  02:14:45  fritz
+ * Misc Bugfixes
+ *
+ * Revision 1.1  1994/12/14  17:56:06  fritz
+ * Initial revision
+ *
+ */
+
+#include "icn.h"
+
+
+
+/*
+ * Verbose bootcode- and protocol-downloading.
+ */
+#undef BOOT_DEBUG
+
+/*
+ * Verbose Shmem-Mapping.
+ */
+#undef MAP_DEBUG
+
+/* If defined, no bootcode- and protocol-downloading is supported and
+ * you must use an external loader
+ */
+#undef LOADEXTERN
+
+static char
+*revision = "$Revision: 1.17 $";
+
+static void icn_pollcard(unsigned long dummy);
+
+/* Try to allocate a new buffer, link it into queue. */
+static u_char *
+ icn_new_buf(pqueue ** queue, int length)
+{
+       pqueue *p;
+       pqueue *q;
+
+       if ((p = *queue)) {
+               while (p) {
+                       q = p;
+                       p = (pqueue *) p->next;
+               }
+               p = (pqueue *) kmalloc(sizeof(pqueue) + length, GFP_ATOMIC);
+               q->next = (u_char *) p;
+       } else
+               p = *queue = (pqueue *) kmalloc(sizeof(pqueue) + length, GFP_ATOMIC);
+       if (p) {
+               p->size = sizeof(pqueue) + length;
+               p->length = length;
+               p->next = NULL;
+               p->rptr = p->buffer;
+               return p->buffer;
+       } else {
+               return (u_char *) NULL;
+       }
+}
+
+#ifdef MODULE
+static void icn_free_queue(pqueue ** queue)
+{
+       pqueue *p;
+       pqueue *q;
+
+       p = *queue;
+       while (p) {
+               q = p;
+               p = (pqueue *) p->next;
+               kfree_s(q, q->size);
+       }
+       *queue = (pqueue *) 0;
+}
+#endif
+
+/* Put a value into a shift-register, highest bit first.
+ * Parameters:
+ *            port     = port for output (bit 0 is significant)
+ *            val      = value to be output
+ *            firstbit = Bit-Number of highest bit
+ *            bitcount = Number of bits to output
+ */
+static inline void icn_shiftout(unsigned short port,
+                    unsigned long val,
+                    int firstbit,
+                    int bitcount)
+{
+
+       register u_char s;
+       register u_char c;
+
+       for (s = firstbit, c = bitcount; c > 0; s--, c--)
+               OUTB_P((u_char) ((val >> s) & 1) ? 0xff : 0, port);
+}
+
+/*
+ * Map Cannel0 (Bank0/Bank8) or Channel1 (Bank4/Bank12)
+ */
+static inline void icn_map_channel(int channel)
+{
+       static u_char chan2bank[] =
+       {0, 4, 8, 12};
+
+#ifdef MAP_DEBUG
+       printk(KERN_DEBUG "icn_map_channel %d %d\n", dev->channel, channel);
+#endif
+       if (channel == dev->channel)
+               return;
+       OUTB_P(0, ICN_MAPRAM);  /* Disable RAM          */
+       icn_shiftout(ICN_BANK, chan2bank[channel], 3, 4);       /* Select Bank          */
+       OUTB_P(0xff, ICN_MAPRAM);       /* Enable RAM           */
+       dev->channel = channel;
+#ifdef MAP_DEBUG
+       printk(KERN_DEBUG "icn_map_channel done\n");
+#endif
+}
+
+static inline int icn_lock_channel(int channel)
+{
+       register int retval;
+       ulong flags;
+
+#ifdef MAP_DEBUG
+       printk(KERN_DEBUG "icn_lock_channel %d\n", channel);
+#endif
+       save_flags(flags);
+       cli();
+       if (dev->channel == channel) {
+               dev->chanlock++;
+               retval = 1;
+#ifdef MAP_DEBUG
+               printk(KERN_DEBUG "icn_lock_channel %d OK\n", channel);
+#endif
+       } else {
+               retval = 0;
+#ifdef MAP_DEBUG
+               printk(KERN_DEBUG "icn_lock_channel %d FAILED, dc=%d\n", channel, dev->channel);
+#endif
+       }
+       restore_flags(flags);
+       return retval;
+}
+
+static inline void icn_release_channel(void)
+{
+       ulong flags;
+
+#ifdef MAP_DEBUG
+       printk(KERN_DEBUG "icn_release_channel l=%d\n", dev->chanlock);
+#endif
+       save_flags(flags);
+       cli();
+       if (dev->chanlock)
+               dev->chanlock--;
+       restore_flags(flags);
+}
+
+static inline int icn_trymaplock_channel(int channel)
+{
+       ulong flags;
+
+       save_flags(flags);
+       cli();
+#ifdef MAP_DEBUG
+       printk(KERN_DEBUG "trymaplock c=%d dc=%d l=%d\n", channel, dev->channel,
+              dev->chanlock);
+#endif
+       if ((!dev->chanlock) || (dev->channel == channel)) {
+               dev->chanlock++;
+               icn_map_channel(channel);
+               restore_flags(flags);
+#ifdef MAP_DEBUG
+               printk(KERN_DEBUG "trymaplock %d OK\n", channel);
+#endif
+               return 1;
+       }
+       restore_flags(flags);
+#ifdef MAP_DEBUG
+       printk(KERN_DEBUG "trymaplock %d FAILED\n", channel);
+#endif
+       return 0;
+}
+
+static inline void icn_maprelease_channel(int channel)
+{
+       ulong flags;
+
+       save_flags(flags);
+       cli();
+#ifdef MAP_DEBUG
+       printk(KERN_DEBUG "map_release c=%d l=%d\n", channel, dev->chanlock);
+#endif
+       if (dev->chanlock)
+               dev->chanlock--;
+       if (!dev->chanlock)
+               icn_map_channel(channel);
+       restore_flags(flags);
+}
+
+/* Get Data from the B-Channel, assemble fragmented packets and put them
+ * into receive-queue. Wake up any B-Channel-reading processes.
+ * This routine is called via timer-callback from pollbchan().
+ * It schedules itself while any B-Channel is open.
+ */
+
+#ifdef DEBUG_RCVCALLBACK
+static int max_pending[2] =
+{0, 0};
+#endif
+
+static void icn_pollbchan_receive(int channel, icn_dev * dev)
+{
+       int mch = channel + ((dev->secondhalf) ? 2 : 0);
+       int eflag;
+       int cnt;
+       int flags;
+#ifdef DEBUG_RCVCALLBACK
+       int rcv_pending1;
+       int rcv_pending2;
+       int akt_pending;
+#endif
+
+       if (icn_trymaplock_channel(mch)) {
+               while (rbavl) {
+                       cnt = rbuf_l;
+                       if ((dev->rcvidx[channel] + cnt) > 4000) {
+                               printk(KERN_WARNING "icn: bogus packet on ch%d, dropping.\n",
+                                      channel + 1);
+                               dev->rcvidx[channel] = 0;
+                               eflag = 0;
+                       } else {
+                               memcpy(&dev->rcvbuf[channel][dev->rcvidx[channel]], rbuf_d, cnt);
+                               dev->rcvidx[channel] += cnt;
+                               eflag = rbuf_f;
+                       }
+                       rbnext;
+                       icn_maprelease_channel(mch & 2);
+                       if (!eflag) {
+                               save_flags(flags);
+                               cli();
+#ifdef DEBUG_RCVCALLBACK
+                               rcv_pending1 =
+                                   (dev->shmem->data_control.ecnr > dev->shmem->data_control.ecns) ?
+                                   0xf - dev->shmem->data_control.ecnr + dev->shmem->data_control.ecns :
+                                   dev->shmem->data_control.ecns - dev->shmem->data_control.ecnr;
+#endif
+                               dev->interface.rcvcallb(dev->myid, channel, dev->rcvbuf[channel],
+                                                  dev->rcvidx[channel]);
+                               dev->rcvidx[channel] = 0;
+#ifdef DEBUG_RCVCALLBACK
+                               rcv_pending2 =
+                                   (dev->shmem->data_control.ecnr > dev->shmem->data_control.ecns) ?
+                                   0xf - dev->shmem->data_control.ecnr + dev->shmem->data_control.ecns :
+                                   dev->shmem->data_control.ecns - dev->shmem->data_control.ecnr;
+                               akt_pending = rcv_pending2 - rcv_pending1;
+                               if (akt_pending > max_pending[channel]) {
+                                       max_pending[channel] = akt_pending;
+                                       printk(KERN_DEBUG "ICN_DEBUG: pend: %d %d\n", max_pending[0], max_pending[1]);
+                               }
+#endif
+                               restore_flags(flags);
+                       }
+                       if (!icn_trymaplock_channel(mch))
+                               break;
+               }
+               icn_maprelease_channel(mch & 2);
+       }
+}
+
+/* Send data-packet to B-Channel, split it up into fragments of
+ * ICN_FRAGSIZE length. If last fragment is sent out, signal
+ * success to upper layers via statcallb with ISDN_STAT_BSENT argument.
+ * This routine is called via timer-callback from pollbchan() or
+ * directly from sendbuf().
+ */
+
+static void icn_pollbchan_send(int channel, icn_dev * dev)
+{
+       int mch = channel + ((dev->secondhalf) ? 2 : 0);
+       int eflag = 0;
+       int cnt;
+       int left;
+       int flags;
+       pqueue *p;
+       isdn_ctrl cmd;
+
+       if (!dev->sndcount[channel])
+               return;
+       if (icn_trymaplock_channel(mch)) {
+               while (sbfree && dev->sndcount[channel]) {
+                       left = dev->spqueue[channel]->length;
+                       cnt =
+                           (sbuf_l =
+                            (left > ICN_FRAGSIZE) ? ((sbuf_f = 0xff), ICN_FRAGSIZE) : ((sbuf_f = 0), left));
+                       memcpy(sbuf_d, dev->spqueue[channel]->rptr, cnt);
+                       sbnext; /* switch to next buffer        */
+                       icn_maprelease_channel(mch & 2);
+                       dev->spqueue[channel]->rptr += cnt;
+                       eflag = ((dev->spqueue[channel]->length -= cnt) == 0);
+                       save_flags(flags);
+                       cli();
+                       p = dev->spqueue[channel];
+                       dev->sndcount[channel] -= cnt;
+                       if (eflag)
+                               dev->spqueue[channel] = (pqueue *) dev->spqueue[channel]->next;
+                       restore_flags(flags);
+                       if (eflag) {
+                               kfree_s(p, p->size);
+                               cmd.command = ISDN_STAT_BSENT;
+                               cmd.driver = dev->myid;
+                               cmd.arg = channel;
+                               dev->interface.statcallb(&cmd);
+                       }
+                       if (!icn_trymaplock_channel(mch))
+                               break;
+               }
+               icn_maprelease_channel(mch & 2);
+       }
+}
+
+/* Send/Receive Data to/from the B-Channel.
+ * This routine is called via timer-callback.
+ * It schedules itself while any B-Channel is open.
+ */
+
+static void icn_pollbchan(unsigned long dummy)
+{
+       unsigned long flags;
+
+       dev->flags |= ICN_FLAGS_RBTIMER;
+       if (dev->flags & ICN_FLAGS_B1ACTIVE) {
+               icn_pollbchan_receive(0, dev);
+               icn_pollbchan_send(0, dev);
+       }
+       if (dev->flags & ICN_FLAGS_B2ACTIVE) {
+               icn_pollbchan_receive(1, dev);
+               icn_pollbchan_send(1, dev);
+       }
+       if (dev->doubleS0) {
+               if (dev2->flags & ICN_FLAGS_B1ACTIVE) {
+                       icn_pollbchan_receive(0, dev2);
+                       icn_pollbchan_send(0, dev2);
+               }
+               if (dev2->flags & ICN_FLAGS_B2ACTIVE) {
+                       icn_pollbchan_receive(1, dev2);
+                       icn_pollbchan_send(1, dev2);
+               }
+       }
+       if (dev->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) {
+               /* schedule b-channel polling again */
+               save_flags(flags);
+               cli();
+               del_timer(&dev->rb_timer);
+               dev->rb_timer.function = icn_pollbchan;
+               dev->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+               add_timer(&dev->rb_timer);
+               restore_flags(flags);
+       } else
+               dev->flags &= ~ICN_FLAGS_RBTIMER;
+       if (dev->doubleS0) {
+               if (dev2->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) {
+                       /* schedule b-channel polling again */
+                       save_flags(flags);
+                       cli();
+                       del_timer(&dev2->rb_timer);
+                       dev2->rb_timer.function = icn_pollbchan;
+                       dev2->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+                       add_timer(&dev2->rb_timer);
+                       restore_flags(flags);
+               } else
+                       dev2->flags &= ~ICN_FLAGS_RBTIMER;
+       }
+}
+
+static void icn_pollit(icn_dev * dev)
+{
+       int mch = dev->secondhalf ? 2 : 0;
+       int avail = 0;
+       int dflag = 0;
+       int left;
+       u_char c;
+       int ch;
+       int flags;
+       int i;
+       u_char *p;
+       isdn_ctrl cmd;
+
+       if (icn_trymaplock_channel(mch)) {
+               avail = msg_avail;
+               for (left = avail, i = msg_o; left > 0; i++, left--) {
+                       c = dev->shmem->comm_buffers.iopc_buf[i & 0xff];
+                       save_flags(flags);
+                       cli();
+                       *dev->msg_buf_write++ = (c == 0xff) ? '\n' : c;
+                       /* No checks for buffer overflow for raw-status-device */
+                       if (dev->msg_buf_write > dev->msg_buf_end)
+                               dev->msg_buf_write = dev->msg_buf;
+                       restore_flags(flags);
+                       if (c == 0xff) {
+                               dev->imsg[dev->iptr] = 0;
+                               dev->iptr = 0;
+                               if (dev->imsg[0] == '0' && dev->imsg[1] >= '0' &&
+                                   dev->imsg[1] <= '2' && dev->imsg[2] == ';') {
+                                       ch = dev->imsg[1] - '0';
+                                       p = &dev->imsg[3];
+                                       if (!strncmp(p, "BCON_", 5)) {
+                                               switch (ch) {
+                                               case 1:
+                                                       dev->flags |= ICN_FLAGS_B1ACTIVE;
+                                                       break;
+                                               case 2:
+                                                       dev->flags |= ICN_FLAGS_B2ACTIVE;
+                                                       break;
+                                               }
+                                               cmd.command = ISDN_STAT_BCONN;
+                                               cmd.driver = dev->myid;
+                                               cmd.arg = ch - 1;
+                                               dev->interface.statcallb(&cmd);
+                                               continue;
+                                       }
+                                       if (!strncmp(p, "TEI OK", 6)) {
+                                               cmd.command = ISDN_STAT_RUN;
+                                               cmd.driver = dev->myid;
+                                               cmd.arg = ch - 1;
+                                               dev->interface.statcallb(&cmd);
+                                               continue;
+                                       }
+                                       if (!strncmp(p, "BDIS_", 5)) {
+                                               switch (ch) {
+                                               case 1:
+                                                       dev->flags &= ~ICN_FLAGS_B1ACTIVE;
+                                                       dflag |= 1;
+                                                       break;
+                                               case 2:
+                                                       dev->flags &= ~ICN_FLAGS_B2ACTIVE;
+                                                       dflag |= 2;
+                                                       break;
+                                               }
+                                               cmd.command = ISDN_STAT_BHUP;
+                                               cmd.arg = ch - 1;
+                                               cmd.driver = dev->myid;
+                                               dev->interface.statcallb(&cmd);
+                                               continue;
+                                       }
+                                       if (!strncmp(p, "DCON_", 5)) {
+                                               cmd.command = ISDN_STAT_DCONN;
+                                               cmd.arg = ch - 1;
+                                               cmd.driver = dev->myid;
+                                               dev->interface.statcallb(&cmd);
+                                               continue;
+                                       }
+                                       if (!strncmp(p, "DDIS_", 5)) {
+                                               cmd.command = ISDN_STAT_DHUP;
+                                               cmd.arg = ch - 1;
+                                               cmd.driver = dev->myid;
+                                               dev->interface.statcallb(&cmd);
+                                               continue;
+                                       }
+                                       if (!strncmp(p, "E_L1: ACT FAIL", 14)) {
+                                               cmd.command = ISDN_STAT_BHUP;
+                                               cmd.arg = 0;
+                                               cmd.driver = dev->myid;
+                                               dev->interface.statcallb(&cmd);
+                                               cmd.command = ISDN_STAT_DHUP;
+                                               cmd.arg = 0;
+                                               cmd.driver = dev->myid;
+                                               dev->interface.statcallb(&cmd);
+                                               continue;
+                                       }
+                                       if (!strncmp(p, "CIF", 3)) {
+                                               cmd.command = ISDN_STAT_CINF;
+                                               cmd.arg = ch - 1;
+                                               strncpy(cmd.num, p + 3, sizeof(cmd.num) - 1);
+                                               cmd.driver = dev->myid;
+                                               dev->interface.statcallb(&cmd);
+                                               continue;
+                                       }
+                                       if (!strncmp(p, "CAU", 3)) {
+                                               cmd.command = ISDN_STAT_CAUSE;
+                                               cmd.arg = ch - 1;
+                                               strncpy(cmd.num, p + 3, sizeof(cmd.num) - 1);
+                                               cmd.driver = dev->myid;
+                                               dev->interface.statcallb(&cmd);
+                                               continue;
+                                       }
+                                       if (!strncmp(p, "DCAL_I", 6)) {
+                                               cmd.command = ISDN_STAT_ICALL;
+                                               cmd.driver = dev->myid;
+                                               cmd.arg = ch - 1;
+                                               strncpy(cmd.num, p + 6, sizeof(cmd.num) - 1);
+                                               dev->interface.statcallb(&cmd);
+                                               continue;
+                                       }
+                                       if (!strncmp(p, "FCALL", 5)) {
+                                               cmd.command = ISDN_STAT_ICALL;
+                                               cmd.driver = dev->myid;
+                                               cmd.arg = ch - 1;
+                                               strcpy(cmd.num, "LEASED,07,00,1");
+                                               dev->interface.statcallb(&cmd);
+                                               continue;
+                                       }
+                                       if (!strncmp(p, "DSCA_I", 6)) {
+                                               cmd.command = ISDN_STAT_ICALL;
+                                               cmd.driver = dev->myid;
+                                               cmd.arg = ch - 1;
+                                               strncpy(cmd.num, p + 6, sizeof(cmd.num) - 1);
+                                               dev->interface.statcallb(&cmd);
+                                               continue;
+                                       }
+                                       if (!strncmp(p, "NO D-CHAN", 9)) {
+                                               cmd.command = ISDN_STAT_NODCH;
+                                               cmd.driver = dev->myid;
+                                               cmd.arg = ch - 1;
+                                               strncpy(cmd.num, p + 6, sizeof(cmd.num) - 1);
+                                               dev->interface.statcallb(&cmd);
+                                               continue;
+                                       }
+                               } else {
+                                       p = dev->imsg;
+                                       if (!strncmp(p, "DRV1.", 5)) {
+                                                printk(KERN_INFO "icn: %s\n",p);
+                                               if (!strncmp(p + 7, "TC", 2)) {
+                                                       dev->ptype = ISDN_PTYPE_1TR6;
+                                                       dev->interface.features |= ISDN_FEATURE_P_1TR6;
+                                                       printk(KERN_INFO "icn: 1TR6-Protocol loaded and running\n");
+                                               }
+                                               if (!strncmp(p + 7, "EC", 2)) {
+                                                       dev->ptype = ISDN_PTYPE_EURO;
+                                                       dev->interface.features |= ISDN_FEATURE_P_EURO;
+                                                       printk(KERN_INFO "icn: Euro-Protocol loaded and running\n");
+                                               }
+                                               continue;
+                                       }
+                               }
+                       } else {
+                               dev->imsg[dev->iptr] = c;
+                               if (dev->iptr < 59)
+                                       dev->iptr++;
+                       }
+               }
+               msg_o = (msg_o + avail) & 0xff;
+               icn_release_channel();
+       }
+       if (avail) {
+               cmd.command = ISDN_STAT_STAVAIL;
+               cmd.driver = dev->myid;
+               cmd.arg = avail;
+               dev->interface.statcallb(&cmd);
+       }
+       if (dflag & 1)
+               dev->interface.rcvcallb(dev->myid, 0, dev->rcvbuf[0], 0);
+       if (dflag & 2)
+               dev->interface.rcvcallb(dev->myid, 1, dev->rcvbuf[1], 0);
+       if (dev->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE))
+               if (!(dev->flags & ICN_FLAGS_RBTIMER)) {
+                       /* schedule b-channel polling */
+                       dev->flags |= ICN_FLAGS_RBTIMER;
+                       save_flags(flags);
+                       cli();
+                       del_timer(&dev->rb_timer);
+                       dev->rb_timer.function = icn_pollbchan;
+                       dev->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+                       add_timer(&dev->rb_timer);
+                       restore_flags(flags);
+               }
+}
+
+/*
+ * Check Statusqueue-Pointer from isdn-card.
+ * If there are new status-replies from the interface, check
+ * them against B-Channel-connects/disconnects and set flags arrcordingly.
+ * Wake-Up any processes, who are reading the status-device.
+ * If there are B-Channels open, initiate a timer-callback to
+ * icn_pollbchan().
+ * This routine is called periodically via timer.
+ */
+
+static void icn_pollcard(unsigned long dummy)
+{
+       ulong flags;
+
+       icn_pollit(dev);
+       if (dev->doubleS0)
+               icn_pollit(dev2);
+       /* schedule again */
+       save_flags(flags);
+       cli();
+       del_timer(&dev->st_timer);
+       dev->st_timer.function = icn_pollcard;
+       dev->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+       add_timer(&dev->st_timer);
+       restore_flags(flags);
+}
+
+/* Send a packet to the transmit-buffers, handle fragmentation if necessary.
+ * Parameters:
+ *            channel = Number of B-channel
+ *            buffer  = pointer to packet
+ *            len     = size of packet (max 4000)
+ *            dev     = pointer to device-struct
+ *            user    = 1 = call from userproc, 0 = call from kernel
+ * Return:
+ *        Number of bytes transferred, -E??? on error
+ */
+
+static int icn_sendbuf(int channel, const u_char * buffer, int len, int user, icn_dev * dev)
+{
+       register u_char *p;
+       int flags;
+
+       if (len > 4000)
+               return -EINVAL;
+       if (len) {
+               if (dev->sndcount[channel] > ICN_MAX_SQUEUE)
+                       return 0;
+               save_flags(flags);
+               cli();
+               p = icn_new_buf(&dev->spqueue[channel], len);
+               if (!p) {
+                       restore_flags(flags);
+                       return 0;
+               }
+               if (user) {
+                       memcpy_fromfs(p, buffer, len);
+               } else {
+                       memcpy(p, buffer, len);
+               }
+               dev->sndcount[channel] += len;
+               icn_pollbchan_send(channel, dev);
+               restore_flags(flags);
+       }
+       return len;
+}
+
+#ifndef LOADEXTERN
+static int icn_check_loader(int cardnumber)
+{
+       int timer = 0;
+
+       while (1) {
+#ifdef BOOT_DEBUG
+               printk(KERN_DEBUG "Loader %d ?\n", cardnumber);
+#endif
+               if (dev->shmem->data_control.scns ||
+                   dev->shmem->data_control.scnr) {
+                       if (timer++ > 5) {
+                               printk(KERN_WARNING "icn: Boot-Loader %d timed out.\n", cardnumber);
+                               icn_release_channel();
+                               return -EIO;
+                       }
+#ifdef BOOT_DEBUG
+                       printk(KERN_DEBUG "Loader %d TO?\n", cardnumber);
+#endif
+                       current->state = TASK_INTERRUPTIBLE;
+                       current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+                       schedule();
+               } else {
+#ifdef BOOT_DEBUG
+                       printk(KERN_DEBUG "Loader %d OK\n", cardnumber);
+#endif
+                       icn_release_channel();
+                       return 0;
+               }
+       }
+}
+
+/* Load the boot-code into the interface-card's memory and start it.
+ * Always called from user-process.
+ * 
+ * Parameters:
+ *            buffer = pointer to packet
+ * Return:
+ *        0 if successfully loaded
+ */
+
+#ifdef BOOT_DEBUG
+#define SLEEP(sec) { \
+int slsec = sec; \
+  printk(KERN_DEBUG "SLEEP(%d)\n",slsec); \
+  while (slsec) { \
+    current->state = TASK_INTERRUPTIBLE; \
+    current->timeout = jiffies + HZ; \
+    schedule(); \
+    slsec--; \
+  } \
+}
+#else
+#define SLEEP(sec)
+#endif
+
+static int icn_loadboot(u_char * buffer, icn_dev * dev)
+{
+       int ret;
+       ulong flags;
+
+#ifdef BOOT_DEBUG
+       printk(KERN_DEBUG "icn_loadboot called, buffaddr=%08lx\n", (ulong) buffer);
+#endif
+       if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE1)))
+               return ret;
+       save_flags(flags);
+       cli();
+       if (!dev->rvalid) {
+               if (check_region(dev->port, ICN_PORTLEN)) {
+                       printk(KERN_WARNING "icn: ports 0x%03x-0x%03x in use.\n", dev->port,
+                              dev->port + ICN_PORTLEN);
+                       restore_flags(flags);
+                       return -EBUSY;
+               }
+               request_region(dev->port, ICN_PORTLEN, regname);
+               dev->rvalid = 1;
+       }
+       if (!dev->mvalid) {
+               if (check_shmem((ulong) dev->shmem, 0x4000)) {
+                       printk(KERN_WARNING "icn: memory at 0x%08lx in use.\n", (ulong) dev->shmem);
+                       restore_flags(flags);
+                       return -EBUSY;
+               }
+               request_shmem((ulong) dev->shmem, 0x4000, regname);
+               dev->mvalid = 1;
+       }
+       restore_flags(flags);
+       OUTB_P(0, ICN_RUN);     /* Reset Controler */
+       OUTB_P(0, ICN_MAPRAM);  /* Disable RAM     */
+       icn_shiftout(ICN_CFG, 0x0f, 3, 4);      /* Windowsize= 16k */
+       icn_shiftout(ICN_CFG, (unsigned long) dev->shmem, 23, 10);      /* Set RAM-Addr.   */
+#ifdef BOOT_DEBUG
+       printk(KERN_DEBUG "shmem=%08lx\n", (ulong) dev->shmem);
+#endif
+       SLEEP(1);
+       save_flags(flags);
+       cli();
+       dev->channel = 1;       /* Force Mapping   */
+#ifdef BOOT_DEBUG
+       printk(KERN_DEBUG "Map Bank 0\n");
+#endif
+       icn_map_channel(0);             /* Select Bank 0   */
+       icn_lock_channel(0);    /* Lock Bank 0     */
+       restore_flags(flags);
+       SLEEP(1);
+       memcpy_fromfs(dev->shmem, buffer, ICN_CODE_STAGE1);     /* Copy code       */
+#ifdef BOOT_DEBUG
+       printk(KERN_DEBUG "Bootloader transfered\n");
+#endif
+       if (dev->doubleS0) {
+               SLEEP(1);
+               save_flags(flags);
+               cli();
+               icn_release_channel();
+#ifdef BOOT_DEBUG
+               printk(KERN_DEBUG "Map Bank 8\n");
+#endif
+               icn_map_channel(2);     /* Select Bank 8   */
+               icn_lock_channel(2);    /* Lock Bank 8     */
+               restore_flags(flags);
+               SLEEP(1);
+               memcpy_fromfs(dev->shmem, buffer, ICN_CODE_STAGE1);     /* Copy code       */
+#ifdef BOOT_DEBUG
+               printk(KERN_DEBUG "Bootloader transfered\n");
+#endif
+       }
+       SLEEP(1);
+       OUTB_P(0xff, ICN_RUN);  /* Start Boot-Code */
+       if ((ret = icn_check_loader(dev->doubleS0 ? 2 : 1)))
+               return ret;
+       if (!dev->doubleS0)
+               return 0;
+       /* reached only, if we have a Double-S0-Card */
+       save_flags(flags);
+       cli();
+#ifdef BOOT_DEBUG
+       printk(KERN_DEBUG "Map Bank 0\n");
+#endif
+       icn_map_channel(0);             /* Select Bank 0   */
+       icn_lock_channel(0);    /* Lock Bank 0     */
+       restore_flags(flags);
+       SLEEP(1);
+       return (icn_check_loader(1));
+}
+
+static int icn_loadproto(u_char * buffer, icn_dev * ldev)
+{
+       register u_char *p = buffer;
+       uint left = ICN_CODE_STAGE2;
+       uint cnt;
+       int timer;
+       int ret;
+       unsigned long flags;
+
+#ifdef BOOT_DEBUG
+       printk(KERN_DEBUG "icn_loadproto called\n");
+#endif
+       if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE2)))
+               return ret;
+       timer = 0;
+       save_flags(flags);
+       cli();
+       if (ldev->secondhalf) {
+               icn_map_channel(2);
+               icn_lock_channel(2);
+       } else {
+               icn_map_channel(0);
+               icn_lock_channel(0);
+       }
+       restore_flags(flags);
+       while (left) {
+               if (sbfree) {   /* If there is a free buffer...  */
+                       cnt = MIN(256, left);
+                       memcpy_fromfs(&sbuf_l, p, cnt);         /* copy data                     */
+                       sbnext; /* switch to next buffer         */
+                       p += cnt;
+                       left -= cnt;
+                       timer = 0;
+               } else {
+#ifdef BOOT_DEBUG
+                       printk(KERN_DEBUG "boot 2 !sbfree\n");
+#endif
+                       if (timer++ > 5) {
+                               icn_maprelease_channel(0);
+                               return -EIO;
+                       }
+                       current->state = TASK_INTERRUPTIBLE;
+                       current->timeout = jiffies + 10;
+                       schedule();
+               }
+       }
+       sbuf_n = 0x20;
+       timer = 0;
+       while (1) {
+               if (cmd_o || cmd_i) {
+#ifdef BOOT_DEBUG
+                       printk(KERN_DEBUG "Proto?\n");
+#endif
+                       if (timer++ > 5) {
+                               printk(KERN_WARNING "icn: Protocol timed out.\n");
+#ifdef BOOT_DEBUG
+                               printk(KERN_DEBUG "Proto TO!\n");
+#endif
+                               icn_maprelease_channel(0);
+                               return -EIO;
+                       }
+#ifdef BOOT_DEBUG
+                       printk(KERN_DEBUG "Proto TO?\n");
+#endif
+                       current->state = TASK_INTERRUPTIBLE;
+                       current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+                       schedule();
+               } else {
+                       if ((ldev->secondhalf) || (!dev->doubleS0)) {
+                               save_flags(flags);
+                               cli();
+#ifdef BOOT_DEBUG
+                               printk(KERN_DEBUG "Proto loaded, install poll-timer %d\n",
+                                      ldev->secondhalf);
+#endif
+                               init_timer(&dev->st_timer);
+                               dev->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+                               dev->st_timer.function = icn_pollcard;
+                               add_timer(&dev->st_timer);
+                               restore_flags(flags);
+                       }
+                       icn_maprelease_channel(0);
+                       return 0;
+               }
+       }
+}
+#endif                         /* !LOADEXTERN */
+
+/* Read the Status-replies from the Interface */
+static int icn_readstatus(u_char * buf, int len, int user, icn_dev * dev)
+{
+       int count;
+       u_char *p;
+
+       for (p = buf, count = 0; count < len; p++, count++) {
+               if (user)
+                       put_fs_byte(*dev->msg_buf_read++, p);
+               else
+                       *p = *dev->msg_buf_read++;
+               if (dev->msg_buf_read > dev->msg_buf_end)
+                       dev->msg_buf_read = dev->msg_buf;
+       }
+       return count;
+}
+
+/* Put command-strings into the command-queue of the Interface */
+static int icn_writecmd(const u_char * buf, int len, int user, icn_dev * dev, int waitflg)
+{
+       int mch = dev->secondhalf ? 2 : 0;
+       int avail;
+       int pp;
+       int i;
+       int count;
+       int ocount;
+       unsigned long flags;
+       u_char *p;
+       isdn_ctrl cmd;
+       u_char msg[0x100];
+
+       while (1) {
+               if (icn_trymaplock_channel(mch)) {
+                       avail = cmd_free;
+                       count = MIN(avail, len);
+                       if (user)
+                               memcpy_fromfs(msg, buf, count);
+                       else
+                               memcpy(msg, buf, count);
+                       save_flags(flags);
+                       cli();
+                       ocount = 1;
+                       *dev->msg_buf_write++ = '>';
+                       if (dev->msg_buf_write > dev->msg_buf_end)
+                               dev->msg_buf_write = dev->msg_buf;
+                       for (p = msg, pp = cmd_i, i = count; i > 0; i--, p++, pp++) {
+                               dev->shmem->comm_buffers.pcio_buf[pp & 0xff] = (*p == '\n') ? 0xff : *p;
+                               *dev->msg_buf_write++ = *p;
+                               if ((*p == '\n') && (i > 1)) {
+                                       *dev->msg_buf_write++ = '>';
+                                       if (dev->msg_buf_write > dev->msg_buf_end)
+                                               dev->msg_buf_write = dev->msg_buf;
+                                       ocount++;
+                               }
+                               /* No checks for buffer overflow of raw-status-device */
+                               if (dev->msg_buf_write > dev->msg_buf_end)
+                                       dev->msg_buf_write = dev->msg_buf;
+                               ocount++;
+                       }
+                       restore_flags(flags);
+                       cmd.command = ISDN_STAT_STAVAIL;
+                       cmd.driver = dev->myid;
+                       cmd.arg = ocount;
+                       dev->interface.statcallb(&cmd);
+                       cmd_i = (cmd_i + count) & 0xff;
+                       icn_release_channel();
+                       waitflg = 0;
+               } else
+                       count = 0;
+               if (!waitflg)
+                       break;
+               current->timeout = jiffies + 10;
+               schedule();
+       }
+       return count;
+}
+
+static void icn_stopdriver(icn_dev * ldev)
+{
+       unsigned long flags;
+       isdn_ctrl cmd;
+
+       save_flags(flags);
+       cli();
+       del_timer(&dev->st_timer);
+       del_timer(&ldev->rb_timer);
+       cmd.command = ISDN_STAT_STOP;
+       cmd.driver = ldev->myid;
+       ldev->interface.statcallb(&cmd);
+       restore_flags(flags);
+}
+
+static int my_atoi(char *s)
+{
+       int i, n;
+
+       n = 0;
+       if (!s)
+               return -1;
+       for (i = 0; *s >= '0' && *s <= '9'; i++, s++)
+               n = 10 * n + (*s - '0');
+       return n;
+}
+
+static int icn_command(isdn_ctrl * c, icn_dev * ldev)
+{
+       ulong a;
+       ulong flags;
+       int i;
+       char cbuf[60];
+       isdn_ctrl cmd;
+
+       switch (c->command) {
+       case ISDN_CMD_IOCTL:
+               memcpy(&a, c->num, sizeof(ulong));
+               switch (c->arg) {
+               case ICN_IOCTL_SETMMIO:
+                       if ((unsigned long) dev->shmem != (a & 0x0ffc000)) {
+                               if (check_shmem((ulong) (a & 0x0ffc000), 0x4000)) {
+                                       printk(KERN_WARNING "icn: memory at 0x%08lx in use.\n",
+                                              (ulong) (a & 0x0ffc000));
+                                       return -EINVAL;
+                               }
+                               icn_stopdriver(dev);
+                               if (dev->doubleS0)
+                                       icn_stopdriver(dev2);
+                               save_flags(flags);
+                               cli();
+                               if (dev->mvalid)
+                                       release_shmem((ulong) dev->shmem, 0x4000);
+                               dev->mvalid = 0;
+                               dev->shmem = (icn_shmem *) (a & 0x0ffc000);
+                               if (dev->doubleS0)
+                                       dev2->shmem = (icn_shmem *) (a & 0x0ffc000);
+                               restore_flags(flags);
+                               printk(KERN_INFO "icn: mmio set to 0x%08lx\n",
+                                      (unsigned long) dev->shmem);
+                       }
+                       break;
+               case ICN_IOCTL_GETMMIO:
+                       return (int) dev->shmem;
+               case ICN_IOCTL_SETPORT:
+                       if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330
+                           || a == 0x340 || a == 0x350 || a == 0x360 ||
+                           a == 0x308 || a == 0x318 || a == 0x328 || a == 0x338
+                           || a == 0x348 || a == 0x358 || a == 0x368) {
+                               if (dev->port != (unsigned short) a) {
+                                       if (check_region((unsigned short) a, ICN_PORTLEN)) {
+                                               printk(KERN_WARNING "icn: ports 0x%03x-0x%03x in use.\n",
+                                                      (int) a, (int) a + ICN_PORTLEN);
+                                               return -EINVAL;
+                                       }
+                                       icn_stopdriver(dev);
+                                       if (dev->doubleS0)
+                                               icn_stopdriver(dev2);
+                                       save_flags(flags);
+                                       cli();
+                                       if (dev->rvalid)
+                                               release_region(dev->port, ICN_PORTLEN);
+                                       dev->port = (unsigned short) a;
+                                       dev->rvalid = 0;
+                                       if (dev->doubleS0) {
+                                               dev2->port = (unsigned short) a;
+                                               dev2->rvalid = 0;
+                                       }
+                                       restore_flags(flags);
+                                       printk(KERN_INFO "icn: port set to 0x%03x\n", dev->port);
+                               }
+                       } else
+                               return -EINVAL;
+                       break;
+               case ICN_IOCTL_GETPORT:
+                       return (int) dev->port;
+               case ICN_IOCTL_GETDOUBLE:
+                       return (int) dev->doubleS0;
+               case ICN_IOCTL_DEBUGVAR:
+                       return (ulong) ldev;
+#ifndef LOADEXTERN
+               case ICN_IOCTL_LOADBOOT:
+                       icn_stopdriver(dev);
+                       if (dev->doubleS0)
+                               icn_stopdriver(dev2);
+                       return (icn_loadboot((u_char *) a, dev));
+               case ICN_IOCTL_LOADPROTO:
+                       icn_stopdriver(dev);
+                       if (dev->doubleS0)
+                               icn_stopdriver(dev2);
+                       if ((i = (icn_loadproto((u_char *) a, dev))))
+                               return i;
+                       if (dev->doubleS0)
+                               i = icn_loadproto((u_char *) (a + ICN_CODE_STAGE2), dev2);
+                       return i;
+#endif
+               case ICN_IOCTL_LEASEDCFG:
+                       if (a) {
+                               if (!ldev->leased) {
+                                       ldev->leased = 1;
+                                       while (ldev->ptype == ISDN_PTYPE_UNKNOWN) {
+                                               current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+                                               schedule();
+                                       }
+                                       current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+                                       schedule();
+                                       sprintf(cbuf, "00;FV2ON\n01;EAZ1\n");
+                                       i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+                                       printk(KERN_INFO "icn: Leased-line mode enabled\n");
+                                       cmd.command = ISDN_STAT_RUN;
+                                       cmd.driver = ldev->myid;
+                                       cmd.arg = 0;
+                                       ldev->interface.statcallb(&cmd);
+                               }
+                       } else {
+                               if (ldev->leased) {
+                                       ldev->leased = 0;
+                                       sprintf(cbuf, "00;FV2OFF\n");
+                                       i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+                                       printk(KERN_INFO "icn: Leased-line mode disabled\n");
+                                       cmd.command = ISDN_STAT_RUN;
+                                       cmd.driver = ldev->myid;
+                                       cmd.arg = 0;
+                                       ldev->interface.statcallb(&cmd);
+                               }
+                       }
+                       return 0;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case ISDN_CMD_DIAL:
+               if (ldev->leased)
+                       break;
+               if ((c->arg & 255) < ICN_BCH) {
+                       char *p;
+                       char *p2;
+                       char dial[50];
+                       char sis[50];
+                       char dcode[4];
+                       int si1, si2;
+
+                       a = c->arg;
+                       strcpy(sis, c->num);
+                       p = strrchr(sis, ',');
+                       *p++ = '\0';
+                       si2 = my_atoi(p);
+                       p = strrchr(sis, ',') + 1;
+                       si1 = my_atoi(p);
+                       p = c->num;
+                       if (*p == 's' || *p == 'S') {
+                               /* Dial for SPV */
+                               p++;
+                               strcpy(dcode, "SCA");
+                       } else
+                               /* Normal Dial */
+                               strcpy(dcode, "CAL");
+                       strcpy(dial, p);
+                       p = strchr(dial, ',');
+                       *p++ = '\0';
+                       p2 = strchr(p, ',');
+                       *p2 = '\0';
+                       sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1), dcode, dial, si1,
+                               si2, p);
+                       i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+               }
+               break;
+       case ISDN_CMD_ACCEPTD:
+               if (c->arg < ICN_BCH) {
+                       a = c->arg + 1;
+                       sprintf(cbuf, "%02d;DCON_R\n", (int) a);
+                       i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+               }
+               break;
+       case ISDN_CMD_ACCEPTB:
+               if (c->arg < ICN_BCH) {
+                       a = c->arg + 1;
+                       sprintf(cbuf, "%02d;BCON_R\n", (int) a);
+                       i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+               }
+               break;
+       case ISDN_CMD_HANGUP:
+               if (c->arg < ICN_BCH) {
+                       a = c->arg + 1;
+                       sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a);
+                       i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+               }
+               break;
+       case ISDN_CMD_SETEAZ:
+               if (ldev->leased)
+                       break;
+               if (c->arg < ICN_BCH) {
+                       a = c->arg + 1;
+                       if (ldev->ptype == ISDN_PTYPE_EURO) {
+                               sprintf(cbuf, "%02d;MS%s%s\n", (int) a, c->num[0] ? "N" : "ALL", c->num);
+                       } else
+                               sprintf(cbuf, "%02d;EAZ%s\n", (int) a, c->num[0] ? c->num : "0123456789");
+                       i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+               }
+               break;
+       case ISDN_CMD_CLREAZ:
+               if (ldev->leased)
+                       break;
+               if (c->arg < ICN_BCH) {
+                       a = c->arg + 1;
+                       if (ldev->ptype == ISDN_PTYPE_EURO)
+                               sprintf(cbuf, "%02d;MSNC\n", (int) a);
+                       else
+                               sprintf(cbuf, "%02d;EAZC\n", (int) a);
+                       i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+               }
+               break;
+       case ISDN_CMD_SETL2:
+               if ((c->arg & 255) < ICN_BCH) {
+                       a = c->arg;
+                       switch (a >> 8) {
+                       case ISDN_PROTO_L2_X75I:
+                               sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1);
+                               break;
+                       case ISDN_PROTO_L2_HDLC:
+                               sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+                       i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+                       ldev->l2_proto[a & 255] = (a >> 8);
+               }
+               break;
+       case ISDN_CMD_GETL2:
+               if ((c->arg & 255) < ICN_BCH)
+                       return ldev->l2_proto[c->arg & 255];
+               else
+                       return -ENODEV;
+       case ISDN_CMD_SETL3:
+               return 0;
+       case ISDN_CMD_GETL3:
+               if ((c->arg & 255) < ICN_BCH)
+                       return ISDN_PROTO_L3_TRANS;
+               else
+                       return -ENODEV;
+       case ISDN_CMD_GETEAZ:
+               break;
+       case ISDN_CMD_SETSIL:
+               break;
+       case ISDN_CMD_GETSIL:
+               break;
+       case ISDN_CMD_LOCK:
+               MOD_INC_USE_COUNT;
+               break;
+       case ISDN_CMD_UNLOCK:
+               MOD_DEC_USE_COUNT;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*
+ * For second half of doubleS0-Card add channel-offset.
+ */
+static int if_command1(isdn_ctrl * c)
+{
+       return (icn_command(c, dev));
+}
+
+static int if_command2(isdn_ctrl * c)
+{
+       return (icn_command(c, dev2));
+}
+
+static int if_writecmd1(const u_char * buf, int len, int user)
+{
+       return (icn_writecmd(buf, len, user, dev, 0));
+}
+
+static int if_writecmd2(const u_char * buf, int len, int user)
+{
+       return (icn_writecmd(buf, len, user, dev2, 0));
+}
+
+static int if_readstatus1(u_char * buf, int len, int user)
+{
+       return (icn_readstatus(buf, len, user, dev));
+}
+
+static int if_readstatus2(u_char * buf, int len, int user)
+{
+       return (icn_readstatus(buf, len, user, dev2));
+}
+
+static int if_sendbuf1(int id, int channel, const u_char * buffer, int len,
+                      int user)
+{
+       return (icn_sendbuf(channel, buffer, len, user, dev));
+}
+
+static int if_sendbuf2(int id, int channel, const u_char * buffer, int len,
+                      int user)
+{
+       return (icn_sendbuf(channel, buffer, len, user, dev2));
+}
+
+#ifdef MODULE
+#define icn_init init_module
+#else
+void icn_setup(char *str, int *ints)
+{
+       char *p;
+       static char sid[20];
+       static char sid2[20];
+
+       if (ints[0])
+               portbase = ints[1];
+       if (ints[0]>1)
+               membase  = ints[2];
+       if (strlen(str)) {
+               strcpy(sid,str);
+               icn_id = sid;
+               if ((p = strchr(sid,','))) {
+                       *p++ = 0;
+                       strcpy(sid2,p);
+                       icn_id2 = sid2;
+               }
+       }
+}
+#endif
+
+int icn_init(void)
+{
+#ifdef LOADEXTERN
+       unsigned long flags;
+#endif
+       char *p;
+       isdn_ctrl cmd;
+       char rev[10];
+
+       if (!(dev = (icn_devptr) kmalloc(sizeof(icn_dev), GFP_KERNEL))) {
+               printk(KERN_WARNING "icn: Could not allocate device-struct.\n");
+               return -EIO;
+       }
+       memset((char *) dev, 0, sizeof(icn_dev));
+       dev->port = portbase;
+       dev->shmem = (icn_shmem *) (membase & 0x0ffc000);
+       if (strlen(icn_id2))
+               dev->doubleS0 = 1;
+       dev->interface.channels = ICN_BCH;
+       dev->interface.maxbufsize = 4000;
+       dev->interface.command = if_command1;
+       dev->interface.writebuf = if_sendbuf1;
+       dev->interface.writecmd = if_writecmd1;
+       dev->interface.readstat = if_readstatus1;
+       dev->interface.features = ISDN_FEATURE_L2_X75I |
+                ISDN_FEATURE_L2_HDLC |
+                ISDN_FEATURE_L3_TRANS |
+                ISDN_FEATURE_P_UNKNOWN;
+       dev->ptype = ISDN_PTYPE_UNKNOWN;
+       strncpy(dev->interface.id, icn_id, sizeof(dev->interface.id) - 1);
+       dev->msg_buf_write = dev->msg_buf;
+       dev->msg_buf_read = dev->msg_buf;
+       dev->msg_buf_end = &dev->msg_buf[sizeof(dev->msg_buf) - 1];
+       memset((char *) dev->l2_proto, ISDN_PROTO_L2_X75I, sizeof(dev->l2_proto));
+       if (strlen(icn_id2)) {
+               if (!(dev2 = (icn_devptr) kmalloc(sizeof(icn_dev), GFP_KERNEL))) {
+                       printk(KERN_WARNING "icn: Could not allocate device-struct.\n");
+                       kfree(dev);
+                       kfree(dev2);
+                       return -EIO;
+               }
+               memcpy((char *) dev2, (char *) dev, sizeof(icn_dev));
+               dev2->interface.command = if_command2;
+               dev2->interface.writebuf = if_sendbuf2;
+               dev2->interface.writecmd = if_writecmd2;
+               dev2->interface.readstat = if_readstatus2;
+               strncpy(dev2->interface.id, icn_id2,
+                        sizeof(dev->interface.id) - 1);
+               dev2->msg_buf_write = dev2->msg_buf;
+               dev2->msg_buf_read = dev2->msg_buf;
+               dev2->msg_buf_end = &dev2->msg_buf[sizeof(dev2->msg_buf) - 1];
+               dev2->secondhalf = 1;
+       }
+       if (!register_isdn(&dev->interface)) {
+               printk(KERN_WARNING "icn: Unable to register\n");
+               kfree(dev);
+               if (dev->doubleS0)
+                       kfree(dev2);
+               return -EIO;
+       }
+       dev->myid = dev->interface.channels;
+       sprintf(regname, "icn-isdn (%s)", dev->interface.id);
+       if (dev->doubleS0) {
+               if (!register_isdn(&dev2->interface)) {
+                       printk(KERN_WARNING "icn: Unable to register\n");
+                       kfree(dev2);
+                       if (dev->doubleS0) {
+                               icn_stopdriver(dev);
+                               cmd.command = ISDN_STAT_UNLOAD;
+                               cmd.driver = dev->myid;
+                               dev->interface.statcallb(&cmd);
+                               kfree(dev);
+                       }
+                       return -EIO;
+               }
+               dev2->myid = dev2->interface.channels;
+       }
+       
+       /* No symbols to export, hide all symbols */
+       register_symtab(NULL);
+
+       if ((p = strchr(revision, ':'))) {
+               strcpy(rev, p + 1);
+               p = strchr(rev, '$');
+               *p = 0;
+       } else
+               strcpy(rev, " ??? ");
+       printk(KERN_NOTICE "ICN-ISDN-driver Rev%sport=0x%03x mmio=0x%08x id='%s'\n",
+              rev, dev->port, (uint) dev->shmem, dev->interface.id);
+#ifdef LOADEXTERN
+       save_flags(flags);
+       cli();
+       init_timer(&dev->st_timer);
+       dev->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+       dev->st_timer.function = icn_pollcard;
+       add_timer(&dev->st_timer);
+       restore_flags(flags);
+#endif
+       return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+       isdn_ctrl cmd;
+       int i;
+
+       icn_stopdriver(dev);
+       cmd.command = ISDN_STAT_UNLOAD;
+       cmd.driver = dev->myid;
+       dev->interface.statcallb(&cmd);
+       if (dev->doubleS0) {
+               icn_stopdriver(dev2);
+               cmd.command = ISDN_STAT_UNLOAD;
+               cmd.driver = dev2->myid;
+               dev2->interface.statcallb(&cmd);
+       }
+       if (dev->rvalid) {
+               OUTB_P(0, ICN_RUN);     /* Reset Controler      */
+               OUTB_P(0, ICN_MAPRAM);  /* Disable RAM          */
+               release_region(dev->port, ICN_PORTLEN);
+       }
+       if (dev->mvalid)
+               release_shmem((ulong) dev->shmem, 0x4000);
+       if (dev->doubleS0) {
+               for (i = 0; i < ICN_BCH; i++)
+                       icn_free_queue(&dev2->spqueue[1]);
+               kfree(dev2);
+       }
+       for (i = 0; i < ICN_BCH; i++)
+               icn_free_queue(&dev->spqueue[1]);
+       kfree(dev);
+       printk(KERN_NOTICE "ICN-ISDN-driver unloaded\n");
+}
+#endif
diff --git a/drivers/isdn/icn/icn.h b/drivers/isdn/icn/icn.h
new file mode 100644 (file)
index 0000000..69eeacf
--- /dev/null
@@ -0,0 +1,294 @@
+/* $Id: icn.h,v 1.12 1996/01/22 05:01:22 fritz Exp fritz $
+ *
+ * ISDN lowlevel-module for the ICN active ISDN-Card.
+ *
+ * Copyright 1994 by Fritz Elfert (fritz@wuemaus.franken.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ * $Log: icn.h,v $
+ * Revision 1.12  1996/01/22 05:01:22  fritz
+ * Revert to GPL.
+ *
+ * Revision 1.11  1995/12/18  18:25:00  fritz
+ * Support for ICN-2B Cards.
+ * Change for supporting user-setable service-octet.
+ *
+ * Revision 1.10  1995/10/29  21:43:10  fritz
+ * Added support for leased lines.
+ *
+ * Revision 1.9  1995/04/23  13:42:10  fritz
+ * Added some constants for distingushing 1TR6 and DSS1
+ *
+ * Revision 1.8  1995/03/25  23:18:55  fritz
+ * Changed ICN_PORTLEN to reflect correct number of ports.
+ *
+ * Revision 1.7  1995/03/15  12:52:06  fritz
+ * Some cleanup
+ *
+ * Revision 1.6  1995/02/20  03:49:22  fritz
+ * Fixed ICN_MAX_SQUEUE to correctly reflect outstanding bytes, not number
+ * of buffers.
+ *
+ * Revision 1.5  1995/01/29  23:36:50  fritz
+ * Minor cleanup.
+ *
+ * Revision 1.4  1995/01/09  07:41:20  fritz
+ * Added GPL-Notice
+ *
+ * Revision 1.3  1995/01/04  05:14:20  fritz
+ * removed include of linux/asm/string.h for compiling with Linux 1.1.76
+ *
+ * Revision 1.2  1995/01/02  02:15:57  fritz
+ * Misc. Bugfixes
+ *
+ * Revision 1.1  1994/12/14  18:02:38  fritz
+ * Initial revision
+ *
+ */
+
+#ifndef icn_h
+#define icn_h
+
+#define ICN_IOCTL_SETMMIO   0
+#define ICN_IOCTL_GETMMIO   1
+#define ICN_IOCTL_SETPORT   2
+#define ICN_IOCTL_GETPORT   3
+#define ICN_IOCTL_LOADBOOT  4
+#define ICN_IOCTL_LOADPROTO 5
+#define ICN_IOCTL_LEASEDCFG 6
+#define ICN_IOCTL_GETDOUBLE 7
+#define ICN_IOCTL_DEBUGVAR  8
+
+#if defined(__KERNEL__) || defined(__DEBUGVAR__)
+
+#ifdef __KERNEL__
+/* Kernel includes */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+
+#endif                         /* __KERNEL__ */
+
+/* some useful macros for debugging */
+#ifdef ICN_DEBUG_PORT
+#define OUTB_P(v,p) {printk(KERN_DEBUG "icn: outb_p(0x%02x,0x%03x)\n",v,p); outb_p(v,p);}
+#else
+#define OUTB_P outb
+#endif
+
+/* Defaults for Port-Address and shared-memory */
+#define ICN_BASEADDR 0x320
+#define ICN_PORTLEN (0x04)
+#define ICN_MEMADDR 0x0d0000
+
+/* Macros for accessing ports */
+#define ICN_CFG    (dev->port)
+#define ICN_MAPRAM (dev->port+1)
+#define ICN_RUN    (dev->port+2)
+#define ICN_BANK   (dev->port+3)
+
+#define ICN_FLAGS_B1ACTIVE 1   /* B-Channel-1 is open                 */
+#define ICN_FLAGS_B2ACTIVE 2   /* B-Channel-2 is open                 */
+#define ICN_FLAGS_RBTIMER  8   /* cyclic scheduling of B-Channel-poll */
+
+#define ICN_BOOT_TIMEOUT1  100 /* Delay for Boot-download (jiffies)   */
+#define ICN_CHANLOCK_DELAY  10 /* Delay for Channel-mapping (jiffies) */
+
+#define ICN_TIMER_BCREAD 3     /* B-Channel poll-cycle                */
+#define ICN_TIMER_DCREAD 50    /* D-Channel poll-cycle                */
+
+#define ICN_CODE_STAGE1 4096   /* Size of bootcode                    */
+#define ICN_CODE_STAGE2 65536  /* Size of protocol-code               */
+
+#define ICN_MAX_SQUEUE 65536   /* Max. outstanding send-data          */
+#define ICN_FRAGSIZE (250)     /* Max. size of send-fragments         */
+#define ICN_BCH 2              /* Number of supported channels        */
+
+/* type-definitions for accessing the mmap-io-areas */
+
+#define SHM_DCTL_OFFSET (0)    /* Offset to data-controlstructures in shm */
+#define SHM_CCTL_OFFSET (0x1d2)        /* Offset to comm-controlstructures in shm */
+#define SHM_CBUF_OFFSET (0x200)        /* Offset to comm-buffers in shm           */
+#define SHM_DBUF_OFFSET (0x2000)       /* Offset to data-buffers in shm           */
+
+typedef struct {
+       unsigned char length;   /* Bytecount of fragment (max 250)     */
+       unsigned char endflag;  /* 0=last frag., 0xff=frag. continued  */
+       unsigned char data[ICN_FRAGSIZE];       /* The data                            */
+       /* Fill to 256 bytes */
+       char unused[0x100 - ICN_FRAGSIZE - 2];
+} frag_buf;
+
+typedef union {
+       struct {
+               unsigned char scns;     /* Index to free SendFrag.             */
+               unsigned char scnr;     /* Index to active SendFrag   READONLY */
+               unsigned char ecns;     /* Index to free RcvFrag.     READONLY */
+               unsigned char ecnr;     /* Index to valid RcvFrag              */
+               char unused[6];
+               unsigned short fuell1;  /* Internal Buf Bytecount              */
+       } data_control;
+       struct {
+               char unused[SHM_CCTL_OFFSET];
+               unsigned char iopc_i;   /* Read-Ptr Status-Queue      READONLY */
+               unsigned char iopc_o;   /* Write-Ptr Status-Queue              */
+               unsigned char pcio_i;   /* Write-Ptr Command-Queue             */
+               unsigned char pcio_o;   /* Read-Ptr Command Queue     READONLY */
+       } comm_control;
+       struct {
+               char unused[SHM_CBUF_OFFSET];
+               unsigned char pcio_buf[0x100];  /* Ring-Buffer Command-Queue           */
+               unsigned char iopc_buf[0x100];  /* Ring-Buffer Status-Queue            */
+       } comm_buffers;
+       struct {
+               char unused[SHM_DBUF_OFFSET];
+               frag_buf receive_buf[0x10];
+               frag_buf send_buf[0x10];
+       } data_buffers;
+} icn_shmem;
+
+/* Sendbuffer-queue-element */
+typedef struct {
+       char *next;
+       short length;
+       short size;
+       u_char *rptr;
+       u_char buffer[1];
+} pqueue;
+
+typedef struct {
+       unsigned short port;    /* Base-port-adress                 */
+       icn_shmem *shmem;       /* Pointer to memory-mapped-buffers */
+       int myid;               /* Driver-Nr. assigned by linklevel */
+       int rvalid;             /* IO-portregion has been requested */
+       int mvalid;             /* IO-shmem has been requested      */
+       int leased;             /* Flag: This Adapter is connected  */
+       /*       to a leased line           */
+       unsigned short flags;   /* Statusflags                      */
+       int doubleS0;           /* Flag: Double-S0-Card             */
+       int secondhalf;         /* Flag: Second half of a doubleS0  */
+       int ptype;              /* Protocoltype (1TR6 or Euro)      */
+       struct timer_list st_timer;     /* Timer for Status-Polls           */
+       struct timer_list rb_timer;     /* Timer for B-Channel-Polls        */
+       int channel;            /* Currently mapped Channel         */
+       int chanlock;           /* Semaphore for Channel-Mapping    */
+       u_char rcvbuf[ICN_BCH][4096];   /* B-Channel-Receive-Buffers      */
+       int rcvidx[ICN_BCH];    /* Index for above buffers          */
+       int l2_proto[ICN_BCH];  /* Current layer-2-protocol         */
+       isdn_if interface;      /* Interface to upper layer         */
+       int iptr;               /* Index to imsg-buffer             */
+       char imsg[60];          /* Internal buf for status-parsing  */
+       char msg_buf[2048];     /* Buffer for status-messages       */
+       char *msg_buf_write;    /* Writepointer for statusbuffer    */
+       char *msg_buf_read;     /* Readpointer for statusbuffer     */
+       char *msg_buf_end;      /* Pointer to end of statusbuffer   */
+       int sndcount[ICN_BCH];  /* Byte-counters for B-Ch.-send     */
+       pqueue *spqueue[ICN_BCH];       /* Pointers to start of Send-Queue  */
+#ifdef DEBUG_RCVCALLBACK
+       int akt_pending[ICN_BCH];
+       int max_pending[ICN_BCH];
+#endif
+} icn_dev;
+
+typedef icn_dev *icn_devptr;
+
+#ifdef __KERNEL__
+static icn_dev *dev = (icn_dev *) 0;
+static icn_dev *dev2 = (icn_dev *) 0;
+
+/* With modutils >= 1.1.67 Integers can be changed while loading a
+ * module. For this reason define the Port-Base an Shmem-Base as
+ * integers.
+ */
+int portbase = ICN_BASEADDR;
+int membase = ICN_MEMADDR;
+char *icn_id = "\0";
+char *icn_id2 = "\0";
+static char regname[35];       /* Name used for port/mem-registration */
+
+#endif                         /* __KERNEL__ */
+
+/* Utility-Macros */
+
+/* Return true, if there is a free transmit-buffer */
+#define sbfree (((dev->shmem->data_control.scns+1) & 0xf) != \
+                dev->shmem->data_control.scnr)
+
+/* Switch to next transmit-buffer */
+#define sbnext (dev->shmem->data_control.scns = \
+               ((dev->shmem->data_control.scns+1) & 0xf))
+
+/* Shortcuts for transmit-buffer-access */
+#define sbuf_n dev->shmem->data_control.scns
+#define sbuf_d dev->shmem->data_buffers.send_buf[sbuf_n].data
+#define sbuf_l dev->shmem->data_buffers.send_buf[sbuf_n].length
+#define sbuf_f dev->shmem->data_buffers.send_buf[sbuf_n].endflag
+
+/* Return true, if there is receive-data is available */
+#define rbavl  (dev->shmem->data_control.ecnr != \
+                dev->shmem->data_control.ecns)
+
+/* Switch to next receive-buffer */
+#define rbnext (dev->shmem->data_control.ecnr = \
+               ((dev->shmem->data_control.ecnr+1) & 0xf))
+
+/* Shortcuts for receive-buffer-access */
+#define rbuf_n dev->shmem->data_control.ecnr
+#define rbuf_d dev->shmem->data_buffers.receive_buf[rbuf_n].data
+#define rbuf_l dev->shmem->data_buffers.receive_buf[rbuf_n].length
+#define rbuf_f dev->shmem->data_buffers.receive_buf[rbuf_n].endflag
+
+/* Shortcuts for command-buffer-access */
+#define cmd_o (dev->shmem->comm_control.pcio_o)
+#define cmd_i (dev->shmem->comm_control.pcio_i)
+
+/* Return free space in command-buffer */
+#define cmd_free ((cmd_i>=cmd_o)?0x100-cmd_i+cmd_o:cmd_o-cmd_i)
+
+/* Shortcuts for message-buffer-access */
+#define msg_o (dev->shmem->comm_control.iopc_o)
+#define msg_i (dev->shmem->comm_control.iopc_i)
+
+/* Return length of Message, if avail. */
+#define msg_avail ((msg_o>msg_i)?0x100-msg_o+msg_i:msg_i-msg_o)
+
+#define MIN(a,b) ((a<b)?a:b)
+#define MAX(a,b) ((a>b)?a:b)
+
+/* Hopefully, a separate resource-registration-scheme for shared-memory
+ * will be introduced into the kernel. Until then, we use the normal
+ * routines, designed for port-registration.
+ */
+#define check_shmem   check_region
+#define release_shmem release_region
+#define request_shmem request_region
+
+#endif                         /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif                         /* icn_h */
diff --git a/drivers/isdn/isdn_cards.c b/drivers/isdn/isdn_cards.c
new file mode 100644 (file)
index 0000000..b28c045
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include <linux/config.h>
+
+#ifdef CONFIG_ISDN_DRV_ICN
+extern void icn_init(void);
+#endif
+
+#ifdef CONFIG_ISDN_DRV_TELES
+extern void teles_init(void);
+#endif
+
+void isdn_cards_init(void)
+{
+#if CONFIG_ISDN_DRV_ICN
+        icn_init();
+#endif
+#if CONFIG_ISDN_DRV_TELES
+        teles_init();
+#endif
+}
+
diff --git a/drivers/isdn/isdn_cards.h b/drivers/isdn/isdn_cards.h
new file mode 100644 (file)
index 0000000..9ebb2f3
--- /dev/null
@@ -0,0 +1,3 @@
+
+extern void isdn_cards_init(void);
+
diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c
new file mode 100644 (file)
index 0000000..785f857
--- /dev/null
@@ -0,0 +1,1887 @@
+/* $Id: isdn_common.c,v 1.4 1996/02/11 02:33:26 fritz Exp fritz $
+ *
+ * Linux ISDN subsystem, common used functions (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ * $Log: isdn_common.c,v $
+ * Revision 1.4  1996/02/11 02:33:26  fritz
+ * Fixed bug in main timer-dispatcher.
+ * Bugfix: Lot of tty-callbacks got called regardless of the events already
+ * been handled by network-devices.
+ * Changed ioctl-names.
+ *
+ * Revision 1.3  1996/01/22 05:16:11  fritz
+ * Changed ioctl-names.
+ * Fixed bugs in isdn_open and isdn_close regarding PPP_MINOR.
+ *
+ * Revision 1.2  1996/01/21 16:52:40  fritz
+ * Support for sk_buffs added, changed header-stuffing.
+ *
+ * Revision 1.1  1996/01/09 04:12:52  fritz
+ * Initial revision
+ *
+ */
+
+#ifndef STANDALONE
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/version.h>
+#ifndef __GENKSYMS__      /* Don't want genksyms report unneeded structs */
+#include <linux/isdn.h>
+#endif
+#include "isdn_common.h"
+#include "isdn_tty.h"
+#include "isdn_net.h"
+#include "isdn_ppp.h"
+#include "isdn_cards.h"
+
+
+
+/* Debugflags */
+#undef  ISDN_DEBUG_STATCALLB
+
+isdn_dev *dev = (isdn_dev *) 0;
+
+static int  has_exported = 0;
+static char *isdn_revision      = "$Revision: 1.4 $";
+
+extern char *isdn_net_revision;
+extern char *isdn_tty_revision;
+#ifdef CONFIG_ISDN_PPP
+extern char *isdn_ppp_revision;
+#else
+static char *isdn_ppp_revision = ": none $";
+#endif
+
+void isdn_MOD_INC_USE_COUNT(void)
+{
+       MOD_INC_USE_COUNT;
+}
+
+void isdn_MOD_DEC_USE_COUNT(void)
+{
+       MOD_DEC_USE_COUNT;
+}
+
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+void isdn_dumppkt(char *s, u_char * p, int len, int dumplen)
+{
+       int dumpc;
+
+       printk(KERN_DEBUG "%s(%d) ", s, len);
+       for (dumpc = 0; (dumpc < dumplen) && (len); len--, dumpc++)
+               printk(" %02x", *p++);
+       printk("\n");
+}
+#endif
+
+/* Try to allocate a new buffer, link it into queue. */
+u_char *
+ isdn_new_buf(pqueue ** queue, int length)
+{
+       pqueue *p;
+       pqueue *q;
+
+       if ((p = *queue)) {
+               while (p) {
+                       q = p;
+                       p = (pqueue *) p->next;
+               }
+               p = (pqueue *) kmalloc(sizeof(pqueue) + length, GFP_ATOMIC);
+               q->next = (u_char *) p;
+       } else
+               p = *queue = (pqueue *) kmalloc(sizeof(pqueue) + length, GFP_ATOMIC);
+       if (p) {
+               p->size = sizeof(pqueue) + length;
+               p->length = length;
+               p->next = NULL;
+               p->rptr = p->buffer;
+               return p->buffer;
+       } else {
+               return (u_char *) NULL;
+       }
+}
+
+static void isdn_free_queue(pqueue ** queue)
+{
+       pqueue *p;
+       pqueue *q;
+
+       p = *queue;
+       while (p) {
+               q = p;
+               p = (pqueue *) p->next;
+               kfree_s(q, q->size);
+       }
+       *queue = (pqueue *) 0;
+}
+
+int isdn_dc2minor(int di, int ch)
+{
+       int i;
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+               if (dev->chanmap[i] == ch && dev->drvmap[i] == di)
+                       return i;
+       return -1;
+}
+
+static int isdn_timer_cnt1 = 0;
+static int isdn_timer_cnt2 = 0;
+
+static void isdn_timer_funct(ulong dummy)
+{
+       int tf = dev->tflags;
+
+       if (tf & ISDN_TIMER_FAST) {
+               if (tf & ISDN_TIMER_MODEMREAD)
+                       isdn_tty_readmodem();
+               if (tf & ISDN_TIMER_MODEMPLUS)
+                       isdn_tty_modem_escape();
+               if (tf & ISDN_TIMER_MODEMXMIT)
+                       isdn_tty_modem_xmit();
+       }
+       if (tf & ISDN_TIMER_SLOW) {
+               if (++isdn_timer_cnt1 > ISDN_TIMER_02SEC) {
+                       isdn_timer_cnt1 = 0;
+                       if (tf & ISDN_TIMER_NETDIAL)
+                               isdn_net_dial();
+               }
+                if (++isdn_timer_cnt2 > ISDN_TIMER_1SEC) {
+                        isdn_timer_cnt2 = 0;
+                        if (tf & ISDN_TIMER_NETHANGUP)
+                                isdn_net_autohup();
+                        if (tf & ISDN_TIMER_MODEMRING)
+                                isdn_tty_modem_ring();
+#if (defined CONFIG_ISDN_PPP ) && (defined ISDN_CONFIG_MPP)
+                        if (tf & ISDN_TIMER_IPPP)
+                                isdn_ppp_timer_timeout();
+#endif
+                }
+       }
+       if (tf) {
+                int flags;
+
+               save_flags(flags);
+               cli();
+               del_timer(&dev->timer);
+               dev->timer.function = isdn_timer_funct;
+               dev->timer.expires = jiffies + ISDN_TIMER_RES;
+               add_timer(&dev->timer);
+               restore_flags(flags);
+       }
+}
+
+void isdn_timer_ctrl(int tf, int onoff)
+{
+       int flags;
+
+       save_flags(flags);
+       cli();
+       if ((tf & ISDN_TIMER_SLOW) && (!(dev->tflags & ISDN_TIMER_SLOW))) {
+               /* If the slow-timer wasn't activated until now */
+               isdn_timer_cnt1 = 0;
+               isdn_timer_cnt2 = 0;
+       }
+       if (onoff)
+               dev->tflags |= tf;
+       else
+               dev->tflags &= ~tf;
+       if (dev->tflags) {
+               del_timer(&dev->timer);
+               dev->timer.function = isdn_timer_funct;
+               dev->timer.expires = jiffies + ISDN_TIMER_RES;
+               add_timer(&dev->timer);
+       }
+       restore_flags(flags);
+}
+
+/* Receive a packet from B-Channel. (Called from low-level-module)
+ * Parameters:
+ *
+ * di      = Driver-Index.
+ * channel = Number of B-Channel (0...)
+ * buf     = pointer to packet-data
+ * len     = Length of packet-data
+ *
+ */
+static void isdn_receive_callback(int di, int channel, u_char * buf, int len)
+{
+       ulong flags;
+       char *p;
+       int i;
+       int midx;
+
+       if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+               return;
+        if ((i = isdn_dc2minor(di,channel))==-1)
+                return;
+       /* First, try to deliver data to network-device */
+       if (isdn_net_receive_callback(i, buf, len))
+               return;
+       /* No network-device found, deliver to tty or raw-channel */
+       if (len) {
+               save_flags(flags);
+               cli();
+                midx = dev->m_idx[i];
+                if (dev->mdm.atmodem[midx].mdmreg[13] & 2)
+                        /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
+                        if ((buf[0] == 1) && ((buf[1] == 0) || (buf[1] == 1))) {
+#ifdef ISDN_DEBUG_MODEM_DUMP
+                                isdn_dumppkt("T70strip1:", buf, len, len);
+#endif
+                                buf += 4;
+                                len -= 4;
+#ifdef ISDN_DEBUG_MODEM_DUMP
+                                isdn_dumppkt("T70strip2:", buf, len, len);
+#endif
+                        }
+                /* Try to deliver directly via tty-flip-buf if queue is empty */
+                if (!dev->drv[di]->rpqueue[channel])
+                        if (isdn_tty_try_read(midx, buf, len)) {
+                                restore_flags(flags);
+                                return;
+                        }
+                /* Direct deliver failed or queue wasn't empty.
+                 * Queue up for later dequeueing via timer-irq.
+                 */
+                p = isdn_new_buf(&dev->drv[di]->rpqueue[channel], len);
+                if (!p) {
+                        printk(KERN_WARNING "isdn: malloc of rcvbuf failed, dropping.\n");
+                        dev->drv[di]->rcverr[channel]++;
+                        restore_flags(flags);
+                        return;
+                } else {
+                        memcpy(p, buf, len);
+                        dev->drv[di]->rcvcount[channel] += len;
+                }
+                /* Schedule dequeuing */
+                if ((dev->modempoll) && (midx >= 0)) {
+                        if (dev->mdm.rcvsched[midx])
+                                isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+                }
+                wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]);
+               restore_flags(flags);
+       }
+}
+
+void isdn_all_eaz(int di, int ch)
+{
+       isdn_ctrl cmd;
+
+       cmd.driver = di;
+       cmd.arg = ch;
+       cmd.command = ISDN_CMD_SETEAZ;
+       cmd.num[0] = '\0';
+       (void) dev->drv[di]->interface->command(&cmd);
+}
+
+static int isdn_status_callback(isdn_ctrl * c)
+{
+       int di;
+       int mi;
+       ulong flags;
+       int i;
+       int r;
+       isdn_ctrl cmd;
+
+       di = c->driver;
+        i = isdn_dc2minor(di, c->arg);
+       switch (c->command) {
+                case ISDN_STAT_BSENT:
+                       if (i<0)
+                               return -1;
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        if (isdn_net_stat_callback(i, c->command))
+                                return 0;
+#if FUTURE
+                        isdn_tty_bsent(di, c->arg);
+#endif
+                        wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]);
+                        break;
+                case ISDN_STAT_STAVAIL:
+                        save_flags(flags);
+                        cli();
+                        dev->drv[di]->stavail += c->arg;
+                        restore_flags(flags);
+                        wake_up_interruptible(&dev->drv[di]->st_waitq);
+                        break;
+                case ISDN_STAT_RUN:
+                        dev->drv[di]->running = 1;
+                        for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+                                if (dev->drvmap[i] == di)
+                                        isdn_all_eaz(di, dev->chanmap[i]);
+                        break;
+                case ISDN_STAT_STOP:
+                        dev->drv[di]->running = 0;
+                        break;
+                case ISDN_STAT_ICALL:
+                       if (i<0)
+                               return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->num);
+#endif
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED) {
+                                cmd.driver = di;
+                                cmd.arg = c->arg;
+                                cmd.command = ISDN_CMD_HANGUP;
+                                dev->drv[di]->interface->command(&cmd);
+                                return 0;
+                        }
+
+                       /* Try to find a network-interface which will accept incoming call */
+                       r = isdn_net_find_icall(di, c->arg, i, c->num);
+                       switch (r) {
+                                case 0:
+                                        /* No network-device replies. Schedule RING-message to
+                                         * tty and set RI-bit of modem-status.
+                                         */
+                                        if ((mi = isdn_tty_find_icall(di, c->arg, c->num)) >= 0) {
+                                                dev->mdm.msr[mi] |= UART_MSR_RI;
+                                                isdn_tty_modem_result(2, &dev->mdm.info[mi]);
+                                                isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
+                                        } else if (dev->drv[di]->reject_bus) {
+                                                cmd.driver = di;
+                                                cmd.arg = c->arg;
+                                                cmd.command = ISDN_CMD_HANGUP;
+                                                dev->drv[di]->interface->command(&cmd);
+                                        }
+                                        break;
+                                case 1:
+                                        /* Schedule connection-setup */
+                                        isdn_net_dial();
+                                        cmd.driver = di;
+                                        cmd.arg = c->arg;
+                                        cmd.command = ISDN_CMD_ACCEPTD;
+                                        dev->drv[di]->interface->command(&cmd);
+                                        break;
+                                case 2:        /* For calling back, first reject incoming call ... */
+                                case 3:        /* Interface found, but down, reject call actively  */
+                                        cmd.driver = di;
+                                        cmd.arg = c->arg;
+                                        cmd.command = ISDN_CMD_HANGUP;
+                                        dev->drv[di]->interface->command(&cmd);
+                                        if (r == 2)
+                                                /* ... then start dialing. */
+                                                isdn_net_dial();
+                                        break;
+                       }
+                        return 0;
+                        break;
+                case ISDN_STAT_CINF:
+                       if (i<0)
+                               return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->num);
+#endif
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        if (strcmp(c->num, "0"))
+                                isdn_net_stat_callback(i, c->command);
+                        break;
+                case ISDN_STAT_CAUSE:
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->num);
+#endif
+                        printk(KERN_INFO "isdn: cause: %s\n", c->num);
+                        break;
+                case ISDN_STAT_DCONN:
+                       if (i<0)
+                               return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "DCONN: %ld\n", c->arg);
+#endif
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        /* Find any network-device, waiting for D-channel setup */
+                        if (isdn_net_stat_callback(i, c->command))
+                                break;
+                       if ((mi = dev->m_idx[i]) >= 0)
+                               /* If any tty has just dialed-out, setup B-Channel */
+                               if (dev->mdm.info[mi].flags &
+                                   (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+                                       if (dev->mdm.dialing[mi] == 1) {
+                                               dev->mdm.dialing[mi] = 2;
+                                               cmd.driver = di;
+                                               cmd.arg = c->arg;
+                                               cmd.command = ISDN_CMD_ACCEPTB;
+                                               dev->drv[di]->interface->command(&cmd);
+                                               return 0;
+                                       }
+                               }
+                        break;
+                case ISDN_STAT_DHUP:
+                       if (i<0)
+                               return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "DHUP: %ld\n", c->arg);
+#endif
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        dev->drv[di]->flags &= ~(1 << (c->arg));
+                        isdn_info_update();
+                        /* Signal hangup to network-devices */
+                        if (isdn_net_stat_callback(i, c->command))
+                                break;
+                       if ((mi = dev->m_idx[i]) >= 0) {
+                               /* Signal hangup to tty-device */
+                               if (dev->mdm.info[mi].flags &
+                                   (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+                                       if (dev->mdm.dialing[mi] == 1) {
+                                               dev->mdm.dialing[mi] = 0;
+                                               isdn_tty_modem_result(7, &dev->mdm.info[mi]);
+                                       }
+                                       if (dev->mdm.online[mi])
+                                               isdn_tty_modem_result(3, &dev->mdm.info[mi]);
+#ifdef ISDN_DEBUG_MODEM_HUP
+                                       printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
+#endif
+                                       isdn_tty_modem_hup(&dev->mdm.info[mi]);
+                                       dev->mdm.msr[mi] &= ~(UART_MSR_DCD | UART_MSR_RI);
+                                       return 0;
+                               }
+                       }
+                        break;
+                case ISDN_STAT_BCONN:
+                       if (i<0)
+                               return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "BCONN: %ld\n", c->arg);
+#endif
+                        /* Signal B-channel-connect to network-devices */
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        dev->drv[di]->flags |= (1 << (c->arg));
+                        isdn_info_update();
+                        if (isdn_net_stat_callback(i, c->command))
+                                break;
+                       if ((mi = dev->m_idx[i]) >= 0) {
+                               /* Schedule CONNECT-Message to any tty, waiting for it and
+                                * set DCD-bit of it's modem-status.
+                                */
+                               if (dev->mdm.info[mi].flags &
+                                   (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+                                       dev->mdm.msr[mi] |= UART_MSR_DCD;
+                                       if (dev->mdm.dialing[mi])
+                                               dev->mdm.dialing[mi] = 0;
+                                       dev->mdm.rcvsched[mi] = 1;
+                                       isdn_tty_modem_result(5, &dev->mdm.info[mi]);
+                               }
+                       }
+                        break;
+                case ISDN_STAT_BHUP:
+                       if (i<0)
+                               return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "BHUP: %ld\n", c->arg);
+#endif
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        dev->drv[di]->flags &= ~(1 << (c->arg));
+                        isdn_info_update();
+                       if ((mi = dev->m_idx[i]) >= 0) {
+                               /* Signal hangup to tty-device, schedule NO CARRIER-message */
+                               if (dev->mdm.info[mi].flags &
+                                   (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+                                       dev->mdm.msr[mi] &= ~(UART_MSR_DCD | UART_MSR_RI);
+                                       if (dev->mdm.online[mi])
+                                               isdn_tty_modem_result(3, &dev->mdm.info[mi]);
+#ifdef ISDN_DEBUG_MODEM_HUP
+                                       printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n");
+#endif
+                                       isdn_tty_modem_hup(&dev->mdm.info[mi]);
+                               }
+                       }
+                        break;
+                case ISDN_STAT_NODCH:
+                       if (i<0)
+                               return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "NODCH: %ld\n", c->arg);
+#endif
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        if (isdn_net_stat_callback(i, c->command))
+                                break;
+                       if ((mi = dev->m_idx[i]) >= 0) {
+                               if (dev->mdm.info[mi].flags &
+                                   (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+                                       if (dev->mdm.dialing[mi]) {
+                                               dev->mdm.dialing[mi] = 0;
+                                               isdn_tty_modem_result(6, &dev->mdm.info[mi]);
+                                       }
+                                       dev->mdm.msr[mi] &= ~UART_MSR_DCD;
+                                       if (dev->mdm.online[mi]) {
+                                               isdn_tty_modem_result(3, &dev->mdm.info[mi]);
+                                               dev->mdm.online[mi] = 0;
+                                       }
+                               }
+                       }
+                        break;
+                case ISDN_STAT_ADDCH:
+                        break;
+                case ISDN_STAT_UNLOAD:
+                        save_flags(flags);
+                        cli();
+                        for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+                                if (dev->drvmap[i] == di) {
+                                        dev->drvmap[i] = -1;
+                                        dev->chanmap[i] = -1;
+                                        dev->mdm.info[i].isdn_driver = -1;
+                                        dev->mdm.info[i].isdn_channel = -1;
+                                        isdn_info_update();
+                                }
+                        dev->drivers--;
+                        dev->channels -= dev->drv[di]->channels;
+                        kfree(dev->drv[di]->rcverr);
+                        kfree(dev->drv[di]->rcvcount);
+                        for (i = 0; i < dev->drv[di]->channels; i++)
+                                isdn_free_queue(&dev->drv[di]->rpqueue[i]);
+                        kfree(dev->drv[di]->rcv_waitq);
+                        kfree(dev->drv[di]->snd_waitq);
+                        kfree(dev->drv[di]);
+                        dev->drv[di] = NULL;
+                        dev->drvid[di][0] = '\0';
+                        isdn_info_update();
+                        restore_flags(flags);
+                        return 0;
+                default:
+                        return -1;
+       }
+       return 0;
+}
+
+/*
+ * Get integer from char-pointer, set pointer to end of number
+ */
+int isdn_getnum(char **p)
+{
+       int v = -1;
+
+       while (*p[0] >= '0' && *p[0] <= '9')
+               v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p[0]++) - '0');
+       return v;
+}
+
+int isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, int user)
+{
+       int avail;
+       int left;
+       int count;
+       int copy_l;
+       int dflag;
+       int flags;
+       pqueue *p;
+       u_char *cp;
+
+       if (!dev->drv[di]->rpqueue[channel]) {
+               if (user)
+                       interruptible_sleep_on(&dev->drv[di]->rcv_waitq[channel]);
+               else
+                       return 0;
+       }
+       if (!dev->drv[di])
+               return 0;
+       save_flags(flags);
+       cli();
+       avail = dev->drv[di]->rcvcount[channel];
+       restore_flags(flags);
+       left = MIN(len, avail);
+       cp = buf;
+       count = 0;
+       while (left) {
+               if ((copy_l = dev->drv[di]->rpqueue[channel]->length) > left) {
+                       copy_l = left;
+                       dflag = 0;
+               } else
+                       dflag = 1;
+               p = dev->drv[di]->rpqueue[channel];
+               if (user)
+                       memcpy_tofs(cp, p->rptr, copy_l);
+               else
+                       memcpy(cp, p->rptr, copy_l);
+               if (fp) {
+                       memset(fp, 0, copy_l);
+                       fp += copy_l;
+               }
+               left -= copy_l;
+               count += copy_l;
+               cp += copy_l;
+               if (dflag) {
+                       if (fp)
+                               *(fp - 1) = 0xff;
+                       save_flags(flags);
+                       cli();
+                       dev->drv[di]->rpqueue[channel] = (pqueue *) p->next;
+                       kfree_s(p, p->size);
+                       restore_flags(flags);
+               } else {
+                       p->rptr += copy_l;
+                       p->length -= copy_l;
+               }
+               save_flags(flags);
+               cli();
+               dev->drv[di]->rcvcount[channel] -= copy_l;
+               restore_flags(flags);
+       }
+       return count;
+}
+
+static __inline int isdn_minor2drv(int minor)
+{
+       return (dev->drvmap[minor]);
+}
+
+static __inline int isdn_minor2chan(int minor)
+{
+       return (dev->chanmap[minor]);
+}
+
+static char *
+ isdn_statstr(void)
+{
+       static char istatbuf[2048];
+       char *p;
+       int i;
+
+       sprintf(istatbuf, "idmap:\t");
+       p = istatbuf + strlen(istatbuf);
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+               sprintf(p, "%s ", (dev->drvmap[i] < 0) ? "-" : dev->drvid[dev->drvmap[i]]);
+               p = istatbuf + strlen(istatbuf);
+       }
+       sprintf(p, "\nchmap:\t");
+       p = istatbuf + strlen(istatbuf);
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+               sprintf(p, "%d ", dev->chanmap[i]);
+               p = istatbuf + strlen(istatbuf);
+       }
+       sprintf(p, "\ndrmap:\t");
+       p = istatbuf + strlen(istatbuf);
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+               sprintf(p, "%d ", dev->drvmap[i]);
+               p = istatbuf + strlen(istatbuf);
+       }
+       sprintf(p, "\nusage:\t");
+       p = istatbuf + strlen(istatbuf);
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+               sprintf(p, "%d ", dev->usage[i]);
+               p = istatbuf + strlen(istatbuf);
+       }
+       sprintf(p, "\nflags:\t");
+       p = istatbuf + strlen(istatbuf);
+       for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
+               if (dev->drv[i]) {
+                       sprintf(p, "%ld ", dev->drv[i]->flags);
+                       p = istatbuf + strlen(istatbuf);
+               } else {
+                       sprintf(p, "? ");
+                       p = istatbuf + strlen(istatbuf);
+               }
+       }
+       sprintf(p, "\nphone:\t");
+       p = istatbuf + strlen(istatbuf);
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+               sprintf(p, "%s ", dev->num[i]);
+               p = istatbuf + strlen(istatbuf);
+       }
+       sprintf(p, "\n");
+       return istatbuf;
+}
+
+/* Module interface-code */
+
+void isdn_info_update(void)
+{
+       infostruct *p = dev->infochain;
+
+       while (p) {
+               *(p->private) = 1;
+               p = (infostruct *) p->next;
+       }
+       wake_up_interruptible(&(dev->info_waitq));
+}
+
+static int isdn_read(struct inode *inode, struct file *file, char *buf, int count)
+{
+       uint minor = MINOR(inode->i_rdev);
+       int len = 0;
+       ulong flags;
+       int drvidx;
+       int chidx;
+
+       if (minor == ISDN_MINOR_STATUS) {
+               char *p;
+               if (!file->private_data)
+                       interruptible_sleep_on(&(dev->info_waitq));
+               save_flags(flags);
+               p = isdn_statstr();
+               restore_flags(flags);
+               file->private_data = 0;
+               if ((len = strlen(p)) <= count) {
+                       memcpy_tofs(buf, p, len);
+                       file->f_pos += len;
+                       return len;
+               }
+               return 0;
+       }
+       if (!dev->drivers)
+               return -ENODEV;
+       if (minor < ISDN_MINOR_CTRL) {
+               drvidx = isdn_minor2drv(minor);
+               if (drvidx < 0)
+                       return -ENODEV;
+               if (!dev->drv[drvidx]->running)
+                       return -ENODEV;
+               chidx = isdn_minor2chan(minor);
+               len = isdn_readbchan(drvidx, chidx, buf, 0, count, 1);
+               file->f_pos += len;
+               return len;
+       }
+       if (minor <= ISDN_MINOR_CTRLMAX) {
+               drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+               if (drvidx < 0)
+                       return -ENODEV;
+               if (!dev->drv[drvidx]->stavail)
+                       interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq));
+               if (dev->drv[drvidx]->interface->readstat)
+                       len = dev->drv[drvidx]->interface->
+                           readstat(buf, MIN(count, dev->drv[drvidx]->stavail), 1);
+               else
+                       len = 0;
+               save_flags(flags);
+               cli();
+               dev->drv[drvidx]->stavail -= len;
+               restore_flags(flags);
+               file->f_pos += len;
+               return len;
+       }
+#ifdef CONFIG_ISDN_PPP
+       if (minor <= ISDN_MINOR_PPPMAX)
+               return (isdn_ppp_read(minor - ISDN_MINOR_PPP, file, buf, count));
+#endif
+       return -ENODEV;
+}
+
+static int isdn_lseek(struct inode *inode, struct file *file, off_t offset, int orig)
+{
+       return -ESPIPE;
+}
+
+static int isdn_write(struct inode *inode, struct file *file, const char *buf, int count)
+{
+       uint minor = MINOR(inode->i_rdev);
+       int drvidx;
+       int chidx;
+
+       if (minor == ISDN_MINOR_STATUS)
+               return -EPERM;
+       if (!dev->drivers)
+               return -ENODEV;
+       if (minor < ISDN_MINOR_CTRL) {
+               drvidx = isdn_minor2drv(minor);
+               if (drvidx < 0)
+                       return -ENODEV;
+               if (!dev->drv[drvidx]->running)
+                       return -ENODEV;
+               chidx = isdn_minor2chan(minor);
+               while (dev->drv[drvidx]->interface->writebuf(drvidx, chidx, buf, count, 1) != count)
+                       interruptible_sleep_on(&dev->drv[drvidx]->snd_waitq[chidx]);
+               return count;
+       }
+       if (minor <= ISDN_MINOR_CTRLMAX) {
+               drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+               if (drvidx < 0)
+                       return -ENODEV;
+               /*
+                * We want to use the isdnctrl device to load the firmware
+                *
+                if (!dev->drv[drvidx]->running)
+                return -ENODEV;
+                */
+               if (dev->drv[drvidx]->interface->writecmd)
+                       return (dev->drv[drvidx]->interface->writecmd(buf, count, 1));
+               else
+                       return count;
+       }
+#ifdef CONFIG_ISDN_PPP
+       if (minor <= ISDN_MINOR_PPPMAX)
+               return (isdn_ppp_write(minor - ISDN_MINOR_PPP, file, buf, count));
+#endif
+       return -ENODEV;
+}
+
+static int isdn_select(struct inode *inode, struct file *file, int type, select_table * st)
+{
+       uint minor = MINOR(inode->i_rdev);
+
+       if (minor == ISDN_MINOR_STATUS) {
+               if (file->private_data)
+                       return 1;
+               else {
+                       if (st)
+                               select_wait(&(dev->info_waitq), st);
+                       return 0;
+               }
+       }
+       if (minor <= ISDN_MINOR_CTRLMAX)
+               return 1;
+#ifdef CONFIG_ISDN_PPP
+       if (minor <= ISDN_MINOR_PPPMAX)
+               return (isdn_ppp_select(minor - ISDN_MINOR_PPP, file, type, st));
+#endif
+       return -ENODEV;
+}
+
+static int isdn_set_allcfg(char *src)
+{
+       int ret;
+       int i;
+       ulong flags;
+       char buf[1024];
+       isdn_net_ioctl_cfg cfg;
+       isdn_net_ioctl_phone phone;
+
+       if ((ret = isdn_net_rmall()))
+               return ret;
+       save_flags(flags);
+       cli();
+       if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(int)))) {
+               restore_flags(flags);
+               return ret;
+       }
+       memcpy_tofs((char *) &i, src, sizeof(int));
+       while (i) {
+               char *c;
+               char *c2;
+
+               if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(cfg)))) {
+                       restore_flags(flags);
+                       return ret;
+               }
+               memcpy_tofs((char *) &cfg, src, sizeof(cfg));
+               src += sizeof(cfg);
+               if (!isdn_net_new(cfg.name, NULL)) {
+                       restore_flags(flags);
+                       return -EIO;
+               }
+               if ((ret = isdn_net_setcfg(&cfg))) {
+                       restore_flags(flags);
+                       return ret;
+               }
+               if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(buf)))) {
+                       restore_flags(flags);
+                       return ret;
+               }
+               memcpy_fromfs(buf, src, sizeof(buf));
+               src += sizeof(buf);
+               c = buf;
+               while (*c) {
+                       if ((c2 = strchr(c, ' ')))
+                               *c2++ = '\0';
+                       strcpy(phone.phone, c);
+                       strcpy(phone.name, cfg.name);
+                       phone.outgoing = 0;
+                       if ((ret = isdn_net_addphone(&phone))) {
+                               restore_flags(flags);
+                               return ret;
+                       }
+                       if (c2)
+                               c = c2;
+                       else
+                               c += strlen(c);
+               }
+               if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(buf)))) {
+                       restore_flags(flags);
+                       return ret;
+               }
+               memcpy_fromfs(buf, src, sizeof(buf));
+               src += sizeof(buf);
+               c = buf;
+               while (*c) {
+                       if ((c2 = strchr(c, ' ')))
+                               *c2++ = '\0';
+                       strcpy(phone.phone, c);
+                       strcpy(phone.name, cfg.name);
+                       phone.outgoing = 1;
+                       if ((ret = isdn_net_addphone(&phone))) {
+                               restore_flags(flags);
+                               return ret;
+                       }
+                       if (c2)
+                               c = c2;
+                       else
+                               c += strlen(c);
+               }
+               i--;
+       }
+       restore_flags(flags);
+       return 0;
+}
+
+static int isdn_get_allcfg(char *dest)
+{
+       isdn_net_ioctl_cfg cfg;
+       isdn_net_ioctl_phone phone;
+       isdn_net_dev *p;
+       ulong flags;
+       int ret;
+
+       /* Walk through netdev-chain */
+       save_flags(flags);
+       cli();
+       p = dev->netdev;
+       while (p) {
+               if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 10))) {
+                       restore_flags(flags);
+                       return ret;
+               }
+               strcpy(cfg.eaz, p->local.msn);
+               cfg.exclusive = p->local.exclusive;
+               if (p->local.pre_device >= 0) {
+                       sprintf(cfg.drvid, "%s,%d", dev->drvid[p->local.pre_device],
+                               p->local.pre_channel);
+               } else
+                       cfg.drvid[0] = '\0';
+               cfg.onhtime = p->local.onhtime;
+               cfg.charge = p->local.charge;
+               cfg.l2_proto = p->local.l2_proto;
+               cfg.l3_proto = p->local.l3_proto;
+               cfg.p_encap = p->local.p_encap;
+               cfg.secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0;
+               cfg.callback = (p->local.flags & ISDN_NET_CALLBACK) ? 1 : 0;
+               cfg.chargehup = (p->local.hupflags & 4) ? 1 : 0;
+               cfg.ihup = (p->local.hupflags & 8) ? 1 : 0;
+               memcpy_tofs(dest, p->local.name, 10);
+               dest += 10;
+               memcpy_tofs(dest, (char *) &cfg, sizeof(cfg));
+               dest += sizeof(cfg);
+               strcpy(phone.name, p->local.name);
+               phone.outgoing = 0;
+               if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
+                       restore_flags(flags);
+                       return ret;
+               } else
+                       dest += ret;
+               strcpy(phone.name, p->local.name);
+               phone.outgoing = 1;
+               if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
+                       restore_flags(flags);
+                       return ret;
+               } else
+                       dest += ret;
+               p = p->next;
+       }
+       restore_flags(flags);
+       return 0;
+}
+
+static int isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+       uint minor = MINOR(inode->i_rdev);
+       isdn_ctrl c;
+       int drvidx;
+       int chidx;
+       int ret;
+       char *s;
+       char name[10];
+       char bname[21];
+       isdn_ioctl_struct iocts;
+       isdn_net_ioctl_phone phone;
+       isdn_net_ioctl_cfg cfg;
+
+       if (minor == ISDN_MINOR_STATUS)
+               return -EPERM;
+       if (!dev->drivers)
+               return -ENODEV;
+       if (minor < ISDN_MINOR_CTRL) {
+               drvidx = isdn_minor2drv(minor);
+               if (drvidx < 0)
+                       return -ENODEV;
+               chidx = isdn_minor2chan(minor);
+               if (!dev->drv[drvidx]->running)
+                       return -ENODEV;
+               return 0;
+       }
+       if (minor <= ISDN_MINOR_CTRLMAX) {
+               switch (cmd) {
+#ifdef CONFIG_NETDEVICES
+                        case IIOCNETAIF:
+                                /* Add a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
+                                                return ret;
+                                        memcpy_fromfs(name, (char *) arg, sizeof(name));
+                                        s = name;
+                                } else
+                                        s = NULL;
+                                if ((s = isdn_net_new(s, NULL))) {
+                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg, strlen(s) + 1)))
+                                                return ret;
+                                        memcpy_tofs((char *) arg, s, strlen(s) + 1);
+                                        return 0;
+                                } else
+                                        return -ENODEV;
+                        case IIOCNETASL:
+                                /* Add a slave to a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(bname))))
+                                                return ret;
+                                        memcpy_fromfs(bname, (char *) arg, sizeof(bname));
+                                } else
+                                        return -EINVAL;
+                                if ((s = isdn_net_newslave(bname))) {
+                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg, strlen(s) + 1)))
+                                                return ret;
+                                        memcpy_tofs((char *) arg, s, strlen(s) + 1);
+                                        return 0;
+                                } else
+                                        return -ENODEV;
+                        case IIOCNETDIF:
+                                /* Delete a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
+                                                return ret;
+                                        memcpy_fromfs(name, (char *) arg, sizeof(name));
+                                        return isdn_net_rm(name);
+                                } else
+                                        return -EINVAL;
+                        case IIOCNETSCF:
+                                /* Set configurable parameters of a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(cfg))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &cfg, (char *) arg, sizeof(cfg));
+                                        return isdn_net_setcfg(&cfg);
+                                } else
+                                        return -EINVAL;
+                        case IIOCNETGCF:
+                                /* Get configurable parameters of a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(cfg))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &cfg, (char *) arg, sizeof(cfg));
+                                        if (!(ret = isdn_net_getcfg(&cfg))) {
+                                                if ((ret = verify_area(VERIFY_WRITE, (void *) arg, sizeof(cfg))))
+                                                        return ret;
+                                                memcpy_tofs((char *) arg, (char *) &cfg, sizeof(cfg));
+                                        }
+                                        return ret;
+                                } else
+                                        return -EINVAL;
+                        case IIOCNETANM:
+                                /* Add a phone-number to a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &phone, (char *) arg, sizeof(phone));
+                                        return isdn_net_addphone(&phone);
+                                } else
+                                        return -EINVAL;
+                        case IIOCNETGNM:
+                                /* Get list of phone-numbers of a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &phone, (char *) arg, sizeof(phone));
+                                        return isdn_net_getphones(&phone, (char *) arg);
+                                } else
+                                        return -EINVAL;
+                        case IIOCNETDNM:
+                                /* Delete a phone-number of a network-interface */
+                                if (arg) {
+                                        memcpy_fromfs((char *) &phone, (char *) arg, sizeof(phone));
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone))))
+                                                return ret;
+                                        return isdn_net_delphone(&phone);
+                                } else
+                                        return -EINVAL;
+                        case IIOCNETDIL:
+                                /* Force dialing of a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
+                                                return ret;
+                                        memcpy_fromfs(name, (char *) arg, sizeof(name));
+                                        return isdn_net_force_dial(name);
+                                } else
+                                        return -EINVAL;
+#ifdef CONFIG_ISDN_PPP
+                        case IIOCNETALN:
+                                if(arg) {
+                                        if ((ret = verify_area(VERIFY_READ,
+                                                               (void*)arg,
+                                                               sizeof(name))))
+                                                return ret;
+                                } else
+                                        return -EINVAL;
+                                memcpy_fromfs(name,(char*)arg,sizeof(name));
+                                return isdn_ppp_dial_slave(name);
+                        case IIOCNETDLN:
+                                /* remove one link from bundle; removed for i4l 0.7.1  */
+                                return 2;
+#endif
+                        case IIOCNETHUP:
+                                /* Force hangup of a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
+                                                return ret;
+                                        memcpy_fromfs(name, (char *) arg, sizeof(name));
+                                        return isdn_net_force_hangup(name);
+                                } else
+                                        return -EINVAL;
+                                break;
+#endif                         /* CONFIG_NETDEVICES */
+                        case IIOCSETVER:
+                                dev->net_verbose = arg;
+                                printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
+                                return 0;
+                        case IIOCSETGST:
+                                if (arg)
+                                        dev->global_flags |= ISDN_GLOBAL_STOPPED;
+                                else
+                                        dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
+                                printk(KERN_INFO "isdn: Global Mode %s\n",
+                                       (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
+                                return 0;
+                        case IIOCSETBRJ:
+                                drvidx = -1;
+                                if (arg) {
+                                        int i;
+                                        char *p;
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg,
+                                                               sizeof(isdn_ioctl_struct))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
+                                        if (strlen(iocts.drvid)) {
+                                                if ((p = strchr(iocts.drvid, ',')))
+                                                        *p = 0;
+                                                drvidx = -1;
+                                                for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+                                                        if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+                                                                drvidx = i;
+                                                                break;
+                                                        }
+                                        }
+                                }
+                                if (drvidx == -1)
+                                        return -ENODEV;
+                                dev->drv[drvidx]->reject_bus = iocts.arg;
+                                return 0;
+                        case IIOCGETSET:
+                                /* Get complete setup (all network-interfaces and profile-
+                                   settings of all tty-devices */
+                                if (arg)
+                                        return (isdn_get_allcfg((char *) arg));
+                                else
+                                        return -EINVAL;
+                                break;
+                        case IIOCSETSET:
+                                /* Set complete setup (all network-interfaces and profile-
+                                   settings of all tty-devices */
+                                if (arg)
+                                        return (isdn_set_allcfg((char *) arg));
+                                else
+                                        return -EINVAL;
+                                break;
+                        case IIOCSIGPRF:
+                                dev->profd = current;
+                                return 0;
+                                break;
+                        case IIOCGETPRF:
+                                /* Get all Modem-Profiles */
+                                if (arg) {
+                                        char *p = (char *) arg;
+                                        int i;
+                                        
+                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+                                                               (ISDN_MODEM_ANZREG + ISDN_MSNLEN)
+                                                               * ISDN_MAX_CHANNELS)))
+                                                return ret;
+                                        
+                                        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+                                                memcpy_tofs(p, dev->mdm.atmodem[i].profile, ISDN_MODEM_ANZREG);
+                                                p += ISDN_MODEM_ANZREG;
+                                                memcpy_tofs(p, dev->mdm.atmodem[i].pmsn, ISDN_MSNLEN);
+                                                p += ISDN_MSNLEN;
+                                        }
+                                        return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS;
+                                } else
+                                        return -EINVAL;
+                                break;
+                        case IIOCSETPRF:
+                                /* Set all Modem-Profiles */
+                                if (arg) {
+                                        char *p = (char *) arg;
+                                        int i;
+                                        
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg,
+                                                               (ISDN_MODEM_ANZREG + ISDN_MSNLEN)
+                                                               * ISDN_MAX_CHANNELS)))
+                                                return ret;
+                                        
+                                        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+                                                memcpy_fromfs(dev->mdm.atmodem[i].profile, p, ISDN_MODEM_ANZREG);
+                                                p += ISDN_MODEM_ANZREG;
+                                                memcpy_fromfs(dev->mdm.atmodem[i].pmsn, p, ISDN_MSNLEN);
+                                                p += ISDN_MSNLEN;
+                                        }
+                                        return 0;
+                                } else
+                                        return -EINVAL;
+                                break;
+                        case IIOCSETMAP:
+                        case IIOCGETMAP:
+                                /* Set/Get MSN->EAZ-Mapping for a driver */
+                                if (arg) {
+                                        int i;
+                                        char *p;
+                                        char nstring[255];
+                                        
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg,
+                                                               sizeof(isdn_ioctl_struct))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
+                                        if (strlen(iocts.drvid)) {
+                                                drvidx = -1;
+                                                for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+                                                        if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+                                                                drvidx = i;
+                                                                break;
+                                                        }
+                                        } else
+                                                drvidx = 0;
+                                        if (drvidx == -1)
+                                                return -ENODEV;
+                                        if (cmd == IIOCSETMAP) {
+                                                if ((ret = verify_area(VERIFY_READ, (void *) iocts.arg, 255)))
+                                                        return ret;
+                                                memcpy_fromfs(nstring, (char *) iocts.arg, 255);
+                                                memset(dev->drv[drvidx]->msn2eaz, 0,
+                                                       sizeof(dev->drv[drvidx]->msn2eaz));
+                                                p = strtok(nstring, ",");
+                                                i = 0;
+                                                while ((p) && (i < 10)) {
+                                                        strcpy(dev->drv[drvidx]->msn2eaz[i++], p);
+                                                        p = strtok(NULL, ",");
+                                                }
+                                        } else {
+                                                p = nstring;
+                                                for (i = 0; i < 10; i++)
+                                                        p += sprintf(p, "%s%s",
+                                                                     strlen(dev->drv[drvidx]->msn2eaz[i]) ?
+                                                                     dev->drv[drvidx]->msn2eaz[i] : "-",
+                                                                     (i < 9) ? "," : "\0");
+                                                if ((ret = verify_area(VERIFY_WRITE, (void *) iocts.arg,
+                                                                       strlen(nstring) + 1)))
+                                                        return ret;
+                                                memcpy_tofs((char *) iocts.arg, nstring, strlen(nstring) + 1);
+                                        }
+                                        return 0;
+                                } else
+                                        return -EINVAL;
+                        case IIOCDBGVAR:
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg, sizeof(ulong))))
+                                                return ret;
+                                        memcpy_tofs((char *) arg, (char *) &dev, sizeof(ulong));
+                                        return 0;
+                                } else
+                                        return -EINVAL;
+                                break;
+                        default:
+                                if ((cmd&IIOCDRVCTL)==IIOCDRVCTL)
+                                        cmd = ((cmd>>_IOC_NRSHIFT)&_IOC_NRMASK)& ISDN_DRVIOCTL_MASK;
+                               else
+                                       return -EINVAL;
+                                if (arg) {
+                                        int i;
+                                        char *p;
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg,
+                                                               sizeof(isdn_ioctl_struct))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
+                                        if (strlen(iocts.drvid)) {
+                                                if ((p = strchr(iocts.drvid, ',')))
+                                                        *p = 0;
+                                                drvidx = -1;
+                                                for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+                                                        if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+                                                                drvidx = i;
+                                                                break;
+                                                        }
+                                        } else
+                                                drvidx = 0;
+                                        if (drvidx == -1)
+                                                return -ENODEV;
+                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+                                                               sizeof(isdn_ioctl_struct))))
+                                                return ret;
+                                        c.driver = drvidx;
+                                        c.command = ISDN_CMD_IOCTL;
+                                        c.arg = cmd;
+                                        memcpy(c.num, (char *) &iocts.arg, sizeof(ulong));
+                                        ret = dev->drv[drvidx]->interface->command(&c);
+                                        memcpy((char *) &iocts.arg, c.num, sizeof(ulong));
+                                        memcpy_tofs((char *) arg, &iocts, sizeof(isdn_ioctl_struct));
+                                        return ret;
+                                } else
+                                        return -EINVAL;
+               }
+       }
+#ifdef CONFIG_ISDN_PPP
+       if (minor <= ISDN_MINOR_PPPMAX)
+               return (isdn_ppp_ioctl(minor - ISDN_MINOR_PPP, file, cmd, arg));
+#endif
+       return -ENODEV;
+}
+
+/*
+ * Open the device code.
+ * MOD_INC_USE_COUNT make sure that the driver memory is not freed
+ * while the device is in use.
+ */
+static int isdn_open(struct inode *ino, struct file *filep)
+{
+       uint minor = MINOR(ino->i_rdev);
+       int drvidx;
+       int chidx;
+       isdn_ctrl c;
+
+       if (minor == ISDN_MINOR_STATUS) {
+               infostruct *p;
+
+               if ((p = (infostruct *) kmalloc(sizeof(infostruct), GFP_KERNEL))) {
+                       MOD_INC_USE_COUNT;
+                       p->next = (char *) dev->infochain;
+                       p->private = (char *) &(filep->private_data);
+                       dev->infochain = p;
+                       /* At opening we allow a single update */
+                       filep->private_data = (char *) 1;
+                       return 0;
+               } else
+                       return -ENOMEM;
+       }
+       if (!dev->channels)
+               return -ENODEV;
+       if (minor < ISDN_MINOR_CTRL) {
+               drvidx = isdn_minor2drv(minor);
+               if (drvidx < 0)
+                       return -ENODEV;
+               chidx = isdn_minor2chan(minor);
+               if (!dev->drv[drvidx]->running)
+                       return -ENODEV;
+               if (!(dev->drv[drvidx]->flags & (1 << chidx)))
+                       return -ENODEV;
+               c.command = ISDN_CMD_LOCK;
+               c.driver = drvidx;
+               (void) dev->drv[drvidx]->interface->command(&c);
+               MOD_INC_USE_COUNT;
+               return 0;
+       }
+       if (minor <= ISDN_MINOR_CTRLMAX) {
+               drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+               if (drvidx < 0)
+                       return -ENODEV;
+               c.command = ISDN_CMD_LOCK;
+               c.driver = drvidx;
+               MOD_INC_USE_COUNT;
+               (void) dev->drv[drvidx]->interface->command(&c);
+               return 0;
+       }
+#ifdef CONFIG_ISDN_PPP
+       if (minor <= ISDN_MINOR_PPPMAX) {
+               int ret;
+               if (!(ret = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep)))
+                       MOD_INC_USE_COUNT;
+               return ret;
+       }
+#endif
+       return -ENODEV;
+}
+
+static void isdn_close(struct inode *ino, struct file *filep)
+{
+       uint minor = MINOR(ino->i_rdev);
+       int drvidx;
+       isdn_ctrl c;
+
+       if (!dev->channels)
+               return;
+       MOD_DEC_USE_COUNT;
+       if (minor == ISDN_MINOR_STATUS) {
+               infostruct *p = dev->infochain;
+               infostruct *q = NULL;
+               while (p) {
+                       if (p->private == (char *) &(filep->private_data)) {
+                               if (q)
+                                       q->next = p->next;
+                               else
+                                       dev->infochain = (infostruct *) (p->next);
+                               return;
+                       }
+                       q = p;
+                       p = (infostruct *) (p->next);
+               }
+               printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
+       }
+       if (minor < ISDN_MINOR_CTRL) {
+               drvidx = isdn_minor2drv(minor);
+               if (drvidx < 0)
+                       return;
+               c.command = ISDN_CMD_UNLOCK;
+               c.driver = drvidx;
+               (void) dev->drv[drvidx]->interface->command(&c);
+               return;
+       }
+       if (minor <= ISDN_MINOR_CTRLMAX) {
+               drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+               if (drvidx < 0)
+                       return;
+               if (dev->profd == current)
+                       dev->profd = NULL;
+               c.command = ISDN_CMD_UNLOCK;
+               c.driver = drvidx;
+               (void) dev->drv[drvidx]->interface->command(&c);
+               return;
+       }
+#ifdef CONFIG_ISDN_PPP
+       if (minor <= ISDN_MINOR_PPPMAX) {
+               isdn_ppp_release(minor - ISDN_MINOR_PPP, filep);
+        }
+#endif
+}
+
+static struct file_operations isdn_fops =
+{
+       isdn_lseek,
+       isdn_read,
+       isdn_write,
+       NULL,                   /* isdn_readdir */
+       isdn_select,            /* isdn_select */
+       isdn_ioctl,             /* isdn_ioctl */
+       NULL,                   /* isdn_mmap */
+       isdn_open,
+       isdn_close,
+       NULL                    /* fsync */
+};
+
+char *
+ isdn_map_eaz2msn(char *msn, int di)
+{
+       driver *this = dev->drv[di];
+       int i;
+
+       if (strlen(msn) == 1) {
+               i = msn[0] - '0';
+               if ((i >= 0) && (i <= 9))
+                       if (strlen(this->msn2eaz[i]))
+                               return (this->msn2eaz[i]);
+       }
+       return (msn);
+}
+
+/*
+ * Find an unused ISDN-channel, whose feature-flags match the
+ * given L2- and L3-protocols.
+ */
+int isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
+                    ,int pre_chan)
+{
+       int i;
+       ulong flags;
+       ulong features;
+
+       save_flags(flags);
+       cli();
+       features = (1 << l2_proto) | (0x100 << l3_proto);
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+               if (USG_NONE(dev->usage[i]) &&
+                   (dev->drvmap[i] != -1)) {
+                       int d = dev->drvmap[i];
+                       if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) &&
+                       ((pre_dev != d) || (pre_chan != dev->chanmap[i])))
+                               continue;
+                       if ((dev->drv[d]->running)) {
+                               if ((dev->drv[d]->interface->features & features) == features) {
+                                       if ((pre_dev < 0) || (pre_chan < 0)) {
+                                               dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
+                                               dev->usage[i] |= usage;
+                                               isdn_info_update();
+                                               restore_flags(flags);
+                                               return i;
+                                       } else {
+                                               if ((pre_dev == d) && (pre_chan == dev->chanmap[i])) {
+                                                       dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
+                                                       dev->usage[i] |= usage;
+                                                       isdn_info_update();
+                                                       restore_flags(flags);
+                                                       return i;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       restore_flags(flags);
+       return -1;
+}
+
+/*
+ * Set state of ISDN-channel to 'unused'
+ */
+void isdn_free_channel(int di, int ch, int usage)
+{
+       int i;
+       ulong flags;
+
+       save_flags(flags);
+       cli();
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+               if (((dev->usage[i] & ISDN_USAGE_MASK) == usage) &&
+                   (dev->drvmap[i] == di) &&
+                   (dev->chanmap[i] == ch)) {
+                       dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE);
+                       strcpy(dev->num[i], "???");
+                       isdn_info_update();
+                       restore_flags(flags);
+                       return;
+               }
+       restore_flags(flags);
+}
+
+/*
+ * Cancel Exclusive-Flag for ISDN-channel
+ */
+void isdn_unexclusive_channel(int di, int ch)
+{
+       int i;
+       ulong flags;
+
+       save_flags(flags);
+       cli();
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+               if ((dev->drvmap[i] == di) &&
+                   (dev->chanmap[i] == ch)) {
+                       dev->usage[i] &= ~ISDN_USAGE_EXCLUSIVE;
+                       isdn_info_update();
+                       restore_flags(flags);
+                       return;
+               }
+       restore_flags(flags);
+}
+
+/*
+ *  receive callback handler for drivers supporting sk_buff's.
+ */
+
+void isdn_receive_skb_callback(int drvidx, int chan, struct sk_buff *skb) 
+{
+        int i;
+
+       if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+               return;
+        if ((i = isdn_dc2minor(drvidx,chan))==-1)
+                return;
+       if (isdn_net_rcv_skb(i, skb) == 0) {
+               isdn_receive_callback(drvidx, chan, skb->data, skb->len);
+               skb->free = 1;
+               kfree_skb(skb, FREE_READ);
+       }
+}
+
+/*
+ *  writebuf replacement for SKB_ABLE drivers
+ */
+
+int isdn_writebuf_stub(int drvidx, int chan, const u_char *buf, int len, 
+                      int user)
+{
+        struct sk_buff * skb;
+        
+        skb = alloc_skb(dev->drv[drvidx]->interface->hl_hdrlen + len, GFP_ATOMIC);
+        if (skb == NULL)
+                return 0;
+
+        skb_reserve(skb, dev->drv[drvidx]->interface->hl_hdrlen);
+        skb->free = 1;
+
+        if (user)
+                memcpy_fromfs(skb_put(skb, len), buf, len);
+        else
+                memcpy(skb_put(skb, len), buf, len);
+
+        return dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, skb);
+}
+/*
+ * Low-level-driver registration
+ */
+
+
+int register_isdn(isdn_if * i)
+{
+       driver *d;
+       int n, j, k;
+       ulong flags;
+       int drvidx;
+
+       if (dev->drivers >= ISDN_MAX_DRIVERS) {
+               printk(KERN_WARNING "register_isdn: Max. %d drivers supported\n",
+                      ISDN_MAX_DRIVERS);
+               return 0;
+       }
+       n = i->channels;
+       if (dev->channels + n >= ISDN_MAX_CHANNELS) {
+               printk(KERN_WARNING "register_isdn: Max. %d channels supported\n",
+                      ISDN_MAX_CHANNELS);
+               return 0;
+       }
+       if (!(d = (driver *) kmalloc(sizeof(driver), GFP_KERNEL))) {
+               printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n");
+               return 0;
+       }
+       memset((char *) d, 0, sizeof(driver));
+       if (!(d->rcverr = (int *) kmalloc(sizeof(int) * n, GFP_KERNEL))) {
+               printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n");
+               kfree(d);
+               return 0;
+       }
+       memset((char *) d->rcverr, 0, sizeof(int) * n);
+       if (!(d->rcvcount = (int *) kmalloc(sizeof(int) * n, GFP_KERNEL))) {
+               printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n");
+               kfree(d->rcverr);
+               kfree(d);
+               return 0;
+       }
+       memset((char *) d->rcvcount, 0, sizeof(int) * n);
+       if (!(d->rpqueue = (pqueue **) kmalloc(sizeof(pqueue *) * n, GFP_KERNEL))) {
+               printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
+               kfree(d->rcvcount);
+               kfree(d->rcverr);
+               kfree(d);
+               return 0;
+       }
+       memset((char *) d->rpqueue, 0, sizeof(pqueue *) * n);
+       if (!(d->rcv_waitq = (struct wait_queue **)
+             kmalloc(sizeof(struct wait_queue *) * n, GFP_KERNEL))) {
+               printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n");
+               kfree(d->rpqueue);
+               kfree(d->rcvcount);
+               kfree(d->rcverr);
+               kfree(d);
+               return 0;
+       }
+       memset((char *) d->rcv_waitq, 0, sizeof(struct wait_queue *) * n);
+       if (!(d->snd_waitq = (struct wait_queue **)
+             kmalloc(sizeof(struct wait_queue *) * n, GFP_KERNEL))) {
+               printk(KERN_WARNING "register_isdn: Could not alloc snd_waitq\n");
+               kfree(d->rcv_waitq);
+               kfree(d->rpqueue);
+               kfree(d->rcvcount);
+               kfree(d->rcverr);
+               kfree(d);
+               return 0;
+       }
+       memset((char *) d->snd_waitq, 0, sizeof(struct wait_queue *) * n);
+       d->channels = n;
+       d->loaded = 1;
+       d->maxbufsize = i->maxbufsize;
+       d->pktcount = 0;
+       d->stavail = 0;
+       d->running = 0;
+       d->flags = 0;
+       d->interface = i;
+       for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
+               if (!dev->drv[drvidx])
+                       break;
+       i->channels = drvidx;
+
+       if (i->writebuf_skb && (!i->writebuf))
+                i->writebuf = isdn_writebuf_stub;
+       i->rcvcallb_skb = isdn_receive_skb_callback;
+       i->rcvcallb     = isdn_receive_callback;
+       i->statcallb    = isdn_status_callback;
+       if (!strlen(i->id))
+               sprintf(i->id, "line%d", drvidx);
+       save_flags(flags);
+       cli();
+       for (j = 0; j < n; j++)
+               for (k = 0; k < ISDN_MAX_CHANNELS; k++)
+                       if (dev->chanmap[k] < 0) {
+                               dev->chanmap[k] = j;
+                               dev->drvmap[k] = drvidx;
+                               break;
+                       }
+       dev->drv[drvidx] = d;
+       dev->channels += n;
+       strcpy(dev->drvid[drvidx], i->id);
+       isdn_info_update();
+       dev->drivers++;
+       restore_flags(flags);
+       return 1;
+}
+
+/*
+ *****************************************************************************
+ * And now the modules code.
+ *****************************************************************************
+ */
+
+extern int printk(const char *fmt,...);
+
+#ifdef MODULE
+#define isdn_init init_module
+#endif
+
+static char *isdn_getrev(const char *revision)
+{
+       static char rev[20];
+       char *p;
+
+       if ((p = strchr(revision, ':'))) {
+               strcpy(rev, p + 2);
+               p = strchr(rev, '$');
+               *--p = 0;
+       } else
+               strcpy(rev, "???");
+       return rev;
+}
+
+static struct symbol_table isdn_syms = {
+#include <linux/symtab_begin.h>
+        X(register_isdn),
+#include <linux/symtab_end.h>
+};
+
+static void isdn_export_syms(void)
+{
+        register_symtab(&isdn_syms);
+        has_exported = 1;
+}
+
+/*
+ * Allocate and initialize all data, register modem-devices
+ */
+int isdn_init(void)
+{
+       int i;
+
+       sti();
+       if (!(dev = (isdn_dev *) kmalloc(sizeof(isdn_dev), GFP_KERNEL))) {
+               printk(KERN_WARNING "isdn: Could not allocate device-struct.\n");
+               return -EIO;
+       }
+       memset((char *) dev, 0, sizeof(isdn_dev));
+       for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+               dev->drvmap[i] = -1;
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+               dev->chanmap[i] = -1;
+               dev->m_idx[i] = -1;
+               strcpy(dev->num[i], "???");
+       }
+       if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) {
+               printk(KERN_WARNING "isdn: Could not register control devices\n");
+               kfree(dev);
+               return -EIO;
+       }
+       if ((i = isdn_tty_modem_init()) < 0) {
+               printk(KERN_WARNING "isdn: Could not register tty devices\n");
+               if (i == -3)
+                       tty_unregister_driver(&dev->mdm.cua_modem);
+               if (i <= -2)
+                       tty_unregister_driver(&dev->mdm.tty_modem);
+               kfree(dev);
+               unregister_chrdev(ISDN_MAJOR, "isdn");
+               return -EIO;
+       }
+#ifdef CONFIG_ISDN_PPP
+       if (isdn_ppp_init() < 0) {
+               printk(KERN_WARNING "isdn: Could not create PPP-device-structs\n");
+               tty_unregister_driver(&dev->mdm.tty_modem);
+               tty_unregister_driver(&dev->mdm.cua_modem);
+               for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+                       kfree(dev->mdm.info[i].xmit_buf);
+               unregister_chrdev(ISDN_MAJOR, "isdn");
+               kfree(dev);
+               return -EIO;
+       }
+#endif                         /* CONFIG_ISDN_PPP */
+
+        if (!has_exported)
+                isdn_export_syms();
+
+       printk(KERN_NOTICE "ISDN subsystem Rev: %s/%s/%s/%s",
+              isdn_getrev(isdn_revision),
+              isdn_getrev(isdn_tty_revision),
+              isdn_getrev(isdn_net_revision),
+              isdn_getrev(isdn_ppp_revision));
+
+#ifdef MODULE
+       printk(" loaded\n");
+#else
+       printk("\n");
+       isdn_cards_init();
+#endif
+       isdn_info_update();
+       return 0;
+}
+
+#ifdef MODULE
+/*
+ * Unload module
+ */
+void cleanup_module(void)
+{
+       int flags;
+       int i;
+
+#ifdef CONFIG_ISDN_PPP
+       isdn_ppp_cleanup();
+#endif
+       save_flags(flags);
+       cli();
+       if (isdn_net_rmall() < 0) {
+               printk(KERN_WARNING "isdn: net-device busy, remove cancelled\n");
+               restore_flags(flags);
+               return;
+       }
+       if (tty_unregister_driver(&dev->mdm.tty_modem)) {
+               printk(KERN_WARNING "isdn: ttyI-device busy, remove cancelled\n");
+               restore_flags(flags);
+               return;
+       }
+       if (tty_unregister_driver(&dev->mdm.cua_modem)) {
+               printk(KERN_WARNING "isdn: cui-device busy, remove cancelled\n");
+               restore_flags(flags);
+               return;
+       }
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+               kfree(dev->mdm.info[i].xmit_buf - 4);
+       if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) {
+               printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n");
+       } else {
+               del_timer(&dev->timer);
+               kfree(dev);
+               printk(KERN_NOTICE "ISDN-subsystem unloaded\n");
+       }
+       restore_flags(flags);
+}
+#endif
diff --git a/drivers/isdn/isdn_common.h b/drivers/isdn/isdn_common.h
new file mode 100644 (file)
index 0000000..bf772d1
--- /dev/null
@@ -0,0 +1,58 @@
+/* $Id: isdn_common.h,v 1.1 1996/01/10 21:37:19 fritz Exp fritz $
+ *
+ * header for Linux ISDN subsystem, common used funtions and debugging-switches (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ * $Log: isdn_common.h,v $
+ * Revision 1.1  1996/01/10 21:37:19  fritz
+ * Initial revision
+ *
+ */
+
+#undef  ISDN_DEBUG_MODEM_OPEN
+#undef  ISDN_DEBUG_MODEM_IOCTL
+#undef  ISDN_DEBUG_MODEM_WAITSENT
+#undef  ISDN_DEBUG_MODEM_HUP
+#undef  ISDN_DEBUG_MODEM_ICALL
+#undef  ISDN_DEBUG_MODEM_DUMP
+#undef  ISDN_DEBUG_AT
+#undef  ISDN_DEBUG_NET_DUMP
+#undef  ISDN_DEBUG_NET_DIAL
+#undef  ISDN_DEBUG_NET_BUILDHDR
+#undef  ISDN_DEBUG_NET_ICALL
+
+/* Prototypes */
+extern void  isdn_MOD_INC_USE_COUNT(void);
+extern void  isdn_MOD_DEC_USE_COUNT(void);
+extern void  isdn_free_channel(int di, int ch, int usage);
+extern void  isdn_all_eaz(int di, int ch);
+extern int   isdn_dc2minor(int di, int ch);
+extern void  isdn_info_update(void);
+extern char* isdn_map_eaz2msn(char *msn, int di);
+extern void  isdn_timer_ctrl(int tf, int onoff);
+extern void  isdn_unexclusive_channel(int di, int ch);
+extern int   isdn_getnum(char **);
+extern int   isdn_readbchan (int di, int channel, u_char *buf,
+                            u_char *fp, int len, int user);
+extern int   isdn_get_free_channel(int usage, int l2_proto, int l3_proto,
+                                  int pre_dev, int pre_chan);
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+extern void  isdn_dumppkt(char *, u_char *, int, int);
+#endif
diff --git a/drivers/isdn/isdn_net.c b/drivers/isdn/isdn_net.c
new file mode 100644 (file)
index 0000000..96b2dfd
--- /dev/null
@@ -0,0 +1,2283 @@
+/* $Id: isdn_net.c,v 1.4 1996/02/19 15:23:38 fritz Exp fritz $
+ *
+ * Linux ISDN subsystem, network interfaces and related functions (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ * $Log: isdn_net.c,v $
+ * Revision 1.4  1996/02/19 15:23:38  fritz
+ * Bugfix: Sync-PPP packets got compressed twice, when resent due to
+ *         send-queue-full reject.
+ *
+ * Revision 1.3  1996/02/11 02:22:28  fritz
+ * Changed status- receive-callbacks to use pointer-arrays for finding
+ * a corresponding interface instead of looping over all interfaces.
+ * Activate Auto-hangup-timer only when interface is online.
+ * Some bugfixes in the dialing-statemachine.
+ * Lot of bugfixes in sk_buff'ized encapsulation handling.
+ * For speedup connection-setup after dialing, remember sk_buf that triggered
+ * dialing.
+ * Fixed isdn_net_log_packet according to different encapsulations.
+ * Correct ARP-handling for ETHERNET-encapsulation.
+ *
+ * Revision 1.2  1996/01/22 05:05:12  fritz
+ * Changed returncode-logic for isdn_net_start_xmit() and it's
+ * helper-functions.
+ * Changed handling of buildheader for RAWIP and ETHERNET-encapsulation.
+ *
+ * Revision 1.1  1996/01/09 04:12:34  fritz
+ * Initial revision
+ *
+ */
+
+#ifndef STANDALONE
+#include <linux/config.h>
+#endif
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include <linux/if_arp.h>
+#include "isdn_common.h"
+#include "isdn_net.h"
+#ifdef CONFIG_ISDN_PPP
+#include "isdn_ppp.h"
+#endif
+
+/* In ksyms.c, but why not in some .h ??? */
+extern int arp_find(unsigned char *, u32, struct device *, u32,
+                    struct sk_buff *);
+
+/* Prototypes */
+
+int isdn_net_force_dial_lp(isdn_net_local *);
+static int isdn_net_wildmat(char *s, char *p);
+static int isdn_net_start_xmit(struct sk_buff *, struct device *);
+static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *);
+char *isdn_net_revision = "$Revision: 1.4 $";
+
+ /*
+  * Code for raw-networking over ISDN
+  */
+
+static void
+isdn_net_reset(struct device *dev)
+{
+       ulong flags;
+
+       save_flags(flags);
+       cli();                  /* Avoid glitch on writes to CMD regs */
+       dev->interrupt = 0;
+       dev->tbusy = 0;
+       restore_flags(flags);
+}
+
+/* Open/initialize the board. */
+static int
+isdn_net_open(struct device *dev)
+{
+       int i;
+       struct device *p;
+
+       isdn_net_reset(dev);
+       dev->start = 1;
+       /* Fill in the MAC-level header. */
+       for (i = 0; i < ETH_ALEN - sizeof(ulong); i++)
+               dev->dev_addr[i] = 0xfc;
+       memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(ulong));
+
+        /* If this interface has slaves, start them also */
+
+       if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+               while (p) {
+                       isdn_net_reset(p);
+                       p->start = 1;
+                       p = (((isdn_net_local *) p->priv)->slave);
+               }
+       }
+
+       isdn_MOD_INC_USE_COUNT();
+       return 0;
+}
+
+/*
+ * Assign an ISDN-channel to a net-interface
+ */
+static void
+isdn_net_bind_channel(isdn_net_local * lp, int idx)
+{
+        ulong flags;
+
+        save_flags(flags);
+        cli();
+       lp->isdn_device = dev->drvmap[idx];
+       lp->isdn_channel = dev->chanmap[idx];
+        dev->rx_netdev[idx] = lp->netdev;
+        dev->st_netdev[idx] = lp->netdev;
+        restore_flags(flags);
+}
+
+/*
+ * Perform auto-hangup and cps-calculation for net-interfaces.
+ *
+ * auto-hangup:
+ * Increment idle-counter (this counter is reset on any incoming or
+ * outgoing packet), if counter exceeds configured limit either do a
+ * hangup immediately or - if configured - wait until just before the next
+ * charge-info.
+ *
+ * cps-calculation (needed for dynamic channel-bundling):
+ * Since this function is called every second, simply reset the
+ * byte-counter of the interface after copying it to the cps-variable.
+ */
+void
+isdn_net_autohup()
+{
+       isdn_net_dev *p = dev->netdev;
+        int anymore;
+       ulong flags;
+
+       save_flags(flags);
+       cli();
+        anymore = 0;
+       while (p) {
+               isdn_net_local *l = (isdn_net_local *) & (p->local);
+               l->cps = l->transcount;
+               l->transcount = 0;
+               if (dev->net_verbose > 3)
+                       printk(KERN_DEBUG "%s: %d bogocps\n", l->name, l->cps);
+               if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
+                        anymore = 1;
+                       l->huptimer++;
+                       if ((l->onhtime) && (l->huptimer > l->onhtime))
+                               if (l->outgoing) {
+                                       if (l->hupflags & 4) {
+                                               if (l->hupflags & 1)
+                                                       isdn_net_hangup(&p->dev);
+                                               else if (jiffies - l->chargetime > l->chargeint)
+                                                       isdn_net_hangup(&p->dev);
+                                       } else
+                                               isdn_net_hangup(&p->dev);
+                               } else if (l->hupflags & 8)
+                                       isdn_net_hangup(&p->dev);
+               }
+               p = (isdn_net_dev *) p->next;
+       }
+        isdn_timer_ctrl(ISDN_TIMER_NETHANGUP,anymore);
+       restore_flags(flags);
+}
+
+/*
+ * Handle status-messages from ISDN-interfacecard.
+ * This function is called from within the main-status-dispatcher
+ * isdn_status_callback, which itself is called from the lowlevel-driver.
+ * Return: 1 = Event handled, 0 = not for us or unknown Event.
+ */
+int
+isdn_net_stat_callback(int idx, int cmd)
+{
+       isdn_net_dev *p = dev->st_netdev[idx];
+
+       if (p) {
+               isdn_net_local *lp = &(p->local);
+                switch (cmd) {
+                       case ISDN_STAT_BSENT:
+                               /* A packet has successfully been sent out */
+                               if ((lp->flags & ISDN_NET_CONNECTED) &&
+                                   (!lp->dialstate)) {
+                                       lp->stats.tx_packets++;
+                                        if (clear_bit(0,(void*)&(p->dev.tbusy)))
+                                                mark_bh(NET_BH);
+                               }
+                               return 1;
+                       case ISDN_STAT_DCONN:
+                               /* D-Channel is up */
+                               if (lp->dialstate == 4 || lp->dialstate == 7
+                                    || lp->dialstate == 8) {
+                                       lp->dialstate++;
+                                        return 1;
+                                }
+                               break;
+                       case ISDN_STAT_DHUP:
+                               /* Either D-Channel-hangup or error during dialout */
+                               if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
+                                       lp->flags &= ~ISDN_NET_CONNECTED;
+                                       isdn_free_channel(lp->isdn_device, lp->isdn_channel,
+                                                          ISDN_USAGE_NET);
+#ifdef CONFIG_ISDN_PPP
+                                       isdn_ppp_free(lp);
+#endif
+                                       isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+                                       printk(KERN_INFO "%s: remote hangup\n", lp->name);
+                                       printk(KERN_INFO "%s: Chargesum is %d\n", lp->name,
+                                              lp->charge);
+                                       lp->isdn_device = -1;
+                                       lp->isdn_channel = -1;
+                                       dev->st_netdev[idx] = NULL;
+                                       dev->rx_netdev[idx] = NULL;
+                                        return 1;
+                               }
+                                break;
+                       case ISDN_STAT_BCONN:
+                               /* B-Channel is up */
+                               if (lp->dialstate >= 5 && lp->dialstate <= 10) {
+                                       if (lp->dialstate <= 6) {
+                                                dev->usage[idx] |= ISDN_USAGE_OUTGOING;
+                                                isdn_info_update();
+                                       } else
+                                                dev->rx_netdev[idx] = p;
+                                       lp->dialstate = 0;
+                                        isdn_timer_ctrl(ISDN_TIMER_NETHANGUP,1);
+                                       printk(KERN_INFO "isdn_net: %s connected\n", lp->name);
+                                       /* If first Chargeinfo comes before B-Channel connect,
+                                        * we correct the timestamp here.
+                                        */
+                                       lp->chargetime = jiffies;
+                                        /* Immediately send first skb to speed up arp */
+                                        if (lp->first_skb) {
+                                                if (!(isdn_net_xmit(&p->dev,lp,lp->first_skb)))
+                                                        lp->first_skb = NULL;
+                                        }
+                                        return 1;
+                               }
+                               break;
+                       case ISDN_STAT_NODCH:
+                               /* No D-Channel avail. */
+                               if (lp->dialstate == 4) {
+                                       lp->dialstate--;
+                                        return 1;
+                                }
+                               break;
+                       case ISDN_STAT_CINF:
+                               /* Charge-info from TelCo. Calculate interval between
+                                * charge-infos and set timestamp for last info for
+                                * usage by isdn_net_autohup()
+                                */
+                               lp->charge++;
+                               if (lp->hupflags & 2) {
+                                       lp->hupflags &= ~1;
+                                       lp->chargeint = jiffies - lp->chargetime - (2 * HZ);
+                               }
+                               if (lp->hupflags & 1)
+                                       lp->hupflags |= 2;
+                               lp->chargetime = jiffies;
+                               return 1;
+                }
+       }
+        return 0;
+}
+
+/*
+ * Check, if a numer contains wilcard-characters, in which case it
+ * is for incoming purposes only.
+ */
+static int
+isdn_net_checkwild(char *num)
+{
+       return ((strchr(num, '?')) ||
+               (strchr(num, '*')) ||
+               (strchr(num, '[')) ||
+               (strchr(num, ']')) ||
+               (strchr(num, '^')));
+}
+
+/*
+ * Perform dialout for net-interfaces and timeout-handling for
+ * D-Channel-up and B-Channel-up Messages.
+ * This function is initially called from within isdn_net_start_xmit() or
+ * or isdn_net_find_icall() after initializing the dialstate for an
+ * interface. If further calls are needed, the function schedules itself
+ * for a timer-callback via isdn_timer_function().
+ * The dialstate is also affected by incoming status-messages from
+ * the ISDN-Channel which are handled in isdn_net_stat_callback() above.
+ */
+void
+isdn_net_dial(void)
+{
+       isdn_net_dev *p = dev->netdev;
+       int anymore = 0;
+       int i;
+       isdn_ctrl cmd;
+
+       while (p) {
+               switch (p->local.dialstate) {
+               case 0:
+                       /* Nothing to do for this interface */
+                       break;
+               case 1:
+                       /* Initiate dialout. Set phone-number-pointer to first number
+                        * of interface.
+                        */
+                       p->local.dial = p->local.phone[1];
+                       anymore = 1;
+                       p->local.dialstate++;
+                       break;
+                       /* Prepare dialing. Clear EAZ, then set EAZ. */
+               case 2:
+                       cmd.driver = p->local.isdn_device;
+                       cmd.arg = p->local.isdn_channel;
+                       cmd.command = ISDN_CMD_CLREAZ;
+                       dev->drv[p->local.isdn_device]->interface->command(&cmd);
+                       sprintf(cmd.num, "%s", isdn_map_eaz2msn(p->local.msn, cmd.driver));
+                       cmd.command = ISDN_CMD_SETEAZ;
+                       dev->drv[p->local.isdn_device]->interface->command(&cmd);
+                       p->local.dialretry = 0;
+                       anymore = 1;
+                       p->local.dialstate++;
+                       break;
+               case 3:
+                       /* Setup interface, dial current phone-number, switch to next number.
+                        * If list of phone-numbers is exhausted, increment
+                        * retry-counter.
+                        */
+                       cmd.driver = p->local.isdn_device;
+                       cmd.command = ISDN_CMD_SETL2;
+                       cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
+                       dev->drv[p->local.isdn_device]->interface->command(&cmd);
+                       cmd.driver = p->local.isdn_device;
+                       cmd.command = ISDN_CMD_SETL3;
+                       cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
+                       dev->drv[p->local.isdn_device]->interface->command(&cmd);
+                       cmd.driver = p->local.isdn_device;
+                       cmd.arg = p->local.isdn_channel;
+                       p->local.huptimer = 0;
+                       p->local.outgoing = 1;
+                       p->local.hupflags |= 1;
+                       if (!strcmp(p->local.dial->num, "LEASED")) {
+                               p->local.dialstate = 4;
+                               printk(KERN_INFO "%s: Open leased line ...\n", p->local.name);
+                       } else {
+                               cmd.command = ISDN_CMD_DIAL;
+                               sprintf(cmd.num, "%s,%s,7,0", p->local.dial->num,
+                                 isdn_map_eaz2msn(p->local.msn, cmd.driver));
+                               i = isdn_dc2minor(p->local.isdn_device, p->local.isdn_channel);
+                               if (i >= 0) {
+                                       strcpy(dev->num[i], p->local.dial->num);
+                                       isdn_info_update();
+                               }
+                               printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name,
+                                p->local.dialretry, p->local.dial->num);
+                               /*
+                                * Switch to next number or back to start if at end of list.
+                                */
+                               if (!(p->local.dial = (isdn_net_phone *) p->local.dial->next)) {
+                                       p->local.dial = p->local.phone[1];
+                                       p->local.dialretry++;
+                               }
+                               p->local.dtimer = 0;
+#ifdef ISDN_DEBUG_NET_DIAL
+                               printk(KERN_DEBUG "dial: d=%d c=%d\n", p->local.isdn_device,
+                                      p->local.isdn_channel);
+#endif
+                               dev->drv[p->local.isdn_device]->interface->command(&cmd);
+                       }
+                       anymore = 1;
+                       p->local.dialstate++;
+                       break;
+               case 4:
+                       /* Wait for D-Channel-connect or incoming call, if passive
+                        * callback configured. If timeout and max retries not
+                        * reached, switch back to state 3.
+                        */
+                       if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+                               if (p->local.dialretry < p->local.dialmax) {
+                                       p->local.dialstate = 3;
+                               } else
+                                       isdn_net_hangup(&p->dev);
+                       anymore = 1;
+                       break;
+               case 5:
+                       /* Got D-Channel-Connect, send B-Channel-request */
+                       cmd.driver = p->local.isdn_device;
+                       cmd.arg = p->local.isdn_channel;
+                       cmd.command = ISDN_CMD_ACCEPTB;
+                       anymore = 1;
+                       p->local.dtimer = 0;
+                       p->local.dialstate++;
+                       dev->drv[p->local.isdn_device]->interface->command(&cmd);
+                       break;
+               case 6:
+                       /* Wait for B- or D-Channel-connect. If timeout,
+                         * switch back to state 3.
+                        */
+#ifdef ISDN_DEBUG_NET_DIAL
+                       printk(KERN_DEBUG "dialtimer2: %d\n", p->local.dtimer);
+#endif
+                       if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+                               p->local.dialstate = 3;
+                       anymore = 1;
+                       break;
+               case 7:
+                       /* Got incoming Call, setup L2 and L3 protocols,
+                         * then wait for D-Channel-connect
+                         */
+#ifdef ISDN_DEBUG_NET_DIAL
+                       printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
+#endif
+                       cmd.driver = p->local.isdn_device;
+                       cmd.command = ISDN_CMD_SETL2;
+                       cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
+                       dev->drv[p->local.isdn_device]->interface->command(&cmd);
+                       cmd.driver = p->local.isdn_device;
+                       cmd.command = ISDN_CMD_SETL3;
+                       cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
+                       dev->drv[p->local.isdn_device]->interface->command(&cmd);
+                       if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT15)
+                               isdn_net_hangup(&p->dev);
+                       else {
+                               anymore = 1;
+                                p->local.dialstate++;
+                        }
+                       break;
+               case 9:
+                       /* Got incoming D-Channel-Connect, send B-Channel-request */
+                       cmd.driver = p->local.isdn_device;
+                       cmd.arg = p->local.isdn_channel;
+                       cmd.command = ISDN_CMD_ACCEPTB;
+                       dev->drv[p->local.isdn_device]->interface->command(&cmd);
+                       anymore = 1;
+                       p->local.dtimer = 0;
+                       p->local.dialstate++;
+                       break;
+               case 8:
+               case 10:
+                       /*  Wait for B- or D-channel-connect */
+#ifdef ISDN_DEBUG_NET_DIAL
+                       printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
+#endif
+                       if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+                               isdn_net_hangup(&p->dev);
+                       else
+                               anymore = 1;
+                       break;
+               default:
+                       printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n",
+                              p->local.dialstate, p->local.name);
+               }
+               p = (isdn_net_dev *) p->next;
+       }
+       isdn_timer_ctrl(ISDN_TIMER_NETDIAL, anymore);
+}
+
+/*
+ * Send-data-helpfunction for net-interfaces
+ */
+int
+isdn_net_send(u_char * buf, int di, int ch, int len)
+{
+       int l;
+
+       if ((l = dev->drv[di]->interface->writebuf(di, ch, buf, len, 0)) == len)
+               return 1;
+       /* Device driver queue full (or packet > 4000 bytes, should never
+        * happen)
+        */
+       if (l == -EINVAL)
+               printk(KERN_ERR "isdn_net: Huh, sending pkt too big!\n");
+       return 0;
+}
+
+/*
+ * Perform hangup for a net-interface.
+ */
+void
+isdn_net_hangup(struct device *d)
+{
+       isdn_net_local *lp = (isdn_net_local *) d->priv;
+       isdn_ctrl cmd;
+       ulong flags;
+
+       save_flags(flags);
+       cli();
+       if (lp->flags & ISDN_NET_CONNECTED) {
+               printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name);
+               lp->dialstate = 0;
+                dev->rx_netdev[isdn_dc2minor(lp->isdn_device,lp->isdn_channel)] = NULL;
+                dev->st_netdev[isdn_dc2minor(lp->isdn_device,lp->isdn_channel)] = NULL;
+               isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
+#ifdef CONFIG_ISDN_PPP
+               isdn_ppp_free(lp);
+#endif
+               lp->flags &= ~ISDN_NET_CONNECTED;
+               cmd.driver = lp->isdn_device;
+               cmd.command = ISDN_CMD_HANGUP;
+               cmd.arg = lp->isdn_channel;
+               (void) dev->drv[cmd.driver]->interface->command(&cmd);
+               printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge);
+               isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+               lp->isdn_device = -1;
+               lp->isdn_channel = -1;
+       }
+       restore_flags(flags);
+}
+
+typedef struct {
+       unsigned short source;
+       unsigned short dest;
+} ip_ports;
+
+static void
+isdn_net_log_packet(u_char * buf, isdn_net_local * lp)
+{
+        u_char *p = buf;
+       unsigned short proto = ETH_P_IP;
+        int data_ofs;
+        int len;
+       ip_ports *ipp;
+       char addinfo[100];
+
+        addinfo[0] = '\0';
+        switch (lp->p_encap) {
+                case ISDN_NET_ENCAP_IPTYP:
+                        proto = ntohs(*(unsigned short *)&buf[0]);
+                        p = &buf[2];
+                        break;
+                case ISDN_NET_ENCAP_ETHER:
+                        proto = ntohs(*(unsigned short *)&buf[12]);
+                        p = &buf[14];
+                        break;
+                case ISDN_NET_ENCAP_CISCOHDLC:
+                        proto = ntohs(*(unsigned short *)&buf[2]);
+                        p = &buf[4];
+                        break;
+                case ISDN_NET_ENCAP_SYNCPPP:
+                        len = 4;
+#ifdef CONFIG_ISDN_MPP
+                        if (lp->ppp_minor!=-1) {
+                                if (ippp_table[lp->ppp_minor].mpppcfg &
+                                    SC_MP_PROT) {
+                                        if (ippp_table[lp->ppp_minor].mpppcfg &
+                                            SC_OUT_SHORT_SEQ)
+                                                len = 7;
+                                        else
+                                                len = 9;
+                                }
+                        }
+#endif
+                        p = &buf[len];
+                        break;
+        }
+       data_ofs = ((p[0] & 15) * 4);
+        switch (proto) {
+               case ETH_P_IP:
+                       switch (p[9]) {
+                                case 1:
+                                        strcpy(addinfo, " ICMP");
+                                        break;
+                                case 2:
+                                        strcpy(addinfo, " IGMP");
+                                        break;
+                                case 4:
+                                        strcpy(addinfo, " IPIP");
+                                        break;
+                                case 6:
+                                        ipp = (ip_ports *) (&p[data_ofs]);
+                                        sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source),
+                                                ntohs(ipp->dest));
+                                        break;
+                                case 8:
+                                        strcpy(addinfo, " EGP");
+                                        break;
+                                case 12:
+                                        strcpy(addinfo, " PUP");
+                                        break;
+                                case 17:
+                                        ipp = (ip_ports *) (&p[data_ofs]);
+                                        sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source),
+                                                ntohs(ipp->dest));
+                                        break;
+                                case 22:
+                                        strcpy(addinfo, " IDP");
+                                        break;
+                       }
+                       printk(KERN_INFO "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
+                              p[12], p[13], p[14], p[15],
+                              p[16], p[17], p[18], p[19],
+                              addinfo);
+                       break;
+               case ETH_P_ARP:
+                       printk(KERN_INFO "OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
+                              p[14], p[15], p[16], p[17],
+                              p[24], p[25], p[26], p[27]);
+                       break;
+        }
+}
+
+/*
+ * Generic routine to send out an skbuf.
+ * If lowlevel-device does not support supports skbufs, use
+ * standard send-routine, else sind directly.
+ *
+ * Return: 0 on success, !0 on failure.
+ * Side-effects: ndev->tbusy is cleared on success.
+ */
+int
+isdn_net_send_skb(struct device *ndev, isdn_net_local *lp,
+                  struct sk_buff *skb)
+{
+       int ret;
+       
+       lp->transcount += skb->len;
+       if (dev->drv[lp->isdn_device]->interface->writebuf_skb) 
+               ret = dev->drv[lp->isdn_device]->interface->
+                       writebuf_skb(lp->isdn_device, lp->isdn_channel, skb);
+       else {        
+               if ((ret = isdn_net_send(skb->data, lp->isdn_device,
+                                    lp->isdn_channel, skb->len)))
+                       dev_kfree_skb(skb, FREE_WRITE);
+       }
+
+       if (ret)
+               clear_bit(0, (void *)&(ndev->tbusy));
+       return (!ret);
+}                                      
+       
+
+/*
+ *  Helper function for isdn_net_start_xmit.
+ *  When called, the connection is already established.
+ *  Based on cps-calculation, check if device is overloaded.
+ *  If so, and if a slave exists, trigger dialing for it.
+ *  If any slave is online, deliver packets using a simple round robin
+ *  scheme.
+ *
+ *  Return: 0 on success, !0 on failure.
+ */
+
+static int
+isdn_net_xmit(struct device *ndev, isdn_net_local *lp, struct sk_buff *skb) 
+{
+        int ret;
+
+       /* For the other encaps the header has allready been built */
+#ifdef CONFIG_ISDN_PPP
+       if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+               return (isdn_ppp_xmit(skb, ndev));
+#endif         
+       /* Reset hangup-timeout */
+       lp->huptimer = 0;
+       if (lp->cps > 7000) {
+               /* Device overloaded */
+
+               /* 
+                * Packet-delivery via round-robin over master 
+                * and all connected slaves.
+                */
+               if (lp->master)
+                       /* Slaves always deliver themselves */
+                       ret = isdn_net_send_skb(ndev, lp, skb);
+               else {
+                       isdn_net_local *slp = (isdn_net_local *) (lp->srobin->priv);
+                       /* Master delivers via srobin and maintains srobin */
+                       if (lp->srobin == ndev)
+                               ret = isdn_net_send_skb(ndev, lp, skb);
+                       else
+                               ret = ndev->tbusy = isdn_net_start_xmit(skb, lp->srobin);
+                       lp->srobin = (slp->slave) ? slp->slave : ndev;
+                       slp = (isdn_net_local *) (lp->srobin->priv);
+                       if (!((slp->flags & ISDN_NET_CONNECTED) && (slp->dialstate == 0)))
+                               lp->srobin = ndev;
+               }
+               /* Slave-startup using delay-variable */
+               if (lp->slave) {
+                       if (!lp->sqfull) {
+                               /* First time overload: set timestamp only */
+                               lp->sqfull = 1;
+                               lp->sqfull_stamp = jiffies;
+                       } 
+                       else {
+                               /* subsequent overload: if slavedelay exceeded, start dialing */
+                               if ((jiffies - lp->sqfull_stamp) > lp->slavedelay)
+                                       isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
+                       }
+               }
+       } 
+       else {
+               /* Not overloaded, deliver locally */
+               ret = isdn_net_send_skb(ndev, lp, skb);
+               if (lp->sqfull && ((jiffies - lp->sqfull_stamp) > (lp->slavedelay + (10*HZ) )))
+                       lp->sqfull = 0;
+       }
+       return ret;
+}
+
+/*
+ * Try sending a packet.
+ * If this interface isn't connected to a ISDN-Channel, find a free channel,
+ * and start dialing.
+ */
+int
+isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev)
+{
+       isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+
+
+       if (ndev->tbusy) {
+               if (jiffies - ndev->trans_start < 20)
+                       return 1;
+                if (!lp->dialstate)
+                        lp->stats.tx_errors++;
+               ndev->tbusy = 0;
+               ndev->trans_start = jiffies;
+       }
+       if (skb == NULL) {
+               dev_tint(ndev);
+               return 0;
+       }
+       /* Avoid timer-based retransmission conflicts. */
+       if (set_bit(0, (void *) &ndev->tbusy) != 0)
+               printk(KERN_WARNING
+                       "%s: Transmitter access conflict.\n",
+                       ndev->name);
+       else {
+               u_char *buf = skb->data;
+#ifdef ISDN_DEBUG_NET_DUMP
+               isdn_dumppkt("S:", buf, skb->len, 40);
+#endif
+               if (!(lp->flags & ISDN_NET_CONNECTED)) {
+                       int chi;
+                       if (lp->phone[1]) {
+                               ulong flags;
+                               save_flags(flags);
+                               cli();
+                               /* Grab a free ISDN-Channel */
+                               if ((chi = 
+                                     isdn_get_free_channel(ISDN_USAGE_NET,
+                                                           lp->l2_proto,
+                                                           lp->l3_proto,
+                                                           lp->pre_device,
+                                                           lp->pre_channel)) < 0) {
+                                        printk(KERN_WARNING
+                                               "isdn_net_start_xmit: No channel for %s\n",
+                                               ndev->name);
+                                       restore_flags(flags);
+                                       return 1;
+                               }
+                                /* Log packet, which triggered dialing */
+                               if (dev->net_verbose)
+                                        isdn_net_log_packet(buf, lp);
+                               lp->dialstate = 1;
+                               lp->flags |= ISDN_NET_CONNECTED;
+                               /* Connect interface with channel */
+                               isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+                               if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+                                       if (isdn_ppp_bind(lp) < 0) {
+                                               lp->dialstate = 0;
+                                               isdn_free_channel(lp->isdn_device,
+                                                                  lp->isdn_channel,
+                                                                  ISDN_USAGE_NET);
+                                                restore_flags(flags);
+                                               return 1;
+                                       }
+#endif
+                                /* remember first skb to speed up arp
+                                 * when using encap ETHER
+                                 */
+                                lp->first_skb = skb;
+                               /* Initiate dialing */
+                               isdn_net_dial();
+                                ndev->tbusy = 0;
+                               restore_flags(flags);
+                                return 0;
+                       } else {
+                                /*
+                                 * Having no phone-number is a permanent
+                                 * failure or misconfiguration.
+                                 * Instead of just dropping, we should also
+                                 * have the upper layers to respond
+                                 * with an ICMP No route to host in the
+                                 * future, however at the moment, i don't
+                                 * know a simple way to do that.
+                                 * The same applies, when the telecom replies
+                                 * "no destination" to our dialing-attempt.
+                                 */
+                                printk(KERN_WARNING
+                                       "isdn_net: No phone number for %s, packet dropped\n",
+                                       ndev->name);
+                               dev_kfree_skb(skb, FREE_WRITE);
+                               ndev->tbusy = 0;
+                                return 0;
+                       }
+               } else {
+                        /* Connection is established, try sending */
+                       ndev->trans_start = jiffies;
+                       if (!lp->dialstate) {
+                                if (lp->first_skb) {
+                                        if (isdn_net_xmit(ndev,lp,lp->first_skb))
+                                                return 1;
+                                        lp->first_skb = NULL;
+                                }
+                               return(isdn_net_xmit(ndev, lp, skb));
+                       } else
+                               ndev->tbusy = 1;
+               }
+       }
+       return 1;
+}
+
+/*
+ * Shutdown a net-interface.
+ */
+static int
+isdn_net_close(struct device *dev)
+{
+       struct device *p;
+
+       dev->tbusy = 1;
+       dev->start = 0;
+       isdn_net_hangup(dev);
+       if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+               /* If this interface has slaves, stop them also */
+               while (p) {
+                       isdn_net_hangup(p);
+                       p->tbusy = 1;
+                       p->start = 0;
+                       p = (((isdn_net_local *) p->priv)->slave);
+               }
+       }
+       isdn_MOD_DEC_USE_COUNT();
+       return 0;
+}
+
+/*
+ * Get statistics
+ */
+static struct enet_statistics *
+ isdn_net_get_stats(struct device *dev)
+{
+       isdn_net_local *lp = (isdn_net_local *) dev->priv;
+       return &lp->stats;
+}
+
+/*      This is simply a copy from std. eth.c EXCEPT we pull ETH_HLEN
+ *      instead of dev->hard_header_len off. This is done, because the
+ *      lowlevel-driver has already pulled of it's stuff, when we get
+ *      here and this routine only get's called whit p_encap == ETHER.
+ *      Determine the packet's protocol ID. The rule here is that we
+ *      assume 802.3 if the type field is short enough to be a length.
+ *      This is normal practice and works for any 'now in use' protocol.
+ */
+
+unsigned short isdn_net_type_trans(struct sk_buff *skb, struct device *dev)
+{
+        struct ethhdr *eth;
+        unsigned char *rawp;
+        
+        skb_pull(skb,ETH_HLEN);
+        eth= skb->mac.ethernet;
+        
+        if(*eth->h_dest&1) {
+                if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+                        skb->pkt_type=PACKET_BROADCAST;
+                else
+                        skb->pkt_type=PACKET_MULTICAST;
+        }
+        
+        /*
+         *      This ALLMULTI check should be redundant by 1.4
+         *      so don't forget to remove it.
+         */
+        
+        else if (dev->flags&(IFF_PROMISC|IFF_ALLMULTI)) {
+                if (memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
+                        skb->pkt_type=PACKET_OTHERHOST;
+        }
+
+        if (ntohs(eth->h_proto) >= 1536)
+                return eth->h_proto;
+
+        rawp = skb->data;
+
+        /*
+         *      This is a magic hack to spot IPX packets. Older Novell breaks
+         *      the protocol design and runs IPX over 802.3 without an 802.2 LLC
+         *      layer. We look for FFFF which isnt a used 802.2 SSAP/DSAP. This
+         *      won't work for fault tolerant netware but does for the rest.
+         */
+        if (*(unsigned short *)rawp == 0xFFFF)
+                return htons(ETH_P_802_3);
+        /*
+         *      Real 802.2 LLC
+         */
+        return htons(ETH_P_802_2);
+}
+
+/* 
+ * Got a packet from ISDN-Channel.
+ */
+static void
+isdn_net_receive(struct device *ndev, struct sk_buff *skb)
+{
+       isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+#ifdef CONFIG_ISDN_PPP
+        isdn_net_local *olp = lp;  /* original 'lp' */
+#endif
+
+       lp->transcount += skb->len;
+       lp->stats.rx_packets++;
+       lp->huptimer = 0;
+
+       if (lp->master) {
+               /* Bundling: If device is a slave-device, deliver to master, also
+                * handle master's statistics and hangup-timeout
+                */
+               ndev = lp->master;
+               lp = (isdn_net_local *) ndev->priv;
+               lp->stats.rx_packets++;
+               lp->huptimer = 0;
+       }
+
+       skb->dev = ndev;
+       skb->pkt_type = PACKET_HOST;
+        skb->mac.raw = skb->data;
+#ifdef ISDN_DEBUG_NET_DUMP
+        isdn_dumppkt("R:", skb->data, skb->len, 40);
+#endif
+       switch (lp->p_encap) {
+       case ISDN_NET_ENCAP_ETHER:
+               /* Ethernet over ISDN */
+               skb->protocol = isdn_net_type_trans(skb,ndev);
+               break;
+       case ISDN_NET_ENCAP_RAWIP:
+               /* RAW-IP without MAC-Header */
+               skb->protocol = htons(ETH_P_IP);
+               break;
+       case ISDN_NET_ENCAP_CISCOHDLC:
+               /* CISCO-HDLC IP with type field and  fake I-frame-header */
+               skb_pull(skb, 2);
+               /* Fall through */
+       case ISDN_NET_ENCAP_IPTYP:
+               /* IP with type field */
+               skb->protocol = *(unsigned short *)&(skb->data[0]);
+               skb_pull(skb, 2);
+                if (*(unsigned short *)skb->data == 0xFFFF)
+                        skb->protocol = htons(ETH_P_802_3);
+               break;
+#ifdef CONFIG_ISDN_PPP
+       case ISDN_NET_ENCAP_SYNCPPP:
+               isdn_ppp_receive(lp->netdev, olp, skb);
+               return;
+#endif
+        default:
+                printk(KERN_WARNING "%s: unknown encapsulation, dropping\n",
+                       lp->name);
+                kfree_skb(skb,FREE_READ);
+                return;
+       }
+       netif_rx(skb);
+       return;
+}
+
+/*
+ * A packet arrived via ISDN. Search interface-chain for a corresponding
+ * interface. If found, deliver packet to receiver-function and return 1,
+ * else return 0.
+ */
+int
+isdn_net_receive_callback(int idx, u_char * buf, int len)
+{
+       isdn_net_dev *p = dev->rx_netdev[idx];
+       struct sk_buff *skb;
+
+       if (p) {
+               isdn_net_local *lp = &p->local;
+               if ((lp->flags & ISDN_NET_CONNECTED) &&
+                   (!lp->dialstate)) {
+                       skb = dev_alloc_skb(len);
+                       if (skb == NULL) {
+                               printk(KERN_WARNING "out of memory\n");
+                               return 0;
+                       }
+                       memcpy(skb_put(skb, len), buf, len);
+                       isdn_net_receive(&p->dev, skb);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/*
+ *  receive callback for lovlevel drivers, which support skb's
+ */
+
+int
+isdn_net_rcv_skb(int idx, struct sk_buff *skb) 
+{
+       isdn_net_dev *p = dev->rx_netdev[idx];
+
+       if (p) {
+               isdn_net_local *lp = &p->local;
+               if ((lp->flags & ISDN_NET_CONNECTED) &&
+                   (!lp->dialstate)) {
+                       isdn_net_receive(&p->dev, skb);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static int
+my_eth_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+              void *daddr, void *saddr, unsigned len)
+{
+       struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
+
+       /* 
+        * Set the protocol type. For a packet of type ETH_P_802_3 we
+         * put the length here instead. It is up to the 802.2 layer to
+         * carry protocol information.
+        */
+       
+       if(type!=ETH_P_802_3) 
+               eth->h_proto = htons(type);
+       else
+               eth->h_proto = htons(len);
+
+       /*
+        *      Set the source hardware address. 
+        */
+       if(saddr)
+               memcpy(eth->h_source,saddr,dev->addr_len);
+       else
+               memcpy(eth->h_source,dev->dev_addr,dev->addr_len);
+
+       /*
+        *      Anyway, the loopback-device should never use this function... 
+        */
+
+       if (dev->flags & IFF_LOOPBACK) {
+               memset(eth->h_dest, 0, dev->addr_len);
+               return(dev->hard_header_len);
+       }
+       
+       if(daddr) {
+               memcpy(eth->h_dest,daddr,dev->addr_len);
+               return dev->hard_header_len;
+       }
+       
+       return -dev->hard_header_len;
+}
+
+/*
+ *  build an header
+ *  depends on encaps that is beeing used.
+ */
+static int
+isdn_net_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+                void *daddr, void *saddr, unsigned plen)
+{
+       isdn_net_local *lp = dev->priv;
+       ushort len = 0;
+       
+       switch (lp->p_encap) {
+                case ISDN_NET_ENCAP_ETHER:
+                        len = my_eth_header(skb, dev, type, daddr, saddr, plen);
+                        break;
+                case ISDN_NET_ENCAP_RAWIP:
+                        printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n");
+                       len = 0;
+                        break;
+                case ISDN_NET_ENCAP_IPTYP:
+                        /* ethernet type field */
+                        *((ushort*) skb_push(skb, 2)) = htons(type);
+                        len = 2;
+                        break;
+                case ISDN_NET_ENCAP_CISCOHDLC:
+                       skb_push(skb, 4);
+                        skb->data[0] = 0x0f;
+                        skb->data[1] = 0x00;
+                        *((ushort*)&skb->data[2]) = htons(type);
+                        len = 4;
+                        break;
+#ifdef CONFIG_ISDN_PPP
+                case ISDN_NET_ENCAP_SYNCPPP:
+                        /* reserve space to be filled in isdn_ppp_xmit */
+                        len = 4;
+#ifdef CONFIG_ISDN_MPP
+                        if (lp->ppp_minor!=-1) {
+                                if (ippp_table[lp->ppp_minor].mpppcfg &
+                                    SC_MP_PROT) {
+                                        if (ippp_table[lp->ppp_minor].mpppcfg &
+                                            SC_OUT_SHORT_SEQ)
+                                                len = 7;
+                                        else
+                                                len = 9;
+                                }
+                        }
+#endif
+                        /* Initialize first 4 bytes to a value, which is
+                         * guaranteed to be invalid. Need that to check
+                         * for already compressed packets in isdn_ppp_xmit().
+                         */
+                        *((unsigned long *)skb_push(skb, len)) = 0;
+                        break;
+#endif
+       }
+       return len;
+}
+
+/* We don't need to send arp, because we have point-to-point connections. */
+
+static int
+isdn_net_rebuild_header(void *buff, struct device *dev, ulong dst,
+                        struct sk_buff *skb)
+{
+       isdn_net_local *lp = dev->priv;
+        int ret = 0;
+
+        if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
+                struct ethhdr *eth = (struct ethhdr *)buff;
+                
+                /*
+                 *      Only ARP/IP is currently supported
+                 */
+                
+                if(eth->h_proto != htons(ETH_P_IP)) {
+                        printk(KERN_WARNING
+                               "isdn_net_rebuild_header: Don't know how to resolve type %d addresses?\n",
+                               (int)eth->h_proto);
+                        memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+                        return 0;
+                }
+                /*
+                 *      Try and get ARP to resolve the header.
+                 */
+#ifdef CONFIG_INET       
+                ret = arp_find((unsigned char *)&(eth->h_dest), dst, dev, dev->pa_addr,skb)? 1 : 0;
+#endif  
+        }
+       return ret;
+}
+
+/*
+ * Interface-setup. (called just after registering a new interface)
+ */
+static int
+isdn_net_init(struct device *ndev)
+{
+       ushort max_hlhdr_len = 0;
+        isdn_net_local *lp = (isdn_net_local *)ndev->priv;
+       int drvidx, i;
+
+       if (ndev == NULL) {
+               printk(KERN_WARNING "isdn_net_init: dev = NULL!\n");
+               return -ENODEV;
+       }
+       if (ndev->priv == NULL) {
+               printk(KERN_WARNING "isdn_net_init: dev->priv = NULL!\n");
+               return -ENODEV;
+       }
+
+        ether_setup(ndev);
+        lp->org_hcb               = ndev->header_cache_bind;
+        lp->org_hcu               = ndev->header_cache_update;
+
+       /* Setup the generic properties */
+
+        ndev->hard_header         = NULL;
+        ndev->header_cache_bind   = NULL;
+        ndev->header_cache_update = NULL;
+        ndev->mtu                 = 1500;
+        ndev->flags               = IFF_NOARP;
+        ndev->family              = AF_INET;
+        ndev->type                = ARPHRD_ETHER;  
+        ndev->addr_len            = ETH_ALEN;
+        ndev->pa_addr             = 0;
+        ndev->pa_brdaddr          = 0;
+        ndev->pa_mask             = 0;
+        ndev->pa_alen             = 4;
+
+        for (i = 0; i < ETH_ALEN; i++)
+                ndev->broadcast[i]=0xff;
+
+       for (i = 0; i < DEV_NUMBUFFS; i++)
+                skb_queue_head_init(&ndev->buffs[i]);
+       
+       /* The ISDN-specific entries in the device structure. */
+       ndev->open                = &isdn_net_open;
+       ndev->hard_start_xmit     = &isdn_net_start_xmit;
+
+       /* 
+        *  up till binding we ask the protocol layer to reserve as much
+        *  as we migth need for HL layer
+         */
+       
+       for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
+               if (dev->drv[drvidx])
+                       if (max_hlhdr_len < dev->drv[drvidx]->interface->hl_hdrlen)
+                               max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen;
+
+       ndev->hard_header_len     = ETH_HLEN + max_hlhdr_len;
+
+       ndev->stop                = &isdn_net_close;
+       ndev->get_stats           = &isdn_net_get_stats;
+       ndev->rebuild_header      = &isdn_net_rebuild_header;
+
+#ifdef CONFIG_ISDN_PPP
+       ndev->do_ioctl            = isdn_ppp_dev_ioctl;
+#endif
+       return 0;
+}
+
+/*
+ * I picked the pattern-matching-functions from an old GNU-tar version (1.10)
+ * It was originaly written and put to PD by rs@mirror.TMC.COM (Rich Salz)
+ */
+
+static int
+isdn_net_Star(char *s, char *p)
+{
+       while (isdn_net_wildmat(s, p) == 0)
+               if (*++s == '\0')
+                       return (0);
+       return (1);
+}
+
+/*
+ * Shell-type Pattern-matching for incoming caller-Ids
+ * This function gets a string in s and checks, if it matches the pattern
+ * given in p. It returns 1 on success, 0 otherwise.
+ *
+ * Posible Patterns:
+ *
+ * '?'     matches one character
+ * '*'     matches zero or more characters
+ * [xyz]   matches the set of charcters in brackets.
+ * [^xyz]  matches any single character not in the set of characters
+ */
+
+static int
+isdn_net_wildmat(char *s, char *p)
+{
+       register int last;
+       register int matched;
+       register int reverse;
+
+       for (; *p; s++, p++)
+               switch (*p) {
+                        case '\\':
+                                /*
+                                 * Literal match with following character,
+                                 * fall through.
+                                 */
+                                p++;
+                        default:
+                                if (*s != *p)
+                                        return (0);
+                                continue;
+                        case '?':
+                                /* Match anything. */
+                                if (*s == '\0')
+                                        return (0);
+                                continue;
+                        case '*':
+                                /* Trailing star matches everything. */
+                                return (*++p ? isdn_net_Star(s, p) : 1);
+                        case '[':
+                                /* [^....] means inverse character class. */
+                                if ((reverse = (p[1] == '^')))
+                                        p++;
+                                for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
+                                        /* This next line requires a good C compiler. */
+                                        if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
+                                                matched = 1;
+                                if (matched == reverse)
+                                        return (0);
+                                continue;
+               }
+       return (*s == '\0');
+}
+
+static void
+isdn_net_swapbind(int drvidx)
+{
+       isdn_net_dev *p;
+
+#ifdef ISDN_DEBUG_NET_ICALL
+       printk(KERN_DEBUG "n_fi: swapping ch of %d\n", drvidx);
+#endif
+       p = dev->netdev;
+       while (p) {
+               if (p->local.pre_device == drvidx)
+                       switch (p->local.pre_channel) {
+                       case 0:
+                               p->local.pre_channel = 1;
+                               break;
+                       case 1:
+                               p->local.pre_channel = 0;
+                               break;
+                       }
+               p = (isdn_net_dev *) p->next;
+       }
+}
+
+static void
+isdn_net_swap_usage(int i1, int i2)
+{
+       int u1 = dev->usage[i1] & ISDN_USAGE_EXCLUSIVE;
+       int u2 = dev->usage[i2] & ISDN_USAGE_EXCLUSIVE;
+
+#ifdef ISDN_DEBUG_NET_ICALL
+       printk(KERN_DEBUG "n_fi: usage of %d and %d\n", i1, i2);
+#endif
+       dev->usage[i1] &= ~ISDN_USAGE_EXCLUSIVE;
+       dev->usage[i1] |= u2;
+       dev->usage[i2] &= ~ISDN_USAGE_EXCLUSIVE;
+       dev->usage[i2] |= u1;
+       isdn_info_update();
+}
+
+/*
+ * An incoming call-request has arrived.
+ * Search the interface-chain for an aproppriate interface.
+ * If found, connect the interface to the ISDN-channel and initiate
+ * D- and B-Channel-setup. If secure-flag is set, accept only
+ * configured phone-numbers. If callback-flag is set, initiate
+ * callback-dialing.
+ *
+ * Return-Value: 0 = No appropriate interface for this call.
+ *               1 = Call accepted
+ *               2 = Do callback
+ */
+int
+isdn_net_find_icall(int di, int ch, int idx, char *num)
+{
+       char *eaz;
+       int si1;
+       int si2;
+       int ematch;
+       int swapped;
+       int sidx = 0;
+       isdn_net_dev *p;
+       isdn_net_phone *n;
+       ulong flags;
+       char nr[31];
+       char *s;
+
+       /* Search name in netdev-chain */
+       save_flags(flags);
+       cli();
+       if (num[0] == ',') {
+               nr[0] = '0';
+               strncpy(&nr[1], num, 30);
+               printk(KERN_WARNING "isdn_net: Incoming call without OAD, assuming '0'\n");
+       } else
+               strncpy(nr, num, 30);
+       s = strtok(nr, ",");
+       s = strtok(NULL, ",");
+       if (!s) {
+               printk(KERN_WARNING "isdn_net: Incoming callinfo garbled, ignored: %s\n",
+                      num);
+               restore_flags(flags);
+               return 0;
+       }
+       si1 = (int)simple_strtoul(s,NULL,10);
+       s = strtok(NULL, ",");
+       if (!s) {
+               printk(KERN_WARNING "isdn_net: Incoming callinfo garbled, ignored: %s\n",
+                      num);
+               restore_flags(flags);
+               return 0;
+       }
+       si2 = (int)simple_strtoul(s,NULL,10);
+       eaz = strtok(NULL, ",");
+       if (!eaz) {
+               printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n");
+               eaz = "0";
+       }
+       if (dev->net_verbose > 1)
+               printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz);
+       /* Accept only calls with Si1 = 7 (Data-Transmission) */
+       if (si1 != 7) {
+               if (dev->net_verbose > 1)
+                       printk(KERN_INFO "isdn_net: Service-Indicator not 7, ignored\n");
+               return 0;
+       }
+       n = (isdn_net_phone *) 0;
+       p = dev->netdev;
+       ematch = 0;
+#ifdef ISDN_DEBUG_NET_ICALL
+       printk(KERN_DEBUG "n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx,
+              dev->usage[idx]);
+#endif
+       swapped = 0;
+       while (p) {
+               /* If last check has trigered as binding-swap, revert it */
+               switch (swapped) {
+               case 2:
+                       isdn_net_swap_usage(idx, sidx);
+                       /* fall through */
+               case 1:
+                       isdn_net_swapbind(di);
+                       break;
+               }
+               swapped = 0;
+               if (!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz))
+                       ematch = 1;
+#ifdef ISDN_DEBUG_NET_ICALL
+               printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
+                      p->local.name, p->local.msn, p->local.flags, p->local.dialstate);
+#endif
+               if ((!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) &&       /* EAZ is matching   */
+                   (((!(p->local.flags & ISDN_NET_CONNECTED)) &&       /* but not connected */
+                     (USG_NONE(dev->usage[idx]))) ||   /* and ch. unused or */
+                    (((p->local.dialstate == 4) &&     /* if dialing        */
+                      (!(p->local.flags & ISDN_NET_CALLBACK)))         /* but no callback   */
+                    ))) {
+#ifdef ISDN_DEBUG_NET_ICALL
+                       printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
+                              p->local.pre_device, p->local.pre_channel);
+#endif
+                       if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) {
+                               if ((p->local.pre_channel != ch) ||
+                                   (p->local.pre_device != di)) {
+                                       /* Here we got a problem:
+                                          If using an ICN-Card, an incoming call is always signaled on
+                                          on the first channel of the card, if both channels are
+                                          down. However this channel may be bound exclusive. If the
+                                          second channel is free, this call should be accepted.
+                                          The solution is horribly but it runs, so what:
+                                          We exchange the exclusive bindings of the two channels, the
+                                          corresponding variables in the interface-structs.
+                                        */
+                                       if (ch == 0) {
+                                               sidx = isdn_dc2minor(di, 1);
+#ifdef ISDN_DEBUG_NET_ICALL
+                                               printk(KERN_DEBUG "n_fi: ch is 0\n");
+#endif
+                                               if (USG_NONE(dev->usage[sidx])) {
+                                                       /* Second Channel is free, now see if it is bound
+                                                          exclusive too. */
+                                                       if (dev->usage[sidx] & ISDN_USAGE_EXCLUSIVE) {
+#ifdef ISDN_DEBUG_NET_ICALL
+                                                               printk(KERN_DEBUG "n_fi: 2nd channel is down and bound\n");
+#endif
+                                                               /* Yes, swap bindings only, if the original
+                                                                  binding is bound to channel 1 of this driver */
+                                                               if ((p->local.pre_device == di) &&
+                                                                   (p->local.pre_channel == 1)) {
+                                                                       isdn_net_swapbind(di);
+                                                                       swapped = 1;
+                                                               } else {
+                                                                       /* ... else iterate next device */
+                                                                       p = (isdn_net_dev *) p->next;
+                                                                       continue;
+                                                               }
+                                                       } else {
+#ifdef ISDN_DEBUG_NET_ICALL
+                                                               printk(KERN_DEBUG "n_fi: 2nd channel is down and unbound\n");
+#endif
+                                                               /* No, swap always and swap excl-usage also */
+                                                               isdn_net_swap_usage(idx, sidx);
+                                                               isdn_net_swapbind(di);
+                                                               swapped = 2;
+                                                       }
+                                                       /* Now check for exclusive binding again */
+#ifdef ISDN_DEBUG_NET_ICALL
+                                                       printk(KERN_DEBUG "n_fi: final check\n");
+#endif
+                                                       if ((dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) &&
+                                                           ((p->local.pre_channel != ch) ||
+                                                            (p->local.pre_device != di))) {
+#ifdef ISDN_DEBUG_NET_ICALL
+                                                               printk(KERN_DEBUG "n_fi: final check failed\n");
+#endif
+                                                               p = (isdn_net_dev *) p->next;
+                                                               continue;
+                                                       }
+                                               }
+                                       } else {
+                                               /* We are already on the second channel, so nothing to do */
+#ifdef ISDN_DEBUG_NET_ICALL
+                                               printk(KERN_DEBUG "n_fi: already on 2nd channel\n");
+#endif
+                                               p = (isdn_net_dev *) p->next;
+                                               continue;
+                                       }
+                               }
+                       }
+#ifdef ISDN_DEBUG_NET_ICALL
+                       printk(KERN_DEBUG "n_fi: match2\n");
+#endif
+                       n = p->local.phone[0];
+                       if (p->local.flags & ISDN_NET_SECURE) {
+                               while (n) {
+                                       if (isdn_net_wildmat(nr, n->num))
+                                               break;
+                                       n = (isdn_net_phone *) n->next;
+                               }
+                       }
+                       if (n || (!(p->local.flags & ISDN_NET_SECURE))) {
+                               isdn_net_local *lp = &(p->local);
+#ifdef ISDN_DEBUG_NET_ICALL
+                               printk(KERN_DEBUG "n_fi: match3\n");
+#endif
+                               /* Here we got an interface matched, now see if it is up.
+                                * If not, reject the call actively.
+                                */
+                               if (!p->dev.start) {
+                                       restore_flags(flags);
+                                       printk(KERN_INFO "%s: incoming call, if down -> rejected\n",
+                                              lp->name);
+                                       return 3;
+                               }
+                               /* Interface is up, now see if it's a slave. If so, see if
+                                * it's master and parent slave is online. If not, reject the call.
+                                */
+                               if (lp->master) {
+                                       isdn_net_local *mlp = (isdn_net_local *) lp->master->priv;
+                                       printk(KERN_DEBUG "ICALLslv: %s\n", lp->name);
+                                       printk(KERN_DEBUG "master=%s\n", mlp->name);
+                                       if (mlp->flags & ISDN_NET_CONNECTED) {
+                                               printk(KERN_DEBUG "master online\n");
+                                               /* Master is online, find parent-slave (master if first slave) */
+                                               while (mlp->slave) {
+                                                       if ((isdn_net_local *) mlp->slave->priv == lp)
+                                                               break;
+                                                       mlp = (isdn_net_local *) mlp->slave->priv;
+                                               }
+                                       } else
+                                               printk(KERN_DEBUG "master offline\n");
+                                       /* Found parent, if it's offline iterate next device */
+                                       printk(KERN_DEBUG "mlpf: %d\n", mlp->flags & ISDN_NET_CONNECTED);
+                                       if (!(mlp->flags & ISDN_NET_CONNECTED)) {
+                                               p = (isdn_net_dev *) p->next;
+                                               continue;
+                                       }
+                               }
+                               if (lp->flags & ISDN_NET_CALLBACK) {
+                                       int chi;
+                                       printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n",
+                                              lp->name, nr, eaz);
+                                       if (lp->phone[1]) {
+                                               /* Grab a free ISDN-Channel */
+                                               if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
+                                                           lp->l3_proto,
+                                                         lp->pre_device,
+                                                lp->pre_channel)) < 0) {
+                                                       printk(KERN_WARNING "isdn_net_find_icall: No channel for %s\n", lp->name);
+                                                       restore_flags(flags);
+                                                       return 0;
+                                               }
+                                               /* Setup dialstate. */
+                                               lp->dialstate = 1;
+                                               lp->flags |= ISDN_NET_CONNECTED;
+                                               /* Connect interface with channel */
+                                               isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+                                               if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+                                                       if (isdn_ppp_bind(lp) < 0) {
+                                                               isdn_free_channel(p->local.isdn_device, p->local.isdn_channel,
+                                                                            ISDN_USAGE_NET);
+                                                               lp->dialstate = 0;
+                                                               restore_flags(flags);
+                                                               return 0;
+                                                       }
+#endif
+                                               /* Initiate dialing by returning 2 */
+                                               restore_flags(flags);
+                                               return 2;
+                                       } else
+                                               printk(KERN_WARNING "isdn_net: %s: No phone number\n", lp->name);
+                                       restore_flags(flags);
+                                       return 0;
+                               } else {
+                                       printk(KERN_DEBUG "%s: call from %s -> %s accepted\n", lp->name, nr,
+                                              eaz);
+#if 0
+/* why is this a CONFIG_ISDN_PPP feature ??? */
+#ifdef CONFIG_ISDN_PPP
+                                       if (p->local.isdn_device != -1) {
+                                               isdn_free_channel(p->local.isdn_device, p->local.isdn_channel,
+                                                        ISDN_USAGE_NET);
+                                       }
+#endif
+#endif
+                                       /* if this interface is dialing, it does it probably on a different
+                                          device, so free this device */
+                                       if (p->local.dialstate == 4)
+                                               isdn_free_channel(p->local.isdn_device, p->local.isdn_channel,
+                                                        ISDN_USAGE_NET);
+                                       dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+                                       dev->usage[idx] |= ISDN_USAGE_NET;
+                                       strcpy(dev->num[idx], nr);
+                                       isdn_info_update();
+                                        dev->st_netdev[idx] = lp->netdev;
+                                       p->local.isdn_device = di;
+                                       p->local.isdn_channel = ch;
+                                       p->local.ppp_minor = -1;
+                                       p->local.flags |= ISDN_NET_CONNECTED;
+                                       p->local.dialstate = 7;
+                                       p->local.dtimer = 0;
+                                       p->local.outgoing = 0;
+                                       p->local.huptimer = 0;
+                                       p->local.hupflags |= 1;
+#ifdef CONFIG_ISDN_PPP
+                                       if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+                                               if (isdn_ppp_bind(lp) < 0) {
+                                                       isdn_free_channel(p->local.isdn_device, p->local.isdn_channel,
+                                                        ISDN_USAGE_NET);
+                                                       lp->dialstate = 0;
+                                                       restore_flags(flags);
+                                                       return 0;
+                                               }
+#endif
+                                       restore_flags(flags);
+                                       return 1;
+                               }
+                       }
+               }
+               p = (isdn_net_dev *) p->next;
+       }
+       /* If none of configured EAZ/MSN matched and not verbose, be silent */
+       if (ematch || dev->net_verbose)
+               printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz);
+       restore_flags(flags);
+       return 0;
+}
+
+/*
+ * Search list of net-interfaces for an interface with given name.
+ */
+isdn_net_dev *
+ isdn_net_findif(char *name)
+{
+       isdn_net_dev *p = dev->netdev;
+
+       while (p) {
+               if (!strcmp(p->local.name, name))
+                       return p;
+               p = (isdn_net_dev *) p->next;
+       }
+       return (isdn_net_dev *) NULL;
+}
+
+/*
+ * Force a net-interface to dial out.
+ * This is called from the userlevel-routine below or
+ * from isdn_net_start_xmit().
+ */
+int isdn_net_force_dial_lp(isdn_net_local * lp)
+{
+       if ((!(lp->flags & ISDN_NET_CONNECTED)) && !lp->dialstate) {
+               int chi;
+               if (lp->phone[1]) {
+                       ulong flags;
+                       save_flags(flags);
+                       cli();
+                       /* Grab a free ISDN-Channel */
+                       if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
+                                                   lp->l3_proto,
+                                                   lp->pre_device,
+                                                lp->pre_channel)) < 0) {
+                               printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name);
+                               restore_flags(flags);
+                               return -EAGAIN;
+                       }
+                       lp->dialstate = 1;
+                       lp->flags |= ISDN_NET_CONNECTED;
+                       /* Connect interface with channel */
+                       isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+                       if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+                               if (isdn_ppp_bind(lp) < 0) {
+                                       lp->dialstate = 0;
+                                       isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
+                                       return 1;
+                               }
+#endif
+                       /* Initiate dialing */
+                       isdn_net_dial();
+                       restore_flags(flags);
+                       return 0;
+               } else
+                       return -EINVAL;
+       } else
+               return -EBUSY;
+}
+
+/*
+ * Force a net-interface to dial out.
+ * This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
+ */
+int 
+isdn_net_force_dial(char *name)
+{
+       isdn_net_dev *p = isdn_net_findif(name);
+
+       if (!p)
+               return -ENODEV;
+       return (isdn_net_force_dial_lp(&p->local));
+}
+
+/*
+ * Allocate a new network-interface and initialize it's data structures.
+ */
+char *
+ isdn_net_new(char *name, struct device *master)
+{
+       isdn_net_dev *netdev;
+
+       /* Avoid creating an existing interface */
+       if (isdn_net_findif(name)) {
+               printk(KERN_WARNING "isdn_net: interface %s already exists\n", name);
+               return NULL;
+       }
+       if (!(netdev = (isdn_net_dev *) kmalloc(sizeof(isdn_net_dev), GFP_KERNEL))) {
+               printk(KERN_WARNING "isdn_net: Could not allocate net-device\n");
+               return NULL;
+       }
+       memset(netdev, 0, sizeof(isdn_net_dev));
+       if (name == NULL)
+               strcpy(netdev->local.name, "         ");
+       else
+               strcpy(netdev->local.name, name);
+       netdev->dev.name      = netdev->local.name;
+       netdev->dev.priv      = &netdev->local;
+       netdev->dev.init      = isdn_net_init;
+       netdev->local.p_encap = ISDN_NET_ENCAP_RAWIP;
+       if (master) {
+               /* Device shall be a slave */
+               struct device *p = (((isdn_net_local *) master->priv)->slave);
+               struct device *q = master;
+
+               netdev->local.master = master;
+               /* Put device at end of slave-chain */
+               while (p) {
+                       q = p;
+                       p = (((isdn_net_local *) p->priv)->slave);
+               }
+               ((isdn_net_local *) q->priv)->slave = &(netdev->dev);
+               q->interrupt = 0;
+               q->tbusy = 0;
+               q->start = master->start;
+       } else {
+               /* Device shall be a master */
+               if (register_netdev(&netdev->dev) != 0) {
+                       printk(KERN_WARNING "isdn_net: Could not register net-device\n");
+                       kfree(netdev);
+                       return NULL;
+               }
+       }
+       netdev->local.magic = ISDN_NET_MAGIC;
+
+#ifdef CONFIG_ISDN_PPP
+       netdev->mp_last = NULL; /* mpqueue is empty */
+       netdev->ib.next_num = 0;
+       netdev->ib.last = NULL;
+#endif
+       netdev->queue = &netdev->local;
+       netdev->local.last = &netdev->local;
+       netdev->local.netdev = netdev;
+       netdev->local.next = &netdev->local;
+
+       netdev->local.isdn_device = -1;
+       netdev->local.isdn_channel = -1;
+       netdev->local.pre_device = -1;
+       netdev->local.pre_channel = -1;
+       netdev->local.exclusive = -1;
+       netdev->local.ppp_minor = -1;
+       netdev->local.l2_proto = ISDN_PROTO_L2_X75I;
+       netdev->local.l3_proto = ISDN_PROTO_L3_TRANS;
+       netdev->local.slavedelay = 10 * HZ;
+       netdev->local.srobin = &netdev->dev;
+       netdev->local.hupflags = 8;     /* Do hangup even on incoming calls */
+       netdev->local.onhtime = 10;     /* Default hangup-time for saving costs
+                                          of those who forget configuring this */
+       /* The following should be configurable via ioctl */
+       netdev->local.dialmax = 1;
+       /* Put into to netdev-chain */
+       netdev->next = (void *) dev->netdev;
+       dev->netdev = netdev;
+       return netdev->dev.name;
+}
+
+char *
+ isdn_net_newslave(char *parm)
+{
+       char *p = strchr(parm, ',');
+       isdn_net_dev *n;
+       char newname[10];
+
+       if (p) {
+               /* Slave-Name MUST not be empty */
+               if (!strlen(p + 1))
+                       return NULL;
+               strcpy(newname, p + 1);
+               *p = 0;
+               /* Master must already exist */
+               if (!(n = isdn_net_findif(parm)))
+                       return NULL;
+               /* Master must be a real interface, not a slave */
+               if (n->local.master)
+                       return NULL;
+               return (isdn_net_new(newname, &(n->dev)));
+       }
+       return NULL;
+}
+
+/*
+ * Set interface-parameters.
+ * Allways set all parameters, so the user-level application is responsible
+ * for not overwriting existing setups. It has to get the current
+ * setup first, if only selected parameters are to be changed.
+ */
+int isdn_net_setcfg(isdn_net_ioctl_cfg * cfg)
+{
+       isdn_net_dev *p = isdn_net_findif(cfg->name);
+       ulong features;
+       int i;
+       int drvidx;
+       int chidx;
+       char drvid[25];
+
+       if (p) {
+               /* See if any registered driver supports the features we want */
+               features = (1 << cfg->l2_proto) | (256 << cfg->l3_proto);
+               for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+                       if (dev->drv[i])
+                               if ((dev->drv[i]->interface->features & features) == features)
+                                       break;
+               if (i == ISDN_MAX_DRIVERS) {
+                       printk(KERN_WARNING "isdn_net: No driver with selected features\n");
+                       return -ENODEV;
+               }
+                if ((p->local.p_encap != cfg->p_encap) &&
+                    ((p->local.p_encap == ISDN_NET_ENCAP_RAWIP) ||
+                     (cfg->p_encap == ISDN_NET_ENCAP_RAWIP)        ))
+                        if (p->dev.start) {
+                                printk(KERN_WARNING
+                                       "%s: cannot change encap when if is up\n",
+                                       p->local.name);
+                                return -EBUSY;
+                        }
+#ifndef CONFIG_ISDN_PPP
+                if (cfg->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+                        printk(KERN_WARNING "%s: SyncPPP not configured\n",
+                               p->local.name);
+                        return -EINVAL;
+                }
+#endif
+               if (strlen(cfg->drvid)) {
+                       /* A bind has been requested ... */
+                       char *c,*e;
+
+                       drvidx = -1;
+                       chidx = -1;
+                       strcpy(drvid, cfg->drvid);
+                       if ((c = strchr(drvid, ','))) {
+                               /* The channel-number is appended to the driver-Id with a comma */
+                               chidx = (int)simple_strtoul(c + 1,&e,10);
+                               if (e == c)
+                                       chidx = -1;
+                               *c = '\0';
+                       }
+                       for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+                               /* Lookup driver-Id in array */
+                               if (!(strcmp(dev->drvid[i], drvid))) {
+                                       drvidx = i;
+                                       break;
+                               }
+                       if ((drvidx == -1) || (chidx == -1))
+                               /* Either driver-Id or channel-number invalid */
+                               return -ENODEV;
+               } else {
+                       /* Parameters are valid, so get them */
+                       drvidx = p->local.pre_device;
+                       chidx = p->local.pre_channel;
+               }
+               if (cfg->exclusive > 0) {
+                       int flags;
+
+                       /* If binding is exclusive, try to grab the channel */
+                       save_flags(flags);
+                       if ((i = isdn_get_free_channel(ISDN_USAGE_NET, p->local.l2_proto,
+                                                 p->local.l3_proto,
+                                                 drvidx,
+                                                 chidx)) < 0) {
+                               /* Grab failed, because desired channel is in use */
+                               p->local.exclusive = -1;
+                               restore_flags(flags);
+                               return -EBUSY;
+                       }
+                       /* All went ok, so update isdninfo */
+                       dev->usage[i] = ISDN_USAGE_EXCLUSIVE;
+                       isdn_info_update();
+                       restore_flags(flags);
+                       p->local.exclusive = i;
+               } else {
+                       /* Non-exclusive binding or unbind. */
+                       p->local.exclusive = -1;
+                       if ((p->local.pre_device != -1) && (cfg->exclusive == -1)) {
+                               isdn_unexclusive_channel(p->local.pre_device, p->local.pre_channel);
+                               drvidx = -1;
+                               chidx = -1;
+                       }
+               }
+               strcpy(p->local.msn, cfg->eaz);
+               p->local.pre_device  = drvidx;
+               p->local.pre_channel = chidx;
+               p->local.onhtime     = cfg->onhtime;
+               p->local.charge      = cfg->charge;
+               p->local.l2_proto    = cfg->l2_proto;
+               p->local.l3_proto    = cfg->l3_proto;
+               p->local.slavedelay  = cfg->slavedelay * HZ;
+               p->local.p_encap     = cfg->p_encap;
+               if (cfg->secure)
+                       p->local.flags |= ISDN_NET_SECURE;
+               else
+                       p->local.flags &= ~ISDN_NET_SECURE;
+               if (cfg->callback)
+                       p->local.flags |= ISDN_NET_CALLBACK;
+               else
+                       p->local.flags &= ~ISDN_NET_CALLBACK;
+               if (cfg->chargehup)
+                       p->local.hupflags |= 4;
+               else
+                       p->local.hupflags &= ~4;
+               if (cfg->ihup)
+                       p->local.hupflags |= 8;
+               else
+                       p->local.hupflags &= ~8;
+                if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) {
+                        p->dev.hard_header         = NULL;
+                        p->dev.header_cache_bind   = NULL;
+                        p->dev.header_cache_update = NULL;
+                        p->dev.flags               = IFF_NOARP;
+                } else {
+                        p->dev.hard_header = isdn_net_header;
+                        if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) {
+                                p->dev.header_cache_bind   = p->local.org_hcb;
+                                p->dev.header_cache_update = p->local.org_hcu;
+                                p->dev.flags = IFF_BROADCAST | IFF_MULTICAST;
+                        } else {
+                                p->dev.header_cache_bind   = NULL;
+                                p->dev.header_cache_update = NULL;
+                                p->dev.flags               = IFF_NOARP;
+                        }
+                }
+                return 0;
+       }
+       return -ENODEV;
+}
+
+/*
+ * Perform get-interface-parameters.ioctl
+ */
+int isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
+{
+       isdn_net_dev *p = isdn_net_findif(cfg->name);
+
+       if (p) {
+               strcpy(cfg->eaz, p->local.msn);
+               cfg->exclusive = p->local.exclusive;
+               if (p->local.pre_device >= 0) {
+                       sprintf(cfg->drvid, "%s,%d", dev->drvid[p->local.pre_device],
+                               p->local.pre_channel);
+               } else
+                       cfg->drvid[0] = '\0';
+               cfg->onhtime = p->local.onhtime;
+               cfg->charge = p->local.charge;
+               cfg->l2_proto = p->local.l2_proto;
+               cfg->l3_proto = p->local.l3_proto;
+               cfg->p_encap = p->local.p_encap;
+               cfg->secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0;
+               cfg->callback = (p->local.flags & ISDN_NET_CALLBACK) ? 1 : 0;
+               cfg->chargehup = (p->local.hupflags & 4) ? 1 : 0;
+               cfg->ihup = (p->local.hupflags & 8) ? 1 : 0;
+               cfg->slavedelay = p->local.slavedelay / HZ;
+               if (p->local.slave)
+                       strcpy(cfg->slave, ((isdn_net_local *) p->local.slave->priv)->name);
+               else
+                       cfg->slave[0] = '\0';
+               if (p->local.master)
+                       strcpy(cfg->master, ((isdn_net_local *) p->local.master->priv)->name);
+               else
+                       cfg->master[0] = '\0';
+               return 0;
+       }
+       return -ENODEV;
+}
+
+/*
+ * Add a phone-number to an interface.
+ */
+int isdn_net_addphone(isdn_net_ioctl_phone * phone)
+{
+       isdn_net_dev *p = isdn_net_findif(phone->name);
+       isdn_net_phone *n;
+
+       if (isdn_net_checkwild(phone->phone) && (phone->outgoing & 1))
+               return -EINVAL;
+       if (p) {
+               if (!(n = (isdn_net_phone *) kmalloc(sizeof(isdn_net_phone), GFP_KERNEL)))
+                       return -ENOMEM;
+               strcpy(n->num, phone->phone);
+               n->next = p->local.phone[phone->outgoing & 1];
+               p->local.phone[phone->outgoing & 1] = n;
+               return 0;
+       }
+       return -ENODEV;
+}
+
+/*
+ * Return a string of all phone-numbers of an interface.
+ */
+int isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones)
+{
+       isdn_net_dev *p = isdn_net_findif(phone->name);
+       int inout = phone->outgoing & 1;
+       int more = 0;
+       int count = 0;
+       isdn_net_phone *n;
+       int flags;
+       int ret;
+
+       if (!p)
+               return -ENODEV;
+       save_flags(flags);
+       cli();
+       inout &= 1;
+       n = p->local.phone[inout];
+        if (n)
+                count++;
+       while (n) {
+               if (more) {
+                       put_fs_byte(' ', phones++);
+                       count++;
+               }
+               if ((ret = verify_area(VERIFY_WRITE, (void *) phones, strlen(n->num) + 1))) {
+                       restore_flags(flags);
+                       return ret;
+               }
+               memcpy_tofs(phones, n->num, strlen(n->num) + 1);
+               phones += strlen(n->num);
+               count += strlen(n->num);
+               n = n->next;
+               more = 1;
+       }
+       restore_flags(flags);
+       return count;
+}
+
+/*
+ * Delete a phone-number from an interface.
+ */
+
+int isdn_net_delphone(isdn_net_ioctl_phone * phone)
+{
+       isdn_net_dev *p = isdn_net_findif(phone->name);
+       int inout = phone->outgoing & 1;
+       isdn_net_phone *n;
+       isdn_net_phone *m;
+
+       if (p) {
+               n = p->local.phone[inout];
+               m = NULL;
+               while (n) {
+                       if (!strcmp(n->num, phone->phone)) {
+                               if (m)
+                                       m->next = n->next;
+                               else
+                                       p->local.phone[inout] = n->next;
+                               kfree(n);
+                               return 0;
+                       }
+                       m = n;
+                       n = (isdn_net_phone *) n->next;
+               }
+               return -EINVAL;
+       }
+       return -ENODEV;
+}
+
+/*
+ * Delete all phone-numbers of an interface.
+ */
+static int isdn_net_rmallphone(isdn_net_dev * p)
+{
+       isdn_net_phone *n;
+       isdn_net_phone *m;
+       int flags;
+       int i;
+
+       save_flags(flags);
+       cli();
+       for (i = 0; i < 2; i++) {
+               n = p->local.phone[i];
+               while (n) {
+                       m = n->next;
+                       kfree(n);
+                       n = m;
+               }
+               p->local.phone[i] = NULL;
+       }
+       restore_flags(flags);
+       return 0;
+}
+
+/*
+ * Force a hangup of a network-interface.
+ */
+int isdn_net_force_hangup(char *name)
+{
+       isdn_net_dev *p = isdn_net_findif(name);
+       int flags;
+       struct device *q;
+
+       if (p) {
+               save_flags(flags);
+               cli();
+               if (p->local.isdn_device < 0) {
+                       restore_flags(flags);
+                       return 1;
+               }
+               isdn_net_hangup(&p->dev);
+               q = p->local.slave;
+               /* If this interface has slaves, do a hangup for them also. */
+               while (q) {
+                       isdn_net_hangup(q);
+                       q = (((isdn_net_local *) q->priv)->slave);
+               }
+               restore_flags(flags);
+               return 0;
+       }
+       return -ENODEV;
+}
+
+/*
+ * Helper-function for isdn_net_rm: Do the real work.
+ */
+static int isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q)
+{
+       int flags;
+
+       save_flags(flags);
+       cli();
+       if (p->local.master) {
+               /* If it's a slave, it may be removed even if it is busy. However
+                * it has to be hung up first.
+                */
+               isdn_net_hangup(&p->dev);
+               p->dev.start = 0;
+       }
+       if (p->dev.start) {
+               restore_flags(flags);
+               return -EBUSY;
+       }
+       /* Free all phone-entries */
+       isdn_net_rmallphone(p);
+       /* If interface is bound exclusive, free channel-usage */
+       if (p->local.exclusive != -1)
+               isdn_unexclusive_channel(p->local.pre_device, p->local.pre_channel);
+       if (p->local.master) {
+               /* It's a slave-device, so update master's slave-pointer if necessary */
+               if (((isdn_net_local *) (p->local.master->priv))->slave == &p->dev)
+                       ((isdn_net_local *) (p->local.master->priv))->slave = p->local.slave;
+       } else
+               /* Unregister only if it's a master-device */
+               unregister_netdev(&p->dev);
+       /* Unlink device from chain */
+       if (q)
+               q->next = p->next;
+       else
+               dev->netdev = p->next;
+       if (p->local.slave) {
+               /* If this interface has a slave, remove it also */
+               char *slavename = ((isdn_net_local *) (p->local.slave->priv))->name;
+               isdn_net_dev *n = dev->netdev;
+               q = NULL;
+               while (n) {
+                       if (!strcmp(n->local.name, slavename)) {
+                               isdn_net_realrm(n, q);
+                               break;
+                       }
+                       q = n;
+                       n = (isdn_net_dev *) n->next;
+               }
+       }
+       /* If no more net-devices remain, disable auto-hangup timer */
+       if (dev->netdev == NULL)
+               isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
+       restore_flags(flags);
+
+#ifdef CONFIG_ISDN_PPP
+       isdn_ppp_free_mpqueue(p);
+#endif
+       kfree(p);
+
+       return 0;
+}
+
+/*
+ * Remove a single network-interface.
+ */
+int isdn_net_rm(char *name)
+{
+       isdn_net_dev *p;
+       isdn_net_dev *q;
+
+       /* Search name in netdev-chain */
+       p = dev->netdev;
+       q = NULL;
+       while (p) {
+               if (!strcmp(p->local.name, name))
+                       return (isdn_net_realrm(p, q));
+               q = p;
+               p = (isdn_net_dev *) p->next;
+       }
+       /* If no more net-devices remain, disable auto-hangup timer */
+       if (dev->netdev == NULL)
+               isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
+       return -ENODEV;
+}
+
+/*
+ * Remove all network-interfaces
+ */
+int isdn_net_rmall(void)
+{
+       int flags;
+       int ret;
+
+       /* Walk through netdev-chain */
+       save_flags(flags);
+       cli();
+       while (dev->netdev) {
+               if (!dev->netdev->local.master) {
+                       /* Remove master-devices only, slaves get removed with their master */
+                       if ((ret = isdn_net_realrm(dev->netdev, NULL))) {
+                               restore_flags(flags);
+                               return ret;
+                       }
+               }
+       }
+       dev->netdev = NULL;
+       restore_flags(flags);
+       return 0;
+}
+
+
+
+
+
+
+
diff --git a/drivers/isdn/isdn_net.h b/drivers/isdn/isdn_net.h
new file mode 100644 (file)
index 0000000..80da035
--- /dev/null
@@ -0,0 +1,49 @@
+/* $Id: isdn_net.h,v 1.1 1996/02/11 02:35:13 fritz Exp fritz $
+ *
+ * header for Linux ISDN subsystem, network releted funtions (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ * $Log: isdn_net.h,v $
+ * Revision 1.1  1996/02/11 02:35:13  fritz
+ * Initial revision
+ *
+ */
+
+extern char*         isdn_net_new(char *, struct device *);
+extern char*         isdn_net_newslave(char *);
+extern int           isdn_net_rm(char *);
+extern int           isdn_net_rmall(void);
+extern int           isdn_net_stat_callback(int, int);
+extern int           isdn_net_receive_callback(int, u_char *, int);
+extern int           isdn_net_setcfg(isdn_net_ioctl_cfg *);
+extern int           isdn_net_getcfg(isdn_net_ioctl_cfg *);
+extern int           isdn_net_addphone(isdn_net_ioctl_phone *);
+extern int           isdn_net_getphones(isdn_net_ioctl_phone *, char *);
+extern int           isdn_net_delphone(isdn_net_ioctl_phone *);
+extern int           isdn_net_find_icall(int, int, int, char *);
+extern void          isdn_net_hangup(struct device *);
+extern void          isdn_net_dial(void);
+extern void          isdn_net_autohup(void);
+extern int           isdn_net_force_hangup(char *);
+extern int           isdn_net_force_dial(char *);
+extern isdn_net_dev* isdn_net_findif(char *);
+extern int           isdn_net_send_skb(struct device *, isdn_net_local *, 
+                                     struct sk_buff *);
+extern int           isdn_net_rcv_skb(int, struct sk_buff *); 
diff --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c
new file mode 100644 (file)
index 0000000..c940768
--- /dev/null
@@ -0,0 +1,1231 @@
+/* $Id: isdn_ppp.c,v 1.4 1996/02/19 15:25:50 fritz Exp fritz $
+ *
+ * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
+ *
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ * $Log: isdn_ppp.c,v $
+ * Revision 1.4  1996/02/19 15:25:50  fritz
+ * Bugfix: Sync-PPP packets got compressed twice, when resent due to
+ * send-queue-full reject.
+ *
+ * Revision 1.3  1996/02/11 02:27:12  fritz
+ * Lot of Bugfixes my Michael.
+ * Moved calls to skb_push() into isdn_net_header()
+ * Fixed a possible race-condition in isdn_ppp_timer_timeout().
+ *
+ * Revision 1.2  1996/01/22 05:08:06  fritz
+ * Merged in Michael's patches for MP.
+ * Minor changes in isdn_ppp_xmit.
+ *
+ * Revision 1.1  1996/01/09 04:11:29  fritz
+ * Initial revision
+ *
+ */
+
+/* TODO: right tbusy handling when using MP */
+
+#ifndef STANDALONE
+#include <linux/config.h>
+#endif
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_ppp.h"
+#include "isdn_net.h"
+
+#ifndef PPP_IPX
+#define PPP_IPX 0x002b 
+#endif
+/* Prototypes */
+static int isdn_ppp_fill_rq(char *buf, int len, int minor);
+static int isdn_ppp_hangup(int);
+static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
+                       struct sk_buff *skb, int proto);
+static int isdn_ppp_if_get_unit(char **namebuf);
+
+#ifdef CONFIG_ISDN_MPP
+static int isdn_ppp_bundle(int, int);
+static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask);
+static void isdn_ppp_cleanup_queue(isdn_net_dev * dev, long min);
+static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb,
+               int BEbyte, int *sqno, int min_sqno);
+#endif
+
+char *isdn_ppp_revision              = "$Revision: 1.4 $";
+struct ippp_struct *ippp_table = (struct ippp_struct *) 0;
+
+extern int isdn_net_force_dial_lp(isdn_net_local *);
+
+int isdn_ppp_free(isdn_net_local * lp)
+{
+       if (lp->ppp_minor < 0)
+               return 0;
+
+#ifdef CONFIG_ISDN_MPP
+       if(lp->master)
+       {
+               isdn_net_dev *p = dev->netdev;
+               lp->last->next = lp->next;
+               lp->next->last = lp->last;
+               if(lp->netdev->queue == lp)
+                       lp->netdev->queue = lp->next;
+                lp->next = lp->last = lp;
+               while(p) {
+                       if(lp == &p->local) {
+                               lp->netdev = p;
+                               break;
+                       }
+                       p=p->next;
+               }
+       } else {
+                lp->netdev->ib.bundled = 0;
+               /* last link: free mpqueue, free sqqueue ? */
+       }
+
+#endif
+
+       isdn_ppp_hangup(lp->ppp_minor);
+#if 0
+       printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_minor, (long) lp,(long) ippp_table[lp->ppp_minor].lp);
+#endif
+       ippp_table[lp->ppp_minor].lp = NULL;
+       return 0;
+}
+
+int isdn_ppp_bind(isdn_net_local * lp)
+{
+       int i;
+       int unit = 0;
+       char *name;
+       long flags;
+
+       if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
+               return 0;
+
+       save_flags(flags);
+       cli();
+
+        /* 
+         * search a free device 
+         */
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+               if (ippp_table[i].state == IPPP_OPEN) {         /* OPEN, but not connected! */
+#if 0
+                       printk(KERN_DEBUG "find_minor, %d lp: %08lx\n", i, (long) lp);
+#endif
+                       break;
+               }
+       }
+
+       if (i >= ISDN_MAX_CHANNELS) {
+               restore_flags(flags);
+               printk(KERN_WARNING "isdn_ppp_bind: Can't find usable ippp device.\n");
+               return -1;
+       }
+       lp->ppp_minor = i;
+       ippp_table[lp->ppp_minor].lp = lp;
+
+       name = lp->name;
+       unit = isdn_ppp_if_get_unit(&name); /* get unit number from interface name .. ugly! */
+       ippp_table[lp->ppp_minor].unit = unit;
+
+       ippp_table[lp->ppp_minor].state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
+
+       restore_flags(flags);
+
+        /*
+         * kick the ipppd on the new device 
+         */
+       if (ippp_table[lp->ppp_minor].wq)
+               wake_up_interruptible(&ippp_table[lp->ppp_minor].wq);
+
+       return lp->ppp_minor;
+}
+
+static int isdn_ppp_hangup(int minor)
+{
+       if (minor < 0 || minor >= ISDN_MAX_CHANNELS)
+               return 0;
+
+       if (ippp_table[minor].state && ippp_table[minor].wq)
+               wake_up_interruptible(&ippp_table[minor].wq);
+
+       ippp_table[minor].state = IPPP_CLOSEWAIT;
+       return 1;
+}
+
+/*
+ * isdn_ppp_open 
+ */
+
+int isdn_ppp_open(int minor, struct file *file)
+{
+#if 0
+       printk(KERN_DEBUG "ippp, open, minor: %d state: %04x\n", minor,ippp_table[minor].state);
+#endif
+       if (ippp_table[minor].state)
+               return -EBUSY;
+
+       ippp_table[minor].lp = 0;
+       ippp_table[minor].mp_seqno = 0;  /* MP sequence number */
+       ippp_table[minor].pppcfg = 0;    /* ppp configuration */
+       ippp_table[minor].mpppcfg = 0;   /* mppp configuration */
+       ippp_table[minor].range = 0x1000000;    /* MP: 24 bit range */
+       ippp_table[minor].last_link_seqno = -1; /* MP: maybe set to Bundle-MIN, when joining a bundle ?? */
+       ippp_table[minor].unit = -1;    /* set, when we have our interface */
+       ippp_table[minor].mru = 1524;   /* MRU, default 1524 */
+       ippp_table[minor].maxcid = 16;  /* VJ: maxcid */
+       ippp_table[minor].tk = current;
+       ippp_table[minor].wq = NULL;    /* read() wait queue */
+       ippp_table[minor].wq1 = NULL;   /* select() wait queue */
+       ippp_table[minor].first = ippp_table[minor].rq + NUM_RCV_BUFFS - 1; /* receive queue */
+       ippp_table[minor].last = ippp_table[minor].rq;
+#ifdef CONFIG_ISDN_PPP_VJ
+        /*
+         * VJ header compression init
+         */
+       ippp_table[minor].cbuf = kmalloc(ippp_table[minor].mru + PPP_HARD_HDR_LEN + 2, GFP_KERNEL);
+
+       if (ippp_table[minor].cbuf == NULL) {
+               printk(KERN_DEBUG "ippp: Can't allocate memory buffer for VJ compression.\n");
+               return -ENOMEM;
+       }
+       ippp_table[minor].slcomp = slhc_init(16, 16);   /* not necessary for 2. link in bundle */
+#endif
+
+       ippp_table[minor].state = IPPP_OPEN;
+
+       return 0;
+}
+
+void isdn_ppp_release(int minor, struct file *file)
+{
+       int i;
+
+       if (minor < 0 || minor >= ISDN_MAX_CHANNELS)
+               return;
+
+#if 0
+       printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", minor, (long) ippp_table[minor].lp);
+#endif
+
+       if (ippp_table[minor].lp) {     /* a lp address says: this link is still up */
+               isdn_net_dev *p = dev->netdev;
+               while(p) {      /* find interface for our lp; */
+                       if(&p->local == ippp_table[minor].lp)
+                               break;
+                       p = p->next;
+               }
+               if(!p) {
+                       printk(KERN_ERR "isdn_ppp_release: Can't find device for net_local\n");
+                       p = ippp_table[minor].lp->netdev;
+               }
+               ippp_table[minor].lp->ppp_minor = -1;
+               isdn_net_hangup(&p->dev); /* lp->ppp_minor==-1 => no calling of isdn_ppp_hangup() */
+               ippp_table[minor].lp = NULL;
+       }
+       for (i = 0; i < NUM_RCV_BUFFS; i++) {
+               if (ippp_table[minor].rq[i].buf)
+                       kfree(ippp_table[minor].rq[i].buf);
+       }
+
+#ifdef CONFIG_ISDN_PPP_VJ
+       slhc_free(ippp_table[minor].slcomp);
+       kfree(ippp_table[minor].cbuf);
+#endif
+
+       ippp_table[minor].state = 0;
+}
+
+static int get_arg(void *b, unsigned long *val)
+{
+       int r;
+       if ((r = verify_area(VERIFY_READ, (void *) b, sizeof(unsigned long))))
+                return r;
+       memcpy_fromfs((void *) val, b, sizeof(unsigned long));
+       return 0;
+}
+
+static int set_arg(void *b, unsigned long val)
+{
+       int r;
+       if ((r = verify_area(VERIFY_WRITE, b, sizeof(unsigned long))))
+                return r;
+       memcpy_tofs(b, (void *) &val, sizeof(unsigned long));
+       return 0;
+}
+
+int isdn_ppp_ioctl(int minor, struct file *file, unsigned int cmd, unsigned long arg)
+{
+       unsigned long val;
+       int r;
+
+#if 0
+       printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x",minor,cmd);
+       printk(KERN_DEBUG " state: %x\n",ippp_table[minor].state);
+#endif
+
+       if (!(ippp_table[minor].state & IPPP_OPEN))
+               return -EINVAL;
+
+       switch (cmd) {
+#if 0
+       case PPPIOCSINPSIG:     /* obsolete: set input ready signal */
+               /* usual: sig = SIGIO *//* we always deliver a SIGIO */
+               break;
+#endif
+       case PPPIOCBUNDLE:
+#ifdef CONFIG_ISDN_MPP
+               if ((r = get_arg((void *) arg, &val)))
+                       return r;
+               printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
+                        (int) minor, (int) ippp_table[minor].unit, (int) val);
+               return isdn_ppp_bundle(minor, val);
+#else
+               return -1;
+#endif
+               break;
+       case PPPIOCGUNIT:       /* get ppp/isdn unit number */
+               if ((r = set_arg((void *) arg, ippp_table[minor].unit)))
+                       return r;
+               break;
+       case PPPIOCGMPFLAGS:    /* get configuration flags */
+               if ((r = set_arg((void *) arg, ippp_table[minor].mpppcfg)))
+                       return r;
+               break;
+       case PPPIOCSMPFLAGS:    /* set configuration flags */
+               if ((r = get_arg((void *) arg, &val)))
+                       return r;
+               ippp_table[minor].mpppcfg = val;
+               break;
+       case PPPIOCGFLAGS:      /* get configuration flags */
+               if ((r = set_arg((void *) arg, ippp_table[minor].pppcfg)))
+                       return r;
+               break;
+       case PPPIOCSFLAGS:      /* set configuration flags */
+               if ((r = get_arg((void *) arg, &val))) {
+                       return r;
+               }
+               if (val & SC_ENABLE_IP && !(ippp_table[minor].pppcfg & SC_ENABLE_IP)) {
+                       ippp_table[minor].lp->netdev->dev.tbusy = 0;
+                       mark_bh(NET_BH); /* OK .. we are ready to send the first buffer */
+               }
+               ippp_table[minor].pppcfg = val;
+               break;
+#if 0
+       case PPPIOCGSTAT:       /* read PPP statistic information */
+               break;
+       case PPPIOCGTIME:       /* read time delta information */
+               break;
+#endif
+       case PPPIOCSMRU:        /* set receive unit size for PPP */
+               if ((r = get_arg((void *) arg, &val)))
+                       return r;
+               ippp_table[minor].mru = val;
+               break;
+       case PPPIOCSMPMRU:
+               break;
+       case PPPIOCSMPMTU:
+               break;
+       case PPPIOCSMAXCID:     /* set the maximum compression slot id */
+               if ((r = get_arg((void *) arg, &val)))
+                       return r;
+               ippp_table[minor].maxcid = val;
+               break;
+       case PPPIOCGDEBUG:
+               break;
+       case PPPIOCSDEBUG:
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+int isdn_ppp_select(int minor, struct file *file, int type, select_table * st)
+{
+       struct ippp_buf_queue *bf, *bl;
+       unsigned long flags;
+
+#if 0
+       printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n",minor,type);
+#endif
+
+       if (!(ippp_table[minor].state & IPPP_OPEN))
+               return -EINVAL;
+
+       switch (type) {
+       case SEL_IN:
+               save_flags(flags);
+               cli();
+               bl = ippp_table[minor].last;
+               bf = ippp_table[minor].first;
+               if (bf->next == bl && !(ippp_table[minor].state & IPPP_NOBLOCK)) {
+                       select_wait(&ippp_table[minor].wq, st);
+                       restore_flags(flags);
+                       return 0;
+               }
+               ippp_table[minor].state &= ~IPPP_NOBLOCK;
+               restore_flags(flags);
+               return 1;
+       case SEL_OUT:
+                /* we're always ready to send .. */
+               return 1;
+       case SEL_EX:
+               select_wait(&ippp_table[minor].wq1, st);
+               return 0;
+       }
+       return 1;
+}
+
+/*
+ *  fill up isdn_ppp_read() queue ..
+ */
+
+static int isdn_ppp_fill_rq(char *buf, int len, int minor)
+{
+       struct ippp_buf_queue *bf, *bl;
+       unsigned long flags;
+
+       if (minor < 0 || minor >= ISDN_MAX_CHANNELS) {
+               printk(KERN_WARNING "ippp: illegal minor.\n");
+               return 0;
+       }
+       if (!(ippp_table[minor].state & IPPP_CONNECT)) {
+               printk(KERN_DEBUG "ippp: device not activated.\n");
+               return 0;
+       }
+       save_flags(flags);
+       cli();
+
+       bf = ippp_table[minor].first;
+       bl = ippp_table[minor].last;
+
+       if (bf == bl) {
+               printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n");
+               bf = bf->next;
+               kfree(bf->buf);
+               ippp_table[minor].first = bf;
+       }
+       bl->buf = (char *) kmalloc(len, GFP_ATOMIC);
+       if (!bl->buf) {
+               printk(KERN_WARNING "ippp: Can't alloc buf\n");
+               restore_flags(flags);
+               return 0;
+       }
+       bl->len = len;
+
+       memcpy(bl->buf, buf, len);
+
+       ippp_table[minor].last = bl->next;
+       restore_flags(flags);
+
+       if (ippp_table[minor].wq)
+               wake_up_interruptible(&ippp_table[minor].wq);
+
+       return len;
+}
+
+/*
+ * read() .. non-blocking: ipppd calls it only after select()
+ *           reports, that there is data
+ */
+
+int isdn_ppp_read(int minor, struct file *file, char *buf, int count)
+{
+       struct ippp_struct *c = &ippp_table[minor];
+       struct ippp_buf_queue *b;
+       int r;
+       unsigned long flags;
+
+       if (!(ippp_table[minor].state & IPPP_OPEN))
+               return 0;
+
+       if ((r = verify_area(VERIFY_WRITE, (void *) buf, count)))
+               return r;
+
+       save_flags(flags);
+       cli();
+
+       b = c->first->next;
+       if (!b->buf) {
+               restore_flags(flags);
+               return -EAGAIN;
+       }
+       if (b->len < count)
+               count = b->len;
+       memcpy_tofs(buf, b->buf, count);
+       kfree(b->buf);
+       b->buf = NULL;
+       c->first = b;
+       restore_flags(flags);
+
+       return count;
+}
+
+/*
+ * ipppd wanna write a packet to the card .. non-blocking
+ */
+int isdn_ppp_write(int minor, struct file *file,  const char *buf, int count)
+{
+       isdn_net_local *lp;
+
+       if (!(ippp_table[minor].state & IPPP_CONNECT))
+               return 0;
+
+       lp = ippp_table[minor].lp;
+
+       /* -> push it directly to the lowlevel interface */
+
+       if (!lp)
+               printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
+       else {
+               if (lp->isdn_device < 0 || lp->isdn_channel < 0)
+                       return 0;
+
+               if (dev->drv[lp->isdn_device]->running && lp->dialstate == 0 &&
+                   (lp->flags & ISDN_NET_CONNECTED))
+                       dev->drv[lp->isdn_device]->interface->writebuf(
+                                lp->isdn_device,lp->isdn_channel, buf, count, 1);
+       }
+
+       return count;
+}
+
+/*
+ * init memory, structures etc. 
+ */
+
+int isdn_ppp_init(void)
+{
+       int i, j;
+
+       if (!(ippp_table = (struct ippp_struct *)
+             kmalloc(sizeof(struct ippp_struct) * ISDN_MAX_CHANNELS, GFP_KERNEL))) {
+               printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n");
+               return -1;
+       }
+       memset((char *) ippp_table, 0, sizeof(struct ippp_struct) * ISDN_MAX_CHANNELS);
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+               ippp_table[i].state = 0;
+               ippp_table[i].first = ippp_table[i].rq + NUM_RCV_BUFFS - 1;
+               ippp_table[i].last = ippp_table[i].rq;
+
+               for (j = 0; j < NUM_RCV_BUFFS; j++) {
+                       ippp_table[i].rq[j].buf = NULL;
+                       ippp_table[i].rq[j].last = ippp_table[i].rq +
+                           (NUM_RCV_BUFFS + j - 1) % NUM_RCV_BUFFS;
+                       ippp_table[i].rq[j].next = ippp_table[i].rq + (j + 1) % NUM_RCV_BUFFS;
+               }
+       }
+       return 0;
+}
+
+void isdn_ppp_cleanup(void)
+{
+       kfree(ippp_table);
+}
+
+/*
+ * handler for incoming packets on a syncPPP interface
+ */
+
+void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
+{
+#if 0
+       printk(KERN_DEBUG "recv, skb %d\n",skb->len);
+#endif
+
+       if(skb->data[0] == 0xff && skb->data[1] == 0x03)
+               skb_pull(skb,2);
+       else if (ippp_table[lp->ppp_minor].pppcfg & SC_REJ_COMP_AC)
+               return;         /* discard it silently */
+
+#ifdef CONFIG_ISDN_MPP
+       if (!(ippp_table[lp->ppp_minor].mpppcfg & SC_REJ_MP_PROT)) {
+               int proto;
+               int sqno_end;
+               if (skb->data[0] & 0x1) {
+                       proto = skb->data[0];
+                       skb_pull(skb,1);        /* protocol ID is only 8 bit */
+               } else {
+                       proto = ((int) skb->data[0] << 8) + skb->data[1];
+                       skb_pull(skb,2);
+               }
+               if (proto == PPP_MP) {
+                       isdn_net_local *lpq;
+                       int sqno, min_sqno, tseq;
+                       u_char BEbyte = skb->data[0];
+#if 0
+                       printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_minor, proto ,
+                               (int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2], 
+                               (int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
+#endif
+                       if (!(ippp_table[lp->ppp_minor].mpppcfg & SC_IN_SHORT_SEQ)) {
+                               sqno = ((int) skb->data[1] << 16) + ((int) skb->data[2] << 8) + (int) skb->data[3];
+                               skb_pull(skb,4);
+                       } else {
+                               sqno = (((int) skb->data[0] & 0xf) << 8) + (int) skb->data[1];
+                               skb_pull(skb,2);
+                       }
+
+                       if ((tseq = ippp_table[lp->ppp_minor].last_link_seqno) >= sqno) {
+                               int range = ippp_table[lp->ppp_minor].range;
+                               if (tseq + 1024 < range + sqno) /* redundancy check .. not MP conform */
+                                       printk(KERN_WARNING "isdn_ppp_receive, MP, detected overflow with sqno: %d, last: %d !!!\n", sqno, tseq);
+                               else {
+                                       sqno += range;
+                                       ippp_table[lp->ppp_minor].last_link_seqno = sqno;
+                               }
+                       } else
+                               ippp_table[lp->ppp_minor].last_link_seqno = sqno;
+
+                       for (min_sqno = 0, lpq = net_dev->queue;;) {
+                               if (ippp_table[lpq->ppp_minor].last_link_seqno > min_sqno)
+                                       min_sqno = ippp_table[lpq->ppp_minor].last_link_seqno;
+                               lpq = lpq->next;
+                               if (lpq == net_dev->queue)
+                                       break;
+                       }
+                       if (min_sqno >= ippp_table[lpq->ppp_minor].range) {     /* OK, every link overflowed */
+                               int mask = ippp_table[lpq->ppp_minor].range - 1;        /* range is a power of 2 */
+                               isdn_ppp_cleanup_queue(net_dev, min_sqno);
+                               isdn_ppp_mask_queue(net_dev, mask);
+                               net_dev->ib.next_num &= mask;
+                               {
+                                       struct sqqueue *q = net_dev->ib.sq;
+                                       while (q) {
+                                               q->sqno_start &= mask;
+                                               q->sqno_end &= mask;
+                                       }
+                               }
+                               min_sqno &= mask;
+                               for (lpq = net_dev->queue;;) {
+                                       ippp_table[lpq->ppp_minor].last_link_seqno &= mask;
+                                       lpq = lpq->next;
+                                       if (lpq == net_dev->queue)
+                                               break;
+                               }
+                       }
+                       if ((BEbyte & (MP_BEGIN_FRAG | MP_END_FRAG)) != (MP_BEGIN_FRAG | MP_END_FRAG)) {
+                               printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_minor);
+                               if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb , BEbyte, &sqno, min_sqno)) < 0)
+                                       return;         /* no packet complete */
+                       } else
+                               sqno_end = sqno;
+
+                       /*
+                        * MP buffer management .. reorders incoming packets ..
+                        * lotsa mem-copies and not heavily tested.
+                        *
+                        * first check whether there is more than one link in the bundle
+                        * then check whether the number is in order
+                        */
+                       net_dev->ib.modify = 1;         /* block timeout-timer */
+                       if (net_dev->ib.bundled && net_dev->ib.next_num != sqno) {
+                               /*
+                                * packet is not 'in order'
+                                */
+                               struct sqqueue *q;
+
+                               q = (struct sqqueue *) kmalloc(sizeof(struct sqqueue), GFP_ATOMIC);
+                               if (!q) {
+                                       printk(KERN_WARNING "ippp: err, no memory !!\n");
+                                       net_dev->ib.modify = 0;
+                                       return;         /* discard */
+                               }
+                               q->skb = skb;
+                               q->sqno_end = sqno_end;
+                               q->sqno_start = sqno;
+                               q->timer = jiffies + (ISDN_TIMER_1SEC) * 5;     /* timeout after 5 seconds */
+
+                               if (!net_dev->ib.sq) {
+                                       net_dev->ib.sq = q;
+                                       q->next = NULL;
+                               } else {
+                                       struct sqqueue *ql = net_dev->ib.sq;
+                                       if (ql->sqno_start > q->sqno_start) {
+                                               q->next = ql;
+                                               net_dev->ib.sq = q;
+                                       } else {
+                                               while (ql->next && ql->next->sqno_start < q->sqno_start)
+                                                       ql = ql->next;
+                                               q->next = ql->next;
+                                               ql->next = q;
+                                       }
+                               }
+                               net_dev->ib.modify = 0;
+                               return;
+                       } else {
+                               /* 
+                                * packet was 'in order' .. push it higher
+                                */
+                               struct sqqueue *q;
+
+                               net_dev->ib.next_num = sqno_end + 1;
+                               isdn_ppp_push_higher(net_dev, lp, skb, -1);
+
+                                /*
+                                 * check queue, whether we have still buffered the next packet(s)
+                                 */
+                               while ((q = net_dev->ib.sq) && q->sqno_start == net_dev->ib.next_num) {
+                                       isdn_ppp_push_higher(net_dev, lp, q->skb, -1);
+                                       net_dev->ib.sq = q->next;
+                                       net_dev->ib.next_num = q->sqno_end + 1;
+                                       kfree(q);
+                               }
+                       }
+                       net_dev->ib.modify = 0;
+
+               } else
+                       isdn_ppp_push_higher(net_dev, lp, skb , proto);
+       } else
+#endif
+               isdn_ppp_push_higher(net_dev, lp, skb , -1);
+}
+
+
+static void isdn_ppp_push_higher(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb,int proto)
+{
+       struct device *dev = &net_dev->dev;
+
+       if (proto < 0) {        /* MP, oder normales Paket bei REJ_MP, MP Pakete gehen bei REJ zum pppd */
+               if (skb->data[0] & 0x01) {      /* is it odd? */
+                       proto = (unsigned char) skb->data[0];
+                       skb_pull(skb,1);        /* protocol ID is only 8 bit */
+               } else {
+                       proto = ((int) (unsigned char) skb->data[0] << 8) + (unsigned char) skb->data[1];
+                       skb_pull(skb,2);
+               }
+       }
+
+#if 0
+       printk(KERN_DEBUG "push, skb %d %04x\n",skb->len,proto);
+#endif
+
+       switch (proto) {
+       case PPP_IPX: /* untested */
+               skb->dev = dev;
+               skb->mac.raw = skb->data;
+               skb->protocol = htons(ETH_P_IPX);
+               break;
+#ifdef CONFIG_ISDN_PPP_VJ
+       case PPP_VJC_UNCOMP:
+               slhc_remember(ippp_table[net_dev->local.ppp_minor].slcomp, skb->data, skb->len);
+#endif
+       case PPP_IP:
+               skb->dev = dev;
+               skb->mac.raw = skb->data;
+               skb->protocol = htons(ETH_P_IP);
+               break;
+       case PPP_VJC_COMP:
+#ifdef CONFIG_ISDN_PPP_VJ
+               {
+                       struct sk_buff *skb_old = skb;
+                       int pkt_len;
+                       skb = dev_alloc_skb(skb_old->len + 40);
+
+                       if (!skb) {
+                               printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+                               net_dev->local.stats.rx_dropped++;
+                               return;
+                       }
+                       skb->dev = dev;
+                       skb_put(skb,skb_old->len + 40);
+                       memcpy(skb->data, skb_old->data, skb_old->len);
+                       skb->mac.raw = skb->data;
+                       pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_minor].slcomp,
+                                                 skb->data, skb_old->len);
+                       skb_trim(skb, pkt_len);
+                       dev_kfree_skb(skb_old,FREE_WRITE);
+                       skb->protocol = htons(ETH_P_IP);
+               }
+#else
+               printk(KERN_INFO "isdn: Ooopsa .. VJ-Compression support not compiled into isdn driver.\n");
+               lp->stats.rx_dropped++;
+               return;
+#endif
+               break;
+       default:
+               skb_push(skb,4);
+               skb->data[0] = 0xff;
+               skb->data[1] = 0x03;
+               skb->data[2] = (proto>>8);
+               skb->data[3] = proto & 0xff;
+               isdn_ppp_fill_rq(skb->data, skb->len, lp->ppp_minor);   /* push data to pppd device */
+               dev_kfree_skb(skb,FREE_WRITE);
+               return;
+       }
+
+       netif_rx(skb);
+       net_dev->local.stats.rx_packets++;
+       /* Reset hangup-timer */
+       lp->huptimer = 0;
+
+       return;
+}
+
+/*
+ * send ppp frame .. we expect a PIDCOMPable proto -- 
+ *  (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
+ */
+int isdn_ppp_xmit(struct sk_buff *skb, struct device *dev)
+{
+       isdn_net_dev *nd = ((isdn_net_local *) dev->priv)->netdev;
+       isdn_net_local *lp = nd->queue;
+       int proto = PPP_IP;     /* 0x21 */
+       struct ippp_struct *ipt = ippp_table + lp->ppp_minor;
+       struct ippp_struct *ipts = ippp_table + lp->netdev->local.ppp_minor;
+
+        /* If packet is to be resent, it has already been processed and
+         * therefore it's first bytes are already initialized. In this case
+         * send it immediately ...
+         */
+        if (*((unsigned long *)skb->data) != 0)
+          return (isdn_net_send_skb(dev , lp , skb));
+
+        /* ... else packet needs processing. */
+
+/* future: step to next 'lp' when this lp is 'tbusy' */
+
+#if 0
+       printk(KERN_DEBUG  "xmit, skb %d\n",skb->len);
+#endif
+
+#ifdef CONFIG_ISDN_PPP_VJ
+       if (ipt->pppcfg & SC_COMP_TCP) {
+               u_char *buf = skb->data;
+               int pktlen;
+               int len = 4;
+#ifdef CONFIG_ISDN_MPP
+               if (ipt->mpppcfg & SC_MP_PROT) /* sigh */ 
+                       if (ipt->mpppcfg & SC_OUT_SHORT_SEQ)
+                               len += 3;
+                       else
+                               len += 5;
+#endif
+               buf += len;
+               pktlen = slhc_compress(ipts->slcomp, buf, skb->len-len, ipts->cbuf,
+                               &buf, !(ipts->pppcfg & SC_NO_TCP_CCID));
+               skb_trim(skb,pktlen+len);
+               if(buf != skb->data+len) { /* copied to new buffer ??? (btw: WHY must slhc copy it?? *sigh*)  */
+                       memcpy(skb->data+len,buf,pktlen);
+               }
+               if (skb->data[len] & SL_TYPE_COMPRESSED_TCP) {  /* cslip? style -> PPP */
+                       proto = PPP_VJC_COMP;
+                       skb->data[len] ^= SL_TYPE_COMPRESSED_TCP;
+               } else {
+                       if (skb->data[len] >= SL_TYPE_UNCOMPRESSED_TCP)
+                               proto = PPP_VJC_UNCOMP;
+                       skb->data[len] = (skb->data[len] & 0x0f) | 0x40;
+               }
+       }
+#endif
+
+#if 0
+       printk(KERN_DEBUG  "xmit, skb %d %04x\n",skb->len,proto);
+#endif
+
+#ifdef CONFIG_ISDN_MPP
+       if (ipt->mpppcfg & SC_MP_PROT) {
+               /* we get mp_seqno from static isdn_net_local */
+               long mp_seqno = ipts->mp_seqno;
+               ipts->mp_seqno++;
+               nd->queue = nd->queue->next;
+               if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) {
+                       /* skb_push(skb, 3); Done in isdn_net_header() */
+                       mp_seqno &= 0xfff;
+                       skb->data[4] = MP_BEGIN_FRAG | MP_END_FRAG | (mp_seqno >> 8);   /* (B)egin & (E)ndbit .. */
+                       skb->data[5] = mp_seqno & 0xff;
+                       skb->data[6] = proto;   /* PID compression */
+               } else {
+                       /* skb_push(skb, 5); Done in isdn_net_header () */
+                       skb->data[4] = MP_BEGIN_FRAG | MP_END_FRAG;     /* (B)egin & (E)ndbit .. */
+                       skb->data[5] = (mp_seqno >> 16) & 0xff; /* sequence nubmer: 24bit */
+                       skb->data[6] = (mp_seqno >> 8) & 0xff;
+                       skb->data[7] = (mp_seqno >> 0) & 0xff;
+                       skb->data[8] = proto;   /* PID compression */
+               }
+               proto = PPP_MP; /* MP Protocol, 0x003d */
+       }
+#endif
+       skb->data[0] = 0xff;        /* All Stations */
+       skb->data[1] = 0x03;        /* Unumbered information */
+       skb->data[2] = proto >> 8;
+       skb->data[3] = proto & 0xff;
+
+       lp->huptimer = 0;
+       if (!(ipt->pppcfg & SC_ENABLE_IP)) {    /* PPP connected ? */
+               printk(KERN_INFO "isdn, xmit: Packet blocked: %d %d\n", lp->isdn_device, lp->isdn_channel);
+               return 1;
+       }
+        /* tx-stats are now updated via BSENT-callback */
+       return (isdn_net_send_skb(dev , lp , skb));
+}
+
+void isdn_ppp_free_mpqueue(isdn_net_dev * p)
+{
+       struct mpqueue *ql, *q = p->mp_last;
+       while (q) {
+               ql = q->next;
+               dev_kfree_skb(q->skb,FREE_WRITE);
+               kfree(q);
+               q = ql;
+       }
+}
+
+#ifdef CONFIG_ISDN_MPP
+
+static int isdn_ppp_bundle(int minor, int unit)
+{
+       char ifn[IFNAMSIZ + 1];
+       long flags;
+       isdn_net_dev *p;
+       isdn_net_local *lp,*nlp;
+
+       sprintf(ifn, "ippp%d", unit);
+       p = isdn_net_findif(ifn);
+       if (!p)
+               return -1;
+
+       isdn_timer_ctrl(ISDN_TIMER_IPPP, 1);    /* enable timer for ippp/MP */
+
+       save_flags(flags);
+       cli();
+
+       nlp = ippp_table[minor].lp;
+
+       lp = p->queue;
+       p->ib.bundled = 1;
+       nlp->last = lp->last;
+       lp->last->next = nlp;
+       lp->last = nlp;
+       nlp->next = lp;
+       p->queue = nlp;
+
+       nlp->netdev = lp->netdev;
+
+       ippp_table[nlp->ppp_minor].unit = ippp_table[lp->ppp_minor].unit;
+/* maybe also SC_CCP stuff */
+       ippp_table[nlp->ppp_minor].pppcfg |= ippp_table[lp->ppp_minor].pppcfg &
+           (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
+
+       ippp_table[nlp->ppp_minor].mpppcfg |= ippp_table[lp->ppp_minor].mpppcfg &
+           (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
+#if 0
+       if (ippp_table[nlp->ppp_minor].mpppcfg != ippp_table[lp->ppp_minor].mpppcfg) {
+               printk(KERN_WARNING "isdn_ppp_bundle: different MP options %04x and %04x\n",
+                      ippp_table[nlp->ppp_minor].mpppcfg, ippp_table[lp->ppp_minor].mpppcfg);
+       }
+#endif
+
+       restore_flags(flags);
+       return 0;
+}
+
+
+static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask)
+{
+       struct mpqueue *q = dev->mp_last;
+       while (q) {
+               q->sqno &= mask;
+               q = q->next;
+       }
+}
+
+
+static int isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff ** skb, int BEbyte, int *sqnop, int min_sqno)
+{
+       struct mpqueue *qe, *q1, *q;
+       long cnt, flags;
+       int pktlen, sqno_end;
+       int sqno = *sqnop;
+
+       q1 = (struct mpqueue *) kmalloc(sizeof(struct mpqueue), GFP_KERNEL);
+       if (!q1) {
+               printk(KERN_WARNING "isdn_ppp_fill_mpqueue: Can't alloc struct memory.\n");
+               save_flags(flags);
+               cli();
+               isdn_ppp_cleanup_queue(dev, min_sqno);
+               restore_flags(flags);
+               return -1;
+       }
+       q1->skb = *skb;
+       q1->sqno = sqno;
+       q1->BEbyte = BEbyte;
+       q1->time = jiffies;
+
+       save_flags(flags);
+       cli();
+
+       if (!(q = dev->mp_last)) {
+               dev->mp_last = q1;
+               q1->next = NULL;
+               q1->last = NULL;
+               isdn_ppp_cleanup_queue(dev, min_sqno);  /* not necessary */
+               restore_flags(flags);
+               return -1;
+       }
+       for (;;) {              /* the faster way would be to step from the queue-end to the start */
+               if (sqno > q->sqno) {
+                       if (q->next) {
+                               q = q->next;
+                               continue;
+                       }
+                       q->next = q1;
+                       q1->next = NULL;
+                       q1->last = q;
+                       break;
+               }
+               if (sqno == q->sqno)
+                       printk(KERN_WARNING "isdn_fill_mpqueue: illegal sqno received!!\n");
+               q1->last = q->last;
+               q1->next = q;
+               if (q->last) {
+                       q->last->next = q1;
+               } else
+                       dev->mp_last = q1;
+               q->last = q1;
+               break;
+       }
+
+/* now we check whether we completed a packet with this fragment */
+       pktlen = -q1->skb->len;
+       q = q1;
+       cnt = q1->sqno;
+       while (!(q->BEbyte & MP_END_FRAG)) {
+               cnt++;
+               if (!(q->next) || q->next->sqno != cnt) {
+                       isdn_ppp_cleanup_queue(dev, min_sqno);
+                       restore_flags(flags);
+                       return -1;
+               }
+               pktlen += q->skb->len;
+               q = q->next;
+       }
+       pktlen += q->skb->len;
+       qe = q;
+
+       q = q1;
+       cnt = q1->sqno;
+       while (!(q->BEbyte & MP_BEGIN_FRAG)) {
+               cnt--;
+               if (!(q->last) || q->last->sqno != cnt) {
+                       isdn_ppp_cleanup_queue(dev, min_sqno);
+                       restore_flags(flags);
+                       return -1;
+               }
+               pktlen += q->skb->len;
+               q = q->last;
+       }
+       pktlen += q->skb->len;
+
+       if (q->last)
+               q->last->next = qe->next;
+       else
+               dev->mp_last = qe->next;
+
+       if (qe->next)
+               qe->next->last = q->last;
+       qe->next = NULL;
+       sqno_end = qe->sqno;
+       *sqnop = q->sqno;
+
+       isdn_ppp_cleanup_queue(dev, min_sqno);
+       restore_flags(flags);
+
+       *skb = dev_alloc_skb(pktlen + 40); /* not needed: +40 for VJ compression .. */
+
+       if (!(*skb)) {
+               while (q) {
+                       struct mpqueue *ql = q->next;
+                       dev_kfree_skb(q->skb,FREE_WRITE);
+                       kfree(q);
+                       q = ql;
+               }
+               return -2;
+       }
+       cnt = 0;
+       skb_put(*skb,pktlen);
+       while (q) {
+               struct mpqueue *ql = q->next;
+               memcpy((*skb)->data + cnt, q->skb->data, q->skb->len);
+               cnt += q->skb->len;
+               dev_kfree_skb(q->skb,FREE_WRITE);
+               kfree(q);
+               q = ql;
+       }
+
+       return sqno_end;
+}
+
+/*
+ * remove stale packets from list
+ */
+
+static void isdn_ppp_cleanup_queue(isdn_net_dev * dev, long min_sqno)
+{
+/* z.z einfaches aussortieren gammeliger pakete. Fuer die Zukunft:
+   eventuell, solange vorne kein B-paket ist und sqno<=min_sqno: auch rauswerfen
+   wenn sqno<min_sqno und Luecken vorhanden sind: auch weg (die koennen nicht mehr gefuellt werden)
+   bei paketen groesser min_sqno: ueber mp_mrru: wenn summe ueber pktlen der rumhaengenden Pakete 
+   groesser als mrru ist: raus damit , Pakete muessen allerdings zusammenhaengen sonst koennte
+   ja ein Paket mit B und eins mit E dazwischenpassen */
+
+       struct mpqueue *ql, *q = dev->mp_last;
+       while (q) {
+               if (q->sqno < min_sqno) {
+                       if (q->BEbyte & MP_END_FRAG) {
+                               printk(KERN_DEBUG "ippp: freeing stale packet!\n");
+                               if ((dev->mp_last = q->next))
+                                       q->next->last = NULL;
+                               while (q) {
+                                       ql = q->last;
+                                       dev_kfree_skb(q->skb,FREE_WRITE);
+                                       kfree(q);
+                                       q = ql;
+                               }
+                               q = dev->mp_last;
+                       } else
+                               q = q->next;
+               } else
+                       break;
+       }
+}
+
+/*
+ * a buffered packet timed-out?
+ */
+
+#endif
+
+void isdn_ppp_timer_timeout(void)
+{
+#ifdef CONFIG_ISDN_MPP
+       isdn_net_dev *net_dev = dev->netdev;
+       struct sqqueue *q, *ql = NULL, *qn;
+
+       while (net_dev) {
+               isdn_net_local *lp = &net_dev->local;
+               if (net_dev->ib.modify) {       /* interface locked? */
+                        net_dev = net_dev->next;
+                       continue;
+                }
+
+               q = net_dev->ib.sq;
+               while (q) {
+                       if (q->sqno_start == net_dev->ib.next_num || q->timer < jiffies) {
+                               ql = net_dev->ib.sq;
+                               net_dev->ib.sq = q->next;
+                               net_dev->ib.next_num = q->sqno_end + 1;
+                               q->next = NULL;
+                               for (; ql;) {
+                                       isdn_ppp_push_higher(net_dev, lp, ql->skb, -1);
+                                       qn = ql->next;
+                                       kfree(ql);
+                                       ql = qn;
+                               }
+                               q = net_dev->ib.sq;
+                       } else
+                               q = q->next;
+               }
+               net_dev = net_dev->next;
+       }
+#endif
+}
+
+int isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+       int error;
+       char *r;
+       int len;
+       isdn_net_local *lp = (isdn_net_local *) dev->priv;
+
+       if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
+               return -EINVAL;
+
+       switch (cmd) {
+       case SIOCGPPPVER:
+               r = (char *) ifr->ifr_ifru.ifru_data;
+               len = strlen(PPP_VERSION) + 1;
+               error = verify_area(VERIFY_WRITE, r, len);
+               if (!error)
+                       memcpy_tofs(r, PPP_VERSION, len);
+               break;
+       default:
+               error = -EINVAL;
+       }
+       return error;
+}
+
+static int isdn_ppp_if_get_unit(char **namebuf)
+{
+       char *name = *namebuf;
+       int len, i, unit = 0, deci;
+
+       len = strlen(name);
+       for (i = 0, deci = 1; i < len; i++, deci *= 10) {
+               if (name[len - 1 - i] >= '0' && name[len - 1 - i] <= '9')
+                       unit += (name[len - 1 - i] - '0') * deci;
+               else
+                       break;
+       }
+       if (!i)
+               unit = -1;
+
+       *namebuf = name + len - 1 - i;
+       return unit;
+
+}
+
+
+int isdn_ppp_dial_slave(char *name)
+{
+#ifdef CONFIG_ISDN_MPP
+       isdn_net_dev *ndev;
+       isdn_net_local *lp;
+       struct device *sdev;
+
+       if(!(ndev = isdn_net_findif(name)))
+               return 1;
+       lp = &ndev->local;
+       if(!(lp->flags & ISDN_NET_CONNECTED))
+               return 5;
+
+       sdev = lp->slave;
+       while(sdev)
+       {
+               isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
+               if(!(mlp->flags & ISDN_NET_CONNECTED))
+                       break;
+               sdev = mlp->slave;
+       }
+       if(!sdev)
+               return 2;
+
+       isdn_net_force_dial_lp((isdn_net_local *) sdev->priv);
+       return 0;
+#else
+       return -1;
+#endif
+}
+
+
diff --git a/drivers/isdn/isdn_ppp.h b/drivers/isdn/isdn_ppp.h
new file mode 100644 (file)
index 0000000..e9c12ad
--- /dev/null
@@ -0,0 +1,44 @@
+/* $Id: isdn_ppp.h,v 1.1 1996/01/10 21:39:10 fritz Exp fritz $
+ *
+ * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel).
+ *
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ * $Log: isdn_ppp.h,v $
+ * Revision 1.1  1996/01/10 21:39:10  fritz
+ * Initial revision
+ *
+ */
+
+extern void isdn_ppp_timer_timeout(void);
+extern int  isdn_ppp_read(int , struct file *, char *, int);
+extern int  isdn_ppp_write(int , struct file *, const char *, int);
+extern int  isdn_ppp_open(int , struct file *);
+extern int  isdn_ppp_init(void);
+extern void isdn_ppp_cleanup(void);
+extern int  isdn_ppp_free(isdn_net_local *);
+extern int  isdn_ppp_bind(isdn_net_local *);
+extern int  isdn_ppp_xmit(struct sk_buff *, struct device *);
+extern void isdn_ppp_receive(isdn_net_dev *, isdn_net_local *, struct sk_buff *);
+extern int  isdn_ppp_dev_ioctl(struct device *, struct ifreq *, int);
+extern void isdn_ppp_free_mpqueue(isdn_net_dev *);
+extern int  isdn_ppp_select(int, struct file *, int, select_table *);
+extern int  isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long);
+extern void isdn_ppp_release(int, struct file *);
+extern int  isdn_ppp_dial_slave(char *);
+
+extern struct ippp_struct *ippp_table;
diff --git a/drivers/isdn/isdn_tty.c b/drivers/isdn/isdn_tty.c
new file mode 100644 (file)
index 0000000..71e5604
--- /dev/null
@@ -0,0 +1,2068 @@
+/* $Id: isdn_tty.c,v 1.3 1996/02/11 02:12:32 fritz Exp fritz $
+ *
+ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * 
+ * 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_tty.c,v $
+ * Revision 1.3  1996/02/11 02:12:32  fritz
+ * Bugfixes according to similar fixes in standard serial.c of kernel.
+ *
+ * Revision 1.2  1996/01/22 05:12:25  fritz
+ * replaced my_atoi by simple_strtoul
+ *
+ * Revision 1.1  1996/01/09 04:13:18  fritz
+ * Initial revision
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_tty.h"
+
+/* Prototypes */
+
+static int  isdn_tty_edit_at(const char *, int, modem_info *, int);
+static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int);
+static void isdn_tty_modem_reset_regs(atemu *, int);
+
+/* Leave this unchanged unless you know what you do! */
+#define MODEM_PARANOIA_CHECK
+#define MODEM_DO_RESTART
+
+static char *isdn_ttyname_ttyI = "ttyI";
+static char *isdn_ttyname_cui  = "cui";
+char *isdn_tty_revision        = "$Revision: 1.3 $";
+
+int isdn_tty_try_read(int i, u_char * buf, int len)
+{
+        int c;
+        struct tty_struct *tty;
+
+       if (i < 0)
+               return 0;
+       if (dev->mdm.online[i]) {
+               if ((tty = dev->mdm.info[i].tty)) {
+                       if (dev->mdm.info[i].MCR & UART_MCR_RTS) {
+                               c = TTY_FLIPBUF_SIZE - tty->flip.count - 1;
+                               if (c >= len) {
+                                       if (len > 1) {
+                                               memcpy(tty->flip.char_buf_ptr, buf, len);
+                                               tty->flip.count += len;
+                                               memset(tty->flip.flag_buf_ptr, 0, len);
+                                               if (dev->mdm.atmodem[i].mdmreg[12] & 128)
+                                                       tty->flip.flag_buf_ptr[len - 1] = 0xff;
+                                               tty->flip.flag_buf_ptr += len;
+                                               tty->flip.char_buf_ptr += len;
+                                       } else
+                                               tty_insert_flip_char(tty, buf[0], 0);
+                                       queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+                                       return 1;
+                               }
+                       }
+               }
+       }
+       return 0;
+}
+
+void isdn_tty_readmodem(void)
+{
+       int resched = 0;
+       int midx;
+       int i;
+       int c;
+       int r;
+       ulong flags;
+       struct tty_struct *tty;
+       modem_info *info;
+
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+               if ((midx = dev->m_idx[i]) >= 0)
+                       if (dev->mdm.online[midx]) {
+                               save_flags(flags);
+                               cli();
+                               r = 0;
+                               info = &dev->mdm.info[midx];
+                               if ((tty = info->tty)) {
+                                       if (info->MCR & UART_MCR_RTS) {
+                                               c = TTY_FLIPBUF_SIZE - tty->flip.count - 1;
+                                               if (c > 0) {
+                                                       r = isdn_readbchan(info->isdn_driver, info->isdn_channel,
+                                                                     tty->flip.char_buf_ptr,
+                                                                     tty->flip.flag_buf_ptr, c, 0);
+                                                       if (!(dev->mdm.atmodem[midx].mdmreg[12] & 128))
+                                                               memset(tty->flip.flag_buf_ptr, 0, r);
+                                                       tty->flip.count += r;
+                                                       tty->flip.flag_buf_ptr += r;
+                                                       tty->flip.char_buf_ptr += r;
+                                                       if (r)
+                                                               queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+                                               }
+                                       } else
+                                               r = 1;
+                               } else
+                                       r = 1;
+                               restore_flags(flags);
+                               if (r) {
+                                       dev->mdm.rcvsched[midx] = 0;
+                                       resched = 1;
+                               } else
+                                       dev->mdm.rcvsched[midx] = 1;
+                       }
+       }
+       if (!resched)
+               isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0);
+}
+
+/************************************************************
+ *
+ * Modem-functions
+ *
+ * mostly "stolen" from original Linux-serial.c and friends.
+ *
+ ************************************************************/
+
+static void isdn_tty_dial(char *n, modem_info * info, atemu * m)
+{
+       isdn_ctrl cmd;
+       ulong flags;
+       int i;
+
+       save_flags(flags);
+       cli();
+       i = isdn_get_free_channel(ISDN_USAGE_MODEM, m->mdmreg[14], m->mdmreg[15], -1, -1);
+       if (i < 0) {
+               restore_flags(flags);
+               isdn_tty_modem_result(6, info);
+       } else {
+               if (strlen(m->msn)) {
+                       info->isdn_driver = dev->drvmap[i];
+                       info->isdn_channel = dev->chanmap[i];
+                       info->drv_index = i;
+                       dev->m_idx[i] = info->line;
+                       dev->usage[i] |= ISDN_USAGE_OUTGOING;
+                       isdn_info_update();
+                       restore_flags(flags);
+                       cmd.driver = info->isdn_driver;
+                       cmd.arg = info->isdn_channel;
+                       cmd.command = ISDN_CMD_CLREAZ;
+                       dev->drv[info->isdn_driver]->interface->command(&cmd);
+                       strcpy(cmd.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+                       cmd.driver = info->isdn_driver;
+                       cmd.command = ISDN_CMD_SETEAZ;
+                       dev->drv[info->isdn_driver]->interface->command(&cmd);
+                       cmd.driver = info->isdn_driver;
+                       cmd.command = ISDN_CMD_SETL2;
+                       cmd.arg = info->isdn_channel + (m->mdmreg[14] << 8);
+                       dev->drv[info->isdn_driver]->interface->command(&cmd);
+                       cmd.driver = info->isdn_driver;
+                       cmd.command = ISDN_CMD_SETL3;
+                       cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
+                       dev->drv[info->isdn_driver]->interface->command(&cmd);
+                       cmd.driver = info->isdn_driver;
+                       cmd.arg = info->isdn_channel;
+                       sprintf(cmd.num, "%s,%s,%d,%d", n, isdn_map_eaz2msn(m->msn, info->isdn_driver),
+                               m->mdmreg[18], m->mdmreg[19]);
+                       cmd.command = ISDN_CMD_DIAL;
+                       dev->mdm.dialing[info->line] = 1;
+                       strcpy(dev->num[i], n);
+                       isdn_info_update();
+                       dev->drv[info->isdn_driver]->interface->command(&cmd);
+               } else
+                       restore_flags(flags);
+       }
+}
+
+void isdn_tty_modem_hup(modem_info * info)
+{
+       isdn_ctrl cmd;
+
+       dev->mdm.rcvsched[info->line] = 0;
+       dev->mdm.online[info->line] = 0;
+       if (info->isdn_driver >= 0) {
+               cmd.driver = info->isdn_driver;
+               cmd.command = ISDN_CMD_HANGUP;
+               cmd.arg = info->isdn_channel;
+               dev->drv[info->isdn_driver]->interface->command(&cmd);
+               isdn_all_eaz(info->isdn_driver, info->isdn_channel);
+               isdn_free_channel(info->isdn_driver, info->isdn_channel, ISDN_USAGE_MODEM);
+       }
+       dev->m_idx[info->drv_index] = -1;
+       info->isdn_driver = -1;
+       info->isdn_channel = -1;
+       info->drv_index = -1;
+}
+
+static inline int isdn_tty_paranoia_check(modem_info * info, dev_t device, const char *routine)
+{
+#ifdef MODEM_PARANOIA_CHECK
+       if (!info) {
+               printk(KERN_WARNING "isdn: null info_struct for (%d, %d) in %s\n",
+                      MAJOR(device), MINOR(device), routine);
+               return 1;
+       }
+       if (info->magic != ISDN_ASYNC_MAGIC) {
+               printk(KERN_WARNING "isdn: bad magic for modem struct (%d, %d) in %s\n",
+                      MAJOR(device), MINOR(device), routine);
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void isdn_tty_change_speed(modem_info * info)
+{
+       uint cflag, cval, fcr, quot;
+       int i;
+
+       if (!info->tty || !info->tty->termios)
+               return;
+       cflag = info->tty->termios->c_cflag;
+
+       quot = i = cflag & CBAUD;
+       if (i & CBAUDEX) {
+               i &= ~CBAUDEX;
+               if (i < 1 || i > 2)
+                       info->tty->termios->c_cflag &= ~CBAUDEX;
+               else
+                       i += 15;
+       }
+       if (quot) {
+               info->MCR |= UART_MCR_DTR;
+       } else {
+               info->MCR &= ~UART_MCR_DTR;
+               isdn_tty_modem_reset_regs(&dev->mdm.atmodem[info->line], 0);
+               if (dev->mdm.online[info->line]) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+                       printk(KERN_DEBUG "Mhup in changespeed\n");
+#endif
+                       isdn_tty_modem_hup(info);
+                       isdn_tty_modem_result(3, info);
+               }
+               return;
+       }
+       /* byte size and parity */
+       cval = cflag & (CSIZE | CSTOPB);
+       cval >>= 4;
+       if (cflag & PARENB)
+               cval |= UART_LCR_PARITY;
+       if (!(cflag & PARODD))
+               cval |= UART_LCR_EPAR;
+       fcr = 0;
+
+       /* CTS flow control flag and modem status interrupts */
+       if (cflag & CRTSCTS) {
+               info->flags |= ISDN_ASYNC_CTS_FLOW;
+       } else
+               info->flags &= ~ISDN_ASYNC_CTS_FLOW;
+       if (cflag & CLOCAL)
+               info->flags &= ~ISDN_ASYNC_CHECK_CD;
+       else {
+               info->flags |= ISDN_ASYNC_CHECK_CD;
+       }
+}
+
+static int isdn_tty_startup(modem_info * info)
+{
+       ulong flags;
+
+       if (info->flags & ISDN_ASYNC_INITIALIZED)
+               return 0;
+       if (!info->type) {
+               if (info->tty)
+                       set_bit(TTY_IO_ERROR, &info->tty->flags);
+               return 0;
+       }
+       save_flags(flags);
+       cli();
+
+#ifdef ISDN_DEBUG_MODEM_OPEN
+       printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line);
+#endif
+       /*
+        * Now, initialize the UART 
+        */
+       info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+       if (info->tty)
+               clear_bit(TTY_IO_ERROR, &info->tty->flags);
+       /*
+        * and set the speed of the serial port
+        */
+       isdn_tty_change_speed(info);
+
+       info->flags |= ISDN_ASYNC_INITIALIZED;
+       dev->mdm.msr[info->line] |= UART_MSR_DSR;
+#if FUTURE
+       info->send_outstanding = 0;
+#endif
+       restore_flags(flags);
+       return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void isdn_tty_shutdown(modem_info * info)
+{
+       ulong flags;
+
+       if (!(info->flags & ISDN_ASYNC_INITIALIZED))
+               return;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+       printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line);
+#endif
+       save_flags(flags);
+       cli();                  /* Disable interrupts */
+       if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+               info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS);
+               isdn_tty_modem_reset_regs(&dev->mdm.atmodem[info->line], 0);
+               if (dev->mdm.online[info->line]) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+                       printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
+#endif
+                       isdn_tty_modem_hup(info);
+               }
+       }
+       if (info->tty)
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       info->flags &= ~ISDN_ASYNC_INITIALIZED;
+       restore_flags(flags);
+}
+
+static int isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
+{
+       int c, total = 0;
+       modem_info *info = (modem_info *) tty->driver_data;
+       ulong flags;
+       int i;
+
+       if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write"))
+               return 0;
+       if (!tty)
+               return 0;
+       save_flags(flags);
+       cli();
+       while (1) {
+               c = MIN(count, info->xmit_size - info->xmit_count - 1);
+               if (info->isdn_driver >= 0) {
+#if 0
+                       if (info->isdn_driver != 0) {
+                               printk(KERN_DEBUG "FIDO: Zwei HW-Treiber geladen? Ansonsten ist was faul.\n");
+                               break;
+                       }
+                       int drvidx = info->isdn_driver;
+                       driver *driv = dev->drv[drvidx];
+                       i = driv->maxbufsize;
+#else
+                       i = dev->drv[info->isdn_driver]->maxbufsize;
+#endif
+                       c = MIN(c, i);
+               }
+               if (c <= 0)
+                       break;
+               i = info->line;
+               if (dev->mdm.online[i]) {
+                       isdn_tty_check_esc(buf, dev->mdm.atmodem[i].mdmreg[2], c,
+                                      &(dev->mdm.atmodem[i].pluscount),
+                            &(dev->mdm.atmodem[i].lastplus), from_user);
+                       if (from_user)
+                               memcpy_fromfs(&(info->xmit_buf[info->xmit_count]), buf, c);
+                       else
+                               memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
+                       info->xmit_count += c;
+                       if (dev->mdm.atmodem[i].mdmreg[13] & 1) {
+                               char *bufptr;
+                               int buflen;
+#if 0
+                               printk(KERN_DEBUG "WB1: %d\n", info->xmit_count);
+#endif
+                               bufptr = info->xmit_buf;
+                               buflen = info->xmit_count;
+                               if (dev->mdm.atmodem[i].mdmreg[13] & 2) {
+                                       /* Add T.70 simplyfied header */
+
+#ifdef ISDN_DEBUG_MODEM_DUMP
+                                       isdn_dumppkt("T70pack1:", bufptr, buflen, 40);
+#endif
+                                       bufptr -= 4;
+                                       buflen += 4;
+                                       memcpy(bufptr, "\1\0\1\0", 4);
+#ifdef ISDN_DEBUG_MODEM_DUMP
+                                       isdn_dumppkt("T70pack2:", bufptr, buflen, 40);
+#endif
+                               }
+                               if (dev->drv[info->isdn_driver]->interface->
+                                   writebuf(info->isdn_driver, info->isdn_channel, bufptr, buflen, 0) > 0) {
+                                       info->xmit_count = 0;
+                                       info->xmit_size = dev->mdm.atmodem[i].mdmreg[16] * 16;
+#if FUTURE
+                                       info->send_outstanding++;
+                                       dev->mdm.msr[i] &= ~UART_MSR_CTS;
+#endif
+                                       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+                                           tty->ldisc.write_wakeup)
+                                               (tty->ldisc.write_wakeup) (tty);
+                                       wake_up_interruptible(&tty->write_wait);
+                               }
+                       }
+               } else {
+                       if (dev->mdm.dialing[i]) {
+                               dev->mdm.dialing[i] = 0;
+#ifdef ISDN_DEBUG_MODEM_HUP
+                               printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
+#endif
+                               isdn_tty_modem_hup(info);
+                               isdn_tty_modem_result(3, info);
+                       } else
+                               c = isdn_tty_edit_at(buf, c, info, from_user);
+               }
+               buf += c;
+               count -= c;
+               total += c;
+       }
+       if (info->xmit_count)
+               isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
+       restore_flags(flags);
+       return total;
+}
+
+static int isdn_tty_write_room(struct tty_struct *tty)
+{
+       modem_info *info = (modem_info *) tty->driver_data;
+       int ret;
+
+       if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write_room"))
+               return 0;
+       if (!dev->mdm.online[info->line])
+               return info->xmit_size - 1;
+       ret = info->xmit_size - info->xmit_count - 1;
+       return (ret < 0) ? 0 : ret;
+}
+
+static int isdn_tty_chars_in_buffer(struct tty_struct *tty)
+{
+       modem_info *info = (modem_info *) tty->driver_data;
+
+       if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_chars_in_buffer"))
+               return 0;
+       if (!dev->mdm.online[info->line])
+               return 0;
+       return (info->xmit_count);
+}
+
+static void isdn_tty_flush_buffer(struct tty_struct *tty)
+{
+       modem_info *info = (modem_info *) tty->driver_data;
+       uint flags;
+
+       if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_buffer"))
+               return;
+       save_flags(flags);
+       cli();
+       info->xmit_count = 0;
+       restore_flags(flags);
+       wake_up_interruptible(&tty->write_wait);
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+           tty->ldisc.write_wakeup)
+               (tty->ldisc.write_wakeup) (tty);
+}
+
+static void isdn_tty_flush_chars(struct tty_struct *tty)
+{
+       modem_info *info = (modem_info *) tty->driver_data;
+
+       if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_chars"))
+               return;
+       if (info->xmit_count > 0)
+               isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_throttle()
+ * 
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void isdn_tty_throttle(struct tty_struct *tty)
+{
+       modem_info *info = (modem_info *) tty->driver_data;
+
+       if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_throttle"))
+               return;
+       if (I_IXOFF(tty))
+               info->x_char = STOP_CHAR(tty);
+       info->MCR &= ~UART_MCR_RTS;
+}
+
+static void isdn_tty_unthrottle(struct tty_struct *tty)
+{
+       modem_info *info = (modem_info *) tty->driver_data;
+
+       if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_unthrottle"))
+               return;
+       if (I_IXOFF(tty)) {
+               if (info->x_char)
+                       info->x_char = 0;
+               else
+                       info->x_char = START_CHAR(tty);
+       }
+       info->MCR |= UART_MCR_RTS;
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+/*
+ * isdn_tty_get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ *          is emptied.  On bus types like RS485, the transmitter must
+ *          release the bus after transmitting. This must be done when
+ *          the transmit shift register is empty, not be done when the
+ *          transmit holding register is empty.  This functionality
+ *          allows RS485 driver to be written in user space. 
+ */
+static int isdn_tty_get_lsr_info(modem_info * info, uint * value)
+{
+       u_char status;
+       uint result;
+       ulong flags;
+
+       save_flags(flags);
+       cli();
+       status = dev->mdm.msr[info->line];
+       restore_flags(flags);
+       result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
+       put_fs_long(result, (ulong *) value);
+       return 0;
+}
+
+
+static int isdn_tty_get_modem_info(modem_info * info, uint * value)
+{
+       u_char control, status;
+       uint result;
+       ulong flags;
+
+       control = info->MCR;
+       save_flags(flags);
+       cli();
+       status = dev->mdm.msr[info->line];
+       restore_flags(flags);
+       result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
+           | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
+           | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
+           | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
+           | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
+           | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
+       put_fs_long(result, (ulong *) value);
+       return 0;
+}
+
+static int isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value)
+{
+       uint arg = get_fs_long((ulong *) value);
+
+       switch (cmd) {
+       case TIOCMBIS:
+               if (arg & TIOCM_RTS) {
+                       info->MCR |= UART_MCR_RTS;
+               }
+               if (arg & TIOCM_DTR) {
+                       info->MCR |= UART_MCR_DTR;
+               }
+               break;
+       case TIOCMBIC:
+               if (arg & TIOCM_RTS) {
+                       info->MCR &= ~UART_MCR_RTS;
+               }
+               if (arg & TIOCM_DTR) {
+                       info->MCR &= ~UART_MCR_DTR;
+                       isdn_tty_modem_reset_regs(&dev->mdm.atmodem[info->line], 0);
+                       if (dev->mdm.online[info->line]) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+                               printk(KERN_DEBUG "Mhup in TIOCMBIC\n");
+#endif
+                               isdn_tty_modem_hup(info);
+                               isdn_tty_modem_result(3, info);
+                       }
+               }
+               break;
+       case TIOCMSET:
+               info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
+                            | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+                            | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+               if (!(info->MCR & UART_MCR_DTR)) {
+                       isdn_tty_modem_reset_regs(&dev->mdm.atmodem[info->line], 0);
+                       if (dev->mdm.online[info->line]) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+                               printk(KERN_DEBUG "Mhup in TIOCMSET\n");
+#endif
+                               isdn_tty_modem_hup(info);
+                               isdn_tty_modem_result(3, info);
+                       }
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int isdn_tty_ioctl(struct tty_struct *tty, struct file *file,
+                      uint cmd, ulong arg)
+{
+       modem_info *info = (modem_info *) tty->driver_data;
+       int error;
+       int retval;
+
+       if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_ioctl"))
+               return -ENODEV;
+       switch (cmd) {
+       case TCSBRK:            /* SVID version: non-zero arg --> no break */
+               retval = tty_check_change(tty);
+               if (retval)
+                       return retval;
+               tty_wait_until_sent(tty, 0);
+               return 0;
+       case TCSBRKP:           /* support for POSIX tcsendbreak() */
+               retval = tty_check_change(tty);
+               if (retval)
+                       return retval;
+               tty_wait_until_sent(tty, 0);
+               return 0;
+       case TIOCGSOFTCAR:
+               error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+               if (error)
+                       return error;
+               put_fs_long(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg);
+               return 0;
+       case TIOCSSOFTCAR:
+               arg = get_fs_long((ulong *) arg);
+               tty->termios->c_cflag =
+                   ((tty->termios->c_cflag & ~CLOCAL) |
+                    (arg ? CLOCAL : 0));
+               return 0;
+       case TIOCMGET:
+               error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
+               if (error)
+                       return error;
+               return isdn_tty_get_modem_info(info, (uint *) arg);
+       case TIOCMBIS:
+       case TIOCMBIC:
+       case TIOCMSET:
+               return isdn_tty_set_modem_info(info, cmd, (uint *) arg);
+       case TIOCSERGETLSR:     /* Get line status register */
+               error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
+               if (error)
+                       return error;
+               else
+                       return isdn_tty_get_lsr_info(info, (uint *) arg);
+
+       case TIOCGSERIAL:
+               return -ENOIOCTLCMD;
+#if 0
+               error = verify_area(VERIFY_WRITE, (void *) arg,
+                                   sizeof(struct serial_struct));
+               if (error)
+                       return error;
+               return get_serial_info(info,
+                                      (struct serial_struct *) arg);
+#endif
+       case TIOCSSERIAL:
+               return -ENOIOCTLCMD;
+#if 0
+               return set_serial_info(info,
+                                      (struct serial_struct *) arg);
+#endif
+       case TIOCSERCONFIG:
+               return -ENOIOCTLCMD;
+#if 0
+               return do_autoconfig(info);
+#endif
+
+       case TIOCSERGWILD:
+               return -ENOIOCTLCMD;
+#if 0
+               error = verify_area(VERIFY_WRITE, (void *) arg,
+                                   sizeof(int));
+               if (error)
+                       return error;
+               put_fs_long(modem_wild_int_mask, (ulong *) arg);
+               return 0;
+#endif
+       case TIOCSERSWILD:
+               return -ENOIOCTLCMD;
+#if 0
+               if (!suser())
+                       return -EPERM;
+               modem_wild_int_mask = get_fs_long((ulong *) arg);
+               if (modem_wild_int_mask < 0)
+                       modem_wild_int_mask = check_wild_interrupts(0);
+               return 0;
+#endif
+       case TIOCSERGSTRUCT:
+               return -ENOIOCTLCMD;
+#if 0
+               error = verify_area(VERIFY_WRITE, (void *) arg,
+                                   sizeof(modem_info));
+               if (error)
+                       return error;
+               memcpy_tofs((modem_info *) arg,
+                           info, sizeof(modem_info));
+               return 0;
+#endif
+       default:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+               printk(KERN_DEBUG "unsupp. ioctl 0x%08x on ttyi%d\n", cmd, info->line);
+#endif
+               return -ENOIOCTLCMD;
+       }
+       return 0;
+}
+
+static void isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+       modem_info *info = (modem_info *) tty->driver_data;
+
+       if (tty->termios->c_cflag == old_termios->c_cflag)
+               return;
+       isdn_tty_change_speed(info);
+       if ((old_termios->c_cflag & CRTSCTS) &&
+           !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+       }
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_open() and friends
+ * ------------------------------------------------------------
+ */
+static int isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
+{
+       struct wait_queue wait = {current, NULL};
+       int do_clocal = 0;
+       unsigned long flags;
+       int retval;
+
+       /*
+        * If the device is in the middle of being closed, then block
+        * until it's done, and then try again.
+        */
+       if (tty_hung_up_p(filp) ||
+           (info->flags & ISDN_ASYNC_CLOSING)) {
+               if (info->flags & ISDN_ASYNC_CLOSING)
+                        interruptible_sleep_on(&info->close_wait);
+#ifdef MODEM_DO_RESTART
+               if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
+                       return -EAGAIN;
+               else
+                       return -ERESTARTSYS;
+#else
+               return -EAGAIN;
+#endif
+       }
+       /*
+        * If this is a callout device, then just make sure the normal
+        * device isn't being used.
+        */
+       if (tty->driver.subtype == ISDN_SERIAL_TYPE_CALLOUT) {
+               if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
+                       return -EBUSY;
+               if ((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+                   (info->flags & ISDN_ASYNC_SESSION_LOCKOUT) &&
+                   (info->session != current->session))
+                       return -EBUSY;
+               if ((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+                   (info->flags & ISDN_ASYNC_PGRP_LOCKOUT) &&
+                   (info->pgrp != current->pgrp))
+                       return -EBUSY;
+               info->flags |= ISDN_ASYNC_CALLOUT_ACTIVE;
+               return 0;
+       }
+       /*
+        * If non-blocking mode is set, then make the check up front
+        * and then exit.
+        */
+       if (filp->f_flags & O_NONBLOCK) {
+               if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
+                       return -EBUSY;
+               info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
+               return 0;
+       }
+       if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) {
+               if (info->normal_termios.c_cflag & CLOCAL)
+                       do_clocal = 1;
+       } else {
+               if (tty->termios->c_cflag & CLOCAL)
+                       do_clocal = 1;
+       }
+       /*
+        * Block waiting for the carrier detect and the line to become
+        * free (i.e., not in use by the callout).  While we are in
+        * this loop, info->count is dropped by one, so that
+        * isdn_tty_close() knows when to free things.  We restore it upon
+        * exit, either normal or abnormal.
+        */
+       retval = 0;
+       add_wait_queue(&info->open_wait, &wait);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+       printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n",
+              info->line, info->count);
+#endif
+        save_flags(flags);
+        cli();
+        if (!(tty_hung_up_p(filp)))
+                info->count--;
+        restore_flags(flags);
+       info->blocked_open++;
+       while (1) {
+               current->state = TASK_INTERRUPTIBLE;
+               if (tty_hung_up_p(filp) ||
+                   !(info->flags & ISDN_ASYNC_INITIALIZED)) {
+#ifdef MODEM_DO_RESTART
+                       if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
+                               retval = -EAGAIN;
+                       else
+                               retval = -ERESTARTSYS;
+#else
+                       retval = -EAGAIN;
+#endif
+                       break;
+               }
+               if (!(info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+                   !(info->flags & ISDN_ASYNC_CLOSING) &&
+                   (do_clocal || (
+                                         dev->mdm.msr[info->line] &
+                                         UART_MSR_DCD))) {
+                       break;
+               }
+               if (current->signal & ~current->blocked) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+#ifdef ISDN_DEBUG_MODEM_OPEN
+               printk(KERN_DEBUG "isdn_tty_block_til_ready blocking: ttyi%d, count = %d\n",
+                      info->line, info->count);
+#endif
+               schedule();
+       }
+       current->state = TASK_RUNNING;
+       remove_wait_queue(&info->open_wait, &wait);
+       if (!tty_hung_up_p(filp))
+               info->count++;
+       info->blocked_open--;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+       printk(KERN_DEBUG "isdn_tty_block_til_ready after blocking: ttyi%d, count = %d\n",
+              info->line, info->count);
+#endif
+       if (retval)
+               return retval;
+       info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
+       return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int isdn_tty_open(struct tty_struct *tty, struct file *filp)
+{
+       modem_info *info;
+       int retval, line;
+
+       line = MINOR(tty->device) - tty->driver.minor_start;
+       if (line < 0 || line > ISDN_MAX_CHANNELS)
+               return -ENODEV;
+       info = &dev->mdm.info[line];
+       if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_open"))
+               return -ENODEV;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+       printk(KERN_DEBUG "isdn_tty_open %s%d, count = %d\n", tty->driver.name,
+              info->line, info->count);
+#endif
+       info->count++;
+       tty->driver_data = info;
+       info->tty = tty;
+       /*
+        * Start up serial port
+        */
+       retval = isdn_tty_startup(info);
+       if (retval) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+               printk(KERN_DEBUG "isdn_tty_open return after startup\n");
+#endif
+               return retval;
+       }
+       retval = isdn_tty_block_til_ready(tty, filp, info);
+       if (retval) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+               printk(KERN_DEBUG "isdn_tty_open return after isdn_tty_block_til_ready \n");
+#endif
+               return retval;
+       }
+       if ((info->count == 1) && (info->flags & ISDN_ASYNC_SPLIT_TERMIOS)) {
+               if (tty->driver.subtype == ISDN_SERIAL_TYPE_NORMAL)
+                       *tty->termios = info->normal_termios;
+               else
+                       *tty->termios = info->callout_termios;
+               isdn_tty_change_speed(info);
+       }
+       info->session = current->session;
+       info->pgrp = current->pgrp;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+       printk(KERN_DEBUG "isdn_tty_open ttyi%d successful...\n", info->line);
+#endif
+       dev->modempoll++;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+       printk(KERN_DEBUG "isdn_tty_open normal exit\n");
+#endif
+       return 0;
+}
+
+static void isdn_tty_close(struct tty_struct *tty, struct file *filp)
+{
+       modem_info *info = (modem_info *) tty->driver_data;
+       ulong flags;
+       ulong timeout;
+
+       if (!info || isdn_tty_paranoia_check(info, tty->device, "isdn_tty_close"))
+               return;
+       save_flags(flags);
+       cli();
+       if (tty_hung_up_p(filp)) {
+               restore_flags(flags);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+               printk(KERN_DEBUG "isdn_tty_close return after tty_hung_up_p\n");
+#endif
+               return;
+       }
+       dev->modempoll--;
+       if ((tty->count == 1) && (info->count != 1)) {
+               /*
+                * Uh, oh.  tty->count is 1, which means that the tty
+                * structure will be freed.  Info->count should always
+                * be one in these conditions.  If it's greater than
+                * one, we've got real problems, since it means the
+                * serial port won't be shutdown.
+                */
+               printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, "
+                      "info->count is %d\n", info->count);
+               info->count = 1;
+       }
+       if (--info->count < 0) {
+               printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n",
+                      info->line, info->count);
+               info->count = 0;
+       }
+       if (info->count) {
+               restore_flags(flags);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+               printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n");
+#endif
+               return;
+       }
+       info->flags |= ISDN_ASYNC_CLOSING;
+       /*
+        * Save the termios structure, since this port may have
+        * separate termios for callout and dialin.
+        */
+       if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
+               info->normal_termios = *tty->termios;
+       if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
+               info->callout_termios = *tty->termios;
+
+       tty->closing = 1;
+
+       /*
+        * At this point we stop accepting input.  To do this, we
+        * disable the receive line status interrupts, and tell the
+        * interrupt driver to stop checking the data ready bit in the
+        * line status register.
+        */
+       if (info->flags & ISDN_ASYNC_INITIALIZED) {
+               tty_wait_until_sent(tty, 3000);         /* 30 seconds timeout */
+               /*
+                * Before we drop DTR, make sure the UART transmitter
+                * has completely drained; this is especially
+                * important if there is a transmit FIFO!
+                */
+               timeout = jiffies + HZ;
+               while (!(dev->mdm.mlr[info->line]
+                        & UART_LSR_TEMT)) {
+                       current->state = TASK_INTERRUPTIBLE;
+                       current->timeout = jiffies + 20;
+                       schedule();
+                       if (jiffies > timeout)
+                               break;
+               }
+       }
+       isdn_tty_shutdown(info);
+       if (tty->driver.flush_buffer)
+               tty->driver.flush_buffer(tty);
+       if (tty->ldisc.flush_buffer)
+               tty->ldisc.flush_buffer(tty);
+       info->tty = 0;
+       tty->closing = 0;
+#if 00
+       if (tty->ldisc.num != ldiscs[N_TTY].num) {
+               if (tty->ldisc.close)
+                       (tty->ldisc.close) (tty);
+               tty->ldisc = ldiscs[N_TTY];
+               tty->termios->c_line = N_TTY;
+               if (tty->ldisc.open)
+                       (tty->ldisc.open) (tty);
+       }
+#endif
+       if (info->blocked_open) {
+               if (info->close_delay) {
+                       current->state = TASK_INTERRUPTIBLE;
+                       current->timeout = jiffies + info->close_delay;
+                       schedule();
+               }
+               wake_up_interruptible(&info->open_wait);
+       }
+       info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE |
+                        ISDN_ASYNC_CLOSING);
+       wake_up_interruptible(&info->close_wait);
+       restore_flags(flags);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+       printk(KERN_DEBUG "isdn_tty_close normal exit\n");
+#endif
+}
+
+/*
+ * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void isdn_tty_hangup(struct tty_struct *tty)
+{
+       modem_info *info = (modem_info *) tty->driver_data;
+
+       if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_hangup"))
+               return;
+       isdn_tty_shutdown(info);
+       info->count = 0;
+       info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE);
+       info->tty = 0;
+       wake_up_interruptible(&info->open_wait);
+}
+
+static void isdn_tty_reset_profile(atemu * m)
+{
+       m->profile[0] = 0;
+       m->profile[1] = 0;
+       m->profile[2] = 43;
+       m->profile[3] = 13;
+       m->profile[4] = 10;
+       m->profile[5] = 8;
+       m->profile[6] = 3;
+       m->profile[7] = 60;
+       m->profile[8] = 2;
+       m->profile[9] = 6;
+       m->profile[10] = 7;
+       m->profile[11] = 70;
+       m->profile[12] = 0x45;
+       m->profile[13] = 0;
+       m->profile[14] = ISDN_PROTO_L2_X75I;
+       m->profile[15] = ISDN_PROTO_L3_TRANS;
+       m->profile[16] = ISDN_SERIAL_XMIT_SIZE / 16;
+       m->profile[17] = ISDN_MODEM_WINSIZE;
+       m->profile[18] = 7;
+       m->profile[19] = 0;
+       m->pmsn[0] = '\0';
+}
+
+static void isdn_tty_modem_reset_regs(atemu * m, int force)
+{
+       if ((m->mdmreg[12] & 32) || force) {
+               memcpy(m->mdmreg, m->profile, ISDN_MODEM_ANZREG);
+               memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
+       }
+       m->mdmcmdl = 0;
+}
+
+static void modem_write_profile(atemu * m)
+{
+       memcpy(m->profile, m->mdmreg, ISDN_MODEM_ANZREG);
+       memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
+       if (dev->profd)
+               send_sig(SIGIO, dev->profd, 1);
+}
+
+int isdn_tty_modem_init(void)
+{
+       modem *m;
+       int i;
+       modem_info *info;
+
+       m = &dev->mdm;
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+               isdn_tty_reset_profile(&(m->atmodem[i]));
+               isdn_tty_modem_reset_regs(&(m->atmodem[i]), 1);
+       }
+       memset(&m->tty_modem, 0, sizeof(struct tty_driver));
+       m->tty_modem.magic = TTY_DRIVER_MAGIC;
+       m->tty_modem.name = isdn_ttyname_ttyI;
+       m->tty_modem.major = ISDN_TTY_MAJOR;
+       m->tty_modem.minor_start = 0;
+       m->tty_modem.num = ISDN_MAX_CHANNELS;
+       m->tty_modem.type = TTY_DRIVER_TYPE_SERIAL;
+       m->tty_modem.subtype = ISDN_SERIAL_TYPE_NORMAL;
+       m->tty_modem.init_termios = tty_std_termios;
+       m->tty_modem.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       m->tty_modem.flags = TTY_DRIVER_REAL_RAW;
+       m->tty_modem.refcount = &m->refcount;
+       m->tty_modem.table = m->modem_table;
+       m->tty_modem.termios = m->modem_termios;
+       m->tty_modem.termios_locked = m->modem_termios_locked;
+       m->tty_modem.open = isdn_tty_open;
+       m->tty_modem.close = isdn_tty_close;
+       m->tty_modem.write = isdn_tty_write;
+       m->tty_modem.put_char = NULL;
+       m->tty_modem.flush_chars = isdn_tty_flush_chars;
+       m->tty_modem.write_room = isdn_tty_write_room;
+       m->tty_modem.chars_in_buffer = isdn_tty_chars_in_buffer;
+       m->tty_modem.flush_buffer = isdn_tty_flush_buffer;
+       m->tty_modem.ioctl = isdn_tty_ioctl;
+       m->tty_modem.throttle = isdn_tty_throttle;
+       m->tty_modem.unthrottle = isdn_tty_unthrottle;
+       m->tty_modem.set_termios = isdn_tty_set_termios;
+       m->tty_modem.stop = NULL;
+       m->tty_modem.start = NULL;
+       m->tty_modem.hangup = isdn_tty_hangup;
+       /*
+        * The callout device is just like normal device except for
+        * major number and the subtype code.
+        */
+       m->cua_modem = m->tty_modem;
+       m->cua_modem.name = isdn_ttyname_cui;
+       m->cua_modem.major = ISDN_TTYAUX_MAJOR;
+       m->tty_modem.minor_start = 0;
+       m->cua_modem.subtype = ISDN_SERIAL_TYPE_CALLOUT;
+
+       if (tty_register_driver(&m->tty_modem)) {
+               printk(KERN_WARNING "isdn: Unable to register modem-device\n");
+               return -1;
+       }
+       if (tty_register_driver(&m->cua_modem)) {
+               printk(KERN_WARNING "Couldn't register modem-callout-device\n");
+               return -2;
+       }
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+               info = &(m->info[i]);
+               info->magic = ISDN_ASYNC_MAGIC;
+               info->line = i;
+               info->tty = 0;
+               info->close_delay = 50;
+               info->x_char = 0;
+               info->count = 0;
+               info->blocked_open = 0;
+               info->callout_termios = m->cua_modem.init_termios;
+               info->normal_termios = m->tty_modem.init_termios;
+               info->open_wait = 0;
+               info->close_wait = 0;
+               info->type = ISDN_PORT_16550A;
+               info->isdn_driver = -1;
+               info->isdn_channel = -1;
+               info->drv_index = -1;
+               info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
+               if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) {
+                       printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
+                       return -3;
+               }
+               info->xmit_buf += 4;    /* Make room for T.70 header */
+       }
+       return 0;
+}
+
+/*
+ * An incoming call-request has arrived.
+ * Search the tty-devices for an aproppriate device and bind
+ * it to the ISDN-Channel.
+ * Return Index to dev->mdm or -1 if none found.
+ */
+int isdn_tty_find_icall(int di, int ch, char *num)
+{
+       char *eaz;
+       int i;
+       int idx;
+       int si1;
+       int si2;
+       char *s;
+       char nr[31];
+       ulong flags;
+
+       save_flags(flags);
+       cli();
+       if (num[0] == ',') {
+               nr[0] = '0';
+               strncpy(&nr[1], num, 29);
+               printk(KERN_WARNING "isdn: Incoming call without OAD, assuming '0'\n");
+       } else
+               strncpy(nr, num, 30);
+       s = strtok(nr, ",");
+       s = strtok(NULL, ",");
+       if (!s) {
+               printk(KERN_WARNING "isdn: Incoming callinfo garbled, ignored: %s\n",
+                      num);
+               restore_flags(flags);
+               return -1;
+       }
+       si1 = (int)simple_strtoul(s,NULL,10);
+       s = strtok(NULL, ",");
+       if (!s) {
+               printk(KERN_WARNING "isdn: Incoming callinfo garbled, ignored: %s\n",
+                      num);
+               restore_flags(flags);
+               return -1;
+       }
+       si2 = (int)simple_strtoul(s,NULL,10);
+       eaz = strtok(NULL, ",");
+       if (!eaz) {
+               printk(KERN_WARNING "isdn: Incoming call without CPN, assuming '0'\n");
+               eaz = "0";
+       }
+#ifdef ISDN_DEBUG_MODEM_ICALL
+       printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
+#endif
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+#ifdef ISDN_DEBUG_MODEM_ICALL
+               printk(KERN_DEBUG "m_fi: i=%d msn=%s mmsn=%s mreg18=%d mreg19=%d\n", i,
+                      dev->mdm.atmodem[i].msn, isdn_map_eaz2msn(dev->mdm.atmodem[i].msn, di),
+                      dev->mdm.atmodem[i].mdmreg[18], dev->mdm.atmodem[i].mdmreg[19]);
+#endif
+               if ((!strcmp(isdn_map_eaz2msn(dev->mdm.atmodem[i].msn, di)
+                            ,eaz)) &&  /* EAZ is matching   */
+                   (dev->mdm.atmodem[i].mdmreg[18] == si1) &&  /* SI1 is matching   */
+                   (dev->mdm.atmodem[i].mdmreg[19] == si2)) {  /* SI2 is matching   */
+                       modem_info *info = &dev->mdm.info[i];
+                       idx = isdn_dc2minor(di, ch);
+#ifdef ISDN_DEBUG_MODEM_ICALL
+                       printk(KERN_DEBUG "m_fi: match1\n");
+                       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]);
+#endif
+                       if ((info->flags & ISDN_ASYNC_NORMAL_ACTIVE) &&
+                           (info->isdn_driver == -1) &&
+                           (info->isdn_channel == -1) &&
+                           (USG_NONE(dev->usage[idx]))) {
+                               info->isdn_driver = di;
+                               info->isdn_channel = ch;
+                               info->drv_index = idx;
+                               dev->m_idx[idx] = info->line;
+                               dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+                               dev->usage[idx] |= ISDN_USAGE_MODEM;
+                               strcpy(dev->num[idx], nr);
+                               isdn_info_update();
+                               restore_flags(flags);
+                               printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
+                                      info->line);
+                               return info->line;
+                       }
+               }
+       }
+       printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
+              dev->drv[di]->reject_bus ? "rejected" : "ignored");
+       restore_flags(flags);
+       return -1;
+}
+
+/*********************************************************************
+ Modem-Emulator-Routines
+ *********************************************************************/
+
+#define cmdchar(c) ((c>' ')&&(c<=0x7f))
+
+/*
+ * Put a message from the AT-emulator into receive-buffer of tty,
+ * convert CR, LF, and BS to values in modem-registers 3, 4 and 5.
+ */
+static void isdn_tty_at_cout(char *msg, modem_info * info)
+{
+       struct tty_struct *tty;
+       atemu *m = &(dev->mdm.atmodem[info->line]);
+       char *p;
+       char c;
+       ulong flags;
+
+       if (!msg) {
+               printk(KERN_WARNING "isdn: Null-Message in isdn_tty_at_cout\n");
+               return;
+       }
+       save_flags(flags);
+       cli();
+       tty = info->tty;
+       for (p = msg; *p; p++) {
+               switch (*p) {
+               case '\r':
+                       c = m->mdmreg[3];
+                       break;
+               case '\n':
+                       c = m->mdmreg[4];
+                       break;
+               case '\b':
+                       c = m->mdmreg[5];
+                       break;
+               default:
+                       c = *p;
+               }
+               if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
+                       restore_flags(flags);
+                       return;
+               }
+               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);
+}
+
+/*
+ * Perform ATH Hangup
+ */
+static void isdn_tty_on_hook(modem_info * info)
+{
+       if (info->isdn_channel >= 0) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+               printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n");
+#endif
+               isdn_tty_modem_hup(info);
+               isdn_tty_modem_result(3, info);
+       }
+}
+
+static void isdn_tty_off_hook(void)
+{
+       printk(KERN_DEBUG "isdn_tty_off_hook\n");
+}
+
+#define PLUSWAIT1 (HZ/2)       /* 0.5 sec. */
+#define PLUSWAIT2 (HZ*3/2)     /* 1.5 sec */
+
+/*
+ * Check Buffer for Modem-escape-sequence, activate timer-callback to
+ * isdn_tty_modem_escape() if sequence found.
+ *
+ * Parameters:
+ *   p          pointer to databuffer
+ *   plus       escape-character
+ *   count      length of buffer
+ *   pluscount  count of valid escape-characters so far
+ *   lastplus   timestamp of last character
+ */
+static void isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount,
+                          int *lastplus, int from_user)
+{
+       char cbuf[3];
+
+       if (plus > 127)
+               return;
+       if (count > 3) {
+               p += count - 3;
+               count = 3;
+               *pluscount = 0;
+       }
+       if (from_user) {
+               memcpy_fromfs(cbuf, p, count);
+               p = cbuf;
+       }
+       while (count > 0) {
+               if (*(p++) == plus) {
+                       if ((*pluscount)++) {
+                               /* Time since last '+' > 0.5 sec. ? */
+                               if ((jiffies - *lastplus) > PLUSWAIT1)
+                                       *pluscount = 1;
+                       } else {
+                               /* Time since last non-'+' < 1.5 sec. ? */
+                               if ((jiffies - *lastplus) < PLUSWAIT2)
+                                       *pluscount = 0;
+                       }
+                       if ((*pluscount == 3) && (count = 1))
+                               isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, 1);
+                       if (*pluscount > 3)
+                               *pluscount = 1;
+               } else
+                       *pluscount = 0;
+               *lastplus = jiffies;
+               count--;
+       }
+}
+
+/*
+ * Return result of AT-emulator to tty-receive-buffer, depending on
+ * modem-register 12, bit 0 and 1.
+ * For CONNECT-messages also switch to online-mode.
+ * For RING-message handle auto-ATA if register 0 != 0
+ */
+void isdn_tty_modem_result(int code, modem_info * info)
+{
+       atemu *m = &dev->mdm.atmodem[info->line];
+       static char *msg[] =
+       {"OK", "CONNECT", "RING", "NO CARRIER", "ERROR",
+        "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
+        "RINGING", "NO MSN/EAZ"};
+       ulong flags;
+       char s[4];
+
+       switch (code) {
+       case 2:
+               m->mdmreg[1]++; /* RING */
+               if (m->mdmreg[1] == m->mdmreg[0]) {
+                       /* Accept incoming call */
+                       isdn_ctrl cmd;
+                       m->mdmreg[1] = 0;
+                       dev->mdm.msr[info->line] &= ~UART_MSR_RI;
+                       cmd.driver = info->isdn_driver;
+                       cmd.arg = info->isdn_channel;
+                       cmd.command = ISDN_CMD_ACCEPTD;
+                       dev->drv[info->isdn_driver]->interface->command(&cmd);
+               }
+               break;
+       case 3:
+               /* NO CARRIER */
+               save_flags(flags);
+               cli();
+               dev->mdm.msr[info->line] &= ~(UART_MSR_DCD | UART_MSR_RI);
+               if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+                       restore_flags(flags);
+                       return;
+               }
+               restore_flags(flags);
+               break;
+       case 1:
+       case 5:
+               dev->mdm.online[info->line] = 1;
+               break;
+       }
+       if (m->mdmreg[12] & 1) {
+               /* Show results */
+               isdn_tty_at_cout("\r\n", info);
+               if (m->mdmreg[12] & 2) {
+                       /* Show numeric results */
+                       sprintf(s, "%d", code);
+                       isdn_tty_at_cout(s, info);
+               } else {
+                       if (code == 2) {
+                               isdn_tty_at_cout("CALLER NUMBER: ", info);
+                               isdn_tty_at_cout(dev->num[info->drv_index], info);
+                               isdn_tty_at_cout("\r\n", info);
+                       }
+                       isdn_tty_at_cout(msg[code], info);
+                       if (code == 5) {
+                               /* Append Protocol to CONNECT message */
+                               isdn_tty_at_cout((m->mdmreg[14] != 3) ? "/X.75" : "/HDLC", info);
+                               if (m->mdmreg[13] & 2)
+                                       isdn_tty_at_cout("/T.70", info);
+                       }
+               }
+               isdn_tty_at_cout("\r\n", info);
+       }
+       if (code == 3) {
+               save_flags(flags);
+               cli();
+               if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+                       restore_flags(flags);
+                       return;
+               }
+               if ((info->flags & ISDN_ASYNC_CHECK_CD) &&
+                   (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+                      (info->flags & ISDN_ASYNC_CALLOUT_NOHUP))))
+                       tty_hangup(info->tty);
+               restore_flags(flags);
+       }
+}
+
+/*
+ * Display a modem-register-value.
+ */
+static void isdn_tty_show_profile(int ridx, modem_info * info)
+{
+       char v[6];
+
+       sprintf(v, "%d\r\n", dev->mdm.atmodem[info->line].mdmreg[ridx]);
+       isdn_tty_at_cout(v, info);
+}
+
+/*
+ * Get MSN-string from char-pointer, set pointer to end of number
+ */
+static void isdn_tty_get_msnstr(char *n, char **p)
+{
+       while ((*p[0] >= '0' && *p[0] <= '9') || (*p[0] == ','))
+               *n++ = *p[0]++;
+       *n = '\0';
+}
+
+/*
+ * Get phone-number from modem-commandbuffer
+ */
+static void isdn_tty_getdial(char *p, char *q)
+{
+       int first = 1;
+
+       while (strchr("0123456789,#.*WPTS-", *p) && *p) {
+               if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first))
+                       *q++ = *p;
+               p++;
+               first = 0;
+       }
+       *q = 0;
+}
+
+/*
+ * Parse and perform an AT-command-line.
+ *
+ * Parameter:
+ *   channel   index to line (minor-device)
+ */
+static void isdn_tty_parse_at(modem_info * info)
+{
+       atemu *m = &dev->mdm.atmodem[info->line];
+       char *p;
+       int mreg;
+       int mval;
+       int i;
+       char rb[100];
+       char ds[40];
+       isdn_ctrl cmd;
+
+#ifdef ISDN_DEBUG_AT
+       printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
+#endif
+       for (p = &m->mdmcmd[2]; *p;) {
+               switch (*p) {
+               case 'A':
+                       /* A - Accept incoming call */
+                       p++;
+                       if (m->mdmreg[1]) {
+#define FIDOBUG
+#ifdef FIDOBUG
+/* Variables fido... defined temporarily for finding a strange bug */
+                               driver *fido_drv;
+                               isdn_if *fido_if;
+                               int fido_isdn_driver;
+                               modem_info *fido_modem_info;
+                               int (*fido_command) (isdn_ctrl *);
+#endif
+                               /* Accept incoming call */
+                               m->mdmreg[1] = 0;
+                               dev->mdm.msr[info->line] &= ~UART_MSR_RI;
+                               cmd.driver = info->isdn_driver;
+                               cmd.command = ISDN_CMD_SETL2;
+                               cmd.arg = info->isdn_channel + (m->mdmreg[14] << 8);
+                               dev->drv[info->isdn_driver]->interface->command(&cmd);
+                               cmd.driver = info->isdn_driver;
+                               cmd.command = ISDN_CMD_SETL3;
+                               cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
+                               dev->drv[info->isdn_driver]->interface->command(&cmd);
+                               cmd.driver = info->isdn_driver;
+                               cmd.arg = info->isdn_channel;
+                               cmd.command = ISDN_CMD_ACCEPTD;
+#ifdef FIDOBUG
+                               fido_modem_info = info;
+                               fido_isdn_driver = fido_modem_info->isdn_driver;
+                               fido_drv = dev->drv[fido_isdn_driver];
+                               fido_if = fido_drv->interface;
+                               fido_command = fido_if->command;
+                               fido_command(&cmd);
+#else
+                               dev->drv[info->isdn_driver]->interface->command(&cmd);
+#endif
+                       } else {
+                               isdn_tty_modem_result(8, info);
+                               return;
+                       }
+                       break;
+               case 'D':
+                       /* D - Dial */
+                       isdn_tty_getdial(++p, ds);
+                       p += strlen(p);
+                       if (!strlen(m->msn))
+                               isdn_tty_modem_result(10, info);
+                       else if (strlen(ds))
+                               isdn_tty_dial(ds, info, m);
+                       else
+                               isdn_tty_modem_result(4, info);
+                       return;
+               case 'E':
+                       /* E - Turn Echo on/off */
+                       p++;
+                       switch (*p) {
+                       case '0':
+                               p++;
+                               m->mdmreg[12] &= ~4;
+                               break;
+                       case '1':
+                               p++;
+                               m->mdmreg[12] |= 4;
+                               break;
+                       default:
+                               isdn_tty_modem_result(4, info);
+                               return;
+                       }
+                       break;
+               case 'H':
+                       /* H - On/Off-hook */
+                       p++;
+                       switch (*p) {
+                       case '0':
+                               p++;
+                               isdn_tty_on_hook(info);
+                               break;
+                       case '1':
+                               p++;
+                               isdn_tty_off_hook();
+                               break;
+                       default:
+                               isdn_tty_on_hook(info);
+                               break;
+                       }
+                       break;
+               case 'I':
+                       /* I - Information */
+                       p++;
+                       isdn_tty_at_cout("ISDN for Linux  (c) by Fritz Elfert\r\n", info);
+                       switch (*p) {
+                       case '0':
+                       case '1':
+                               p++;
+                               break;
+                       default:
+                       }
+                       break;
+               case 'O':
+                       /* O - Go online */
+                       p++;
+                       if (dev->mdm.msr[info->line] & UART_MSR_DCD)    /* if B-Channel is up */
+                               isdn_tty_modem_result(5, info);
+                       else
+                               isdn_tty_modem_result(3, info);
+                       return;
+               case 'Q':
+                       /* Q - Turn Emulator messages on/off */
+                       p++;
+                       switch (*p) {
+                       case '0':
+                               p++;
+                               m->mdmreg[12] |= 1;
+                               break;
+                       case '1':
+                               p++;
+                               m->mdmreg[12] &= ~1;
+                               break;
+                       default:
+                               isdn_tty_modem_result(4, info);
+                               return;
+                       }
+                       break;
+               case 'S':
+                       /* S - Set/Get Register */
+                       p++;
+                       mreg = isdn_getnum(&p);
+                       if (mreg < 0 || mreg > ISDN_MODEM_ANZREG) {
+                               isdn_tty_modem_result(4, info);
+                               return;
+                       }
+                       switch (*p) {
+                       case '=':
+                               p++;
+                               mval = isdn_getnum(&p);
+                               if (mval >= 0 && mval <= 255) {
+                                       if ((mreg == 16) && ((mval * 16) > ISDN_SERIAL_XMIT_SIZE)) {
+                                               isdn_tty_modem_result(4, info);
+                                               return;
+                                       }
+                                       m->mdmreg[mreg] = mval;
+                               } else {
+                                       isdn_tty_modem_result(4, info);
+                                       return;
+                               }
+                               break;
+                       case '?':
+                               p++;
+                               isdn_tty_show_profile(mreg, info);
+                               return;
+                               break;
+                       default:
+                               isdn_tty_modem_result(4, info);
+                               return;
+                       }
+                       break;
+               case 'V':
+                       /* V - Numeric or ASCII Emulator-messages */
+                       p++;
+                       switch (*p) {
+                       case '0':
+                               p++;
+                               m->mdmreg[12] |= 2;
+                               break;
+                       case '1':
+                               p++;
+                               m->mdmreg[12] &= ~2;
+                               break;
+                       default:
+                               isdn_tty_modem_result(4, info);
+                               return;
+                       }
+                       break;
+               case 'Z':
+                       /* Z - Load Registers from Profile */
+                       p++;
+                       isdn_tty_modem_reset_regs(m, 1);
+                       break;
+               case '+':
+                       p++;
+                       switch (*p) {
+                       case 'F':
+                               break;
+                       }
+                       break;
+               case '&':
+                       p++;
+                       switch (*p) {
+                       case 'B':
+                               /* &B - Set Buffersize */
+                               p++;
+                               i = isdn_getnum(&p);
+                               if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE)) {
+                                       isdn_tty_modem_result(4, info);
+                                       return;
+                               }
+                               m->mdmreg[16] = i / 16;
+                               break;
+                       case 'D':
+                               /* &D - Set DCD-Low-behavior */
+                               p++;
+                               switch (isdn_getnum(&p)) {
+                               case 2:
+                                       m->mdmreg[12] &= ~32;
+                                       break;
+                               case 3:
+                                       m->mdmreg[12] |= 32;
+                                       break;
+                               default:
+                                       isdn_tty_modem_result(4, info);
+                                       return;
+                               }
+                               break;
+                       case 'E':
+                               /* &E -Set EAZ/MSN */
+                               p++;
+                               isdn_tty_get_msnstr(m->msn, &p);
+                               break;
+                       case 'F':
+                               /* &F -Set Factory-Defaults */
+                               p++;
+                               isdn_tty_reset_profile(m);
+                               isdn_tty_modem_reset_regs(m, 1);
+                               break;
+                       case 'S':
+                               /* &S - Set Windowsize */
+                               p++;
+                               i = isdn_getnum(&p);
+                               if ((i > 0) && (i < 9))
+                                       m->mdmreg[17] = i;
+                               else {
+                                       isdn_tty_modem_result(4, info);
+                                       return;
+                               }
+                               break;
+                       case 'V':
+                               /* &V - Show registers */
+                               p++;
+                               for (i = 0; i < ISDN_MODEM_ANZREG; i++) {
+                                       sprintf(rb, "S%d=%d%s", i, m->mdmreg[i], (i == 6) ? "\r\n" : " ");
+                                       isdn_tty_at_cout(rb, info);
+                               }
+                               sprintf(rb, "\r\nEAZ/MSN: %s\r\n", strlen(m->msn) ? m->msn : "None");
+                               isdn_tty_at_cout(rb, info);
+                               break;
+                       case 'W':
+                               /* &W - Write Profile */
+                               p++;
+                               switch (*p) {
+                               case '0':
+                                       p++;
+                                       modem_write_profile(m);
+                                       break;
+                               default:
+                                       isdn_tty_modem_result(4, info);
+                                       return;
+                               }
+                               break;
+                       case 'X':
+                               /* &X - Switch to BTX-Mode */
+                               p++;
+                               switch (*p) {
+                               case '0':
+                                       p++;
+                                       m->mdmreg[13] &= ~2;
+                                       break;
+                               case '1':
+                                       p++;
+                                       m->mdmreg[13] |= 2;
+                                       m->mdmreg[14] = 0;
+                                       m->mdmreg[16] = 7;
+                                       m->mdmreg[18] = 7;
+                                       m->mdmreg[19] = 0;
+                                       break;
+                               default:
+                                       isdn_tty_modem_result(4, info);
+                                       return;
+                               }
+                               break;
+                       default:
+                               isdn_tty_modem_result(4, info);
+                               return;
+                       }
+                       break;
+               default:
+                       isdn_tty_modem_result(4, info);
+                       return;
+               }
+       }
+       isdn_tty_modem_result(0, info);
+}
+
+/* Need own toupper() because standard-toupper is not available
+ * within modules.
+ */
+#define my_toupper(c) (((c>='a')&&(c<='z'))?(c&0xdf):c)
+
+/*
+ * Perform line-editing of AT-commands
+ *
+ * Parameters:
+ *   p        inputbuffer
+ *   count    length of buffer
+ *   channel  index to line (minor-device)
+ *   user     flag: buffer is in userspace
+ */
+static int isdn_tty_edit_at(const char *p, int count, modem_info * info, int user)
+{
+       atemu *m = &dev->mdm.atmodem[info->line];
+       int total = 0;
+       u_char c;
+       char eb[2];
+       int cnt;
+
+       for (cnt = count; cnt > 0; p++, cnt--) {
+               if (user)
+                       c = get_fs_byte(p);
+               else
+                       c = *p;
+               total++;
+               if (c == m->mdmreg[3] || c == m->mdmreg[4]) {
+                       /* Separator (CR oder LF) */
+                       m->mdmcmd[m->mdmcmdl] = 0;
+                       if (m->mdmreg[12] & 4) {
+                               eb[0] = c;
+                               eb[1] = 0;
+                               isdn_tty_at_cout(eb, info);
+                       }
+                       if (m->mdmcmdl >= 2)
+                               isdn_tty_parse_at(info);
+                       m->mdmcmdl = 0;
+                       continue;
+               }
+               if (c == m->mdmreg[5] && m->mdmreg[5] < 128) {
+                       /* Backspace-Funktion */
+                       if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) {
+                               if (m->mdmcmdl)
+                                       m->mdmcmdl--;
+                               if (m->mdmreg[12] & 4)
+                                       isdn_tty_at_cout("\b", info);
+                       }
+                       continue;
+               }
+               if (cmdchar(c)) {
+                       if (m->mdmreg[12] & 4) {
+                               eb[0] = c;
+                               eb[1] = 0;
+                               isdn_tty_at_cout(eb, info);
+                       }
+                       if (m->mdmcmdl < 255) {
+                               c = my_toupper(c);
+                               switch (m->mdmcmdl) {
+                               case 0:
+                                       if (c == 'A')
+                                               m->mdmcmd[m->mdmcmdl++] = c;
+                                       break;
+                               case 1:
+                                       if (c == 'T')
+                                               m->mdmcmd[m->mdmcmdl++] = c;
+                                       break;
+                               default:
+                                       m->mdmcmd[m->mdmcmdl++] = c;
+                               }
+                       }
+               }
+       }
+       return total;
+}
+
+/*
+ * Switch all modem-channels who are online and got a valid
+ * escape-sequence 1.5 seconds ago, to command-mode.
+ * This function is called every second via timer-interrupt from within 
+ * timer-dispatcher isdn_timer_function()
+ */
+void isdn_tty_modem_escape(void)
+{
+       int ton = 0;
+       int i;
+       int midx;
+
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+               if (USG_MODEM(dev->usage[i]))
+                       if ((midx = dev->m_idx[i]) >= 0)
+                               if (dev->mdm.online[midx]) {
+                                       ton = 1;
+                                       if ((dev->mdm.atmodem[midx].pluscount == 3) &&
+                                           ((jiffies - dev->mdm.atmodem[midx].lastplus) > PLUSWAIT2)) {
+                                               dev->mdm.atmodem[midx].pluscount = 0;
+                                               dev->mdm.online[midx] = 0;
+                                               isdn_tty_modem_result(0, &dev->mdm.info[midx]);
+                                       }
+                               }
+       isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton);
+}
+
+/*
+ * Put a RING-message to all modem-channels who have the RI-bit set.
+ * This function is called every second via timer-interrupt from within 
+ * timer-dispatcher isdn_timer_function()
+ */
+void isdn_tty_modem_ring(void)
+{
+       int ton = 0;
+       int i;
+       int midx;
+
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+               if (USG_MODEM(dev->usage[i]))
+                       if ((midx = dev->m_idx[i]) >= 0)
+                               if (dev->mdm.msr[midx] & UART_MSR_RI) {
+                                       ton = 1;
+                                       isdn_tty_modem_result(2, &dev->mdm.info[midx]);
+                               }
+       isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton);
+}
+
+void isdn_tty_modem_xmit(void)
+{
+       int ton = 0;
+       int i;
+       int midx;
+       char *bufptr;
+       int buflen;
+
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+               if (USG_MODEM(dev->usage[i]))
+                       if ((midx = dev->m_idx[i]) >= 0)
+                               if (dev->mdm.online[midx]) {
+                                       modem_info *info = &(dev->mdm.info[midx]);
+                                       ulong flags;
+
+                                       save_flags(flags);
+                                       cli();
+                                       if (info->xmit_count > 0) {
+                                               struct tty_struct *tty = info->tty;
+                                               ton = 1;
+#if 0
+                                               printk(KERN_DEBUG "WB2: %d\n", info->xmit_count);
+#endif
+                                               bufptr = info->xmit_buf;
+                                               buflen = info->xmit_count;
+                                               if (dev->mdm.atmodem[midx].mdmreg[13] & 2) {
+                                                       /* Add T.70 simplyfied header */
+#ifdef ISDN_DEBUG_MODEM_DUMP
+                                                       isdn_dumppkt("T70pack3:", bufptr, buflen, 40);
+#endif
+                                                       bufptr -= 4;
+                                                       buflen += 4;
+                                                       memcpy(bufptr, "\1\0\1\0", 4);
+#ifdef ISDN_DEBUG_MODEM_DUMP
+                                                       isdn_dumppkt("T70pack4:", bufptr, buflen, 40);
+#endif
+                                               }
+                                               if (dev->drv[info->isdn_driver]->interface->
+                                                   writebuf(info->isdn_driver, info->isdn_channel, bufptr, buflen, 0) > 0) {
+                                                       info->xmit_count = 0;
+                                                       info->xmit_size = dev->mdm.atmodem[midx].mdmreg[16] * 16;
+#if FUTURE
+                                                       info->send_outstanding++;
+                                                       dev->mdm.msr[midx] &= ~UART_MSR_CTS;
+#endif
+                                                       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+                                                           tty->ldisc.write_wakeup)
+                                                               (tty->ldisc.write_wakeup) (tty);
+                                                       wake_up_interruptible(&tty->write_wait);
+                                               }
+                                       }
+                                       restore_flags(flags);
+                               }
+       isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
+}
+
+#if FUTURE
+/*
+ * A packet has been output successfully.
+ * Search the tty-devices for an aproppriate device, decrement it's
+ * counter for outstanding packets, and set CTS if this counter reaches 0.
+ */
+void isdn_tty_bsent(int drv, int chan)
+{
+       int i;
+       ulong flags;
+
+       save_flags(flags);
+       cli();
+       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+               modem_info *info = &dev->mdm.info[i];
+               if ((info->isdn_driver == drv) &&
+                   (info->isdn_channel == chan) &&
+                   (info->send_outstanding)) {
+                       if (!(--info->send_outstanding))
+                               dev->mdm.msr[i] |= UART_MSR_CTS;
+                       restore_flags(flags);
+                       return;
+               }
+       }
+       restore_flags(flags);
+       return;
+}
+#endif                         /* FUTURE */
diff --git a/drivers/isdn/isdn_tty.h b/drivers/isdn/isdn_tty.h
new file mode 100644 (file)
index 0000000..e128464
--- /dev/null
@@ -0,0 +1,39 @@
+/* $Id: isdn_tty.h,v 1.1 1996/01/10 21:39:22 fritz Exp fritz $
+ *
+ * header for Linux ISDN subsystem, tty related functions (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * 
+ * 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_tty.h,v $
+ * Revision 1.1  1996/01/10 21:39:22  fritz
+ * Initial revision
+ *
+ */
+
+extern void  isdn_tty_modem_result(int, modem_info *);
+extern void  isdn_tty_modem_escape(void);
+extern void  isdn_tty_modem_ring(void);
+extern void  isdn_tty_modem_xmit(void);
+extern void  isdn_tty_modem_hup(modem_info *);
+extern int   isdn_tty_modem_init(void);
+extern void  isdn_tty_readmodem(void);
+extern int   isdn_tty_try_read(int, u_char *, int);
+extern int   isdn_tty_find_icall(int, int, char *);
+#if FUTURE
+extern void  isdn_tty_bsent(int, int);
+#endif
diff --git a/drivers/isdn/teles/Makefile b/drivers/isdn/teles/Makefile
new file mode 100644 (file)
index 0000000..a252f46
--- /dev/null
@@ -0,0 +1,17 @@
+L_OBJS :=
+M_OBJS :=
+O_OBJS := mod.o card.o config.o buffers.o tei.o isdnl2.o isdnl3.o \
+llglue.o q931.o callc.o fsm.o
+
+O_TARGET :=
+ifeq ($(CONFIG_ISDN_DRV_TELES),y)
+  O_TARGET += teles.o
+else
+  ifeq ($(CONFIG_ISDN_DRV_TELES),m)
+    O_TARGET += teles.o
+    M_OBJS += teles.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/isdn/teles/buffers.c b/drivers/isdn/teles/buffers.c
new file mode 100644 (file)
index 0000000..3d57142
--- /dev/null
@@ -0,0 +1,315 @@
+#define __NO_VERSION__
+#include "teles.h"
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+
+void
+BufPoolInit(struct BufPool *bp, int order, int bpps,
+           int maxpages)
+{
+#ifdef DEBUG_MAGIC
+       generateerror
+           bp->magic = 010167;
+#endif
+
+#if 0
+       printk(KERN_DEBUG "BufPoolInit bp %x\n", bp);
+#endif
+
+       bp->freelist = NULL;
+       bp->pageslist = NULL;
+       bp->pageorder = order;
+       bp->pagescount = 0;
+       bp->bpps = bpps;
+       bp->bufsize = BUFFER_SIZE(order, bpps);
+       bp->maxpages = maxpages;
+}
+
+int
+BufPoolAdd(struct BufPool *bp, int priority)
+{
+       struct Pages   *ptr;
+       long            flags;
+       byte           *bptr;
+       int             i;
+       struct BufHeader *bh = NULL, *prev, *first;
+
+#if 0
+       printk(KERN_DEBUG "BufPoolAdd bp %x\n", bp);
+#endif
+
+       ptr = (struct Pages *) __get_free_pages(priority,bp->pageorder,~0);
+       if (!ptr) {
+               printk(KERN_WARNING "BufPoolAdd couldn't get pages!\n");
+               return (-1);
+       }
+#if 0
+       printk(KERN_DEBUG "Order %d pages allocated at %x\n", bp->pageorder, ptr);
+#endif
+
+       ptr->next = bp->pageslist;
+       bp->pageslist = ptr;
+       bp->pagescount++;
+
+       bptr = (byte *) ptr + sizeof(struct Pages *);
+
+       i = bp->bpps;
+       first = (struct BufHeader *) bptr;
+       prev = NULL;
+       while (i--) {
+               bh = (struct BufHeader *) bptr;
+#ifdef DEBUG_MAGIC
+               bh->magic = 020167;
+#endif
+               bh->next = prev;
+               prev = bh;
+               bh->bp = bp;
+               bptr += PART_SIZE(bp->pageorder, bp->bpps);
+       }
+
+       save_flags(flags);
+       cli();
+       first->next = bp->freelist;
+       bp->freelist = bh;
+       restore_flags(flags);
+       return (0);
+}
+
+void
+BufPoolFree(struct BufPool *bp)
+{
+       struct Pages   *p;
+
+#if 0
+       printk(KERN_DEBUG "BufPoolFree bp %x\n", bp);
+#endif
+
+       while (bp->pagescount--) {
+               p = bp->pageslist->next;
+               free_pages((unsigned long) bp->pageslist, bp->pageorder);
+#if 0
+               printk(KERN_DEBUG "Free pages %x order %d\n", bp->pageslist, bp->pageorder);
+#endif
+               bp->pageslist = p;
+       }
+}
+
+int
+BufPoolGet(struct BufHeader **bh,
+          struct BufPool *bp, int priority, void *heldby, int where)
+{
+       long            flags;
+       int             i;
+
+#ifdef DEBUG_MAGIC
+       if (bp->magic != 010167) {
+               printk(KERN_DEBUG "BufPoolGet: not a BufHeader\n");
+               return (-1);
+       }
+#endif
+
+       save_flags(flags);
+       cli();
+       i = 0;
+       while (!0) {
+               if (bp->freelist) {
+                       *bh = bp->freelist;
+                       bp->freelist = bp->freelist->next;
+                       (*bh)->heldby = heldby;
+                       (*bh)->where = where;
+                       restore_flags(flags);
+                       return (0);
+               }
+               if ((i == 0) && (bp->pagescount < bp->maxpages)) {
+                        if (BufPoolAdd(bp, priority)) {
+                                restore_flags(flags);
+                                return -1;
+                        }
+                       i++;
+               } else {
+                       *bh = NULL;
+                       restore_flags(flags);
+                       return (-1);
+               }
+       }
+
+}
+
+void
+BufPoolRelease(struct BufHeader *bh)
+{
+       struct BufPool *bp;
+       long            flags;
+
+#ifdef DEBUG_MAGIC
+       if (bh->magic != 020167) {
+               printk(KERN_DEBUG "BufPoolRelease: not a BufHeader\n");
+               printk(KERN_DEBUG "called from %x\n", __builtin_return_address(0));
+               return;
+       }
+#endif
+
+       bp = bh->bp;
+
+#ifdef DEBUG_MAGIC
+       if (bp->magic != 010167) {
+               printk(KERN_DEBUG "BufPoolRelease: not a BufPool\n");
+               return;
+       }
+#endif
+
+       save_flags(flags);
+       cli();
+       bh->next = bp->freelist;
+       bp->freelist = bh;
+       restore_flags(flags);
+}
+
+void
+BufQueueLink(struct BufQueue *bq,
+            struct BufHeader *bh)
+{
+       unsigned long   flags;
+
+       save_flags(flags);
+       cli();
+       if (!bq->head)
+               bq->head = bh;
+       if (bq->tail)
+               bq->tail->next = bh;
+       bq->tail = bh;
+       bh->next = NULL;
+       restore_flags(flags);
+}
+
+void
+BufQueueLinkFront(struct BufQueue *bq,
+                 struct BufHeader *bh)
+{
+       unsigned long   flags;
+
+       save_flags(flags);
+       cli();
+       bh->next = bq->head;
+       bq->head = bh;
+       if (!bq->tail)
+               bq->tail = bh;
+       restore_flags(flags);
+}
+
+int
+BufQueueUnlink(struct BufHeader **bh, struct BufQueue *bq)
+{
+       long            flags;
+
+       save_flags(flags);
+       cli();
+
+       if (bq->head) {
+               if (bq->tail == bq->head)
+                       bq->tail = NULL;
+               *bh = bq->head;
+               bq->head = (*bh)->next;
+               restore_flags(flags);
+               return (0);
+       } else {
+               restore_flags(flags);
+               return (-1);
+       }
+}
+
+void
+BufQueueInit(struct BufQueue *bq)
+{
+#ifdef DEBUG_MAGIC
+       bq->magic = 030167;
+#endif
+       bq->head = NULL;
+       bq->tail = NULL;
+}
+
+void
+BufQueueRelease(struct BufQueue *bq)
+{
+       struct BufHeader *bh;
+
+       while (bq->head) {
+               BufQueueUnlink(&bh, bq);
+               BufPoolRelease(bh);
+       }
+}
+
+int
+BufQueueLength(struct BufQueue *bq)
+{
+       int             i = 0;
+       struct BufHeader *bh;
+
+       bh = bq->head;
+       while (bh) {
+               i++;
+               bh = bh->next;
+       }
+       return (i);
+}
+
+void
+BufQueueDiscard(struct BufQueue *q, int pr, void *heldby,
+               int releasetoo)
+{
+       long            flags;
+       struct BufHeader *sp;
+
+       save_flags(flags);
+       cli();
+
+       while (!0) {
+               sp = q->head;
+               if (!sp)
+                       break;
+               if ((sp->primitive == pr) && (sp->heldby == heldby)) {
+                       q->head = sp->next;
+                       if (q->tail == sp)
+                               q->tail = NULL;
+                       if (releasetoo)
+                               BufPoolRelease(sp);
+               } else
+                       break;
+       }
+
+       sp = q->head;
+       if (sp)
+               while (sp->next) {
+                       if ((sp->next->primitive == pr) && (sp->next->heldby == heldby)) {
+                               if (q->tail == sp->next)
+                                       q->tail = sp;
+                               if (releasetoo)
+                                       BufPoolRelease(sp->next);
+                               sp->next = sp->next->next;
+                       } else
+                               sp = sp->next;
+               }
+       restore_flags(flags);
+}
+
+void
+Sfree(byte * ptr)
+{
+#if 0
+       printk(KERN_DEBUG "Sfree %x\n", ptr);
+#endif
+       kfree(ptr);
+}
+
+byte           *
+Smalloc(int size, int pr, char *why)
+{
+       byte           *p;
+
+       p = (byte *) kmalloc(size, pr);
+#if 0
+       printk(KERN_DEBUG "Smalloc %s size %d res %x\n", why, size, p);
+#endif
+       return (p);
+}
diff --git a/drivers/isdn/teles/callc.c b/drivers/isdn/teles/callc.c
new file mode 100644 (file)
index 0000000..c08d34b
--- /dev/null
@@ -0,0 +1,1316 @@
+#define __NO_VERSION__
+#include "teles.h"
+
+extern struct IsdnCard cards[];
+extern int      nrcards;
+extern int      drid;
+extern isdn_if  iif;
+extern void     teles_mod_dec_use_count(void);
+extern void     teles_mod_inc_use_count(void);
+
+static int      init_ds(int chan, int incoming);
+static void     release_ds(int chan);
+static char    *strcpyupto(char *dest, char *src, char upto);
+
+static struct Fsm callcfsm =
+{NULL, 0, 0},   lcfsm =
+{NULL, 0, 0};
+
+struct Channel *chanlist;
+static int      chancount = 0;
+unsigned int    debugflags = 0;
+
+#define TMR_DCHAN_EST 2000
+
+static void
+stat_debug(struct Channel *chanp, char *s)
+{
+       char            tmp[100], tm[32];
+
+       jiftime(tm, jiffies);
+       sprintf(tmp, "%s Channel %d HL->LL %s\n", tm, chanp->chan, s);
+       teles_putstatus(tmp);
+}
+
+#ifdef DEFINED_BUT_NOT_USED
+static void
+stat_error(struct Channel *chanp, char *s)
+{
+        char            tmp[100];
+
+        sprintf(tmp, "Channel %d: %s\n", chanp->chan, s);
+        teles_putstatus(tmp);
+}
+#endif
+
+enum {
+        ST_NULL,           /*  0 inactive                                               */
+        ST_OUT,            /*  1 outgoing, awaiting SETUP confirm                       */
+        ST_CLEAR,          /*  2 call release, awaiting RELEASE confirm                 */
+        ST_OUT_W,          /*  3 outgoing, awaiting d-channel establishment             */
+        ST_REL_W,          /*  4 awaiting d-channel release                             */
+        ST_IN_W,           /*  5 incoming, awaiting d-channel establishment             */
+        ST_IN,             /*  6 incoming call received                                 */
+        ST_IN_SETUP,       /*  7 incoming, SETUP response sent                          */
+        ST_IN_DACT,        /*  8 incoming connected, no b-channel prot.                 */
+        ST_OUT_ESTB,       /* 10 outgoing connected, awaiting b-channel prot. estbl.    */
+        ST_ACTIVE,         /* 11 active, b channel prot. established                    */
+        ST_BC_HANGUP,      /* 12 call clear. (initiator), awaiting b channel prot. rel. */
+        ST_PRO_W,          /* 13 call clear. (initiator), DISCONNECT req. sent          */
+        ST_ANT_W,          /* 14 call clear. (receiver), awaiting DISCONNECT ind.       */
+        ST_DISC_BC_HANGUP, /*    d channel gone, wait for b channel deactivation        */
+        ST_D_ERR,          /*    d channel released while active                        */
+};
+
+#define STATE_COUNT (ST_D_ERR+1)
+
+static char    *strState[] =
+{
+        "ST_NULL",
+        "ST_OUT",
+        "ST_CLEAR",
+        "ST_OUT_W",
+        "ST_REL_W",
+        "ST_IN_W",
+        "ST_IN",
+        "ST_IN_SETUP",
+        "ST_IN_DACT",
+        "ST_OUT_ESTB",
+        "ST_ACTIVE",
+        "ST_BC_HANGUP",
+        "ST_PRO_W",
+        "ST_ANT_W",
+        "ST_DISC_BC_HANGUP",
+        "ST_D_ERR",
+};
+
+enum {
+        EV_DIAL,           /*  0 */
+        EV_SETUP_CNF,      /*  1 */
+        EV_ACCEPTB,        /*  2 */
+        EV_DISCONNECT_CNF, /*  5 */
+        EV_DISCONNECT_IND, /*  6 */
+        EV_RELEASE_CNF,    /*  7 */
+        EV_DLEST,          /*  8 */
+        EV_DLRL,           /*  9 */
+        EV_SETUP_IND,      /* 10 */
+        EV_RELEASE_IND,    /* 11 */
+        EV_ACCEPTD,        /* 12 */
+        EV_SETUP_CMPL_IND, /* 13 */
+        EV_BC_EST,         /* 14 */
+        EV_WRITEBUF,       /* 15 */
+        EV_DATAIN,         /* 16 */
+        EV_HANGUP,         /* 17 */
+        EV_BC_REL,         /* 18 */
+        EV_CINF,           /* 19 */
+};
+
+#define EVENT_COUNT (EV_CINF+1)
+
+static char    *strEvent[] =
+{
+        "EV_DIAL",
+        "EV_SETUP_CNF",
+        "EV_ACCEPTB",
+        "EV_DISCONNECT_CNF",
+        "EV_DISCONNECT_IND",
+        "EV_RELEASE_CNF",
+        "EV_DLEST",
+        "EV_DLRL",
+        "EV_SETUP_IND",
+        "EV_RELEASE_IND",
+        "EV_ACCEPTD",
+        "EV_SETUP_CMPL_IND",
+        "EV_BC_EST",
+        "EV_WRITEBUF",
+        "EV_DATAIN",
+        "EV_HANGUP",
+        "EV_BC_REL",
+        "EV_CINF",
+};
+
+enum {
+        ST_LC_NULL,
+        ST_LC_ACTIVATE_WAIT,
+        ST_LC_DELAY,
+        ST_LC_ESTABLISH_WAIT,
+        ST_LC_CONNECTED,
+        ST_LC_RELEASE_WAIT,
+};
+
+#define LC_STATE_COUNT (ST_LC_RELEASE_WAIT+1)
+
+static char    *strLcState[] =
+{
+        "ST_LC_NULL",
+        "ST_LC_ACTIVATE_WAIT",
+        "ST_LC_DELAY",
+        "ST_LC_ESTABLISH_WAIT",
+        "ST_LC_CONNECTED",
+        "ST_LC_RELEASE_WAIT",
+};
+
+enum {
+        EV_LC_ESTABLISH,
+        EV_LC_PH_ACTIVATE,
+        EV_LC_PH_DEACTIVATE,
+        EV_LC_DL_ESTABLISH,
+        EV_LC_TIMER,
+        EV_LC_DL_RELEASE,
+        EV_LC_RELEASE,
+};
+
+#define LC_EVENT_COUNT (EV_LC_RELEASE+1)
+
+static char    *strLcEvent[] =
+{
+        "EV_LC_ESTABLISH",
+        "EV_LC_PH_ACTIVATE",
+        "EV_LC_PH_DEACTIVATE",
+        "EV_LC_DL_ESTABLISH",
+        "EV_LC_TIMER",
+        "EV_LC_DL_RELEASE",
+        "EV_LC_RELEASE",
+};
+
+#define LC_D  0
+#define LC_B  1
+
+static int
+my_atoi(char *s)
+{
+        int             i, n;
+
+        n = 0;
+        if (!s)
+                return -1;
+        for (i = 0; *s >= '0' && *s <= '9'; i++, s++)
+                n = 10 * n + (*s - '0');
+        return n;
+}
+
+/*
+ * Dial out
+ */
+static void
+r1(struct FsmInst *fi, int event, void *arg)
+{
+        isdn_ctrl      *ic = arg;
+        struct Channel *chanp = fi->userdata;
+        char           *ptr;
+        char            sis[3];
+
+        /* Destination Phone-Number */
+        ptr = strcpyupto(chanp->para.called, ic->num, ',');
+        /* Source Phone-Number */
+        ptr = strcpyupto(chanp->para.calling, ptr + 1, ',');
+        if (!strcmp(chanp->para.calling, "0"))
+                chanp->para.calling[0] = '\0';
+
+        /* Service-Indicator 1 */
+        ptr = strcpyupto(sis, ptr + 1, ',');
+        chanp->para.info = my_atoi(sis);
+
+        /* Service-Indicator 2 */
+        ptr = strcpyupto(sis, ptr + 1, '\0');
+        chanp->para.info2 = my_atoi(sis);
+
+        chanp->l2_active_protocol = chanp->l2_protocol;
+        chanp->incoming = 0;
+        chanp->lc_b.l2_start = !0;
+
+        switch (chanp->l2_active_protocol) {
+          case (ISDN_PROTO_L2_X75I):
+                  chanp->lc_b.l2_establish = !0;
+                  break;
+          case (ISDN_PROTO_L2_HDLC):
+                  chanp->lc_b.l2_establish = 0;
+                  break;
+          default:
+                  printk(KERN_WARNING "r1 unknown protocol\n");
+                  break;
+        }
+
+        FsmChangeState(fi, ST_OUT_W);
+        FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+ll_hangup(struct Channel *chanp, int bchantoo)
+{
+        isdn_ctrl       ic;
+
+        if (bchantoo) {
+                if (chanp->debug & 1)
+                        stat_debug(chanp, "STAT_BHUP");
+                ic.driver = drid;
+                ic.command = ISDN_STAT_BHUP;
+                ic.arg = chanp->chan;
+                iif.statcallb(&ic);
+        }
+        if (chanp->debug & 1)
+                stat_debug(chanp, "STAT_DHUP");
+        ic.driver = drid;
+        ic.command = ISDN_STAT_DHUP;
+        ic.arg = chanp->chan;
+        iif.statcallb(&ic);
+}
+
+static void
+r2(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+
+        FsmChangeState(fi, ST_CLEAR);
+        ll_hangup(chanp, 0);
+}
+
+static void
+r3(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+        FsmChangeState(fi, ST_REL_W);
+}
+
+static void
+r4(struct FsmInst *fi, int event, void *arg)
+{
+        FsmChangeState(fi, ST_NULL);
+}
+
+static void
+r5(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->para.callref = chanp->outcallref;
+
+        chanp->outcallref++;
+        if (chanp->outcallref == 128)
+                chanp->outcallref = 64;
+
+        chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL);
+
+        FsmChangeState(fi, ST_OUT);
+}
+
+static void
+r6(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_IN_W);
+        FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+r7(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+        isdn_ctrl       ic;
+
+        chanp->is.l4.l4l3(&chanp->is, CC_ALERTING_REQ, NULL);
+
+        FsmChangeState(fi, ST_IN);
+
+        /*
+         * Report incoming calls only once to linklevel, use octet 3 of
+         * channel identification information element. (it's value
+         * is copied to chanp->para.bchannel in l3s12(), file isdnl3.c)
+         */
+        if (((chanp->chan & 1) + 1) & chanp->para.bchannel) {
+                if (chanp->debug & 1)
+                        stat_debug(chanp, "STAT_ICALL");
+                ic.driver = drid;
+                ic.command = ISDN_STAT_ICALL;
+                ic.arg = chanp->chan;
+                /*
+                 * No need to return "unknown" for calls without OAD,
+                 * cause that's handled in linklevel now (replaced by '0')
+                 */
+                sprintf(ic.num, "%s,%d,0,%s", chanp->para.calling, chanp->para.info,
+                        chanp->para.called);
+                iif.statcallb(&ic);
+        }
+}
+
+static void
+r8(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_IN_SETUP);
+        chanp->is.l4.l4l3(&chanp->is, CC_SETUP_RSP, NULL);
+
+}
+
+static void
+r9(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_IN_DACT);
+
+        chanp->l2_active_protocol = chanp->l2_protocol;
+        chanp->incoming = !0;
+        chanp->lc_b.l2_start = 0;
+
+        switch (chanp->l2_active_protocol) {
+          case (ISDN_PROTO_L2_X75I):
+                  chanp->lc_b.l2_establish = !0;
+                  break;
+          case (ISDN_PROTO_L2_HDLC):
+                  chanp->lc_b.l2_establish = 0;
+                  break;
+          default:
+                  printk(KERN_WARNING "r9 unknown protocol\n");
+                  break;
+        }
+
+        init_ds(chanp->chan, !0);
+
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+r10(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_OUT_ESTB);
+
+        init_ds(chanp->chan, 0);
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+
+}
+
+static void
+r12(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->debug & 1)
+                stat_debug(chanp, "STAT_DCONN");
+        ic.driver = drid;
+        ic.command = ISDN_STAT_DCONN;
+        ic.arg = chanp->chan;
+        iif.statcallb(&ic);
+
+        if (chanp->debug & 1)
+                stat_debug(chanp, "STAT_BCONN");
+        ic.driver = drid;
+        ic.command = ISDN_STAT_BCONN;
+        ic.arg = chanp->chan;
+        iif.statcallb(&ic);
+}
+
+#ifdef DEFINED_BUT_NOT_USED
+static void
+prp(byte * p, int size)
+{
+        while (size--)
+                printk("%2x ", *p++);
+        printk("\n");
+}
+#endif
+
+static void
+r15(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->data_open = 0;
+        FsmChangeState(fi, ST_BC_HANGUP);
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+r16(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_PRO_W);
+        chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+}
+
+static void
+r17(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->data_open = 0;
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_ANT_W);
+}
+
+static void
+r18(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_REL_W);
+        FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+
+        ll_hangup(chanp, !0);
+}
+
+static void
+r19(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_CLEAR);
+
+        chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+
+        ll_hangup(chanp, !0);
+}
+
+static void
+r20(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_NULL);
+
+        ll_hangup(chanp, 0);
+}
+
+static void
+r21(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->data_open = 0;
+        FsmChangeState(fi, ST_DISC_BC_HANGUP);
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+r22(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_CLEAR);
+
+        chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+
+        ll_hangup(chanp, !0);
+}
+
+static void
+r23(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_PRO_W);
+        chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+}
+
+static void
+r24(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->data_open = 0;
+        FsmChangeState(fi, ST_D_ERR);
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+r25(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_NULL);
+
+        ll_hangup(chanp, !0);
+}
+
+static void
+r26(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+        isdn_ctrl       ic;
+
+
+        ic.driver = drid;
+        ic.command = ISDN_STAT_CINF;
+        ic.arg = chanp->chan;
+        sprintf(ic.num, "%d", chanp->para.chargeinfo);
+        iif.statcallb(&ic);
+}
+
+
+
+static struct FsmNode fnlist[] =
+{
+        {ST_NULL, EV_DIAL, r1},
+        {ST_OUT_W, EV_DLEST, r5},
+        {ST_OUT_W, EV_DLRL, r20},
+        {ST_OUT, EV_DISCONNECT_IND, r2},
+        {ST_CLEAR, EV_RELEASE_CNF, r3},
+        {ST_REL_W, EV_DLRL, r4},
+        {ST_NULL, EV_SETUP_IND, r6},
+        {ST_IN_W, EV_DLEST, r7},
+        {ST_IN, EV_RELEASE_IND, r3},
+        {ST_IN, EV_ACCEPTD, r8},
+        {ST_IN_SETUP, EV_SETUP_CMPL_IND, r9},
+        {ST_OUT, EV_SETUP_CNF, r10},
+        {ST_OUT_ESTB, EV_BC_EST, r12},
+        {ST_OUT_ESTB, EV_BC_REL, r23},
+        {ST_IN_DACT, EV_BC_EST, r12},
+        {ST_ACTIVE, EV_HANGUP, r15},
+        {ST_BC_HANGUP, EV_BC_REL, r16},
+        {ST_BC_HANGUP, EV_DISCONNECT_IND, r21},
+        {ST_ACTIVE, EV_BC_REL, r17},
+        {ST_ACTIVE, EV_DISCONNECT_IND, r21},
+        {ST_ACTIVE, EV_DLRL, r24},
+        {ST_ACTIVE, EV_CINF, r26},
+        {ST_PRO_W, EV_RELEASE_IND, r18},
+        {ST_ANT_W, EV_DISCONNECT_IND, r19},
+        {ST_DISC_BC_HANGUP, EV_BC_REL, r22},
+        {ST_D_ERR, EV_BC_REL, r25},
+};
+
+#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
+
+static void
+lc_r1(struct FsmInst *fi, int event, void *arg)
+{
+        struct LcFsm   *lf = fi->userdata;
+
+        FsmChangeState(fi, ST_LC_ACTIVATE_WAIT);
+        FsmAddTimer(&lf->act_timer, 1000, EV_LC_TIMER, NULL, 50);
+        lf->st->ma.manl1(lf->st, PH_ACTIVATE, NULL);
+
+}
+
+static void
+lc_r6(struct FsmInst *fi, int event, void *arg)
+{
+        struct LcFsm   *lf = fi->userdata;
+
+        FsmDelTimer(&lf->act_timer, 50);
+        FsmChangeState(fi, ST_LC_DELAY);
+        FsmAddTimer(&lf->act_timer, 40, EV_LC_TIMER, NULL, 51);
+}
+
+static void
+lc_r2(struct FsmInst *fi, int event, void *arg)
+{
+        struct LcFsm   *lf = fi->userdata;
+
+        if (lf->l2_establish) {
+                FsmChangeState(fi, ST_LC_ESTABLISH_WAIT);
+                if (lf->l2_start)
+                        lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL);
+        } else {
+                FsmChangeState(fi, ST_LC_CONNECTED);
+                lf->lccall(lf, LC_ESTABLISH, NULL);
+        }
+}
+
+static void
+lc_r3(struct FsmInst *fi, int event, void *arg)
+{
+        struct LcFsm   *lf = fi->userdata;
+
+        FsmChangeState(fi, ST_LC_CONNECTED);
+        lf->lccall(lf, LC_ESTABLISH, NULL);
+}
+
+static void
+lc_r4(struct FsmInst *fi, int event, void *arg)
+{
+        struct LcFsm   *lf = fi->userdata;
+
+        if (lf->l2_establish) {
+                FsmChangeState(fi, ST_LC_RELEASE_WAIT);
+                lf->st->ma.manl2(lf->st, DL_RELEASE, NULL);
+        } else {
+                FsmChangeState(fi, ST_LC_NULL);
+                lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+                lf->lccall(lf, LC_RELEASE, NULL);
+        }
+}
+
+static void
+lc_r5(struct FsmInst *fi, int event, void *arg)
+{
+        struct LcFsm   *lf = fi->userdata;
+
+        FsmChangeState(fi, ST_LC_NULL);
+        lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+        lf->lccall(lf, LC_RELEASE, NULL);
+}
+
+static struct FsmNode LcFnList[] =
+{
+        {ST_LC_NULL, EV_LC_ESTABLISH, lc_r1},
+        {ST_LC_ACTIVATE_WAIT, EV_LC_PH_ACTIVATE, lc_r6},
+        {ST_LC_DELAY, EV_LC_TIMER, lc_r2},
+        {ST_LC_ESTABLISH_WAIT, EV_LC_DL_ESTABLISH, lc_r3},
+        {ST_LC_CONNECTED, EV_LC_RELEASE, lc_r4},
+        {ST_LC_CONNECTED, EV_LC_DL_RELEASE, lc_r5},
+        {ST_LC_RELEASE_WAIT, EV_LC_DL_RELEASE, lc_r5},
+        {ST_LC_ACTIVATE_WAIT, EV_LC_TIMER, lc_r5},
+        {ST_LC_ESTABLISH_WAIT, EV_LC_DL_RELEASE, lc_r5},
+};
+
+#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode))
+
+void
+CallcNew(void)
+{
+        callcfsm.state_count = STATE_COUNT;
+        callcfsm.event_count = EVENT_COUNT;
+        callcfsm.strEvent = strEvent;
+        callcfsm.strState = strState;
+        FsmNew(&callcfsm, fnlist, FNCOUNT);
+
+        lcfsm.state_count = LC_STATE_COUNT;
+        lcfsm.event_count = LC_EVENT_COUNT;
+        lcfsm.strEvent = strLcEvent;
+        lcfsm.strState = strLcState;
+        FsmNew(&lcfsm, LcFnList, LC_FN_COUNT);
+}
+
+void
+CallcFree(void)
+{
+        FsmFree(&lcfsm);
+        FsmFree(&callcfsm);
+}
+
+static void
+release_ds(int chan)
+{
+        struct PStack  *st = &chanlist[chan].ds;
+        struct IsdnCardState *sp;
+        struct HscxState *hsp;
+
+        sp = st->l1.hardware;
+        hsp = sp->hs + chanlist[chan].hscx;
+
+        close_hscxstate(hsp);
+
+        switch (chanlist[chan].l2_active_protocol) {
+          case (ISDN_PROTO_L2_X75I):
+                  releasestack_isdnl2(st);
+                  break;
+          case (ISDN_PROTO_L2_HDLC):
+                  releasestack_transl2(st);
+                  break;
+        }
+}
+
+static void
+cc_l1man(struct PStack *st, int pr, void *arg)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+        switch (pr) {
+          case (PH_ACTIVATE):
+                  FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_ACTIVATE, NULL);
+                  break;
+          case (PH_DEACTIVATE):
+                  FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+                  break;
+        }
+}
+
+static void
+cc_l2man(struct PStack *st, int pr, void *arg)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+        switch (pr) {
+          case (DL_ESTABLISH):
+                  FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_ESTABLISH, NULL);
+                  break;
+          case (DL_RELEASE):
+                  FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_RELEASE, NULL);
+                  break;
+        }
+}
+
+static void
+dcc_l1man(struct PStack *st, int pr, void *arg)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+        switch (pr) {
+          case (PH_ACTIVATE):
+                  FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_ACTIVATE, NULL);
+                  break;
+          case (PH_DEACTIVATE):
+                  FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+                  break;
+        }
+}
+
+static void
+dcc_l2man(struct PStack *st, int pr, void *arg)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+        switch (pr) {
+          case (DL_ESTABLISH):
+                  FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_ESTABLISH, NULL);
+                  break;
+          case (DL_RELEASE):
+                  FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL);
+                  break;
+        }
+}
+
+static void
+ll_handler(struct PStack *st, int pr,
+           struct BufHeader *ibh)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+        switch (pr) {
+          case (CC_DISCONNECT_IND):
+                  FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
+                  break;
+          case (CC_RELEASE_CNF):
+                  FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
+                  break;
+          case (CC_SETUP_IND):
+                  FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+                  break;
+          case (CC_RELEASE_IND):
+                  FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL);
+                  break;
+          case (CC_SETUP_COMPLETE_IND):
+                  FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
+                  break;
+          case (CC_SETUP_CNF):
+                  FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+                  break;
+          case (CC_INFO_CHARGE):
+                  FsmEvent(&chanp->fi, EV_CINF, NULL);
+                  break;
+        }
+}
+
+static void
+init_is(int chan, unsigned int ces)
+{
+        struct PStack  *st = &(chanlist[chan].is);
+        struct IsdnCardState *sp = chanlist[chan].sp;
+        char            tmp[128];
+
+        setstack_teles(st, sp);
+
+        st->l2.sap = 0;
+
+        st->l2.tei = 255;
+
+        st->l2.ces = ces;
+        st->l2.extended = !0;
+        st->l2.laptype = LAPD;
+        st->l2.window = 1;
+        st->l2.orig = !0;
+        st->l2.t200 = 1000;               /* 1000 milliseconds  */
+        if (st->protocol == ISDN_PTYPE_1TR6) {
+                st->l2.n200 = 3;          /* try 3 times        */
+                st->l2.t203 = 10000;      /* 10000 milliseconds */
+        } else {
+                st->l2.n200 = 4;          /* try 4 times        */
+                st->l2.t203 = 5000;       /* 5000 milliseconds  */
+        }
+
+        sprintf(tmp, "Channel %d q.921", chan);
+        setstack_isdnl2(st, tmp);
+        setstack_isdnl3(st);
+        st->l2.debug = 2;
+        st->l3.debug = 2;
+        st->l2.debug = 0xff;
+        st->l3.debug = 0xff;
+        st->l4.userdata = chanlist + chan;
+        st->l4.l2writewakeup = NULL;
+
+        st->l3.l3l4 = ll_handler;
+        st->l1.l1man = cc_l1man;
+        st->l2.l2man = cc_l2man;
+
+        st->pa = &chanlist[chan].para;
+        teles_addlist(sp, st);
+}
+
+static void
+callc_debug(struct FsmInst *fi, char *s)
+{
+        char            str[80], tm[32];
+        struct Channel *chanp = fi->userdata;
+
+        jiftime(tm, jiffies);
+        sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s);
+        teles_putstatus(str);
+}
+
+static void
+lc_debug(struct FsmInst *fi, char *s)
+{
+        char            str[256], tm[32];
+        struct LcFsm   *lf = fi->userdata;
+
+        jiftime(tm, jiffies);
+        sprintf(str, "%s Channel %d lc %s\n", tm, lf->ch->chan, s);
+        teles_putstatus(str);
+}
+
+static void
+dlc_debug(struct FsmInst *fi, char *s)
+{
+        char            str[256], tm[32];
+        struct LcFsm   *lf = fi->userdata;
+
+        jiftime(tm, jiffies);
+        sprintf(str, "%s Channel %d dlc %s\n", tm, lf->ch->chan, s);
+        teles_putstatus(str);
+}
+
+static void
+lccall_d(struct LcFsm *lf, int pr, void *arg)
+{
+        struct Channel *chanp = lf->ch;
+
+        switch (pr) {
+          case (LC_ESTABLISH):
+                  FsmEvent(&chanp->fi, EV_DLEST, NULL);
+                  break;
+          case (LC_RELEASE):
+                  FsmEvent(&chanp->fi, EV_DLRL, NULL);
+                  break;
+        }
+}
+
+static void
+lccall_b(struct LcFsm *lf, int pr, void *arg)
+{
+        struct Channel *chanp = lf->ch;
+
+        switch (pr) {
+          case (LC_ESTABLISH):
+                  FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+                  break;
+          case (LC_RELEASE):
+                  FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+                  break;
+        }
+}
+
+static void
+init_chan(int chan, int cardnr, int hscx,
+          unsigned int ces)
+{
+        struct IsdnCard *card = cards + cardnr;
+        struct Channel *chanp = chanlist + chan;
+
+        chanp->sp = card->sp;
+        chanp->hscx = hscx;
+        chanp->chan = chan;
+        chanp->incoming = 0;
+        chanp->debug = 0;
+        init_is(chan, ces);
+
+        chanp->fi.fsm = &callcfsm;
+        chanp->fi.state = ST_NULL;
+        chanp->fi.debug = 0;
+        chanp->fi.userdata = chanp;
+        chanp->fi.printdebug = callc_debug;
+
+        chanp->lc_d.lcfi.fsm = &lcfsm;
+        chanp->lc_d.lcfi.state = ST_LC_NULL;
+        chanp->lc_d.lcfi.debug = 0;
+        chanp->lc_d.lcfi.userdata = &chanp->lc_d;
+        chanp->lc_d.lcfi.printdebug = lc_debug;
+        chanp->lc_d.type = LC_D;
+        chanp->lc_d.ch = chanp;
+        chanp->lc_d.st = &chanp->is;
+        chanp->lc_d.l2_establish = !0;
+        chanp->lc_d.l2_start = !0;
+        chanp->lc_d.lccall = lccall_d;
+        FsmInitTimer(&chanp->lc_d.lcfi, &chanp->lc_d.act_timer);
+
+        chanp->lc_b.lcfi.fsm = &lcfsm;
+        chanp->lc_b.lcfi.state = ST_LC_NULL;
+        chanp->lc_b.lcfi.debug = 0;
+        chanp->lc_b.lcfi.userdata = &chanp->lc_b;
+        chanp->lc_b.lcfi.printdebug = dlc_debug;
+        chanp->lc_b.type = LC_B;
+        chanp->lc_b.ch = chanp;
+        chanp->lc_b.st = &chanp->ds;
+        chanp->lc_b.l2_establish = !0;
+        chanp->lc_b.l2_start = !0;
+        chanp->lc_b.lccall = lccall_b;
+        FsmInitTimer(&chanp->lc_b.lcfi, &chanp->lc_b.act_timer);
+
+        chanp->outcallref = 64;
+        chanp->data_open = 0;
+}
+
+int
+CallcNewChan(void)
+{
+        int             i, ces, c;
+
+        chancount = 0;
+        for (i = 0; i < nrcards; i++)
+                if (cards[i].sp)
+                        chancount += 2;
+
+        chanlist = (struct Channel *) Smalloc(sizeof(struct Channel) *
+                                      chancount, GFP_KERNEL, "chanlist");
+
+        c = 0;
+        ces = randomces();
+        for (i = 0; i < nrcards; i++)
+                if (cards[i].sp) {
+                        init_chan(c++, i, 1, ces++);
+                        ces %= 0xffff;
+                        init_chan(c++, i, 0, ces++);
+                        ces %= 0xffff;
+                }
+        printk(KERN_INFO "channels %d\n", chancount);
+        return (chancount);
+
+}
+
+static void
+release_is(int chan)
+{
+        struct PStack  *st = &chanlist[chan].is;
+
+        releasestack_isdnl2(st);
+        teles_rmlist(st->l1.hardware, st);
+        BufQueueRelease(&st->l2.i_queue);
+}
+
+static void
+release_chan(int chan)
+{
+#if 0
+        release_ds(chan);
+#endif
+        release_is(chan);
+}
+
+void
+CallcFreeChan(void)
+{
+        int             i;
+
+        for (i = 0; i < chancount; i++)
+                release_chan(i);
+        Sfree((void *) chanlist);
+}
+
+static void
+lldata_handler(struct PStack *st, int pr,
+               void *arg)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+        byte           *ptr;
+        int             size;
+        struct BufHeader *ibh = arg;
+
+        switch (pr) {
+          case (DL_DATA):
+                  if (chanp->data_open) {
+                          ptr = DATAPTR(ibh);
+                          ptr += chanp->ds.l2.ihsize;
+                          size = ibh->datasize - chanp->ds.l2.ihsize;
+                          iif.rcvcallb(drid, chanp->chan, ptr, size);
+                  }
+                  BufPoolRelease(ibh);
+                  break;
+          default:
+                  printk(KERN_WARNING "lldata_handler unknown primitive\n");
+                  break;
+        }
+}
+
+static void
+lltrans_handler(struct PStack *st, int pr,
+                struct BufHeader *ibh)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+        byte           *ptr;
+
+        switch (pr) {
+          case (PH_DATA):
+                  if (chanp->data_open) {
+                          ptr = DATAPTR(ibh);
+                          iif.rcvcallb(drid, chanp->chan, ptr, ibh->datasize);
+                  }
+                  BufPoolRelease(ibh);
+                  break;
+          default:
+                  printk(KERN_WARNING "lltrans_handler unknown primitive\n");
+                  break;
+        }
+}
+
+static void
+ll_writewakeup(struct PStack *st)
+{
+        struct Channel *chanp = st->l4.userdata;
+        isdn_ctrl       ic;
+
+        ic.driver = drid;
+        ic.command = ISDN_STAT_BSENT;
+        ic.arg = chanp->chan;
+        iif.statcallb(&ic);
+}
+
+static int
+init_ds(int chan, int incoming)
+{
+        struct PStack  *st = &(chanlist[chan].ds);
+        struct IsdnCardState *sp = (struct IsdnCardState *)
+        chanlist[chan].is.l1.hardware;
+        struct HscxState *hsp = sp->hs + chanlist[chan].hscx;
+        char            tmp[128];
+
+        st->l1.hardware = sp;
+
+        hsp->mode = 2;
+        hsp->transbufsize = 4000;
+
+        if (setstack_hscx(st, hsp))
+                return (-1);
+
+        st->l2.extended = 0;
+        st->l2.laptype = LAPB;
+        st->l2.orig = !incoming;
+        st->l2.t200 = 1000;        /* 1000 milliseconds */
+        st->l2.window = 3;
+        st->l2.n200 = 4;           /* try 4 times       */
+        st->l2.t203 = 5000;        /* 5000 milliseconds */
+
+        st->l2.debug = 0xff;
+        st->l3.debug = 0xff;
+        switch (chanlist[chan].l2_active_protocol) {
+          case (ISDN_PROTO_L2_X75I):
+                  sprintf(tmp, "Channel %d x.75", chan);
+                  setstack_isdnl2(st, tmp);
+                  st->l2.l2l3 = lldata_handler;
+                  st->l1.l1man = dcc_l1man;
+                  st->l2.l2man = dcc_l2man;
+                  st->l4.userdata = chanlist + chan;
+                  st->l4.l1writewakeup = NULL;
+                  st->l4.l2writewakeup = ll_writewakeup;
+                  st->l2.l2m.debug = debugflags & 16;
+                  st->ma.manl2(st, MDL_NOTEIPROC, NULL);
+                  st->l1.hscxmode = 2;        /* Packet-Mode ? */
+                  st->l1.hscxchannel = chanlist[chan].para.bchannel - 1;
+                  break;
+          case (ISDN_PROTO_L2_HDLC):
+                  st->l1.l1l2 = lltrans_handler;
+                  st->l1.l1man = dcc_l1man;
+                  st->l4.userdata = chanlist + chan;
+                  st->l4.l1writewakeup = ll_writewakeup;
+                  st->l1.hscxmode = 2;
+                  st->l1.hscxchannel = chanlist[chan].para.bchannel - 1;
+                  break;
+        }
+
+        return (0);
+
+}
+
+static void
+channel_report(int i)
+{
+}
+
+static void
+command_debug(struct Channel *chanp, char *s)
+{
+        char            tmp[64], tm[32];
+
+        jiftime(tm, jiffies);
+        sprintf(tmp, "%s Channel %d LL->HL %s\n", tm, chanp->chan, s);
+        teles_putstatus(tmp);
+}
+
+static void
+distr_debug(void)
+{
+        int             i;
+
+        for (i = 0; i < chancount; i++) {
+                chanlist[i].debug = debugflags & 1;
+                chanlist[i].fi.debug = debugflags & 2;
+                chanlist[i].is.l2.l2m.debug = debugflags & 8;
+                chanlist[i].ds.l2.l2m.debug = debugflags & 16;
+        }
+        for (i = 0; i < nrcards; i++)
+                if (cards[i].sp)
+                        cards[i].sp->dlogflag = debugflags & 4;
+}
+
+int
+teles_command(isdn_ctrl * ic)
+{
+        struct Channel *chanp;
+        char            tmp[64];
+        int             i;
+        unsigned int    num;
+
+        switch (ic->command) {
+          case (ISDN_CMD_SETEAZ):
+                  chanp = chanlist + ic->arg;
+                  if (chanp->debug & 1)
+                          command_debug(chanp, "SETEAZ");
+                  return (0);
+          case (ISDN_CMD_DIAL):
+                  chanp = chanlist + (ic->arg & 0xff);
+                  if (chanp->debug & 1) {
+                          sprintf(tmp, "DIAL %s", ic->num);
+                          command_debug(chanp, tmp);
+                  }
+                  FsmEvent(&chanp->fi, EV_DIAL, ic);
+                  return (0);
+          case (ISDN_CMD_ACCEPTB):
+                  chanp = chanlist + ic->arg;
+                  if (chanp->debug & 1)
+                          command_debug(chanp, "ACCEPTB");
+                  FsmEvent(&chanp->fi, EV_ACCEPTB, NULL);
+                  break;
+          case (ISDN_CMD_ACCEPTD):
+                  chanp = chanlist + ic->arg;
+                  if (chanp->debug & 1)
+                          command_debug(chanp, "ACCEPTD");
+                  FsmEvent(&chanp->fi, EV_ACCEPTD, NULL);
+                  break;
+          case (ISDN_CMD_HANGUP):
+                  chanp = chanlist + ic->arg;
+                  if (chanp->debug & 1)
+                          command_debug(chanp, "HANGUP");
+                  FsmEvent(&chanp->fi, EV_HANGUP, NULL);
+                  break;
+          case (ISDN_CMD_LOCK):
+                  teles_mod_inc_use_count();
+                  break;
+          case (ISDN_CMD_UNLOCK):
+                  teles_mod_dec_use_count();
+                  break;
+          case (ISDN_CMD_IOCTL):
+                  switch (ic->arg) {
+                    case (0):
+                            for (i = 0; i < nrcards; i++)
+                                    if (cards[i].sp)
+                                            teles_reportcard(i);
+                            for (i = 0; i < chancount; i++)
+                                    channel_report(i);
+                            break;
+                    case (1):
+                            debugflags = *(unsigned int *) ic->num;
+                            distr_debug();
+                            sprintf(tmp, "debugging flags set to %x\n", debugflags);
+                            teles_putstatus(tmp);
+                            break;
+                    case (2):
+                            num = *(unsigned int *) ic->num;
+                            i = num >> 8;
+                            if (i >= chancount)
+                                    break;
+                            chanp = chanlist + i;
+                            chanp->impair = num & 0xff;
+                            if (chanp->debug & 1) {
+                                    sprintf(tmp, "IMPAIR %x", chanp->impair);
+                                    command_debug(chanp, tmp);
+                            }
+                            break;
+                  }
+                  break;
+          case (ISDN_CMD_SETL2):
+                  chanp = chanlist + (ic->arg & 0xff);
+                  if (chanp->debug & 1) {
+                          sprintf(tmp, "SETL2 %ld", ic->arg >> 8);
+                          command_debug(chanp, tmp);
+                  }
+                  chanp->l2_protocol = ic->arg >> 8;
+                  break;
+          default:
+                  break;
+        }
+
+        return (0);
+}
+
+int
+teles_writebuf(int id, int chan, const u_char * buf, int count, int user)
+{
+        struct Channel *chanp = chanlist + chan;
+        struct PStack  *st = &chanp->ds;
+        struct BufHeader *ibh;
+        int             err, i;
+        byte           *ptr;
+
+        err = BufPoolGet(&ibh, st->l1.sbufpool, GFP_ATOMIC, st, 21);
+        if (err)
+                return (0);
+
+        if (count > BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS)) {
+                printk(KERN_WARNING "teles_writebuf: packet too large!\n");
+                return (-EINVAL);
+        }
+        ptr = DATAPTR(ibh);
+        if (chanp->lc_b.l2_establish)
+                i = st->l2.ihsize;
+        else
+                i = 0;
+
+        ptr += i;
+
+        if (user)
+                memcpy_fromfs(ptr, buf, count);
+        else
+                memcpy(ptr, buf, count);
+        ibh->datasize = count + i;
+
+        if (chanp->data_open) {
+                if (chanp->lc_b.l2_establish)
+                        chanp->ds.l3.l3l2(&chanp->ds, DL_DATA, ibh);
+                else
+                        chanp->ds.l2.l2l1(&chanp->ds, PH_DATA, ibh);
+                return (count);
+        } else {
+                BufPoolRelease(ibh);
+                return (0);
+        }
+
+}
+
+static char    *
+strcpyupto(char *dest, char *src, char upto)
+{
+        while (*src && (*src != upto) && (*src != '\0'))
+                *dest++ = *src++;
+        *dest = '\0';
+        return (src);
+}
diff --git a/drivers/isdn/teles/card.c b/drivers/isdn/teles/card.c
new file mode 100644 (file)
index 0000000..710a022
--- /dev/null
@@ -0,0 +1,1772 @@
+/*
+ * card.c     low level stuff for the Teles S0 isdn card
+ * 
+ * Author     Jan den Ouden
+ * 
+ * 
+ * 
+ * Changelog:
+ * 
+ * Beat Doebeli         log all D channel traffic
+ * 
+ */
+
+#define __NO_VERSION__
+#include "teles.h"
+
+#define INCLUDE_INLINE_FUNCS
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+
+#undef DCHAN_VERBOSE
+
+extern void     tei_handler(struct PStack *st, byte pr,
+                           struct BufHeader *ibh);
+extern struct   IsdnCard cards[];
+extern int      nrcards;
+
+#define byteout(addr,val) outb_p(val,addr)
+#define bytein(addr) inb_p(addr)
+
+static inline   byte
+readisac_0(byte * cardm, byte offset)
+{
+       return *(byte *) (cardm + 0x100 + ((offset & 1) ? 0x1ff : 0) + offset);
+}
+
+static inline   byte
+readisac_3(int iobase, byte offset)
+{
+        return (bytein(iobase - 0x420 + offset));
+}
+
+#define READISAC(mbase,ibase,ofs) \
+        ((mbase)?readisac_0(mbase,ofs):readisac_3(ibase,ofs))
+
+static inline void
+writeisac_0(byte * cardm, byte offset, byte value)
+{
+       *(byte *) (cardm + 0x100 + ((offset & 1) ? 0x1ff : 0) + offset) = value;
+}
+
+static inline void
+writeisac_3(int iobase, byte offset, byte value)
+{
+       byteout(iobase - 0x420 + offset, value);
+}
+
+#define WRITEISAC(mbase,ibase,ofs,val) \
+        ((mbase)?writeisac_0(mbase,ofs,val):writeisac_3(ibase,ofs,val))
+
+static inline void
+readisac_s(int iobase, byte offset, byte * dest, int count)
+{
+       insb(iobase - 0x420 + offset, dest, count);
+}
+
+static inline void
+writeisac_s(int iobase, byte offset, byte * src, int count)
+{
+       outsb(iobase - 0x420 + offset, src, count);
+}
+
+static inline   byte
+readhscx_0(byte * base, byte hscx, byte offset)
+{
+       return *(byte *) (base + 0x180 + ((offset & 1) ? 0x1FF : 0) +
+                         ((hscx & 1) ? 0x40 : 0) + offset);
+}
+
+static inline   byte
+readhscx_3(int iobase, byte hscx, byte offset)
+{
+       return (bytein(iobase - (hscx ? 0x820 : 0xc20) + offset));
+}
+
+#define READHSCX(mbase,ibase,hscx,ofs) \
+        ((mbase)?readhscx_0(mbase,hscx,ofs):readhscx_3(ibase,hscx,ofs))
+
+static inline void
+writehscx_0(byte * base, byte hscx, byte offset, byte data)
+{
+       *(byte *) (base + 0x180 + ((offset & 1) ? 0x1FF : 0) +
+                  ((hscx & 1) ? 0x40 : 0) + offset) = data;
+}
+
+static inline void
+writehscx_3(int iobase, byte hscx, byte offset, byte data)
+{
+       byteout(iobase - (hscx ? 0x820 : 0xc20) + offset, data);
+}
+
+static inline void
+readhscx_s(int iobase, byte hscx, byte offset, byte * dest, int count)
+{
+       insb(iobase - (hscx ? 0x820 : 0xc20) + offset, dest, count);
+}
+
+static inline void
+writehscx_s(int iobase, byte hscx, byte offset, byte * src, int count)
+{
+       outsb(iobase - (hscx ? 0x820 : 0xc20) + offset, src, count);
+}
+
+#define ISAC_MASK 0x20
+#define ISAC_ISTA 0x20
+#define ISAC_STAR 0x21
+#define ISAC_CMDR 0x21
+#define ISAC_EXIR 0x24
+
+#define ISAC_RBCH 0x2a
+
+#define ISAC_ADF2 0x39
+#define ISAC_SPCR 0x30
+#define ISAC_ADF1 0x38
+#define ISAC_CIX0 0x31
+#define ISAC_STCR 0x37
+#define ISAC_MODE 0x22
+#define ISAC_RSTA 0x27
+#define ISAC_RBCL 0x25
+#define ISAC_TIMR 0x23
+#define ISAC_SQXR 0x3b
+
+#define HSCX_ISTA 0x20
+#define HSCX_CCR1 0x2f
+#define HSCX_CCR2 0x2c
+#define HSCX_TSAR 0x31
+#define HSCX_TSAX 0x30
+#define HSCX_XCCR 0x32
+#define HSCX_RCCR 0x33
+#define HSCX_MODE 0x22
+#define HSCX_CMDR 0x21
+#define HSCX_EXIR 0x24
+#define HSCX_XAD1 0x24
+#define HSCX_XAD2 0x25
+#define HSCX_RAH2 0x27
+#define HSCX_RSTA 0x27
+#define HSCX_TIMR 0x23
+#define HSCX_STAR 0x21
+#define HSCX_RBCL 0x25
+#define HSCX_XBCH 0x2d
+#define HSCX_VSTR 0x2e
+#define HSCX_RLCR 0x2e
+#define HSCX_MASK 0x20
+
+static inline void
+waitforCEC_0(byte * base, byte hscx)
+{
+       long            to = 10;
+
+       while ((readhscx_0(base, hscx, HSCX_STAR) & 0x04) && to) {
+               udelay(5);
+               to--;
+       }
+       if (!to)
+               printk(KERN_WARNING "waitforCEC timeout\n");
+}
+
+static inline void
+waitforCEC_3(int iobase, byte hscx)
+{
+       long            to = 10;
+
+       while ((readhscx_3(iobase, hscx, HSCX_STAR) & 0x04) && to) {
+               udelay(5);
+               to--;
+       }
+       if (!to)
+               printk(KERN_WARNING "waitforCEC timeout\n");
+}
+
+static inline void
+waitforXFW_0(byte * base, byte hscx)
+{
+       long            to = 10;
+
+       waitforCEC_0(base, hscx);
+
+       while ((!(readhscx_0(base, hscx, HSCX_STAR) & 0x40)) && to) {
+               udelay(5);
+               to--;
+       }
+       if (!to)
+               printk(KERN_WARNING "waitforXFW timeout\n");
+}
+
+static inline void
+waitforXFW_3(int iobase, byte hscx)
+{
+       long            to = 10;
+
+       waitforCEC_3(iobase, hscx);
+
+       while ((!(readhscx_3(iobase, hscx, HSCX_STAR) & 0x40)) && to) {
+               udelay(5);
+               to--;
+       }
+       if (!to)
+               printk(KERN_WARNING "waitforXFW timeout\n");
+}
+
+static inline void
+writehscxCMDR_0(byte * base, byte hscx, byte data)
+{
+       long            flags;
+
+       save_flags(flags);
+       cli();
+       waitforCEC_0(base, hscx);
+       writehscx_0(base, hscx, HSCX_CMDR, data);
+       restore_flags(flags);
+}
+
+static inline void
+writehscxCMDR_3(int iobase, byte hscx, byte data)
+{
+       long            flags;
+
+       save_flags(flags);
+       cli();
+       waitforCEC_3(iobase, hscx);
+       writehscx_3(iobase, hscx, HSCX_CMDR, data);
+       restore_flags(flags);
+}
+
+#define WRITEHSCX_CMDR(mbase,ibase,hscx,data) \
+        ((mbase)?writehscxCMDR_0(mbase,hscx,data):writehscxCMDR_3(ibase,hscx,data))
+
+/*
+ * fast interrupt here
+ */
+
+#define ISAC_RCVBUFREADY 0
+#define ISAC_XMTBUFREADY 1
+#define ISAC_PHCHANGE    2
+
+#define HSCX_RCVBUFREADY 0
+#define HSCX_XMTBUFREADY 1
+
+void
+teles_hscxreport(struct IsdnCardState *sp, int hscx)
+{
+        printk(KERN_DEBUG "HSCX %d\n", hscx);
+        if (sp->membase) {
+                printk(KERN_DEBUG "  ISTA %x\n", readhscx_0(sp->membase,
+                                                          hscx, HSCX_ISTA));
+                printk(KERN_DEBUG "  STAR %x\n", readhscx_0(sp->membase,
+                                                          hscx, HSCX_STAR));
+                printk(KERN_DEBUG "  EXIR %x\n", readhscx_0(sp->membase,
+                                                          hscx, HSCX_EXIR));
+        } else {
+                printk(KERN_DEBUG "  ISTA %x\n", readhscx_3(sp->iobase,
+                                                          hscx, HSCX_ISTA));
+                printk(KERN_DEBUG "  STAR %x\n", readhscx_3(sp->iobase,
+                                                          hscx, HSCX_STAR));
+                printk(KERN_DEBUG "  EXIR %x\n", readhscx_3(sp->iobase,
+                                                          hscx, HSCX_EXIR));
+        }
+}
+
+void
+teles_report(struct IsdnCardState *sp)
+{
+       printk(KERN_DEBUG "ISAC\n");
+        if (sp->membase) {
+               printk(KERN_DEBUG "  ISTA %x\n", readisac_0(sp->membase,
+                                                            ISAC_ISTA));
+               printk(KERN_DEBUG "  STAR %x\n", readisac_0(sp->membase,
+                                                           ISAC_STAR));
+               printk(KERN_DEBUG "  EXIR %x\n", readisac_0(sp->membase,
+                                                           ISAC_EXIR));
+        } else {
+                printk(KERN_DEBUG "  ISTA %x\n", readisac_3(sp->iobase,
+                                                          ISAC_ISTA));
+                printk(KERN_DEBUG "  STAR %x\n", readisac_3(sp->iobase,
+                                                          ISAC_STAR));
+                printk(KERN_DEBUG "  EXIR %x\n", readisac_3(sp->iobase,
+                                                          ISAC_EXIR));
+        }
+       teles_hscxreport(sp, 0);
+       teles_hscxreport(sp, 1);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+static void
+hscx_sched_event(struct HscxState *hsp, int event)
+{
+       hsp->event |= 1 << event;
+       queue_task_irq_off(&hsp->tqueue, &tq_immediate);
+       mark_bh(IMMEDIATE_BH);
+}
+
+static void
+hscx_empty_fifo(struct HscxState *hsp, int count)
+{
+       byte             *ptr;
+       struct BufHeader *ibh = hsp->rcvibh;
+
+       if (hsp->sp->debug)
+               printk(KERN_DEBUG "hscx_empty_fifo\n");
+
+       if (hsp->rcvptr + count > BUFFER_SIZE(HSCX_RBUF_ORDER,
+                                             HSCX_RBUF_BPPS)) {
+               printk(KERN_WARNING
+                       "hscx_empty_fifo: incoming packet too large\n");
+               WRITEHSCX_CMDR(hsp->membase, hsp->iobase, hsp->hscx, 0x80);
+               return;
+       }
+       ptr = DATAPTR(ibh);
+       ptr += hsp->rcvptr;
+
+       hsp->rcvptr += count;
+        if (hsp->membase) {
+                while (count--)
+                        *ptr++ = readhscx_0(hsp->membase, hsp->hscx, 0x0);
+                writehscxCMDR_0(hsp->membase, hsp->hscx, 0x80);
+        } else {
+                readhscx_s(hsp->iobase, hsp->hscx, 0x3e, ptr, count);
+                writehscxCMDR_3(hsp->iobase, hsp->hscx, 0x80);
+        }
+#ifdef BCHAN_VERBOSE
+        {
+                int i;
+                printk(KERN_DEBUG "hscx_empty_fifo");
+                for (i = 0; i < count; i++)
+                        printk(" %2x", ptr[i]);
+                printk("\n");
+        }
+#endif                         /* BCHAN_VERBOSE */
+}
+
+static void
+hscx_fill_fifo(struct HscxState *hsp)
+{
+       struct BufHeader *ibh;
+       int              more, count;
+       byte             *ptr;
+
+       if (hsp->sp->debug)
+               printk(KERN_DEBUG "hscx_fill_fifo\n");
+
+       ibh = hsp->xmtibh;
+       if (!ibh)
+               return;
+
+       count = ibh->datasize - hsp->sendptr;
+       if (count <= 0)
+               return;
+
+#if 0
+       if (!hsp->sendptr) {
+               ptr = DATAPTR(ibh);
+               printk(KERN_DEBUG "snd bytes %2x %2x %2x %2x %2x\n", ptr[0], ptr[1], ptr[2],
+                      ptr[3], ptr[4]);
+       }
+#endif
+
+       more = 0;
+       if (count > 32) {
+               more = !0;
+               count = 32;
+       }
+       ptr = DATAPTR(ibh);
+       ptr += hsp->sendptr;
+       hsp->sendptr += count;
+
+#ifdef BCHAN_VERBOSE
+        {
+                int i;
+                printk(KERN_DEBUG "hscx_fill_fifo ");
+                for (i = 0; i < count; i++)
+                        printk(" %2x", ptr[i]);
+                printk("\n");
+        }
+#endif
+        if (hsp->membase) {
+                waitforXFW_0(hsp->membase, hsp->hscx);
+                while (count--)
+                        writehscx_0(hsp->membase, hsp->hscx, 0x0, *ptr++);
+                writehscxCMDR_0(hsp->membase, hsp->hscx, more ? 0x8 : 0xa);
+        } else {
+                waitforXFW_3(hsp->iobase, hsp->hscx);
+                writehscx_s(hsp->iobase, hsp->hscx, 0x3e, ptr, count);
+                writehscxCMDR_3(hsp->iobase, hsp->hscx, more ? 0x8 : 0xa);
+        }
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *sp, byte val, byte hscx)
+{
+       byte             r;
+       struct HscxState *hsp = sp->hs + hscx;
+       int              count;
+
+       if (!hsp->init)
+               return;
+
+       if (val & 0x80) {       /* RME */
+
+               r = READHSCX(hsp->membase, sp->iobase, hsp->hscx, HSCX_RSTA);
+               if ((r & 0xf0) != 0xa0) {
+                       if (!r & 0x80)
+                               printk(KERN_WARNING
+                                       "Teles: HSCX invalid frame\n");
+                       if (r & 0x40)
+                               printk(KERN_WARNING "Teles: HSCX RDO\n");
+                       if (!r & 0x20)
+                               printk(KERN_WARNING "Teles: HSCX CRC error\n");
+                       if (hsp->rcvibh)
+                               BufPoolRelease(hsp->rcvibh);
+                       hsp->rcvibh = NULL;
+                       WRITEHSCX_CMDR(hsp->membase, hsp->iobase, hsp->hscx,
+                                       0x80);
+                       goto afterRME;
+               }
+               if (!hsp->rcvibh)
+                       if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+                                       GFP_ATOMIC, (void *) 1, 1)) {
+                               printk(KERN_WARNING
+                                       "HSCX RME out of buffers at %ld\n",
+                                       jiffies);
+                               WRITEHSCX_CMDR(hsp->membase, hsp->iobase,
+                                               hsp->hscx, 0x80);
+                               goto afterRME;
+                       } else
+                               hsp->rcvptr = 0;
+
+               count = READHSCX(hsp->membase, sp->iobase, hsp->hscx,
+                                 HSCX_RBCL) & 0x1f;
+               if (count == 0)
+                       count = 32;
+               hscx_empty_fifo(hsp, count);
+               hsp->rcvibh->datasize = hsp->rcvptr - 1;
+               BufQueueLink(&hsp->rq, hsp->rcvibh);
+               hsp->rcvibh = NULL;
+               hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+       }
+      afterRME:
+       if (val & 0x40) {       /* RPF */
+               if (!hsp->rcvibh)
+                       if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+                                      GFP_ATOMIC, (void *) 1, 2)) {
+                               printk(KERN_WARNING
+                                       "HSCX RME out of buffers at %ld\n",
+                                       jiffies);
+                               WRITEHSCX_CMDR(hsp->membase, hsp->iobase,
+                                               hsp->hscx, 0x80);
+                               goto afterRPF;
+                       } else
+                               hsp->rcvptr = 0;
+
+               hscx_empty_fifo(hsp, 32);
+#ifdef VOICE
+               hsp->rcvibh->datasize = hsp->rcvptr - 1;
+               BufQueueLink(&hsp->rq, hsp->rcvibh);
+               hsp->rcvibh = NULL;
+               hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+#endif
+       }
+      afterRPF:
+       if (val & 0x10) {       /* XPR */
+               if (hsp->xmtibh)
+                       if (hsp->xmtibh->datasize > hsp->sendptr) {
+                               hscx_fill_fifo(hsp);
+                               goto afterXPR;
+                       } else {
+                               if (hsp->releasebuf)
+                                       BufPoolRelease(hsp->xmtibh);
+                               hsp->xmtibh = NULL;
+                               hsp->sendptr = 0;
+                               if (hsp->st->l4.l1writewakeup)
+                                       hsp->st->l4.l1writewakeup(hsp->st);
+                       }
+               if (!BufQueueUnlink(&hsp->xmtibh, &hsp->sq)) {
+                       hsp->releasebuf = !0;
+                       hscx_fill_fifo(hsp);
+               } else
+                       hscx_sched_event(hsp, HSCX_XMTBUFREADY);
+       }
+      afterXPR:
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+static void
+isac_sched_event(struct IsdnCardState *sp, int event)
+{
+       sp->event |= 1 << event;
+       queue_task_irq_off(&sp->tqueue, &tq_immediate);
+       mark_bh(IMMEDIATE_BH);
+}
+
+static void
+empty_fifo(struct IsdnCardState *sp, int count)
+{
+       byte             *ptr;
+       struct BufHeader *ibh = sp->rcvibh;
+
+       if (sp->debug)
+               printk(KERN_DEBUG "empty_fifo\n");
+
+       if (sp->rcvptr >= 3072) {
+               printk(KERN_WARNING "empty_fifo rcvptr %d\n", sp->rcvptr);
+               return;
+       }
+       ptr = DATAPTR(ibh);
+       ptr += sp->rcvptr;
+       sp->rcvptr += count;
+
+        if (sp->membase) {
+#ifdef DCHAN_VERBOSE
+                printk(KERN_DEBUG "empty_fifo ");
+                while (count--) {
+                        *ptr = readisac_0(sp->membase, 0x0);
+                        printk("%2x ", *ptr);
+                        ptr++;
+                }
+                printk("\n");
+#else
+                while (count--)
+                        *ptr++ = readisac_0(sp->membase, 0x0);
+#endif
+                writeisac_0(sp->membase, ISAC_CMDR, 0x80);
+        } else {
+#ifdef DCHAN_VERBOSE
+                int i;
+                printk(KERN_DEBUG "empty_fifo ");
+                readisac_s(sp->iobase, 0x3e, ptr, count);
+                for (i = 0; i < count; i++)
+                        printk("%2x ", ptr[i]);
+                printk("\n");
+#else
+                readisac_s(sp->iobase, 0x3e, ptr, count);
+#endif
+                writeisac_3(sp->iobase, ISAC_CMDR, 0x80);
+        }
+}
+
+static void
+fill_fifo(struct IsdnCardState *sp)
+{
+       struct BufHeader *ibh;
+       int              count, more;
+       byte             *ptr;
+
+       if (sp->debug)
+               printk(KERN_DEBUG "fill_fifo\n");
+
+       ibh = sp->xmtibh;
+       if (!ibh)
+               return;
+
+       count = ibh->datasize - sp->sendptr;
+       if (count <= 0)
+               return;
+       if (count >= 3072)
+               return;
+
+       more = 0;
+       if (count > 32) {
+               more = !0;
+               count = 32;
+       }
+       ptr = DATAPTR(ibh);
+       ptr += sp->sendptr;
+       sp->sendptr += count;
+
+        if (sp->membase) {
+#ifdef DCHAN_VERBOSE
+                printk(KERN_DEBUG "fill_fifo ");
+                while (count--) {
+                        writeisac_0(sp->membase, 0x0, *ptr);
+                        printk("%2x ", *ptr);
+                        ptr++;
+                }
+                printk("\n");
+#else
+                while (count--)
+                        writeisac_0(sp->membase, 0x0, *ptr++);
+#endif
+                writeisac_0(sp->membase, ISAC_CMDR, more ? 0x8 : 0xa);
+        } else {
+#ifdef DCHAN_VERBOSE
+                int i;
+                writeisac_s(sp->iobase, 0x3e, ptr, count);
+                printk(KERN_DEBUG "fill_fifo ");
+                for (i = 0; i < count; i++)
+                        printk("%2x ", ptr[i]);
+                printk("\n");
+#else
+                writeisac_s(sp->iobase, 0x3e, ptr, count);
+#endif
+                writeisac_3(sp->iobase, ISAC_CMDR, more ? 0x8 : 0xa);
+        }
+}
+
+static int
+act_wanted(struct IsdnCardState *sp)
+{
+       struct PStack  *st;
+
+       st = sp->stlist;
+       while (st)
+               if (st->l1.act_state)
+                       return (!0);
+               else
+                       st = st->next;
+       return (0);
+}
+
+static void
+ph_command(struct IsdnCardState *sp, unsigned int command)
+{
+       printk(KERN_DEBUG "ph_command %d\n", command);
+       WRITEISAC(sp->membase, sp->iobase, ISAC_CIX0, (command << 2) | 3);
+}
+
+static void
+isac_new_ph(struct IsdnCardState *sp)
+{
+       int             enq;
+
+       enq = act_wanted(sp);
+
+       switch (sp->ph_state) {
+         case (0):
+         case (6):
+                 if (enq)
+                         ph_command(sp, 0);
+                 else
+                         ph_command(sp, 15);
+                 break;
+         case (7):
+                 if (enq)
+                         ph_command(sp, 9);
+                 break;
+         case (12):
+         case (13):
+                 sp->ph_active = 5;
+                 isac_sched_event(sp, ISAC_PHCHANGE);
+                 if (!sp->xmtibh)
+                         if (!BufQueueUnlink(&sp->xmtibh, &sp->sq))
+                                 sp->sendptr = 0;
+                 if (sp->xmtibh)
+                         fill_fifo(sp);
+                 break;
+         case (4):
+         case (8):
+                 break;
+         default:
+                 sp->ph_active = 0;
+                 break;
+       }
+}
+
+static void
+teles_interrupt(int intno, struct pt_regs *regs)
+{
+       byte                 val, val2, r;
+       struct IsdnCardState *sp;
+       unsigned int         count;
+       struct HscxState     *hsp;
+
+       sp = (struct IsdnCardState *) irq2dev_map[intno];
+
+       if (!sp) {
+               printk(KERN_WARNING "Teles: Spurious interrupt!\n");
+               return;
+       }
+       val = READHSCX(sp->membase, sp->iobase, 1, HSCX_ISTA);
+
+       if (val & 0x01) {
+               hsp = sp->hs + 1;
+               printk(KERN_WARNING "HSCX B EXIR %x xmitbh %lx rcvibh %lx\n",
+                       READHSCX(sp->membase, sp->iobase, 1, HSCX_EXIR),
+                       (long) hsp->xmtibh,
+                      (long) hsp->rcvibh);
+       }
+       if (val & 0xf8) {
+               if (sp->debug)
+                       printk(KERN_DEBUG "HSCX B interrupt %x\n", val);
+               hscx_interrupt(sp, val, 1);
+       }
+       if (val & 0x02) {
+               printk(KERN_WARNING "HSCX A EXIR %x\n",
+                       READHSCX(sp->membase, sp->iobase, 0, HSCX_EXIR));
+       }
+
+/* ??? Why do that vvvvvvvvvvvvvvvvvvvvv different on Teles 16-3 ??? */
+        if (sp->membase) {
+                if (val & 0x04) {
+                        val = readhscx_0(sp->membase, 0, HSCX_ISTA);
+                        if (sp->debug)
+                                printk(KERN_DEBUG "HSCX A interrupt %x\n",
+                                       val);
+                        hscx_interrupt(sp, val, 0);
+                }
+        } else {
+                val2 = readhscx_3(sp->iobase, 0, HSCX_ISTA);
+                if (sp->debug)
+                        printk(KERN_DEBUG "HSCX A ISTA %x\n", val2);
+                if (val & 0x04) {
+                        if (sp->debug)
+                                printk(KERN_DEBUG "HSCX A interrupt %x\n",
+                                       val2);
+                        hscx_interrupt(sp, val2, 0);
+                }
+        }
+/* ??? Why do that ^^^^^^^^^^^^^^^^^^^^^ different on Teles 16-3 ??? */
+
+       val = READISAC(sp->membase, sp->iobase, ISAC_ISTA);
+
+       if (sp->debug)
+               printk(KERN_DEBUG "ISAC interrupt %x\n", val);
+
+       if (val & 0x80) {       /* RME */
+
+               r = READISAC(sp->membase, sp->iobase, ISAC_RSTA);
+               if ((r & 0x70) != 0x20) {
+                       if (r & 0x40)
+                               printk(KERN_WARNING "Teles: ISAC RDO\n");
+                       if (!r & 0x20)
+                               printk(KERN_WARNING "Teles: ISAC CRC error\n");
+                       if (sp->rcvibh)
+                               BufPoolRelease(sp->rcvibh);
+                       sp->rcvibh = NULL;
+                       WRITEISAC(sp->membase, sp->iobase, ISAC_CMDR, 0x80);
+                       goto afterRME;
+               }
+               if (!sp->rcvibh)
+                       if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+                                       GFP_ATOMIC,
+                                      (void *) 1, 3)) {
+                               printk(KERN_WARNING
+                                       "ISAC RME out of buffers!\n");
+                               WRITEISAC(sp->membase, sp->iobase, 
+                                          ISAC_CMDR, 0x80);
+                               goto afterRME;
+                       } else
+                               sp->rcvptr = 0;
+
+               count = READISAC(sp->membase, sp->iobase, ISAC_RBCL) & 0x1f;
+               if (count == 0)
+                       count = 32;
+               empty_fifo(sp, count);
+               sp->rcvibh->datasize = sp->rcvptr;
+               BufQueueLink(&(sp->rq), sp->rcvibh);
+               sp->rcvibh = NULL;
+               isac_sched_event(sp, ISAC_RCVBUFREADY);
+       }
+      afterRME:
+       if (val & 0x40) {       /* RPF */
+               if (!sp->rcvibh)
+                       if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+                                       GFP_ATOMIC,
+                                      (void *) 1, 4)) {
+                               printk(KERN_WARNING
+                                       "ISAC RME out of buffers!\n");
+                               WRITEISAC(sp->membase, sp->iobase,
+                                          ISAC_CMDR, 0x80);
+                               goto afterRPF;
+                       } else
+                               sp->rcvptr = 0;
+               empty_fifo(sp, 32);
+       }
+      afterRPF:
+       if (val & 0x20) {
+       }
+       if (val & 0x10) {       /* XPR */
+               if (sp->xmtibh)
+                       if (sp->xmtibh->datasize > sp->sendptr) {
+                               fill_fifo(sp);
+                               goto afterXPR;
+                       } else {
+                               if (sp->releasebuf)
+                                       BufPoolRelease(sp->xmtibh);
+                               sp->xmtibh = NULL;
+                               sp->sendptr = 0;
+                       }
+               if (!BufQueueUnlink(&sp->xmtibh, &sp->sq)) {
+                       sp->releasebuf = !0;
+                       fill_fifo(sp);
+               } else
+                       isac_sched_event(sp, ISAC_XMTBUFREADY);
+       }
+      afterXPR:
+       if (val & 0x04) {       /* CISQ */
+               sp->ph_state = (READISAC(sp->membase, sp->iobase, ISAC_CIX0)
+                                >> 2) & 0xf;
+               printk(KERN_DEBUG "l1state %d\n", sp->ph_state);
+               isac_new_ph(sp);
+       }
+        if (sp->membase) {
+                writeisac_0(sp->membase, ISAC_MASK, 0xFF);
+                writehscx_0(sp->membase, 0, HSCX_MASK, 0xFF);
+                writehscx_0(sp->membase, 1, HSCX_MASK, 0xFF);
+                writeisac_0(sp->membase, ISAC_MASK, 0x0);
+                writehscx_0(sp->membase, 0, HSCX_MASK, 0x0);
+                writehscx_0(sp->membase, 1, HSCX_MASK, 0x0);
+        } else {
+                writeisac_3(sp->iobase, ISAC_MASK, 0xFF);
+                writehscx_3(sp->iobase, 0, HSCX_MASK, 0xFF);
+                writehscx_3(sp->iobase, 1, HSCX_MASK, 0xFF);
+                writeisac_3(sp->iobase, ISAC_MASK, 0x0);
+                writehscx_3(sp->iobase, 0, HSCX_MASK, 0x0);
+                writehscx_3(sp->iobase, 1, HSCX_MASK, 0x0);
+        }
+}
+
+/*
+ * soft interrupt
+ */
+
+static void
+act_ivated(struct IsdnCardState *sp)
+{
+       struct PStack  *st;
+
+       st = sp->stlist;
+       while (st) {
+               if (st->l1.act_state == 1) {
+                       st->l1.act_state = 2;
+                       st->l1.l1man(st, PH_ACTIVATE, NULL);
+               }
+               st = st->next;
+       }
+}
+
+static void
+process_new_ph(struct IsdnCardState *sp)
+{
+       if (sp->ph_active == 5)
+               act_ivated(sp);
+}
+
+static void
+process_xmt(struct IsdnCardState *sp)
+{
+       struct PStack  *stptr;
+
+       if (sp->xmtibh)
+               return;
+
+       stptr = sp->stlist;
+       while (stptr != NULL)
+               if (stptr->l1.requestpull) {
+                       stptr->l1.requestpull = 0;
+                       stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL);
+                       break;
+               } else
+                       stptr = stptr->next;
+}
+
+static void
+process_rcv(struct IsdnCardState *sp)
+{
+       struct BufHeader *ibh, *cibh;
+       struct PStack    *stptr;
+       byte             *ptr;
+       int              found, broadc;
+       char             tmp[64];
+
+       while (!BufQueueUnlink(&ibh, &sp->rq)) {
+               stptr = sp->stlist;
+               ptr = DATAPTR(ibh);
+               broadc = (ptr[1] >> 1) == 127;
+
+               if (broadc && sp->dlogflag && (!(ptr[0] >> 2)))
+                       dlogframe(sp, ptr + 3, ibh->datasize - 3,
+                                 "Q.931 frame network->user broadcast");
+
+               if (broadc) {
+                       while (stptr != NULL) {
+                               if ((ptr[0] >> 2) == stptr->l2.sap)
+                                       if (!BufPoolGet(&cibh, &sp->rbufpool, GFP_ATOMIC,
+                                                       (void *) 1, 5)) {
+                                               memcpy(DATAPTR(cibh), DATAPTR(ibh), ibh->datasize);
+                                               cibh->datasize = ibh->datasize;
+                                               stptr->l1.l1l2(stptr, PH_DATA, cibh);
+                                       } else
+                                               printk(KERN_WARNING "isdn broadcast buffer shortage\n");
+                               stptr = stptr->next;
+                       }
+                       BufPoolRelease(ibh);
+               } else {
+                       found = 0;
+                       while (stptr != NULL)
+                               if (((ptr[0] >> 2) == stptr->l2.sap) &&
+                                   ((ptr[1] >> 1) == stptr->l2.tei)) {
+                                       stptr->l1.l1l2(stptr, PH_DATA, ibh);
+                                       found = !0;
+                                       break;
+                               } else
+                                       stptr = stptr->next;
+                       if (!found) {
+                               /* BD 10.10.95
+                                * Print out D-Channel msg not processed
+                                * by isdn4linux
+                                 */
+
+                               if ((!(ptr[0] >> 2)) && (!(ptr[2] & 0x01))) {
+                                       sprintf(tmp, "Q.931 frame network->user with tei %d (not for us)", ptr[1] >> 1);
+                                       dlogframe(sp, ptr + 4, ibh->datasize - 4, tmp);
+                               }
+                               BufPoolRelease(ibh);
+                       }
+               }
+
+       }
+
+}
+
+static void
+isac_bh(struct IsdnCardState *sp)
+{
+       if (!sp)
+               return;
+
+       if (clear_bit(ISAC_PHCHANGE, &sp->event))
+               process_new_ph(sp);
+       if (clear_bit(ISAC_RCVBUFREADY, &sp->event))
+               process_rcv(sp);
+       if (clear_bit(ISAC_XMTBUFREADY, &sp->event))
+               process_xmt(sp);
+}
+
+
+static void
+hscx_process_xmt(struct HscxState *hsp)
+{
+       struct PStack  *st = hsp->st;
+
+       if (hsp->xmtibh)
+               return;
+
+       if (st->l1.requestpull) {
+               st->l1.requestpull = 0;
+               st->l1.l1l2(st, PH_PULL_ACK, NULL);
+       }
+       if (!hsp->active)
+               if ((!hsp->xmtibh) && (!hsp->sq.head))
+                       modehscx(hsp, 0, 0);
+}
+
+static void
+hscx_process_rcv(struct HscxState *hsp)
+{
+       struct BufHeader *ibh;
+
+#ifdef DEBUG_MAGIC
+       if (hsp->magic != 301270) {
+               printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n");
+               return;
+       }
+#endif
+       while (!BufQueueUnlink(&ibh, &hsp->rq)) {
+               hsp->st->l1.l1l2(hsp->st, PH_DATA, ibh);
+       }
+}
+
+static void
+hscx_bh(struct HscxState *hsp)
+{
+
+       if (!hsp)
+               return;
+
+       if (clear_bit(HSCX_RCVBUFREADY, &hsp->event))
+               hscx_process_rcv(hsp);
+       if (clear_bit(HSCX_XMTBUFREADY, &hsp->event))
+               hscx_process_xmt(hsp);
+
+}
+
+/*
+ * interrupt stuff ends here
+ */
+
+static void
+restart_ph(struct IsdnCardState *sp)
+{
+       switch (sp->ph_active) {
+         case (0):
+                 if (sp->ph_state == 6)
+                         ph_command(sp, 0);
+                 else
+                         ph_command(sp, 1);
+                 sp->ph_active = 1;
+                 break;
+       }
+}
+
+static void
+initisac(byte * cardmem, int iobase)
+{
+        if (cardmem) {
+                writeisac_0(cardmem, ISAC_MASK, 0xff);
+                writeisac_0(cardmem, ISAC_ADF2, 0x0);
+                writeisac_0(cardmem, ISAC_SPCR, 0xa);
+                writeisac_0(cardmem, ISAC_ADF1, 0x2);
+                writeisac_0(cardmem, ISAC_STCR, 0x70);
+                writeisac_0(cardmem, ISAC_MODE, 0xc9);
+                writeisac_0(cardmem, ISAC_CMDR, 0x41);
+                writeisac_0(cardmem, ISAC_CIX0, (1 << 2) | 3);
+        } else {
+                writeisac_3(iobase, ISAC_MASK, 0xff);
+                writeisac_3(iobase, ISAC_ADF2, 0x80);
+                writeisac_3(iobase, ISAC_SQXR, 0x2f);
+                writeisac_3(iobase, ISAC_SPCR, 0x00);
+                writeisac_3(iobase, ISAC_ADF1, 0x02);
+                writeisac_3(iobase, ISAC_STCR, 0x70);
+                writeisac_3(iobase, ISAC_MODE, 0xc9);
+                writeisac_3(iobase, ISAC_TIMR, 0x00);
+                writeisac_3(iobase, ISAC_ADF1, 0x00);
+                writeisac_3(iobase, ISAC_CMDR, 0x41);
+                writeisac_3(iobase, ISAC_CIX0, (1 << 2) | 3);
+        }
+}
+
+static int
+checkcard(int cardnr)
+{
+       int             timout;
+       byte            cfval, val;
+       struct IsdnCard *card = cards + cardnr;
+
+        if (!card->iobase) {
+                if (card->membase) {
+                        printk(KERN_NOTICE
+                               "Teles 8 assumed, mem: %lx irq: %d proto: %s\n",
+                               (long) card->membase, card->interrupt,
+                               (card->protocol == ISDN_PTYPE_1TR6) ?
+                               "1TR6" : "EDSS1");
+                        printk(KERN_INFO "HSCX version A: %x B:%x\n",
+                               readhscx_0(card->membase, 0, HSCX_VSTR) & 0xf,
+                               readhscx_0(card->membase, 1, HSCX_VSTR) & 0xf);
+                }
+        } else {
+                if (check_region(card->iobase, 8)) {
+                        printk(KERN_WARNING
+                               "teles: ports %x-%x already in use\n",
+                               card->iobase,
+                               card->iobase + 8 );
+                        return -1;
+                }
+                switch (card->interrupt) {
+                case 2:
+                        cfval = 0x00;
+                        break;
+                case 3:
+                        cfval = 0x02;
+                        break;
+                case 4:
+                        cfval = 0x04;
+                        break;
+                case 5:
+                        cfval = 0x06;
+                        break;
+                case 10:
+                        cfval = 0x08;
+                        break;
+                case 11:
+                        cfval = 0x0A;
+                        break;
+                case 12:
+                        cfval = 0x0C;
+                        break;
+                case 15:
+                        cfval = 0x0E;
+                        break;
+                default:
+                        cfval = 0x00;
+                        break;
+                }
+                if (card->membase) {
+                        cfval |= (((unsigned int) card->membase >> 9) & 0xF0);
+                        
+                        if (bytein(card->iobase + 0) != 0x51) {
+                                printk(KERN_INFO "XXX Byte at %x is %x\n",
+                                       card->iobase + 0,
+                                       bytein(card->iobase + 0));
+                                return -2;
+                        }
+                        if (bytein(card->iobase + 1) != 0x93) {
+                                printk(KERN_INFO "XXX Byte at %x is %x\n",
+                                       card->iobase + 1,
+                                       bytein(card->iobase + 1));
+                                return -2;
+                        }
+                        val = bytein(card->iobase + 2);        /* 0x1e=without AB
+                                                         * 0x1f=with AB
+                                                         */
+                        if (val != 0x1e && val != 0x1f) {
+                                printk(KERN_INFO "XXX Byte at %x is %x\n",
+                                       card->iobase + 2,
+                                       bytein(card->iobase + 2));
+                                return -2;
+                        }
+                }
+                request_region(card->iobase, 8, "teles");
+                cli();
+                timout = jiffies + (HZ / 10) + 1;
+                byteout(card->iobase + 4, cfval);
+                sti();
+                while (jiffies <= timout);
+                
+                cli();
+                timout = jiffies + (HZ / 10) + 1;
+                byteout(card->iobase + 4, cfval | 1);
+                sti();
+                while (jiffies <= timout);
+                
+                if (card->membase)
+                        printk(KERN_NOTICE
+                               "Teles 16.0 found, io: %x mem: %lx irq: %d proto: %s\n",
+                               card->iobase, (long) card->membase,
+                               card->interrupt,
+                               (card->protocol == ISDN_PTYPE_1TR6) ?
+                               "1TR6" : "EDSS1");
+                else
+                        printk(KERN_NOTICE
+                               "Teles 16.3 found, io: %x irq: %d proto: %s\n",
+                               card->iobase, card->interrupt,
+                               (card->protocol == ISDN_PTYPE_1TR6) ?
+                               "1TR6" : "EDSS1");
+                printk(KERN_INFO "HSCX version A:%x B:%x\n",
+                       READHSCX(card->membase, card->iobase, 0,
+                                HSCX_VSTR) & 0xf,
+                       READHSCX(card->membase, card->iobase, 1,
+                                HSCX_VSTR) & 0xf);
+                
+        }
+        if (card->membase) {
+                cli();
+                timout = jiffies + (HZ / 5) + 1;
+                *(byte *) (card->membase + 0x80) = 0;
+                sti();
+                while (jiffies <= timout);
+                
+                cli();
+                *(byte *) (card->membase + 0x80) = 1;
+                timout = jiffies + (HZ / 5) + 1;
+                sti();
+                while (jiffies <= timout);
+        }
+       return (0);
+}
+
+void
+modehscx(struct HscxState *hs, int mode,
+        int ichan)
+{
+       struct IsdnCardState *sp = hs->sp;
+       int             hscx = hs->hscx;
+
+       printk(KERN_DEBUG "modehscx hscx %d mode %d ichan %d\n",
+              hscx, mode, ichan);
+
+       if (hscx == 0)
+               ichan = 1 - ichan;      /* raar maar waar... */
+
+        if (sp->membase) {
+                writehscx_0(sp->membase, hscx, HSCX_CCR1, 0x85);
+                writehscx_0(sp->membase, hscx, HSCX_XAD1, 0xFF);
+                writehscx_0(sp->membase, hscx, HSCX_XAD2, 0xFF);
+                writehscx_0(sp->membase, hscx, HSCX_RAH2, 0xFF);
+                writehscx_0(sp->membase, hscx, HSCX_XBCH, 0x0);
+
+                switch (mode) {
+                case (0):
+                        writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+                        writehscx_0(sp->membase, hscx, HSCX_TSAX, 0xff);
+                        writehscx_0(sp->membase, hscx, HSCX_TSAR, 0xff);
+                        writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+                        writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+                        writehscx_0(sp->membase, hscx, HSCX_MODE, 0x84);
+                        break;
+                case (1):
+                        if (ichan == 0) {
+                                writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x7);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x7);
+                                writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+                                writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+                        } else {
+                                writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x3);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x3);
+                                writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+                                writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+                        }
+                        writehscx_0(sp->membase, hscx, HSCX_MODE, 0xe4);
+                        writehscx_0(sp->membase, hscx, HSCX_CMDR, 0x41);
+                        break;
+                case (2):
+                        if (ichan == 0) {
+                                writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x7);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x7);
+                                writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+                                writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+                        } else {
+                                writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x3);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x3);
+                                writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+                                writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+                        }
+                        writehscx_0(sp->membase, hscx, HSCX_MODE, 0x8c);
+                        writehscx_0(sp->membase, hscx, HSCX_CMDR, 0x41);
+                        break;
+                }
+                writehscx_0(sp->membase, hscx, HSCX_ISTA, 0x00);
+        } else {
+                writehscx_3(sp->iobase, hscx, HSCX_CCR1, 0x85);
+                writehscx_3(sp->iobase, hscx, HSCX_XAD1, 0xFF);
+                writehscx_3(sp->iobase, hscx, HSCX_XAD2, 0xFF);
+                writehscx_3(sp->iobase, hscx, HSCX_RAH2, 0xFF);
+                writehscx_3(sp->iobase, hscx, HSCX_XBCH, 0x00);
+                writehscx_3(sp->iobase, hscx, HSCX_RLCR, 0x00);
+                
+                switch (mode) {
+                case (0):
+                        writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+                        writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0xff);
+                        writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0xff);
+                        writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+                        writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+                        writehscx_3(sp->iobase, hscx, HSCX_MODE, 0x84);
+                        break;
+                case (1):
+                        if (ichan == 0) {
+                                writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x2f);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x2f);
+                                writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+                                writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+                        } else {
+                                writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x3);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x3);
+                                writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+                                writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+                        }
+                        writehscx_3(sp->iobase, hscx, HSCX_MODE, 0xe4);
+                        writehscx_3(sp->iobase, hscx, HSCX_CMDR, 0x41);
+                        break;
+                case (2):
+                        if (ichan == 0) {
+                                writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x2f);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x2f);
+                                writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+                                writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+                        } else {
+                                writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x3);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x3);
+                                writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+                                writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+                        }
+                        writehscx_3(sp->iobase, hscx, HSCX_MODE, 0x8c);
+                        writehscx_3(sp->iobase, hscx, HSCX_CMDR, 0x41);
+                        break;
+                }
+                writehscx_3(sp->iobase, hscx, HSCX_ISTA, 0x00);
+        }
+}
+
+void
+teles_addlist(struct IsdnCardState *sp,
+             struct PStack *st)
+{
+       st->next = sp->stlist;
+       sp->stlist = st;
+}
+
+void
+teles_rmlist(struct IsdnCardState *sp,
+            struct PStack *st)
+{
+       struct PStack  *p;
+
+       if (sp->stlist == st)
+               sp->stlist = st->next;
+       else {
+               p = sp->stlist;
+               while (p)
+                       if (p->next == st) {
+                               p->next = st->next;
+                               return;
+                       } else
+                               p = p->next;
+       }
+}
+
+
+static void
+teles_l2l1(struct PStack *st, int pr,
+          struct BufHeader *ibh)
+{
+       struct IsdnCardState *sp = (struct IsdnCardState *)
+       st->l1.hardware;
+
+
+       switch (pr) {
+         case (PH_DATA):
+                 if (sp->xmtibh)
+                         BufQueueLink(&sp->sq, ibh);
+                 else {
+                         sp->xmtibh = ibh;
+                         sp->sendptr = 0;
+                         sp->releasebuf = !0;
+                         fill_fifo(sp);
+                 }
+                 break;
+         case (PH_DATA_PULLED):
+                 if (sp->xmtibh) {
+                         printk(KERN_DEBUG "teles_l2l1: this shouldn't happen\n");
+                         break;
+                 }
+                 sp->xmtibh = ibh;
+                 sp->sendptr = 0;
+                 sp->releasebuf = 0;
+                 fill_fifo(sp);
+                 break;
+         case (PH_REQUEST_PULL):
+                 if (!sp->xmtibh) {
+                         st->l1.requestpull = 0;
+                         st->l1.l1l2(st, PH_PULL_ACK, NULL);
+                 } else
+                         st->l1.requestpull = !0;
+                 break;
+       }
+}
+
+static void
+check_ph_act(struct IsdnCardState *sp)
+{
+       struct PStack  *st = sp->stlist;
+
+       while (st) {
+               if (st->l1.act_state)
+                       return;
+               st = st->next;
+       }
+       sp->ph_active = 0;
+}
+
+static void
+teles_manl1(struct PStack *st, int pr,
+           void *arg)
+{
+       struct IsdnCardState *sp = (struct IsdnCardState *)
+       st->l1.hardware;
+       long            flags;
+
+       switch (pr) {
+         case (PH_ACTIVATE):
+                 save_flags(flags);
+                 cli();
+                 if (sp->ph_active == 5) {
+                         st->l1.act_state = 2;
+                         restore_flags(flags);
+                         st->l1.l1man(st, PH_ACTIVATE, NULL);
+                 } else {
+                         st->l1.act_state = 1;
+                         if (sp->ph_active == 0)
+                                 restart_ph(sp);
+                         restore_flags(flags);
+                 }
+                 break;
+         case (PH_DEACTIVATE):
+                 st->l1.act_state = 0;
+                 check_ph_act(sp);
+                 break;
+       }
+}
+
+static void
+teles_l2l1discardq(struct PStack *st, int pr,
+                  void *heldby, int releasetoo)
+{
+       struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+
+#ifdef DEBUG_MAGIC
+       if (sp->magic != 301271) {
+               printk(KERN_DEBUG "isac_discardq magic not 301271\n");
+               return;
+       }
+#endif
+
+       BufQueueDiscard(&sp->sq, pr, heldby, releasetoo);
+}
+
+void
+setstack_teles(struct PStack *st, struct IsdnCardState *sp)
+{
+       st->l1.hardware = sp;
+       st->l1.sbufpool = &(sp->sbufpool);
+       st->l1.rbufpool = &(sp->rbufpool);
+       st->l1.smallpool = &(sp->smallpool);
+       st->protocol = sp->teistack->protocol;
+
+       setstack_tei(st);
+
+       st->l1.stlistp = &(sp->stlist);
+       st->l1.act_state = 0;
+       st->l2.l2l1 = teles_l2l1;
+       st->l2.l2l1discardq = teles_l2l1discardq;
+       st->ma.manl1 = teles_manl1;
+       st->l1.requestpull = 0;
+}
+
+void
+init_hscxstate(struct IsdnCardState *sp,
+              int hscx)
+{
+       struct HscxState *hsp = sp->hs + hscx;
+
+       hsp->sp = sp;
+       hsp->hscx = hscx;
+       hsp->membase = sp->membase;
+       hsp->iobase = sp->iobase;
+
+       hsp->tqueue.next = 0;
+       hsp->tqueue.sync = 0;
+       hsp->tqueue.routine = (void *) (void *) hscx_bh;
+       hsp->tqueue.data = hsp;
+
+       hsp->inuse = 0;
+       hsp->init = 0;
+       hsp->active = 0;
+
+#ifdef DEBUG_MAGIC
+       hsp->magic = 301270;
+#endif
+}
+
+void
+initcard(int cardnr)
+{
+       struct IsdnCardState *sp;
+       struct IsdnCard *card = cards + cardnr;
+
+       sp = (struct IsdnCardState *)
+           Smalloc(sizeof(struct IsdnCardState), GFP_KERNEL,
+                   "struct IsdnCardState");
+
+       sp->membase = card->membase;
+       sp->iobase = card->iobase;
+       sp->cardnr = cardnr;
+
+       BufPoolInit(&sp->sbufpool, ISAC_SBUF_ORDER, ISAC_SBUF_BPPS,
+                   ISAC_SBUF_MAXPAGES);
+       BufPoolInit(&sp->rbufpool, ISAC_RBUF_ORDER, ISAC_RBUF_BPPS,
+                   ISAC_RBUF_MAXPAGES);
+       BufPoolInit(&sp->smallpool, ISAC_SMALLBUF_ORDER, ISAC_SMALLBUF_BPPS,
+                   ISAC_SMALLBUF_MAXPAGES);
+
+       sp->dlogspace = Smalloc(4096, GFP_KERNEL, "dlogspace");
+
+       initisac(card->membase, card->iobase);
+
+       sp->rcvibh = NULL;
+       sp->rcvptr = 0;
+       sp->xmtibh = NULL;
+       sp->sendptr = 0;
+       sp->event = 0;
+       sp->tqueue.next = 0;
+       sp->tqueue.sync = 0;
+       sp->tqueue.routine = (void *) (void *) isac_bh;
+       sp->tqueue.data = sp;
+
+       BufQueueInit(&sp->rq);
+       BufQueueInit(&sp->sq);
+
+       sp->stlist = NULL;
+
+       sp->ph_active = 0;
+
+       sp->dlogflag = 0;
+       sp->debug = 0;
+
+       sp->releasebuf = 0;
+#ifdef DEBUG_MAGIC
+       sp->magic = 301271;
+#endif
+
+       cards[sp->cardnr].sp = sp;
+
+       init_hscxstate(sp, 0);
+       init_hscxstate(sp, 1);
+
+       modehscx(sp->hs, 0, 0);
+       modehscx(sp->hs + 1, 0, 0);
+
+       WRITEISAC(sp->membase, sp->iobase, ISAC_MASK, 0x0);
+}
+
+static int
+get_irq(int cardnr)
+{
+       struct IsdnCard *card = cards + cardnr;
+       long            flags;
+
+       save_flags(flags);
+       cli();
+       if (request_irq(card->interrupt, &teles_interrupt,
+                       SA_INTERRUPT, "teles")) {
+               printk(KERN_WARNING "Teles couldn't get interrupt %d\n",
+                       card->interrupt);
+               restore_flags(flags);
+               return (!0);
+       }
+       irq2dev_map[card->interrupt] = (void *) card->sp;
+       restore_flags(flags);
+       return (0);
+}
+
+static void
+release_irq(int cardnr)
+{
+       struct IsdnCard *card = cards + cardnr;
+
+       irq2dev_map[card->interrupt] = NULL;
+       free_irq(card->interrupt);
+}
+
+void
+close_hscxstate(struct HscxState *hs)
+{
+       modehscx(hs, 0, 0);
+       hs->inuse = 0;
+
+       if (hs->init) {
+               BufPoolFree(&hs->smallpool);
+               BufPoolFree(&hs->rbufpool);
+               BufPoolFree(&hs->sbufpool);
+       }
+       hs->init = 0;
+}
+
+void
+closecard(int cardnr)
+{
+       struct IsdnCardState *sp = cards[cardnr].sp;
+
+       cards[cardnr].sp = NULL;
+
+       Sfree(sp->dlogspace);
+
+       BufPoolFree(&sp->smallpool);
+       BufPoolFree(&sp->rbufpool);
+       BufPoolFree(&sp->sbufpool);
+
+       close_hscxstate(sp->hs + 1);
+       close_hscxstate(sp->hs);
+
+       if (cards[cardnr].iobase)
+               release_region(cards[cardnr].iobase, 8);
+
+       Sfree((void *) sp);
+}
+
+void
+teles_shiftcards(int idx)
+{
+        int i;
+
+        for (i = idx; i < 15; i++)
+                memcpy(&cards[i],&cards[i+1],sizeof(cards[i]));
+}
+
+int
+teles_inithardware(void)
+{
+        int             foundcards = 0;
+       int             i = 0;
+
+       while (i < nrcards) {
+                if (!cards[i].protocol)
+                        break;
+               switch (checkcard(i)) {
+                 case (0):
+                         initcard(i);
+                         if (get_irq(i)) {
+                                 closecard(i);
+                                  teles_shiftcards(i);
+                          } else {
+                                  foundcards++;
+                                  i++;
+                          }
+                         break;
+                 case (-1):
+                          teles_shiftcards(i);
+                         break;
+                 case (-2):
+                         release_region(cards[i].iobase, 8);
+                          teles_shiftcards(i);
+                         printk(KERN_WARNING "NO Teles card found at 0x%x!\n", cards[i].iobase);
+                         break;
+               }
+        }
+        return foundcards;
+}
+
+void
+teles_closehardware(void)
+{
+       int             i;
+
+       for (i = 0; i < nrcards; i++)
+               if (cards[i].sp) {
+                       release_irq(i);
+                       closecard(i);
+               }
+}
+
+static void
+hscx_l2l1(struct PStack *st, int pr,
+         struct BufHeader *ibh)
+{
+       struct IsdnCardState *sp = (struct IsdnCardState *)
+       st->l1.hardware;
+       struct HscxState *hsp = sp->hs + st->l1.hscx;
+
+       switch (pr) {
+         case (PH_DATA):
+                 if (hsp->xmtibh)
+                         BufQueueLink(&hsp->sq, ibh);
+                 else {
+                         hsp->xmtibh = ibh;
+                         hsp->sendptr = 0;
+                         hsp->releasebuf = !0;
+                         hscx_fill_fifo(hsp);
+                 }
+                 break;
+         case (PH_DATA_PULLED):
+                 if (hsp->xmtibh) {
+                         printk(KERN_DEBUG "hscx_l2l1: this shouldn't happen\n");
+                         break;
+                 }
+                 hsp->xmtibh = ibh;
+                 hsp->sendptr = 0;
+                 hsp->releasebuf = 0;
+                 hscx_fill_fifo(hsp);
+                 break;
+         case (PH_REQUEST_PULL):
+                 if (!hsp->xmtibh) {
+                         st->l1.requestpull = 0;
+                         st->l1.l1l2(st, PH_PULL_ACK, NULL);
+                 } else
+                         st->l1.requestpull = !0;
+                 break;
+       }
+
+}
+
+extern struct IsdnBuffers *tracebuf;
+
+static void
+hscx_l2l1discardq(struct PStack *st, int pr, void *heldby,
+                 int releasetoo)
+{
+       struct IsdnCardState *sp = (struct IsdnCardState *)
+       st->l1.hardware;
+       struct HscxState *hsp = sp->hs + st->l1.hscx;
+
+#ifdef DEBUG_MAGIC
+       if (hsp->magic != 301270) {
+               printk(KERN_DEBUG "hscx_discardq magic not 301270\n");
+               return;
+       }
+#endif
+
+       BufQueueDiscard(&hsp->sq, pr, heldby, releasetoo);
+}
+
+static int
+open_hscxstate(struct IsdnCardState *sp,
+              int hscx)
+{
+       struct HscxState *hsp = sp->hs + hscx;
+
+       if (!hsp->init) {
+               BufPoolInit(&hsp->sbufpool, HSCX_SBUF_ORDER, HSCX_SBUF_BPPS,
+                           HSCX_SBUF_MAXPAGES);
+               BufPoolInit(&hsp->rbufpool, HSCX_RBUF_ORDER, HSCX_RBUF_BPPS,
+                           HSCX_RBUF_MAXPAGES);
+               BufPoolInit(&hsp->smallpool, HSCX_SMALLBUF_ORDER, HSCX_SMALLBUF_BPPS,
+                           HSCX_SMALLBUF_MAXPAGES);
+       }
+       hsp->init = !0;
+
+       BufQueueInit(&hsp->rq);
+       BufQueueInit(&hsp->sq);
+
+       hsp->releasebuf = 0;
+       hsp->rcvibh = NULL;
+       hsp->xmtibh = NULL;
+       hsp->rcvptr = 0;
+       hsp->sendptr = 0;
+       hsp->event = 0;
+       return (0);
+}
+
+static void
+hscx_manl1(struct PStack *st, int pr,
+          void *arg)
+{
+       struct IsdnCardState *sp = (struct IsdnCardState *)
+       st->l1.hardware;
+       struct HscxState *hsp = sp->hs + st->l1.hscx;
+
+       switch (pr) {
+         case (PH_ACTIVATE):
+                 hsp->active = !0;
+                 modehscx(hsp, st->l1.hscxmode, st->l1.hscxchannel);
+                 st->l1.l1man(st, PH_ACTIVATE, NULL);
+                 break;
+         case (PH_DEACTIVATE):
+                 if (!hsp->xmtibh)
+                         modehscx(hsp, 0, 0);
+
+                 hsp->active = 0;
+                 break;
+       }
+}
+
+int
+setstack_hscx(struct PStack *st, struct HscxState *hs)
+{
+       if (open_hscxstate(st->l1.hardware, hs->hscx))
+               return (-1);
+
+       st->l1.hscx = hs->hscx;
+       st->l2.l2l1 = hscx_l2l1;
+       st->ma.manl1 = hscx_manl1;
+       st->l2.l2l1discardq = hscx_l2l1discardq;
+
+       st->l1.sbufpool = &hs->sbufpool;
+       st->l1.rbufpool = &hs->rbufpool;
+       st->l1.smallpool = &hs->smallpool;
+       st->l1.act_state = 0;
+       st->l1.requestpull = 0;
+
+       hs->st = st;
+       return (0);
+}
+
+void
+teles_reportcard(int cardnr)
+{
+
+       printk(KERN_DEBUG "teles_reportcard\n");
+
+}
diff --git a/drivers/isdn/teles/config.c b/drivers/isdn/teles/config.c
new file mode 100644 (file)
index 0000000..7524d02
--- /dev/null
@@ -0,0 +1,40 @@
+#define __NO_VERSION__
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include "teles.h"
+
+/*
+ * This structure array contains one entry per card. An entry looks
+ * like this:
+ * 
+ * { membase,irq,portbase,protocol,NULL }
+ *
+ * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6
+ *
+ * Cards which don't have an io port (Teles 8 bit cards for
+ * example) can be entered with io port 0x0
+ *
+ * For the Teles 16.3, membase has to be set to 0.
+ *
+ */
+
+struct IsdnCard cards[] =
+{
+       {(byte *) 0xd0000, 15, 0xd80, ISDN_PTYPE_EURO, NULL},   /* example */
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+       {NULL, 0, 0, 0, NULL},
+};
diff --git a/drivers/isdn/teles/fsm.c b/drivers/isdn/teles/fsm.c
new file mode 100644 (file)
index 0000000..94ef1fe
--- /dev/null
@@ -0,0 +1,148 @@
+#define __NO_VERSION__
+#include "teles.h"
+
+void
+FsmNew(struct Fsm *fsm,
+       struct FsmNode *fnlist, int fncount)
+{
+       int             i;
+
+       fsm->jumpmatrix = (int *) Smalloc(4L * fsm->state_count * fsm->event_count,
+                                         GFP_KERNEL, "Fsm jumpmatrix");
+       memset(fsm->jumpmatrix, 0, 4L * fsm->state_count * fsm->event_count);
+
+       for (i = 0; i < fncount; i++)
+               fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
+                             fnlist[i].state] = (int) fnlist[i].routine;
+}
+
+void
+FsmFree(struct Fsm *fsm)
+{
+       Sfree((void *) fsm->jumpmatrix);
+}
+
+int
+FsmEvent(struct FsmInst *fi, int event, void *arg)
+{
+       void            (*r) (struct FsmInst *, int, void *);
+       char            str[80];
+
+       r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
+       if (r) {
+               if (fi->debug) {
+                       sprintf(str, "State %s Event %s",
+                               fi->fsm->strState[fi->state],
+                               fi->fsm->strEvent[event]);
+                       fi->printdebug(fi, str);
+               }
+               r(fi, event, arg);
+               return (0);
+       } else {
+               if (fi->debug) {
+                       sprintf(str, "State %s Event %s no routine",
+                               fi->fsm->strState[fi->state],
+                               fi->fsm->strEvent[event]);
+                       fi->printdebug(fi, str);
+               }
+               return (!0);
+       }
+}
+
+void
+FsmChangeState(struct FsmInst *fi, int newstate)
+{
+       char            str[80];
+
+       fi->state = newstate;
+       if (fi->debug) {
+               sprintf(str, "ChangeState %s",
+                       fi->fsm->strState[newstate]);
+               fi->printdebug(fi, str);
+       }
+}
+
+static void
+FsmExpireTimer(struct FsmTimer *ft)
+{
+       FsmEvent(ft->fi, ft->event, ft->arg);
+}
+
+void
+FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
+{
+       ft->fi = fi;
+       ft->tl.function = (void *) FsmExpireTimer;
+       ft->tl.data = (long) ft;
+       init_timer(&ft->tl);
+}
+
+void
+FsmDelTimer(struct FsmTimer *ft, int where)
+{
+       long            flags;
+
+#if 0
+       if (ft->fi->debug) {
+               sprintf(str, "FsmDelTimer %lx %d", ft, where);
+               ft->fi->printdebug(ft->fi, str);
+       }
+#endif
+
+       save_flags(flags);
+       cli();
+       if (ft->tl.next)
+               del_timer(&ft->tl);
+       restore_flags(flags);
+}
+
+int
+FsmAddTimer(struct FsmTimer *ft,
+           int millisec, int event, void *arg, int where)
+{
+
+#if 0
+       if (ft->fi->debug) {
+               sprintf(str, "FsmAddTimer %lx %d %d", ft, millisec, where);
+               ft->fi->printdebug(ft->fi, str);
+       }
+#endif
+
+       if (ft->tl.next) {
+               printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
+               return -1;
+       }
+       init_timer(&ft->tl);
+       ft->event = event;
+       ft->arg = arg;
+       ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+       add_timer(&ft->tl);
+       return 0;
+}
+
+int
+FsmTimerRunning(struct FsmTimer *ft)
+{
+       return (ft->tl.next != NULL);
+}
+
+void
+jiftime(char *s, long mark)
+{
+       s += 8;
+
+       *s-- = '\0';
+       *s-- = mark % 10 + '0';
+       mark /= 10;
+       *s-- = mark % 10 + '0';
+       mark /= 10;
+       *s-- = '.';
+       *s-- = mark % 10 + '0';
+       mark /= 10;
+       *s-- = mark % 6 + '0';
+       mark /= 6;
+       *s-- = ':';
+       *s-- = mark % 10 + '0';
+       mark /= 10;
+       *s-- = mark % 10 + '0';
+}
diff --git a/drivers/isdn/teles/isdnl2.c b/drivers/isdn/teles/isdnl2.c
new file mode 100644 (file)
index 0000000..b0ae253
--- /dev/null
@@ -0,0 +1,1451 @@
+#define __NO_VERSION__
+#include "teles.h"
+
+#define TIMER_1 2000
+
+static void     l2m_debug(struct FsmInst *fi, char *s);
+
+struct Fsm      l2fsm =
+{NULL, 0, 0};
+
+#if 0
+
+
+enum {
+       ST_PH_NULL,
+       ST_PH_ACTIVATED,
+       ST_PH_ACTIVE,
+};
+
+#define PH_STATE_COUNT (ST_PH_ACTIVE+1)
+
+static char    *strPhState[] =
+{
+       "ST_PH_NULL",
+       "ST_PH_ACTIVATED",
+       "ST_PH_ACTIVE",
+};
+
+enum {
+       EV_PH_ACTIVATE_REQ,
+       EV_PH_ACTIVATE,
+       EV_PH_DEACTIVATE_REQ,
+       EV_PH_DEACTIVATE,
+};
+
+#define PH_EVENT_COUNT (EV_PH_DEACTIVATE+1)
+
+static char    *strPhEvent[] =
+{
+       "EV_PH_ACTIVATE_REQ",
+       "EV_PH_ACTIVATE",
+       "EV_PH_DEACTIVATE_REQ",
+       "EV_PH_DEACTIVATE",
+};
+
+#endif
+
+enum {
+       ST_L2_1,
+       ST_L2_3,
+       ST_L2_4,
+       ST_L2_5,
+       ST_L2_6,
+       ST_L2_7,
+       ST_L2_8,
+};
+
+#define L2_STATE_COUNT (ST_L2_8+1)
+
+static char    *strL2State[] =
+{
+       "ST_L2_1",
+       "ST_L2_3",
+       "ST_L2_4",
+       "ST_L2_5",
+       "ST_L2_6",
+       "ST_L2_7",
+       "ST_L2_8",
+};
+
+enum {
+       EV_L2_UI,
+       EV_L2_SABMX,
+       EV_L2_UA,
+       EV_L2_DISC,
+       EV_L2_I,
+       EV_L2_RR,
+       EV_L2_REJ,
+       EV_L2_FRMR,
+       EV_L2_DL_DATA,
+       EV_L2_DL_ESTABLISH,
+       EV_L2_MDL_ASSIGN,
+       EV_L2_DL_UNIT_DATA,
+       EV_L2_DL_RELEASE,
+       EV_L2_MDL_NOTEIPROC,
+       EV_L2_T200,
+       EV_L2_ACK_PULL,
+       EV_L2_T203,
+       EV_L2_RNR,
+};
+
+#define L2_EVENT_COUNT (EV_L2_RNR+1)
+
+static char    *strL2Event[] =
+{
+       "EV_L2_UI",
+       "EV_L2_SABMX",
+       "EV_L2_UA",
+       "EV_L2_DISC",
+       "EV_L2_I",
+       "EV_L2_RR",
+       "EV_L2_REJ",
+       "EV_L2_FRMR",
+       "EV_L2_DL_DATA",
+       "EV_L2_DL_ESTABLISH",
+       "EV_L2_MDL_ASSIGN",
+       "EV_L2_DL_UNIT_DATA",
+       "EV_L2_DL_RELEASE",
+       "EV_L2_MDL_NOTEIPROC",
+       "EV_L2_T200",
+       "EV_L2_ACK_PULL",
+       "EV_L2_T203",
+       "EV_L2_RNR",
+};
+
+#if 0
+static void
+ph_r1(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+
+       FsmChangeState(fi, ST_PH_ACTIVATED);
+       st->l1.service_down(st, PH_ACTIVATE, NULL);
+}
+
+static void
+ph_r2(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+
+       FsmChangeState(fi, ST_PH_ACTIVE);
+       st->l3.service_up(st, DL_ACTIVATE_CNF, NULL);
+}
+
+static void
+ph_r3(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+
+       FsmChangeState(fi, ST_PH_NULL);
+       st->l1.service_down(st, PH_DEACTIVATE, NULL);
+}
+
+static void
+ph_r4(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+
+       FsmChangeState(fi, ST_PH_NULL);
+       st->l3.service_up(st, DL_DEACTIVATE_IND, NULL);
+}
+
+static struct FsmNode PhFnList[] =
+{
+       {ST_PH_NULL, EV_PH_ACTIVATE_REQ, ph_r1},
+       {ST_PH_ACTIVATED, EV_PH_ACTIVATE, ph_r2},
+       {ST_PH_ACTIVATED, EV_PH_DEACTIVATE, ph_r4},
+       {ST_PH_ACTIVE, EV_PH_DEACTIVATE_REQ, ph_r3},
+};
+
+#define PH_FN_COUNT (sizeof(PhFnList)/sizeof(struct FsmNode))
+#endif
+
+int             errcount = 0;
+
+static int      l2addrsize(struct Layer2 *tsp);
+
+static int
+cansend(struct PStack *st)
+{
+       int             p1;
+
+       p1 = (st->l2.va + st->l2.window) % (st->l2.extended ? 128 : 8);
+       return (st->l2.vs != p1);
+}
+
+static void
+discard_i_queue(struct PStack *st)
+{
+       struct BufHeader *ibh;
+
+       while (!BufQueueUnlink(&ibh, &st->l2.i_queue))
+               BufPoolRelease(ibh);
+}
+
+#ifdef DEFINED_BUT_NOT_USED
+static void
+discard_window(struct PStack *st)
+{
+       struct BufHeader *ibh;
+       struct Layer2  *l2;
+       int             i, p1, p2;
+
+       l2 = &st->l2;
+       p1 = l2->vs - l2->va;
+       if (p1 < 0)
+               p1 += l2->extended ? 128 : 8;
+
+       for (i = 0; i < p1; i++) {
+               p2 = (i + l2->sow) % l2->window;
+               ibh = l2->windowar[p2];
+               BufPoolRelease(ibh);
+       }
+}
+#endif
+
+int
+l2headersize(struct Layer2 *tsp, int UI)
+{
+       return ((tsp->extended && (!UI) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1));
+}
+
+int
+l2addrsize(struct Layer2 *tsp)
+{
+       return (tsp->laptype == LAPD ? 2 : 1);
+}
+
+static int
+sethdraddr(struct Layer2 *tsp,
+          struct BufHeader *ibh, int rsp)
+{
+       byte           *ptr = DATAPTR(ibh);
+       int             crbit;
+
+       if (tsp->laptype == LAPD) {
+               crbit = rsp;
+               if (!tsp->orig)
+                       crbit = !crbit;
+               *ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0);
+               *ptr++ = (tsp->tei << 1) | 1;
+               return (2);
+       } else {
+               crbit = rsp;
+               if (tsp->orig)
+                       crbit = !crbit;
+               if (crbit)
+                       *ptr++ = 1;
+               else
+                       *ptr++ = 3;
+               return (1);
+       }
+}
+
+static void
+enqueue_ui(struct PStack *st,
+          struct BufHeader *ibh)
+{
+#ifdef FRITZDEBUG
+       static char     tmp[100];
+
+       sprintf(tmp, "enqueue_ui: %d bytes\n", ibh->datasize);
+       teles_putstatus(tmp);
+#endif
+       st->l2.l2l1(st, PH_DATA, ibh);
+}
+
+static void
+enqueue_super(struct PStack *st,
+             struct BufHeader *ibh)
+{
+#ifdef FRITZDEBUG
+       static char     tmp[100];
+
+       sprintf(tmp, "enqueue_super: %d bytes\n", ibh->datasize);
+       teles_putstatus(tmp);
+#endif
+       st->l2.l2l1(st, PH_DATA, ibh);
+}
+
+static int
+legalnr(struct PStack *st, int nr)
+{
+       struct Layer2  *l2 = &st->l2;
+       int             lnr, lvs;
+
+       lvs = (l2->vs >= l2->va) ? l2->vs : (l2->vs + l2->extended ? 128 : 8);
+       lnr = (nr >= l2->va) ? nr : (nr + l2->extended ? 128 : 8);
+       return (lnr <= lvs);
+}
+
+static void
+setva(struct PStack *st, int nr)
+{
+       struct Layer2  *l2 = &st->l2;
+
+       if (l2->va != nr) {
+                while (l2->va != nr) {
+                        l2->va = (l2->va + 1) % (l2->extended ? 128 : 8);
+                        BufPoolRelease(l2->windowar[l2->sow]);
+                        l2->sow = (l2->sow + 1) % l2->window;
+                }
+                if (st->l4.l2writewakeup)
+                        st->l4.l2writewakeup(st);
+        }
+}
+
+static void
+l2s1(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+
+       st->l2.l2tei(st, MDL_ASSIGN, (void *)st->l2.ces);
+       FsmChangeState(fi, ST_L2_3);
+}
+
+static void
+l2s2(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct BufHeader *ibh = arg;
+
+       byte           *ptr;
+       int             i;
+
+       i = sethdraddr(&(st->l2), ibh, 0);
+       ptr = DATAPTR(ibh);
+       ptr += i;
+       *ptr = 0x3;
+
+       enqueue_ui(st, ibh);
+}
+
+static void
+l2s3(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct BufHeader *ibh = arg;
+
+       st->l2.l2l3(st, DL_UNIT_DATA, ibh);
+}
+
+static void
+establishlink(struct FsmInst *fi)
+{
+       struct PStack  *st = fi->userdata;
+       struct BufHeader *ibh;
+       int             i;
+       byte           *ptr;
+
+       FsmChangeState(fi, ST_L2_5);
+       st->l2.rc = 0;
+
+       if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 1))
+               if (st->l2.l2m.debug)
+                       l2m_debug(&st->l2.l2m, "FAT 1");
+
+
+       if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
+               return;
+       i = sethdraddr(&st->l2, ibh, 0);
+       ptr = DATAPTR(ibh);
+       ptr += i;
+       if (st->l2.extended)
+               *ptr = 0x7f;
+       else
+               *ptr = 0x3f;
+       ibh->datasize = i + 1;
+
+       enqueue_super(st, ibh);
+}
+
+static void
+l2s11(struct FsmInst *fi, int event, void *arg)
+{
+       establishlink(fi);
+}
+
+static void
+l2s13(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct Channel *chanp = st->l4.userdata;
+       byte           *ptr;
+       struct BufHeader *ibh;
+       int             i;
+
+       FsmChangeState(fi, ST_L2_6);
+
+       FsmDelTimer(&st->l2.t203_timer, 1);
+       if (st->l2.t200_running) {
+               FsmDelTimer(&st->l2.t200_timer, 2);
+               st->l2.t200_running = 0;
+       }
+       st->l2.rc = 0;
+       if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 2))
+               if (st->l2.l2m.debug)
+                       l2m_debug(&st->l2.l2m, "FAT 2");
+
+
+       if ((chanp->impair == 2) && (st->l2.laptype == LAPB))
+               goto nodisc;
+
+       if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 9))
+               return;
+       i = sethdraddr(&(st->l2), ibh, 0);
+       ptr = DATAPTR(ibh);
+       ptr += i;
+       *ptr = 0x53;
+       ibh->datasize = i + 1;
+       enqueue_super(st, ibh);
+
+      nodisc:
+       discard_i_queue(st);
+}
+
+static void
+l2s12(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct BufHeader *ibh = arg;
+       byte           *ptr;
+       int             i;
+
+       BufPoolRelease(ibh);
+       st->l2.vs = 0;
+       st->l2.va = 0;
+       st->l2.vr = 0;
+       st->l2.sow = 0;
+       FsmChangeState(fi, ST_L2_7);
+       if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3))
+               if (st->l2.l2m.debug)
+                       l2m_debug(&st->l2.l2m, "FAT 3");
+
+       st->l2.l2man(st, DL_ESTABLISH, NULL);
+
+       if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 10))
+               return;
+       i = sethdraddr(&(st->l2), ibh, 0);
+       ptr = DATAPTR(ibh);
+       ptr += i;
+       *ptr = 0x73;
+       ibh->datasize = i + 1;
+       enqueue_super(st, ibh);
+
+}
+
+static void
+l2s14(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct BufHeader *ibh = arg;
+       struct Channel *chanp = st->l4.userdata;
+       byte           *ptr;
+       int             i, p;
+
+       ptr = DATAPTR(ibh);
+       ptr += l2addrsize(&(st->l2));
+       p = (*ptr) & 0x10;
+       BufPoolRelease(ibh);
+
+       FsmChangeState(fi, ST_L2_4);
+
+       FsmDelTimer(&st->l2.t203_timer, 3);
+       if (st->l2.t200_running) {
+               FsmDelTimer(&st->l2.t200_timer, 4);
+               st->l2.t200_running = 0;
+       }
+       if ((chanp->impair == 1) && (st->l2.laptype == LAPB))
+               goto noresponse;
+
+       if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 11))
+               return;
+       i = sethdraddr(&(st->l2), ibh, 0);
+       ptr = DATAPTR(ibh);
+       ptr += i;
+       *ptr = 0x63 | (p ? 0x10 : 0x0);
+       ibh->datasize = i + 1;
+       enqueue_super(st, ibh);
+
+      noresponse:
+       st->l2.l2man(st, DL_RELEASE, NULL);
+
+}
+
+static void
+l2s5(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct BufHeader *ibh = arg;
+       int             f;
+       byte           *data;
+
+       data = DATAPTR(ibh);
+       data += l2addrsize(&(st->l2));
+
+       f = *data & 0x10;
+       BufPoolRelease(ibh);
+
+       if (f) {
+               st->l2.vs = 0;
+               st->l2.va = 0;
+               st->l2.vr = 0;
+               st->l2.sow = 0;
+               FsmChangeState(fi, ST_L2_7);
+
+               FsmDelTimer(&st->l2.t200_timer, 5);
+               if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 4))
+                       if (st->l2.l2m.debug)
+                               l2m_debug(&st->l2.l2m, "FAT 4");
+
+
+               st->l2.l2man(st, DL_ESTABLISH, NULL);
+       }
+}
+
+static void
+l2s15(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct BufHeader *ibh = arg;
+       int             f;
+       byte           *data;
+
+       data = DATAPTR(ibh);
+       data += l2addrsize(&st->l2);
+
+       f = *data & 0x10;
+       BufPoolRelease(ibh);
+
+       if (f) {
+               FsmDelTimer(&st->l2.t200_timer, 6);
+               FsmChangeState(fi, ST_L2_4);
+               st->l2.l2man(st, DL_RELEASE, NULL);
+       }
+}
+
+static void
+l2s6(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct Channel *chanp = st->l4.userdata;
+       struct BufHeader *ibh = arg;
+       int             p, i, seq, rsp;
+       byte           *ptr;
+       struct Layer2  *l2;
+
+       l2 = &st->l2;
+       ptr = DATAPTR(ibh);
+
+       if (l2->laptype == LAPD) {
+               rsp = ptr[0] & 0x2;
+               if (l2->orig)
+                       rsp = !rsp;
+       } else {
+               rsp = ptr[0] == 0x3;
+               if (l2->orig)
+                       rsp = !rsp;
+       }
+
+       ptr += l2addrsize(l2);
+
+       if (l2->extended) {
+               p = (ptr[1] & 0x1) == 0x1;
+               seq = ptr[1] >> 1;
+       } else {
+               p = (ptr[0] & 0x10);
+               seq = (ptr[0] >> 5) & 0x7;
+       }
+       BufPoolRelease(ibh);
+
+       if ((chanp->impair == 4) && (st->l2.laptype == LAPB))
+               goto noresp;
+
+       if ((!rsp) && p) {
+               if (!BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 12)) {
+                       i = sethdraddr(l2, ibh, !0);
+                       ptr = DATAPTR(ibh);
+                       ptr += i;
+
+                       if (l2->extended) {
+                               *ptr++ = 0x1;
+                               *ptr++ = (l2->vr << 1) | (p ? 1 : 0);
+                               i += 2;
+                       } else {
+                               *ptr++ = (l2->vr << 5) | 0x1 | (p ? 0x10 : 0x0);
+                               i += 1;
+                       }
+                       ibh->datasize = i;
+                       enqueue_super(st, ibh);
+               }
+       }
+      noresp:
+       if (legalnr(st, seq))
+               if (seq == st->l2.vs) {
+                       setva(st, seq);
+                       FsmDelTimer(&st->l2.t200_timer, 7);
+                       st->l2.t200_running = 0;
+                       FsmDelTimer(&st->l2.t203_timer, 8);
+                       if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 5))
+                               if (st->l2.l2m.debug)
+                                       l2m_debug(&st->l2.l2m, "FAT 5");
+
+                       if (st->l2.i_queue.head)
+                               st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+               } else if (st->l2.va != seq) {
+                       setva(st, seq);
+                       FsmDelTimer(&st->l2.t200_timer, 9);
+                       if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 6))
+                               if (st->l2.l2m.debug)
+                                       l2m_debug(&st->l2.l2m, "FAT 6");
+
+                       if (st->l2.i_queue.head)
+                               st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+               }
+}
+
+static void
+l2s7(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct BufHeader *ibh = arg;
+       int             i;
+       byte           *ptr;
+       struct IsdnCardState *sp = st->l1.hardware;
+       char            str[64];
+
+       i = sethdraddr(&st->l2, ibh, 0);
+       ptr = DATAPTR(ibh);
+
+       if (st->l2.laptype == LAPD)
+               if (sp->dlogflag) {
+                       sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+                       dlogframe(sp, ptr + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+                                 str);
+               }
+       BufQueueLink(&st->l2.i_queue, ibh);
+
+       st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+}
+
+static void
+l2s8(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct Channel *chanp = st->l4.userdata;
+       struct BufHeader *ibh = arg;
+       byte           *ptr;
+       struct BufHeader *ibh2;
+       struct IsdnCardState *sp = st->l1.hardware;
+       struct Layer2  *l2 = &(st->l2);
+       int             i, p, seq, nr, wasok;
+       char            str[64];
+
+       ptr = DATAPTR(ibh);
+       ptr += l2addrsize(l2);
+       if (l2->extended) {
+               p = (ptr[1] & 0x1) == 0x1;
+               seq = ptr[0] >> 1;
+               nr = (ptr[1] >> 1) & 0x7f;
+       } else {
+               p = (ptr[0] & 0x10);
+               seq = (ptr[0] >> 1) & 0x7;
+               nr = (ptr[0] >> 5) & 0x7;
+       }
+
+       if (l2->vr == seq) {
+               wasok = !0;
+
+               l2->vr = (l2->vr + 1) % (l2->extended ? 128 : 8);
+               l2->rejexp = 0;
+
+               ptr = DATAPTR(ibh);
+               if (st->l2.laptype == LAPD)
+                       if (sp->dlogflag) {
+                               sprintf(str, "Q.931 frame network->user tei %d", st->l2.tei);
+                               dlogframe(st->l1.hardware, ptr + l2->ihsize,
+                                       ibh->datasize - l2->ihsize, str);
+                       }
+             label8_1:
+               if ((chanp->impair == 3) && (st->l2.laptype == LAPB))
+                       goto noRR;
+
+               if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 13)) {
+                       i = sethdraddr(&(st->l2), ibh2, p);
+                       ptr = DATAPTR(ibh2);
+                       ptr += i;
+
+                       if (l2->extended) {
+                               *ptr++ = 0x1;
+                               *ptr++ = (l2->vr << 1) | (p ? 1 : 0);
+                               i += 2;
+                       } else {
+                               *ptr++ = (l2->vr << 5) | 0x1 | (p ? 0x10 : 0x0);
+                               i += 1;
+                       }
+                       ibh2->datasize = i;
+                       enqueue_super(st, ibh2);
+                     noRR:
+               }
+       } else {
+               /* n(s)!=v(r) */
+               wasok = 0;
+               BufPoolRelease(ibh);
+               if (st->l2.rejexp) {
+                       if (p)
+                               goto label8_1;
+               } else {
+                       st->l2.rejexp = !0;
+                       if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 14)) {
+                               i = sethdraddr(&(st->l2), ibh2, p);
+                               ptr = DATAPTR(ibh2);
+                               ptr += i;
+
+                               if (l2->extended) {
+                                       *ptr++ = 0x9;
+                                       *ptr++ = (l2->vr << 1) | (p ? 1 : 0);
+                                       i += 2;
+                               } else {
+                                       *ptr++ = (l2->vr << 5) | 0x9 | (p ? 0x10 : 0x0);
+                                       i += 1;
+                               }
+                               ibh2->datasize = i;
+                               enqueue_super(st, ibh2);
+                       }
+               }
+       }
+
+       if (legalnr(st, nr))
+               if (nr == st->l2.vs) {
+                       setva(st, nr);
+                       FsmDelTimer(&st->l2.t200_timer, 10);
+                       st->l2.t200_running = 0;
+                       FsmDelTimer(&st->l2.t203_timer, 11);
+                       if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 7))
+                               if (st->l2.l2m.debug)
+                                       l2m_debug(&st->l2.l2m, "FAT 5");
+
+                       if (st->l2.i_queue.head)
+                               st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+               } else if (nr != st->l2.va) {
+                       setva(st, nr);
+                       FsmDelTimer(&st->l2.t200_timer, 12);
+                       if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 8))
+                               if (st->l2.l2m.debug)
+                                       l2m_debug(&st->l2.l2m, "FAT 6");
+
+                       if (st->l2.i_queue.head)
+                               st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+               }
+       if (wasok)
+               st->l2.l2l3(st, DL_DATA, ibh);
+
+}
+
+static void
+l2s17(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+
+       st->l2.tei = (int) arg;
+       establishlink(fi);
+}
+
+static void
+enquiry_response(struct PStack *st)
+{
+       struct BufHeader *ibh2;
+       int             i;
+       byte           *ptr;
+       struct Layer2  *l2;
+
+       l2 = &st->l2;
+       if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 16)) {
+               i = sethdraddr(&(st->l2), ibh2, !0);
+               ptr = DATAPTR(ibh2);
+               ptr += i;
+
+               if (l2->extended) {
+                       *ptr++ = 0x1;
+                       *ptr++ = (l2->vr << 1) | 0x1;
+                       i += 2;
+               } else {
+                       *ptr++ = (l2->vr << 5) | 0x1 | 0x10;
+                       i += 1;
+               }
+               ibh2->datasize = i;
+               enqueue_super(st, ibh2);
+       }
+}
+
+static void
+invoke_retransmission(struct PStack *st, int nr)
+{
+       struct Layer2  *l2 = &st->l2;
+       int             p1;
+
+       if (l2->vs != nr) {
+               while (l2->vs != nr) {
+
+                       l2->vs = l2->vs - 1;
+                       if (l2->vs < 0)
+                               l2->vs += l2->extended ? 128 : 8;
+
+                       p1 = l2->vs - l2->va;
+                       if (p1 < 0)
+                               p1 += l2->extended ? 128 : 8;
+                       p1 = (p1 + l2->sow) % l2->window;
+
+                       BufQueueLinkFront(&l2->i_queue, l2->windowar[p1]);
+               }
+               st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+       }
+}
+
+static void
+l2s16(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct BufHeader *ibh = arg;
+       int             p, seq, rsp;
+       byte           *ptr;
+       struct Layer2  *l2;
+
+       l2 = &(st->l2);
+       ptr = DATAPTR(ibh);
+
+       if (l2->laptype == LAPD) {
+               rsp = ptr[0] & 0x2;
+               if (l2->orig)
+                       rsp = !rsp;
+       } else {
+               rsp = ptr[0] == 0x3;
+               if (l2->orig)
+                       rsp = !rsp;
+       }
+
+
+       ptr += l2addrsize(l2);
+
+       if (l2->extended) {
+               p = (ptr[1] & 0x1) == 0x1;
+               seq = ptr[1] >> 1;
+       } else {
+               p = (ptr[0] & 0x10);
+               seq = (ptr[0] >> 5) & 0x7;
+       }
+       BufPoolRelease(ibh);
+
+       if ((!rsp) && p)
+               enquiry_response(st);
+
+       if (!legalnr(st, seq))
+               return;
+
+       setva(st, seq);
+       invoke_retransmission(st, seq);
+
+}
+
+static void
+l2s19(struct FsmInst *fi, int event, void *arg)
+{
+       FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2s20(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       int             i;
+       struct BufHeader *ibh;
+       byte           *ptr;
+
+       if (st->l2.rc == st->l2.n200) {
+               FsmChangeState(fi, ST_L2_4);
+               st->l2.l2man(st, DL_RELEASE, NULL);
+       } else {
+               st->l2.rc++;
+
+               if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 9))
+                       if (st->l2.l2m.debug)
+                               l2m_debug(&st->l2.l2m, "FAT 7");
+
+               if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
+                       return;
+
+               i = sethdraddr(&st->l2, ibh, 0);
+               ptr = DATAPTR(ibh);
+               ptr += i;
+               if (st->l2.extended)
+                       *ptr = 0x7f;
+               else
+                       *ptr = 0x3f;
+               ibh->datasize = i + 1;
+               enqueue_super(st, ibh);
+       }
+}
+
+static void
+l2s21(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct Channel *chanp = st->l4.userdata;
+       int             i;
+       struct BufHeader *ibh;
+       byte           *ptr;
+
+       if (st->l2.rc == st->l2.n200) {
+               FsmChangeState(fi, ST_L2_4);
+               st->l2.l2man(st, DL_RELEASE, NULL);
+       } else {
+               st->l2.rc++;
+
+               if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 10))
+                       if (st->l2.l2m.debug)
+                               l2m_debug(&st->l2.l2m, "FAT 8");
+
+
+               if ((chanp->impair == 2) && (st->l2.laptype == LAPB))
+                       goto nodisc;
+
+               if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
+                       return;
+
+               i = sethdraddr(&st->l2, ibh, 0);
+               ptr = DATAPTR(ibh);
+               ptr += i;
+               *ptr = 0x53;
+               ibh->datasize = i + 1;
+               enqueue_super(st, ibh);
+             nodisc:
+
+       }
+}
+
+static void
+l2s22(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct BufHeader *ibh;
+       struct Layer2  *l2 = &st->l2;
+       byte           *ptr;
+       int             p1;
+
+       if (!cansend(st))
+               return;
+
+       if (BufQueueUnlink(&ibh, &l2->i_queue))
+               return;
+
+
+       p1 = l2->vs - l2->va;
+       if (p1 < 0)
+               p1 += l2->extended ? 128 : 8;
+       p1 = (p1 + l2->sow) % l2->window;
+       l2->windowar[p1] = ibh;
+
+       ptr = DATAPTR(ibh);
+       ptr += l2addrsize(l2);
+
+       if (l2->extended) {
+               *ptr++ = l2->vs << 1;
+               *ptr++ = (l2->vr << 1) | 0x1;
+               l2->vs = (l2->vs + 1) % 128;
+       } else {
+               *ptr++ = (l2->vr << 5) | (l2->vs << 1) | 0x10;
+               l2->vs = (l2->vs + 1) % 8;
+       }
+
+       st->l2.l2l1(st, PH_DATA_PULLED, ibh);
+
+       if (!st->l2.t200_running) {
+               FsmDelTimer(&st->l2.t203_timer, 13);
+               if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 11))
+                       if (st->l2.l2m.debug)
+                               l2m_debug(&st->l2.l2m, "FAT 9");
+
+               st->l2.t200_running = !0;
+       }
+       if (l2->i_queue.head && cansend(st))
+               st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+
+}
+
+static void
+transmit_enquiry(struct PStack *st)
+{
+       struct BufHeader *ibh;
+       byte           *ptr;
+
+       if (!BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 12)) {
+               ptr = DATAPTR(ibh);
+               ptr += sethdraddr(&st->l2, ibh, 0);
+
+               if (st->l2.extended) {
+                       *ptr++ = 0x1;
+                       *ptr++ = (st->l2.vr << 1) | 1;
+               } else {
+                       *ptr++ = (st->l2.vr << 5) | 0x11;
+               }
+               ibh->datasize = ptr - DATAPTR(ibh);
+               enqueue_super(st, ibh);
+               if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 12))
+                       if (st->l2.l2m.debug)
+                               l2m_debug(&st->l2.l2m, "FAT 10");
+
+               st->l2.t200_running = !0;
+       }
+}
+
+static void
+l2s23(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+
+       st->l2.t200_running = 0;
+
+       st->l2.rc = 1;
+       FsmChangeState(fi, ST_L2_8);
+       transmit_enquiry(st);
+}
+
+static void
+l2s24(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct BufHeader *ibh = arg;
+       int             p, seq, rsp;
+       byte           *ptr;
+       struct Layer2  *l2;
+
+       l2 = &st->l2;
+       ptr = DATAPTR(ibh);
+
+       if (l2->laptype == LAPD) {
+               rsp = ptr[0] & 0x2;
+               if (l2->orig)
+                       rsp = !rsp;
+       } else {
+               rsp = ptr[0] == 0x3;
+               if (l2->orig)
+                       rsp = !rsp;
+       }
+
+
+       ptr += l2addrsize(l2);
+
+       if (l2->extended) {
+               p = (ptr[1] & 0x1) == 0x1;
+               seq = ptr[1] >> 1;
+       } else {
+               p = (ptr[0] & 0x10);
+               seq = (ptr[0] >> 5) & 0x7;
+       }
+       BufPoolRelease(ibh);
+
+       if (rsp && p) {
+               if (legalnr(st, seq)) {
+                       FsmChangeState(fi, ST_L2_7);
+                       setva(st, seq);
+                       if (st->l2.t200_running) {
+                               FsmDelTimer(&st->l2.t200_timer, 14);
+                               st->l2.t200_running = 0;
+                       }
+                       if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 13))
+                               if (st->l2.l2m.debug)
+                                       l2m_debug(&st->l2.l2m, "FAT 11");
+
+                       invoke_retransmission(st, seq);
+               }
+       } else {
+               if (!rsp && p)
+                       enquiry_response(st);
+               if (legalnr(st, seq)) {
+                       setva(st, seq);
+               }
+       }
+}
+
+static void
+l2s25(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+
+       st->l2.rc = 0;
+       FsmChangeState(fi, ST_L2_8);
+       transmit_enquiry(st);
+}
+
+static void
+l2s26(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+
+       if (st->l2.rc == st->l2.n200) {
+               l2s13(fi, event, NULL);
+       } else {
+               st->l2.rc++;
+               transmit_enquiry(st);
+       }
+}
+
+static void
+l2s27(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct BufHeader *ibh = arg;
+       byte           *ptr;
+       int             i, p, est;
+
+       ptr = DATAPTR(ibh);
+       ptr += l2addrsize(&st->l2);
+
+       if (st->l2.extended)
+               p = ptr[1] & 0x1;
+       else
+               p = ptr[0] & 0x10;
+
+       BufPoolRelease(ibh);
+
+       if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 10))
+               return;
+       i = sethdraddr(&st->l2, ibh, 0);
+       ptr = DATAPTR(ibh);
+       ptr += i;
+       *ptr = 0x63 | p;
+       ibh->datasize = i + 1;
+       enqueue_super(st, ibh);
+
+       if (st->l2.vs != st->l2.va) {
+               discard_i_queue(st);
+               est = !0;
+       } else
+               est = 0;
+
+       FsmDelTimer(&st->l2.t200_timer, 15);
+       st->l2.t200_running = 0;
+
+       if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3))
+               if (st->l2.l2m.debug)
+                       l2m_debug(&st->l2.l2m, "FAT 12");
+
+       st->l2.vs = 0;
+       st->l2.va = 0;
+       st->l2.vr = 0;
+       st->l2.sow = 0;
+
+
+       if (est)
+               st->l2.l2man(st, DL_ESTABLISH, NULL);
+
+}
+
+static void
+l2s28(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack  *st = fi->userdata;
+       struct BufHeader *ibh = arg;
+       byte           *ptr;
+       char            tmp[64];
+
+       ptr = DATAPTR(ibh);
+       ptr += l2addrsize(&st->l2);
+       ptr++;
+
+       if (st->l2.l2m.debug) {
+               if (st->l2.extended)
+                       sprintf(tmp, "FRMR information %2x %2x %2x %2x %2x",
+                               ptr[0], ptr[1], ptr[2], ptr[3], ptr[4]);
+               else
+                       sprintf(tmp, "FRMR information %2x %2x %2x",
+                               ptr[0], ptr[1], ptr[2]);
+
+               l2m_debug(&st->l2.l2m, tmp);
+       }
+       BufPoolRelease(ibh);
+}
+
+static int
+IsUI(byte * data, int ext)
+{
+       return ((data[0] & 0xef) == 0x3);
+}
+
+static int
+IsUA(byte * data, int ext)
+{
+       return ((data[0] & 0xef) == 0x63);
+}
+
+static int
+IsDISC(byte * data, int ext)
+{
+       return ((data[0] & 0xef) == 0x43);
+}
+
+static int
+IsRR(byte * data, int ext)
+{
+       if (ext)
+               return (data[0] == 0x1);
+       else
+               return ((data[0] & 0xf) == 1);
+}
+
+static int
+IsI(byte * data, int ext)
+{
+       return ((data[0] & 0x1) == 0x0);
+}
+
+static int
+IsSABMX(byte * data, int ext)
+{
+       return (ext ? data[0] == 0x7f : data[0] == 0x3f);
+}
+
+static int
+IsREJ(byte * data, int ext)
+{
+       return (ext ? data[0] == 0x9 : (data[0] & 0xf) == 0x9);
+}
+
+static int
+IsFRMR(byte * data, int ext)
+{
+       return ((data[0] & 0xef) == 0x87);
+}
+
+static int
+IsRNR(byte * data, int ext)
+{
+       if (ext)
+               return (data[0] == 0x5);
+       else
+               return ((data[0] & 0xf) == 5);
+}
+
+static struct FsmNode L2FnList[] =
+{
+       {ST_L2_1, EV_L2_DL_ESTABLISH, l2s1},
+       {ST_L2_1, EV_L2_MDL_NOTEIPROC, l2s19},
+       {ST_L2_3, EV_L2_MDL_ASSIGN, l2s17},
+       {ST_L2_4, EV_L2_DL_UNIT_DATA, l2s2},
+       {ST_L2_4, EV_L2_DL_ESTABLISH, l2s11},
+       {ST_L2_7, EV_L2_DL_UNIT_DATA, l2s2},
+       {ST_L2_7, EV_L2_DL_DATA, l2s7},
+       {ST_L2_7, EV_L2_DL_RELEASE, l2s13},
+       {ST_L2_7, EV_L2_ACK_PULL, l2s22},
+       {ST_L2_8, EV_L2_DL_RELEASE, l2s13},
+
+       {ST_L2_1, EV_L2_UI, l2s3},
+       {ST_L2_4, EV_L2_UI, l2s3},
+       {ST_L2_4, EV_L2_SABMX, l2s12},
+       {ST_L2_5, EV_L2_UA, l2s5},
+       {ST_L2_6, EV_L2_UA, l2s15},
+       {ST_L2_7, EV_L2_UI, l2s3},
+       {ST_L2_7, EV_L2_DISC, l2s14},
+       {ST_L2_7, EV_L2_I, l2s8},
+       {ST_L2_7, EV_L2_RR, l2s6},
+       {ST_L2_7, EV_L2_REJ, l2s16},
+       {ST_L2_7, EV_L2_SABMX, l2s27},
+       {ST_L2_7, EV_L2_FRMR, l2s28},
+       {ST_L2_8, EV_L2_RR, l2s24},
+       {ST_L2_8, EV_L2_DISC, l2s14},
+       {ST_L2_8, EV_L2_FRMR, l2s28},
+
+       {ST_L2_5, EV_L2_T200, l2s20},
+       {ST_L2_6, EV_L2_T200, l2s21},
+       {ST_L2_7, EV_L2_T200, l2s23},
+       {ST_L2_7, EV_L2_T203, l2s25},
+       {ST_L2_8, EV_L2_T200, l2s26},
+};
+
+#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
+
+static void
+isdnl2_l1l2(struct PStack *st, int pr, struct BufHeader *arg)
+{
+       struct BufHeader *ibh;
+       byte           *datap;
+       int             ret = !0;
+
+       switch (pr) {
+         case (PH_DATA):
+
+                 ibh = arg;
+                 datap = DATAPTR(ibh);
+                 datap += l2addrsize(&st->l2);
+
+                 if (IsI(datap, st->l2.extended))
+                         ret = FsmEvent(&st->l2.l2m, EV_L2_I, ibh);
+                 else if (IsRR(datap, st->l2.extended))
+                         ret = FsmEvent(&st->l2.l2m, EV_L2_RR, ibh);
+                 else if (IsUI(datap, st->l2.extended))
+                         ret = FsmEvent(&st->l2.l2m, EV_L2_UI, ibh);
+                 else if (IsSABMX(datap, st->l2.extended))
+                         ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, ibh);
+                 else if (IsUA(datap, st->l2.extended))
+                         ret = FsmEvent(&st->l2.l2m, EV_L2_UA, ibh);
+                 else if (IsDISC(datap, st->l2.extended))
+                         ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, ibh);
+                 else if (IsREJ(datap, st->l2.extended))
+                         ret = FsmEvent(&st->l2.l2m, EV_L2_REJ, ibh);
+                 else if (IsFRMR(datap, st->l2.extended))
+                         ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, ibh);
+                 else if (IsRNR(datap, st->l2.extended))
+                         ret = FsmEvent(&st->l2.l2m, EV_L2_RNR, ibh);
+
+                 if (ret)
+                         BufPoolRelease(ibh);
+
+                 break;
+         case (PH_PULL_ACK):
+                 FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg);
+                 break;
+       }
+}
+
+static void
+isdnl2_l3l2(struct PStack *st, int pr,
+           void *arg)
+{
+       switch (pr) {
+         case (DL_DATA):
+                 if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg))
+                         BufPoolRelease((struct BufHeader *) arg);
+                 break;
+         case (DL_UNIT_DATA):
+                 if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg))
+                         BufPoolRelease((struct BufHeader *) arg);
+                 break;
+       }
+}
+
+static void
+isdnl2_manl2(struct PStack *st, int pr,
+            void *arg)
+{
+       switch (pr) {
+         case (DL_ESTABLISH):
+                 FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
+                 break;
+         case (DL_RELEASE):
+                 FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE, arg);
+                 break;
+         case (MDL_NOTEIPROC):
+                 FsmEvent(&st->l2.l2m, EV_L2_MDL_NOTEIPROC, NULL);
+                 break;
+       }
+}
+
+static void
+isdnl2_teil2(struct PStack *st, int pr,
+            void *arg)
+{
+       switch (pr) {
+         case (MDL_ASSIGN):
+                 FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg);
+                 break;
+       }
+}
+
+void
+releasestack_isdnl2(struct PStack *st)
+{
+       FsmDelTimer(&st->l2.t200_timer, 15);
+       FsmDelTimer(&st->l2.t203_timer, 16);
+}
+
+static void
+l2m_debug(struct FsmInst *fi, char *s)
+{
+       struct PStack  *st = fi->userdata;
+       char            tm[32], str[256];
+
+       jiftime(tm, jiffies);
+       sprintf(str, "%s %s %s\n", tm, st->l2.debug_id, s);
+       teles_putstatus(str);
+}
+
+
+void
+setstack_isdnl2(struct PStack *st, char *debug_id)
+{
+       st->l1.l1l2 = isdnl2_l1l2;
+       st->l3.l3l2 = isdnl2_l3l2;
+       st->ma.manl2 = isdnl2_manl2;
+       st->ma.teil2 = isdnl2_teil2;
+
+       st->l2.uihsize = l2headersize(&st->l2, !0);
+       st->l2.ihsize = l2headersize(&st->l2, 0);
+       BufQueueInit(&(st->l2.i_queue));
+       st->l2.rejexp = 0;
+       st->l2.debug = 1;
+
+       st->l2.l2m.fsm = &l2fsm;
+       st->l2.l2m.state = ST_L2_1;
+       st->l2.l2m.debug = 0;
+       st->l2.l2m.userdata = st;
+       st->l2.l2m.printdebug = l2m_debug;
+       strcpy(st->l2.debug_id, debug_id);
+
+       FsmInitTimer(&st->l2.l2m, &st->l2.t200_timer);
+       FsmInitTimer(&st->l2.l2m, &st->l2.t203_timer);
+       st->l2.t200_running = 0;
+}
+
+#ifdef DEFINED_BUT_NOT_USED
+static void
+trans_acceptph(struct PStack *st, struct BufHeader *ibh)
+{
+#if 0
+       st->l3.service_up(st, DL_DATA, ibh);
+#endif
+}
+
+
+static void
+transdown(struct PStack *st, int pr,
+         struct BufHeader *ibh)
+{
+       if (pr == DL_DATA) {
+               ibh->primitive = !0;
+               st->l2.l2l1(st, PH_DATA, ibh);
+       }
+}
+#endif
+
+void
+setstack_transl2(struct PStack *st)
+{
+#if 0
+       st->l2.phdata_up = trans_acceptph;
+       st->l2.service_down = (void *) transdown;
+       st->l2.ihsize = 0;
+       st->l2.debug = 0;
+#endif
+}
+
+void
+releasestack_transl2(struct PStack *st)
+{
+}
+
+void
+Isdnl2New(void)
+{
+       l2fsm.state_count = L2_STATE_COUNT;
+       l2fsm.event_count = L2_EVENT_COUNT;
+       l2fsm.strEvent = strL2Event;
+       l2fsm.strState = strL2State;
+       FsmNew(&l2fsm, L2FnList, L2_FN_COUNT);
+}
+
+void
+Isdnl2Free(void)
+{
+       FsmFree(&l2fsm);
+}
diff --git a/drivers/isdn/teles/isdnl3.c b/drivers/isdn/teles/isdnl3.c
new file mode 100644 (file)
index 0000000..5d303ba
--- /dev/null
@@ -0,0 +1,537 @@
+#define __NO_VERSION__
+#define P_1TR6
+#include "teles.h"
+#include "l3_1TR6.h"
+#define DEBUG_1TR6 0
+
+static void
+i_down(struct PStack *st,
+       struct BufHeader *ibh)
+{
+       st->l3.l3l2(st, DL_DATA, ibh);
+}
+
+static void
+newl3state(struct PStack *st, int state)
+{
+       st->l3.state = state;
+       if (DEBUG_1TR6 > 4)
+               printk(KERN_INFO "isdnl3: bc:%d cr:%x new state %d\n",
+                      st->pa->bchannel, st->pa->callref, state);
+
+}
+
+static void
+l3_message(struct PStack *st, int mt)
+{
+       struct BufHeader *dibh;
+       byte           *p;
+       int             size;
+
+       BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 18);
+       p = DATAPTR(dibh);
+       p += st->l2.ihsize;
+       size = st->l2.ihsize;
+
+       *p++ = 0x8;
+       *p++ = 0x1;
+       *p++ = st->l3.callref;
+       *p++ = mt;
+       size += 4;
+
+       dibh->datasize = size;
+       i_down(st, dibh);
+}
+
+static void
+l3s3(struct PStack *st, byte pr, void *arg)
+{
+       l3_message(st, MT_RELEASE);
+       newl3state(st, 19);
+}
+
+static void
+l3s4(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *ibh = arg;
+
+       BufPoolRelease(ibh);
+       newl3state(st, 0);
+       st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
+}
+
+static void
+l3s5(struct PStack *st, byte pr,
+     void *arg)
+{
+       struct BufHeader *dibh;
+       byte           *p;
+       char           *teln;
+
+       st->l3.callref = st->pa->callref;
+       BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 19);
+       p = DATAPTR(dibh);
+       p += st->l2.ihsize;
+
+       *p++ = 0x8;
+       *p++ = 0x1;
+       *p++ = st->l3.callref;
+       *p++ = MT_SETUP;
+       *p++ = 0xa1;
+
+       /*
+         * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
+         */
+       switch (st->pa->info) {
+         case 1:               /* Telephony                               */
+                 *p++ = 0x4;   /* BC-IE-code                              */
+                 *p++ = 0x3;   /* Length                                  */
+                 *p++ = 0x90;  /* Coding Std. national, 3.1 kHz audio     */
+                 *p++ = 0x90;  /* Circuit-Mode 64kbps                     */
+                 *p++ = 0xa3;  /* A-Law Audio                             */
+                 break;
+         case 5:               /* Datatransmission 64k, BTX               */
+         case 7:               /* Datatransmission 64k                    */
+         default:
+                 *p++ = 0x4;   /* BC-IE-code                              */
+                 *p++ = 0x2;   /* Length                                  */
+                 *p++ = 0x88;  /* Coding Std. nat., unrestr. dig. Inform. */
+                 *p++ = 0x90;  /* Packet-Mode 64kbps                      */
+                 break;
+       }
+/*
+ * What about info2? Mapping to High-Layer-Compatibility?
+ */
+
+#if 0                          /* user-user not allowed in The Netherlands! */
+       *p++ = 0x7f;
+       *p++ = 0x2;
+       *p++ = 0x0;
+       *p++ = 66;
+#endif
+
+       if (st->pa->calling[0] != '\0') {
+               *p++ = 0x6c;
+               *p++ = strlen(st->pa->calling) + 1;
+               /* Classify as AnyPref. */
+               *p++ = 0x81;    /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+               teln = st->pa->calling;
+               while (*teln)
+                       *p++ = *teln++ & 0x7f;
+       }
+       *p++ = 0x70;
+       *p++ = strlen(st->pa->called) + 1;
+       /* Classify as AnyPref. */
+       *p++ = 0x81;            /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+
+       teln = st->pa->called;
+       while (*teln)
+               *p++ = *teln++ & 0x7f;
+
+
+       dibh->datasize = p - DATAPTR(dibh);
+
+       newl3state(st, 1);
+       i_down(st, dibh);
+
+}
+
+static void
+l3s6(struct PStack *st, byte pr, void *arg)
+{
+       byte           *p;
+       struct BufHeader *ibh = arg;
+
+       p = DATAPTR(ibh);
+       if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+                       0x18, 0))) {
+               st->pa->bchannel = p[2] & 0x3;
+       } else
+               printk(KERN_WARNING "octect 3 not found\n");
+
+       BufPoolRelease(ibh);
+       newl3state(st, 3);
+       st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
+}
+
+static void
+l3s7(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *ibh = arg;
+
+       BufPoolRelease(ibh);
+       newl3state(st, 12);
+       st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
+}
+
+static void
+l3s8(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *ibh = arg;
+
+       BufPoolRelease(ibh);
+       st->l3.l3l4(st, CC_SETUP_CNF, NULL);
+       newl3state(st, 10);
+}
+
+static void
+l3s11(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *ibh = arg;
+
+       BufPoolRelease(ibh);
+       newl3state(st, 4);
+       st->l3.l3l4(st, CC_ALERTING_IND, NULL);
+}
+
+static void
+l3s12(struct PStack *st, byte pr, void *arg)
+{
+       byte           *p;
+       struct BufHeader *ibh = arg;
+
+       p = DATAPTR(ibh);
+       p += st->l2.uihsize;
+       st->pa->callref = getcallref(p);
+       st->l3.callref = 0x80 + st->pa->callref;
+
+       /*
+         * Channel Identification
+         */
+       p = DATAPTR(ibh);
+       if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+                       0x18, 0))) {
+               st->pa->bchannel = p[2] & 0x3;
+       } else
+               printk(KERN_WARNING "l3s12: Channel ident not found\n");
+
+       p = DATAPTR(ibh);
+       if (st->protocol == ISDN_PTYPE_1TR6) {
+               if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, 0x01, 6))) {
+                       st->pa->info = p[2];
+                       st->pa->info2 = p[3];
+               } else
+                       printk(KERN_WARNING "l3s12(1TR6): ServiceIndicator not found\n");
+       } else {
+               /*
+                 * Bearer Capabilities
+                 */
+               if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, 0x04, 0))) {
+                       switch (p[2] & 0x1f) {
+                         case 0x00:
+                                  /* Speech */
+                         case 0x10:
+                                  /* 3.1 Khz audio */
+                                 st->pa->info = 1;
+                                 break;
+                         case 0x08:
+                                  /* Unrestricted digital information */
+                                 st->pa->info = 7;
+                                 break;
+                         case 0x09:
+                                  /* Restricted digital information */
+                                 st->pa->info = 2;
+                                 break;
+                         case 0x11:
+                                  /* Unrestr. digital information  with tones/announcements */
+                                 st->pa->info = 3;
+                                 break;
+                         case 0x18:
+                                  /* Video */
+                                 st->pa->info = 4;
+                                 break;
+                         default:
+                                 st->pa->info = 0;
+                       }
+               } else
+                       printk(KERN_WARNING "l3s12: Bearer capabilities not found\n");
+       }
+
+       p = DATAPTR(ibh);
+       if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+                       0x70, 0)))
+               iecpy(st->pa->called, p, 1);
+       else
+               strcpy(st->pa->called, "");
+
+       p = DATAPTR(ibh);
+       if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+                       0x6c, 0))) {
+               if (st->protocol == ISDN_PTYPE_1TR6)
+                       iecpy(st->pa->calling, p, 1);
+               else
+                       iecpy(st->pa->calling, p, 2);
+       } else
+               strcpy(st->pa->calling, "");
+       BufPoolRelease(ibh);
+
+       if (st->pa->info == 7) {
+               newl3state(st, 6);
+               st->l3.l3l4(st, CC_SETUP_IND, NULL);
+       } else {
+               printk(KERN_WARNING "non-digital call: %s -> %s\n",
+                      st->pa->calling,
+                      st->pa->called);
+       }
+}
+
+static void
+l3s13(struct PStack *st, byte pr, void *arg)
+{
+       newl3state(st, 0);
+}
+
+#ifdef DEFINED_BUT_NOT_USED
+static void
+l3s15(struct PStack *st, byte pr, void *arg)
+{
+       newl3state(st, 0);
+       st->l3.l3l4(st, CC_REJECT, NULL);
+}
+#endif
+
+static void
+l3s16(struct PStack *st, byte pr,
+      void *arg)
+{
+       st->l3.callref = 0x80 + st->pa->callref;
+       l3_message(st, MT_CONNECT);
+       newl3state(st, 8);
+}
+
+static void
+l3s17(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *ibh = arg;
+
+       BufPoolRelease(ibh);
+       st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
+       newl3state(st, 10);
+}
+
+static void
+l3s18(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *dibh;
+       byte           *p;
+       int             size;
+
+       BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
+       p = DATAPTR(dibh);
+       p += st->l2.ihsize;
+       size = st->l2.ihsize;
+
+       *p++ = 0x8;
+       *p++ = 0x1;
+       *p++ = st->l3.callref;
+       *p++ = MT_DISCONNECT;
+       size += 4;
+
+       *p++ = IE_CAUSE;
+       *p++ = 0x2;
+       *p++ = 0x80;
+       *p++ = 0x90;
+       size += 4;
+
+       dibh->datasize = size;
+       i_down(st, dibh);
+
+       newl3state(st, 11);
+}
+
+static void
+l3s19(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *ibh = arg;
+
+       BufPoolRelease(ibh);
+       newl3state(st, 0);
+       l3_message(st, MT_RELEASE_COMPLETE);
+       st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+}
+
+static void
+l3s20(struct PStack *st, byte pr,
+      void *arg)
+{
+       l3_message(st, MT_ALERTING);
+       newl3state(st, 7);
+}
+
+struct stateentry {
+       int             state;
+       byte            primitive;
+       void            (*rout) (struct PStack *, byte, void *);
+};
+
+static struct stateentry downstatelist[] =
+{
+       {0, CC_SETUP_REQ, l3s5},
+       {6, CC_REJECT_REQ, l3s13},
+       {6, CC_SETUP_RSP, l3s16},
+       {6, CC_ALERTING_REQ, l3s20},
+       {7, CC_SETUP_RSP, l3s16},
+       {10, CC_DISCONNECT_REQ, l3s18},
+       {12, CC_RELEASE_REQ, l3s3},
+};
+
+static int      downsllen = sizeof(downstatelist) /
+sizeof(struct stateentry);
+
+static struct stateentry datastatelist[] =
+{
+       {0, MT_SETUP, l3s12},
+       {1, MT_CALL_PROCEEDING, l3s6},
+       {1, MT_RELEASE_COMPLETE, l3s7},
+       {3, MT_DISCONNECT, l3s7},
+       {3, MT_CONNECT, l3s8},
+       {3, MT_ALERTING, l3s11},
+       {4, MT_CONNECT, l3s8},
+       {4, MT_DISCONNECT, l3s7},
+       {4, MT_RELEASE, l3s19},
+       {7, MT_RELEASE, l3s19},
+       {8, MT_CONNECT_ACKNOWLEDGE, l3s17},
+       {10, MT_DISCONNECT, l3s7},
+       {11, MT_RELEASE, l3s19},
+       {19, MT_RELEASE_COMPLETE, l3s4},
+};
+
+static int      datasllen = sizeof(datastatelist) /
+sizeof(struct stateentry);
+
+#ifdef P_1TR6
+#include "l3_1TR6.c"
+#endif
+
+static void
+l3up(struct PStack *st,
+     int pr, void *arg)
+{
+       int             i, mt, size;
+       byte           *ptr;
+       struct BufHeader *ibh = arg;
+
+       if (pr == DL_DATA) {
+               ptr = DATAPTR(ibh);
+               ptr += st->l2.ihsize;
+               size = ibh->datasize - st->l2.ihsize;
+               if (DEBUG_1TR6 > 6) {
+                       printk(KERN_INFO "isdnl3/l3up DL_DATA size=%d\n", size);
+                       for (i = 0; i < size; i++)
+                               printk(KERN_INFO "l3up data %x\n", ptr[i]);
+               }
+               mt = ptr[3];
+               switch (ptr[0]) {
+#ifdef P_1TR6
+                 case PROTO_DIS_N0:
+                         BufPoolRelease(ibh);
+                         break;
+                 case PROTO_DIS_N1:
+                         for (i = 0; i < datasl_1tr6t_len; i++)
+                                 if ((st->l3.state == datastatelist_1tr6t[i].state) &&
+                                     (mt == datastatelist_1tr6t[i].primitive))
+                                         break;
+                         if (i == datasl_1tr6t_len) {
+                                 BufPoolRelease(ibh);
+                                 if (DEBUG_1TR6 > 0)
+                                         printk(KERN_INFO "isdnl3up unhandled 1tr6 state %d MT %s\n",
+                                                st->l3.state, mt_trans(PROTO_DIS_N1, mt));
+                         } else
+                                 datastatelist_1tr6t[i].rout(st, pr, ibh);
+                         break;
+#endif
+                 default:      /* E-DSS1 */
+                         for (i = 0; i < datasllen; i++)
+                                 if ((st->l3.state == datastatelist[i].state) &&
+                                     (mt == datastatelist[i].primitive))
+                                         break;
+                         if (i == datasllen) {
+                                 BufPoolRelease(ibh);
+                         } else
+                                 datastatelist[i].rout(st, pr, ibh);
+               }
+       } else if (pr == DL_UNIT_DATA) {
+               ptr = DATAPTR(ibh);
+               ptr += st->l2.uihsize;
+               size = ibh->datasize - st->l2.uihsize;
+               if (DEBUG_1TR6 > 6) {
+                       printk(KERN_INFO "isdnl3/l3up DL_UNIT_DATA size=%d\n", size);
+                       for (i = 0; i < size; i++)
+                               printk(KERN_INFO "l3up data %x\n", ptr[i]);
+               }
+               mt = ptr[3];
+               switch (ptr[0]) {
+#ifdef P_1TR6
+                 case PROTO_DIS_N0:
+                         BufPoolRelease(ibh);
+                         break;
+                 case PROTO_DIS_N1:
+                         for (i = 0; i < datasl_1tr6t_len; i++)
+                                 if ((st->l3.state == datastatelist_1tr6t[i].state) &&
+                                     (mt == datastatelist_1tr6t[i].primitive))
+                                         break;
+                         if (i == datasl_1tr6t_len) {
+                                 if (DEBUG_1TR6 > 0) {
+                                         printk(KERN_INFO "isdnl3up unhandled 1tr6 state %d MT %s\n"
+                                                ,st->l3.state, mt_trans(PROTO_DIS_N1, mt));
+                                 }
+                                 BufPoolRelease(ibh);
+                         } else
+                                 datastatelist_1tr6t[i].rout(st, pr, ibh);
+                         break;
+#endif
+                 default:      /* E-DSS1 */
+                         for (i = 0; i < datasllen; i++)
+                                 if ((st->l3.state == datastatelist[i].state) &&
+                                     (mt == datastatelist[i].primitive))
+                                         break;
+                         if (i == datasllen) {
+                                 BufPoolRelease(ibh);
+                         } else
+                                 datastatelist[i].rout(st, pr, ibh);
+               }
+       }
+}
+
+static void
+l3down(struct PStack *st,
+       int pr, void *arg)
+{
+       int             i;
+       struct BufHeader *ibh = arg;
+
+       switch (st->protocol) {
+#ifdef P_1TR6
+         case ISDN_PTYPE_1TR6:
+                 for (i = 0; i < downsl_1tr6t_len; i++)
+                         if ((st->l3.state == downstatelist_1tr6t[i].state) &&
+                             (pr == downstatelist_1tr6t[i].primitive))
+                                 break;
+                 if (i == downsl_1tr6t_len) {
+                         if (DEBUG_1TR6 > 0) {
+                                 printk(KERN_INFO "isdnl3down unhandled 1tr6 state %d primitiv %x\n", st->l3.state, pr);
+                         }
+                 } else
+                         downstatelist_1tr6t[i].rout(st, pr, ibh);
+                 break;
+#endif
+         default:
+                 for (i = 0; i < downsllen; i++)
+                         if ((st->l3.state == downstatelist[i].state) &&
+                             (pr == downstatelist[i].primitive))
+                                 break;
+                 if (i == downsllen) {
+                 } else
+                         downstatelist[i].rout(st, pr, ibh);
+       }
+}
+
+void
+setstack_isdnl3(struct PStack *st)
+{
+       st->l4.l4l3 = l3down;
+       st->l2.l2l3 = l3up;
+       st->l3.state = 0;
+       st->l3.callref = 0;
+       st->l3.debug = 0;
+}
diff --git a/drivers/isdn/teles/l3_1TR6.c b/drivers/isdn/teles/l3_1TR6.c
new file mode 100644 (file)
index 0000000..71b6df8
--- /dev/null
@@ -0,0 +1,502 @@
+#define DEBUG_1TR6 0
+
+char           *
+mt_trans(int pd, int mt)
+{
+       int             i;
+
+       if (pd == PROTO_DIS_N0) {
+               for (i = 0; i < (sizeof(mtdesc_n0) / sizeof(struct MTypeDesc)); i++) {
+                       if (mt == mtdesc_n0[i].mt)
+                               return (mtdesc_n0[i].descr);
+               }
+               return ("unkown Message Type PD=N0");
+       } else if (pd == PROTO_DIS_N1) {
+               for (i = 0; i < (sizeof(mtdesc_n1) / sizeof(struct MTypeDesc)); i++) {
+                       if (mt == mtdesc_n1[i].mt)
+                               return (mtdesc_n1[i].descr);
+               }
+               return ("unkown Message Type PD=N1");
+       }
+       return ("unkown Protokolldiscriminator");
+}
+
+static void
+l3_1TR6_message(struct PStack *st, int mt, int pd)
+{
+       struct BufHeader *dibh;
+       byte           *p;
+
+       BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 18);
+       p = DATAPTR(dibh);
+       p += st->l2.ihsize;
+
+       *p++ = pd;
+       *p++ = 0x1;
+       *p++ = st->l3.callref;
+       *p++ = mt;
+
+       dibh->datasize = p - DATAPTR(dibh);
+       i_down(st, dibh);
+}
+
+static void
+l3_1tr6_setup(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *dibh;
+       byte           *p;
+       char           *teln;
+
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: sent SETUP\n");
+#endif
+
+       st->l3.callref = st->pa->callref;
+       BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 19);
+       p = DATAPTR(dibh);
+       p += st->l2.ihsize;
+
+       *p++ = PROTO_DIS_N1;
+       *p++ = 0x1;
+       *p++ = st->l3.callref;
+       *p++ = MT_N1_SETUP;
+
+
+       if (st->pa->calling[0] != '\0') {
+               *p++ = WE0_origAddr;
+               *p++ = strlen(st->pa->calling) + 1;
+               /* Classify as AnyPref. */
+               *p++ = 0x81;    /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+               teln = st->pa->calling;
+               while (*teln)
+                       *p++ = *teln++ & 0x7f;
+       }
+       *p++ = WE0_destAddr;
+       *p++ = strlen(st->pa->called) + 1;
+       /* Classify as AnyPref. */
+       *p++ = 0x81;            /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+
+       teln = st->pa->called;
+       while (*teln)
+               *p++ = *teln++ & 0x7f;
+
+       *p++ = WE_Shift_F6;
+       /* Codesatz 6 fuer Service */
+       *p++ = WE6_serviceInd;
+       *p++ = 2;               /* len=2 info,info2 */
+       *p++ = st->pa->info;
+       *p++ = st->pa->info2;
+
+       dibh->datasize = p - DATAPTR(dibh);
+
+       newl3state(st, 1);
+       i_down(st, dibh);
+
+}
+
+
+static void
+l3_1tr6_tu_setup(struct PStack *st, byte pr, void *arg)
+{
+       byte           *p;
+       struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: TU_SETUP\n");
+#endif
+
+       p = DATAPTR(ibh);
+       p += st->l2.uihsize;
+       st->pa->callref = getcallref(p);
+       st->l3.callref = 0x80 + st->pa->callref;
+
+       /*
+         * Channel Identification
+         */
+       p = DATAPTR(ibh);
+       if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+                       WE0_chanID, 0))) {
+               st->pa->bchannel = p[2] & 0x3;
+       } else
+               printk(KERN_INFO "l3tu_setup: Channel ident not found\n");
+
+       p = DATAPTR(ibh);
+
+       if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, WE6_serviceInd, 6))) {
+               st->pa->info = p[2];
+               st->pa->info2 = p[3];
+       } else
+               printk(KERN_INFO "l3s12(1TR6): ServiceIndicator not found\n");
+
+       p = DATAPTR(ibh);
+       if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+                       WE0_destAddr, 0)))
+               iecpy(st->pa->called, p, 1);
+       else
+               strcpy(st->pa->called, "");
+
+       p = DATAPTR(ibh);
+       if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+                       WE0_origAddr, 0))) {
+               iecpy(st->pa->calling, p, 1);
+       } else
+               strcpy(st->pa->calling, "");
+
+       BufPoolRelease(ibh);
+
+       if (st->pa->info == 7) {
+               newl3state(st, 6);
+               st->l3.l3l4(st, CC_SETUP_IND, NULL);
+       }
+}
+
+static void
+l3_1tr6_tu_setup_ack(struct PStack *st, byte pr, void *arg)
+{
+       byte           *p;
+       struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: SETUP_ACK\n");
+#endif
+
+       p = DATAPTR(ibh);
+       if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+                       WE0_chanID, 0))) {
+               st->pa->bchannel = p[2] & 0x3;
+       } else
+               printk(KERN_INFO "octect 3 not found\n");
+
+       BufPoolRelease(ibh);
+       newl3state(st, 2);
+}
+
+static void
+l3_1tr6_tu_call_sent(struct PStack *st, byte pr, void *arg)
+{
+       byte           *p;
+       struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: CALL_SENT\n");
+#endif
+
+       p = DATAPTR(ibh);
+       if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+                       WE0_chanID, 0))) {
+               st->pa->bchannel = p[2] & 0x3;
+       } else
+               printk(KERN_INFO "octect 3 not found\n");
+
+       BufPoolRelease(ibh);
+       newl3state(st, 3);
+       st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
+}
+
+static void
+l3_1tr6_tu_alert(struct PStack *st, byte pr, void *arg)
+{
+       byte           *p;
+       struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: TU_ALERT\n");
+#endif
+
+       p = DATAPTR(ibh);
+       if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+                       WE6_statusCalled, 6))) {
+               if (DEBUG_1TR6 > 2)
+                       printk(KERN_INFO "status called %x\n", p[2]);
+       } else if (DEBUG_1TR6 > 0)
+               printk(KERN_INFO "statusCalled not found\n");
+
+       BufPoolRelease(ibh);
+       newl3state(st, 4);
+       st->l3.l3l4(st, CC_ALERTING_IND, NULL);
+}
+
+static void
+l3_1tr6_tu_info(struct PStack *st, byte pr, void *arg)
+{
+       byte           *p;
+       int             i;
+       char            a_charge[8];
+       struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: TU_INFO\n");
+#endif
+
+       p = DATAPTR(ibh);
+       if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+                       WE6_chargingInfo, 6))) {
+               iecpy(a_charge, p, 1);
+               st->pa->chargeinfo = 0;
+               for (i = 0; i < strlen(a_charge); i++) {
+                       st->pa->chargeinfo *= 10;
+                       st->pa->chargeinfo += a_charge[i] & 0xf;
+                       st->l3.l3l4(st, CC_INFO_CHARGE, NULL);
+               }
+               if (DEBUG_1TR6 > 2)
+                       printk(KERN_INFO "chargingInfo %d\n", st->pa->chargeinfo);
+       } else if (DEBUG_1TR6 > 2)
+               printk(KERN_INFO "chargingInfo not found\n");
+
+       BufPoolRelease(ibh);
+}
+
+static void
+l3_1tr6_tu_info_s2(struct PStack *st, byte pr, void *arg)
+{
+       byte           *p;
+       int             i;
+       struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: TU_INFO 2\n");
+#endif
+
+       if (DEBUG_1TR6 > 4) {
+               p = DATAPTR(ibh);
+               for (i = 0; i < ibh->datasize; i++) {
+                       printk(KERN_INFO "Info DATA %x\n", p[i]);
+               }
+       }
+       BufPoolRelease(ibh);
+}
+
+static void
+l3_1tr6_tu_connect(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: CONNECT\n");
+#endif
+
+       BufPoolRelease(ibh);
+       st->l3.l3l4(st, CC_SETUP_CNF, NULL);
+       newl3state(st, 10);
+}
+
+static void
+l3_1tr6_tu_rel(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: REL\n");
+#endif
+
+
+       BufPoolRelease(ibh);
+       l3_1TR6_message(st, MT_N1_REL_ACK, PROTO_DIS_N1);
+       st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+       newl3state(st, 0);
+}
+
+static void
+l3_1tr6_tu_rel_ack(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: REL_ACK\n");
+#endif
+
+
+       BufPoolRelease(ibh);
+       newl3state(st, 0);
+       st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
+}
+
+static void
+l3_1tr6_tu_disc(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *ibh = arg;
+       byte           *p;
+       int             i;
+       char            a_charge[8];
+
+
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: TU_DISC\n");
+#endif
+
+
+       p = DATAPTR(ibh);
+       if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+                       WE6_chargingInfo, 6))) {
+               iecpy(a_charge, p, 1);
+               st->pa->chargeinfo = 0;
+               for (i = 0; i < strlen(a_charge); i++) {
+                       st->pa->chargeinfo *= 10;
+                       st->pa->chargeinfo += a_charge[i] & 0xf;
+                       st->l3.l3l4(st, CC_INFO_CHARGE, NULL);
+               }
+               if (DEBUG_1TR6 > 2)
+                       printk(KERN_INFO "chargingInfo %d\n", st->pa->chargeinfo);
+       } else if (DEBUG_1TR6 > 2)
+               printk(KERN_INFO "chargingInfo not found\n");
+
+       p = DATAPTR(ibh);
+       if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+                       WE0_cause, 0))) {
+               if (p[1] > 0) {
+                       st->pa->cause = p[2];
+               } else {
+                       st->pa->cause = 0;
+               }
+               if (DEBUG_1TR6 > 1)
+                       printk(KERN_INFO "Cause %x\n", st->pa->cause);
+       } else if (DEBUG_1TR6 > 0)
+               printk(KERN_INFO "Cause not found\n");
+
+       BufPoolRelease(ibh);
+       newl3state(st, 12);
+       st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
+}
+
+
+static void
+l3_1tr6_tu_connect_ack(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: CONN_ACK\n");
+#endif
+
+
+       BufPoolRelease(ibh);
+       st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
+       newl3state(st, 10);
+}
+
+static void
+l3_1tr6_alert(struct PStack *st, byte pr,
+             void *arg)
+{
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: send ALERT\n");
+#endif
+
+       l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1);
+       newl3state(st, 7);
+}
+
+static void
+l3_1tr6_conn(struct PStack *st, byte pr,
+            void *arg)
+{
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: send CONNECT\n");
+#endif
+
+       st->l3.callref = 0x80 + st->pa->callref;
+       l3_1TR6_message(st, MT_N1_CONN, PROTO_DIS_N1);
+       newl3state(st, 8);
+}
+
+static void
+l3_1tr6_ignore(struct PStack *st, byte pr, void *arg)
+{
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: IGNORE\n");
+#endif
+
+       newl3state(st, 0);
+}
+
+static void
+l3_1tr6_disconn_req(struct PStack *st, byte pr, void *arg)
+{
+       struct BufHeader *dibh;
+       byte           *p;
+
+#if DEBUG_1TR6
+       printk(KERN_INFO "1tr6: send DISCON\n");
+#endif
+
+       BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
+       p = DATAPTR(dibh);
+       p += st->l2.ihsize;
+
+       *p++ = PROTO_DIS_N1;
+       *p++ = 0x1;
+       *p++ = st->l3.callref;
+       *p++ = MT_N1_DISC;
+
+       *p++ = WE0_cause;
+       *p++ = 0x0;             /* Laenge = 0 normales Ausloesen */
+
+       dibh->datasize = p - DATAPTR(dibh);
+
+       i_down(st, dibh);
+
+       newl3state(st, 11);
+}
+
+static void
+l3_1tr6_rel_req(struct PStack *st, byte pr, void *arg)
+{
+       l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1);
+       newl3state(st, 19);
+}
+
+
+
+static struct stateentry downstatelist_1tr6t[] =
+{
+       {0, CC_SETUP_REQ, l3_1tr6_setup},
+       {6, CC_REJECT_REQ, l3_1tr6_ignore},
+       {6, CC_SETUP_RSP, l3_1tr6_conn},
+       {6, CC_ALERTING_REQ, l3_1tr6_alert},
+       {7, CC_SETUP_RSP, l3_1tr6_conn},
+       {7, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+       {8, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+       {10, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+       {12, CC_RELEASE_REQ, l3_1tr6_rel_req}
+};
+
+static int      downsl_1tr6t_len = sizeof(downstatelist_1tr6t) /
+sizeof(struct stateentry);
+
+static struct stateentry datastatelist_1tr6t[] =
+{
+       {0, MT_N1_SETUP, l3_1tr6_tu_setup},
+       {0, MT_N1_REL, l3_1tr6_tu_rel},
+       {1, MT_N1_SETUP_ACK, l3_1tr6_tu_setup_ack},
+       {1, MT_N1_CALL_SENT, l3_1tr6_tu_call_sent},
+       {1, MT_N1_REL, l3_1tr6_tu_rel},
+       {1, MT_N1_DISC, l3_1tr6_tu_disc},
+       {2, MT_N1_CALL_SENT, l3_1tr6_tu_call_sent},
+       {2, MT_N1_ALERT, l3_1tr6_tu_alert},
+       {2, MT_N1_CONN, l3_1tr6_tu_connect},
+       {2, MT_N1_REL, l3_1tr6_tu_rel},
+       {2, MT_N1_DISC, l3_1tr6_tu_disc},
+       {2, MT_N1_INFO, l3_1tr6_tu_info_s2},
+       {3, MT_N1_ALERT, l3_1tr6_tu_alert},
+       {3, MT_N1_CONN, l3_1tr6_tu_connect},
+       {3, MT_N1_REL, l3_1tr6_tu_rel},
+       {3, MT_N1_DISC, l3_1tr6_tu_disc},
+       {4, MT_N1_ALERT, l3_1tr6_tu_alert},
+       {4, MT_N1_CONN, l3_1tr6_tu_connect},
+       {4, MT_N1_REL, l3_1tr6_tu_rel},
+       {4, MT_N1_DISC, l3_1tr6_tu_disc},
+       {7, MT_N1_REL, l3_1tr6_tu_rel},
+       {7, MT_N1_DISC, l3_1tr6_tu_disc},
+       {8, MT_N1_REL, l3_1tr6_tu_rel},
+       {8, MT_N1_DISC, l3_1tr6_tu_disc},
+       {8, MT_N1_CONN_ACK, l3_1tr6_tu_connect_ack},
+       {10, MT_N1_REL, l3_1tr6_tu_rel},
+       {10, MT_N1_DISC, l3_1tr6_tu_disc},
+       {10, MT_N1_INFO, l3_1tr6_tu_info},
+       {11, MT_N1_REL, l3_1tr6_tu_rel},
+       {12, MT_N1_REL, l3_1tr6_tu_rel},
+       {19, MT_N1_REL_ACK, l3_1tr6_tu_rel_ack}
+};
+
+static int      datasl_1tr6t_len = sizeof(datastatelist_1tr6t) /
+sizeof(struct stateentry);
diff --git a/drivers/isdn/teles/l3_1TR6.h b/drivers/isdn/teles/l3_1TR6.h
new file mode 100644 (file)
index 0000000..7cca599
--- /dev/null
@@ -0,0 +1,202 @@
+#ifndef l3_1TR6
+#define l3_1TR6
+
+#define PROTO_DIS_N0 0x40
+#define PROTO_DIS_N1 0x41
+
+/*
+ * MsgType N0
+ */
+#define MT_N0_REG_IND 61
+#define MT_N0_CANC_IND 62
+#define MT_N0_FAC_STA 63
+#define MT_N0_STA_ACK 64
+#define MT_N0_STA_REJ 65
+#define MT_N0_FAC_INF 66
+#define MT_N0_INF_ACK 67
+#define MT_N0_INF_REJ 68
+#define MT_N0_CLOSE 75
+#define MT_N0_CLO_ACK 77
+
+
+/*
+ * MsgType N1
+ */
+
+#define MT_N1_ESC 0x00
+#define MT_N1_ALERT 0x01
+#define MT_N1_CALL_SENT 0x02
+#define MT_N1_CONN 0x07
+#define MT_N1_CONN_ACK 0x0F
+#define MT_N1_SETUP 0x05
+#define MT_N1_SETUP_ACK 0x0D
+#define MT_N1_RES 0x26
+#define MT_N1_RES_ACK 0x2E
+#define MT_N1_RES_REJ 0x22
+#define MT_N1_SUSP 0x25
+#define MT_N1_SUSP_ACK 0x2D
+#define MT_N1_SUSP_REJ 0x21
+#define MT_N1_USER_INFO 0x20
+#define MT_N1_DET 0x40
+#define MT_N1_DISC 0x45
+#define MT_N1_REL 0x4D
+#define MT_N1_REL_ACK 0x5A
+#define MT_N1_CANC_ACK 0x6E
+#define MT_N1_CANC_REJ 0x67
+#define MT_N1_CON_CON 0x69
+#define MT_N1_FAC 0x60
+#define MT_N1_FAC_ACK 0x68
+#define MT_N1_FAC_CAN 0x66
+#define MT_N1_FAC_REG 0x64
+#define MT_N1_FAC_REJ 0x65
+#define MT_N1_INFO 0x6D
+#define MT_N1_REG_ACK 0x6C
+#define MT_N1_REG_REJ 0x6F
+#define MT_N1_STAT 0x63
+
+
+struct MTypeDesc {
+       byte            mt;
+       char           *descr;
+};
+
+static struct MTypeDesc mtdesc_n0[] =
+{
+       {MT_N0_REG_IND, "MT_N0_REG_IND"},
+       {MT_N0_CANC_IND, "MT_N0_CANC_IND"},
+       {MT_N0_FAC_STA, "MT_N0_FAC_STA"},
+       {MT_N0_STA_ACK, "MT_N0_STA_ACK"},
+       {MT_N0_STA_REJ, "MT_N0_STA_REJ"},
+       {MT_N0_FAC_INF, "MT_N0_FAC_INF"},
+       {MT_N0_INF_ACK, "MT_N0_INF_ACK"},
+       {MT_N0_INF_REJ, "MT_N0_INF_REJ"},
+       {MT_N0_CLOSE, "MT_N0_CLOSE"},
+       {MT_N0_CLO_ACK, "MT_N0_CLO_ACK"}
+};
+
+static struct MTypeDesc mtdesc_n1[] =
+{
+       {MT_N1_ESC, "MT_N1_ESC"},
+       {MT_N1_ALERT, "MT_N1_ALERT"},
+       {MT_N1_CALL_SENT, "MT_N1_CALL_SENT"},
+       {MT_N1_CONN, "MT_N1_CONN"},
+       {MT_N1_CONN_ACK, "MT_N1_CONN_ACK"},
+       {MT_N1_SETUP, "MT_N1_SETUP"},
+       {MT_N1_SETUP_ACK, "MT_N1_SETUP_ACK"},
+       {MT_N1_RES, "MT_N1_RES"},
+       {MT_N1_RES_ACK, "MT_N1_RES_ACK"},
+       {MT_N1_RES_REJ, "MT_N1_RES_REJ"},
+       {MT_N1_SUSP, "MT_N1_SUSP"},
+       {MT_N1_SUSP_ACK, "MT_N1_SUSP_ACK"},
+       {MT_N1_SUSP_REJ, "MT_N1_SUSP_REJ"},
+       {MT_N1_USER_INFO, "MT_N1_USER_INFO"},
+       {MT_N1_DET, "MT_N1_DET"},
+       {MT_N1_DISC, "MT_N1_DISC"},
+       {MT_N1_REL, "MT_N1_REL"},
+       {MT_N1_REL_ACK, "MT_N1_REL_ACK"},
+       {MT_N1_CANC_ACK, "MT_N1_CANC_ACK"},
+       {MT_N1_CANC_REJ, "MT_N1_CANC_REJ"},
+       {MT_N1_CON_CON, "MT_N1_CON_CON"},
+       {MT_N1_FAC, "MT_N1_FAC"},
+       {MT_N1_FAC_ACK, "MT_N1_FAC_ACK"},
+       {MT_N1_FAC_CAN, "MT_N1_FAC_CAN"},
+       {MT_N1_FAC_REG, "MT_N1_FAC_REG"},
+       {MT_N1_FAC_REJ, "MT_N1_FAC_REJ"},
+       {MT_N1_INFO, "MT_N1_INFO"},
+       {MT_N1_REG_ACK, "MT_N1_REG_ACK"},
+       {MT_N1_REG_REJ, "MT_N1_REG_REJ"},
+       {MT_N1_STAT, "MT_N1_STAT"}
+};
+
+
+/*
+ * W Elemente
+ */
+
+#define WE_Shift_F0 0x90
+#define WE_Shift_F6 0x96
+#define WE_Shift_OF0 0x98
+#define WE_Shift_OF6 0x9E
+
+#define WE0_cause 0x08
+#define WE0_connAddr 0x0C
+#define WE0_callID 0x10
+#define WE0_chanID 0x18
+#define WE0_netSpecFac 0x20
+#define WE0_display 0x28
+#define WE0_keypad 0x2C
+#define WE0_origAddr 0x6C
+#define WE0_destAddr 0x70
+#define WE0_userInfo 0x7E
+
+#define WE0_moreData 0xA0
+#define WE0_congestLevel 0xB0
+
+#define WE6_serviceInd 0x01
+#define WE6_chargingInfo 0x02
+#define WE6_date 0x03
+#define WE6_facSelect 0x05
+#define WE6_facStatus 0x06
+#define WE6_statusCalled 0x07
+#define WE6_addTransAttr 0x08
+
+/*
+ * FacCodes
+ */
+#define FAC_Sperre 0x01
+#define FAC_Sperre_All 0x02
+#define FAC_Sperre_Fern 0x03
+#define FAC_Sperre_Intl 0x04
+#define FAC_Sperre_Interk 0x05
+
+#define FAC_Forward1 0x02
+#define FAC_Forward2 0x03
+#define FAC_Konferenz 0x06
+#define FAC_GrabBchan 0x0F
+#define FAC_Reactivate 0x10
+#define FAC_Konferenz3 0x11
+#define FAC_Dienstwechsel1 0x12
+#define FAC_Dienstwechsel2 0x13
+#define FAC_NummernIdent 0x14
+#define FAC_GBG 0x15
+#define FAC_DisplayUebergeben 0x17
+#define FAC_DisplayUmgeleitet 0x1A
+#define FAC_Unterdruecke 0x1B
+#define FAC_Deactivate 0x1E
+#define FAC_Activate 0x1D
+#define FAC_SVC 0x1F
+#define FAC_Rueckwechsel 0x23
+#define FAC_Umleitung 0x24
+
+/*
+ * Cause codes
+ */
+#define CAUSE_InvCRef 0x01
+#define CAUSE_BearerNotImpl 0x03
+#define CAUSE_CIDunknown 0x07
+#define CAUSE_CIDinUse 0x08
+#define CAUSE_NoChans 0x0A
+#define CAUSE_FacNotImpl 0x10
+#define CAUSE_FacNotSubscr 0x11
+#define CAUSE_OutgoingBarred 0x20
+#define CAUSE_UserAssessBusy 0x21
+#define CAUSE_NegativeGBG 0x22
+#define CAUSE_UnknownGBG 0x23
+#define CAUSE_NoSPVknown 0x25
+#define CAUSE_DestNotObtain 0x35
+#define CAUSE_NumberChanged 0x38
+#define CAUSE_OutOfOrder 0x39
+#define CAUSE_NoUserResponse 0x3A
+#define CAUSE_UserBusy 0x3B
+#define CAUSE_IncomingBarred 0x3D
+#define CAUSE_CallRejected 0x3E
+#define CAUSE_NetworkCongestion 0x59
+#define CAUSE_RemoteUser 0x5A
+#define CAUSE_LocalProcErr 0x70
+#define CAUSE_RemoteProcErr 0x71
+#define CAUSE_RemoteUserSuspend 0x72
+#define CAUSE_RemoteUserResumed 0x73
+#define CAUSE_UserInfoDiscarded 0x7F
+
+
+#endif
diff --git a/drivers/isdn/teles/llglue.c b/drivers/isdn/teles/llglue.c
new file mode 100644 (file)
index 0000000..f9fe2dc
--- /dev/null
@@ -0,0 +1,124 @@
+#define __NO_VERSION__
+#include "teles.h"
+#include <linux/malloc.h>
+#include <linux/timer.h>
+
+
+extern struct Channel *chanlist;
+int             drid;
+char            *teles_id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+isdn_if         iif;
+
+#define TELES_STATUS_BUFSIZE 4096
+static byte    *teles_status_buf = NULL;
+static byte    *teles_status_read = NULL;
+static byte    *teles_status_write = NULL;
+static byte    *teles_status_end = NULL;
+
+int
+teles_readstatus(byte * buf, int len, int user)
+{
+       int             count;
+       byte           *p;
+
+       for (p = buf, count = 0; count < len; p++, count++) {
+               if (user)
+                       put_fs_byte(*teles_status_read++, p);
+               else
+                       *p++ = *teles_status_read++;
+               if (teles_status_read > teles_status_end)
+                       teles_status_read = teles_status_buf;
+       }
+       return count;
+}
+
+void
+teles_putstatus(char *buf)
+{
+       long            flags;
+       int             len, count, i;
+       byte           *p;
+       isdn_ctrl       ic;
+
+       save_flags(flags);
+       cli();
+       count = 0;
+       len = strlen(buf);
+       for (p = buf, i = len; i > 0; i--, p++) {
+               *teles_status_write++ = *p;
+               if (teles_status_write > teles_status_end)
+                       teles_status_write = teles_status_buf;
+               count++;
+       }
+       restore_flags(flags);
+       if (count) {
+               ic.command = ISDN_STAT_STAVAIL;
+               ic.driver = drid;
+               ic.arg = count;
+               iif.statcallb(&ic);
+       }
+}
+
+
+int
+ll_init(void)
+{
+       isdn_ctrl       ic;
+
+       teles_status_buf = Smalloc(TELES_STATUS_BUFSIZE,
+                                  GFP_KERNEL, "teles_status_buf");
+       if (!teles_status_buf) {
+               printk(KERN_ERR "teles: Could not allocate status-buffer\n");
+               return (-EIO);
+       } else {
+               teles_status_read = teles_status_buf;
+               teles_status_write = teles_status_buf;
+               teles_status_end = teles_status_buf + TELES_STATUS_BUFSIZE - 1;
+       }
+
+       iif.channels = CallcNewChan();
+       iif.maxbufsize = BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS);
+       iif.features =
+           ISDN_FEATURE_L2_X75I |
+           ISDN_FEATURE_L2_HDLC |
+           ISDN_FEATURE_L3_TRANS |
+           ISDN_FEATURE_P_1TR6 |
+           ISDN_FEATURE_P_EURO;
+
+       iif.command = teles_command;
+       iif.writebuf = teles_writebuf;
+       iif.writecmd = NULL;
+       iif.readstat = teles_readstatus;
+       strncpy(iif.id, teles_id, sizeof(iif.id) - 1);
+
+       register_isdn(&iif);
+       drid = iif.channels;
+
+       ic.driver = drid;
+       ic.command = ISDN_STAT_RUN;
+       iif.statcallb(&ic);
+       return 0;
+}
+
+void
+ll_stop(void)
+{
+       isdn_ctrl       ic;
+
+       ic.command = ISDN_STAT_STOP;
+       ic.driver = drid;
+       iif.statcallb(&ic);
+
+       CallcFreeChan();
+}
+
+void
+ll_unload(void)
+{
+       isdn_ctrl       ic;
+
+       ic.command = ISDN_STAT_UNLOAD;
+       ic.driver = drid;
+       iif.statcallb(&ic);
+}
diff --git a/drivers/isdn/teles/mod.c b/drivers/isdn/teles/mod.c
new file mode 100644 (file)
index 0000000..7e4fd4e
--- /dev/null
@@ -0,0 +1,137 @@
+#include "teles.h"
+
+
+
+extern struct IsdnCard cards[];
+extern char   *teles_id;
+
+int             nrcards;
+
+typedef struct {
+       byte           *membase;
+       int             interrupt;
+       unsigned int    iobase;
+       unsigned int    protocol;
+} io_type;
+
+io_type         io[] =
+{
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+       {0, 0, 0, 0},
+};
+
+void
+teles_mod_dec_use_count(void)
+{
+       MOD_DEC_USE_COUNT;
+}
+
+void
+teles_mod_inc_use_count(void)
+{
+       MOD_INC_USE_COUNT;
+}
+
+#ifdef MODULE
+#define teles_init init_module
+#else
+void teles_setup(char *str, int *ints)
+{
+        int  i, j, argc;
+       static char sid[20];
+
+        argc = ints[0];
+        i = 0;
+        j = 1;
+        while (argc && (i<16)) {
+                if (argc) {
+                        io[i].iobase    = ints[j];
+                        j++; argc--;
+                }
+                if (argc) {
+                        io[i].interrupt = ints[j];
+                        j++; argc--;
+                }
+                if (argc) {
+                        io[i].membase   = (byte *)ints[j];
+                        j++; argc--;
+                }
+                if (argc) {
+                        io[i].protocol  = ints[j];
+                        j++; argc--;
+                }
+                i++;
+        }
+       if (strlen(str)) {
+               strcpy(sid,str);
+               teles_id = sid;
+       }
+}
+#endif
+
+int
+teles_init(void)
+{
+       int             i;
+
+       nrcards = 0;
+       for (i = 0; i < 16; i++) {
+               if (io[i].protocol) {
+                       cards[i].membase   = io[i].membase;
+                       cards[i].interrupt = io[i].interrupt;
+                       cards[i].iobase    = io[i].iobase;
+                       cards[i].protocol  = io[i].protocol;
+               }
+       }
+       for (i = 0; i < 16; i++)
+                if (cards[i].protocol)
+                        nrcards++;
+       printk(KERN_DEBUG "teles: Total %d card%s defined\n",
+               nrcards, (nrcards > 1) ? "s" : "");
+       if (teles_inithardware()) {
+                /* Install only, if at least one card found */
+                Isdnl2New();
+                TeiNew();
+                CallcNew();
+                ll_init();
+
+               /* No symbols to export, hide all symbols */
+               register_symtab(NULL);
+
+#ifdef MODULE
+                printk(KERN_NOTICE "Teles module installed\n");
+#endif
+                return (0);
+        } else
+                return -EIO;
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+
+       ll_stop();
+       TeiFree();
+       Isdnl2Free();
+       CallcFree();
+       teles_closehardware();
+       ll_unload();
+       printk(KERN_NOTICE "Teles module removed\n");
+
+}
+#endif
diff --git a/drivers/isdn/teles/q931.c b/drivers/isdn/teles/q931.c
new file mode 100644 (file)
index 0000000..c460f08
--- /dev/null
@@ -0,0 +1,869 @@
+/*
+ * q931.c               code to decode ITU Q.931 call control messages
+ * 
+ * Author               Jan den Ouden
+ * 
+ * Changelog
+ * 
+ * Pauline Middelink    general improvements
+ * 
+ * Beat Doebeli         cause texts, display information element
+ * 
+ */
+
+
+#define __NO_VERSION__
+#include "teles.h"
+
+byte           *
+findie(byte * p, int size, byte ie, int wanted_set)
+{
+       int             l, codeset, maincodeset;
+       byte           *pend = p + size;
+
+       /* skip protocol discriminator, callref and message type */
+       p++;
+       l = (*p++) & 0xf;
+       p += l;
+       p++;
+       codeset = 0;
+       maincodeset = 0;
+       /* while there are bytes left... */
+       while (p < pend) {
+               if ((*p & 0xf0) == 0x90) {
+                       codeset = *p & 0x07;
+                       if (!(*p & 0x08))
+                               maincodeset = codeset;
+               }
+               if (*p & 0x80)
+                       p++;
+               else {
+                       if (codeset == wanted_set) {
+                               if (*p == ie)
+                                       return (p);
+                               if (*p > ie)
+                                       return (NULL);
+                       }
+                       p++;
+                       l = *p++;
+                       p += l;
+                       codeset = maincodeset;
+               }
+       }
+       return (NULL);
+}
+
+void
+iecpy(byte * dest, byte * iestart, int ieoffset)
+{
+       byte           *p;
+       int             l;
+
+       p = iestart + ieoffset + 2;
+       l = iestart[1] - ieoffset;
+       while (l--)
+               *dest++ = *p++;
+       *dest++ = '\0';
+}
+
+int
+getcallref(byte * p)
+{
+       p++;                    /* prot discr */
+       p++;                    /* callref length */
+       return (*p);            /* assuming one-byte callref */
+}
+
+/*
+ * According to Table 4-2/Q.931
+ */
+static
+struct MessageType {
+       byte            nr;
+       char           *descr;
+} mtlist[] = {
+
+       {
+               0x1, "ALERTING"
+       },
+       {
+               0x2, "CALL PROCEEDING"
+       },
+       {
+               0x7, "CONNECT"
+       },
+       {
+               0xf, "CONNECT ACKNOWLEDGE"
+       },
+       {
+               0x3, "PROGRESS"
+       },
+       {
+               0x5, "SETUP"
+       },
+       {
+               0xd, "SETUP ACKNOWLEDGE"
+       },
+       {
+               0x26, "RESUME"
+       },
+       {
+               0x2e, "RESUME ACKNOWLEDGE"
+       },
+       {
+               0x22, "RESUME REJECT"
+       },
+       {
+               0x25, "SUSPEND"
+       },
+       {
+               0x2d, "SUSPEND ACKNOWLEDGE"
+       },
+       {
+               0x21, "SUSPEND REJECT"
+       },
+       {
+               0x20, "USER INFORMATION"
+       },
+       {
+               0x45, "DISCONNECT"
+       },
+       {
+               0x4d, "RELEASE"
+       },
+       {
+               0x5a, "RELEASE COMPLETE"
+       },
+       {
+               0x46, "RESTART"
+       },
+       {
+               0x4e, "RESTART ACKNOWLEDGE"
+       },
+       {
+               0x60, "SEGMENT"
+       },
+       {
+               0x79, "CONGESTION CONTROL"
+       },
+       {
+               0x7b, "INFORMATION"
+       },
+       {
+               0x62, "FACILITY"
+       },
+       {
+               0x6e, "NOTIFY"
+       },
+       {
+               0x7d, "STATUS"
+       },
+       {
+               0x75, "STATUS ENQUIRY"
+       }
+};
+
+#define MTSIZE sizeof(mtlist)/sizeof(struct MessageType)
+
+
+static
+int
+prbits(char *dest, byte b, int start, int len)
+{
+       char           *dp = dest;
+
+       b = b << (8 - start);
+       while (len--) {
+               if (b & 0x80)
+                       *dp++ = '1';
+               else
+                       *dp++ = '0';
+               b = b << 1;
+       }
+       return (dp - dest);
+}
+
+static
+byte           *
+skipext(byte * p)
+{
+       while (!(*p++ & 0x80));
+       return (p);
+}
+
+/*
+ * Cause Values According to Q.850
+ * edescr: English description
+ * ddescr: German description used by Swissnet II (Swiss Telecom
+ *         not yet written...
+ */
+
+static
+struct CauseValue {
+       byte            nr;
+       char           *edescr;
+       char           *ddescr;
+} cvlist[] = {
+
+       {
+               0x01, "Unallocated (unassigned) number", "Nummer nicht zugeteilt"
+       },
+       {
+               0x02, "No route to specified transit network", ""
+       },
+       {
+               0x03, "No route to destination", ""
+       },
+       {
+               0x04, "Send special information tone", ""
+       },
+       {
+               0x05, "Misdialled trunk prefix", ""
+       },
+       {
+               0x06, "Channel unacceptable", "Kanal nicht akzeptierbar"
+       },
+       {
+               0x07, "Channel awarded and being delivered in an established channel", ""
+       },
+       {
+               0x08, "Preemption", ""
+       },
+       {
+               0x09, "Preemption - circuit reserved for reuse", ""
+       },
+       {
+               0x10, "Normal call clearing", "Normale Ausloesung"
+       },
+       {
+               0x11, "User busy", "TNB besetzt"
+       },
+       {
+               0x12, "No user responding", ""
+       },
+       {
+               0x13, "No answer from user (user alerted)", ""
+       },
+       {
+               0x14, "Subscriber absent", ""
+       },
+       {
+               0x15, "Call rejected", ""
+       },
+       {
+               0x16, "Number changed", ""
+       },
+       {
+               0x1a, "non-selected user clearing", ""
+       },
+       {
+               0x1b, "Destination out of order", ""
+       },
+       {
+               0x1c, "Invalid number format (address incomplete)", ""
+       },
+       {
+               0x1d, "Facility rejected", ""
+       },
+       {
+               0x1e, "Response to Status enuiry", ""
+       },
+       {
+               0x1f, "Normal, unspecified", ""
+       },
+       {
+               0x22, "No circuit/channel available", ""
+       },
+       {
+               0x26, "Network out of order", ""
+       },
+       {
+               0x27, "Permanent frame mode connection out-of-service", ""
+       },
+       {
+               0x28, "Permanent frame mode connection operational", ""
+       },
+       {
+               0x29, "Temporary failure", ""
+       },
+       {
+               0x2a, "Switching equipment congestion", ""
+       },
+       {
+               0x2b, "Access information discarded", ""
+       },
+       {
+               0x2c, "Requested circuit/channel not available", ""
+       },
+       {
+               0x2e, "Precedence call blocked", ""
+       },
+       {
+               0x2f, "Resource unavailable, unspecified", ""
+       },
+       {
+               0x31, "Quality of service unavailable", ""
+       },
+       {
+               0x32, "Requested facility not subscribed", ""
+       },
+       {
+               0x35, "Outgoing calls barred within CUG", ""
+       },
+       {
+               0x37, "Incoming calls barred within CUG", ""
+       },
+       {
+               0x39, "Bearer capability not auhorized", ""
+       },
+       {
+               0x3a, "Bearer capability not presently available", ""
+       },
+       {
+               0x3e, "Inconsistency in designated outgoing access information and subscriber class ", " "
+       },
+       {
+               0x3f, "Service or option not available, unspecified", ""
+       },
+       {
+               0x41, "Bearer capability not implemented", ""
+       },
+       {
+               0x42, "Channel type not implemented", ""
+       },
+       {
+               0x43, "Requested facility not implemented", ""
+       },
+       {
+               0x44, "Only restricted digital information bearer capability is available", ""
+       },
+       {
+               0x4f, "Service or option not implemented", ""
+       },
+       {
+               0x51, "Invalid call reference value", ""
+       },
+       {
+               0x52, "Identified channel does not exist", ""
+       },
+       {
+               0x53, "A suspended call exists, but this call identity does not", ""
+       },
+       {
+               0x54, "Call identity in use", ""
+       },
+       {
+               0x55, "No call suspended", ""
+       },
+       {
+               0x56, "Call having the requested call identity has been cleared", ""
+       },
+       {
+               0x57, "User not member of CUG", ""
+       },
+       {
+               0x58, "Incompatible destination", ""
+       },
+       {
+               0x5a, "Non-existent CUG", ""
+       },
+       {
+               0x5b, "Invalid transit network selection", ""
+       },
+       {
+               0x5f, "Invalid message, unspecified", ""
+       },
+       {
+               0x60, "Mandatory information element is missing", ""
+       },
+       {
+               0x61, "Message type non-existent or not implemented", ""
+       },
+       {
+               0x62, "Message not compatible with call state or message type non-existent or not implemented ", " "
+       },
+       {
+               0x63, "Information element/parameter non-existent or not implemented", ""
+       },
+       {
+               0x64, "Invalid information element contents", ""
+       },
+       {
+               0x65, "Message not compatible with call state", ""
+       },
+       {
+               0x66, "Recovery on timer expiry", ""
+       },
+       {
+               0x67, "Parameter non-existent or not implemented - passed on", ""
+       },
+       {
+               0x6e, "Message with unrecognized parameter discarded", ""
+       },
+       {
+               0x6f, "Protocol error, unspecified", ""
+       },
+       {
+               0x7f, "Interworking, unspecified", ""
+       },
+};
+
+#define CVSIZE sizeof(cvlist)/sizeof(struct CauseValue)
+
+static
+int
+prcause(char *dest, byte * p)
+{
+       byte           *end;
+       char           *dp = dest;
+       int             i, cause;
+
+       end = p + p[1] + 1;
+       p += 2;
+       dp += sprintf(dp, "    coding ");
+       dp += prbits(dp, *p, 7, 2);
+       dp += sprintf(dp, " location ");
+       dp += prbits(dp, *p, 4, 4);
+       *dp++ = '\n';
+       p = skipext(p);
+
+       cause = 0x7f & *p++;
+
+       /* locate cause value */
+       for (i = 0; i < CVSIZE; i++)
+               if (cvlist[i].nr == cause)
+                       break;
+
+       /* display cause value if it exists */
+       if (i == CVSIZE)
+               dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+       else
+               dp += sprintf(dp, "  cause value %x : %s \n", cause, cvlist[i].edescr);
+
+
+#if 0
+       dp += sprintf(dp,"    cause value ");
+        dp += prbits(dp,*p++,7,7);
+        *dp++ = '\n';
+#endif
+       while (!0) {
+               if (p > end)
+                       break;
+               dp += sprintf(dp, "    diag attribute %d ", *p++ & 0x7f);
+               dp += sprintf(dp, " rej %d ", *p & 0x7f);
+               if (*p & 0x80) {
+                       *dp++ = '\n';
+                       break;
+               } else
+                       dp += sprintf(dp, " av %d\n", (*++p) & 0x7f);
+       }
+       return (dp - dest);
+
+}
+
+static
+int
+prchident(char *dest, byte * p)
+{
+       char           *dp = dest;
+
+       p += 2;
+       dp += sprintf(dp, "    octet 3 ");
+       dp += prbits(dp, *p, 8, 8);
+       *dp++ = '\n';
+       return (dp - dest);
+}
+static
+int
+prcalled(char *dest, byte * p)
+{
+       int             l;
+       char           *dp = dest;
+
+       p++;
+       l = *p++ - 1;
+       dp += sprintf(dp, "    octet 3 ");
+       dp += prbits(dp, *p++, 8, 8);
+       *dp++ = '\n';
+       dp += sprintf(dp, "    number digits ");
+       while (l--)
+               *dp++ = *p++;
+       *dp++ = '\n';
+       return (dp - dest);
+}
+static
+int
+prcalling(char *dest, byte * p)
+{
+       int             l;
+       char           *dp = dest;
+
+       p++;
+       l = *p++ - 1;
+       dp += sprintf(dp, "    octet 3 ");
+       dp += prbits(dp, *p, 8, 8);
+       *dp++ = '\n';
+       if (!(*p & 0x80)) {
+               dp += sprintf(dp, "    octet 3a ");
+               dp += prbits(dp, *++p, 8, 8);
+               *dp++ = '\n';
+               l--;
+       };
+
+       p++;
+
+       dp += sprintf(dp, "    number digits ");
+       while (l--)
+               *dp++ = *p++;
+       *dp++ = '\n';
+       return (dp - dest);
+}
+
+static
+int
+prbearer(char *dest, byte * p)
+{
+       char           *dp = dest, ch;
+
+       p += 2;
+       dp += sprintf(dp, "    octet 3  ");
+       dp += prbits(dp, *p++, 8, 8);
+       *dp++ = '\n';
+       dp += sprintf(dp, "    octet 4  ");
+       dp += prbits(dp, *p, 8, 8);
+       *dp++ = '\n';
+       if ((*p++ & 0x1f) == 0x18) {
+               dp += sprintf(dp, "    octet 4.1 ");
+               dp += prbits(dp, *p++, 8, 8);
+               *dp++ = '\n';
+       }
+       /* check for user information layer 1 */
+       if ((*p & 0x60) == 0x20) {
+               ch = ' ';
+               do {
+                       dp += sprintf(dp, "    octet 5%c ", ch);
+                       dp += prbits(dp, *p, 8, 8);
+                       *dp++ = '\n';
+                       if (ch == ' ')
+                               ch = 'a';
+                       else
+                               ch++;
+               }
+               while (!(*p++ & 0x80));
+       }
+       /* check for user information layer 2 */
+       if ((*p & 0x60) == 0x40) {
+               dp += sprintf(dp, "    octet 6  ");
+               dp += prbits(dp, *p++, 8, 8);
+               *dp++ = '\n';
+       }
+       /* check for user information layer 3 */
+       if ((*p & 0x60) == 0x60) {
+               dp += sprintf(dp, "    octet 7  ");
+               dp += prbits(dp, *p++, 8, 8);
+               *dp++ = '\n';
+       }
+       return (dp - dest);
+}
+
+static
+int
+general(char *dest, byte * p)
+{
+       char           *dp = dest;
+       char            ch = ' ';
+       int             l, octet = 3;
+
+       p++;
+       l = *p++;
+       /* Iterate over all octets in the information element */
+       while (l--) {
+               dp += sprintf(dp, "    octet %d%c ", octet, ch);
+               dp += prbits(dp, *p++, 8, 8);
+               *dp++ = '\n';
+
+               /* last octet in group? */
+               if (*p & 0x80) {
+                       octet++;
+                       ch = ' ';
+               } else if (ch == ' ')
+                       ch = 'a';
+
+               else
+                       ch++;
+       }
+       return (dp - dest);
+}
+
+static
+int
+display(char *dest, byte * p)
+{
+       char           *dp = dest;
+       char            ch = ' ';
+       int             l, octet = 3;
+
+       p++;
+       l = *p++;
+       /* Iterate over all octets in the * display-information element */
+       dp += sprintf(dp, "   \"");
+       while (l--) {
+               dp += sprintf(dp, "%c", *p++);
+
+               /* last octet in group? */
+               if (*p & 0x80) {
+                       octet++;
+                       ch = ' ';
+               } else if (ch == ' ')
+                       ch = 'a';
+
+               else
+                       ch++;
+       }
+       *dp++ = '\"';
+       *dp++ = '\n';
+       return (dp - dest);
+}
+
+int
+prfacility(char *dest, byte * p)
+{
+       char           *dp = dest;
+       int             l, l2;
+
+       p++;
+       l = *p++;
+       dp += sprintf(dp, "    octet 3 ");
+       dp += prbits(dp, *p++, 8, 8);
+       dp += sprintf(dp, "\n");
+       l -= 1;
+
+       while (l > 0) {
+               dp += sprintf(dp, "   octet 4 ");
+               dp += prbits(dp, *p++, 8, 8);
+               dp += sprintf(dp, "\n");
+               dp += sprintf(dp, "   octet 5 %d\n", l2 = *p++ & 0x7f);
+               l -= 2;
+               dp += sprintf(dp, "   contents ");
+               while (l2--) {
+                       dp += sprintf(dp, "%2x ", *p++);
+                       l--;
+               }
+               dp += sprintf(dp, "\n");
+       }
+
+       return (dp - dest);
+}
+
+static
+struct InformationElement {
+       byte            nr;
+       char           *descr;
+       int             (*f) (char *, byte *);
+} ielist[] = {
+
+       {
+               0x00, "Segmented message", general
+       },
+       {
+               0x04, "Bearer capability", prbearer
+       },
+       {
+               0x08, "Cause", prcause
+       },
+       {
+               0x10, "Call identity", general
+       },
+       {
+               0x14, "Call state", general
+       },
+       {
+               0x18, "Channel identification", prchident
+       },
+       {
+               0x1c, "Facility", prfacility
+       },
+       {
+               0x1e, "Progress indicator", general
+       },
+       {
+               0x20, "Network-specific facilities", general
+       },
+       {
+               0x27, "Notification indicator", general
+       },
+       {
+               0x28, "Display", display
+       },
+       {
+               0x29, "Date/Time", general
+       },
+       {
+               0x2c, "Keypad facility", general
+       },
+       {
+               0x34, "Signal", general
+       },
+       {
+               0x40, "Information rate", general
+       },
+       {
+               0x42, "End-to-end delay", general
+       },
+       {
+               0x43, "Transit delay selection and indication", general
+       },
+       {
+               0x44, "Packet layer binary parameters", general
+       },
+       {
+               0x45, "Packet layer window size", general
+       },
+       {
+               0x46, "Packet size", general
+       },
+       {
+               0x47, "Closed user group", general
+       },
+       {
+               0x4a, "Reverse charge indication", general
+       },
+       {
+               0x6c, "Calling party number", prcalling
+       },
+       {
+               0x6d, "Calling party subaddress", general
+       },
+       {
+               0x70, "Called party number", prcalled
+       },
+       {
+               0x71, "Called party subaddress", general
+       },
+       {
+               0x74, "Redirecting number", general
+       },
+       {
+               0x78, "Transit network selection", general
+       },
+       {
+               0x79, "Restart indicator", general
+       },
+       {
+               0x7c, "Low layer compatibility", general
+       },
+       {
+               0x7d, "High layer compatibility", general
+       },
+       {
+               0x7e, "User-user", general
+       },
+       {
+               0x7f, "Escape for extension", general
+       },
+};
+
+#define IESIZE sizeof(ielist)/sizeof(struct InformationElement)
+
+#ifdef FRITZDEBUG
+void
+hexdump(byte * buf, int len, char *comment)
+{
+       static char     dbuf[1024];
+       char           *p = dbuf;
+
+       p += sprintf(p, "%s: ", comment);
+       while (len) {
+               p += sprintf(p, "%02x ", *p++);
+               len--;
+       }
+       p += sprintf(p, "\n");
+
+       teles_putstatus(dbuf);
+}
+#endif
+
+void
+dlogframe(struct IsdnCardState *sp, byte * buf, int size, char *comment)
+{
+       byte           *bend = buf + size;
+       char           *dp;
+       int             i;
+
+       /* display header */
+       dp = sp->dlogspace;
+       dp += sprintf(dp, "%s\n", comment);
+
+       {
+               byte           *p = buf;
+
+               dp += sprintf(dp, "hex: ");
+               while (p < bend)
+                       dp += sprintf(dp, "%02x ", *p++);
+               dp += sprintf(dp, "\n");
+               teles_putstatus(sp->dlogspace);
+               dp = sp->dlogspace;
+       }
+       /* locate message type */
+       for (i = 0; i < MTSIZE; i++)
+               if (mtlist[i].nr == buf[3])
+                       break;
+
+       /* display message type iff it exists */
+       if (i == MTSIZE)
+               dp += sprintf(dp, "Unknown message type %x!\n", buf[3]);
+       else
+               dp += sprintf(dp, "call reference %d size %d message type %s\n",
+                             buf[2], size, mtlist[i].descr);
+
+       /* display each information element */
+       buf += 4;
+       while (buf < bend) {
+               /* Is it a single octet information element? */
+               if (*buf & 0x80) {
+                       switch ((*buf >> 4) & 7) {
+                         case 1:
+                                 dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
+                                 break;
+                         case 3:
+                                 dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
+                                 break;
+                         case 5:
+                                 dp += sprintf(dp, "  Repeat indicator %x\n", *buf & 0xf);
+                                 break;
+                         case 2:
+                                 if (*buf == 0xa0) {
+                                         dp += sprintf(dp, "  More data\n");
+                                         break;
+                                 }
+                                 if (*buf == 0xa1) {
+                                         dp += sprintf(dp, "  Sending complete\n");
+                                 }
+                                 break;
+                                 /* fall through */
+                         default:
+                                 dp += sprintf(dp, "  Reserved %x\n", *buf);
+                                 break;
+                       }
+                       buf++;
+                       continue;
+               }
+               /* No, locate it in the table */
+               for (i = 0; i < IESIZE; i++)
+                       if (*buf == ielist[i].nr)
+                               break;
+
+               /* When not found, give apropriate msg */
+               if (i != IESIZE) {
+                       dp += sprintf(dp, "  %s\n", ielist[i].descr);
+                       dp += ielist[i].f(dp, buf);
+               } else
+                       dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+
+               /* Skip to next element */
+               buf += buf[1] + 2;
+       }
+
+       dp += sprintf(dp, "\n");
+       teles_putstatus(sp->dlogspace);
+}
diff --git a/drivers/isdn/teles/tei.c b/drivers/isdn/teles/tei.c
new file mode 100644 (file)
index 0000000..d67d3ae
--- /dev/null
@@ -0,0 +1,240 @@
+#define __NO_VERSION__
+#include "teles.h"
+
+extern struct IsdnCard cards[];
+extern int      nrcards;
+
+static struct PStack *
+findces(struct PStack *st, int ces)
+{
+       struct PStack  *ptr = *(st->l1.stlistp);
+
+       while (ptr)
+               if (ptr->l2.ces == ces)
+                       return (ptr);
+               else
+                       ptr = ptr->next;
+       return (NULL);
+}
+
+static struct PStack *
+findtei(struct PStack *st, int tei)
+{
+       struct PStack  *ptr = *(st->l1.stlistp);
+
+       if (tei == 127)
+               return (NULL);
+
+       while (ptr)
+               if (ptr->l2.tei == tei)
+                       return (ptr);
+               else
+                       ptr = ptr->next;
+       return (NULL);
+}
+
+void 
+tei_handler(struct PStack *st,
+           byte pr, struct BufHeader *ibh)
+{
+       byte           *bp;
+       unsigned int    tces;
+       struct PStack  *otsp, *ptr;
+       unsigned int    data;
+
+       if (st->l2.debug)
+               printk(KERN_DEBUG "teihandler %d\n", pr);
+
+       switch (pr) {
+         case (MDL_ASSIGN):
+                 data = (unsigned int) ibh;
+                 BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 6);
+                 if (!ibh)
+                         return;
+                 bp = DATAPTR(ibh);
+                 bp += st->l2.uihsize;
+                 bp[0] = 0xf;
+                 bp[1] = data >> 8;
+                 bp[2] = data & 0xff;
+                 bp[3] = 0x1;
+                 bp[4] = 0xff;
+                 ibh->datasize = 8;
+                 st->l3.l3l2(st, DL_UNIT_DATA, ibh);
+                 break;
+         case (DL_UNIT_DATA):
+                 bp = DATAPTR(ibh);
+                 bp += 3;
+                 if (bp[0] != 0xf)
+                         break;
+                 switch (bp[3]) {
+                   case (2):
+                           tces = (bp[1] << 8) | bp[2];
+                           BufPoolRelease(ibh);
+                           if (st->l3.debug)
+                                   printk(KERN_DEBUG "tei identity assigned for %d=%d\n", tces,
+                                          bp[4] >> 1);
+                           if ((otsp = findces(st, tces)))
+                                   otsp->ma.teil2(otsp, MDL_ASSIGN,
+                                                  (void *)(bp[4] >> 1));
+                           break;
+                   case (4):
+                           if (st->l3.debug)
+                                   printk(KERN_DEBUG "checking identity for %d\n", bp[4] >> 1);
+                           if (bp[4] >> 1 == 0x7f) {
+                                   BufPoolRelease(ibh);
+                                   ptr = *(st->l1.stlistp);
+                                   while (ptr) {
+                                           if ((ptr->l2.tei & 0x7f) != 0x7f) {
+                                                   if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 7))
+                                                           break;
+                                                   bp = DATAPTR(ibh);
+                                                   bp += 3;
+                                                   bp[0] = 0xf;
+                                                   bp[1] = ptr->l2.ces >> 8;
+                                                   bp[2] = ptr->l2.ces & 0xff;
+                                                   bp[3] = 0x5;
+                                                   bp[4] = (ptr->l2.tei << 1) | 1;
+                                                   ibh->datasize = 8;
+                                                   st->l3.l3l2(st, DL_UNIT_DATA, ibh);
+                                           }
+                                           ptr = ptr->next;
+                                   }
+                           } else {
+                                   otsp = findtei(st, bp[4] >> 1);
+                                   BufPoolRelease(ibh);
+                                   if (!otsp)
+                                           break;
+                                   if (st->l3.debug)
+                                           printk(KERN_DEBUG "ces is %d\n", otsp->l2.ces);
+                                   if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 7))
+                                           break;
+                                   bp = DATAPTR(ibh);
+                                   bp += 3;
+                                   bp[0] = 0xf;
+                                   bp[1] = otsp->l2.ces >> 8;
+                                   bp[2] = otsp->l2.ces & 0xff;
+                                   bp[3] = 0x5;
+                                   bp[4] = (otsp->l2.tei << 1) | 1;
+                                   ibh->datasize = 8;
+                                   st->l3.l3l2(st, DL_UNIT_DATA, ibh);
+                           }
+                           break;
+                   default:
+                           BufPoolRelease(ibh);
+                           if (st->l3.debug)
+                                   printk(KERN_DEBUG "tei message unknown %d ai %d\n", bp[3], bp[4] >> 1);
+                 }
+                 break;
+         default:
+                 printk(KERN_WARNING "tei handler unknown primitive %d\n", pr);
+                 break;
+       }
+}
+
+unsigned int 
+randomces(void)
+{
+       int             x = jiffies & 0xffff;
+
+       return (x);
+}
+
+static void 
+tei_man(struct PStack *sp, int i, void *v)
+{
+       printk(KERN_DEBUG "tei_man\n");
+}
+
+static void 
+tei_l2tei(struct PStack *st, int pr, void *arg)
+{
+       struct IsdnCardState *sp = st->l1.hardware;
+
+       tei_handler(sp->teistack, pr, arg);
+}
+
+void 
+setstack_tei(struct PStack *st)
+{
+       st->l2.l2tei = tei_l2tei;
+}
+
+static void 
+init_tei(struct IsdnCardState *sp, int protocol)
+{
+       struct PStack  *st;
+       char            tmp[128];
+
+#define DIRTY_HACK_AGAINST_SIGSEGV
+
+       st = (struct PStack *) Smalloc(sizeof(struct PStack), GFP_KERNEL,
+                                      "struct PStack");
+
+#ifdef DIRTY_HACK_AGAINST_SIGSEGV
+       sp->teistack = st;                      /* struct is not initialized yet */
+       sp->teistack->protocol = protocol;      /* struct is not initialized yet */
+#endif                                         /* DIRTY_HACK_AGAINST_SIGSEGV    */
+
+
+       setstack_teles(st, sp);
+
+       st->l2.extended = !0;
+       st->l2.laptype = LAPD;
+       st->l2.window = 1;
+       st->l2.orig = !0;
+       st->protocol = protocol;
+
+/*
+ * the following is not necessary for tei mng. (broadcast only)
+ */
+
+       st->l2.t200 = 500;      /* 500 milliseconds */
+       st->l2.n200 = 4;        /* try 4 times */
+
+       st->l2.sap = 63;
+       st->l2.tei = 127;
+
+       sprintf(tmp, "Card %d tei ", sp->cardnr);
+       setstack_isdnl2(st, tmp);
+       st->l2.debug = 0;
+       st->l3.debug = 0;
+
+       st->ma.manl2(st, MDL_NOTEIPROC, NULL);
+
+       st->l2.l2l3 = (void *) tei_handler;
+       st->l1.l1man = tei_man;
+       st->l2.l2man = tei_man;
+       st->l4.l2writewakeup = NULL;
+       
+       teles_addlist(sp, st);
+       sp->teistack = st;
+}
+
+static void 
+release_tei(struct IsdnCardState *sp)
+{
+       struct PStack  *st = sp->teistack;
+
+       teles_rmlist(sp, st);
+       Sfree((void *) st);
+}
+
+void 
+TeiNew(void)
+{
+       int             i;
+
+       for (i = 0; i < nrcards; i++)
+               if (cards[i].sp)
+                       init_tei(cards[i].sp, cards[i].protocol);
+}
+
+void 
+TeiFree(void)
+{
+       int             i;
+
+       for (i = 0; i < nrcards; i++)
+               if (cards[i].sp)
+                       release_tei(cards[i].sp);
+}
diff --git a/drivers/isdn/teles/teles.h b/drivers/isdn/teles/teles.h
new file mode 100644 (file)
index 0000000..f6818cf
--- /dev/null
@@ -0,0 +1,475 @@
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+#include <linux/tty.h>
+
+#define PH_ACTIVATE   1
+#define PH_DATA       2
+#define PH_DEACTIVATE 3
+
+#define MDL_ASSIGN       4
+#define DL_UNIT_DATA     5
+#define SC_STARTUP       6
+#define CC_ESTABLISH     7
+#define DL_ESTABLISH     8
+#define DL_DATA          9
+#define CC_S_STATUS_ENQ  10
+
+#define CC_CONNECT       15
+#define CC_CONNECT_ACKNOWLEDGE 16
+#define CO_EOF                 17
+#define SC_DISCONNECT          18
+#define CO_DTMF                19
+#define DL_RELEASE             20
+
+#define CO_ALARM               22
+#define CC_REJECT              23
+
+#define CC_SETUP_REQ           24
+#define CC_SETUP_CNF           25
+#define CC_SETUP_IND           26
+#define CC_SETUP_RSP           27
+#define CC_SETUP_COMPLETE_IND  28
+
+#define CC_DISCONNECT_REQ      29
+#define CC_DISCONNECT_IND      30
+
+#define CC_RELEASE_CNF         31
+#define CC_RELEASE_IND         32
+#define CC_RELEASE_REQ         33
+
+#define CC_REJECT_REQ          34
+
+#define CC_PROCEEDING_IND      35
+
+#define CC_DLRL                36
+#define CC_DLEST               37
+
+#define CC_ALERTING_REQ        38
+#define CC_ALERTING_IND        39
+
+#define DL_STOP                40
+#define DL_START               41
+
+#define MDL_NOTEIPROC          46
+
+#define LC_ESTABLISH           47
+#define LC_RELEASE             48
+
+#define PH_REQUEST_PULL        49
+#define PH_PULL_ACK            50
+#define        PH_DATA_PULLED         51
+#define CC_INFO_CHARGE         52
+
+/*
+ * Message-Types 
+ */
+
+#define MT_ALERTING            0x01
+#define MT_CALL_PROCEEDING     0x02
+#define MT_CONNECT             0x07
+#define MT_CONNECT_ACKNOWLEDGE 0x0f
+#define MT_PROGRESS            0x03
+#define MT_SETUP               0x05
+#define MT_SETUP_ACKNOWLEDGE   0x0d
+#define MT_RESUME              0x26
+#define MT_RESUME_ACKNOWLEDGE  0x2e
+#define MT_RESUME_REJECT       0x22
+#define MT_SUSPEND             0x25
+#define MT_SUSPEND_ACKNOWLEDGE 0x2d
+#define MT_SUSPEND_REJECT      0x21
+#define MT_USER_INFORMATION    0x20
+#define MT_DISCONNECT          0x45
+#define MT_RELEASE             0x4d
+#define MT_RELEASE_COMPLETE    0x5a
+#define MT_RESTART             0x46
+#define MT_RESTART_ACKNOWLEDGE 0x4e
+#define MT_SEGMENT             0x60
+#define MT_CONGESTION_CONTROL  0x79
+#define MT_INFORMATION         0x7b
+#define MT_FACILITY            0x62
+#define MT_NOTIFY              0x6e
+#define MT_STATUS              0x7d
+#define MT_STATUS_ENQUIRY      0x75
+
+#define IE_CAUSE               0x08
+
+struct HscxIoctlArg {
+       int             channel;
+       int             mode;
+       int             transbufsize;
+};
+
+#ifdef __KERNEL__
+
+#undef DEBUG_MAGIC
+
+#define HSCX_SBUF_ORDER     1
+#define HSCX_SBUF_BPPS      2
+#define HSCX_SBUF_MAXPAGES  3
+
+#define HSCX_RBUF_ORDER     1
+#define HSCX_RBUF_BPPS      2
+#define HSCX_RBUF_MAXPAGES  3
+
+#define HSCX_SMALLBUF_ORDER     0
+#define HSCX_SMALLBUF_BPPS      40
+#define HSCX_SMALLBUF_MAXPAGES  1
+
+#define ISAC_SBUF_ORDER     0
+#define ISAC_SBUF_BPPS      16
+#define ISAC_SBUF_MAXPAGES  1
+
+#define ISAC_RBUF_ORDER     0
+#define ISAC_RBUF_BPPS      16
+#define ISAC_RBUF_MAXPAGES  1
+
+#define ISAC_SMALLBUF_ORDER     0
+#define ISAC_SMALLBUF_BPPS      40
+#define ISAC_SMALLBUF_MAXPAGES  1
+
+#define byte unsigned char
+
+#define MAX_WINDOW 8
+
+byte           *Smalloc(int size, int pr, char *why);
+void            Sfree(byte * ptr);
+
+/*
+ * Statemachine 
+ */
+struct Fsm {
+       int            *jumpmatrix;
+       int             state_count, event_count;
+       char          **strEvent, **strState;
+};
+
+struct FsmInst {
+       struct Fsm     *fsm;
+       int             state;
+       int             debug;
+       void           *userdata;
+       int             userint;
+       void            (*printdebug) (struct FsmInst *, char *);
+};
+
+struct FsmNode {
+       int             state, event;
+       void            (*routine) (struct FsmInst *, int, void *);
+};
+
+struct FsmTimer {
+       struct FsmInst *fi;
+       struct timer_list tl;
+       int             event;
+       void           *arg;
+};
+
+struct BufHeader {
+#ifdef DEBUG_MAGIC
+       int             magic;
+#endif
+       struct BufHeader *next;
+       struct BufPool *bp;
+       int             datasize;
+       byte            primitive, where;
+       void           *heldby;
+};
+
+struct Pages {
+       struct Pages   *next;
+};
+
+struct BufPool {
+#ifdef DEBUG_MAGIC
+       int             magic;
+#endif
+       struct BufHeader *freelist;
+       struct Pages   *pageslist;
+       int             pageorder;
+       int             pagescount;
+       int             bpps;
+       int             bufsize;
+       int             maxpages;
+};
+
+struct BufQueue {
+#ifdef DEBUG_MAGIC
+       int             magic;
+#endif
+       struct BufHeader *head, *tail;
+};
+
+struct Layer1 {
+       void           *hardware;
+       int             hscx;
+       struct BufPool *sbufpool, *rbufpool, *smallpool;
+       struct PStack **stlistp;
+       int             act_state;
+       void            (*l1l2) (struct PStack *, int, struct BufHeader *);
+        void            (*l1man) (struct PStack *, int, void *);
+       int             hscxmode, hscxchannel, requestpull;
+};
+
+struct Layer2 {
+       int             sap, tei, ces;
+       int             extended, laptype;
+       int             uihsize, ihsize;
+       int             vs, va, vr;
+       struct BufQueue i_queue;
+       int             window, orig;
+       int             rejexp;
+       int             debug;
+       struct BufHeader *windowar[MAX_WINDOW];
+       int             sow;
+       struct FsmInst  l2m;
+       void            (*l2l1) (struct PStack *, int, struct BufHeader *);
+        void            (*l2l1discardq) (struct PStack *, int, void *, int);
+        void            (*l2man) (struct PStack *, int, void *);
+       void            (*l2l3) (struct PStack *, int, void *);
+       void            (*l2tei) (struct PStack *, int, void *);
+       struct FsmTimer t200_timer, t203_timer;
+       int             t200, n200, t203;
+       int             rc, t200_running;
+       char            debug_id[32];
+};
+
+struct Layer3 {
+       void            (*l3l4) (struct PStack *, int, struct BufHeader *);
+        void            (*l3l2) (struct PStack *, int, void *);
+       int             state, callref;
+       int             debug;
+};
+
+struct Layer4 {
+       void            (*l4l3) (struct PStack *, int, void *);
+       void           *userdata;
+       void            (*l1writewakeup) (struct PStack *);
+       void            (*l2writewakeup) (struct PStack *);
+};
+
+struct Management {
+       void            (*manl1) (struct PStack *, int, void *);
+        void            (*manl2) (struct PStack *, int, void *);
+       void            (*teil2) (struct PStack *, int, void *);
+};
+
+struct Param {
+       int             cause;
+       int             bchannel;
+       int             callref;     /* TEI-Number                      */
+       int             itc;
+       int             info;        /* Service-Indicator               */
+       int             info2;       /* Service-Indicator, second octet */
+       char            calling[40]; /* Called Id                       */
+       char            called[40];  /* Caller Id                       */
+       int             chargeinfo;  /* Charge Info - only for 1tr6 in
+                                     * the moment 
+                                     */
+};
+
+struct PStack {
+       struct PStack  *next;
+       struct Layer1   l1;
+       struct Layer2   l2;
+       struct Layer3   l3;
+       struct Layer4   l4;
+       struct Management ma;
+       struct Param   *pa;
+       int             protocol;     /* EDSS1 or 1TR6 */
+};
+
+struct HscxState {
+       byte           *membase;
+       int             iobase;
+       int             inuse, init, active;
+       struct BufPool  sbufpool, rbufpool, smallpool;
+       struct IsdnCardState *sp;
+       int             hscx, mode;
+       int             transbufsize, receive;
+       struct BufHeader *rcvibh, *xmtibh;
+       int             rcvptr, sendptr;
+       struct PStack  *st;
+       struct tq_struct tqueue;
+       int             event;
+       struct BufQueue rq, sq;
+       int             releasebuf;
+#ifdef DEBUG_MAGIC
+       int             magic;  /* 301270 */
+#endif
+};
+
+struct IsdnCardState {
+#ifdef DEBUG_MAGIC
+       int             magic;
+#endif
+       byte           *membase;
+       int             iobase;
+       struct BufPool  sbufpool, rbufpool, smallpool;
+       struct PStack  *stlist;
+       struct BufHeader *xmtibh, *rcvibh;
+       int             rcvptr, sendptr;
+       int             event;
+       struct tq_struct tqueue;
+       int             ph_active;
+       struct BufQueue rq, sq;
+
+       int             cardnr, ph_state;
+       struct PStack  *teistack;
+       struct HscxState hs[2];
+
+       int             dlogflag;
+       char           *dlogspace;
+       int             debug;
+       int             releasebuf;
+};
+
+struct IsdnCard {
+       byte           *membase;
+       int             interrupt;
+       unsigned int    iobase;
+       int             protocol;       /* EDSS1 or 1TR6 */
+       struct IsdnCardState *sp;
+};
+
+#define DATAPTR(x) ((byte *)x+sizeof(struct BufHeader))
+
+#define LAPD 0
+#define LAPB 1
+
+void            BufPoolInit(struct BufPool *bp, int order, int bpps,
+                           int maxpages);
+int             BufPoolAdd(struct BufPool *bp, int priority);
+void            BufPoolFree(struct BufPool *bp);
+int             BufPoolGet(struct BufHeader **bh,
+             struct BufPool *bp, int priority, void *heldby, int where);
+void            BufPoolRelease(struct BufHeader *bh);
+void            BufQueueLink(struct BufQueue *bq,
+                            struct BufHeader *bh);
+int             BufQueueUnlink(struct BufHeader **bh, struct BufQueue *bq);
+void            BufQueueInit(struct BufQueue *bq);
+void            BufQueueRelease(struct BufQueue *bq);
+void            BufQueueDiscard(struct BufQueue *q, int pr, void *heldby,
+                               int releasetoo);
+int             BufQueueLength(struct BufQueue *bq);
+void            BufQueueLinkFront(struct BufQueue *bq,
+                                 struct BufHeader *bh);
+
+void            l2down(struct PStack *st,
+                      byte pr, struct BufHeader *ibh);
+void            l2up(struct PStack *st,
+                    byte pr, struct BufHeader *ibh);
+void            acceptph(struct PStack *st,
+                        struct BufHeader *ibh);
+void            setstack_isdnl2(struct PStack *st, char *debug_id);
+int             teles_inithardware(void);
+void            teles_closehardware(void);
+
+void            setstack_teles(struct PStack *st, struct IsdnCardState *sp);
+unsigned int    randomces(void);
+void            setstack_isdnl3(struct PStack *st);
+void            teles_addlist(struct IsdnCardState *sp,
+                             struct PStack *st);
+void            releasestack_isdnl2(struct PStack *st);
+void            teles_rmlist(struct IsdnCardState *sp,
+                            struct PStack *st);
+void            newcallref(struct PStack *st);
+
+int             ll_init(void);
+void            ll_stop(void), ll_unload(void);
+int             setstack_hscx(struct PStack *st, struct HscxState *hs);
+void            modehscx(struct HscxState *hs, int mode, int ichan);
+byte           *findie(byte * p, int size, byte ie, int wanted_set);
+int             getcallref(byte * p);
+
+void            FsmNew(struct Fsm *fsm,
+                      struct FsmNode *fnlist, int fncount);
+void            FsmFree(struct Fsm *fsm);
+int             FsmEvent(struct FsmInst *fi,
+                        int event, void *arg);
+void            FsmChangeState(struct FsmInst *fi,
+                              int newstate);
+void            FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
+int             FsmAddTimer(struct FsmTimer *ft,
+                         int millisec, int event, void *arg, int where);
+void            FsmDelTimer(struct FsmTimer *ft, int where);
+int             FsmTimerRunning(struct FsmTimer *ft);
+void            jiftime(char *s, long mark);
+
+void            CallcNew(void);
+void            CallcFree(void);
+int             CallcNewChan(void);
+void            CallcFreeChan(void);
+int             teles_command(isdn_ctrl * ic);
+int             teles_writebuf(int id, int chan, const u_char * buf, int count, int user);
+void            teles_putstatus(char *buf);
+void            teles_reportcard(int cardnr);
+int             ListLength(struct BufHeader *ibh);
+void            dlogframe(struct IsdnCardState *sp, byte * p, int size, char *comment);
+void            iecpy(byte * dest, byte * iestart, int ieoffset);
+void            setstack_transl2(struct PStack *st);
+void            releasestack_transl2(struct PStack *st);
+void            close_hscxstate(struct HscxState *);
+void            setstack_tei(struct PStack *st);
+
+struct LcFsm {
+       struct FsmInst  lcfi;
+       int             type;
+       struct Channel *ch;
+       void            (*lccall) (struct LcFsm *, int, void *);
+       struct PStack  *st;
+       int             l2_establish;
+       int             l2_start;
+       struct FsmTimer act_timer;
+       char            debug_id[32];
+};
+
+struct Channel {
+       struct PStack   ds, is;
+       struct IsdnCardState *sp;
+       int             hscx;
+       int             chan;
+       int             incoming;
+       struct FsmInst  fi;
+       struct LcFsm    lc_d, lc_b;
+       struct Param    para;
+       int             debug;
+#ifdef DEBUG_MAGIC
+       int             magic;  /* 301272 */
+#endif
+       int             l2_protocol, l2_active_protocol;
+       int             l2_primitive, l2_headersize;
+       int             data_open;
+       int             outcallref;
+       int             impair;
+};
+
+#define PART_SIZE(order,bpps) (( (PAGE_SIZE<<order) -\
+  sizeof(void *))/bpps)
+#define BUFFER_SIZE(order,bpps) (PART_SIZE(order,bpps)-\
+  sizeof(struct BufHeader))
+
+#endif
+
+void            Isdnl2New(void);
+void            Isdnl2Free(void);
+void            TeiNew(void);
+void            TeiFree(void);
+
+
+
+
index 5e7dce4fcf1807514d3dbd8ea53a08021d44661f..59dcbed055dcbac03d13ef1663f123d2bf041270 100644 (file)
@@ -710,7 +710,7 @@ init_module(void)
 {
        dev_3c509.base_addr = io;
        dev_3c509.irq       = irq;
-       if (!EISA_bus) {
+       if (!EISA_bus && !io) {
                printk("3c509: WARNING! Module load-time probing works reliably only for EISA-bus!\n");
        }
        if (register_netdev(&dev_3c509) != 0)
index aeb7cb4ace922fb2bb659534de1c02b241761684..367c0c902c4e9b3191f74ef1d3fb19a846e50ab3 100644 (file)
@@ -42,7 +42,7 @@ if [ "$CONFIG_NET_ISA" = "y" ]; then
     tristate 'AT1700 support' CONFIG_AT1700
     tristate 'EtherExpressPro support' CONFIG_EEXPRESS_PRO
     tristate 'EtherExpress support' CONFIG_EEXPRESS
-    bool 'NI5210 support' CONFIG_NI52
+    tristate 'NI5210 support' CONFIG_NI52
     bool 'NI6510 support' CONFIG_NI65
     tristate 'WaveLAN support' CONFIG_WAVELAN
     tristate 'ICL EtherTeam 16i/32 support' CONFIG_ETH16I
index 39bf6f521fc134d4993c906d7ef6e172b55a8444..81b16134b166e45cdb7bf33d2473557c91c15994 100644 (file)
@@ -19,6 +19,18 @@ CONFIG_8390_MODULE  :=
 CONFIG_SLHC_BUILTIN :=
 CONFIG_SLHC_MODULE  :=
 
+ifeq ($(CONFIG_ISDN),y)
+  ifeq ($(CONFIG_ISDN_PPP),y)
+    CONFIG_SLHC_BUILTIN = y
+  endif
+else
+  ifeq ($(CONFIG_ISDN),m)
+    ifeq ($(CONFIG_ISDN_PPP),y)
+      CONFIG_SLHC_MODULE = y
+    endif
+  endif
+endif
+
 ifeq ($(CONFIG_SEEQ8005),y)
 L_OBJS += seeq8005.o
 endif
@@ -292,6 +304,10 @@ endif
 
 ifeq ($(CONFIG_NI52),y)
 L_OBJS += ni52.o
+else
+  ifeq ($(CONFIG_NI52),m)
+  M_OBJS += ni52.o
+  endif
 endif
 
 ifeq ($(CONFIG_NI65),y)
index 6e999a432706d7e80b716127d6ff8bf45b4aeaf6..2615d1e95d9cb6bcbfbcb4ca37bff5b867d9a052 100644 (file)
@@ -173,9 +173,6 @@ ethif_probe(struct device *dev)
 #endif
 #ifdef CONFIG_NI52
        && ni52_probe(dev)
-#endif
-#ifdef CONFIG_NI65
-       && ni65_probe(dev)
 #endif
        && 1 ) {
        return 1;       /* -ENODEV or -EAGAIN would be more accurate. */
index 4a164cf374f73af5dfd110f3d92fbbffa637aca3..ca629d7fde696064cbc90ccc0ddc1f3605d83958 100644 (file)
@@ -357,7 +357,8 @@ int tunnel_init(struct device *dev)
        dev->mtu                = 1500-tunnel_hlen;     /* eth_mtu */
        dev->addr_len           = 0;            /* Is this only for ARP? */
        dev->tx_queue_len       = 2;            /* Small queue */
-                                                                                       it should all run through */
+
+       /* it should all run through */
        memset(dev->broadcast,0xFF, ETH_ALEN);
 
        /* New-style flags. */
index 13d123596b7eb27e89b403a9812f4dbf32ac2194..2148693ff920074b5fa4f4f08274d0129018a8f5 100644 (file)
@@ -4,10 +4,14 @@
  * This is an extension to the Linux operating system, and is covered by the
  * same Gnu Public License that covers that work.
  * 
- * Alphacode 0.62 (95/01/19) for Linux 1.1.82 (or later)
- * Copyrights (c) 1994,1995 by M.Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * Alphacode 0.80 (96/02/19) for Linux 1.3.66 (or later)
+ * Copyrights (c) 1994,1995,1996 by M.Hipp (Michael.Hipp@student.uni-tuebingen.de)
  *    [feel free to mail ....]
  *
+ * when using as module: (no autoprobing!)
+ *   compile with: gcc -D__KERNEL__ -DMODULE -O2 -c ni52.c
+ *   run with e.g: insmod ni52.o io=0x360 irq=9 memstart=0xd0000 memend=0xd4000
+ *
  * CAN YOU PLEASE REPORT ME YOUR PERFORMANCE EXPERIENCES !!.
  * 
  * If you find a bug, please report me:
@@ -18,7 +22,8 @@
  *   maybe the ni5210-card revision and the i82586 version
  *
  * autoprobe for: base_addr: 0x300,0x280,0x360,0x320,0x340
- *                mem_start: 0xc8000,0xd0000,0xd4000,0xd8000 (8K and 16K)
+ *                mem_start: 0xd0000,0xd2000,0xc8000,0xca000,0xd4000,0xd6000,
+ *                           0xd8000,0xcc000,0xce000,0xda000,0xdc000
  *
  * sources:
  *   skeleton.c from Donald Becker
  *   The internal sysbus seems to be slow. So we often lose packets because of
  *   overruns while receiving from a fast remote host. 
  *   This can slow down TCP connections. Maybe the newer ni5210 cards are better.
+ *   my experinece is, that if a machine sends with more then about 500-600K/s
+ *   the fifo/sysbus overflows.
  * 
  * IMPORTANT NOTE:
  *   On fast networks, it's a (very) good idea to have 16K shared memory. With
  *   8K, we can store only 4 receive frames, so it can (easily) happen that a remote 
  *   machine 'overruns' our system.
  *
- * Known i82586 bugs (I'm sure, there are many more!):
+ * Known i82586/card problems (I'm sure, there are many more!):
  *   Running the NOP-mode, the i82586 sometimes seems to forget to report
  *   every xmit-interrupt until we restart the CU.
  *   Another MAJOR bug is, that the RU sometimes seems to ignore the EL-Bit 
  *   usually used) RBD and begins to fill it. (Maybe, this happens only if 
  *   the last buffer from the previous RFD fits exact into the queue and
  *   the next RFD can't fetch an initial RBD. Anyone knows more? )
+ *
+ * results from ftp performance tests with Linux 1.2.5 
+ *   send and receive about 350-400 KByte/s (peak up to 460 kbytes/s)
+ *   sending in NOP-mode: peak performance up to 530K/s (but better don't run this mode)
  */
 
 /*
+ * 19.Feb.96: more Mcast changes, module support (MH)
+ *
  * 18.Nov.95: Mcast changes (AC).
  *
- * 19.Jan.95: verified (MH)
+ * 23.April.95: fixed(?) receiving problems by configuring a RFD more
+ *              than the number of RBD's. Can maybe cause other problems. 
+ * 18.April.95: Added MODULE support (MH)
+ * 17.April.95: MC related changes in init586() and set_multicast_list().
+ *              removed use of 'jiffies' in init586() (MH)
  *
  * 19.Sep.94: Added Multicast support (not tested yet) (MH)
  * 
  *
  * < 30.Sep.93: first versions 
  */
+
+static int debuglevel = 0; /* debug-printk 0: off 1: a few 2: more */
+static int automatic_resume = 0; /* experimental .. better should be zero */
+static int rfdadd = 0; /* rfdadd=1 may be better for 8K MEM cards */
+static int fifo=0x8;   /* don't change */
+
+/* #define REALLY_SLOW_IO */
+
+#include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
 
 #define ni_attn586()  {outb(0,dev->base_addr+NI52_ATTENTION);}
 #define ni_reset586() {outb(0,dev->base_addr+NI52_RESET);}
+#define ni_disint()   {outb(0,dev->base_addr+NI52_INTDIS);}
+#define ni_enaint()   {outb(0,dev->base_addr+NI52_INTENA);}
 
 #define make32(ptr16) (p->memtop + (short) (ptr16) )
 #define make24(ptr32) ((char *) (ptr32) - p->base)
@@ -118,7 +144,7 @@ sizeof(rfd) = 24; sizeof(rbd) = 12;
 sizeof(tbd) = 8; sizeof(transmit_cmd) = 16;
 sizeof(nop_cmd) = 8; 
 
-  * if you don't know the driver, better do not change this values: */
+  * if you don't know the driver, better do not change these values: */
 
 #define RECV_BUFF_SIZE 1524 /* slightly oversized */
 #define XMIT_BUFF_SIZE 1524 /* slightly oversized */
@@ -129,36 +155,38 @@ sizeof(nop_cmd) = 8;
 
 /**************************************************************************/
 
-#define DELAY(x) {int i=jiffies; \
-                  if(loops_per_sec == 1) \
-                     while(i+(x)>jiffies); \
-                  else \
-                     __delay((loops_per_sec>>5)*x); \
-                 }
-
-/* a much shorter delay: */
+/* different DELAYs */
+#define DELAY(x) __delay((loops_per_sec>>5)*(x)); 
 #define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); }
+#define DELAY_18(); { __delay( (loops_per_sec>>18)+1 ); }
 
 /* wait for command with timeout: */
 #define WAIT_4_SCB_CMD() { int i; \
-  for(i=0;i<1024;i++) { \
-    if(!p->scb->cmd) break; \
-    DELAY_16(); \
-    if(i == 1023) { \
-      printk("%s: scb_cmd timed out .. resetting i82586\n",dev->name); \
-      ni_reset586(); } } }
-
+  for(i=0;i<16384;i++) { \
+    if(!p->scb->cmd_cuc) break; \
+    DELAY_18(); \
+    if(i == 16383) { \
+      printk("%s: scb_cmd timed out: %04x,%04x .. disabeling i82586!!\n",dev->name,p->scb->cmd_cuc,p->scb->cus); \
+       if(!p->reseted) { p->reseted = 1; ni_reset586(); } } } }
+
+#define WAIT_4_SCB_CMD_RUC() { int i; \
+  for(i=0;i<16384;i++) { \
+    if(!p->scb->cmd_ruc) break; \
+    DELAY_18(); \
+    if(i == 16383) { \
+      printk("%s: scb_cmd (ruc) timed out: %04x,%04x .. disabeling i82586!!\n",dev->name,p->scb->cmd_ruc,p->scb->rus); \
+       if(!p->reseted) { p->reseted = 1; ni_reset586(); } } } }
+
+#define WAIT_4_STAT_COMPL(addr) { int i; \
+   for(i=0;i<32767;i++) { \
+     if((addr)->cmd_status & STAT_COMPL) break; \
+     DELAY_16(); DELAY_16(); } }
 
 #define NI52_TOTAL_SIZE 16
 #define NI52_ADDR0 0x02
 #define NI52_ADDR1 0x07
 #define NI52_ADDR2 0x01
 
-#ifndef HAVE_PORTRESERVE
-#define check_region(ioaddr, size)              0
-#define request_region(ioaddr, size,name)    do ; while (0)
-#endif
-
 static int     ni52_probe1(struct device *dev,int ioaddr);
 static void    ni52_interrupt(int irq,struct pt_regs *reg_ptr);
 static int     ni52_open(struct device *dev);
@@ -166,6 +194,9 @@ static int     ni52_close(struct device *dev);
 static int     ni52_send_packet(struct sk_buff *,struct device *);
 static struct  enet_statistics *ni52_get_stats(struct device *dev);
 static void    set_multicast_list(struct device *dev);
+#if 0
+static void    ni52_dump(struct device *,void *);
+#endif
 
 /* helper-functions */
 static int     init586(struct device *dev);
@@ -182,6 +213,7 @@ struct priv
   struct enet_statistics stats;
   unsigned long base;
   char *memtop;
+  int lock,reseted;
   volatile struct rfd_struct  *rfd_last,*rfd_top,*rfd_first;
   volatile struct scp_struct  *scp;  /* volatile is important */
   volatile struct iscp_struct *iscp; /* volatile is important */
@@ -198,36 +230,37 @@ struct priv
   volatile int    xmit_count,xmit_last;
 };
 
-
 /**********************************************
  * close device 
  */
-
 static int ni52_close(struct device *dev)
 {
   free_irq(dev->irq);
-  irq2dev_map[dev->irq] = 0;
+  irq2dev_map[dev->irq] = NULL;
 
   ni_reset586(); /* the hard way to stop the receiver */
 
   dev->start = 0;
   dev->tbusy = 0;
 
+  MOD_DEC_USE_COUNT;
+
   return 0;
 }
 
 /**********************************************
  * open device 
  */
-
 static int ni52_open(struct device *dev)
 {
+  ni_disint();
   alloc586(dev);
   init586(dev);  
   startrecv586(dev);
+  ni_enaint();
 
-  if(request_irq(dev->irq, &ni52_interrupt,0,"ni52")) 
-  {    
+  if(request_irq(dev->irq, &ni52_interrupt,0,"ni5210")) 
+  {
     ni_reset586();
     return -EAGAIN;
   }  
@@ -237,16 +270,18 @@ static int ni52_open(struct device *dev)
   dev->tbusy = 0;
   dev->start = 1;
 
+  MOD_INC_USE_COUNT;
+
   return 0; /* most done by init */
 }
 
 /**********************************************
  * Check to see if there's an 82586 out there. 
  */
-
 static int check586(struct device *dev,char *where,unsigned size)
 {
-  struct priv *p = (struct priv *) dev->priv;
+  struct priv pb;
+  struct priv *p = /* (struct priv *) dev->priv*/ &pb;
   char *iscp_addrs[2];
   int i;
 
@@ -254,7 +289,12 @@ static int check586(struct device *dev,char *where,unsigned size)
   p->memtop = where + size;
   p->scp = (struct scp_struct *)(p->base + SCP_DEFAULT_ADDRESS);
   memset((char *)p->scp,0, sizeof(struct scp_struct));
+  for(i=0;i<sizeof(struct scp_struct);i++) /* memory was writeable? */
+    if(((char *)p->scp)[i])
+      return 0;
   p->scp->sysbus = SYSBUSVAL;        /* 1 = 8Bit-Bus, 0 = 16 Bit */
+  if(p->scp->sysbus != SYSBUSVAL)
+    return 0;
   
   iscp_addrs[0] = where;
   iscp_addrs[1]= (char *) p->scp - sizeof(struct iscp_struct);
@@ -269,7 +309,7 @@ static int check586(struct device *dev,char *where,unsigned size)
 
     ni_reset586();
     ni_attn586();
-    DELAY(2);  /* wait a while... */
+    DELAY(1);  /* wait a while... */
 
     if(p->iscp->busy) /* i82586 clears 'busy' after successful init */
       return 0;
@@ -280,13 +320,12 @@ static int check586(struct device *dev,char *where,unsigned size)
 /******************************************************************
  * set iscp at the right place, called by ni52_probe1 and open586. 
  */
-
 void alloc586(struct device *dev)
 {
   struct priv *p =  (struct priv *) dev->priv; 
 
   ni_reset586();
-  DELAY(2);
+  DELAY(1);
 
   p->scp  = (struct scp_struct *)  (p->base + SCP_DEFAULT_ADDRESS);
   p->scb  = (struct scb_struct *)  (dev->mem_start);
@@ -303,21 +342,25 @@ void alloc586(struct device *dev)
   ni_reset586();
   ni_attn586();
 
-  DELAY(2); 
+  DELAY(1); 
 
   if(p->iscp->busy)
     printk("%s: Init-Problems (alloc).\n",dev->name);
 
+  p->reseted = 0;
+
   memset((char *)p->scb,0,sizeof(struct scb_struct));
 }
 
 /**********************************************
  * probe the ni5210-card
  */
-
 int ni52_probe(struct device *dev)
 {
-  int *port, ports[] = {0x300, 0x280, 0x360 , 0x320 , 0x340, 0};
+#ifndef MODULE
+  int *port;
+  static int ports[] = {0x300, 0x280, 0x360 , 0x320 , 0x340, 0};
+#endif
   int base_addr = dev->base_addr;
 
   if (base_addr > 0x1ff)               /* Check a single specified location. */
@@ -327,6 +370,9 @@ int ni52_probe(struct device *dev)
   else if (base_addr > 0)              /* Don't probe at all. */
     return ENXIO;
 
+#ifdef MODULE
+  printk("%s: no autoprobing allowed for modules.\n",dev->name);
+#else
   for (port = ports; *port; port++) {
     int ioaddr = *port;
     if (check_region(ioaddr, NI52_TOTAL_SIZE))
@@ -340,35 +386,57 @@ int ni52_probe(struct device *dev)
       return 0;
   }
 
+#ifdef FULL_IO_PROBE
+  for(dev->base_addr=0x200;dev->base_addr<0x400;dev->base_addr+=8)
+  {
+    int ioaddr = dev->base_addr;
+    if (check_region(ioaddr, NI52_TOTAL_SIZE))
+      continue;
+    if( !(inb(ioaddr+NI52_MAGIC1) == NI52_MAGICVAL1) || 
+        !(inb(ioaddr+NI52_MAGIC2) == NI52_MAGICVAL2))
+      continue;
+    if (ni52_probe1(dev, ioaddr) == 0)
+      return 0;    
+  }
+#endif
+
+#endif
+
   dev->base_addr = base_addr;
   return ENODEV;
 }
 
 static int ni52_probe1(struct device *dev,int ioaddr)
 {
-  long memaddrs[] = { 0xd0000,0xd2000,0xc8000,0xca000,0xd4000,0xd6000,0xd8000, 0 };
   int i,size;
 
   for(i=0;i<ETH_ALEN;i++)
     dev->dev_addr[i] = inb(dev->base_addr+i);
 
   if(dev->dev_addr[0] != NI52_ADDR0 || dev->dev_addr[1] != NI52_ADDR1
-                                    || dev->dev_addr[2] != NI52_ADDR2)
+     || dev->dev_addr[2] != NI52_ADDR2)
     return ENODEV;
 
-  printk("%s: Ni52 found at %#3lx, ",dev->name,dev->base_addr);
+  printk("%s: NI5210 found at %#3lx, ",dev->name,dev->base_addr);
 
-  request_region(ioaddr,NI52_TOTAL_SIZE,"ni52");
-
-  dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL); 
-                                  /* warning: we don't free it on errors */
-  if (dev->priv == NULL)
-     return -ENOMEM;
-  memset((char *) dev->priv,0,sizeof(struct priv));
+  request_region(ioaddr,NI52_TOTAL_SIZE,"ni5210");
 
   /* 
    * check (or search) IO-Memory, 8K and 16K
    */
+#ifdef MODULE
+  size = dev->mem_end - dev->mem_start;
+  if(size != 0x2000 && size != 0x4000)
+  {
+    printk("\n%s: Illegal memory size %d. Allowed is 0x2000 or 0x4000 bytes.\n",dev->name,size);
+    return ENODEV;
+  }
+  if(!check586(dev,(char *) dev->mem_start,size))
+  {
+    printk("?memcheck, Can't find memory at 0x%lx with size %d!\n",dev->mem_start,size);
+    return ENODEV;
+  }
+#else
   if(dev->mem_start != 0) /* no auto-mem-probe */
   {
     size = 0x4000; /* check for 16K mem */
@@ -382,7 +450,9 @@ static int ni52_probe1(struct device *dev,int ioaddr)
   }
   else  
   {
-    for(i=0;;i++)
+   static long memaddrs[] = { 0xc8000,0xca000,0xcc000,0xce000,0xd0000,0xd2000,
+                              0xd4000,0xd6000,0xd8000,0xda000,0xdc000, 0 };
+   for(i=0;;i++)
     {
       if(!memaddrs[i]) {
         printk("?memprobe, Can't find io-memory!\n");
@@ -398,7 +468,18 @@ static int ni52_probe1(struct device *dev,int ioaddr)
     }
   }
   dev->mem_end = dev->mem_start + size; /* set mem_end showed by 'ifconfig' */
+#endif
+
+  dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL); 
+  if(dev->priv == NULL)
+  {
+    printk("%s: Ooops .. can't allocate private driver memory.\n",dev->name);
+    return -ENOMEM;
+  }
+                                  /* warning: we don't free it on errors */
+  memset((char *) dev->priv,0,sizeof(struct priv));
   
+  ((struct priv *) (dev->priv))->memtop = (char *) dev->mem_start + size;
   ((struct priv *) (dev->priv))->base =  dev->mem_start + size - 0x01000000;
   alloc586(dev);
 
@@ -420,11 +501,13 @@ static int ni52_probe1(struct device *dev,int ioaddr)
       printk("?autoirq, Failed to detect IRQ line!\n"); 
       return 1;
     }
+    printk("IRQ %d (autodetected).\n",dev->irq);
+  }
+  else  {
+    if(dev->irq == 2)
+      dev->irq = 9;
+    printk("IRQ %d (assigned and not checked!).\n",dev->irq);
   }
-  else if(dev->irq == 2) 
-    dev->irq = 9;
-
-  printk("IRQ %d.\n",dev->irq);
 
   dev->open            = &ni52_open;
   dev->stop            = &ni52_close;
@@ -451,7 +534,6 @@ static int ni52_probe1(struct device *dev,int ioaddr)
 static int init586(struct device *dev)
 {
   void *ptr;
-  unsigned long s;
   int i,result=0;
   struct priv *p = (struct priv *) dev->priv;
   volatile struct configure_cmd_struct  *cfg_cmd;
@@ -469,7 +551,7 @@ static int init586(struct device *dev)
   cfg_cmd->cmd_link   = 0xffff;
 
   cfg_cmd->byte_cnt   = 0x0a; /* number of cfg bytes */
-  cfg_cmd->fifo       = 0x08; /* fifo-limit (8=tx:32/rx:64) */
+  cfg_cmd->fifo       = fifo; /* fifo-limit (8=tx:32/rx:64) */
   cfg_cmd->sav_bf     = 0x40; /* hold or discard bad recv frames (bit 7) */
   cfg_cmd->adr_len    = 0x2e; /* addr_len |!src_insert |pre-len |loopback */
   cfg_cmd->priority   = 0x00;
@@ -477,25 +559,31 @@ static int init586(struct device *dev)
   cfg_cmd->time_low   = 0x00;
   cfg_cmd->time_high  = 0xf2;
   cfg_cmd->promisc    = 0;
-  if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
+  if(dev->flags & IFF_ALLMULTI) {
+    int len = ((char *) p->iscp - (char *) ptr - 8) / 6;
+    if(num_addrs > len)  {
+      printk("%s: switching to promisc. mode\n",dev->name);
+      dev->flags|=IFF_PROMISC;
+    }
+  }
+  if(dev->flags&IFF_PROMISC)
   {
-       cfg_cmd->promisc=1;
-       dev->flags|=IFF_PROMISC;
+       cfg_cmd->promisc=1;
+       dev->flags|=IFF_PROMISC;
   }
   cfg_cmd->carr_coll  = 0x00;
  
   p->scb->cbl_offset = make16(cfg_cmd);
+  p->scb->cmd_ruc = 0;
 
-  p->scb->cmd = CUC_START; /* cmd.-unit start */
+  p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */
   ni_attn586();
-  s = jiffies; /* warning: only active with interrupts on !! */
-  while(!(cfg_cmd->cmd_status & STAT_COMPL)) 
-    if(jiffies-s > 30) break;
+
+  WAIT_4_STAT_COMPL(cfg_cmd);
 
   if((cfg_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_COMPL|STAT_OK))
   {
-    printk("%s (ni52): configure command failed: %x\n",dev->name,cfg_cmd->cmd_status);
+    printk("%s: configure command failed: %x\n",dev->name,cfg_cmd->cmd_status);
     return 1; 
   }
 
@@ -512,12 +600,10 @@ static int init586(struct device *dev)
 
   p->scb->cbl_offset = make16(ias_cmd);
 
-  p->scb->cmd = CUC_START; /* cmd.-unit start */
+  p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */
   ni_attn586();
 
-  s = jiffies;
-  while(!(ias_cmd->cmd_status & STAT_COMPL)) 
-    if(jiffies-s > 30) break;
+  WAIT_4_STAT_COMPL(ias_cmd);
 
   if((ias_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_OK|STAT_COMPL)) {
     printk("%s (ni52): individual address setup command failed: %04x\n",dev->name,ias_cmd->cmd_status);
@@ -535,28 +621,27 @@ static int init586(struct device *dev)
   tdr_cmd->status      = 0;
 
   p->scb->cbl_offset = make16(tdr_cmd);
-
-  p->scb->cmd = CUC_START; /* cmd.-unit start */
+  p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */
   ni_attn586();
 
-  s = jiffies; 
-  while(!(tdr_cmd->cmd_status & STAT_COMPL))
-    if(jiffies - s > 30) {
-      printk("%s: Problems while running the TDR.\n",dev->name);
-      result = 1;
-    }
+  WAIT_4_STAT_COMPL(tdr_cmd);
 
-  if(!result)
+  if(!(tdr_cmd->cmd_status & STAT_COMPL))
   {
-    DELAY(2); /* wait for result */
+    printk("%s: Problems while running the TDR.\n",dev->name);
+  }
+  else
+  {
+    DELAY_16(); /* wait for result */
     result = tdr_cmd->status;
 
-    p->scb->cmd = p->scb->status & STAT_MASK;
+    p->scb->cmd_cuc = p->scb->cus & STAT_MASK;
     ni_attn586(); /* ack the interrupts */
 
-    if(result & TDR_LNK_OK) ;
+    if(result & TDR_LNK_OK) 
+      ;
     else if(result & TDR_XCVR_PRB)
-      printk("%s: TDR: Transceiver problem!\n",dev->name);
+      printk("%s: TDR: Transceiver problem. Check the cable(s)!\n",dev->name);
     else if(result & TDR_ET_OPN)
       printk("%s: TDR: No correct termination %d clocks away.\n",dev->name,result & TDR_TIMEMASK);
     else if(result & TDR_ET_SRT) 
@@ -567,12 +652,30 @@ static int init586(struct device *dev)
     else
       printk("%s: TDR: Unknown status %04x\n",dev->name,result);
   }
-   /* 
-    * ack interrupts 
-    */
-  p->scb->cmd = p->scb->status & STAT_MASK;
-  ni_attn586();
+
+  /*
+   * Multicast setup
+   */
+  if(num_addrs && !(dev->flags & IFF_PROMISC) )
+  {
+    mc_cmd = (struct mcsetup_cmd_struct *) ptr;
+    mc_cmd->cmd_status = 0;
+    mc_cmd->cmd_cmd = CMD_MCSETUP | CMD_LAST;
+    mc_cmd->cmd_link = 0xffff;
+    mc_cmd->mc_cnt = num_addrs * 6;
+
+    for(i=0;i<num_addrs;i++,dmi=dmi->next)
+      memcpy((char *) mc_cmd->mc_list[i], dmi->dmi_addr,6);
+
+    p->scb->cbl_offset = make16(mc_cmd);
+    p->scb->cmd_cuc = CUC_START;
+    ni_attn586();
+
+    WAIT_4_STAT_COMPL(mc_cmd);
+
+    if( (mc_cmd->cmd_status & (STAT_COMPL|STAT_OK)) != (STAT_COMPL|STAT_OK) )
+      printk("%s: Can't apply multicast-address-list.\n",dev->name);
+  }
 
    /*
     * alloc nop/xmit-cmds
@@ -586,8 +689,6 @@ static int init586(struct device *dev)
     p->nop_cmds[i]->cmd_link   = make16((p->nop_cmds[i]));
     ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
   }
-  p->xmit_cmds[0] = (struct transmit_cmd_struct *)ptr; /* transmit cmd/buff 0 */
-  ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
 #else
   for(i=0;i<NUM_XMIT_BUFFS;i++)
   {
@@ -596,59 +697,18 @@ static int init586(struct device *dev)
     p->nop_cmds[i]->cmd_status = 0;
     p->nop_cmds[i]->cmd_link   = make16((p->nop_cmds[i]));
     ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
-    p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /*transmit cmd/buff 0*/
-    ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
   }
 #endif
 
   ptr = alloc_rfa(dev,(void *)ptr); /* init receive-frame-area */ 
 
-  /* 
-   * Multicast setup
-   */
-  
-  if(dev->mc_count)
-  { /* I don't understand this: do we really need memory after the init? */
-    int len = ((char *) p->iscp - (char *) ptr - 8) / 6;
-    if(len <= 0)
-    {
-      printk("%s: Ooooops, no memory for MC-Setup!\n",dev->name);
-    }
-    else
-    {
-      if(len < num_addrs)
-      {
-       /* BUG - should go ALLMULTI in this case */
-        num_addrs = len;
-        printk("%s: Sorry, can only apply %d MC-Address(es).\n",dev->name,num_addrs);
-      }
-      mc_cmd = (struct mcsetup_cmd_struct *) ptr;
-      mc_cmd->cmd_status = 0;
-      mc_cmd->cmd_cmd = CMD_MCSETUP | CMD_LAST;
-      mc_cmd->cmd_link = 0xffff;
-      mc_cmd->mc_cnt = num_addrs * 6;
-      for(i=0;i<num_addrs;i++)
-      {
-               memcpy((char *) mc_cmd->mc_list[i], dmi->dmi_addr,6);
-               dmi=dmi->next;
-      }
-      p->scb->cbl_offset = make16(mc_cmd);
-      p->scb->cmd = CUC_START;
-      ni_attn586();
-      s = jiffies;
-      while(!(mc_cmd->cmd_status & STAT_COMPL))
-        if(jiffies - s > 30)
-          break;
-      if(!(mc_cmd->cmd_status & STAT_COMPL))
-        printk("%s: Can't apply multicast-address-list.\n",dev->name);
-    }
-  }
-
   /*
    * alloc xmit-buffs / init xmit_cmds
    */
   for(i=0;i<NUM_XMIT_BUFFS;i++)
   {
+    p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /*transmit cmd/buff 0*/
+    ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
     p->xmit_cbuffs[i] = (char *)ptr; /* char-buffs */
     ptr = (char *) ptr + XMIT_BUFF_SIZE;
     p->xmit_buffs[i] = (struct tbd_struct *)ptr; /* TBD */
@@ -660,6 +720,7 @@ static int init586(struct device *dev)
     }   
     memset((char *)(p->xmit_cmds[i]) ,0, sizeof(struct transmit_cmd_struct));
     memset((char *)(p->xmit_buffs[i]),0, sizeof(struct tbd_struct));
+    p->xmit_cmds[i]->cmd_link = make16(p->nop_cmds[(i+1)%NUM_XMIT_BUFFS]);
     p->xmit_cmds[i]->cmd_status = STAT_COMPL;
     p->xmit_cmds[i]->cmd_cmd = CMD_XMIT | CMD_INT;
     p->xmit_cmds[i]->tbd_offset = make16((p->xmit_buffs[i]));
@@ -674,18 +735,27 @@ static int init586(struct device *dev)
 #endif
 
    /*
-    * 'start transmitter' (nop-loop)
+    * 'start transmitter'
     */
 #ifndef NO_NOPCOMMANDS
   p->scb->cbl_offset = make16(p->nop_cmds[0]);
-  p->scb->cmd = CUC_START;
+  p->scb->cmd_cuc = CUC_START;
   ni_attn586();
   WAIT_4_SCB_CMD();
 #else
-  p->xmit_cmds[0]->cmd_link = 0xffff;
-  p->xmit_cmds[0]->cmd_cmd  = CMD_XMIT | CMD_LAST | CMD_INT;
+  p->xmit_cmds[0]->cmd_link = make16(p->xmit_cmds[0]);
+  p->xmit_cmds[0]->cmd_cmd  = CMD_XMIT | CMD_SUSPEND | CMD_INT;
 #endif
 
+  /*
+   * ack. interrupts
+   */
+  p->scb->cmd_cuc = p->scb->cus & STAT_MASK;
+  ni_attn586();
+  DELAY_16();
+
+  ni_enaint();
+
   return 0;
 }
 
@@ -701,20 +771,22 @@ static void *alloc_rfa(struct device *dev,void *ptr)
   int i;
   struct priv *p = (struct priv *) dev->priv;
 
-  memset((char *) rfd,0,sizeof(struct rfd_struct)*p->num_recv_buffs);
+  memset((char *) rfd,0,sizeof(struct rfd_struct)*(p->num_recv_buffs+rfdadd));
   p->rfd_first = rfd;
 
-  for(i = 0; i < p->num_recv_buffs; i++)
-    rfd[i].next = make16(rfd + (i+1) % p->num_recv_buffs);
-  rfd[p->num_recv_buffs-1].last = RFD_SUSP;   /* RU suspend */
+  for(i = 0; i < (p->num_recv_buffs+rfdadd); i++) {
+    rfd[i].next = make16(rfd + (i+1) % (p->num_recv_buffs+rfdadd) );
+    rfd[i].rbd_offset = 0xffff;
+  }
+  rfd[p->num_recv_buffs-1+rfdadd].last = RFD_SUSP;   /* RU suspend */
 
-  ptr = (void *) (rfd + p->num_recv_buffs);
+  ptr = (void *) (rfd + (p->num_recv_buffs + rfdadd) );
 
   rbd = (struct rbd_struct *) ptr;
   ptr = (void *) (rbd + p->num_recv_buffs);
 
    /* clr descriptors */
-  memset((char *) rbd,0,sizeof(struct rbd_struct)*p->num_recv_buffs);
+  memset((char *) rbd,0,sizeof(struct rbd_struct)*(p->num_recv_buffs));
 
   for(i=0;i<p->num_recv_buffs;i++)
   {
@@ -725,7 +797,7 @@ static void *alloc_rfa(struct device *dev,void *ptr)
   }
 
   p->rfd_top  = p->rfd_first;
-  p->rfd_last = p->rfd_first + p->num_recv_buffs - 1;
+  p->rfd_last = p->rfd_first + (p->num_recv_buffs - 1 + rfdadd);
 
   p->scb->rfa_offset           = make16(p->rfd_first);
   p->rfd_first->rbd_offset     = make16(rbd);
@@ -742,54 +814,73 @@ static void ni52_interrupt(int irq,struct pt_regs *reg_ptr)
 {
   struct device *dev = (struct device *) irq2dev_map[irq];
   unsigned short stat;
+  int cnt=0;
   struct priv *p;
 
-  if (dev == NULL) {
-    printk ("ni52-interrupt: irq %d for unknown device.\n",(int) -(((struct pt_regs *)reg_ptr)->orig_eax+2));
+  if (!dev) {
+    printk ("ni5210-interrupt: irq %d for unknown device.\n",irq);
     return;
   }
   p = (struct priv *) dev->priv;
 
+  if(debuglevel > 1)
+    printk("I");
+
   dev->interrupt = 1;
 
-  while((stat=p->scb->status & STAT_MASK))
-  {
-    p->scb->cmd = stat;
-    ni_attn586(); /* ack inter. */
+  WAIT_4_SCB_CMD(); /* wait for last command  */
 
-   if(stat & STAT_CX)    /* command with I-bit set complete */
-      ni52_xmt_int(dev);
+  while((stat=p->scb->cus & STAT_MASK))
+  {
+    p->scb->cmd_cuc = stat;
+    ni_attn586();
 
     if(stat & STAT_FR)   /* received a frame */
       ni52_rcv_int(dev);
 
-#ifndef NO_NOPCOMMANDS
-    if(stat & STAT_CNA)  /* CU went 'not ready' */
-    {
-      if(dev->start)
-        printk("%s: oops! CU has left active state. stat: %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
-    }
-#endif
-
     if(stat & STAT_RNR) /* RU went 'not ready' */
     {
-      if(p->scb->status & RU_SUSPEND) /* special case: RU_SUSPEND */
+      printk("(R)");
+      if(p->scb->rus & RU_SUSPEND) /* special case: RU_SUSPEND */
       {
         WAIT_4_SCB_CMD();
-        p->scb->cmd = RUC_RESUME;
+        p->scb->cmd_ruc = RUC_RESUME;
         ni_attn586();
+        WAIT_4_SCB_CMD_RUC();
       }
       else
       {
-        printk("%s: Receiver-Unit went 'NOT READY': %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
+        printk("%s: Receiver-Unit went 'NOT READY': %04x/%02x.\n",dev->name,(int) stat,(int) p->scb->rus);
         ni52_rnr_int(dev); 
       }
     }
+
+    if(stat & STAT_CX)    /* command with I-bit set complete */
+       ni52_xmt_int(dev);
+
+#ifndef NO_NOPCOMMANDS
+    if(stat & STAT_CNA)  /* CU went 'not ready' */
+    {
+      if(dev->start)
+        printk("%s: oops! CU has left active state. stat: %04x/%02x.\n",dev->name,(int) stat,(int) p->scb->cus);
+    }
+#endif
+
+    if(debuglevel > 1)
+      printk("%d",cnt++);
+
     WAIT_4_SCB_CMD(); /* wait for ack. (ni52_xmt_int can be faster than ack!!) */
-    if(p->scb->cmd)   /* timed out? */
+    if(p->scb->cmd_cuc)   /* timed out? */
+    {
+      printk("%s: Acknowledge timed out.\n",dev->name);
+      ni_disint();
       break;
+    }
   }
 
+  if(debuglevel > 1)
+    printk("i");
+
   dev->interrupt = 0;
 }
 
@@ -799,17 +890,20 @@ static void ni52_interrupt(int irq,struct pt_regs *reg_ptr)
 
 static void ni52_rcv_int(struct device *dev)
 {
-  int status;
+  int status,cnt=0;
   unsigned short totlen;
   struct sk_buff *skb;
   struct rbd_struct *rbd;
   struct priv *p = (struct priv *) dev->priv;
 
-  for(;(status = p->rfd_top->status) & STAT_COMPL;)
+  if(debuglevel > 0)
+    printk("R");
+
+  for(;(status = p->rfd_top->stat_high) & RFD_COMPL;)
   {
       rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset);
 
-      if(status & STAT_OK) /* frame received without error? */
+      if(status & RFD_OK) /* frame received without error? */
       {
         if( (totlen = rbd->status) & RBD_LAST) /* the first and the last buffer? */
         {
@@ -819,8 +913,9 @@ static void ni52_rcv_int(struct device *dev)
           if(skb != NULL)
           {
             skb->dev = dev;
-            skb_reserve(skb,2);                /* 16 byte alignment */
-            memcpy(skb_put(skb,totlen),(char *) p->base+(unsigned long) rbd->buffer, totlen);
+            skb_reserve(skb,2);
+            skb_put(skb,totlen);
+            eth_copy_and_sum(skb,(char *) p->base+(unsigned long) rbd->buffer,totlen,0);
             skb->protocol=eth_type_trans(skb,dev);
             netif_rx(skb);
             p->stats.rx_packets++;
@@ -830,25 +925,90 @@ static void ni52_rcv_int(struct device *dev)
         }
         else
         {
-          printk("%s: received oversized frame.\n",dev->name);
+          int rstat;
+             /* free all RBD's until RBD_LAST is set */
+          totlen = 0;
+          while(!((rstat=rbd->status) & RBD_LAST))
+          {
+            totlen += rstat & RBD_MASK;
+            if(!rstat)
+            {
+              printk("%s: Whoops .. no end mark in RBD list\n",dev->name);
+              break;
+            }
+            rbd->status = 0;
+            rbd = (struct rbd_struct *) make32(rbd->next);
+          }
+          totlen += rstat & RBD_MASK;
+          rbd->status = 0;
+          printk("%s: received oversized frame! length: %d\n",dev->name,totlen);
           p->stats.rx_dropped++;
-        }
+       }
       }
       else /* frame !(ok), only with 'save-bad-frames' */
       {
         printk("%s: oops! rfd-error-status: %04x\n",dev->name,status);
         p->stats.rx_errors++;
       }
-      p->rfd_top->status = 0;
-      p->rfd_top->last = RFD_SUSP;
-      p->rfd_last->last = 0;        /* delete RU_SUSP  */
+      p->rfd_top->stat_high = 0;
+      p->rfd_top->last = RFD_SUSP; /* maybe exchange by RFD_LAST */
+      p->rfd_top->rbd_offset = 0xffff;
+      p->rfd_last->last = 0;        /* delete RFD_SUSP  */
       p->rfd_last = p->rfd_top;
       p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next); /* step to next RFD */
+      p->scb->rfa_offset = make16(p->rfd_top);
+
+      if(debuglevel > 0)
+        printk("%d",cnt++);
+  }
+
+  if(automatic_resume)
+  {
+    WAIT_4_SCB_CMD();
+    p->scb->cmd_ruc = RUC_RESUME;
+    ni_attn586();
+    WAIT_4_SCB_CMD_RUC();
+  }
+
+#ifdef WAIT_4_BUSY
+  {
+    int i;
+    for(i=0;i<1024;i++)
+    {
+      if(p->rfd_top->status)
+        break;
+      DELAY_16();
+      if(i == 1023)
+        printk("%s: RU hasn't fetched next RFD (not busy/complete)\n",dev->name);
+    }
   }
+#endif
+
+#ifdef 0
+  if(!at_least_one)
+  { 
+    int i;
+    volatile struct rfd_struct *rfds=p->rfd_top;
+    volatile struct rbd_struct *rbds;
+    printk("%s: received a FC intr. without having a frame: %04x %d\n",dev->name,status,old_at_least);
+    for(i=0;i< (p->num_recv_buffs+4);i++)
+    {
+      rbds = (struct rbd_struct *) make32(rfds->rbd_offset);
+      printk("%04x:%04x ",rfds->status,rbds->status);
+      rfds = (struct rfd_struct *) make32(rfds->next);
+    }
+    printk("\nerrs: %04x %04x stat: %04x\n",(int)p->scb->rsc_errs,(int)p->scb->ovrn_errs,(int)p->scb->status);
+    printk("\nerrs: %04x %04x rus: %02x, cus: %02x\n",(int)p->scb->rsc_errs,(int)p->scb->ovrn_errs,(int)p->scb->rus,(int)p->scb->cus);  
+  }
+  old_at_least = at_least_one;
+#endif
+
+  if(debuglevel > 0)
+    printk("r");
 }
 
 /**********************************************************
- * handle 'Receiver went not ready'. 
+ * handle 'Receiver went not ready'.
  */
 
 static void ni52_rnr_int(struct device *dev)
@@ -857,15 +1017,16 @@ static void ni52_rnr_int(struct device *dev)
 
   p->stats.rx_errors++;
 
-  WAIT_4_SCB_CMD();    /* wait for the last cmd */
-  p->scb->cmd = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */
+  WAIT_4_SCB_CMD();    /* wait for the last cmd, WAIT_4_FULLSTAT?? */
+  p->scb->cmd_ruc = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */
   ni_attn586(); 
-  WAIT_4_SCB_CMD();    /* wait for accept cmd. */
+  WAIT_4_SCB_CMD_RUC();    /* wait for accept cmd. */
 
   alloc_rfa(dev,(char *)p->rfd_first);
+/* maybe add a check here, before restarting the RU */
   startrecv586(dev); /* restart RU */
 
-  printk("%s: Receive-Unit restarted. Status: %04x\n",dev->name,p->scb->status);
+  printk("%s: Receive-Unit restarted. Status: %04x\n",dev->name,p->scb->rus);
 
 }
 
@@ -878,6 +1039,9 @@ static void ni52_xmt_int(struct device *dev)
   int status;
   struct priv *p = (struct priv *) dev->priv;
 
+  if(debuglevel > 0)
+    printk("X");
+
   status = p->xmit_cmds[p->xmit_last]->cmd_status;
   if(!(status & STAT_COMPL))
     printk("%s: strange .. xmit-int without a 'COMPLETE'\n",dev->name);
@@ -910,7 +1074,7 @@ static void ni52_xmt_int(struct device *dev)
     } 
   }
 
-#if (NUM_XMIT_BUFFS != 1)
+#if (NUM_XMIT_BUFFS > 1)
   if( (++p->xmit_last) == NUM_XMIT_BUFFS) 
     p->xmit_last = 0;
 #endif
@@ -927,10 +1091,12 @@ static void startrecv586(struct device *dev)
 {
   struct priv *p = (struct priv *) dev->priv;
 
+  WAIT_4_SCB_CMD();
+  WAIT_4_SCB_CMD_RUC();
   p->scb->rfa_offset = make16(p->rfd_first);
-  p->scb->cmd = RUC_START;
+  p->scb->cmd_ruc = RUC_START;
   ni_attn586();                /* start cmd. */
-  WAIT_4_SCB_CMD();    /* wait for accept cmd. (no timeout!!) */
+  WAIT_4_SCB_CMD_RUC();        /* wait for accept cmd. (no timeout!!) */
 }
 
 /******************************************************
@@ -951,28 +1117,31 @@ static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
     if (tickssofar < 5)
       return 1;
 
-    if(p->scb->status & CU_ACTIVE) /* COMMAND-UNIT active? */
+#ifndef NO_NOPCOMMANDS
+    if(p->scb->cus & CU_ACTIVE) /* COMMAND-UNIT active? */
     {
       dev->tbusy = 0;
 #ifdef DEBUG
       printk("%s: strange ... timeout with CU active?!?\n",dev->name);
       printk("%s: X0: %04x N0: %04x N1: %04x %d\n",dev->name,(int)p->xmit_cmds[0]->cmd_status,(int)p->nop_cmds[0]->cmd_status,(int)p->nop_cmds[1]->cmd_status,(int)p->nop_point);
 #endif
-      p->scb->cmd = CUC_ABORT;
+      p->scb->cmd_cuc = CUC_ABORT;
       ni_attn586();
       WAIT_4_SCB_CMD();
       p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]);
-      p->scb->cmd = CUC_START;
+      p->scb->cmd_cuc = CUC_START;
       ni_attn586();
       WAIT_4_SCB_CMD();
       dev->trans_start = jiffies;
       return 0;
     }
     else
+#endif
     {
 #ifdef DEBUG
-      printk("%s: xmitter timed out, try to restart! stat: %04x\n",dev->name,p->scb->status);
+      printk("%s: xmitter timed out, try to restart! stat: %02x\n",dev->name,p->scb->cus);
       printk("%s: command-stats: %04x %04x\n",dev->name,p->xmit_cmds[0]->cmd_status,p->xmit_cmds[1]->cmd_status);
+      printk("%s: check, whether you set the right interrupt number!\n",dev->name);
 #endif
       ni52_close(dev);
       ni52_open(dev);
@@ -995,8 +1164,16 @@ static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
     return 0;
   }
 
-  if (set_bit(0, (void*)&dev->tbusy) != 0)
-     printk("%s: Transmitter access conflict.\n", dev->name);
+  if (set_bit(0, (void*)&dev->tbusy)) {
+    printk("%s: Transmitter access conflict.\n", dev->name);
+    return 1;
+  }
+#if(NUM_XMIT_BUFFS > 1)
+  else if(set_bit(0,(void *) &p->lock)) {
+    printk("%s: Queue was locked\n",dev->name);
+    return 1;
+  }
+#endif
   else
   {
     memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len);
@@ -1004,19 +1181,34 @@ static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
 
 #if (NUM_XMIT_BUFFS == 1)
 #  ifdef NO_NOPCOMMANDS
+
+#ifdef DEBUG
+    if(p->scb->cus & CU_ACTIVE)
+    {
+      printk("%s: Hmmm .. CU is still running and we wanna send a new packet.\n",dev->name);
+      printk("%s: stat: %04x %04x\n",dev->name,p->scb->cus,p->xmit_cmds[0]->cmd_status);
+    }
+#endif
+
     p->xmit_buffs[0]->size = TBD_LAST | len;
     for(i=0;i<16;i++)
     {
-      p->scb->cbl_offset = make16(p->xmit_cmds[0]);
-      p->scb->cmd = CUC_START;
       p->xmit_cmds[0]->cmd_status = 0;
+      WAIT_4_SCB_CMD();
+      if( (p->scb->cus & CU_STATUS) == CU_SUSPEND)
+        p->scb->cmd_cuc = CUC_RESUME;
+      else
+      {
+        p->scb->cbl_offset = make16(p->xmit_cmds[0]);
+        p->scb->cmd_cuc = CUC_START;
+      }
 
       ni_attn586();
       dev->trans_start = jiffies;
       if(!i)
         dev_kfree_skb(skb,FREE_WRITE);
       WAIT_4_SCB_CMD();
-      if( (p->scb->status & CU_ACTIVE)) /* test it, because CU sometimes doesn't start immediately */
+      if( (p->scb->cus & CU_ACTIVE)) /* test it, because CU sometimes doesn't start immediately */
         break;
       if(p->xmit_cmds[0]->cmd_status)
         break;
@@ -1042,18 +1234,23 @@ static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
       next_nop = 0;
 
     p->xmit_cmds[p->xmit_count]->cmd_status  = 0;
-    p->xmit_cmds[p->xmit_count]->cmd_link = p->nop_cmds[next_nop]->cmd_link 
-                                          = make16((p->nop_cmds[next_nop]));
+       /* linkpointer of xmit-command allready points to next nop cmd */
+    p->nop_cmds[next_nop]->cmd_link = make16((p->nop_cmds[next_nop]));
     p->nop_cmds[next_nop]->cmd_status = 0;
 
     p->nop_cmds[p->xmit_count]->cmd_link = make16((p->xmit_cmds[p->xmit_count]));
     dev->trans_start = jiffies;
     p->xmit_count = next_nop;
-  
-    cli();
-    if(p->xmit_count != p->xmit_last)
-      dev->tbusy = 0;
-    sti();
+    {
+      long flags; 
+      save_flags(flags);
+      cli();
+      if(p->xmit_count != p->xmit_last)
+        dev->tbusy = 0;
+      p->lock = 0;
+      restore_flags(flags);
+    }
     dev_kfree_skb(skb,FREE_WRITE);
 #endif
   }
@@ -1070,13 +1267,13 @@ static struct enet_statistics *ni52_get_stats(struct device *dev)
   unsigned short crc,aln,rsc,ovrn;
 
   crc = p->scb->crc_errs; /* get error-statistic from the ni82586 */
-  p->scb->crc_errs -= crc;
+  p->scb->crc_errs = 0;
   aln = p->scb->aln_errs;
-  p->scb->aln_errs -= aln;
+  p->scb->aln_errs = 0;
   rsc = p->scb->rsc_errs;
-  p->scb->rsc_errs -= rsc;
+  p->scb->rsc_errs = 0;
   ovrn = p->scb->ovrn_errs;
-  p->scb->ovrn_errs -= ovrn;
+  p->scb->ovrn_errs = 0;
 
   p->stats.rx_crc_errors += crc;
   p->stats.rx_fifo_errors += ovrn;
@@ -1089,7 +1286,6 @@ static struct enet_statistics *ni52_get_stats(struct device *dev)
 /********************************************************
  * Set MC list ..  
  */
-
 static void set_multicast_list(struct device *dev)
 {
   if(!dev->start)
@@ -1099,12 +1295,92 @@ static void set_multicast_list(struct device *dev)
   }
 
   dev->start = 0;
+
+  ni_disint();
   alloc586(dev);
   init586(dev);  
   startrecv586(dev);
+  ni_enaint();
+
   dev->start = 1;
 }
 
+#ifdef MODULE
+static struct device dev_ni52 = {
+  "        ",  /* "ni5210": device name inserted by net_init.c */
+  0, 0, 0, 0,
+  0x300, 9,   /* I/O address, IRQ */
+  0, 0, 0, NULL, ni52_probe };
+
+/* set: io,irq,memstart,memend or set it when calling insmod */
+int irq=9;
+int io=0x300;
+long memstart=0; /* e.g 0xd0000 */
+long memend=0;   /* e.g 0xd4000 */
+
+int init_module(void)
+{
+  if(io <= 0x0 || !memend || !memstart || irq < 2) {
+    printk("ni52: Autoprobing not allowed for modules.\nni52: Set symbols 'io' 'irq' 'memstart' and 'memend'\n");
+    return -ENODEV;
+  }
+  dev_ni52.irq = irq;
+  dev_ni52.base_addr = io;
+  dev_ni52.mem_end = memend;
+  dev_ni52.mem_start = memstart;
+  if (register_netdev(&dev_ni52) != 0)
+    return -EIO;
+  return 0;
+}
+
+void cleanup_module(void)
+{
+  release_region(dev_ni52.base_addr, NI52_TOTAL_SIZE);
+  kfree(dev_ni52.priv);
+  dev_ni52.priv = NULL;
+  unregister_netdev(&dev_ni52);
+}
+#endif /* MODULE */
+
+#if 0
+/*
+ * DUMP .. we expect a not running CMD unit and enough space
+ */
+void ni52_dump(struct device *dev,void *ptr)
+{
+  struct priv *p = (struct priv *) dev->priv;
+  struct dump_cmd_struct *dump_cmd = (struct dump_cmd_struct *) ptr;
+  int i;
+
+  p->scb->cmd_cuc = CUC_ABORT;
+  ni_attn586();
+  WAIT_4_SCB_CMD();
+  WAIT_4_SCB_CMD_RUC();
+
+  dump_cmd->cmd_status = 0;
+  dump_cmd->cmd_cmd = CMD_DUMP | CMD_LAST;
+  dump_cmd->dump_offset = make16((dump_cmd + 1));
+  dump_cmd->cmd_link = 0xffff;
+
+  p->scb->cbl_offset = make16(dump_cmd);
+  p->scb->cmd_cuc = CUC_START;
+  ni_attn586();
+  WAIT_4_STAT_COMPL(dump_cmd);
+
+  if( (dump_cmd->cmd_status & (STAT_COMPL|STAT_OK)) != (STAT_COMPL|STAT_OK) )
+        printk("%s: Can't get dump information.\n",dev->name);
+
+  for(i=0;i<170;i++) {
+    printk("%02x ",(int) ((unsigned char *) (dump_cmd + 1))[i]);
+    if(i % 24 == 23)
+      printk("\n");
+  }
+  printk("\n");
+}
+#endif
+
 /*
  * END: linux/drivers/net/ni52.c 
  */
+
+
index 23b0a0e89d69dcddd8ef05d68a4e581a40621fab..2bd9584282bbbffe9d4d4a57cf3cc82829c8d230 100644 (file)
@@ -61,8 +61,10 @@ struct iscp_struct
  */
 struct scb_struct
 {
-  unsigned short status;        /* status word */
-  unsigned short cmd;           /* command word */
+  unsigned char rus;
+  unsigned char cus;
+  unsigned char cmd_ruc;           /* command word: RU part */
+  unsigned char cmd_cuc;           /* command word: CU part & ACK */
   unsigned short cbl_offset;    /* pointeroffset, command block list */
   unsigned short rfa_offset;    /* pointeroffset, receive frame area */
   unsigned short crc_errs;      /* CRC-Error counter */
@@ -81,31 +83,31 @@ struct scb_struct
 #define RUC_SUSPEND    0x0030  /* suspend RU */
 #define RUC_ABORT      0x0040  /* abort receiver operation immediately */
 
-#define CUC_MASK       0x0700  /* mask for CU command */
-#define CUC_NOP                0x0000  /* NOP-command */
-#define CUC_START      0x0100  /* start execution of 1. cmd on the CBL */
-#define CUC_RESUME     0x0200  /* resume after suspend */
-#define CUC_SUSPEND    0x0300  /* Suspend CU */
-#define CUC_ABORT      0x0400  /* abort command operation immediately */
+#define CUC_MASK        0x07  /* mask for CU command */
+#define CUC_NOP         0x00  /* NOP-command */
+#define CUC_START       0x01  /* start execution of 1. cmd on the CBL */
+#define CUC_RESUME      0x02  /* resume after suspend */
+#define CUC_SUSPEND     0x03  /* Suspend CU */
+#define CUC_ABORT       0x04  /* abort command operation immediately */
 
-#define ACK_MASK       0xf000  /* mask for ACK command */
-#define ACK_CX         0x8000  /* acknowledges STAT_CX */
-#define ACK_FR         0x4000  /* ack. STAT_FR */
-#define ACK_CNA                0x2000  /* ack. STAT_CNA */
-#define ACK_RNR                0x1000  /* ack. STAT_RNR */
+#define ACK_MASK        0xf0  /* mask for ACK command */
+#define ACK_CX          0x80  /* acknowledges STAT_CX */
+#define ACK_FR          0x40  /* ack. STAT_FR */
+#define ACK_CNA         0x20  /* ack. STAT_CNA */
+#define ACK_RNR         0x10  /* ack. STAT_RNR */
 
 /*
  * possible status values for the status word
  */
-#define STAT_MASK      0xf000  /* mask for cause of interrupt */
-#define STAT_CX                0x8000  /* CU finished cmd with its I bit set */
-#define STAT_FR                0x4000  /* RU finished receiving a frame */
-#define STAT_CNA       0x2000  /* CU left active state */
-#define STAT_RNR       0x1000  /* RU left ready state */
+#define STAT_MASK       0xf0  /* mask for cause of interrupt */
+#define STAT_CX         0x80  /* CU finished cmd with its I bit set */
+#define STAT_FR         0x40  /* RU finished receiving a frame */
+#define STAT_CNA        0x20  /* CU left active state */
+#define STAT_RNR        0x10  /* RU left ready state */
 
-#define CU_STATUS      0x700   /* CU status, 0=idle */
-#define CU_SUSPEND     0x100   /* CU is suspended */
-#define CU_ACTIVE      0x200   /* CU is active */
+#define CU_STATUS       0x7   /* CU status, 0=idle */
+#define CU_SUSPEND      0x1   /* CU is suspended */
+#define CU_ACTIVE       0x2   /* CU is active */
 
 #define RU_STATUS      0x70    /* RU status, 0=idle */
 #define RU_SUSPEND     0x10    /* RU suspended */
@@ -117,8 +119,10 @@ struct scb_struct
  */
 struct rfd_struct
 {
-  unsigned short status;       /* status word */
-  unsigned short last;         /* Bit15,Last Frame on List / Bit14,suspend */
+  unsigned char  stat_low;     /* status word */
+  unsigned char  stat_high;    /* status word */
+  unsigned char  rfd_sf;       /* 82596 mode only */
+  unsigned char  last;         /* Bit15,Last Frame on List / Bit14,suspend */
   unsigned short next;         /* linkoffset to next RFD */
   unsigned short rbd_offset;   /* pointeroffset to RBD-buffer */
   unsigned char  dest[6];      /* ethernet-address, destination */
@@ -127,11 +131,22 @@ struct rfd_struct
   unsigned short zero_dummy;   /* dummy */
 };
 
-#define RFD_LAST     0x8000    /* last: last rfd in the list */
-#define RFD_SUSP     0x4000    /* last: suspend RU after  */
-#define RFD_ERRMASK  0x0fe1     /* status: errormask */
-#define RFD_MATCHADD 0x0002     /* status: Destinationaddress !matches IA */
-#define RFD_RNR      0x0200    /* status: receiver out of resources */
+#define RFD_LAST     0x80      /* last: last rfd in the list */
+#define RFD_SUSP     0x40      /* last: suspend RU after  */
+#define RFD_COMPL    0x80
+#define RFD_OK       0x20
+#define RFD_BUSY     0x40
+#define RFD_ERR_LEN  0x10     /* Length error (if enabled length-checking */
+#define RFD_ERR_CRC  0x08     /* CRC error */
+#define RFD_ERR_ALGN 0x04     /* Alignment error */
+#define RFD_ERR_RNR  0x02     /* status: receiver out of resources */
+#define RFD_ERR_OVR  0x01     /* DMA Overrun! */
+
+#define RFD_ERR_FTS  0x0080    /* Frame to short */
+#define RFD_ERR_NEOP 0x0040    /* No EOP flag (for bitstuffing only) */
+#define RFD_ERR_TRUN 0x0020    /* (82596 only/SF mode) indicates truncated frame */
+#define RFD_MATCHADD 0x0002     /* status: Destinationaddress !matches IA (only 82596) */
+#define RFD_COLLDET  0x0001    /* Detected collision during reception */
 
 /*
  * Receive Buffer Descriptor (RBD)
@@ -230,6 +245,17 @@ struct mcsetup_cmd_struct
   unsigned char  mc_list[0][6];        /* pointer to 6 bytes entries */
 };
 
+/*
+ * DUMP command
+ */
+struct dump_cmd_struct
+{
+  unsigned short cmd_status;
+  unsigned short cmd_cmd;
+  unsigned short cmd_link;
+  unsigned short dump_offset;    /* pointeroffset to DUMP space */
+};
+
 /*
  * transmit command 
  */
index 44a58112f741edd641587acc15bca4111197e88d..ff41336aef7900403a683b8125a3de9e56f9426d 100644 (file)
@@ -1,35 +1,42 @@
 /*
  * ni6510 (am7990 'lance' chip) driver for Linux-net-3 by MH
- * Alphacode v0.33 (94/08/22) for 1.1.47 (or later)
+ * Alphacode v0.51 (96/02/20) for 1.3.66 (or later)
  *
- * ----------------------------------------------------------
- * WARNING: DOESN'T WORK ON MACHINES WITH MORE THAN 16MB !!!!
- * ----------------------------------------------------------
- *
- * copyright (c) 1994 M.Hipp
+ * copyright (c) 1994,1995,1996 by M.Hipp
  *
  * This is an extension to the Linux operating system, and is covered by the
  * same Gnu Public License that covers the Linux-kernel.
  *
  * comments/bugs/suggestions can be sent to:
- *    Michael Hipp
- *    email: mhipp@student.uni-tuebingen.de
+ *   Michael Hipp
+ *   email: Michael.Hipp@student.uni-tuebingen.de
  *
  * sources:
- *  some things are from the 'ni6510-packet-driver for dos by Russ Nelson'
- *  and from the original drivers by D.Becker
- */
-
-/*
- * Nov.18: multicast tweaked (AC).
+ *   some things are from the 'ni6510-packet-driver for dos by Russ Nelson'
+ *   and from the original drivers by D.Becker
+ *
+ * known problems:
+ *   on some PCI boards (including my own) the card/board/ISA-bridge has
+ *   problems with bus master DMA. This results in lotsa overruns.
+ *   It may help to '#define RCV_PARANOIA_CHECK'
+ *   or just play with your BIOS options to optimize ISA-DMA access.
  *
- * Aug.22: changes in xmit_intr (ack more than one xmitted-packet), ni65_send_packet (p->lock) (MH)
+ * credits:
+ *   thanx to Jason Sullivan for sending me a ni6510 card!
  *
- * July.16: fixed bugs in recv_skb and skb-alloc stuff  (MH)
+ * simple performance test:
+ *   8.1 seconds for getting a 8MB file via FTP -> near 1MB/s
  */
 
 /*
- * known BUGS: 16MB limit
+ * 96.Feb.19: fixed a few bugs .. cleanups .. tested for 1.3.66
+ *            hopefully no more 16MB limit
+ *
+ * 95.Nov.18: multicast tweaked (AC).
+ *
+ * 94.Aug.22: changes in xmit_intr (ack more than one xmitted-packet), ni65_send_packet (p->lock) (MH)
+ *
+ * 94,July.16: fixed bugs in recv_skb and skb-alloc stuff  (MH)
  */
 
 #include <linux/kernel.h>
@@ -40,6 +47,7 @@
 #include <linux/ioport.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
+#include <linux/delay.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
 
 #include "ni65.h"
 
-/************************************
- * skeleton-stuff
+/*
+ * the current setting allows max. perforamnce
+ * for 'RCV_PARANOIA_CHECK' read the 'known problems' part in 
+ * the header of this file
  */
+#define RCV_VIA_SKB
+#undef RCV_PARANOIA_CHECK
+#define XMT_VIA_SKB
 
-#ifndef HAVE_PORTRESERVE
-#define check_region(ioaddr, size)              0
-#define request_region(ioaddr, size,name)       do ; while (0)
-#endif
-
-#ifndef NET_DEBUG
-#define NET_DEBUG 2
-#endif
 /*
-static unsigned int net_debug = NET_DEBUG;
-*/
-
+ * a few card specific defines
+ */
 #define NI65_TOTAL_SIZE    16
-
-#define SA_ADDR0 0x02
-#define SA_ADDR1 0x07
-#define SA_ADDR2 0x01
-#define CARD_ID0 0x00
-#define CARD_ID1 0x55
-
-/*****************************************/
+#define NI65_ADDR0 0x02
+#define NI65_ADDR1 0x07
+#define NI65_ADDR2 0x01
+#define NI65_ID0   0x00
+#define NI65_ID1   0x55
 
 #define PORT dev->base_addr
 
+/*
+ * buffer configuration
+ */
 #define RMDNUM 8
-#define RMDNUMMASK 0x6000 /* log2(RMDNUM)<<13 */
+#define RMDNUMMASK 0x60000000 /* log2(RMDNUM)<<29 */
 #define TMDNUM 4
-#define TMDNUMMASK 0x4000 /* log2(TMDNUM)<<13 */
-
-#define R_BUF_SIZE 1518
-#define T_BUF_SIZE 1518
+#define TMDNUMMASK 0x40000000 /* log2(TMDNUM)<<29 */
 
-#define MEMSIZE 8+RMDNUM*8+TMDNUM*8
+#define R_BUF_SIZE 1536
+#define T_BUF_SIZE 1536
 
+/*
+ * lance register defines
+ */
 #define L_DATAREG 0x00
 #define L_ADDRREG 0x02
 
@@ -104,51 +109,53 @@ static unsigned int net_debug = NET_DEBUG;
 #define CSR2 0x02
 #define CSR3 0x03
 
-/* if you #define NO_STATIC the driver is faster but you will have (more) problems with >16MB memory */
-#undef NO_STATIC
-
 #define writereg(val,reg) {outw(reg,PORT+L_ADDRREG);inw(PORT+L_ADDRREG); \
                            outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);}
 #define readreg(reg) (outw(reg,PORT+L_ADDRREG),inw(PORT+L_ADDRREG),\
                        inw(PORT+L_DATAREG))
 #define writedatareg(val) {outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);}
 
-static int   ni65_probe1(struct device *dev,int);
-static void  ni65_interrupt(int irq, struct pt_regs *regs);
-  static void recv_intr(struct device *dev);
-  static void xmit_intr(struct device *dev);
-static int   ni65_open(struct device *dev);
-   static int am7990_reinit(struct device *dev);
-static int   ni65_send_packet(struct sk_buff *skb, struct device *dev);
-static int   ni65_close(struct device *dev);
+static int  ni65_probe1(struct device **dev,int);
+static void ni65_interrupt(int irq, struct pt_regs *regs);
+static void ni65_recv_intr(struct device *dev,int);
+static void ni65_xmit_intr(struct device *dev,int);
+static int  ni65_open(struct device *dev);
+static int  ni65_am7990_reinit(struct device *dev);
+static int  ni65_send_packet(struct sk_buff *skb, struct device *dev);
+static int  ni65_close(struct device *dev);
 static struct enet_statistics *ni65_get_stats(struct device *);
-
 static void set_multicast_list(struct device *dev);
 
 struct priv 
 {
+  struct rmd rmdhead[RMDNUM];
+  struct tmd tmdhead[TMDNUM];
   struct init_block ib; 
-  void *memptr;
-  struct rmd *rmdhead;
-  struct tmd *tmdhead;
   int rmdnum;
   int tmdnum,tmdlast;
+#ifdef RCV_VIA_SKB
   struct sk_buff *recv_skb[RMDNUM];
-  void *tmdbufs[TMDNUM];
+#else
+  void *recvbounce[RMDNUM];
+#endif
+#ifdef XMT_VIA_SKB
+  struct sk_buff *tmd_skb[TMDNUM];
+#endif
+  void *tmdbounce[TMDNUM];
   int lock,xmit_queued;
   struct enet_statistics stats;
 }; 
 
-int irqtab[] = { 9,12,15,5 }; /* irq config-translate */
-int dmatab[] = { 0,3,5,6 };   /* dma config-translate */
+static int irqtab[] = { 9,12,15,5 }; /* irq config-translate */
+static int dmatab[] = { 0,3,5,6 };   /* dma config-translate */
+static int debuglevel = 0;
 
 /*
  * open (most done by init) 
  */
-
 static int ni65_open(struct device *dev)
 {
-  if(am7990_reinit(dev))
+  if(ni65_am7990_reinit(dev))
   {
     dev->tbusy     = 0;
     dev->interrupt = 0;
@@ -174,49 +181,64 @@ static int ni65_close(struct device *dev)
  * Probe The Card (not the lance-chip) 
  * and set hardaddress
  */ 
-
 int ni65_probe(struct device *dev)
 {
-  int *port, ports[] = {0x300,0x320,0x340,0x360, 0};
-  int base_addr = dev->base_addr;
-
-  if (base_addr > 0x1ff)          /* Check a single specified location. */
-     return ni65_probe1(dev, base_addr);
-  else if (base_addr > 0)         /* Don't probe at all. */
-     return ENXIO;
+  int *port;
+  static int ports[] = {0x300,0x320,0x340,0x360, 0};
+
+  if(dev) {
+    int base_addr = dev->base_addr;
+    if (base_addr > 0x1ff)          /* Check a single specified location. */
+       return ni65_probe1(&dev, base_addr);
+    else if (base_addr > 0)         /* Don't probe at all. */
+       return -ENXIO;
+    dev->base_addr = base_addr;
+  }
 
   for (port = ports; *port; port++) 
   {
     int ioaddr = *port;
+
     if (check_region(ioaddr, NI65_TOTAL_SIZE))
        continue;
-    if( !(inb(ioaddr+L_EBASE+6) == CARD_ID0) || 
-        !(inb(ioaddr+L_EBASE+7) == CARD_ID1) )
+    if( !(inb(ioaddr+L_EBASE+6) == NI65_ID0) || 
+        !(inb(ioaddr+L_EBASE+7) == NI65_ID1) )
        continue;
-    dev->base_addr = ioaddr;
-    if (ni65_probe1(dev, ioaddr) == 0)
+    if (ni65_probe1(&dev, ioaddr) == 0)
        return 0;
   }
 
-  dev->base_addr = base_addr;
-  return ENODEV;
+  return -ENODEV;
 }
 
+int ni65_init(void)
+{
+        ni65_probe(NULL);
+        return 0;
+}
 
-static int ni65_probe1(struct device *dev,int ioaddr)
+static int ni65_probe1(struct device **dev1,int ioaddr)
 {
   int i;
-  unsigned char station_addr[6];
+  unsigned char *ptr;
   struct priv *p; 
+  struct device *dev = *dev1;
 
-  for(i=0;i<6;i++)
-    station_addr[i] = dev->dev_addr[i] = inb(PORT+L_EBASE+i);
-
-  if(station_addr[0] != SA_ADDR0 || station_addr[1] != SA_ADDR1)
+  if(inb(ioaddr+L_EBASE+0) != NI65_ADDR0 || inb(ioaddr+L_EBASE+1) != NI65_ADDR1 
+      || inb(ioaddr+L_EBASE+2) != NI65_ADDR2)
   {
-    printk("%s: wrong Hardaddress \n",dev->name);
-    return ENODEV;
+    printk("%s: wrong Hardaddress \n",dev ? dev->name : "ni6510" );
+    return -ENODEV;
+  }
+
+  if(!dev) {
+    dev = init_etherdev(0,0);
+    *dev1 = dev;
   }
+  dev->base_addr = ioaddr;
+
+  for(i=0;i<6;i++)
+    dev->dev_addr[i] = inb(PORT+L_EBASE+i);
 
   if(dev->irq == 0) 
     dev->irq = irqtab[(inw(PORT+L_CONFIG)>>2)&3];
@@ -224,31 +246,28 @@ static int ni65_probe1(struct device *dev,int ioaddr)
     dev->dma = dmatab[inw(PORT+L_CONFIG)&3];
 
   printk("%s: %s found at %#3lx, IRQ %d DMA %d.\n", dev->name,
-           "network card", dev->base_addr, dev->irq,dev->dma);
+           "ni6510", dev->base_addr, dev->irq,dev->dma);
 
   {        
-    int irqval = request_irq(dev->irq, &ni65_interrupt,0,"ni65");
+    int irqval = request_irq(dev->irq, &ni65_interrupt,0,"ni6510");
     if (irqval) {
       printk ("%s: unable to get IRQ %d (irqval=%d).\n", 
                 dev->name,dev->irq, irqval);
-      return EAGAIN;
+      return -EAGAIN;
     }
-    if(request_dma(dev->dma, "ni65") != 0)
+    if(request_dma(dev->dma, "ni6510") != 0)
     {
       printk("%s: Can't request dma-channel %d\n",dev->name,(int) dev->dma);
       free_irq(dev->irq);
-      return EAGAIN;
+      return -EAGAIN;
     }
   }
   irq2dev_map[dev->irq] = dev;
 
-  /* Grab the region so we can find another board if autoIRQ fails. */
-        request_region(ioaddr,NI65_TOTAL_SIZE,"ni65");
-
-  p = dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL);
-  if (p == NULL)
-       return -ENOMEM;
-  memset((char *) dev->priv,0,sizeof(struct priv));
+  /* 
+   * Grab the region so we can find another board if autoIRQ fails. 
+   */
+  request_region(ioaddr,NI65_TOTAL_SIZE,"ni6510");
 
   dev->open               = ni65_open;
   dev->stop               = ni65_close;
@@ -258,47 +277,70 @@ static int ni65_probe1(struct device *dev,int ioaddr)
 
   ether_setup(dev);
 
-  dev->flags        &= ~IFF_MULTICAST;
   dev->interrupt      = 0;
   dev->tbusy          = 0;
   dev->start          = 0;
 
-  if( (p->memptr = kmalloc(MEMSIZE,GFP_KERNEL)) == NULL) {
-    printk("%s: Can't alloc TMD/RMD-buffer.\n",dev->name);
-    return EAGAIN;
+  /* 
+   * we need 8-aligned memory ..
+   */
+  ptr = kmalloc(sizeof(struct priv)+8,GFP_KERNEL|GFP_DMA);
+  if(!ptr)
+    return -ENOMEM;
+  ptr = (unsigned char *) (((unsigned long) ptr + 7) & ~0x7);
+  if( (unsigned long) ptr + sizeof(struct priv) > 0x1000000) {
+    printk("%s: Can't alloc buffer in lower 16MB!\n",dev->name);
+    return -EAGAIN;
   }
-  if( (unsigned long) (p->memptr + MEMSIZE) & 0xff000000) {
-    printk("%s: Can't alloc TMD/RMD buffer in lower 16MB!\n",dev->name);
-    return EAGAIN;
+  p = dev->priv = (struct priv *) ptr;
+  memset((char *) dev->priv,0,sizeof(struct priv));
+
+  for(i=0;i<TMDNUM;i++)
+  {
+    if( (ptr = kmalloc(T_BUF_SIZE,GFP_KERNEL | GFP_DMA )) == NULL) {
+      printk("%s: Can't alloc Xmit-Mem.\n",dev->name);
+      return -ENOMEM;
+    }
+    if( (unsigned long) (ptr+T_BUF_SIZE) > 0x1000000) {
+      printk("%s: Can't alloc Xmit-Mem in lower 16MB!\n",dev->name);
+      return -EAGAIN;
+    }
+    p->tmdbounce[i] = ptr;
+#ifdef XMT_VIA_SKB
+    p->tmd_skb[i] = NULL;
+#endif
   }
-  p->tmdhead = (struct tmd *) ((( (unsigned long)p->memptr ) + 8) & 0xfffffff8);
-  p->rmdhead = (struct rmd *) (p->tmdhead + TMDNUM);   
 
-#ifndef NO_STATIC
-   for(i=0;i<TMDNUM;i++)
+#ifdef RCV_VIA_SKB
+   for(i=0;i<RMDNUM;i++)
    {
-     if( (p->tmdbufs[i] = kmalloc(T_BUF_SIZE,GFP_ATOMIC)) == NULL) {
-       printk("%s: Can't alloc Xmit-Mem.\n",dev->name);
-       return EAGAIN;
+     struct sk_buff *skb;
+     if( !(skb = dev_alloc_skb(R_BUF_SIZE+2)) ) {
+       printk("%s: unable to alloc recv-mem\n",dev->name);
+       return -ENOMEM;
      }
-     if( (unsigned long) (p->tmdbufs[i]+T_BUF_SIZE) & 0xff000000) {
-       printk("%s: Can't alloc Xmit-Mem in lower 16MB!\n",dev->name);
-       return EAGAIN;
+     skb->dev = dev;
+     skb_reserve(skb,2);
+     skb_put(skb,R_BUF_SIZE);  /* grab the whole space .. (not necessary) */
+     if( (unsigned long) (skb->data + R_BUF_SIZE) > 0x1000000 ) {
+       printk("%s: unable to alloc receive-memory in lower 16MB!\n",dev->name);
+       return -EAGAIN;
      }
+     p->recv_skb[i] = skb;
    }
-#endif
-
+#else
    for(i=0;i<RMDNUM;i++)
    {
-     if( (p->recv_skb[i] = dev_alloc_skb(R_BUF_SIZE)) == NULL) {
+     if( !(p->recvbounce[i] = kmalloc(R_BUF_SIZE,GFP_KERNEL | GFP_DMA )) ) {
        printk("%s: unable to alloc recv-mem\n",dev->name);
-       return EAGAIN;
+       return -ENOMEM;
      }
-     if( (unsigned long) (p->recv_skb[i]->data + R_BUF_SIZE) & 0xff000000) {
+     if( (unsigned long) p->recvbounce[i] + R_BUF_SIZE > 0x1000000 ) {
        printk("%s: unable to alloc receive-memory in lower 16MB!\n",dev->name);
-       return EAGAIN;
+       return -EAGAIN;
      }
    }
+#endif
 
   return 0; /* we've found everything */
 }
@@ -307,11 +349,9 @@ static int ni65_probe1(struct device *dev,int ioaddr)
  * init lance (write init-values .. init-buffers) (open-helper)
  */
 
-static int am7990_reinit(struct device *dev)
+static int ni65_am7990_reinit(struct device *dev)
 {
-   int i,j;
-   struct tmd *tmdp;
-   struct rmd *rmdp;
+   int i;
    struct priv *p = (struct priv *) dev->priv;
 
    p->lock = 0;
@@ -324,67 +364,79 @@ static int am7990_reinit(struct device *dev)
    outw(0,PORT+L_RESET); /* first: reset the card */
    if(inw(PORT+L_DATAREG) != 0x4)
    {
-     printk("%s: can't RESET ni6510 card: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
+     printk(KERN_ERR "%s: can't RESET ni6510 card: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
      disable_dma(dev->dma);
      free_dma(dev->dma);
      free_irq(dev->irq);
      return 0;
    }
 
-   /* here: memset all buffs to zero */
-
-   memset(p->memptr,0,MEMSIZE);
-
    p->tmdnum = 0; p->tmdlast = 0;
    for(i=0;i<TMDNUM;i++)
    {
-     tmdp = p->tmdhead + i;
-#ifndef NO_STATIC
-     tmdp->u.buffer = (unsigned long) p->tmdbufs[i];     
+     struct tmd *tmdp = p->tmdhead + i;
+#ifdef XMT_VIA_SKB
+     if(p->tmd_skb[i]) {
+       dev_kfree_skb(p->tmd_skb[i],FREE_WRITE);
+       p->tmd_skb[i] = NULL;
+     }
 #endif
+     tmdp->u.buffer = 0x0;
      tmdp->u.s.status = XMIT_START | XMIT_END;
+     tmdp->blen = tmdp->status2 = 0;
    }
 
    p->rmdnum = 0;
    for(i=0;i<RMDNUM;i++)
    {
-     rmdp = p->rmdhead + i;
+     struct rmd *rmdp = p->rmdhead + i;
+#ifdef RCV_VIA_SKB
      rmdp->u.buffer = (unsigned long) p->recv_skb[i]->data;
-     rmdp->u.s.status = RCV_OWN;
-     rmdp->blen = -R_BUF_SIZE;
+#else
+     rmdp->u.buffer = (unsigned long) p->recvbounce[i];
+#endif
+     rmdp->blen = -(R_BUF_SIZE-8);
      rmdp->mlen = 0;
+     rmdp->u.s.status = RCV_OWN;
    }
    
    for(i=0;i<6;i++)
-   {
      p->ib.eaddr[i] = dev->dev_addr[i];
+
+   for(i=0;i<8;i++)
+     p->ib.filter[i] = 0x0;
+   p->ib.mode = 0x0;
+
+   if(dev->flags & IFF_PROMISC) {
+     p->ib.mode = M_PROM;
    }
-   p->ib.mode = 0;
-   for(i=0;i<8;i++) 
-     p->ib.filter[i] = 0;
-   p->ib.trplow = (unsigned short) (( (unsigned long) p->tmdhead ) & 0xffff);
-   p->ib.trphigh = (unsigned short) ((( (unsigned long) p->tmdhead )>>16) & 0x00ff) | TMDNUMMASK; 
-   p->ib.rrplow = (unsigned short) (( (unsigned long) p->rmdhead ) & 0xffff);
-   p->ib.rrphigh = (unsigned short) ((( (unsigned long) p->rmdhead )>>16) & 0x00ff) | RMDNUMMASK;
+   else if(dev->mc_count || dev->flags & IFF_ALLMULTI) {
+     for(i=0;i<8;i++)
+       p->ib.filter[i] = 0xff;
+   }
+
+   p->ib.trp = (unsigned long) p->tmdhead | TMDNUMMASK;
+   p->ib.rrp = (unsigned long) p->rmdhead | RMDNUMMASK;
 
    writereg(0,CSR3);  /* busmaster/no word-swap */
    writereg((unsigned short) (((unsigned long) &(p->ib)) & 0xffff),CSR1);
    writereg((unsigned short) (((unsigned long) &(p->ib))>>16),CSR2);
-   
+
    writereg(CSR0_INIT,CSR0); /* this changes L_ADDRREG to CSR0 */
 
   /*
-   * NOW, WE NEVER WILL CHANGE THE L_ADDRREG, CSR0 IS ALWAYS SELECTED 
+   * NOW, WE WILL NEVER CHANGE THE L_ADDRREG, CSR0 IS ALWAYS SELECTED 
    */
 
-    for(i=0;i<5;i++)
+    for(i=0;i<32;i++)
     {
-      for(j=0;j<2000000;j++); /* wait a while */
-      if(inw(PORT+L_DATAREG) & CSR0_IDON) break; /* init ok ? */
+      __delay((loops_per_sec>>8)); /* wait a while */
+      if(inw(PORT+L_DATAREG) & CSR0_IDON) 
+        break; /* init ok ? */
     }
-    if(i == 5
+    if(i == 32
     {
-      printk("%s: can't init am7990, status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
+      printk(KERN_ERR "%s: can't init am7990/lance, status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
       disable_dma(dev->dma);
       free_dma(dev->dma);
       free_irq(dev->irq);
@@ -399,42 +451,73 @@ static int am7990_reinit(struct device *dev)
 /* 
  * interrupt handler  
  */
-
 static void ni65_interrupt(int irq, struct pt_regs * regs)
 {
   int csr0;
   struct device *dev = (struct device *) irq2dev_map[irq];
+  int bcnt = 32;
 
   if (dev == NULL) {
-    printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+    printk (KERN_ERR "ni65_interrupt(): irq %d for unknown device.\n", irq);
     return;
   }
 
-  csr0 = inw(PORT+L_DATAREG);
-  writedatareg(csr0 & CSR0_CLRALL); /* ack interrupts, disable int. */
-
   dev->interrupt = 1;
 
-  if(csr0 & CSR0_ERR)
-  {
-     struct priv *p = (struct priv *) dev->priv;
+  while(--bcnt) {
 
-     if(csr0 & CSR0_BABL)
-       p->stats.tx_errors++;
-     if(csr0 & CSR0_MISS)
-       p->stats.rx_errors++;
+    csr0 = inw(PORT+L_DATAREG);
+    writedatareg(csr0 & CSR0_CLRALL); /* ack interrupts, disable int. */
+
+    if(!(csr0 & (CSR0_ERR | CSR0_RINT | CSR0_TINT)))
+      break;
+
+    if(csr0 & CSR0_ERR)
+    {
+      struct priv *p = (struct priv *) dev->priv;
+
+      if(csr0 & CSR0_BABL)
+        p->stats.tx_errors++;
+      if(csr0 & CSR0_MISS)
+        p->stats.rx_errors++;
+      if(csr0 & CSR0_MERR) {
+        writedatareg(CSR0_STOP);
+        writedatareg(CSR0_STRT);
+      }
+    }
+    if(csr0 & CSR0_RINT) /* RECV-int? */
+      ni65_recv_intr(dev,csr0);
+    if(csr0 & CSR0_TINT) /* XMIT-int? */
+      ni65_xmit_intr(dev,csr0);
   }
 
-  if(csr0 & CSR0_RINT) /* RECV-int? */
-  { 
-    recv_intr(dev);
+#ifdef RCV_PARANOIA_CHECK
+{
+  struct priv *p = (struct priv *) dev->priv;
+  int i,f=0;
+  for(i=0;i<RMDNUM;i++) {
+     struct rmd *rmdp = p->rmdhead + ((p->rmdnum - i - 1) & (RMDNUM-1));
+     if(! (rmdp->u.s.status & RCV_OWN) )
+        f = 1;
+     else if(f)
+        break;
   }
-  if(csr0 & CSR0_TINT) /* XMIT-int? */
-  {  
-    xmit_intr(dev);
+
+  if(i < RMDNUM) {
+    p->rmdnum = (p->rmdnum + 8 - i) & (RMDNUM - 1);
+    printk(KERN_ERR "%s: Ooops, receive ring currupted\n",dev->name);
+
+    ni65_recv_intr(dev,csr0);
   }
+}
+#endif
 
-  writedatareg(CSR0_INEA);  /* reenable inter. */
+  if(csr0 & (CSR0_RXON | CSR0_TXON) != (CSR0_RXON | CSR0_TXON) ) {
+    writedatareg(CSR0_STOP);
+    writedatareg(CSR0_STRT | CSR0_INEA);
+  }
+  else
+    writedatareg(CSR0_INEA);
   dev->interrupt = 0;
 
   return;
@@ -444,39 +527,45 @@ static void ni65_interrupt(int irq, struct pt_regs * regs)
  * We have received an Xmit-Interrupt ..
  * send a new packet if necessary
  */
-
-static void xmit_intr(struct device *dev)
+static void ni65_xmit_intr(struct device *dev,int csr0)
 {
-  int tmdstat;
-  struct tmd *tmdp;
   struct priv *p = (struct priv *) dev->priv;
 
-#ifdef NO_STATIC
-  struct sk_buff *skb;
-#endif
-
   while(p->xmit_queued)
   {
-    tmdp = p->tmdhead + p->tmdlast;
-    tmdstat = tmdp->u.s.status;
+    struct tmd *tmdp = p->tmdhead + p->tmdlast;
+    int tmdstat = tmdp->u.s.status;
+
     if(tmdstat & XMIT_OWN)
       break;
-#ifdef NO_STATIC
-    skb = (struct sk_buff *) p->tmdbufs[p->tmdlast];
-    dev_kfree_skb(skb,FREE_WRITE); 
+
+#ifdef XMT_VIA_SKB
+    if(p->tmd_skb[p->tmdlast]) {
+       dev_kfree_skb(p->tmd_skb[p->tmdlast],FREE_WRITE);
+       p->tmd_skb[p->tmdlast] = NULL;
+    }
 #endif
 
     if(tmdstat & XMIT_ERR)
     {
-      printk("%s: xmit-error: %04x %04x\n",dev->name,(int) tmdstat,(int) tmdp->status2);
-      if(tmdp->status2 & XMIT_TDRMASK
-        printk("%s: tdr-problems (e.g. no resistor)\n",dev->name);
-
+#if 0
+      if(tmdp->status2 & XMIT_TDRMASK && debuglevel > 3)
+        printk(KERN_ERR "%s: tdr-problems (e.g. no resistor)\n",dev->name);
+#endif
      /* checking some errors */
-      if(tmdp->status2 & XMIT_RTRY) 
+      if(tmdp->status2 & XMIT_RTRY)
         p->stats.tx_aborted_errors++;
-      if(tmdp->status2 & XMIT_LCAR) 
+      if(tmdp->status2 & XMIT_LCAR)
         p->stats.tx_carrier_errors++;
+      if(tmdp->status2 & (XMIT_BUFF | XMIT_UFLO )) {
+        p->stats.tx_fifo_errors++;
+        writedatareg(CSR0_STOP);
+        writedatareg(CSR0_STRT);
+        if(debuglevel > 1)
+          printk(KERN_ERR "%s: Xmit FIFO/BUFF error\n",dev->name);
+      }
+      if(debuglevel > 2)
+        printk(KERN_ERR "%s: xmit-error: %04x %02x-%04x\n",dev->name,csr0,(int) tmdstat,(int) tmdp->status2);
       p->stats.tx_errors++;
       tmdp->status2 = 0;
     }
@@ -487,7 +576,6 @@ static void xmit_intr(struct device *dev)
     if(p->tmdlast == p->tmdnum)
       p->xmit_queued = 0;
   }
-
   dev->tbusy = 0;
   mark_bh(NET_BH);
 }
@@ -496,66 +584,88 @@ static void xmit_intr(struct device *dev)
  * We have received a packet
  */
 
-static void recv_intr(struct device *dev)
+static void ni65_recv_intr(struct device *dev,int csr0)
 {
   struct rmd *rmdp; 
   int rmdstat,len;
-  struct sk_buff *skb,*skb1;
   struct priv *p = (struct priv *) dev->priv;
 
   rmdp = p->rmdhead + p->rmdnum;
   while(!( (rmdstat = rmdp->u.s.status) & RCV_OWN))
   {
-    if( (rmdstat & (RCV_START | RCV_END)) != (RCV_START | RCV_END) ) /* is packet start & end? */ 
+    if( (rmdstat & (RCV_START | RCV_END | RCV_ERR)) != (RCV_START | RCV_END) ) /* error or oversized? */ 
     {
-      if(rmdstat & RCV_START)
-      {
-        p->stats.rx_errors++;
-        p->stats.rx_length_errors++;
-        printk("%s: packet too long\n",dev->name);
+      if(!(rmdstat & RCV_ERR)) {
+        if(rmdstat & RCV_START)
+        {
+          p->stats.rx_length_errors++;
+          printk(KERN_ERR "%s: recv, packet too long: %d\n",dev->name,rmdp->mlen & 0x0fff);
+        }
+      }
+      else {
+        printk(KERN_ERR "%s: receive-error: %04x, lance-status: %04x/%04x\n",
+                dev->name,(int) rmdstat,csr0,(int) inw(PORT+L_DATAREG) );
+        if(rmdstat & RCV_FRAM)
+          p->stats.rx_frame_errors++;
+        if(rmdstat & RCV_OFLO)
+          p->stats.rx_over_errors++;
+        if(rmdstat & (RCV_OFLO | RCV_BUF_ERR) ) { 
+          writedatareg(CSR0_STOP);
+          writedatareg(CSR0_STRT);
+          if(debuglevel > 1)
+            printk(KERN_ERR "%s: Rcv FIFO/BUFF error.\n",dev->name);
+        }
+        if(rmdstat & RCV_CRC)  p->stats.rx_crc_errors++;
       }
       rmdp->u.s.status = RCV_OWN; /* change owner */
-    }
-    else if(rmdstat & RCV_ERR)
-    {
-      printk("%s: receive-error: %04x\n",dev->name,(int) rmdstat );
       p->stats.rx_errors++;
-      if(rmdstat & RCV_FRAM) p->stats.rx_frame_errors++;
-      if(rmdstat & RCV_OFLO) p->stats.rx_over_errors++;
-      if(rmdstat & RCV_CRC)  p->stats.rx_crc_errors++;
-      rmdp->u.s.status = RCV_OWN;
-      printk("%s: lance-status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
     }
-    else
+    else if( (len = (rmdp->mlen & 0x0fff) - 4) >= 60)
     {
-      len = (rmdp->mlen & 0x0fff) - 4; /* -4: ignore FCS */
-      skb = dev_alloc_skb(R_BUF_SIZE);
-      if(skb != NULL)
+#ifdef RCV_VIA_SKB
+      struct sk_buff *skb = dev_alloc_skb(R_BUF_SIZE+2);
+#else
+      struct sk_buff *skb = dev_alloc_skb(len+2);
+#endif
+      if(skb)
       {
-        if( (unsigned long) (skb->data + R_BUF_SIZE) & 0xff000000) {
-          memcpy(skb_put(skb,len),p->recv_skb[p->rmdnum]->data,len);
-         skb1 = skb;
+        skb_reserve(skb,2);
+       skb->dev = dev;
+#ifdef RCV_VIA_SKB
+        if( (unsigned long) (skb->data + R_BUF_SIZE) > 0x1000000) {
+          skb_put(skb,len);
+          eth_copy_and_sum(skb, (unsigned char *)(p->recv_skb[p->rmdnum]->data),len,0);
         }
         else {
-          skb1 = p->recv_skb[p->rmdnum];
+          struct sk_buff *skb1 = p->recv_skb[p->rmdnum];
+          skb_put(skb,R_BUF_SIZE);
           p->recv_skb[p->rmdnum] = skb;
-          rmdp->u.buffer = (unsigned long) skb_put(skb1,len);
+          rmdp->u.buffer = (unsigned long) skb->data;
+          skb = skb1;
+          skb_trim(skb,len);
         }
+#else
+        skb_put(skb,len);
+        eth_copy_and_sum(skb, (unsigned char *) p->recvbounce[p->rmdnum],len,0);
+#endif
         rmdp->u.s.status = RCV_OWN;
-        rmdp->mlen = 0;   /* not necc ???? */
-        skb1->dev = dev;
         p->stats.rx_packets++;
-        skb1->protocol=eth_type_trans(skb1,dev);
-        netif_rx(skb1);
+        skb->protocol=eth_type_trans(skb,dev);
+        netif_rx(skb);
       }
       else
       {
         rmdp->u.s.status = RCV_OWN;
-        printk("%s: can't alloc new sk_buff\n",dev->name);
+        printk(KERN_ERR "%s: can't alloc new sk_buff\n",dev->name);
         p->stats.rx_dropped++;
       }
     }
-    p->rmdnum++; p->rmdnum &= RMDNUM-1;
+    else {
+      rmdp->u.s.status = RCV_OWN;
+      printk(KERN_INFO "%s: received runt packet\n",dev->name);
+      p->stats.rx_errors++;
+    }
+    p->rmdnum = (p->rmdnum + 1) & (RMDNUM-1);
     rmdp = p->rmdhead + p->rmdnum;
   }
 }
@@ -563,26 +673,23 @@ static void recv_intr(struct device *dev)
 /*
  * kick xmitter ..
  */
-
 static int ni65_send_packet(struct sk_buff *skb, struct device *dev)
 {
   struct priv *p = (struct priv *) dev->priv;
-  struct tmd *tmdp;
 
   if(dev->tbusy)
   {
     int tickssofar = jiffies - dev->trans_start;
-    if (tickssofar < 25)
+    if (tickssofar < 5)
       return 1;
 
-    printk("%s: xmitter timed out, try to restart!\n",dev->name);
-    am7990_reinit(dev);
+    printk(KERN_ERR "%s: xmitter timed out, try to restart!\n",dev->name);
+    ni65_am7990_reinit(dev);
     dev->tbusy=0;
     dev->trans_start = jiffies;
   }
 
-  if(skb == NULL)
-  {
+  if(skb == NULL) {
     dev_tint(dev);
     return 0;
   }
@@ -590,44 +697,50 @@ static int ni65_send_packet(struct sk_buff *skb, struct device *dev)
   if (skb->len <= 0)
     return 0;
 
-  if (set_bit(0, (void*)&dev->tbusy) != 0)
-  {
-     printk("%s: Transmitter access conflict.\n", dev->name);
+  if (set_bit(0, (void*)&dev->tbusy) != 0) {
+     printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name);
      return 1;
   }
-  if(set_bit(0,(void*) &p->lock) != 0)
-  {
-    printk("%s: Queue was locked!\n",dev->name);
-    return 1;
+  if (set_bit(0, (void*)&p->lock)) {
+       printk(KERN_ERR "%s: Queue was locked.\n", dev->name);
+       return 1;
   }
 
   {
     short len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+    struct tmd *tmdp = p->tmdhead + p->tmdnum;
+    long flags;
 
-    tmdp = p->tmdhead + p->tmdnum;
-
-#ifdef NO_STATIC
-    tmdp->u.buffer = (unsigned long) (skb->data);
-    p->tmdbufs[p->tmdnum] = skb;
-#else
-    memcpy((char *) (tmdp->u.buffer & 0x00ffffff),(char *)skb->data,skb->len);
-    dev_kfree_skb (skb, FREE_WRITE);
+#ifdef XMT_VIA_SKB
+    if( (unsigned long) (skb->data + skb->len) > 0x1000000) {
+#endif
+      tmdp->u.buffer = (unsigned long ) p->tmdbounce[p->tmdnum]; 
+      memcpy((char *) tmdp->u.buffer,(char *)skb->data,
+               (skb->len > T_BUF_SIZE) ? T_BUF_SIZE : skb->len);
+      dev_kfree_skb (skb, FREE_WRITE);
+#ifdef XMT_VIA_SKB
+    }
+    else {
+      tmdp->u.buffer = (unsigned long) skb->data;
+      p->tmd_skb[p->tmdnum] = skb;
+    }
 #endif
     tmdp->blen = -len;
-    tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END;
 
+    save_flags(flags);
     cli();
-    p->xmit_queued = 1;
+
+    tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END;
     writedatareg(CSR0_TDMD | CSR0_INEA); /* enable xmit & interrupt */
-    p->tmdnum++; p->tmdnum &= TMDNUM-1;
-    if( !((p->tmdhead + p->tmdnum)->u.s.status & XMIT_OWN) ) 
-      dev->tbusy = 0;
-    p->lock = 0;
-    sti();
 
+    p->xmit_queued = 1;
+    p->tmdnum = (p->tmdnum + 1) & (TMDNUM-1);
+
+    dev->tbusy = (p->tmdnum == p->tmdlast) ? 1 : 0;
+    p->lock = 0;
     dev->trans_start = jiffies;
 
+    restore_flags(flags);
   }
 
   return 0;
@@ -640,6 +753,9 @@ static struct enet_statistics *ni65_get_stats(struct device *dev)
 
 static void set_multicast_list(struct device *dev)
 {
+       if(!ni65_am7990_reinit(dev))
+               printk(KERN_ERR "%s: Can't switch card into MC mode!\n",dev->name);
+       dev->tbusy = 0;
 }
 
 /*
index 144523fa26ef5fd3b0ebc07ac4589bb70213319b..eaddbf28fb3c85ba5528f3ce6ea44012857bccd0 100644 (file)
  * transmit status (2) (valid if XMIT_ERR == 1)
  */
 
-#define XMIT_RTRY      0x0200  /* Failed after 16 retransmissions  */
-#define XMIT_LCAR      0x0400  /* Loss of Carrier */
+#define XMIT_TDRMASK    0x03ff  /* time-domain-reflectometer-value */
+#define XMIT_RTRY      0x0400  /* Failed after 16 retransmissions  */
+#define XMIT_LCAR      0x0800  /* Loss of Carrier */
 #define XMIT_LCOL      0x1000  /* Late collision */
 #define XMIT_RESERV    0x2000  /* Reserved */
 #define XMIT_UFLO      0x4000  /* Underflow (late memory) */
 #define XMIT_BUFF      0x8000  /* Buffering error (no ENP) */
-#define XMIT_TDRMASK    0x003f  /* time-domain-reflectometer-value */
 
 struct init_block 
 {
   unsigned short mode;
   unsigned char eaddr[6];
   unsigned char filter[8];
-  unsigned short rrplow;   /* receive ring pointer (align 8) */
-  unsigned short rrphigh;  /* bit 13-15: number of rmd's (power of 2) */
-  unsigned short trplow;   /* transmit ring pointer (align 8) */
-  unsigned short trphigh;  /* bit 13-15: number of tmd's (power of 2) */
+  /* bit 29-31: number of rmd's (power of 2) */
+  unsigned long rrp;   /* receive ring pointer (align 8) */
+  /* bit 29-31: number of tmd's (power of 2) */
+  unsigned long trp;   /* transmit ring pointer (align 8) */
 };
 
 struct rmd /* Receive Message Descriptor */
index ff8b697f0d9ecb5ee67e1db11acef0bc8b9c1219..b112469d3c8cbd1a109e3f7d11f8f6f49f3f34c5 100644 (file)
@@ -471,8 +471,7 @@ static void slip_write_wakeup(struct tty_struct *tty)
                 * transmission of another packet */
                sl->tx_packets++;
                tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-               if (test_bit(0, (void *) &sl->dev->tbusy)) /* add by VSV */
-                       sl_unlock(sl);
+               sl_unlock(sl);
                mark_bh(NET_BH);
                return;
        }
@@ -803,6 +802,13 @@ slip_close(struct tty_struct *tty)
        
        tty->disc_data = 0;
        sl->tty = NULL;
+       /* VSV = very important to remove timers */
+#ifdef CONFIG_SLIP_SMART
+       if (sl->keepalive)
+               (void)del_timer (&sl->keepalive_timer);
+       if (sl->outfill)
+               (void)del_timer (&sl->outfill_timer);
+#endif
        sl_free(sl);
        unregister_netdev(sl->dev);
        MOD_DEC_USE_COUNT;
@@ -1116,7 +1122,7 @@ slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
        /* VSV changes start here */
         case SIOCSKEEPALIVE:
                 if (sl->keepalive)
-                        del_timer (&sl->keepalive_timer);
+                        (void)del_timer (&sl->keepalive_timer);
                err = verify_area(VERIFY_READ, arg, sizeof(int));
                if (err)  {
                        return -err;
@@ -1329,7 +1335,9 @@ cleanup_module(void)
        {
                for (i = 0; i < slip_maxdev; i++)  
                {
-                       if (slip_ctrls[i] != NULL) 
+                       if (slip_ctrls[i]->dev.start)
+                       /* VSV = if dev->start==0, then device
+                       unregistred while close proc. */ 
                        {
                                unregister_netdev(&(slip_ctrls[i]->dev));
                                kfree(slip_ctrls[i]);
@@ -1355,7 +1363,7 @@ static void sl_outfill(unsigned long sls)
 {
        struct slip *sl=(struct slip *)sls;
 
-       if(sls==NULL)
+       if(sls==0L)
                return;
 
        if(sl->outfill)
@@ -1372,7 +1380,6 @@ static void sl_outfill(unsigned long sls)
                        if (!test_bit(0, (void *) &sl->dev->tbusy))
                        { 
                                /* if device busy no outfill */
-                               sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
                                sl->tty->driver.write(sl->tty, 0, &s, 1);
                        }
                }
index 34dd617c5200a5e92232a3d06bbee2428c765e1c..858b44c4b5736a735e2dc784203527f591027a57 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: advansys.c,v 1.11 1996/01/16 22:39:19 bobf Exp bobf $ */
+/* $Id: advansys.c,v 1.12 1996/02/23 20:48:27 bobf Exp bobf $ */
 /*
  * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
  * 
  * bobf@advansys.com (Bob Frey)
  */
 
-/* The driver has been tested with Linux 1.2.1 and 1.3.57 kernels. */
-#define ASC_VERSION "1.2"      /* AdvanSys Driver Version */
+/* The driver has been tested with Linux v1.2.1 and v1.3.57 kernels. */
+#define ASC_VERSION "1.3"      /* AdvanSys Driver Version */
 
 /*
 
   Documentation for the AdvanSys Driver
 
   A. Adapters Supported by this Driver
-  B. Linux 1.2.X - Directions for Adding the AdvanSys Driver
-  C. Linux 1.3.X - Directions for Adding the AdvanSys Driver
+  B. Linux v1.2.X - Directions for Adding the AdvanSys Driver
+  C. Linux v1.3.X - Directions for Adding the AdvanSys Driver
   D. Source Comments
   E. Driver Compile Time Options and Debugging
   F. Driver LILO Option
        no BIOS so it cannot control a boot device, but it can control
        any secondary devices.
  
-  B. Linux 1.2.X - Directions for Adding the AdvanSys Driver
+  B. Linux v1.2.X - Directions for Adding the AdvanSys Driver
+
+     These directions apply to v1.2.1. For versions that follow v1.2.1
+     but precede v1.3.57 some of the changes for Linux v1.3.X listed
+     below may need to be modified or included.
  
      There are two source files: advansys.h and advansys.c. Copy
      both of these files to the directory /usr/src/linux/drivers/scsi.
              { "advansys=", advansys_setup },
           #endif
 
-     5. If you have the HP 4020i CD-R driver and Linux 1.2.X you should
+     5. If you have the HP 4020i CD-R driver and Linux v1.2.X you should
         add a fix to the CD-ROM target driver. This fix will allow
-        you to mount CDs with the iso9660 file system. Linux 1.3.X
+        you to mount CDs with the iso9660 file system. Linux v1.3.X
         already has this fix. In the file /usr/src/linux/drivers/scsi/sr.c
         and function get_sectorsize() after the line:
 
         'make modules_install'. Use 'insmod' and 'rmmod' to install
         and remove advansys.o.
  
-  C. Linux 1.3.X - Directions for Adding the AdvanSys Driver
+  C. Linux v1.3.X - Directions for Adding the AdvanSys Driver
+
+     These directions apply to v1.3.57. For versions that precede v1.3.57
+     some of these changes may need to be modified or eliminated. Beginning
+     with v1.3.58 this driver is included with the Linux distribution.
 
      There are two source files: advansys.h and advansys.c. Copy
      both of these files to the directory /usr/src/linux/drivers/scsi.
         The following command line will look for an adapter at 0x330
         and set the debug level to 2.
 
-           linux advansys=0x330,0x0,0x0,0x0,0xdeb2
+           linux advansys=0x330,0,0,0,0xdeb2
 
         If the driver is built as a loadable module this variable can be
         defined when the driver is loaded. The following insmod command
  
      2. ADVANSYS_STATS - enable statistics and tracing
  
-        For Linux 1.2.X if ADVANSYS_STATS_1_2_PRINT is defined every
+        For Linux v1.2.X if ADVANSYS_STATS_1_2_PRINT is defined every
         10,000 I/O operations the driver will print statistics to the
         console. This value can be changed by modifying the constant
         used in advansys_queuecommand(). ADVANSYS_STATS_1_2_PRINT is
         off by default.
 
-        For Linux 1.3.X statistics can be accessed by reading the
-        /proc/scsi/advansys/[0-9] files.
+        For Linux v1.3.X statistics can be accessed by reading the
+        /proc/scsi/advansys/[0-(ASC_NUM_BOARD_SUPPORTED-1)] files.
 
         Note: these statistics are currently maintained on a global driver
         basis and not per board.
             recognized on some PCI motherboards.
          3. Add support for the ABP-5140 PnP ISA card.
          4. Fix check condition return status.
-         5. Add conditionally compiled code for Linux 1.3.X.
+         5. Add conditionally compiled code for Linux v1.3.X.
+
+     2/23/96 1.3:
+         1. Fix problem in advansys_biosparam() that resulted in the
+            wrong drive geometry being returned for drives > 1GB with
+            extended translation enabled.
+         2. Add additional tracing during device initialization.
+         3. Change code that only applies to ISA PnP adapter.
+         4. Eliminate 'make dep' warning.
+         5. Try to fix problem with handling resets by increasing their
+            timeout value.
         
   H. Known Problems or Issues
 
         the timeout value. This gives the driver more time to perform
         its own initialization for the board and each device. The timeout
         value is only changed on the first scsi command for each device
-        and never thereafter.
+        and never thereafter. The same change is made for reset commands.
+
+     3. The driver occasionally enters a loop handling reset requests. It
+        isn't clear yet whether this is a bug in the upper or mid-level
+        scsi modules or in this driver.
 
   I. Credits
 
-     Nathan Hartwell (mage@cdc3.cdc.net) provided the directions and
-     and basis for the Linux 1.3.X changes which were included in the
+     Nathan Hartwell <mage@cdc3.cdc.net> provided the directions and
+     and basis for the Linux v1.3.X changes which were included in the
      1.2 release.
 
+     Thomas E Zerucha <zerucha@shell.portal.com> pointed out the bug
+     in advansys_biosparam() which was fixed the 1.3 release.
+
   J. AdvanSys Contact Information
  
      Mail:                   Advanced System Products, Inc.
  */
 
 /*
- * The driver can be used in Linux 1.2.X or 1.3.X.
+ * The driver can be used in Linux v1.2.X or v1.3.X.
  */
 #if !defined(LINUX_1_2) && !defined(LINUX_1_3)
 #ifndef LINUX_VERSION_CODE
  * --- Linux Include Files 
  */
 
-#ifdef MODULE
 #ifdef LINUX_1_3
-#include <linux/autoconf.h>
-#endif /* LINUX_1_3 */ 
+#ifdef MODULE
 #include <linux/module.h>
 #endif /* MODULE */
+#endif /* LINUX_1_3 */
 #include <linux/string.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/malloc.h>
-#include <linux/config.h>
 #ifdef LINUX_1_3
 #include <linux/proc_fs.h>
 #endif /* LINUX_1_3 */ 
@@ -2569,7 +2592,7 @@ STATIC int                interrupts_enabled(void);
 
 #ifdef LINUX_1_3
 /*
- * advansys_proc_info() - /proc/scsi/advansys/[0-ASC_NUM_BOARD_SUPPORTED]
+ * advansys_proc_info() - /proc/scsi/advansys/[0-(ASC_NUM_BOARD_SUPPORTED-1)]
  *
  * *buffer: I/O buffer
  * **start: if inout == FALSE pointer into buffer where user read should start
@@ -3192,10 +3215,10 @@ advansys_info(struct Scsi_Host *shp)
 /*
  * advansys_command()
  *
- * Polled-I/O. Apparently host driver shouldn't return until
+ * Polled-I/O. Apparently host drivers shouldn't return until
  * command is finished.
  *
- * XXX - Can host driver block here instead of spinning on command status?
+ * XXX - Can host drivers block here instead of spinning on command status?
  */
 int
 advansys_command(Scsi_Cmnd *scp)
@@ -3230,7 +3253,7 @@ advansys_queuecommand(Scsi_Cmnd *scp, void (*done)(Scsi_Cmnd *))
 #ifdef LINUX_1_2
        /*
         * For LINUX_1_3, if statistics are enabled they can be accessed
-        * by reading /proc/scsi/advansys/[0-9].
+        * by reading /proc/scsi/advansys/[0-(ASC_NUM_BOARD_SUPPORTED-1)].
         */
 #ifdef ADVANSYS_STATS_1_2_PRINT
        /* Display statistics every 10000 commands. */
@@ -3366,18 +3389,24 @@ advansys_reset(Scsi_Cmnd *scp)
                                tscp->scsi_done(tscp);
                        }
                }
+               /*
+                * XXX - Host drivers should not modify the timeout field.
+                * Allow the SCSI bus reset more time to complete.
+                */
+               scp->timeout += 2000;   /* Add 5 seconds to the request timeout. */
+
                /* Must enable interrupts for AscResetSB() */
                sti();
                boardp = &ASC_BOARD(scp->host)->board;
                scp->result = HOST_BYTE(DID_RESET);
                switch (AscResetSB(boardp)) {
                case ASC_TRUE:
-                       ASC_DBG(1, "advansys_abort: AscResetSB() TRUE\n");
+                       ASC_DBG(1, "advansys_reset: AscResetSB() TRUE\n");
                        ret = SCSI_RESET_SUCCESS;
                        break;
                case ASC_ERROR:
                default:
-                       ASC_DBG(1, "advansys_abort: AscResetSB() ERROR\n");
+                       ASC_DBG(1, "advansys_reset: AscResetSB() ERROR\n");
                        ret = SCSI_RESET_ERROR;
                        break;
                }
@@ -3410,7 +3439,7 @@ advansys_biosparam(Disk *dp, kdev_t dep, int ip[])
        if ((ASC_BOARD(dp->device->host)->board.dvc_cntl & ASC_CNTL_BIOS_GT_1GB) &&
                dp->capacity > 0x200000) {
                        ip[0] = 255;
-                       ip[1] = 64;
+                       ip[1] = 63;
        } else {
                        ip[0] = 64;
                        ip[1] = 32;
@@ -7926,7 +7955,7 @@ AscInitFromEEP(
                        (asc_dvc->cfg->pci_device_id == ASC_PCI_DEVICE_ID_REV_B)) {
                        asc_dvc->pci_fix_asyn_xfer = ASC_ALL_DEVICE_BIT_SET;
                }
-       } else if (asc_dvc->bus_type & ASC_IS_ISAPNP) {
+       } else if ((asc_dvc->bus_type & ASC_IS_ISAPNP) == ASC_IS_ISAPNP) {
 
                if (AscGetChipVersion(iop_base, asc_dvc->bus_type)
                        == ASC_CHIP_VER_ASYN_BUG) {
@@ -8447,11 +8476,28 @@ AscInitPollTarget(
                        AscDispInquiry(tid_no, lun, inq);
 #endif
 
+                       ASC_DBG_PRT_INQUIRY(2, inq, sizeof(ASC_SCSI_INQUIRY));
+
                        if (lun == 0) {
 
+                               ASC_DBG1(2, "AscInitPollTarget: vendor_id: \"%.8s\"\n",
+                                       inq->vendor_id);
+                               ASC_DBG1(2, "AscInitPollTarget: product_id: \"%.16s\"\n",
+                                       inq->product_id);
+                               ASC_DBG1(2, "AscInitPollTarget: product_rev_level: \"%.4s\"\n",
+                                       inq->product_rev_level);
+
+                               ASC_DBG1(2, "AscInitPollTarget: byte3.rsp_data_fmt %x\n",
+                                       inq->byte3.rsp_data_fmt);
+                               ASC_DBG1(2, "AscInitPollTarget: byte3.ansi_apr_ver %x\n",
+                                       inq->byte2.ansi_apr_ver);
+                                       
                                if ((inq->byte3.rsp_data_fmt >= 2) ||
                                        (inq->byte2.ansi_apr_ver >= 2)) {
 
+                                       ASC_DBG1(2, "AscInitPollTarget: byte7.CmdQue %x\n",
+                                               inq->byte7.CmdQue);
+
                                        if (inq->byte7.CmdQue) {
                                                asc_dvc->cfg->can_tagged_qng |= tid_bits;
                                                if (asc_dvc->cfg->cmd_qng_enabled & tid_bits) {
@@ -8460,6 +8506,9 @@ AscInitPollTarget(
                                                          asc_dvc->cfg->max_tag_qng[tid_no];
                                                }
                                        }
+                                       ASC_DBG1(2, "AscInitPollTarget: byte7.Sync %x\n",
+                                               inq->byte7.Sync);
+
                                        if (!inq->byte7.Sync) {
 
                                                asc_dvc->init_sdtr &= ~tid_bits;
index 2af6747a9d7f0285e52c3219945c57f7d6ab0b8d..66852d4827ae351d31b884acd1b7b10d575e2583 100644 (file)
@@ -1,4 +1,7 @@
 *********************************************************
 * Readme.cards (this directory) contains some card     *
 * specific instructions.                               *
+*                                                      *
+* DON'T USE PROGRAMS FROM SND_UTIL PACKAGE EARLIER THAN *
+* snd-util-3.5 WITH THIS SOUND DRIVER VERSION.          *
 *********************************************************
index 2f62e9bcc940b4e6c214b2fa01970250e941dd00..b4b35ada707d2833cb2b8fc727e2382f4a27815a 100644 (file)
@@ -1,2 +1,2 @@
-3.5-beta2
+3.5-beta7
 0x030505
index 5e3fdba21a7e21479a3a8302ae7e3602209d2dff..78edc3239cbb34ba7caf75c22adfd9d9615ee8eb 100644 (file)
@@ -1,6 +1,29 @@
-Changelog for version 3.5-beta2
+Changelog for version 3.5-beta7
 -------------------------------
 
+Since 3.5-beta6
+- Fixed bugs in mmap() support.
+- Minor changes to Maui driver.
+
+Since 3.5-beta5
+- Fixed crash after recording with ESS688. It's generally a good
+  idea to stop inbound DMA transfers before freeing the memory
+  buffer. 
+- Fixed handling of AD1845 codec (for example Shuttle Sound System).
+- Few other fixes.
+
+Since 3.5-beta4
+- Fixed bug in handling of uninitialized instruments with GUS.
+
+Since 3.5-beta3
+- Few changes which decrease popping at end/beginning of audio playback.
+
+Since 3.5-beta2
+- Removed MAD16+CS4231 hack made in previous version since it didn't
+  help.
+- Fixed the above bug in proper way and in proper place. Many thanks
+  to James Hightower.
+
 Since 3.5-beta1
 - Bugfixes.
 - Full duplex audio with MAD16+CS4231 may work now. The driver configures
index faaf1e8144e20e8946d9d69f9499cf388ec12dc4..202e180a6a6f8c41d2a8e52aa9b3e1471bae0d4e 100644 (file)
@@ -25,9 +25,15 @@ OBJS   = audio.o dmabuf.o sb_dsp.o \
         mad16.o mad16_sb_midi.o cs4232.o maui.o
 endif
 
+ifndef TOPDIR
+TOPDIR=/usr/src/linux
+endif
+
 
 build:
+       @echo Compiling modularized sound driver
        @make sound.o
+       @echo Sound module compiled.
 
 install:       sound.o
        cp sound.o $(MODULEDIR)
index 01de214e99d822a7be9ee6c3a72d3514a527d68b..704ff5cb5fdce4b633661f41fc80bcaa36cc2e64 100644 (file)
@@ -1,5 +1,5 @@
 
-Version 3.5-beta2 release notes
+Version 3.5-beta6 release notes
 -------------------------------
 
 IMPORTANT! This version of the driver is compatible only with Linux versions
@@ -38,7 +38,7 @@ them are still on my mailbox and they should be included in versions
 after v3.5 (I will not add aditional features before v3.5 is ready).
 
    ====================================================
--  THIS VERSION ____REQUIRES____ Linux 1.3.58 OR LATER.
+-  THIS VERSION ____REQUIRES____ Linux 1.3.64 OR LATER.
    ====================================================
 
 -  THIS VERSION MAY NOT WORK WITH Linux VERSIONS RELEASED
@@ -46,7 +46,7 @@ after v3.5 (I will not add aditional features before v3.5 is ready).
    your kernel version, please use the sound driver version
    included in your kernel.
 
-You will need the snd-util-3.0.tar.gz and snd-data-0.1.tar.Z
+You may need the snd-util-3.5.tar.gz and snd-data-0.1.tar.Z
 packages to use this driver. They should be in the same
 ftp site or BBS from where you got this driver. For
 example at nic.funet.fi:pub/OS/Linux/*.
@@ -139,6 +139,7 @@ contributors. (I could have forgotten some names.)
        Davor Jadrijevic MAD16 support
        Gregor Hoffleit Mozart support
        Riccardo Facchetti Audio Excel DSP 16 (aedsp16) support
+       James Hightower Spotting a tiny but important bug in CS423x support.
 
 There are propably many other names missing. If you have sent me some
 patches and your name is not in the above list, please inform me.
index 779addb7d1349d257ab4c0eea3c4d4eb194385ed..0d7822104018265ba45e21ffe5ed1f43945d8d47 100644 (file)
@@ -4,6 +4,39 @@ Configuring version 3.5 (for Linux) with some most common soundcards
 NOTE! This document may contain some error. Please inform me
       if you find any mistakes.
 
+Cards that are not (fully) supported by this driver
+---------------------------------------------------
+
+There are many soundcards which don't work with this driver
+version (v3.5). Support for some of them is expected to be
+available during/after summer 1996 (in version 3.6). Please check
+http://personal.eunet.fi/pp/voxware for latest news. Please don't
+mail me and ask about these cards. The unsupported cards are:
+
+       - All PnP soundcards (SB PnP, GUS PnP, Soundscape PnP etc.)
+               (SB PnP in first 3.6-alpha version (Apr?), GUS PnP bit later,
+               Soundscape PnP propably much later, others ???).
+       - Mwave soundcards and motherboards
+               (Version 3.6 or 3.7. Depends on how fast I get a Mwave
+               card and suitable documents for it).
+       - Emu8k (SB 32/AWE)
+               (Propably not before Nov/Dec 96. I know the unofficial
+               AWE programmers guide so don't send me more copies of it).
+       - Diamond Edge 3D
+               (ASAP. In practice this may take relatively long time).
+       - Compaq Deskpro
+               (Version 3.6???)
+       - Miro soundcards
+               (Early 3.6-alpha versions)
+       - OPTi 82C930 based soundcards
+               (Early 3.6-alpha versions?)
+       - Sound Galaxy Washington/Waverider
+               (3.6-alpha versions. Can't promise the waverider synth).
+       - TB Maui
+               (v3.6)
+       - Yamaha OPL4 (on cards having _RAM_ for samples)
+               (Late 96?. Works as OPL3 with current driver versions)
+
 Read this before trying to configure the driver
 -----------------------------------------------
 
@@ -130,7 +163,6 @@ MAD16 and Mozart based cards
 Audio Excell DSP16 
        Support for this card is made by Riccardo Faccetti
        (riccardo@cdc8g5.cdc.polimi.it). See aedsp16.c for more info.
-       (This driver is not functional in 3.5 versions of this driver).
        
 Crystal CS4232 based cards such as AcerMagic S23 and many PC motherboards.
        CS4232 is a PnP multimedia chip which contains a CS3231A codec,
index a734d099cd2bdab317ca6c9ccfb96c81c682140c..4139444bf8074d63d3f48a3ae559537dfb3cc210 100644 (file)
@@ -55,6 +55,7 @@ typedef struct
     int             dual_dma;  /* 1, when two DMA channels allocated */
     unsigned char   MCE_bit;
     unsigned char   saved_regs[16];
+    int             debug_flag;
 
     int             speed;
     unsigned char   speed_bits;
@@ -562,6 +563,7 @@ ad1848_open (int dev, int mode)
 
   devc = (ad1848_info *) audio_devs[dev]->devc;
 
+
   save_flags (flags);
   cli ();
   if (devc->opened)
@@ -663,7 +665,7 @@ set_speed (ad1848_info * devc, int arg)
        arg = 50000;
 
       devc->speed = arg;
-      devc->speed_bits = speed_table[selected].bits;
+      devc->speed_bits = speed_table[3].bits;
       return devc->speed;
     }
 
@@ -864,6 +866,7 @@ ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dm
 
   if (dma_restart)
     {
+      ad_write (devc, 9, ad_read (devc, 9) & ~0x01);   /* Playback disable */
       /* ad1848_halt (dev); */
       DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
     }
@@ -871,7 +874,6 @@ ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dm
   ad_write (devc, 15, (unsigned char) (cnt & 0xff));
   ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
 
-  /* ad_write (devc, 9, ad_read (devc, 9) | 0x01); *//* Playback enable */
   ad_unmute (devc);
 
   devc->xfer_count = cnt;
@@ -916,6 +918,7 @@ ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma
   if (dma_restart)
     {
       /* ad1848_halt (dev); */
+      ad_write (devc, 9, ad_read (devc, 9) & ~0x02);   /* Capture disable */
       DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
     }
 
@@ -931,7 +934,6 @@ ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma
       ad_write (devc, 30, (unsigned char) ((cnt >> 8) & 0xff));
     }
 
-  /*  ad_write (devc, 9, ad_read (devc, 9) | 0x02); *//* Capture enable */
   ad_unmute (devc);
 
   devc->xfer_count = cnt;
@@ -966,7 +968,9 @@ ad1848_prepare_for_IO (int dev, int bsize, int bcount)
       ad_write (devc, 23, devc->speed & 0xff); /* Speed LSB */
     }
 
-  if (fs == (old_fs = ad_read (devc, 8)))      /* No change */
+  old_fs = ad_read (devc, 8);
+
+  if (fs == old_fs)            /* No change */
     {
       restore_flags (flags);
       devc->xfer_count = 0;
@@ -974,6 +978,7 @@ ad1848_prepare_for_IO (int dev, int bsize, int bcount)
     }
 
   ad_enter_MCE (devc);         /* Enables changes to the format select reg */
+
   ad_write (devc, 8, fs);
   /*
    * Write to I8 starts resyncronization. Wait until it completes.
@@ -1027,6 +1032,7 @@ ad1848_halt (int dev)
   unsigned long   flags;
   int             timeout;
 
+
   save_flags (flags);
   cli ();
 
@@ -1035,16 +1041,16 @@ ad1848_halt (int dev)
   ad_write (devc, 9, ad_read (devc, 9) & ~0x03);       /* Stop DMA */
   ad_write (devc, 9, ad_read (devc, 9) & ~0x03);       /* Stop DMA */
 
-  ad_write (devc, 15, 0);      /* Clear DMA counter */
+  ad_write (devc, 15, 4);      /* Clear DMA counter */
   ad_write (devc, 14, 0);      /* Clear DMA counter */
 
   if (devc->mode != MD_1848)
     {
-      ad_write (devc, 30, 0);  /* Clear DMA counter */
+      ad_write (devc, 30, 4);  /* Clear DMA counter */
       ad_write (devc, 31, 0);  /* Clear DMA counter */
     }
 
-  for (timeout = 0; timeout < 1000 && !(inb (io_Status (devc)) & 0x80);
+  for (timeout = 0; timeout < 10000 && !(inb (io_Status (devc)) & 0x80);
        timeout++);             /* Wait for interrupt */
 
   ad_write (devc, 9, ad_read (devc, 9) & ~0x03);       /* Stop DMA */
@@ -1076,6 +1082,9 @@ ad1848_halt_input (int dev)
   ad_write (devc, 9, ad_read (devc, 9) & ~0x02);       /* Stop capture */
 
 
+  outb (0, io_Status (devc));  /* Clear interrupt status */
+  outb (0, io_Status (devc));  /* Clear interrupt status */
+
   devc->irq_mode &= ~PCM_ENABLE_INPUT;
 
   restore_flags (flags);
@@ -1100,6 +1109,9 @@ ad1848_halt_output (int dev)
   ad_write (devc, 9, ad_read (devc, 9) & ~0x01);       /* Stop playback */
 
 
+  outb (0, io_Status (devc));  /* Clear interrupt status */
+  outb (0, io_Status (devc));  /* Clear interrupt status */
+
   devc->irq_mode &= ~PCM_ENABLE_OUTPUT;
 
   restore_flags (flags);
@@ -1160,6 +1172,7 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
   devc->chip_name = "AD1848";
   devc->mode = MD_1848;                /* AD1848 or CS4248 */
   devc->osp = osp;
+  devc->debug_flag = 0;
 
   /*
      * Check that the I/O address is in use.
@@ -1434,11 +1447,9 @@ ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture
       for (i = 16; i < 32; i++)
        ad_write (devc, i, init_values[i]);
 
-      if (devc->mode == MD_4231A || devc->mode == MD_4232)
-       ad_write (devc, 9, init_values[9] | 0x18);      /* Enable full calibration */
 
       if (devc->mode == MD_1845)
-       ad_write (devc, 27, init_values[27] | 0x08);    /* Alternate freq select enabled */
+       ad_write (devc, 27, ad_read (devc, 27) | 0x08);         /* Alternate freq select enabled */
     }
   else
     {
@@ -1464,7 +1475,7 @@ ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture
       if (irq > 0)
        {
          audio_devs[my_dev]->devc = devc;
-         irq2dev[irq] = my_dev;
+         irq2dev[irq] = devc->dev_no = my_dev;
          if (snd_set_irq_handler (devc->irq, ad1848_interrupt,
                                   audio_devs[my_dev]->name,
                                   devc->osp) < 0)
@@ -1549,7 +1560,7 @@ ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int shar
   int             i, dev = 0;
   ad1848_info    *devc = NULL;
 
-  for (i = 0; devc == NULL && nr_ad1848_devs; i++)
+  for (i = 0; devc == NULL && i < nr_ad1848_devs; i++)
     if (dev_info[i].base == io_base)
       {
        devc = &dev_info[i];
index 6f5361b17b991782b2ced8625841a758b99aa3b2..b66110262435eef4052cf0429cc5a39671797f2f 100644 (file)
@@ -67,7 +67,7 @@ set_format (int dev, long fmt)
        else
          fmt = AFMT_U8;        /* This is always supported */
 
-      audio_format[dev] = DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) (long) fmt, 1);
+      audio_format[dev] = DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) fmt, 1);
     }
 
   if (local_conversion[dev])   /* This shadows the HW format */
@@ -145,6 +145,9 @@ audio_release (int dev, struct fileinfo *file)
   dev = dev >> 4;
   mode = file->mode & O_ACCMODE;
 
+  audio_devs[dev]->dmap_out->closing = 1;
+  audio_devs[dev]->dmap_in->closing = 1;
+
   sync_output (dev);
 
   if (audio_devs[dev]->coproc)
@@ -366,7 +369,7 @@ audio_ioctl (int dev, struct fileinfo *file,
        break;
 
       case SNDCTL_DSP_GETFMTS:
-       return snd_ioctl_return ((int *) arg, audio_devs[dev]->format_mask);
+       return snd_ioctl_return ((int *) arg, audio_devs[dev]->format_mask | AFMT_MU_LAW);
        break;
 
       case SNDCTL_DSP_SETFMT:
@@ -430,6 +433,8 @@ audio_ioctl (int dev, struct fileinfo *file,
          if (audio_devs[dev]->trigger)         /* Supports SETTRIGGER */
            info |= DSP_CAP_TRIGGER;
 
+         info |= DSP_CAP_MMAP;
+
          memcpy_tofs ((&((char *) arg)[0]), (char *) &info, sizeof (info));
          return 0;
        }
index 802b881c8611844a735f4cb5c91f385b4d46ccca..25f3f6e0c07b17d1507ba4e309593365a260c1e4 100644 (file)
@@ -1,8 +1,11 @@
 /*
- *      PnP support is not included in this driver version.
+ *      PnP soundcard support is not included in this version.
+ *
  *       AEDSP16 will not work without significant changes.
+ *
+ *       SB Pro and SB16 drivers are always enabled together with SB.
  */
-#define DISABLED_OPTIONS       (B(OPT_PNP)|B(OPT_AEDSP16))
+#define DISABLED_OPTIONS       (B(OPT_PNP)|B(OPT_AEDSP16)|B(OPT_SBPRO)|B(OPT_SB16))
 /*
  * sound/configure.c  - Configuration program for the Linux Sound Driver
  */
@@ -159,7 +162,7 @@ hw_entry        hw_table[] =
 char           *questions[] =
 {
   "ProAudioSpectrum 16 support",
-  "SoundBlaster support",
+  "SoundBlaster (SB, SBPro, SB16, clones) support",
   "Generic OPL2/OPL3 FM synthesizer support",
   "Gravis Ultrasound support",
   "MPU-401 support (NOT for SB16)",
@@ -476,6 +479,20 @@ use_old_config (char *filename)
          if (strcmp (tmp, "KERNEL_SOUNDCARD") == 0)
            continue;
 
+         if (strcmp (tmp, "JAZZ_DMA16") == 0)  /* Rename it (hack) */
+           {
+             printf ("#define SB_DMA2 %s\n",
+                     &buf[18]);
+             continue;
+           }
+
+         if (strcmp (tmp, "SB16_DMA") == 0)    /* Rename it (hack) */
+           {
+             printf ("#define SB_DMA2 %s\n",
+                     &buf[16]);
+             continue;
+           }
+
          tmp[8] = 0;           /* Truncate the string */
          if (strcmp (tmp, "EXCLUDE_") == 0)
            continue;           /* Skip excludes */
@@ -679,6 +696,13 @@ ask_parameters (void)
               "Do you have to use this switch with DOS (Y/n) ?");
       if (think_positively (0))
        printf ("#define BROKEN_BUS_CLOCK\n");
+
+      fprintf (stderr, "PAS16 has SoundBlaster emulation. You should disable\n"
+              "this feature if you have another SB compatible card\n"
+              "on the machine\n"
+              "Do you want to disable SB emulation of PAS16 (y/N) ?");
+      if (think_positively (0))
+       printf ("#define DISABLE_SB_EMULATION\n");
     }
 
 
@@ -1341,6 +1365,9 @@ main (int argc, char *argv[])
        }
     }
 
+  if (selected_options & B (OPT_SB))
+    selected_options |= B (OPT_SBPRO) | B (OPT_SB16);
+
   if (!(selected_options & ANY_DEVS))
     {
       printf ("invalid_configuration__run_make_config_again\n");
index f5fe0464a1652ca739fd332d17189405c334e695..c59f41381ff5a373e3c2d19d5d97050b9884809a 100644 (file)
@@ -84,7 +84,7 @@ static unsigned char crystal_key[] =  /* A 32 byte magic key sequence */
 int
 probe_cs4232 (struct address_info *hw_config)
 {
-  int             i;
+  int             i, n;
   int             base = hw_config->io_base, irq = hw_config->irq;
   int             dma1 = hw_config->dma, dma2 = hw_config->dma2;
 
@@ -111,66 +111,77 @@ probe_cs4232 (struct address_info *hw_config)
  * driver is just a temporary kludge.
  */
 
+/*
+ * Repeat initialization few times since it doesn't always succeed in
+ * first time.
+ */
+
+  for (n = 0; n < 4; n++)
+    {
 /*
  * Wake up the card by sending a 32 byte Crystal key to the key port.
  */
-  for (i = 0; i < 32; i++)
-    CS_OUT (crystal_key[i]);
+      for (i = 0; i < 32; i++)
+       CS_OUT (crystal_key[i]);
 
 /*
  * Now set the CSN (Card Select Number).
  */
 
-  CS_OUT2 (0x06, CSN_NUM);
+      CS_OUT2 (0x06, CSN_NUM);
 
 
 /*
  * Then set some config bytes. First logical device 0 
  */
 
-  CS_OUT2 (0x15, 0x00);                /* Select logical device 0 (WSS/SB/FM) */
-  CS_OUT3 (0x47, (base >> 8) & 0xff, base & 0xff);     /* WSSbase */
+      CS_OUT2 (0x15, 0x00);    /* Select logical device 0 (WSS/SB/FM) */
+      CS_OUT3 (0x47, (base >> 8) & 0xff, base & 0xff); /* WSSbase */
 
-  if (check_region (0x388, 4)) /* Not free */
-    CS_OUT3 (0x48, 0x00, 0x00) /* FMbase off */
-      else
-    CS_OUT3 (0x48, 0x03, 0x88);        /* FMbase 0x388 */
+      if (check_region (0x388, 4))     /* Not free */
+       CS_OUT3 (0x48, 0x00, 0x00)      /* FMbase off */
+         else
+       CS_OUT3 (0x48, 0x03, 0x88);     /* FMbase 0x388 */
 
-  CS_OUT3 (0x42, 0x00, 0x00);  /* SBbase off */
-  CS_OUT2 (0x22, irq);         /* SB+WSS IRQ */
-  CS_OUT2 (0x2a, dma1);                /* SB+WSS DMA */
+      CS_OUT3 (0x42, 0x00, 0x00);      /* SBbase off */
+      CS_OUT2 (0x22, irq);     /* SB+WSS IRQ */
+      CS_OUT2 (0x2a, dma1);    /* SB+WSS DMA */
 
-  if (dma2 != -1)
-    CS_OUT2 (0x25, dma2)       /* WSS DMA2 */
-      else
-    CS_OUT2 (0x25, 4);         /* No WSS DMA2 */
+      if (dma2 != -1)
+       CS_OUT2 (0x25, dma2)    /* WSS DMA2 */
+         else
+       CS_OUT2 (0x25, 4);      /* No WSS DMA2 */
 
-  CS_OUT2 (0x33, 0x01);                /* Activate logical dev 0 */
+      CS_OUT2 (0x33, 0x01);    /* Activate logical dev 0 */
 
 /*
  * Initialize logical device 3 (MPU)
  */
 
 #if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
-  if (mpu_base != 0 && mpu_irq != 0)
-    {
-      CS_OUT2 (0x15, 0x03);    /* Select logical device 3 (MPU) */
-      CS_OUT3 (0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff); /* MPUbase */
-      CS_OUT2 (0x22, mpu_irq); /* MPU IRQ */
-      CS_OUT2 (0x33, 0x01);    /* Activate logical dev 3 */
-    }
+      if (mpu_base != 0 && mpu_irq != 0)
+       {
+         CS_OUT2 (0x15, 0x03); /* Select logical device 3 (MPU) */
+         CS_OUT3 (0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff);      /* MPUbase */
+         CS_OUT2 (0x22, mpu_irq);      /* MPU IRQ */
+         CS_OUT2 (0x33, 0x01); /* Activate logical dev 3 */
+       }
 #endif
 
 /*
  * Finally activate the chip
  */
-  CS_OUT (0x79);
+      CS_OUT (0x79);
 
 /*
  * Then try to detect the codec part of the chip
  */
 
-  return ad1848_detect (hw_config->io_base, NULL, hw_config->osp);
+      if (ad1848_detect (hw_config->io_base, NULL, hw_config->osp))
+       return 1;
+    }
+
+  return 0;
 }
 
 long
index 07d169b2f92084a4f32bd11d7714391c5cb17317..ac4e02c78c20fc254869bb0d80219fc1065001cb 100644 (file)
@@ -95,6 +95,7 @@ struct dma_buffparms {
 #define DMA_RESTART    0x00000002
 #define DMA_ACTIVE     0x00000004
 #define DMA_STARTED    0x00000008
+#define DMA_EMPTY      0x00000010      
 #define DMA_ALLOC_DONE 0x00000020
 
        int      open_mode;
@@ -148,6 +149,7 @@ struct audio_operations {
 #define NEEDS_RESTART          1
 #define DMA_AUTOMODE           2
 #define DMA_DUPLEX             4
+#define DMA_PSEUDO_AUTOMODE    8
        int  format_mask;       /* Bitmask for supported audio formats */
        void *devc;             /* Driver specific info */
        int (*open) (int dev, int mode);
@@ -342,6 +344,9 @@ struct sound_timer_operations {
                {"TRXPRO", 0, SNDCARD_TRXPRO, "MediaTriX AudioTriX Pro",        attach_trix_wss, probe_trix_wss, unload_trix_wss},
                {"TRXPROSB", 0, SNDCARD_TRXPRO_SB, "AudioTriX (SB mode)",       attach_trix_sb, probe_trix_sb, unload_trix_sb},
                {"TRXPROMPU", 0, SNDCARD_TRXPRO_MPU, "AudioTriX MIDI",  attach_trix_mpu, probe_trix_mpu, unload_trix_mpu},
+#endif
+#ifdef CONFIG_PNP
+               {"AD1848", 0, 500, "SoundPort", attach_pnp_ad1848, probe_pnp_ad1848, unload_pnp_ad1848},
 #endif
                {NULL, 0, 0,            "*?*",                  NULL, NULL, NULL}
        };
index 92aedf407cec460b4f84bc7e913e74d6525e1f72..0892fe63b5b1e078a9487dd66c18365ff06485ef 100644 (file)
@@ -44,25 +44,24 @@ static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] =
 {
   {0}};
 
+#define NEUTRAL8       0x80
+#define NEUTRAL16      0x00
+
 static int      ndmaps = 0;
 
 #define MAX_DMAP (MAX_AUDIO_DEV*2)
 
 static struct dma_buffparms dmaps[MAX_DMAP] =
 {
-  {0}};                                /*
+  {0}};
 
-                                  * Primitive way to allocate
-                                  * such a large array.
-                                  * Needs dynamic run-time alloction.
-                                */
 static int      space_in_queue (int dev);
 
 static void     dma_reset_output (int dev);
 static void     dma_reset_input (int dev);
 
 static void
-reorganize_buffers (int dev, struct dma_buffparms *dmap)
+reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording)
 {
   /*
    * This routine breaks the physical device buffers to logical ones.
@@ -81,9 +80,9 @@ reorganize_buffers (int dev, struct dma_buffparms *dmap)
       sz = dsp_dev->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1);
 
       if (sz == 8)
-       dmap->neutral_byte = 0x80;
+       dmap->neutral_byte = NEUTRAL8;
       else
-       dmap->neutral_byte = 0x00;
+       dmap->neutral_byte = NEUTRAL16;
 
       if (sr < 1 || nc < 1 || sz < 1)
        {
@@ -118,11 +117,13 @@ reorganize_buffers (int dev, struct dma_buffparms *dmap)
  */
 
       if (dmap->subdivision == 0)      /* Not already set */
-#ifdef V35A9_COMPATIBLE
-       dmap->subdivision = 1;  /* Init to the default value */
-#else
-       dmap->subdivision = 4;  /* Init to the default value */
+       {
+         dmap->subdivision = 1;        /* Init to the default value */
+#ifndef V35A9_COMPATIBLE
+         if (recording)
+           dmap->subdivision = 4;      /* Use shorter fragments when recording */
 #endif
+       }
 
       bsz /= dmap->subdivision;
 
@@ -155,12 +156,16 @@ reorganize_buffers (int dev, struct dma_buffparms *dmap)
   dmap->nbufs = n;
   dmap->bytes_in_use = n * bsz;
 
+  memset (dmap->raw_buf,
+         dmap->neutral_byte,
+         dmap->bytes_in_use);
+
   for (i = 0; i < dmap->nbufs; i++)
     {
       dmap->counts[i] = 0;
     }
 
-  dmap->flags |= DMA_ALLOC_DONE;
+  dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
 }
 
 static void
@@ -182,7 +187,7 @@ dma_init_buffers (int dev, struct dma_buffparms *dmap)
 
   dmap->dma_mode = DMODE_NONE;
   dmap->mapping_flags = 0;
-  dmap->neutral_byte = 0x80;
+  dmap->neutral_byte = NEUTRAL8;
   dmap->cfrag = -1;
   dmap->closing = 0;
 }
@@ -320,7 +325,7 @@ dma_reset_output (int dev)
   restore_flags (flags);
 
   dma_init_buffers (dev, audio_devs[dev]->dmap_out);
-  reorganize_buffers (dev, audio_devs[dev]->dmap_out);
+  reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0);
 }
 
 static void
@@ -338,7 +343,7 @@ dma_reset_input (int dev)
   restore_flags (flags);
 
   dma_init_buffers (dev, audio_devs[dev]->dmap_in);
-  reorganize_buffers (dev, audio_devs[dev]->dmap_in);
+  reorganize_buffers (dev, audio_devs[dev]->dmap_in, 1);
 }
 
 static int
@@ -354,8 +359,10 @@ dma_sync (int dev)
       save_flags (flags);
       cli ();
 
+      audio_devs[dev]->dmap_out->underrun_count = 0;
       while (!current_got_fatal_signal ()
-            && audio_devs[dev]->dmap_out->qlen)
+            && audio_devs[dev]->dmap_out->qlen
+            && audio_devs[dev]->dmap_out->underrun_count == 0)
        {
 
          {
@@ -472,7 +479,7 @@ activate_recording (int dev, struct dma_buffparms *dmap)
     }
 
   if (!(dmap->flags & DMA_ALLOC_DONE))
-    reorganize_buffers (dev, dmap);
+    reorganize_buffers (dev, dmap, 1);
 
   if (!dmap->dma_mode)
     {
@@ -517,7 +524,7 @@ DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
     }
   else if (!dmap->qlen)
     {
-      int             timeout;
+      int             tmout;
 
       if ((err = activate_recording (dev, dmap)) < 0)
        {
@@ -541,16 +548,16 @@ DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
        }
 
       if (!audio_devs[dev]->go)
-       timeout = 0;
+       tmout = 0;
       else
-       timeout = 2 * HZ;
+       tmout = 2 * HZ;
 
 
       {
        unsigned long   tl;
 
-       if (timeout)
-         current_set_timeout (tl = jiffies + (timeout));
+       if (tmout)
+         current_set_timeout (tl = jiffies + (tmout));
        else
          tl = (unsigned long) -1;
        in_sleep_flag[dev].mode = WK_SLEEP;
@@ -709,6 +716,7 @@ get_buffer_pointer (int dev, int chan, struct dma_buffparms *dmap)
     }
 }
 
+
 int
 DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
 {
@@ -730,7 +738,7 @@ DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
 
     case SNDCTL_DSP_GETBLKSIZE:
       if (!(dmap_out->flags & DMA_ALLOC_DONE))
-       reorganize_buffers (dev, dmap_out);
+       reorganize_buffers (dev, dmap_out, 0);
 
       return snd_ioctl_return ((int *) arg, dmap_out->fragment_size);
       break;
@@ -784,7 +792,7 @@ DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
            return -EINVAL;
 
          if (!(dmap->flags & DMA_ALLOC_DONE))
-           reorganize_buffers (dev, dmap);
+           reorganize_buffers (dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
 
          info->fragstotal = dmap->nbufs;
 
@@ -846,8 +854,16 @@ DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
 
        if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go)
          {
+           int             err;
+
            if (!(dmap_in->flags & DMA_ALLOC_DONE))
-             reorganize_buffers (dev, dmap_in);
+             {
+               reorganize_buffers (dev, dmap_in, 1);
+             }
+
+           if ((err = audio_devs[dev]->prepare_for_input (dev,
+                              dmap_in->fragment_size, dmap_in->nbufs)) < 0)
+             return -err;
 
            activate_recording (dev, dmap_in);
          }
@@ -856,8 +872,16 @@ DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
            dmap_out->mapping_flags & DMA_MAP_MAPPED &&
            audio_devs[dev]->go)
          {
+           int             err;
+
            if (!(dmap_out->flags & DMA_ALLOC_DONE))
-             reorganize_buffers (dev, dmap_out);
+             {
+               reorganize_buffers (dev, dmap_out, 0);
+             }
+
+           if ((err = audio_devs[dev]->prepare_for_output (dev,
+                            dmap_out->fragment_size, dmap_out->nbufs)) < 0)
+             return -err;
 
            dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
            DMAbuf_start_output (dev, 0, dmap_out->fragment_size);
@@ -1009,10 +1033,10 @@ DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
       dma_reset_output (dev);
     }
 
-  dmap->flags &= ~DMA_RESTART;
+  dmap->flags &= ~(DMA_RESTART | DMA_EMPTY);
 
   if (!(dmap->flags & DMA_ALLOC_DONE))
-    reorganize_buffers (dev, dmap);
+    reorganize_buffers (dev, dmap, 0);
 
   if (!dmap->dma_mode)
     {
@@ -1031,7 +1055,7 @@ DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
   while (!space_in_queue (dev) &&
         !abort)
     {
-      int             timeout;
+      int             tmout;
 
       if (dontblock)
        {
@@ -1050,16 +1074,16 @@ DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
        * Wait for free space
        */
       if (!audio_devs[dev]->go)
-       timeout = 0;
+       tmout = 0;
       else
-       timeout = 2 * HZ;
+       tmout = 2 * HZ;
 
 
       {
        unsigned long   tl;
 
-       if (timeout)
-         current_set_timeout (tl = jiffies + (timeout));
+       if (tmout)
+         current_set_timeout (tl = jiffies + (tmout));
        else
          tl = (unsigned long) -1;
        out_sleep_flag[dev].mode = WK_SLEEP;
@@ -1094,11 +1118,6 @@ DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
 
   *buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size;
   *size = dmap->fragment_size;
-#if 1
-  memset (*buf,
-          dmap->neutral_byte,
-          *size);
-#endif
   dmap->counts[dmap->qtail] = 0;
 
   return dmap->qtail;
@@ -1160,6 +1179,14 @@ DMAbuf_start_output (int dev, int buff_no, int l)
                dev, dmap->qlen, dmap->nbufs);
 
       dmap->counts[dmap->qtail] = l;
+      if (l < dmap->fragment_size)
+       {
+         int             p = dmap->fragment_size * dmap->qtail;
+
+         memset (dmap->raw_buf + p + l,
+                 dmap->neutral_byte,
+                 dmap->fragment_size - l);
+       }
 
       if ((l != dmap->fragment_size) &&
          ((audio_devs[dev]->flags & DMA_AUTOMODE) &&
@@ -1266,6 +1293,41 @@ DMAbuf_init (long mem_start)
   return mem_start;
 }
 
+static void
+polish_buffers (struct dma_buffparms *dmap)
+{
+  int             i;
+
+  if (dmap->cfrag < 0)
+    {
+      memset (dmap->raw_buf,
+             dmap->neutral_byte,
+             dmap->bytes_in_use);
+      return;
+    }
+
+  for (i = 0; i < dmap->nbufs; i++)
+    {
+      int             p, l;
+
+      p = dmap->fragment_size * i;
+
+      if (i == dmap->cfrag)
+       {
+         l = dmap->fragment_size - dmap->counts[i];
+       }
+      else
+       l = dmap->fragment_size;
+
+      if (l)
+       {
+         memset (dmap->raw_buf + p,
+                 dmap->neutral_byte,
+                 l);
+       }
+    }
+}
+
 void
 DMAbuf_outputintr (int dev, int event_type)
 {
@@ -1291,12 +1353,6 @@ DMAbuf_outputintr (int dev, int event_type)
   if (dmap->mapping_flags & DMA_MAP_MAPPED)
     {
       /* mmapped access */
-      int             p;
-
-      p = dmap->fragment_size * dmap->qhead;
-      memset (dmap->raw_buf + p,
-             dmap->neutral_byte,
-             dmap->fragment_size);
 
       dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
       dmap->qlen++;            /* Yes increment it (don't decrement) */
@@ -1334,17 +1390,19 @@ DMAbuf_outputintr (int dev, int event_type)
          /* Ignore underrun. Just move the tail pointer forward and go */
          if (dmap->closing)
            {
+             polish_buffers (dmap);
              audio_devs[dev]->halt_xfer (dev);
            }
          else
            {
              dmap->qlen++;
-             dmap->cfrag = -1;
              dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
 
-             memset (audio_devs[dev]->dmap_out->raw_buf,
-                     audio_devs[dev]->dmap_out->neutral_byte,
-                     audio_devs[dev]->dmap_out->bytes_in_use);
+             if (!(dmap->flags & DMA_EMPTY))
+               polish_buffers (dmap);
+
+             dmap->cfrag = -1;
+             dmap->flags |= DMA_EMPTY;
            }
        }
 
index 5c3b9ec2433869a9f2b291dbbb5e0a9c26e60c4b..a3be960f66fc7ef3b3bf30ca2e4cfcecfe9ee25a 100644 (file)
@@ -111,10 +111,20 @@ attach_gus_card (long mem_start, struct address_info *hw_config)
 int
 probe_gus (struct address_info *hw_config)
 {
-  int             io_addr;
+  int             io_addr, irq;
 
   gus_osp = hw_config->osp;
 
+  irq = hw_config->irq;
+
+  if (hw_config->card_subtype == 0)    /* GUS/MAX/ACE */
+    if (irq != 3 && irq != 5 && irq != 7 && irq != 9 &&
+       irq != 11 && irq != 12 && irq != 15)
+      {
+       printk ("GUS: Unsupported IRQ %d\n", irq);
+       return 0;
+      }
+
   if (!check_region (hw_config->io_base, 16))
     if (!check_region (hw_config->io_base + 0x100, 16))
       if (gus_wave_detect (hw_config->io_base))
index 28a0bf45b31aab9ad78dc543edc52212988a26f4..6f28e70e5297bf3787112aca3120bf77e9c0f7e5 100644 (file)
@@ -941,7 +941,7 @@ guswave_set_instr (int dev, int voice, int instr_no)
   sample_no = patch_table[instr_no];
   patch_map[voice] = -1;
 
-  if (sample_no < 0)
+  if (sample_no == NOT_SAMPLE)
     {
       printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice);
       return -EINVAL;          /* Patch not defined */
@@ -2557,7 +2557,7 @@ guswave_patchmgr (int dev, struct patmgr_info *rec)
 
          rec->data.data8[i] = 0;
 
-         while (ptr >= 0 && ptr < free_sample)
+         while (ptr >= 0 && ptr < free_sample && ptr != NOT_SAMPLE)
            {
              rec->data.data8[i]++;
              ptr = samples[ptr].key;   /* Follow link */
@@ -2572,7 +2572,7 @@ guswave_patchmgr (int dev, struct patmgr_info *rec)
 
        n = 0;
 
-       while (ptr >= 0 && ptr < free_sample)
+       while (ptr >= 0 && ptr < free_sample && ptr != NOT_SAMPLE)
          {
            rec->data.data32[n++] = ptr;
            ptr = samples[ptr].key;     /* Follow link */
index 876546f8827d22e0920571f4884a6a26ddac5177..642b2f3e6a9e8d3f7037388b86ee296e96bef73e 100644 (file)
@@ -95,7 +95,7 @@ int hex2hex(char *source, char *target, char *varline)
 
 
        fprintf(outf, "/*\n *\t Computer generated file. Do not edit.\n */\n");
-        fprintf(outf, "static int %s_len = %d\n", l);
+        fprintf(outf, "static int %s_len = %d\n", varline, l);
        fprintf(outf, "static unsigned char %s[] = {\n", varline);
 
        for (i=0;i<l;i++)
index 39f52e7bf3a76c44eaa3e01d600a9d6379b57024..4053de89916cb09a2d4a7b79652d2533463afc7a 100644 (file)
@@ -428,12 +428,6 @@ attach_mad16 (long mem_start, struct address_info *hw_config)
        {
          dma2_bit = 0x04;      /* Enable capture DMA */
 
-         /* Change SB DMA so that it doesn't conflict with codec's DMAs */
-
-         if (dma == 3)
-           mad_write (MC3_PORT, (mad_read (MC3_PORT) & ~0x30) | 0x00);         /*DMA1 */
-         else
-           mad_write (MC3_PORT, (mad_read (MC3_PORT) & ~0x30) | 0x20);         /*DMA3 */
        }
       else
        {
index 4870faa39bd6fe30b3c1f064230f68cb58d82bf6..3ed3f7747e5511b2bcdf631733e3ef178d32c336 100644 (file)
@@ -55,20 +55,67 @@ static int     *maui_osp;
 static int      (*orig_load_patch) (int dev, int format, const char *addr,
                                 int offs, int count, int pmgr_flag) = NULL;
 
+static wait_handle *maui_sleeper = NULL;
+static volatile struct snd_wait maui_sleep_flag =
+{0};
+
 static int
-maui_read (void)
+maui_wait (int mask)
 {
-  int             timeout;
+  int             i;
+
+/*
+ * Perform a short initial wait without sleeping
+ */
 
-  for (timeout = 0; timeout < 1000000; timeout++)
+  for (i = 0; i < 100; i++)
     {
-      if (inb (HOST_STAT_PORT) & STAT_RX_AVAIL)
+      if (inb (HOST_STAT_PORT) & mask)
        {
-         return inb (HOST_DATA_PORT);
+         return 1;
        }
     }
 
-  printk ("Maui: Receive timeout\n");
+/*
+ * Wait up to 15 seconds with sleeping
+ */
+
+  for (i = 0; i < 150; i++)
+    {
+      if (inb (HOST_STAT_PORT) & mask)
+       {
+         return 1;
+       }
+
+
+      {
+       unsigned long   tl;
+
+       if (HZ / 10)
+         current_set_timeout (tl = jiffies + (HZ / 10));
+       else
+         tl = (unsigned long) -1;
+       maui_sleep_flag.mode = WK_SLEEP;
+       module_interruptible_sleep_on (&maui_sleeper);
+       if (!(maui_sleep_flag.mode & WK_WAKEUP))
+         {
+           if (jiffies >= tl)
+             maui_sleep_flag.mode |= WK_TIMEOUT;
+         }
+       maui_sleep_flag.mode &= ~WK_SLEEP;
+      };
+      if (current_got_fatal_signal ())
+       return 0;
+    }
+
+  return 0;
+}
+
+static int
+maui_read (void)
+{
+  if (maui_wait (STAT_RX_AVAIL))
+    return inb (HOST_DATA_PORT);
 
   return -1;
 }
@@ -76,17 +123,11 @@ maui_read (void)
 static int
 maui_write (unsigned char data)
 {
-  int             timeout;
-
-  for (timeout = 0; timeout < 10000000; timeout++)
+  if (maui_wait (STAT_TX_AVAIL))
     {
-      if (inb (HOST_STAT_PORT) & STAT_TX_AVAIL)
-       {
-         outb (data, HOST_DATA_PORT);
-         return 1;
-       }
+      outb (data, HOST_DATA_PORT);
+      return 1;
     }
-
   printk ("Maui: Write timeout\n");
 
   return 0;
@@ -169,7 +210,7 @@ int
 probe_maui (struct address_info *hw_config)
 {
   int             i;
-  int             tmp1, tmp2;
+  int             tmp1, tmp2, ret;
 
   if (check_region (hw_config->io_base, 8))
     return 0;
@@ -180,14 +221,23 @@ probe_maui (struct address_info *hw_config)
   if (snd_set_irq_handler (hw_config->irq, mauiintr, "Maui", maui_osp) < 0)
     return 0;
 
+  maui_sleep_flag.mode = WK_NONE;
 
   if (!maui_write (0xCF))      /* Report hardware version */
     {
+      printk ("No WaveFront firmware detected (card uninitialized?)\n");
       snd_release_irq (hw_config->irq);
       return 0;
     }
 
   if ((tmp1 = maui_read ()) == -1 || (tmp2 = maui_read ()) == -1)
+    {
+      printk ("No WaveFront firmware detected (card uninitialized?)\n");
+      snd_release_irq (hw_config->irq);
+      return 0;
+    }
+
+  if (tmp1 == 0xff || tmp2 == 0xff)
     {
       snd_release_irq (hw_config->irq);
       return 0;
@@ -214,13 +264,16 @@ probe_maui (struct address_info *hw_config)
   if (trace_init)
     printk ("Available DRAM %dk\n", tmp1 / 1024);
 
-  request_region (hw_config->io_base + 2, 6, "Maui");
-
   for (i = 0; i < 1000; i++)
     if (probe_mpu401 (hw_config))
       break;
 
-  return probe_mpu401 (hw_config);
+  ret = probe_mpu401 (hw_config);
+
+  if (ret)
+    request_region (hw_config->io_base + 2, 6, "Maui");
+
+  return ret;
 }
 
 long
index 53dab5b29536d8594d0af117f93cc12ba01f84d0..9519bc64e73bec6959b9e65e17c4c8b7fb088a00 100644 (file)
@@ -1089,7 +1089,10 @@ attach_mpu401 (long mem_start, struct address_info *hw_config)
     {
       /* Verify the hardware again */
       if (!reset_mpu401 (devc))
-       return mem_start;
+       {
+         printk ("MPU401: Device didn't respond\n");
+         return mem_start;
+       }
 
       if (!devc->shared_irq)
        if (snd_set_irq_handler (devc->irq, mpuintr, "mpu401", devc->osp) < 0)
index 2e5cdf762b5a9517f2dbfc5baf8b6dfbc06cc724..9d073f0014767c778079e4db10fd95488526c5d7 100644 (file)
@@ -82,6 +82,7 @@ opl_devinfo;
 
 static struct opl_devinfo *devc = NULL;
 
+static int      force_opl3_mode = 0;
 
 static int      detected_model;
 
@@ -93,7 +94,7 @@ static int      opl3_kill_note (int dev, int voice, int note, int velocity);
 void
 enable_opl3_mode (int left, int right, int both)
 {
-  /* NOP */
+  force_opl3_mode = 1;
 }
 
 static void
@@ -254,7 +255,7 @@ opl3_detect (int ioaddr, int *osp)
    * There is a FM chicp in this address. Detect the type (OPL2 to OPL4)
    */
 
-  if (signature == 0x06)       /* OPL2 */
+  if (signature == 0x06 && !force_opl3_mode)   /* OPL2 */
     {
       detected_model = 2;
     }
index d875d87b80460c98fb0f52d7eb19e817275dc1ef..30b07afbd7f4a10e9ecd5005334ac778f38467b2 100644 (file)
@@ -261,7 +261,7 @@ config_pas_hw (struct address_info *hw_config)
   mix_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER);
   mix_write (5, PARALLEL_MIXER);
 
-#if defined(CONFIG_SB_EMULATION) && defined(CONFIG_SB)
+#if !defined(DISABLE_SB_EMULATION) && defined(CONFIG_SB)
 
   {
     struct address_info *sb_config;
@@ -386,7 +386,7 @@ attach_pas_card (long mem_start, struct address_info *hw_config)
          mem_start = pas_pcm_init (mem_start, hw_config);
 #endif
 
-#if defined(CONFIG_SB_EMULATION) && defined(CONFIG_SB)
+#if !defined(DISABLE_SB_EMULATION) && defined(CONFIG_SB)
 
          sb_dsp_disable_midi ();       /*
                                         * The SB emulation don't support *
index 24da509bfcef9e2de716c9c853948b01c1347952..9dd47c4737dadca5db161a77d80287f5175e2763 100644 (file)
@@ -57,6 +57,7 @@ static unsigned long pcm_count = 0;
 static unsigned short pcm_bitsok = 8;  /* mask of OK bits */
 static int      pcm_busy = 0;
 static int      my_devnum = 0;
+static int      open_mode = 0;
 
 int
 pcm_set_speed (int arg)
@@ -258,6 +259,7 @@ pas_pcm_open (int dev, int mode)
 
 
   pcm_count = 0;
+  open_mode = mode;
 
   return 0;
 }
@@ -322,7 +324,6 @@ pas_pcm_output_block (int dev, unsigned long buf, int count,
       pcm_count = count;
     }
   pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
-  pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL);
 
   pcm_mode = PCM_DAC;
 
@@ -369,18 +370,37 @@ pas_pcm_start_input (int dev, unsigned long buf, int count,
       pcm_count = count;
     }
   pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
-  pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL);
 
   pcm_mode = PCM_ADC;
 
   restore_flags (flags);
 }
 
+static void
+pas_pcm_trigger (int dev, int state)
+{
+  unsigned long   flags;
+
+  save_flags (flags);
+  cli ();
+  state &= open_mode;
+
+  if (state & PCM_ENABLE_OUTPUT)
+    pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL);
+  else if (state & PCM_ENABLE_INPUT)
+    pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL);
+  else
+    pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL);
+
+  restore_flags (flags);
+}
+
 static int
 pas_pcm_prepare_for_input (int dev, int bsize, int bcount)
 {
   return 0;
 }
+
 static int
 pas_pcm_prepare_for_output (int dev, int bsize, int bcount)
 {
@@ -403,7 +423,10 @@ static struct audio_operations pas_pcm_operations =
   pas_pcm_reset,
   pas_pcm_reset,
   NULL,
-  NULL
+  NULL,
+  NULL,
+  NULL,
+  pas_pcm_trigger
 };
 
 long
index dc58c42c5990e6d59e117a2eae8333540b5059c1..721720af35ab59c9b5f3190f80d354e81488c8ff 100644 (file)
@@ -749,6 +749,7 @@ probe_pss_mss (struct address_info *hw_config)
        timeout < 100000 && (inb (hw_config->io_base + 3) & 0x3f) != 0x04;
        timeout++);
 
+  outb (0x0b, hw_config->io_base + 4); /* Required by some cards */
   return probe_ms_sound (hw_config);
 }
 
index e5cfb337a87e3000fb6110e0cff1f3656b2b5174..1285c4da93d6863ef9d969bb3152864015d5b68f 100644 (file)
@@ -536,7 +536,7 @@ sb16_dsp_detect (struct address_info *hw_config)
     {
       Jazz16_set_dma16 (hw_config->dma);
       sb16_dsp_ok = 1;
-      return 0;
+      return 1;
     }
 
   if (!(sb_config = sound_getconf (SNDCARD_SB)))
@@ -580,6 +580,10 @@ sb16_dsp_detect (struct address_info *hw_config)
 void
 unload_sb16 (struct address_info *hw_config)
 {
+  extern int      Jazz16_detected;
+
+  if (Jazz16_detected)
+    return;
 
   sound_free_dma (dma8);
 
index 566d66110864f6240152aa83804cf4f1e961134a..038dfdeb747ef4cba9893876881a0dffa3f06eed 100644 (file)
@@ -40,7 +40,7 @@
 #define        STATPORT   (sb16midi_base+1)
 
 extern int     *sb_osp;
-static int      sb16midi_base = 0x330;
+static int      sb16midi_base = 0;
 
 static int 
 sb16midi_status (void)
@@ -100,6 +100,9 @@ sb16midi_input_loop (void)
 void
 sb16midiintr (int unit)
 {
+  if (sb16midi_base == 0)
+    return;
+
   if (input_avail ())
     sb16midi_input_loop ();
 }
index 9489757dc15fdf335fd8a71a6d6a979895d917e1..c13e75ffefe48bdef28e854c37fd61a03ec5eb88 100644 (file)
@@ -71,7 +71,7 @@ int             sb_dsp_ok = 0;        /*
                                 * initialization  *  */
 static int      midi_disabled = 0;
 int             sb_dsp_highspeed = 0;
-int             sbc_major = 1, sbc_minor = 0;
+int             sbc_major = 0, sbc_minor = 0;
 static int      dsp_stereo = 0;
 static int      dsp_current_speed = DSP_DEFAULT_SPEED;
 static int      sb16 = 0;
@@ -292,7 +292,7 @@ dsp_speaker (char state)
 static int
 ess_speed (int speed)
 {
-  int             rate;
+  int             divider;
   unsigned char   bits = 0;
 
   if (speed < 4000)
@@ -303,20 +303,27 @@ ess_speed (int speed)
   if (speed > 22000)
     {
       bits = 0x80;
-      rate = 256 - (795500 + speed / 2) / speed;
-      speed = 795500 / (256 - rate);
+      divider = 256 - (795500 + speed / 2) / speed;
+      dsp_current_speed = 795500 / (256 - divider);
     }
   else
     {
-      rate = 128 - (397700 + speed / 2) / speed;
-      speed = 397700 / (128 - rate);
+      divider = 128 - (397700 + speed / 2) / speed;
+      dsp_current_speed = 397700 / (128 - divider);
     }
 
-  bits |= (unsigned char) rate;
+  bits |= (unsigned char) divider;
   ess_write (0xa1, bits);
 
-  dsp_current_speed = speed;
-  return speed;
+/*
+ * Set filter divider register
+ */
+
+  speed = (speed * 9) / 20;    /* Set filter rolloff to 90% of speed/2 */
+  divider = 256 - 7160000 / (speed * 82);
+  ess_write (0xa2, divider);
+
+  return dsp_current_speed;
 }
 
 static int
@@ -481,10 +488,10 @@ actually_output_block (int dev, unsigned long buf, int nr_bytes,
 
   if (AudioDrive)
     {
-      int             c = 0xffff - nr_bytes;   /* ES1688 increments the count */
+      short           c = -nr_bytes;
 
-      ess_write (0xa4, (unsigned char) (c & 0xff));
-      ess_write (0xa5, (unsigned char) ((c >> 8) & 0xff));
+      ess_write (0xa4, (unsigned char) ((unsigned short) c & 0xff));
+      ess_write (0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
 
       ess_write (0xb8, ess_read (0xb8) | 0x01);                /* Go */
     }
@@ -571,10 +578,10 @@ actually_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag,
 
   if (AudioDrive)
     {
-      int             c = 0xffff - nr_bytes;   /* ES1688 increments the count */
+      short           c = -nr_bytes;
 
-      ess_write (0xa4, (unsigned char) (c & 0xff));
-      ess_write (0xa5, (unsigned char) ((c >> 8) & 0xff));
+      ess_write (0xa4, (unsigned char) ((unsigned short) c & 0xff));
+      ess_write (0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
 
       ess_write (0xb8, ess_read (0xb8) | 0x01);                /* Go */
     }
@@ -798,6 +805,8 @@ sb_dsp_prepare_for_output (int dev, int bsize, int bcount)
 static void
 sb_dsp_halt_xfer (int dev)
 {
+  if (AudioDrive)
+    sb_reset_dsp ();
 }
 
 static int
@@ -884,6 +893,9 @@ sb_dsp_close (int dev)
        sound_close_dma (dma16);
     }
 
+  if (AudioDrive)
+    sb_reset_dsp ();
+
   /* DMAbuf_close_dma (dev); */
   sb_free_irq ();
   /* sb_dsp_command (0xd4); */
@@ -923,7 +935,7 @@ sb_dsp_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
     {
     case SOUND_PCM_WRITE_RATE:
       if (local)
-       return dsp_speed ((long) arg);
+       return dsp_speed ((int) arg);
       return snd_ioctl_return ((int *) arg, dsp_speed (get_fs_long ((long *) arg)));
       break;
 
@@ -935,7 +947,7 @@ sb_dsp_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
 
     case SOUND_PCM_WRITE_CHANNELS:
       if (local)
-       return dsp_set_stereo ((long) arg - 1) + 1;
+       return dsp_set_stereo ((int) arg - 1) + 1;
       return snd_ioctl_return ((int *) arg, dsp_set_stereo (get_fs_long ((long *) arg) - 1) + 1);
       break;
 
@@ -947,7 +959,7 @@ sb_dsp_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
 
     case SNDCTL_DSP_STEREO:
       if (local)
-       return dsp_set_stereo ((long) arg);
+       return dsp_set_stereo ((int) arg);
       return snd_ioctl_return ((int *) arg, dsp_set_stereo (get_fs_long ((long *) arg)));
       break;
 
@@ -956,7 +968,7 @@ sb_dsp_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
        */
     case SNDCTL_DSP_SETFMT:
       if (local)
-       return dsp_set_bits ((long) arg);
+       return dsp_set_bits ((int) arg);
       return snd_ioctl_return ((int *) arg, dsp_set_bits (get_fs_long ((long *) arg)));
       break;
 
@@ -1214,15 +1226,15 @@ initialize_ProSonic16 (void)
 int
 sb_dsp_detect (struct address_info *hw_config)
 {
-  sbc_base = hw_config->io_base;
-  sbc_irq = hw_config->irq;
-  sbc_dma = hw_config->dma;
-  sb_osp = hw_config->osp;
-
   if (sb_dsp_ok)
     return 0;                  /*
                                 * Already initialized
                                 */
+
+  sbc_base = hw_config->io_base;
+  sbc_irq = hw_config->irq;
+  sbc_dma = hw_config->dma;
+  sb_osp = hw_config->osp;
   dma8 = dma16 = hw_config->dma;
 
   if (sb_reset_dsp ())
@@ -1273,7 +1285,6 @@ ess_init (int ess_minor)  /* ESS1688 Initialization */
   unsigned char   cfg, irq_bits = 0, dma_bits = 0;
 
   AudioDrive = 1;
-  sb_no_recording = 1;         /* Temporary kludge */
 
   if (ess_minor >= 8)          /* ESS1688 doesn't support SB MIDI */
     midi_disabled = 1;
@@ -1466,7 +1477,8 @@ sb_dsp_init (long mem_start, struct address_info *hw_config)
 
   int             mixer_type = 0;
 
-  dsp_get_vers (hw_config);
+  if (sbc_major == 0)
+    dsp_get_vers (hw_config);
 
   if (sbc_major == 0)
     {
index d88a5d6bab03862a31444b8c31432d0d3e5d7829..0cc41670d9b7f7002fad5e87495f9ea7540ce4a6 100644 (file)
@@ -275,13 +275,13 @@ smw_mixer_set (int dev, int value)
       levels[dev] = left | (right << 8);
 
       /* Set left bass and treble values */
-      val = ((levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / 100) << 4;
-      val |= ((levels[SOUND_MIXER_BASS] & 0xff) * 16 / 100) & 0x0f;
+      val = ((levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4;
+      val |= ((levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f;
       sb_setmixer (0x0d, val);
 
       /* Set right bass and treble values */
-      val = (((levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / 100) << 4;
-      val |= (((levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / 100) & 0x0f;
+      val = (((levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4;
+      val |= (((levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f;
       sb_setmixer (0x0e, val);
       break;
 
index 5654aaac5548150e759ff93c7d3197438c7fa63a..e52627305eee18ab7ba5b7c08f765ade82a80783 100644 (file)
@@ -866,7 +866,7 @@ play_event (unsigned char *q)
     case SEQ_WAIT:
       delay = (unsigned int *) q;      /*
                                         * Bytes 1 to 3 are containing the *
-                                        * delay in jiffies
+                                        * delay in 'ticks'
                                         */
       *delay = (*delay >> 8) & 0xffffff;
 
@@ -1097,22 +1097,8 @@ sequencer_open (int dev, struct fileinfo *file)
 
   if (dev)                     /* Patch manager device */
     {
-      int             err;
-
       printk ("Patch manager interface is currently broken. Sorry\n");
       return -ENXIO;
-
-      dev--;
-
-      if (dev >= MAX_SYNTH_DEV)
-       return -ENXIO;
-      if (pmgr_present[dev])
-       return -EBUSY;
-      if ((err = pmgr_open (dev)) < 0)
-       return err;
-
-      pmgr_present[dev] = 1;
-      return err;
     }
 
   save_flags (flags);
index 16779b30f5dcb75db614bd7480c8c549c3636a09..416d86975fcf9e8cd1fd01494f220f46dabb0732 100644 (file)
@@ -387,11 +387,9 @@ sound_write_sw (int dev, struct fileinfo *file, const char *buf, int count)
       return MIDIbuf_write (dev, file, buf, count);
 #endif
 
-    default:
-      return -EPERM;
     }
 
-  return count;
+  return -EPERM;
 }
 
 int
@@ -569,9 +567,6 @@ sound_ioctl_sw (int dev, struct fileinfo *file,
       break;
 #endif
 
-    default:
-      return -EPERM;
-      break;
     }
 
   return -EPERM;
index d9cb4e66713f120f73dbb2f7f08c8c2a2723153b..612bff38007b12563a362cbc5ed886bca6e37fdf 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/major.h>
 
 static int      chrdev_registered = 0;
+static int      sound_major = SOUND_MAJOR;
 
 static int      is_unloading = 0;
 
@@ -143,6 +144,7 @@ sound_release (inode_handle * inode, file_handle * file)
 
   dev = inode_get_rdev (inode);
   dev = MINOR (dev);
+
   files[dev].flags = file_get_flags (file);
 
   sound_release_sw (dev, &files[dev]);
@@ -336,7 +338,7 @@ void
 soundcard_init (void)
 {
 #ifndef MODULE
-  module_register_chrdev (SOUND_MAJOR, "sound", &sound_fops);
+  module_register_chrdev (sound_major, "sound", &sound_fops);
   chrdev_registered = 1;
 #endif
 
@@ -345,7 +347,6 @@ soundcard_init (void)
   sndtable_init (0);           /* Initialize call tables and
                                   * detect cards */
 
-
   if (sndtable_get_cardcount () == 0)
     return;                    /* No cards detected */
 
@@ -380,7 +381,7 @@ free_all_irqs (void)
   for (i = 0; i < 31; i++)
     if (irqs & (1ul << i))
       {
-       printk ("Sound warning: IRQ%d was left allocated. Fixed.\n", i);
+       printk ("Sound warning: IRQ%d was left allocated - fixed.\n", i);
        snd_release_irq (i);
       }
   irqs = 0;
@@ -419,7 +420,7 @@ init_module (void)
   if (i)
     sound_setup ("sound=", ints);
 
-  err = module_register_chrdev (SOUND_MAJOR, "sound", &sound_fops);
+  err = module_register_chrdev (sound_major, "sound", &sound_fops);
   if (err)
     {
       printk ("sound: driver already loaded/included in kernel\n");
@@ -448,7 +449,7 @@ cleanup_module (void)
       int             i;
 
       if (chrdev_registered)
-       module_unregister_chrdev (SOUND_MAJOR, "sound");
+       module_unregister_chrdev (sound_major, "sound");
 
 #ifdef CONFIG_SEQUENCER
       sound_stop_timer ();
@@ -463,10 +464,9 @@ cleanup_module (void)
       for (i = 0; i < 8; i++)
        if (dma_alloc_map[i] != DMA_MAP_UNAVAIL)
          {
-           printk ("Sound: Hmm, DMA%d was left allocated\n", i);
+           printk ("Sound: Hmm, DMA%d was left allocated - fixed\n", i);
            sound_free_dma (i);
          }
-
     }
 }
 #endif
@@ -627,7 +627,7 @@ sound_alloc_dmap (int dev, struct dma_buffparms *dmap, int chan)
   dmap->raw_buf = NULL;
 
   if (debugmem)
-    printk ("sound: buffsize%d %lu\n", dev, audio_devs[dev]->buffsize);
+    printk ("sound: buffsize[%d] = %lu\n", dev, audio_devs[dev]->buffsize);
 
   audio_devs[dev]->buffsize = dma_buffsize;
 
@@ -742,7 +742,7 @@ conf_printf (char *name, struct address_info *hw_config)
   printk ("<%s> at 0x%03x", name, hw_config->io_base);
 
   if (hw_config->irq)
-    printk (" irq %d", hw_config->irq);
+    printk (" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq);
 
   if (hw_config->dma != -1 || hw_config->dma2 != -1)
     {
@@ -763,7 +763,7 @@ conf_printf2 (char *name, int base, int irq, int dma, int dma2)
   printk ("<%s> at 0x%03x", name, base);
 
   if (irq)
-    printk (" irq %d", irq);
+    printk (" irq %d", (irq > 0) ? irq : -irq);
 
   if (dma != -1 || dma2 != -1)
     {
index 87b6a652834e993c4a6ea0ae32612e124d9f211f..28fc7e533ca421f872e2a136e9787971fc6ead39 100644 (file)
@@ -1,2 +1,2 @@
-#define SOUND_VERSION_STRING "3.5-beta2-960210"
+#define SOUND_VERSION_STRING "3.5-beta7-960223"
 #define SOUND_INTERNAL_VERSION 0x030505
index 52d0b1ee5ebf319a6edb66236a2c57e1559dbb71..c169b46819967739c0d92325f83e5da81c076610 100644 (file)
@@ -36,6 +36,8 @@
 
 static int      uart6850_base = 0x330;
 
+static int     *uart6850_osp;
+
 #define        DATAPORT   (uart6850_base)
 #define        COMDPORT   (uart6850_base+1)
 #define        STATPORT   (uart6850_base+1)
@@ -78,8 +80,6 @@ static int      reset_uart6850 (void);
 static void     (*midi_input_intr) (int dev, unsigned char data);
 static void     poll_uart6850 (unsigned long dummy);
 
-static int     *uart6850_osp;
-
 
 static struct timer_list uart6850_timer =
 {NULL, NULL, 0, 0, poll_uart6850};
index fcd23ff3ac5c40c17b5248c558a5e56944d177bd..18bb816d89e669dd6fe91f4da93a0801df90bf3d 100644 (file)
@@ -415,10 +415,13 @@ do_load_aout_library(int fd)
                return error;
        len = PAGE_ALIGN(ex.a_text + ex.a_data);
        bss = ex.a_text + ex.a_data + ex.a_bss;
-       if (bss > len)
-               do_mmap(NULL, start_addr + len, bss-len,
-                       PROT_READ|PROT_WRITE|PROT_EXEC,
-                       MAP_PRIVATE|MAP_FIXED, 0);
+       if (bss > len) {
+               error = do_mmap(NULL, start_addr + len, bss-len,
+                               PROT_READ|PROT_WRITE|PROT_EXEC,
+                               MAP_PRIVATE|MAP_FIXED, 0);
+               if (error != start_addr + len)
+                       return error;
+       }
        return 0;
 }
 
index 4b1163c1eb036aff3c1ed9b0d2b17ed22c13a2c5..e3e81d95e2018510c378cdd5381b945d2cf1242e 100644 (file)
@@ -61,8 +61,11 @@ int block_write(struct inode * inode, struct file * filp, const char * buf, int
        else
                size = INT_MAX;
        while (count>0) {
-               if (block >= size)
+               if (block >= size) {
+                       if (!written)
+                               written = -ENOSPC;
                        return written;
+               }
                chars = blocksize - offset;
                if (chars > count)
                        chars=count;
index 1da9424559069b4d97eeb319b2e8be8909834564..01330fd739ebda98d985f33ee93c0953d6b1afc4 100644 (file)
@@ -72,13 +72,16 @@ int buffermem = 0;
 int nr_buffer_heads = 0;
 extern int *blksize_size[];
 
-/* Here is the parameter block for the bdflush process. */
+/* Here is the parameter block for the bdflush process. If you add or
+ * remove any of the parameters, make sure to update kernel/sysctl.c.
+ */
+
 static void wakeup_bdflush(int);
 
 #define N_PARAM 9
 #define LAV
 
-static union bdflush_param{
+union bdflush_param{
        struct {
                int nfract;  /* Percentage of buffer cache dirty to 
                                activate bdflush */
@@ -109,8 +112,8 @@ static union bdflush_param{
 
 
 /* These are the min and max parameter values that we will allow to be assigned */
-static int bdflush_min[N_PARAM] = {  0,  10,    5,   25,  0,   100,   100, 1, 1};
-static int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 60000, 60000, 2047, 5};
+int bdflush_min[N_PARAM] = {  0,  10,    5,   60,  0,   100,   100, 1, 1};
+int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 60000, 60000, 2047, 5};
 
 /*
  * Rewrote the wait-routines to use the "new" wait-queue functionality,
@@ -1250,11 +1253,6 @@ int generic_readpage(struct inode * inode, struct page * page)
 
        address = page_address(page);
        page->count++;
-       wait_on_page(page);
-       if (page->uptodate) {
-               free_page(address);
-               return 0;
-       }
        page->locked = 1;
        
        i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
index 6528106cc10cd0ddd657bb5a081251815fa43fae..55152909b249d339a2efa475550e693f899f129b 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/fcntl.h>
 #include <linux/stat.h>
 #include <linux/locks.h>
+#include <linux/pagemap.h>
 
 #define        NBUF    32
 
@@ -241,13 +242,14 @@ static int ext_file_write(struct inode * inode, struct file * filp, const char *
                        }
                }
                p = (pos % BLOCK_SIZE) + bh->b_data;
+               memcpy_fromfs(p,buf,c);
+               update_vm_cache(inode, pos, p, c);
                pos += c;
                if (pos > inode->i_size) {
                        inode->i_size = pos;
                        inode->i_dirt = 1;
                }
                written += c;
-               memcpy_fromfs(p,buf,c);
                buf += c;
                mark_buffer_uptodate(bh, 1);
                mark_buffer_dirty(bh, 0);
index 6fbd98c0647d58e25b7e23faa087c68ce08a0ffb..cb0a486f13ca31de684920e546111f937a40072e 100644 (file)
@@ -245,6 +245,7 @@ void ext2_free_inode (struct inode * inode)
                mark_buffer_dirty(bh2, 1);
                es->s_free_inodes_count++;
                mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
+               inode->i_dirt = 0;
                set_inode_dtime (inode, gdp);
        }
        mark_buffer_dirty(bh, 1);
index f05a03baab74537595cd58b868a7e67294df46c9..da2db6619509643bd0a1669691da7c006fc5372c 100644 (file)
@@ -75,6 +75,10 @@ NORET_TYPE void ext2_panic (struct super_block * sb, const char * function,
        va_start (args, fmt);
        vsprintf (error_buf, fmt, args);
        va_end (args);
+       /* this is to prevent panic from syncing this filesystem */
+       if (sb->s_lock)
+               sb->s_lock=0;
+       sb->s_flags |= MS_RDONLY;
        panic ("EXT2-fs panic (device %s): %s: %s\n",
               kdevname(sb->s_dev), function, error_buf);
 }
index c5c4ee157aca6ea29c5ef120105eacff58b8ab5a..d1445163217bac7b08d72dff83bda9e3a574a36d 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/fcntl.h>
 #include <linux/stat.h>
 #include <linux/string.h>
+#include <linux/pagemap.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -345,6 +346,7 @@ int fat_file_write(
                        }
                        written -= left;
                }
+               update_vm_cache(inode, filp->f_pos, bh->b_data + (filp->f_pos & (SECTOR_SIZE-1)), written);
                filp->f_pos += written;
                if (filp->f_pos > inode->i_size) {
                        inode->i_size = filp->f_pos;
index 856f15aac016297b9483e64d23bc826f185caf6f..cf9bc7f5b7f0f5aef080001ce696893f16df2248 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/string.h>
-#include <linux/ctype.h>
 #include <linux/major.h>
 #include <linux/blkdev.h>
 #include <linux/fs.h>
index 4e398d5c65baa69923cffa2ae01002e707737739..466ce7138765eafcac1bb9f35ecad3db2bdb307b 100644 (file)
@@ -257,7 +257,7 @@ ncp_msg_data_ready(struct sock *sk, int len)
                        ntohs(sender.sipx_port),
                        packet_buf[0], packet_buf[1]);
 
-               ncp_trigger_message(sk->ipx_ncp_server);
+               ncp_trigger_message(sk->protinfo.ipx.ncp_server);
 
                set_fs(fs);
        }
@@ -306,7 +306,7 @@ ncp_catch_message(struct ncp_server *server)
         }
 
         sk->data_ready = ncp_msg_data_ready;
-       sk->ipx_ncp_server = server;
+       sk->protinfo.ipx.ncp_server = server;
         return 0;
 }
                 
index d1eca88845d73cc0c374c25e768fad2720569ec4..8edc562fd7ae485de2daeecc2fa392b6eb775fd9 100644 (file)
@@ -103,16 +103,16 @@ static int nfs_fsync(struct inode *inode, struct file *file)
        return 0;
 }
 
-static inline void do_read_nfs(struct inode * inode, char * buf, unsigned long pos)
+static inline int do_read_nfs(struct inode * inode, struct page * page,
+       char * buf, unsigned long pos)
 {
-       int refresh = 0;
+       int result, refresh = 0;
        int count = PAGE_SIZE;
        int rsize = NFS_SERVER(inode)->rsize;
        struct nfs_fattr fattr;
 
+       page->locked = 1;
        do {
-               int result;
-
                if (count < rsize)
                        rsize = count;
                result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode), 
@@ -128,28 +128,26 @@ static inline void do_read_nfs(struct inode * inode, char * buf, unsigned long p
        } while (count);
 
        memset(buf, 0, count);
-       if (refresh)
+       if (refresh) {
                nfs_refresh_inode(inode, &fattr);
+               result = 0;
+               page->uptodate = 1;
+       }
+       page->locked = 0;
+       wake_up(&page->wait);
+       return result;
 }
 
 static int nfs_readpage(struct inode * inode, struct page * page)
 {
+       int error;
        unsigned long address;
 
        address = page_address(page);
        page->count++;
-       wait_on_page(page);
-       if (page->uptodate) {
-               free_page(address);
-               return 0;
-       }
-       page->locked = 1;
-       do_read_nfs(inode, (char *) address, page->offset);
-       page->locked = 0;
-       page->uptodate = 1;
-       wake_up(&page->wait);
+       error = do_read_nfs(inode, page, (char *) address, page->offset);
        free_page(address);
-       return 0;
+       return error;
 }
 
 static int nfs_file_write(struct inode *inode, struct file *file, const char *buf,
index aa13b7845a91e63f0c54d812602c2a718715a468..f3829424d47782945be1d481e0526ddf24ae624d 100644 (file)
@@ -854,8 +854,8 @@ static int get_statm(int pid, char * buffer)
  * f_pos = (number of the vma in the task->mm->mmap list) * MAPS_LINE_LENGTH
  *         + (index into the line)
  */
-#define MAPS_LINE_FORMAT         "%08lx-%08lx %s %08lx %02x:%02x %lu\n"
-#define MAPS_LINE_MAX  49 /* sum of 8  1  8  1 4 1 8  1  2 1  2 1 10 1 */
+#define MAPS_LINE_FORMAT         "%08lx-%08lx %s %08lx %s %lu\n"
+#define MAPS_LINE_MAX  49 /* sum of 8  1  8  1 4 1 8 1 5 1 10 1 */
 
 static int read_maps (int pid, struct file * file, char * buf, int count)
 {
@@ -909,7 +909,7 @@ static int read_maps (int pid, struct file * file, char * buf, int count)
 
                len = sprintf(line, MAPS_LINE_FORMAT,
                              map->vm_start, map->vm_end, str, map->vm_offset,
-                             MAJOR(dev),MINOR(dev), ino);
+                             kdevname(dev), ino);
 
                if (column >= len) {
                        column = 0; /* continue with next line at column 0 */
@@ -958,6 +958,7 @@ extern int get_irq_list(char *);
 extern int get_dma_list(char *);
 extern int get_cpuinfo(char *);
 extern int get_pci_list(char*);
+extern int get_md_status (char *);
 #ifdef __SMP_PROF__
 extern int get_smp_prof_list(char *);
 #endif
@@ -1015,6 +1016,10 @@ static int get_root_array(char * page, int type, char **start, off_t offset, int
 
                case PROC_IOPORTS:
                        return get_ioport_list(page);
+#ifdef CONFIG_BLK_DEV_MD
+               case PROC_MD:
+                       return get_md_status(page);
+#endif
 #ifdef CONFIG_APM
                case PROC_APM:
                        return apm_proc(page);
index 0eaed908d76753c6766fb854bd7474b6188f18b5..aecfb5ea9397f4d9523d5c1ff8d85c79a1fc5873 100644 (file)
@@ -458,7 +458,7 @@ static struct super_block * read_super(kdev_t dev,const char *name,int flags,
  * filesystems which don't use real block-devices.  -- jrs
  */
 
-static char unnamed_dev_in_use[256/8] = { 0, };
+static unsigned int unnamed_dev_in_use[256/32] = { 0, };
 
 kdev_t get_unnamed_dev(void)
 {
@@ -636,7 +636,10 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha
                return -EBUSY;
        }
        vfsmnt = add_vfsmnt(dev, dev_name, dir_name);
-       vfsmnt->mnt_sb = sb;
+       if (vfsmnt) {
+               vfsmnt->mnt_sb = sb;
+               vfsmnt->mnt_flags = flags;
+       }
        sb->s_covered = dir_i;
        dir_i->i_mount = sb->s_mounted;
        return 0;               /* we don't iput(dir_i) - see umount */
index edcaf897ab5eda9ff89100273c2ad6a902de8828..a7c775464dbf0589abbdc113b6df962e62f7b721 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/stat.h>
 #include <linux/string.h>
 #include <linux/locks.h>
+#include <linux/pagemap.h>
 
 #include <asm/segment.h>
 
@@ -248,13 +249,14 @@ static int sysv_file_write(struct inode * inode, struct file * filp, const char
                }
                /* now either c==sb->sv_block_size or buffer_uptodate(bh) */
                p = (pos & sb->sv_block_size_1) + bh->b_data;
+               memcpy_fromfs(p, buf, c);
+               update_vm_cache(inode, pos, p, c);
                pos += c;
                if (pos > inode->i_size) {
                        inode->i_size = pos;
                        inode->i_dirt = 1;
                }
                written += c;
-               memcpy_fromfs(p,buf,c);
                buf += c;
                mark_buffer_uptodate(bh, 1);
                mark_buffer_dirty(bh, 0);
index e0425c9566fa2da42de46a1316071a0d4613d095..680d99213936e2b96553394c7b78b320b6e9413b 100644 (file)
@@ -536,14 +536,15 @@ void sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
 {
        struct statfs tmp;
 
-       tmp.f_type = sb->s_magic;
-       tmp.f_bsize = sb->sv_block_size;
-       tmp.f_blocks = sb->sv_ndatazones;
-       tmp.f_bfree = sysv_count_free_blocks(sb);
-       tmp.f_bavail = tmp.f_bfree;
-       tmp.f_files = sb->sv_ninodes;
-       tmp.f_ffree = sysv_count_free_inodes(sb);
+       tmp.f_type = sb->s_magic;                       /* type of filesystem */
+       tmp.f_bsize = sb->sv_block_size;                /* block size */
+       tmp.f_blocks = sb->sv_ndatazones;               /* total data blocks in file system */
+       tmp.f_bfree = sysv_count_free_blocks(sb);       /* free blocks in fs */
+       tmp.f_bavail = tmp.f_bfree;                     /* free blocks available to non-superuser */
+       tmp.f_files = sb->sv_ninodes;                   /* total file nodes in file system */
+       tmp.f_ffree = sysv_count_free_inodes(sb);       /* free file nodes in fs */
        tmp.f_namelen = SYSV_NAMELEN;
+       /* Don't know what value to put in tmp.f_fsid */ /* file system id */
        memcpy_tofs(buf, &tmp, bufsiz);
 }
 
index 74d862c96ed3207a85136bed6ef0b9f8b71db490..67846925a9d5f7cc22f2aaa46b4fa5d682cb438a 100644 (file)
 #include <linux/types.h>
 #include <linux/ptrace.h>
 #include <linux/mman.h>
+#include <linux/mm.h>
 
 #include <asm/system.h>
 
-extern unsigned long high_memory;
-
 static int check_one_table(struct pde * page_dir)
 {
        if (pgd_none(*page_dir))
index 908588ec8975decc028765a811d024428d487d11..a1b633b410a6c9eba4d16e1c4cfb6459b2862dfd 100644 (file)
@@ -68,7 +68,7 @@ int UMSDOS_ioctl_dir (
                Well, not all case require write access, but it simplify the code
                and let's face it, there is only one client (umssync) for all this
        */
-       if (err = verify_area(VERIFY_WRITE,(void*)data,sizeof(struct umsdos_ioctl)) < 0){
+       if ((err = verify_area(VERIFY_WRITE,(void*)data,sizeof(struct umsdos_ioctl))) < 0) {
                ret = err;
        }else if (current->euid == 0
                || cmd == UMSDOS_GETVERSION){
index 5135c4191c7dd0eb9ef698c40a97c29cd2c099d7..fb94b89c86fb7148b3717671fd5e38d6ac5cd918 100644 (file)
@@ -8,7 +8,6 @@
  */
 
 #include <linux/errno.h>
-#include <linux/ctype.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/umsdos_fs.h>
index 5361ad6ca53317166f9a91b2c555beea39e1e2b0..8dd63f61a3b8ea1ae6859f2fe1cb18b0d7d12c26 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/fcntl.h>
 #include <linux/stat.h>
 #include <linux/locks.h>
+#include <linux/pagemap.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -234,13 +235,14 @@ xiafs_file_write(struct inode * inode, struct file * filp, const char * buf, int
            }
        }
        cp = (pos & (XIAFS_ZSIZE(inode->i_sb)-1)) + bh->b_data;
+       memcpy_fromfs(cp,buf,c);
+       update_vm_cache(inode,pos,cp,c);
        pos += c;
        if (pos > inode->i_size) {
            inode->i_size = pos;
            inode->i_dirt = 1;
        }
        written += c;
-       memcpy_fromfs(cp,buf,c);
        buf += c;
        mark_buffer_uptodate(bh, 1);
        mark_buffer_dirty(bh, 0);
index 453ed481a72ad93c7d8cb808f379b1b7b556c065..8f3706a65b36f5f9746f8232835732beb5c3e61a 100644 (file)
@@ -207,12 +207,12 @@ extern void smp_store_cpu_info(int id);           /* Store per cpu info (like the initial
 
 extern __inline void apic_write(unsigned long reg, unsigned long v)
 {
-       *((unsigned long *)(apic_reg+reg))=v;
+       *((volatile unsigned long *)(apic_reg+reg))=v;
 }
 
 extern __inline unsigned long apic_read(unsigned long reg)
 {
-       return *((unsigned long *)(apic_reg+reg));
+       return *((volatile unsigned long *)(apic_reg+reg));
 }
 
 /*
diff --git a/include/asm-i386/string-486.h b/include/asm-i386/string-486.h
new file mode 100644 (file)
index 0000000..992bfca
--- /dev/null
@@ -0,0 +1,693 @@
+#ifndef _I386_STRING_I486_H_
+#define _I386_STRING_I486_H_
+
+/*
+ * This string-include defines all string functions as inline
+ * functions. Use gcc. It also assumes ds=es=data space, this should be
+ * normal. Most of the string-functions are rather heavily hand-optimized,
+ * see especially strtok,strstr,str[c]spn. They should work, but are not
+ * very easy to understand. Everything is done entirely within the register
+ * set, making the functions fast and clean. 
+ *
+ *             Copyright (C) 1991, 1992 Linus Torvalds
+ *             Revised and optimized for i486/pentium
+ *             1994/03/15 by Alberto Vignani/Davide Parodi @crf.it
+ *
+ *     Split into 2 CPU specific files by Alan Cox to keep #ifdef noise down.
+ */
+
+#define __HAVE_ARCH_STRCPY
+extern inline char * strcpy(char * dest,const char *src)
+{
+register char *tmp= (char *)dest;
+register char dummy;
+__asm__ __volatile__(
+       "\n1:\t"
+       "movb (%0),%2\n\t"
+       "incl %0\n\t"
+       "movb %2,(%1)\n\t"
+       "incl %1\n\t"
+       "testb %2,%2\n\t"
+       "jne 1b"
+       :"=r" (src), "=r" (tmp), "=q" (dummy)
+       :"0" (src), "1" (tmp)
+       :"memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRNCPY
+extern inline char * strncpy(char * dest,const char *src,size_t count)
+{
+register char *tmp= (char *)dest;
+register char dummy;
+if (count) {
+__asm__ __volatile__(
+       "\n1:\t"
+       "movb (%0),%2\n\t"
+       "incl %0\n\t"
+       "movb %2,(%1)\n\t"
+       "incl %1\n\t"
+       "decl %3\n\t"
+       "je 3f\n\t"
+       "testb %2,%2\n\t"
+       "jne 1b\n\t"
+       "2:\tmovb %2,(%1)\n\t"
+       "incl %1\n\t"
+       "decl %3\n\t"
+       "jne 2b\n\t"
+       "3:"
+       :"=r" (src), "=r" (tmp), "=q" (dummy), "=r" (count)
+       :"0" (src), "1" (tmp), "3" (count)
+       :"memory");
+    } /* if (count) */
+return dest;
+}
+
+#define __HAVE_ARCH_STRCAT
+extern inline char * strcat(char * dest,const char * src)
+{
+register char *tmp = (char *)(dest-1);
+register char dummy;
+__asm__ __volatile__(
+       "\n1:\tincl %1\n\t"
+       "cmpb $0,(%1)\n\t"
+       "jne 1b\n"
+       "2:\tmovb (%2),%b0\n\t"
+       "incl %2\n\t"
+       "movb %b0,(%1)\n\t"
+       "incl %1\n\t"
+       "testb %b0,%b0\n\t"
+       "jne 2b\n"
+       :"=q" (dummy), "=r" (tmp), "=r" (src)
+       :"1"  (tmp), "2"  (src)
+       :"memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRNCAT
+extern inline char * strncat(char * dest,const char * src,size_t count)
+{
+register char *tmp = (char *)(dest-1);
+register char dummy;
+__asm__ __volatile__(
+       "\n1:\tincl %1\n\t"
+       "cmpb $0,(%1)\n\t"
+       "jne 1b\n"
+       "2:\tdecl %3\n\t"
+       "js 3f\n\t"
+       "movb (%2),%b0\n\t"
+       "incl %2\n\t"
+       "movb %b0,(%1)\n\t"
+       "incl %1\n\t"
+       "testb %b0,%b0\n\t"
+       "jne 2b\n"
+       "3:\txorl %0,%0\n\t"
+       "movb %b0,(%1)\n\t"
+       :"=q" (dummy), "=r" (tmp), "=r" (src), "=r" (count)
+       :"1"  (tmp), "2"  (src), "3"  (count)
+       :"memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRCMP
+extern inline int strcmp(const char * cs,const char * ct)
+{
+register int __res;
+__asm__ __volatile__(
+       "\n1:\tmovb (%1),%b0\n\t"
+       "incl %1\n\t"
+       "cmpb %b0,(%2)\n\t"
+       "jne 2f\n\t"
+       "incl %2\n\t"
+       "testb %b0,%b0\n\t"
+       "jne 1b\n\t"
+       "xorl %0,%0\n\t"
+       "jmp 3f\n"
+       "2:\tmovl $1,%0\n\t"
+       "jb 3f\n\t"
+       "negl %0\n"
+       "3:"
+       :"=q" (__res), "=r" (cs), "=r" (ct)
+       :"1" (cs), "2" (ct)
+       : "memory" );
+return __res;
+}
+
+#define __HAVE_ARCH_STRNCMP
+extern inline int strncmp(const char * cs,const char * ct,size_t count)
+{
+register int __res;
+__asm__ __volatile__(
+       "\n1:\tdecl %3\n\t"
+       "js 2f\n\t"
+       "movb (%1),%b0\n\t"
+       "incl %1\n\t"
+       "cmpb %b0,(%2)\n\t"
+       "jne 3f\n\t"
+       "incl %2\n\t"
+       "testb %b0,%b0\n\t"
+       "jne 1b\n"
+       "2:\txorl %0,%0\n\t"
+       "jmp 4f\n"
+       "3:\tmovl $1,%0\n\t"
+       "jb 4f\n\t"
+       "negl %0\n"
+       "4:"
+       :"=q" (__res), "=r" (cs), "=r" (ct), "=r" (count)
+       :"1"  (cs), "2"  (ct),  "3" (count));
+return __res;
+}
+
+#define __HAVE_ARCH_STRCHR
+extern inline char * strchr(const char * s, int c)
+{
+register char * __res;
+__asm__ __volatile__(
+       "movb %%al,%%ah\n"
+       "1:\tmovb (%1),%%al\n\t"
+       "cmpb %%ah,%%al\n\t"
+       "je 2f\n\t"
+       "incl %1\n\t"
+       "testb %%al,%%al\n\t"
+       "jne 1b\n\t"
+       "xorl %1,%1\n"
+       "2:\tmovl %1,%0\n\t"
+       :"=a" (__res), "=r" (s)
+       :"0" (c),      "1"  (s));
+return __res;
+}
+
+#define __HAVE_ARCH_STRRCHR
+extern inline char * strrchr(const char * s, int c)
+{
+register char * __res;
+__asm__ __volatile__(
+       "cld\n\t"
+       "movb %%al,%%ah\n"
+       "1:\tlodsb\n\t"
+       "cmpb %%ah,%%al\n\t"
+       "jne 2f\n\t"
+       "leal -1(%%esi),%0\n"
+       "2:\ttestb %%al,%%al\n\t"
+       "jne 1b"
+       :"=d" (__res):"0" (0),"S" (s),"a" (c):"ax","si");
+return __res;
+}
+
+#define __HAVE_ARCH_STRSPN
+extern inline size_t strspn(const char * cs, const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+       "cld\n\t"
+       "movl %4,%%edi\n\t"
+       "repne\n\t"
+       "scasb\n\t"
+       "notl %%ecx\n\t"
+       "decl %%ecx\n\t"
+       "movl %%ecx,%%edx\n"
+       "1:\tlodsb\n\t"
+       "testb %%al,%%al\n\t"
+       "je 2f\n\t"
+       "movl %4,%%edi\n\t"
+       "movl %%edx,%%ecx\n\t"
+       "repne\n\t"
+       "scasb\n\t"
+       "je 1b\n"
+       "2:\tdecl %0"
+       :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+       :"ax","cx","dx","di");
+return __res-cs;
+}
+
+#define __HAVE_ARCH_STRCSPN
+extern inline size_t strcspn(const char * cs, const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+       "cld\n\t"
+       "movl %4,%%edi\n\t"
+       "repne\n\t"
+       "scasb\n\t"
+       "notl %%ecx\n\t"
+       "decl %%ecx\n\t"
+       "movl %%ecx,%%edx\n"
+       "1:\tlodsb\n\t"
+       "testb %%al,%%al\n\t"
+       "je 2f\n\t"
+       "movl %4,%%edi\n\t"
+       "movl %%edx,%%ecx\n\t"
+       "repne\n\t"
+       "scasb\n\t"
+       "jne 1b\n"
+       "2:\tdecl %0"
+       :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+       :"ax","cx","dx","di");
+return __res-cs;
+}
+
+#define __HAVE_ARCH_STRPBRK
+extern inline char * strpbrk(const char * cs,const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+       "cld\n\t"
+       "movl %4,%%edi\n\t"
+       "repne\n\t"
+       "scasb\n\t"
+       "notl %%ecx\n\t"
+       "decl %%ecx\n\t"
+       "movl %%ecx,%%edx\n"
+       "1:\tlodsb\n\t"
+       "testb %%al,%%al\n\t"
+       "je 2f\n\t"
+       "movl %4,%%edi\n\t"
+       "movl %%edx,%%ecx\n\t"
+       "repne\n\t"
+       "scasb\n\t"
+       "jne 1b\n\t"
+       "decl %0\n\t"
+       "jmp 3f\n"
+       "2:\txorl %0,%0\n"
+       "3:"
+       :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+       :"ax","cx","dx","di");
+return __res;
+}
+
+#define __HAVE_ARCH_STRSTR
+extern inline char * strstr(const char * cs,const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+       "cld\n\t" \
+       "movl %4,%%edi\n\t"
+       "repne\n\t"
+       "scasb\n\t"
+       "notl %%ecx\n\t"
+       "decl %%ecx\n\t"        /* NOTE! This also sets Z if searchstring='' */
+       "movl %%ecx,%%edx\n"
+       "1:\tmovl %4,%%edi\n\t"
+       "movl %%esi,%%eax\n\t"
+       "movl %%edx,%%ecx\n\t"
+       "repe\n\t"
+       "cmpsb\n\t"
+       "je 2f\n\t"             /* also works for empty string, see above */
+       "xchgl %%eax,%%esi\n\t"
+       "incl %%esi\n\t"
+       "cmpb $0,-1(%%eax)\n\t"
+       "jne 1b\n\t"
+       "xorl %%eax,%%eax\n\t"
+       "2:"
+       :"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct)
+       :"cx","dx","di","si");
+return __res;
+}
+
+#define __HAVE_ARCH_STRLEN
+extern inline size_t strlen(const char * s)
+{
+/*
+ * slightly slower on a 486, but with better chances of
+ * register allocation
+ */
+register char dummy, *tmp= (char *)s;
+__asm__ __volatile__(
+       "\n1:\t"
+       "movb\t(%0),%1\n\t"
+       "incl\t%0\n\t"
+       "testb\t%1,%1\n\t"
+       "jne\t1b"
+       :"=r" (tmp),"=q" (dummy)
+       :"0" (s)
+       : "memory" );
+return (tmp-s-1);
+}
+
+/* Added by Gertjan van Wingerde to make minix and sysv module work */
+#define __HAVE_ARCH_STRNLEN
+extern inline size_t strnlen(const char * s, size_t count)
+{
+register int __res;
+__asm__ __volatile__(
+       "movl %1,%0\n\t"
+       "jmp 2f\n"
+       "1:\tcmpb $0,(%0)\n\t"
+       "je 3f\n\t"
+       "incl %0\n"
+       "2:\tdecl %2\n\t"
+       "cmpl $-1,%2\n\t"
+       "jne 1b\n"
+       "3:\tsubl %1,%0"
+       :"=a" (__res):"c" (s),"d" (count));
+return __res;
+}
+/* end of additional stuff */
+
+#define __HAVE_ARCH_STRTOK
+extern inline char * strtok(char * s,const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+       "testl %1,%1\n\t"
+       "jne 1f\n\t"
+       "testl %0,%0\n\t"
+       "je 8f\n\t"
+       "movl %0,%1\n"
+       "1:\txorl %0,%0\n\t"
+       "movl $-1,%%ecx\n\t"
+       "xorl %%eax,%%eax\n\t"
+       "cld\n\t"
+       "movl %4,%%edi\n\t"
+       "repne\n\t"
+       "scasb\n\t"
+       "notl %%ecx\n\t"
+       "decl %%ecx\n\t"
+       "je 7f\n\t"                     /* empty delimiter-string */
+       "movl %%ecx,%%edx\n"
+       "2:\tlodsb\n\t"
+       "testb %%al,%%al\n\t"
+       "je 7f\n\t"
+       "movl %4,%%edi\n\t"
+       "movl %%edx,%%ecx\n\t"
+       "repne\n\t"
+       "scasb\n\t"
+       "je 2b\n\t"
+       "decl %1\n\t"
+       "cmpb $0,(%1)\n\t"
+       "je 7f\n\t"
+       "movl %1,%0\n"
+       "3:\tlodsb\n\t"
+       "testb %%al,%%al\n\t"
+       "je 5f\n\t"
+       "movl %4,%%edi\n\t"
+       "movl %%edx,%%ecx\n\t"
+       "repne\n\t"
+       "scasb\n\t"
+       "jne 3b\n\t"
+       "decl %1\n\t"
+       "cmpb $0,(%1)\n\t"
+       "je 5f\n\t"
+       "movb $0,(%1)\n\t"
+       "incl %1\n\t"
+       "jmp 6f\n"
+       "5:\txorl %1,%1\n"
+       "6:\tcmpb $0,(%0)\n\t"
+       "jne 7f\n\t"
+       "xorl %0,%0\n"
+       "7:\ttestl %0,%0\n\t"
+       "jne 8f\n\t"
+       "movl %0,%1\n"
+       "8:"
+       :"=b" (__res),"=S" (___strtok)
+       :"0" (___strtok),"1" (s),"g" (ct)
+       :"ax","cx","dx","di","memory");
+return __res;
+}
+
+#define __memcpy_c(d,s,count) \
+((count%4==0) ? \
+ __memcpy_by4((d),(s),(count)) : \
+ ((count%2==0) ? \
+  __memcpy_by2((d),(s),(count)) : \
+  __memcpy_g((d),(s),(count))))
+
+#define __HAVE_ARCH_MEMCPY
+#define memcpy(d,s,count) \
+(__builtin_constant_p(count) ? \
+ __memcpy_c((d),(s),(count)) : \
+ __memcpy_g((d),(s),(count)))
+
+/*
+ *     These ought to get tweaked to do some cache priming.
+ */
+extern inline void * __memcpy_by4(void * to, const void * from, size_t n)
+{
+register void *tmp = (void *)to;
+register int dummy1,dummy2;
+__asm__ __volatile__ (
+       "\n1:\tmovl (%2),%0\n\t"
+       "addl $4,%2\n\t"
+       "movl %0,(%1)\n\t"
+       "addl $4,%1\n\t"
+       "decl %3\n\t"
+       "jnz 1b"
+       :"=r" (dummy1), "=r" (tmp), "=r" (from), "=r" (dummy2) 
+       :"1" (tmp), "2" (from), "3" (n/4)
+       :"memory");
+return (to);
+}
+
+extern inline void * __memcpy_by2(void * to, const void * from, size_t n)
+{
+register void *tmp = (void *)to;
+register int dummy1,dummy2;
+__asm__ __volatile__ (
+       "shrl $1,%3\n\t"
+       "jz 2f\n"                 /* only a word */
+       "1:\tmovl (%2),%0\n\t"
+       "addl $4,%2\n\t"
+       "movl %0,(%1)\n\t"
+       "addl $4,%1\n\t"
+       "decl %3\n\t"
+       "jnz 1b\n"
+       "2:\tmovw (%2),%w0\n\t"
+       "movw %w0,(%1)"
+       :"=r" (dummy1), "=r" (tmp), "=r" (from), "=r" (dummy2) 
+       :"1" (tmp), "2" (from), "3" (n/2)
+       :"memory");
+return (to);
+}
+
+extern inline void * __memcpy_g(void * to, const void * from, size_t n)
+{
+register void *tmp = (void *)to;
+__asm__ __volatile__ (
+       "cld\n\t"
+       "shrl $1,%%ecx\n\t"
+       "jnc 1f\n\t"
+       "movsb\n"
+       "1:\tshrl $1,%%ecx\n\t"
+       "jnc 2f\n\t"
+       "movsw\n"
+       "2:\trep\n\t"
+       "movsl"
+       : /* no output */
+       :"c" (n),"D" ((long) tmp),"S" ((long) from)
+       :"cx","di","si","memory");
+return (to);
+}
+
+
+#define __HAVE_ARCH_MEMMOVE
+extern inline void * memmove(void * dest,const void * src, size_t n)
+{
+register void *tmp = (void *)dest;
+if (dest<src)
+__asm__ __volatile__ (
+       "cld\n\t"
+       "rep\n\t"
+       "movsb"
+       : /* no output */
+       :"c" (n),"S" (src),"D" (tmp)
+       :"cx","si","di");
+else
+__asm__ __volatile__ (
+       "std\n\t"
+       "rep\n\t"
+       "movsb\n\t"
+       : /* no output */
+       :"c" (n), "S" (n-1+(const char *)src), "D" (n-1+(char *)tmp)
+       :"cx","si","di","memory");
+return dest;
+}
+
+extern inline int memcmp(const void * cs,const void * ct,size_t count)
+{
+register int __res;
+__asm__ __volatile__(
+       "cld\n\t"
+       "repe\n\t"
+       "cmpsb\n\t"
+       "je 1f\n\t"
+       "sbbl %0,%0\n\t"
+       "orb $1,%b0\n"
+       "1:"
+       :"=abd" (__res):"0" (0),"S" (cs),"D" (ct),"c" (count)
+       :"si","di","cx");
+return __res;
+}
+
+#define __HAVE_ARCH_MEMCHR
+extern inline void * memchr(const void * cs,int c,size_t count)
+{
+register void * __res;
+if (!count)
+       return NULL;
+__asm__ __volatile__(
+       "cld\n\t"
+       "repne\n\t"
+       "scasb\n\t"
+       "je 1f\n\t"
+       "movl $1,%0\n"
+       "1:\tdecl %0"
+       :"=D" (__res):"a" (c),"D" (cs),"c" (count)
+       :"cx");
+return __res;
+}
+
+#define __memset_cc(s,c,count) \
+((count%4==0) ? \
+ __memset_cc_by4((s),(c),(count)) : \
+ ((count%2==0) ? \
+  __memset_cc_by2((s),(c),(count)) : \
+  __memset_cg((s),(c),(count))))
+
+#define __memset_gc(s,c,count) \
+((count%4==0) ? \
+ __memset_gc_by4((s),(c),(count)) : \
+ ((count%2==0) ? \
+  __memset_gc_by2((s),(c),(count)) : \
+  __memset_gg((s),(c),(count))))
+
+#define __HAVE_ARCH_MEMSET
+#define memset(s,c,count) \
+(__builtin_constant_p(c) ? \
+ (__builtin_constant_p(count) ? \
+  __memset_cc((s),(c),(count)) : \
+  __memset_cg((s),(c),(count))) : \
+ (__builtin_constant_p(count) ? \
+  __memset_gc((s),(c),(count)) : \
+  __memset_gg((s),(c),(count))))
+
+extern inline void * __memset_cc_by4(void * s, char c, size_t count)
+{
+/*
+ * register char *tmp = s;
+ */
+register char *tmp = (char *)s;
+register int  dummy;
+__asm__ __volatile__ (
+       "\n1:\tmovl %2,(%0)\n\t"
+       "addl $4,%0\n\t"
+       "decl %1\n\t"
+       "jnz 1b"
+       :"=r" (tmp), "=r" (dummy)
+       :"q" (0x01010101UL * (unsigned char) c), "0" (tmp), "1" (count/4)
+       :"memory");
+return s;
+}
+
+extern inline void * __memset_cc_by2(void * s, char c, size_t count)
+{
+register void *tmp = (void *)s;
+register int  dummy;
+__asm__ __volatile__ (
+       "shrl $1,%1\n\t"          /* may be divisible also by 4 */
+       "jz 2f\n"
+       "\n1:\tmovl %2,(%0)\n\t"
+       "addl $4,%0\n\t"
+       "decl %1\n\t"
+       "jnz 1b\n"
+       "2:\tmovw %w2,(%0)"
+       :"=r" (tmp), "=r" (dummy)
+       :"q" (0x01010101UL * (unsigned char) c), "0" (tmp), "1" (count/2)
+       :"memory");
+return s;
+}
+
+extern inline void * __memset_gc_by4(void * s, char c, size_t count)
+{
+register void *tmp = (void *)s;
+register int dummy;
+__asm__ __volatile__ (
+       "movb %b0,%h0\n"
+       "pushw %w0\n\t"
+       "shll $16,%0\n\t"
+       "popw %w0\n"
+       "1:\tmovl %0,(%1)\n\t"
+       "addl $4,%1\n\t"
+       "decl %2\n\t"
+       "jnz 1b\n"
+       :"=q" (c), "=r" (tmp), "=r" (dummy)
+       :"0" ((unsigned) c),  "1"  (tmp), "2" (count/4)
+       :"memory");
+return s;
+}
+
+extern inline void * __memset_gc_by2(void * s, char c, size_t count)
+{
+register void *tmp = (void *)s;
+register int dummy1,dummy2;
+__asm__ __volatile__ (
+       "movb %b0,%h0\n\t"
+       "shrl $1,%2\n\t"          /* may be divisible also by 4 */
+       "jz 2f\n\t"
+       "pushw %w0\n\t"
+       "shll $16,%0\n\t"
+       "popw %w0\n"
+       "1:\tmovl %0,(%1)\n\t"
+       "addl $4,%1\n\t"
+       "decl %2\n\t"
+       "jnz 1b\n"
+       "2:\tmovw %w0,(%1)"
+       :"=q" (dummy1), "=r" (tmp), "=r" (dummy2)
+       :"0" ((unsigned) c),  "1"  (tmp), "2" (count/2)
+       :"memory");
+return s;
+}
+
+extern inline void * __memset_cg(void * s, char c, size_t count)
+{
+register void *tmp = (void *)s;
+__asm__ __volatile__ (
+       "shrl $1,%%ecx\n\t"
+       "rep\n\t"
+       "stosw\n\t"
+       "jnc 1f\n\t"
+       "movb %%al,(%%edi)\n"
+       "1:"
+       : /* no output */
+       :"c" (count),"D" (tmp), "a" (0x0101U * (unsigned char) c)
+       :"cx","di","memory");
+return s;
+}
+
+extern inline void * __memset_gg(void * s,char c,size_t count)
+{
+register void *tmp = (void *)s;
+__asm__ __volatile__ (
+       "movb %%al,%%ah\n\t"
+       "shrl $1,%%ecx\n\t"
+       "rep\n\t"
+       "stosw\n\t"
+       "jnc 1f\n\t"
+       "movb %%al,(%%edi)\n"
+       "1:"
+       : /* no output */
+       :"c" (count),"D" (tmp), "a" (c)
+       :"cx","di","memory");
+return s;
+}
+
+
+/*
+ * find the first occurrence of byte 'c', or 1 past the area if none
+ */
+#define __HAVE_ARCH_MEMSCAN
+extern inline void * memscan(void * addr, int c, size_t size)
+{
+       if (!size)
+               return addr;
+       __asm__("cld
+               repnz; scasb
+               jnz 1f
+               dec %%edi
+1:             "
+               : "=D" (addr), "=c" (size)
+               : "0" (addr), "1" (size), "a" (c));
+       return addr;
+}
+
+#endif
index d93a2a773226b2795e47adeb93b4a85b21ce9441..d4adc32895ff92071d6d4f78d019e194dee59fce 100644 (file)
@@ -1,6 +1,16 @@
 #ifndef _I386_STRING_H_
 #define _I386_STRING_H_
 
+/*
+ * On a 486 or Pentium, we are better off not using the
+ * byte string operations. But on a 386 or a PPro the
+ * byte string ops are faster than doing it by hand
+ * (MUCH faster on a Pentium).
+ */
+#if CPU == 486 || CPU == 586
+#include <asm/string-486.h>
+#else
+
 /*
  * This string-include defines all string functions as inline
  * functions. Use gcc. It also assumes ds=es=data space, this should be
@@ -591,3 +601,4 @@ extern inline void * memscan(void * addr, int c, size_t size)
 }
 
 #endif
+#endif
index 77fa556aed885e396361358e8db7fadc7ee140b8..81d4cf1253daaff72bfd55738c72fbbb888e7982 100644 (file)
@@ -5,6 +5,13 @@
 #include <linux/locks.h>
 #include <linux/config.h>
 
+/*
+ * NR_REQUEST is the number of entries in the request-queue.
+ * NOTE that writes may use only the low 2/3 of these: reads
+ * take precedence.
+ */
+#define NR_REQUEST     64
+
 /*
  * This is used in the elevator algorithm.  We don't prioritise reads
  * over writes any more --- although reads are more time-critical than
@@ -19,7 +26,7 @@
  * These will have to be changed to be aware of different buffer
  * sizes etc.. It actually needs a major cleanup.
  */
-#ifdef IDE_DRIVER
+#if defined(IDE_DRIVER) || defined(MD_DRIVER)
 #define SECTOR_MASK ((BLOCK_SIZE >> 9) - 1)
 #else
 #define SECTOR_MASK (blksize_size[MAJOR_NR] &&     \
@@ -72,6 +79,12 @@ extern int ide_init(void);
 #ifdef CONFIG_BLK_DEV_XD
 extern int xd_init(void);
 #endif
+#ifdef CONFIG_BLK_DEV_LOOP
+extern int loop_init(void);
+#endif
+#ifdef CONFIG_BLK_DEV_MD
+extern int md_init(void);
+#endif CONFIG_BLK_DEV_MD
 
 extern void set_device_ro(kdev_t dev,int flag);
 void add_blkdev_randomness(int major);
@@ -144,6 +157,19 @@ static void floppy_off(unsigned int nr);
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
+/* Kludge to use the same number for both char and block major numbers */
+#elif  (MAJOR_NR == MD_MAJOR) && defined(MD_DRIVER)
+
+#ifndef MD_PERSONALITY
+
+#define DEVICE_NAME "Multiple devices driver"
+#define DEVICE_REQUEST do_md_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#endif
+
 #elif (MAJOR_NR == SCSI_TAPE_MAJOR)
 
 #define DEVICE_NAME "scsitape"
@@ -277,7 +303,7 @@ static void floppy_off(unsigned int nr);
 
 #endif /* MAJOR_NR == whatever */
 
-#if (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER)
+#if ((MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER) && !defined(MD_DRIVER))
 
 #ifndef CURRENT
 #define CURRENT (blk_dev[MAJOR_NR].current_request)
@@ -309,8 +335,10 @@ else \
 
 #endif /* DEVICE_TIMEOUT */
 
+#ifndef MD_PERSONALITY
 static void (DEVICE_REQUEST)(void);
-
+#endif
+  
 #ifdef DEVICE_INTR
 #define CLEAR_INTR SET_INTR(NULL)
 #else
@@ -334,7 +362,7 @@ static void (DEVICE_REQUEST)(void);
 /* end_request() - SCSI devices have their own version */
 /*               - IDE drivers have their own copy too */
 
-#if ! SCSI_MAJOR(MAJOR_NR)
+#if ! SCSI_MAJOR(MAJOR_NR) || (defined(MD_DRIVER) && !defined(MD_PERSONALITY))
 
 #if defined(IDE_DRIVER) && !defined(_IDE_C) /* shared copy for IDE modules */
 void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup);
@@ -343,6 +371,8 @@ void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup);
 #ifdef IDE_DRIVER
 void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup) {
        struct request *req = hwgroup->rq;
+#elif defined(MD_DRIVER)
+static void end_request (int uptodate, struct request * req) {
 #else
 static void end_request(int uptodate) {
        struct request *req = CURRENT;
@@ -380,7 +410,7 @@ static void end_request(int uptodate) {
 #ifdef IDE_DRIVER
        blk_dev[MAJOR(req->rq_dev)].current_request = req->next;
        hwgroup->rq = NULL;
-#else
+#elif !defined(MD_DRIVER)
        DEVICE_OFF(req->rq_dev);
        CURRENT = req->next;
 #endif /* IDE_DRIVER */
@@ -392,6 +422,36 @@ static void end_request(int uptodate) {
 #endif /* defined(IDE_DRIVER) && !defined(_IDE_C) */
 #endif /* ! SCSI_MAJOR(MAJOR_NR) */
 
+#ifdef MD_PERSONALITY
+extern inline void end_redirect (struct request *req)
+{
+  struct buffer_head * bh;
+
+  req->errors = 0;
+  
+  if ((bh = req->bh) != NULL)
+  {
+    req->bh = bh->b_reqnext;
+    bh->b_reqnext = NULL;
+    
+    if ((bh = req->bh) != NULL)
+    {
+      req->sector += req->current_nr_sectors;
+      req->current_nr_sectors = bh->b_size >> 9;
+      
+      if (req->nr_sectors < req->current_nr_sectors)
+      {
+       req->nr_sectors = req->current_nr_sectors;
+       printk("end_redirect : buffer-list destroyed\n");
+      }
+      
+      req->buffer = bh->b_data;
+      return;
+    }
+  }
+}
+#endif /* MD_PERSONALITY */
+
 #endif /* defined(MAJOR_NR) || defined(IDE_DRIVER) */
 
 #endif /* _BLK_H */
index ba4d08afd8869b6b2b5ecb2d8c6f8dd79a7fdd34..65287871b7494fb29fb8ed8c5f042f5aa5415378 100644 (file)
@@ -47,6 +47,10 @@ extern struct blk_dev_struct blk_dev[MAX_BLKDEV];
 extern struct wait_queue * wait_for_request;
 extern void resetup_one_dev(struct gendisk *dev, int drive);
 
+/* md needs those functions to requeue requests */
+extern void add_request(struct blk_dev_struct * dev, struct request * req);
+extern struct request *get_md_request (int max_req, kdev_t dev);
+
 extern int * blk_size[MAX_BLKDEV];
 
 extern int * blksize_size[MAX_BLKDEV];
index 5811ff0f2a76da489837939deb9cbfd6bf429288..7a329a7124212c7579d4060039e04ffcc8eb764b 100644 (file)
@@ -27,6 +27,8 @@
 /* most drives don't deliver everything: */
 #define CD_FRAMESIZE_RAW1 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE) /* 2340 */
 #define CD_FRAMESIZE_RAW0 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE-CD_HEAD_SIZE) /* 2336 */
+/* Optics drive also has a 'read all' mode: */
+#define CD_FRAMESIZE_RAWER 2646 /* bytes per frame */
 
 #define CD_EDC_SIZE         4 /* bytes EDC per most raw data frame types */
 #define CD_ZERO_SIZE        8 /* bytes zero per yellow book mode 1 frame */
@@ -96,22 +98,26 @@ struct cdrom_tochdr
        u_char  cdth_trk1;      /* end track */
 };
 
+struct cdrom_msf0              /* address in MSF format */
+{
+       u_char  minute;
+       u_char  second;
+       u_char  frame;
+};
+
+union cdrom_addr               /* address in either MSF or logical format */
+{
+       struct cdrom_msf0       msf;
+       int                     lba;
+};
+
 struct cdrom_tocentry 
 {
        u_char  cdte_track;
        u_char  cdte_adr        :4;
        u_char  cdte_ctrl       :4;
        u_char  cdte_format;
-       union
-       {
-               struct
-               {
-                       u_char  minute;
-                       u_char  second;
-                       u_char  frame;
-               } msf;
-               int     lba;
-       } cdte_addr;
+       union cdrom_addr cdte_addr;
        u_char  cdte_datamode;
 };
 
@@ -139,26 +145,8 @@ struct cdrom_subchnl
        u_char  cdsc_ctrl:      4;
        u_char  cdsc_trk;
        u_char  cdsc_ind;
-       union
-       {
-               struct                  
-               {
-                       u_char  minute;
-                       u_char  second;
-                       u_char  frame;
-               } msf;
-               int     lba;
-       } cdsc_absaddr;
-       union 
-       {
-               struct 
-               {
-                       u_char  minute;
-                       u_char  second;
-                       u_char  frame;
-               } msf;
-               int     lba;
-       } cdsc_reladdr;
+       union cdrom_addr cdsc_absaddr;
+       union cdrom_addr cdsc_reladdr;
 };
 
 /*
@@ -192,16 +180,7 @@ struct cdrom_read
  */
 struct cdrom_read_audio
 {
-       union
-       {
-               struct                  
-               {
-                       u_char minute;
-                       u_char second;
-                       u_char frame;
-               } msf;
-               int     lba;
-       } addr; /* frame address */
+       union cdrom_addr addr; /* frame address */
        u_char addr_format; /* CDROM_LBA or CDROM_MSF */
        int nframes; /* number of 2352-byte-frames to read at once, limited by the drivers */
        u_char *buf; /* frame buffer (size: nframes*2352 bytes) */
@@ -214,16 +193,7 @@ struct cdrom_read_audio
  */
 struct cdrom_multisession
 {
-       union
-       {
-               struct                  
-               {
-                       u_char minute;
-                       u_char second;
-                       u_char frame;
-               } msf;
-               int lba;
-       } addr; /* frame address: start-of-last-session (not the new "frame 16"!)*/
+       union cdrom_addr addr; /* frame address: start-of-last-session (not the new "frame 16"!)*/
        u_char xa_flag; /* 1: "is XA disk" */
        u_char addr_format; /* CDROM_LBA or CDROM_MSF */
 };
@@ -286,7 +256,7 @@ struct cdrom_multisession
                                        /* (struct cdrom_volctrl) */
 
 /* 
- * these ioctls are used in aztcd.c
+ * these ioctls are used in aztcd.c and optcd.c
  */
 #define CDROMREADRAW           0x5314  /* read data in raw mode */
 #define CDROMREADCOOKED                0x5315  /* read data in cooked mode */
@@ -297,6 +267,12 @@ struct cdrom_multisession
  */
 #define CDROMPLAYBLK           0x5317  /* (struct cdrom_blk) */
 
+/* 
+ * these ioctls are used in optcd.c
+ */
+#define CDROMREADALL           0x5318  /* read all 2646 bytes */
+#define CDROMCLOSETRAY         0x5319  /* pendant of CDROMEJECT */
+
 
 /*
  * CD-ROM-specific SCSI command opcodes
index a4af92b215da0615cd9e307d80986c85d31bd931..dcfac2b6c5cce5c6cca7eb75f959574138b00dc7 100644 (file)
@@ -154,6 +154,7 @@ struct buffer_head {
        /* First cache line: */
        unsigned long b_blocknr;        /* block number */
        kdev_t b_dev;                   /* device (B_FREE = free) */
+       kdev_t b_rdev;                  /* Real device */
        struct buffer_head * b_next;    /* Hash queue list */
        struct buffer_head * b_this_page;       /* circular list of buffers in one page */
 
diff --git a/include/linux/isdn.h b/include/linux/isdn.h
new file mode 100644 (file)
index 0000000..3b0c61b
--- /dev/null
@@ -0,0 +1,566 @@
+/* $Id: isdn.h,v 1.2 1996/02/11 02:10:02 fritz Exp fritz $
+ *
+ * Main header for the Linux ISDN subsystem (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ * $Log: isdn.h,v $
+ * Revision 1.2  1996/02/11 02:10:02  fritz
+ * Changed IOCTL-names
+ * Added rx_netdev, st_netdev, first_skb, org_hcb, and org_hcu to
+ * Netdevice-local struct.
+ *
+ * Revision 1.1  1996/01/10 20:55:07  fritz
+ * Initial revision
+ *
+ */
+
+#ifndef isdn_h
+#define isdn_h
+
+#include <linux/ioctl.h>
+
+#define ISDN_TTY_MAJOR    43
+#define ISDN_TTYAUX_MAJOR 44
+#define ISDN_MAJOR        45
+
+/* The minor-devicenumbers for Channel 0 and 1 are used as arguments for
+ * physical Channel-Mapping, so they MUST NOT be changed without changing
+ * the correspondent code in isdn.c
+ */
+
+#define ISDN_MAX_DRIVERS    16
+#define ISDN_MAX_CHANNELS   16
+#define ISDN_MINOR_B        0
+#define ISDN_MINOR_BMAX     (ISDN_MAX_CHANNELS-1)
+#define ISDN_MINOR_CTRL     ISDN_MAX_CHANNELS
+#define ISDN_MINOR_CTRLMAX  (2*ISDN_MAX_CHANNELS-1)
+#define ISDN_MINOR_PPP      (2*ISDN_MAX_CHANNELS)
+#define ISDN_MINOR_PPPMAX   (3*ISDN_MAX_CHANNELS-1)
+#define ISDN_MINOR_STATUS   128
+
+/* New ioctl-codes */
+#define IIOCNETAIF  _IO('I',1)
+#define IIOCNETDIF  _IO('I',2)
+#define IIOCNETSCF  _IO('I',3)
+#define IIOCNETGCF  _IO('I',4)
+#define IIOCNETANM  _IO('I',5)
+#define IIOCNETDNM  _IO('I',6)
+#define IIOCNETGNM  _IO('I',7)
+#define IIOCGETSET  _IO('I',8)
+#define IIOCSETSET  _IO('I',9)
+#define IIOCSETVER  _IO('I',10)
+#define IIOCNETHUP  _IO('I',11)
+#define IIOCSETGST  _IO('I',12)
+#define IIOCSETBRJ  _IO('I',13)
+#define IIOCSIGPRF  _IO('I',14)
+#define IIOCGETPRF  _IO('I',15)
+#define IIOCSETPRF  _IO('I',16)
+#define IIOCGETMAP  _IO('I',17)
+#define IIOCSETMAP  _IO('I',18)
+#define IIOCNETASL  _IO('I',19)
+#define IIOCNETDIL  _IO('I',20)
+
+#define IIOCNETALN  _IO('I',32)
+#define IIOCNETDLN  _IO('I',33)
+
+#define IIOCDBGVAR  _IO('I',127)
+
+#define IIOCDRVCTL  _IO('I',128)
+
+/* Packet encapsulations for net-interfaces */
+#define ISDN_NET_ENCAP_ETHER     0
+#define ISDN_NET_ENCAP_RAWIP     1
+#define ISDN_NET_ENCAP_IPTYP     2
+#define ISDN_NET_ENCAP_CISCOHDLC 3
+#define ISDN_NET_ENCAP_SYNCPPP   4
+
+/* Facility which currently uses an ISDN-channel */
+#define ISDN_USAGE_NONE       0
+#define ISDN_USAGE_RAW        1
+#define ISDN_USAGE_MODEM      2
+#define ISDN_USAGE_NET        3
+#define ISDN_USAGE_VOICE      4
+#define ISDN_USAGE_FAX        5
+#define ISDN_USAGE_MASK       7 /* Mask to get plain usage */
+#define ISDN_USAGE_EXCLUSIVE 64 /* This bit is set, if channel is exclusive */
+#define ISDN_USAGE_OUTGOING 128 /* This bit is set, if channel is outgoing  */
+
+#define ISDN_MODEM_ANZREG    20        /* Number of Medem-Registers        */
+#define ISDN_MSNLEN          20
+
+typedef struct {
+  char drvid[25];
+  unsigned long arg;
+} isdn_ioctl_struct;
+
+typedef struct {
+  unsigned long isdndev;
+  unsigned long atmodem[ISDN_MAX_CHANNELS];
+  unsigned long info[ISDN_MAX_CHANNELS];
+} debugvar_addr;
+
+typedef struct {
+  char name[10];
+  char phone[20];
+  int  outgoing;
+} isdn_net_ioctl_phone;
+
+typedef struct {
+  char name[10];     /* Name of interface                     */
+  char master[10];   /* Name of Master for Bundling           */
+  char slave[10];    /* Name of Slave for Bundling            */
+  char eaz[256];     /* EAZ/MSN                               */
+  char drvid[25];    /* DriverId for Bindings                 */
+  int  secure;       /* Flag: Secure                          */
+  int  callback;     /* Flag: Callback                        */
+  int  onhtime;      /* Hangup-Timeout                        */
+  int  charge;       /* Charge-Units                          */
+  int  chargehup;    /* Flag: Charge-Hangup                   */
+  int  l2_proto;     /* Layer-2 protocol                      */
+  int  l3_proto;     /* Layer-3 protocol                      */
+  int  p_encap;      /* Encapsulation                         */
+  int  ihup;         /* Flag: Hangup-Timeout on incoming line */
+  int  exclusive;    /* Channel, if bound exclusive           */
+  int  slavedelay;   /* Delay until slave starts up           */
+} isdn_net_ioctl_cfg;
+
+#ifdef __KERNEL__
+
+#ifndef STANDALONE
+#include <linux/config.h>
+#endif
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_reg.h>
+#include <linux/fcntl.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/tcp.h>
+
+#ifdef CONFIG_ISDN_PPP
+
+#ifdef CONFIG_ISDN_PPP_VJ
+#  include "/usr/src/linux/drivers/net/slhc.h"
+#endif
+
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/if_pppvar.h>
+
+#include <linux/isdn_ppp.h>
+#endif
+
+#include <linux/isdnif.h>
+
+#define ISDN_DRVIOCTL_MASK       0x7f  /* Mask for Device-ioctl */
+
+/* Until now unused */
+#define ISDN_SERVICE_VOICE 1
+#define ISDN_SERVICE_AB    1<<1 
+#define ISDN_SERVICE_X21   1<<2
+#define ISDN_SERVICE_G4    1<<3
+#define ISDN_SERVICE_BTX   1<<4
+#define ISDN_SERVICE_DFUE  1<<5
+#define ISDN_SERVICE_X25   1<<6
+#define ISDN_SERVICE_TTX   1<<7
+#define ISDN_SERVICE_MIXED 1<<8
+#define ISDN_SERVICE_FW    1<<9
+#define ISDN_SERVICE_GTEL  1<<10
+#define ISDN_SERVICE_BTXN  1<<11
+#define ISDN_SERVICE_BTEL  1<<12
+
+/* Macros checking plain usage */
+#define USG_NONE(x)     ((x & ISDN_USAGE_MASK)==ISDN_USAGE_NONE)
+#define USG_RAW(x)      ((x & ISDN_USAGE_MASK)==ISDN_USAGE_RAW)
+#define USG_MODEM(x)    ((x & ISDN_USAGE_MASK)==ISDN_USAGE_MODEM)
+#define USG_NET(x)      ((x & ISDN_USAGE_MASK)==ISDN_USAGE_NET)
+#define USG_OUTGOING(x) ((x & ISDN_USAGE_OUTGOING)==ISDN_USAGE_OUTGOING)
+
+/* Timer-delays and scheduling-flags */
+#define ISDN_TIMER_RES       3                     /* Main Timer-Resolution  */
+#define ISDN_TIMER_02SEC     (HZ/ISDN_TIMER_RES/5) /* Slow-Timer1 (0.2 sec.) */
+#define ISDN_TIMER_1SEC      (HZ/ISDN_TIMER_RES)   /* Slow-Timer2 (1 sec.)   */
+#define ISDN_TIMER_MODEMREAD 1
+#define ISDN_TIMER_MODEMPLUS 2
+#define ISDN_TIMER_MODEMRING 4
+#define ISDN_TIMER_MODEMXMIT 8
+#define ISDN_TIMER_NETDIAL   16
+#define ISDN_TIMER_NETHANGUP 32
+#define ISDN_TIMER_IPPP      64
+#define ISDN_TIMER_FAST      (ISDN_TIMER_MODEMREAD | ISDN_TIMER_MODEMPLUS | \
+                              ISDN_TIMER_MODEMXMIT)
+#define ISDN_TIMER_SLOW      (ISDN_TIMER_MODEMRING | ISDN_TIMER_NETHANGUP | \
+                              ISDN_TIMER_NETDIAL)
+
+/* Timeout-Values for isdn_net_dial() */
+#define ISDN_TIMER_DTIMEOUT10 (10*HZ/(ISDN_TIMER_02SEC*ISDN_TIMER_RES))
+#define ISDN_TIMER_DTIMEOUT15 (15*HZ/(ISDN_TIMER_02SEC*ISDN_TIMER_RES))
+
+/* GLOBAL_FLAGS */
+#define ISDN_GLOBAL_STOPPED 1
+
+/*=================== Start of ip-over-ISDN stuff =========================*/
+
+/* Feature- and status-flags for a net-interface */
+#define ISDN_NET_CONNECTED  0x01       /* Bound to ISDN-Channel             */
+#define ISDN_NET_SECURE     0x02       /* Accept calls from phonelist only  */
+#define ISDN_NET_CALLBACK   0x04       /* callback incoming phonenumber     */
+#define ISDN_NET_CLONE      0x08       /* clone a tmp interface when called */
+#define ISDN_NET_TMP        0x10       /* tmp interface until getting an IP */
+#define ISDN_NET_DYNAMIC    0x20       /* this link is dynamically allocted */
+#define ISDN_NET_MAGIC      0x49344C02 /* for paranoia-checking             */
+
+/* Phone-list-element */
+typedef struct {
+  void *next;
+  char num[20];
+} isdn_net_phone;
+
+/* Local interface-data */
+typedef struct isdn_net_local_s {
+  ulong                  magic;
+  char                   name[10];     /* Name of device                   */
+  struct enet_statistics stats;        /* Ethernet Statistics              */
+  int                    isdn_device;  /* Index to isdn-device             */
+  int                    isdn_channel; /* Index to isdn-channel            */
+  int                   ppp_minor;    /* PPPD device minor number         */
+  int                    pre_device;   /* Preselected isdn-device          */
+  int                    pre_channel;  /* Preselected isdn-channel         */
+  int                    exclusive;    /* If non-zero idx to reserved chan.*/
+  int                    flags;        /* Connection-flags                 */
+  int                    dialstate;    /* State for dialing                */
+  int                    dialretry;    /* Counter for Dialout-retries      */
+  int                    dialmax;      /* Max. Nuber of Dial-retries       */
+  char                   msn[ISDN_MSNLEN]; /* MSNs/EAZs for this interface */
+  int                    dtimer;       /* Timeout-counter for dialing      */
+  u_char                 p_encap;      /* Packet encapsulation             */
+                                       /*   0 = Ethernet over ISDN         */
+                                      /*   1 = RAW-IP                     */
+                                       /*   2 = IP with type field         */
+  u_char                 l2_proto;     /* Layer-2-protocol                 */
+                                      /* See ISDN_PROTO_L2..-constants in */
+                                       /* isdnif.h                         */
+                                       /*   0 = X75/LAPB with I-Frames     */
+                                      /*   1 = X75/LAPB with UI-Frames    */
+                                      /*   2 = X75/LAPB with BUI-Frames   */
+                                      /*   3 = HDLC                       */
+  u_char                 l3_proto;     /* Layer-3-protocol                 */
+                                      /* See ISDN_PROTO_L3..-constants in */
+                                       /* isdnif.h                         */
+                                       /*   0 = Transparent                */
+  int                    huptimer;     /* Timeout-counter for auto-hangup  */
+  int                    charge;       /* Counter for charging units       */
+  int                    chargetime;   /* Timer for Charging info          */
+  int                    hupflags;     /* Flags for charge-unit-hangup:    */
+                                      /* bit0: chargeint is invalid       */
+                                      /* bit1: Getting charge-interval    */
+                                       /* bit2: Do charge-unit-hangup      */
+  int                    outgoing;     /* Flag: outgoing call              */
+  int                    onhtime;      /* Time to keep link up             */
+  int                    chargeint;    /* Interval between charge-infos    */
+  int                    onum;         /* Flag: at least 1 outgoing number */
+  int                    cps;          /* current speed of this interface  */
+  int                    transcount;   /* byte-counter for cps-calculation */
+  int                    sqfull;       /* Flag: netdev-queue overloaded    */
+  ulong                  sqfull_stamp; /* Start-Time of overload           */
+  ulong                  slavedelay;   /* Dynamic bundling delaytime       */
+  struct device          *srobin;      /* Ptr to Master device for slaves  */
+  isdn_net_phone         *phone[2];    /* List of remote-phonenumbers      */
+                                      /* phone[0] = Incoming Numbers      */
+                                      /* phone[1] = Outgoing Numbers      */
+  isdn_net_phone         *dial;        /* Pointer to dialed number         */
+  struct device          *master;      /* Ptr to Master device for slaves  */
+  struct device          *slave;       /* Ptr to Slave device for masters  */
+  struct isdn_net_local_s *next;       /* Ptr to next link in bundle       */
+  struct isdn_net_local_s *last;       /* Ptr to last link in bundle       */
+  struct isdn_net_dev_s  *netdev;      /* Ptr to netdev                    */
+  struct sk_buff         *first_skb;   /* Ptr to skb that triggers dialing */
+  struct sk_buff         *sav_skb;     /* Ptr to skb, rejected by LL-driver*/
+                                       /* Ptr to orig. header_cache_bind   */
+  void                   (*org_hcb)(struct hh_cache **, struct device *,
+                                    unsigned short, __u32);
+                                       /* Ptr to orig. header_cache_update */
+  void                   (*org_hcu)(struct hh_cache *, struct device *,
+                                    unsigned char *);
+} isdn_net_local;
+
+#ifdef CONFIG_ISDN_PPP
+struct ippp_bundle {
+  int mp_mrru;                        /* unused                             */
+  struct mpqueue *last;               /* currently defined in isdn_net_dev  */
+  int min;                            /* currently calculated 'on the fly'  */
+  long next_num;                      /* we wanna see this seq.-number next */
+  struct sqqueue *sq;
+  int modify:1;                       /* set to 1 while modifying sqqueue   */
+  int bundled:1;                      /* bundle active ?                    */
+};
+#endif
+
+/* the interface itself */
+typedef struct isdn_net_dev_s {
+  isdn_net_local  local;
+  isdn_net_local *queue;
+  void           *next;                /* Pointer to next isdn-interface   */
+  struct device   dev;                /* interface to upper levels        */
+#ifdef CONFIG_ISDN_PPP
+  struct mpqueue *mp_last; 
+  struct ippp_bundle ib;
+#endif
+} isdn_net_dev;
+
+/*===================== End of ip-over-ISDN stuff ===========================*/
+
+/*======================= Start of ISDN-tty stuff ===========================*/
+
+#define ISDN_ASYNC_MAGIC          0x49344C01 /* for paranoia-checking        */
+#define ISDN_ASYNC_INITIALIZED   0x80000000 /* port was initialized         */
+#define ISDN_ASYNC_CALLOUT_ACTIVE 0x40000000 /* Call out device active       */
+#define ISDN_ASYNC_NORMAL_ACTIVE  0x20000000 /* Normal device active         */
+#define ISDN_ASYNC_CLOSING       0x08000000 /* Serial port is closing       */
+#define ISDN_ASYNC_CTS_FLOW      0x04000000 /* Do CTS flow control          */
+#define ISDN_ASYNC_CHECK_CD      0x02000000 /* i.e., CLOCAL                 */
+#define ISDN_ASYNC_HUP_NOTIFY         0x0001 /* Notify tty on hangups/closes */
+#define ISDN_ASYNC_SESSION_LOCKOUT    0x0100 /* Lock cua opens on session    */
+#define ISDN_ASYNC_PGRP_LOCKOUT       0x0200 /* Lock cua opens on pgrp       */
+#define ISDN_ASYNC_CALLOUT_NOHUP      0x0400 /* No hangup for cui            */
+#define ISDN_ASYNC_SPLIT_TERMIOS      0x0008 /* Sep. termios for dialin/out  */
+#define ISDN_PORT_16550A                   4 /* Type of faked Hardware       */
+#define ISDN_SERIAL_XMIT_SIZE           4000 /* Maximum bufsize for write    */
+#define ISDN_SERIAL_TYPE_NORMAL                   1 /* tty-type                     */
+#define ISDN_SERIAL_TYPE_CALLOUT           2 /* cua-type                     */
+
+/* Private data (similar to async_struct in <linux/serial.h>) */
+typedef struct {
+  int                  magic;
+  int                  flags;           /* defined in tty.h               */
+  int                  type;            /* UART type                      */
+  struct tty_struct    *tty;
+  int                  x_char;          /* xon/xoff character             */
+  int                  close_delay;
+  int                  MCR;             /* Modem control register         */
+  int                  line;
+  int                  count;           /* # of fd on device              */
+  int                  blocked_open;    /* # of blocked opens             */
+  long                 session;         /* Session of opening process     */
+  long                 pgrp;            /* pgrp of opening process        */
+  int                   isdn_driver;    /* Index to isdn-driver           */
+  int                   isdn_channel;    /* Index to isdn-channel          */
+  int                   drv_index;       /* Index to dev->usage            */
+#if FUTURE
+  int                   send_outstanding;/* # of outstanding send-requests */
+#endif
+  int                   xmit_size;       /* max. # of chars in xmit_buf    */
+  int                   xmit_count;      /* # of chars in xmit_buf         */
+  u_char                *xmit_buf;       /* transmit-buffer                */
+  struct termios       normal_termios;
+  struct termios       callout_termios;
+  struct wait_queue    *open_wait;
+  struct wait_queue    *close_wait;
+} modem_info;
+
+#define ISDN_MODEM_WINSIZE 8
+
+/* Private data of AT-command-interpreter */
+typedef struct {
+  u_char              profile[ISDN_MODEM_ANZREG]; /* Modem-Regs. Profile 0  */
+  u_char              mdmreg[ISDN_MODEM_ANZREG];  /* Modem-Registers        */
+  char                pmsn[ISDN_MSNLEN]; /* EAZ/MSNs Profile 0             */
+  char                msn[ISDN_MSNLEN];/* EAZ/MSN                          */
+  int                 mdmcmdl;         /* Length of Modem-Commandbuffer    */
+  int                 pluscount;       /* Counter for +++ sequence         */
+  int                 lastplus;        /* Timestamp of last +              */
+  char                mdmcmd[255];     /* Modem-Commandbuffer              */
+} atemu;
+
+/* Descripion of one ISDN-tty */
+typedef struct {
+  int                msr[ISDN_MAX_CHANNELS];     /* Modem-statusregister   */
+  int                mlr[ISDN_MAX_CHANNELS];     /* Line-statusregister    */
+  int                refcount;                   /* Number of opens        */
+  int                online[ISDN_MAX_CHANNELS];          /* B-Channel is up        */
+  int                dialing[ISDN_MAX_CHANNELS];  /* Dial in progress       */
+  int                rcvsched[ISDN_MAX_CHANNELS]; /* Receive needs schedule */
+  int                ncarrier[ISDN_MAX_CHANNELS]; /* Output NO CARRIER      */
+  struct tty_driver  tty_modem;                          /* tty-device             */
+  struct tty_driver  cua_modem;                          /* cua-device             */
+  struct tty_struct  *modem_table[ISDN_MAX_CHANNELS]; /* ?? copied from Orig */
+  struct termios     *modem_termios[ISDN_MAX_CHANNELS];
+  struct termios     *modem_termios_locked[ISDN_MAX_CHANNELS];
+  atemu              atmodem[ISDN_MAX_CHANNELS];  /* AT-Command-parser      */
+  modem_info         info[ISDN_MAX_CHANNELS];    /* Private data           */
+} modem;
+
+/*======================= End of ISDN-tty stuff ============================*/
+
+/*======================= Start of sync-ppp stuff ==========================*/
+
+
+#define NUM_RCV_BUFFS     64
+#define PPP_HARD_HDR_LEN 4
+
+#define IPPP_OPEN        0x1
+#define IPPP_CONNECT     0x2
+#define IPPP_CLOSEWAIT   0x4
+#define IPPP_NOBLOCK     0x8
+
+#ifdef CONFIG_ISDN_PPP
+
+struct sqqueue {
+  struct sqqueue *next;
+  int sqno_start;
+  int sqno_end;
+  struct sk_buff *skb;
+  long timer;
+};
+  
+struct mpqueue {
+  struct mpqueue *next;
+  struct mpqueue *last;
+  int    sqno;
+  struct sk_buff *skb;
+  int BEbyte;
+  unsigned long time;
+}; 
+
+struct ippp_buf_queue {
+  struct ippp_buf_queue *next;
+  struct ippp_buf_queue *last;
+  char *buf;                 /* NULL here indicates end of queue */
+  int len;
+};
+
+struct ippp_struct {
+  struct ippp_struct *next_link;
+  int state;
+  struct ippp_buf_queue rq[NUM_RCV_BUFFS]; /* packet queue for isdn_ppp_read() */
+  struct ippp_buf_queue *first;  /* pointer to (current) first packet */
+  struct ippp_buf_queue *last;   /* pointer to (current) last used packet in queue */
+  struct wait_queue *wq;
+  struct wait_queue *wq1;
+  struct task_struct *tk;
+  unsigned int mpppcfg;
+  unsigned int pppcfg;
+  unsigned int mru;
+  unsigned int mpmru;
+  unsigned int mpmtu;
+  unsigned int maxcid;
+  isdn_net_local *lp;
+  int unit; 
+  long last_link_seqno;
+  long mp_seqno;
+  long range;
+#ifdef CONFIG_ISDN_PPP_VJ
+  unsigned char *cbuf;
+  struct slcompress *slcomp;
+#endif
+};
+
+#endif
+
+/*======================== End of sync-ppp stuff ===========================*/
+
+/*======================= Start of general stuff ===========================*/
+
+/* Packet-queue-element */
+typedef struct pqueue {
+  char   *next;                                /* Pointer to next packet           */
+  short   length;                      /* Packetlength                     */
+  short   size;                         /* Allocated size                   */
+  u_char *rptr;                                /* Read-pointer for stream-reading  */
+  u_char  buffer[1];                   /* The data (will be alloc'd)       */
+} pqueue;
+
+typedef struct {
+  char *next;
+  char *private;
+} infostruct;
+
+/* Description of hardware-level-driver */
+typedef struct {
+  ulong               flags;            /* Flags                            */
+  int                 channels;         /* Number of channels               */
+  int                 reject_bus;       /* Flag: Reject rejected call on bus*/
+  struct wait_queue  *st_waitq;         /* Wait-Queue for status-read's     */
+  int                 maxbufsize;       /* Maximum Buffersize supported     */
+  unsigned long       pktcount;         /* Until now: unused                */
+  int                 running;          /* Flag: Protocolcode running       */
+  int                 loaded;           /* Flag: Driver loaded              */
+  int                 stavail;          /* Chars avail on Status-device     */
+  isdn_if            *interface;        /* Interface to driver              */
+  int                *rcverr;           /* Error-counters for B-Ch.-receive */
+  int                *rcvcount;         /* Byte-counters for B-Ch.-receive  */
+  pqueue             **rpqueue;         /* Pointers to start of Rcv-Queue   */
+  struct wait_queue  **rcv_waitq;       /* Wait-Queues for B-Channel-Reads  */
+  struct wait_queue  **snd_waitq;       /* Wait-Queue for B-Channel-Send's  */
+  char               msn2eaz[10][ISDN_MSNLEN];  /* Mapping-Table MSN->EAZ   */
+} driver;
+
+/* Main driver-data */
+typedef struct isdn_devt {
+  unsigned short    flags;                    /* Bitmapped Flags:           */
+                                              /*                            */
+  int               drivers;                  /* Current number of drivers  */
+  int               channels;                 /* Current number of channels */
+  int               net_verbose;               /* Verbose-Flag               */
+  int               modempoll;                /* Flag: tty-read active      */
+  int               tflags;                    /* Timer-Flags:               */
+                                              /*  see ISDN_TIMER_..defines  */
+  int               global_flags;
+  infostruct        *infochain;                /* List of open info-devs.    */
+  struct wait_queue *info_waitq;               /* Wait-Queue for isdninfo    */
+  struct timer_list timer;                    /* Misc.-function Timer       */
+  int               chanmap[ISDN_MAX_CHANNELS];/* Map minor->device-channel  */
+  int               drvmap[ISDN_MAX_CHANNELS]; /* Map minor->driver-index    */
+  int               usage[ISDN_MAX_CHANNELS];  /* Used by tty/ip/voice       */
+  char              num[ISDN_MAX_CHANNELS][20];/* Remote number of active ch.*/
+  int               m_idx[ISDN_MAX_CHANNELS];  /* Index for mdm....          */
+  driver            *drv[ISDN_MAX_DRIVERS];    /* Array of drivers           */
+  isdn_net_dev      *netdev;                  /* Linked list of net-if's    */
+  char              drvid[ISDN_MAX_DRIVERS][20];/* Driver-ID                 */
+  struct task_struct *profd;                   /* For iprofd                 */
+  modem             mdm;                      /* tty-driver-data            */
+  isdn_net_dev      *rx_netdev[ISDN_MAX_CHANNELS]; /* rx netdev-pointers     */
+  isdn_net_dev      *st_netdev[ISDN_MAX_CHANNELS]; /* stat netdev-pointers   */
+} isdn_dev;
+
+extern isdn_dev *dev;
+
+/* Utility-Macros */
+#define MIN(a,b) ((a<b)?a:b)
+#define MAX(a,b) ((a>b)?a:b)
+
+#endif /* __KERNEL__ */
+#endif /* isdn_h */
diff --git a/include/linux/isdn_ppp.h b/include/linux/isdn_ppp.h
new file mode 100644 (file)
index 0000000..b14fec0
--- /dev/null
@@ -0,0 +1,30 @@
+extern int isdn_ppp_dial_slave(char *);
+
+struct pppinfo
+{
+  int type; /* set by user */
+  union {
+    char clid[32]; /* calling ID */
+    int  bundles;
+    int  linknumber;
+  } info;
+};
+
+#define PPPIOCLINKINFO _IOWR('t',128,struct pppinfo)
+#define PPPIOCBUNDLE   _IOW('t',129,int)
+#define PPPIOCGMPFLAGS _IOR('t',130,int)
+#define PPPIOCSMPFLAGS _IOW('t',131,int)
+#define PPPIOCSMPMTU   _IOW('t',132,int)
+#define PPPIOCSMPMRU   _IOW('t',133,int)
+
+#define PPP_MP         0x003d
+
+#define SC_MP_PROT       0x00000200
+#define SC_REJ_MP_PROT   0x00000400
+#define SC_OUT_SHORT_SEQ 0x00000800
+#define SC_IN_SHORT_SEQ  0x00004000
+
+#define MP_END_FRAG    0x40
+#define MP_BEGIN_FRAG  0x80
+
diff --git a/include/linux/isdnif.h b/include/linux/isdnif.h
new file mode 100644 (file)
index 0000000..25e34f8
--- /dev/null
@@ -0,0 +1,267 @@
+/* $Id: isdnif.h,v 1.1 1996/01/09 05:50:51 fritz Exp fritz $
+ *
+ * Linux ISDN subsystem
+ *
+ * Definition of the interface between the subsystem and it's lowlevel-drivers.
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    Thinking Objects Software GmbH Wuerzburg
+ * 
+ * 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: isdnif.h,v $
+ * Revision 1.1  1996/01/09 05:50:51  fritz
+ * Initial revision
+ *
+ */
+
+#ifndef isdnif_h
+#define isdnif_h
+
+#ifdef STANDALONE
+#include <linux/k_compat.h>
+#endif
+
+/*
+ * Values for general protocol-selection
+ */
+#define ISDN_PTYPE_UNKNOWN   0   /* Protocol undefined   */
+#define ISDN_PTYPE_1TR6      1   /* german 1TR6-protocol */
+#define ISDN_PTYPE_EURO      2   /* EDSS1-protocol       */
+
+/*
+ * Values for Layer-2-protocol-selection
+ */
+#define ISDN_PROTO_L2_X75I   0   /* X75/LAPB with I-Frames      */
+#define ISDN_PROTO_L2_X75UI  1   /* X75/LAPB with UI-Frames     */
+#define ISDN_PROTO_L2_X75BUI 2   /* X75/LAPB with UI-Frames     */
+#define ISDN_PROTO_L2_HDLC   3   /* HDLC                        */
+
+/*
+ * Values for Layer-3-protocol-selection
+ */
+#define ISDN_PROTO_L3_TRANS  0   /* Transparent                 */
+
+#ifdef __KERNEL__
+
+#include <linux/skbuff.h>
+
+/*
+ * Commands from linklevel to lowlevel
+ *
+ */
+#define ISDN_CMD_IOCTL   0       /* Perform ioctl                         */
+#define ISDN_CMD_DIAL    1       /* Dial out                              */
+#define ISDN_CMD_ACCEPTD 2       /* Accept an incoming call on D-Chan.    */
+#define ISDN_CMD_ACCEPTB 3       /* Request B-Channel connect.            */
+#define ISDN_CMD_HANGUP  4       /* Hangup                                */
+#define ISDN_CMD_CLREAZ  5       /* Clear EAZ(s) of channel               */
+#define ISDN_CMD_SETEAZ  6       /* Set EAZ(s) of channel                 */
+#define ISDN_CMD_GETEAZ  7       /* Get EAZ(s) of channel                 */
+#define ISDN_CMD_SETSIL  8       /* Set Service-Indicator-List of channel */
+#define ISDN_CMD_GETSIL  9       /* Get Service-Indicator-List of channel */
+#define ISDN_CMD_SETL2  10       /* Set B-Chan. Layer2-Parameter          */
+#define ISDN_CMD_GETL2  11       /* Get B-Chan. Layer2-Parameter          */
+#define ISDN_CMD_SETL3  12       /* Set B-Chan. Layer3-Parameter          */
+#define ISDN_CMD_GETL3  13       /* Get B-Chan. Layer3-Parameter          */
+#define ISDN_CMD_LOCK   14       /* Signal usage by upper levels          */
+#define ISDN_CMD_UNLOCK 15       /* Release usage-lock                    */
+
+/*
+ * Status-Values delivered from lowlevel to linklevel via
+ * statcallb().
+ *
+ */
+#define ISDN_STAT_STAVAIL 256    /* Raw status-data available             */
+#define ISDN_STAT_ICALL   257    /* Incoming call detected                */
+#define ISDN_STAT_RUN     258    /* Signal protocol-code is running       */
+#define ISDN_STAT_STOP    259    /* Signal halt of protocol-code          */
+#define ISDN_STAT_DCONN   260    /* Signal D-Channel connect              */
+#define ISDN_STAT_BCONN   261    /* Signal B-Channel connect              */
+#define ISDN_STAT_DHUP    262    /* Signal D-Channel disconnect           */
+#define ISDN_STAT_BHUP    263    /* Signal B-Channel disconnect           */
+#define ISDN_STAT_CINF    264    /* Charge-Info                           */
+#define ISDN_STAT_LOAD    265    /* Signal new lowlevel-driver is loaded  */
+#define ISDN_STAT_UNLOAD  266    /* Signal unload of lowlevel-driver      */
+#define ISDN_STAT_BSENT   267    /* Signal packet sent                    */
+#define ISDN_STAT_NODCH   268    /* Signal no D-Channel                   */
+#define ISDN_STAT_ADDCH   269    /* Add more Channels                     */
+#define ISDN_STAT_CAUSE   270    /* Cause-Message                         */
+
+/*
+ * Values for feature-field of interface-struct.
+ */
+/* Layer 2 */
+#define ISDN_FEATURE_L2_X75I    (0x0001 << ISDN_PROTO_L2_X75I)
+#define ISDN_FEATURE_L2_X75UI   (0x0001 << ISDN_PROTO_L2_X75UI)
+#define ISDN_FEATURE_L2_X75BUI  (0x0001 << ISDN_PROTO_L2_X75BUI)
+#define ISDN_FEATURE_L2_HDLC    (0x0001 << ISDN_PROTO_L2_HDLC)
+
+/* Layer 3 */
+#define ISDN_FEATURE_L3_TRANS   (0x0100 << ISDN_PROTO_L3_TRANS)
+
+/* Signaling */
+#define ISDN_FEATURE_P_UNKNOWN  (0x1000 << ISDN_PTYPE_UNKNOWN)
+#define ISDN_FEATURE_P_1TR6     (0x1000 << ISDN_PTYPE_1TR6)
+#define ISDN_FEATURE_P_EURO     (0x1000 << ISDN_PTYPE_EURO)
+
+/*
+ * Structure for exchanging above infos
+ *
+ */
+typedef struct {
+  int   driver;                  /* Lowlevel-Driver-ID                    */
+  int   command;                 /* Command or Status (see above)         */
+  ulong arg;                     /* Additional Data                       */
+  char  num[50];                 /* Additional Data                       */
+} isdn_ctrl;
+
+/*
+ * The interface-struct itself (initialized at load-time of lowlevel-driver)
+ *
+ * See Documentation/isdn/INTERFACE for a description, how the communication
+ * between the ISDN subsystem and it's drivers is done.
+ *
+ */
+typedef struct {
+  /* Number of channels supported by this driver
+   */
+  int channels;
+
+  /* 
+   * Maximum Size of transmit/receive-buffer this driver supports.
+   */
+  int maxbufsize;
+
+  /* Feature-Flags for this driver.
+   * See defines ISDN_FEATURE_... for Values
+   */
+  unsigned long features;
+
+  /*
+   * Needed for calculating
+   * dev->hard_header_len = linklayer header + hl_hdrlen;
+   * Drivers, not supporting sk_buff's should set this to 0.
+   */
+  unsigned short hl_hdrlen;
+
+  /* Receive-Callback
+   * Parameters:
+   *             int    Driver-ID
+   *             int    local channel-number (0 ...)
+   *             u_char pointer to received data (in Kernel-Space, volatile)
+   *             int    length of data
+   */
+  void (*rcvcallb)(int, int, u_char*, int);
+
+  /*
+   * Receive-Callback using sk_buff's
+   * Parameters:
+   *             int                    Driver-ID
+   *             int                    local channel-number (0 ...)
+   *             struct sk_buff *skb    received Data
+   */
+  void (*rcvcallb_skb)(int, int, struct sk_buff *);
+
+  /* Status-Callback
+   * Parameters:
+   *             isdn_ctrl*
+   *                   driver  = Driver ID.
+   *                   command = One of above ISDN_STAT_... constants.
+   *                   arg     = depending on status-type.
+   *                   num     = depending on status-type.
+   */
+  int (*statcallb)(isdn_ctrl*);
+  /* Send command
+   * Parameters:
+   *             isdn_ctrl*
+   *                   driver  = Driver ID.
+   *                   command = One of above ISDN_CMD_... constants.
+   *                   arg     = depending on command.
+   *                   num     = depending on command.
+   */
+  int (*command)(isdn_ctrl*);
+  /* Send Data
+   * Parameters:
+   *             int    driverId
+   *             int    local channel-number (0 ...)
+   *             u_char pointer to data
+   *             int    length of data
+   *             int    Flag: 0 = Call form Kernel-Space (use memcpy,
+   *                              no schedule allowed) 
+   *                          1 = Data is in User-Space (use memcpy_fromfs,
+   *                              may schedule)
+   */
+  int (*writebuf)(int, int, const u_char*, int, int);
+
+  /*
+   * Send data using sk_buff's
+   * Parameters:
+   *             int                    driverId
+   *             int                    local channel-number (0...)
+   *             struct sk_buff *skb    Data to send
+   */
+  int (*writebuf_skb) (int, int, struct sk_buff *);
+
+  /* Send raw D-Channel-Commands
+   * Parameters:
+   *             u_char pointer data
+   *             int    length of data
+   *             int    Flag: 0 = Call form Kernel-Space (use memcpy,
+   *                              no schedule allowed) 
+   *                          1 = Data is in User-Space (use memcpy_fromfs,
+   *                              may schedule)
+   */
+  int (*writecmd)(const u_char*, int, int);
+  /* Read raw Status replies
+   *             u_char pointer data (volatile)
+   *             int    length of buffer
+   *             int    Flag: 0 = Call form Kernel-Space (use memcpy,
+   *                              no schedule allowed) 
+   *                          1 = Data is in User-Space (use memcpy_fromfs,
+   *                              may schedule)
+   */
+  int (*readstat)(u_char*, int, int);
+  char id[20];
+} isdn_if;
+
+/*
+ * Function which must be called by lowlevel-driver at loadtime with
+ * the following fields of above struct set:
+ *
+ * channels     Number of channels that will be supported.
+ * hl_hdrlen    Space to preserve in sk_buff's when sending. Drivers, not
+ *              supporting sk_buff's should set this to 0.
+ * command      Address of Command-Handler.
+ * features     Bitwise coded Features of this driver. (use ISDN_FEATURE_...)
+ * writebuf     Address of Send-Command-Handler.
+ * writebuf_skb Address of Skbuff-Send-Handler. (NULL if not supported)
+ * writecmd        "    "  D-Channel  " which accepts raw D-Ch-Commands.
+ * readstat        "    "  D-Channel  " which delivers raw Status-Data.
+ *
+ * The linklevel-driver fills the following fields:
+ *
+ * channels      Driver-ID assigned to this driver. (Must be used on all
+ *               subsequent callbacks.
+ * rcvcallb      Address of handler for received data.
+ * rcvcallb_skb  Address of handler for received Skbuff's. (NULL if not supp.)
+ * statcallb        "    "     "    for status-changes.
+ *
+ */
+extern int register_isdn(isdn_if*);
+
+#endif /* __KERNEL__ */
+#endif /* isdnif_h */
+
diff --git a/include/linux/linear.h b/include/linux/linear.h
new file mode 100644 (file)
index 0000000..b690737
--- /dev/null
@@ -0,0 +1,34 @@
+
+#ifndef _LINEAR_H
+#define _LINEAR_H
+
+struct linear_hash
+{
+  struct real_dev *dev0, *dev1;
+};
+
+struct linear_data
+{
+  struct linear_hash *hash_table; /* Dynamically allocated */
+  struct real_dev *smallest;
+  int nr_zones;
+};
+
+#endif
+
+#ifndef _LINEAR_H
+#define _LINEAR_H
+
+struct linear_hash
+{
+  struct real_dev *dev0, *dev1;
+};
+
+struct linear_data
+{
+  struct linear_hash *hash_table; /* Dynamically allocated */
+  struct real_dev *smallest;
+  int nr_zones;
+};
+
+#endif
index c1b2dcf0c44f7276cebe8082d7295a6a43e244da..b5b4831676d804a49f93ae0d904a3fbf92907fac 100644 (file)
@@ -26,7 +26,7 @@
  *  6 - lp
  *  7 - /dev/vcs*
  *  8 -                        scsi disk
- *  9 - scsi tape
+ *  9 - scsi tape              multiple devices driver
  * 10 - mice
  * 11 -                        scsi cdrom
  * 12 - qic02 tape
@@ -67,6 +67,7 @@
 #define VCS_MAJOR      7
 #define SCSI_DISK_MAJOR        8
 #define SCSI_TAPE_MAJOR        9
+#define MD_MAJOR        9
 #define MOUSE_MAJOR    10
 #define SCSI_CDROM_MAJOR 11
 #define QIC02_TAPE_MAJOR 12
diff --git a/include/linux/md.h b/include/linux/md.h
new file mode 100644 (file)
index 0000000..8bece08
--- /dev/null
@@ -0,0 +1,300 @@
+
+/*
+   md.h : Multiple Devices driver for Linux
+          Copyright (C) 1994-96 Marc ZYNGIER
+         <zyngier@ufr-info-p7.ibp.fr> or
+         <maz@gloups.fdn.fr>
+         
+   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.
+   
+   You should have received a copy of the GNU General Public License
+   (for example /usr/src/linux/COPYING); if not, write to the Free
+   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+*/
+
+#ifndef _MD_H
+#define _MD_H
+
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/ioctl.h>
+
+#define MD_VERSION "0.34"
+
+/* ioctls */
+#define REGISTER_DEV _IO (MD_MAJOR, 1)
+#define START_MD     _IO (MD_MAJOR, 2)
+#define STOP_MD      _IO (MD_MAJOR, 3)
+#define MD_INVALID   _IO (MD_MAJOR, 4)
+#define MD_VALID     _IO (MD_MAJOR, 5)
+
+/*
+   personalities :
+   Byte 0 : Chunk size factor
+   Byte 1 : Fault tolerance count for each physical device
+            (   0 means no fault tolerance,
+             0xFF means always tolerate faults)
+   Byte 2 : Personality
+   Byte 3 : Reserved.
+ */
+
+#define FAULT_SHIFT       8
+#define PERSONALITY_SHIFT 16
+
+#define FACTOR_MASK       0xFFUL
+#define FAULT_MASK        0xFF00UL
+#define PERSONALITY_MASK  0xFF0000UL
+
+#define MD_RESERVED       0    /* Not used by now */
+#define LINEAR            (1UL << PERSONALITY_SHIFT)
+#define STRIPED           (2UL << PERSONALITY_SHIFT)
+#define STRIPPED          STRIPED /* Long lasting spelling mistake... */
+#define RAID0             STRIPED
+#define RAID1             (3UL << PERSONALITY_SHIFT)
+#define RAID5             (4UL << PERSONALITY_SHIFT)
+#define MAX_PERSONALITY   5
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <sys/types.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+
+#undef MD_COUNT_SIZE           /* Define this to have stats about
+                                  chunk size in /proc/mdstat */
+#define MAX_REAL     8         /* Max number of physical dev per md dev */
+#define MAX_MD_DEV   4         /* Max number of md dev */
+
+#define FACTOR(a)         ((a)->repartition & FACTOR_MASK)
+#define MAX_FAULT(a)      (((a)->repartition & FAULT_MASK)>>8)
+#define PERSONALITY(a)    ((a)->repartition & PERSONALITY_MASK)
+
+#define FACTOR_SHIFT(a) (PAGE_SHIFT + (a) - 10)
+
+/* Invalidation modes */
+#define VALID          0
+#define INVALID_NEXT   1
+#define INVALID_ALWAYS 2
+#define INVALID        3       /* Only useful to md_valid_device */
+
+/* Return values from personalities to md driver */
+#define REDIRECTED_BHREQ 0 /* Redirected individual buffers
+                             (shouldn't be used anymore since 0.31) */
+#define REDIRECTED_REQ   1 /* Redirected whole request */
+#define REDIRECT_FAILED -1 /* For RAID-1 */
+
+struct real_dev
+{
+  kdev_t dev;                  /* Device number */
+  int size;                    /* Device size (in blocks) */
+  int offset;                  /* Real device offset (in blocks) in md dev
+                                  (only used in linear mode) */
+  struct inode *inode;         /* Lock inode */
+  int fault_count;             /* Fault counter for invalidation */
+  int invalid;                 /* Indicate if the device is disabled :
+                                  VALID          - valid
+                                  INVALID_NEXT   - disabled for next access
+                                  INVALID_ALWAYS - permanently disabled
+                                  (for redundancy modes only) */
+};
+
+struct md_dev;
+
+struct md_personality
+{
+  char *name;
+  int (*map)(int minor, struct md_dev *md_dev, struct request *req);
+  int (*run)(int minor, struct md_dev *md_dev);
+  int (*stop)(int minor, struct md_dev *md_dev);
+  int (*status)(char *page, int minor, struct md_dev *md_dev);
+  int (*ioctl)(struct inode *inode, struct file *file,
+              unsigned int cmd, unsigned long arg);
+  int max_invalid_dev;
+};
+
+struct md_dev
+{
+  struct md_personality *pers;
+  int repartition;
+  int invalid_dev_count;
+  int busy;
+  int nb_dev;
+  void *private;
+#ifdef MD_COUNT_SIZE
+  unsigned int smallest_count;
+  unsigned int biggest_count;
+  unsigned int equal_count;
+#endif
+};
+
+extern struct real_dev devices[MAX_MD_DEV][MAX_REAL];
+extern struct md_dev md_dev[MAX_MD_DEV];
+extern int md_size[MAX_MD_DEV];
+
+extern void make_md_request(struct request *pending, int n);
+extern char *partition_name (kdev_t dev);
+
+#if defined(CONFIG_MD_SUPPORT_RAID1) || defined(CONFIG_MD_SUPPORT_RAID5)
+extern int md_valid_device (int minor, kdev_t dev, int mode);
+extern int md_can_reemit (int minor);
+#endif
+
+extern int register_md_personality (int p_num, struct md_personality *p);
+extern int unregister_md_personality (int p_num);
+
+#endif __KERNEL__
+#endif _MD_H
+
+/*
+   md.h : Multiple Devices driver for Linux
+          Copyright (C) 1994, 1995 Marc ZYNGIER
+         <zyngier@amertume.ufr-info-p7.ibp.fr> or
+         <maz@gloups.fdn.fr>
+         
+   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.
+   
+   You should have received a copy of the GNU General Public License
+   (for example /usr/src/linux/COPYING); if not, write to the Free
+   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+*/
+
+#ifndef _MD_H
+#define _MD_H
+
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/ioctl.h>
+
+#define MD_VERSION "0.33"
+
+/* ioctls */
+#define REGISTER_DEV _IO (MD_MAJOR, 1)
+#define START_MD     _IO (MD_MAJOR, 2)
+#define STOP_MD      _IO (MD_MAJOR, 3)
+#define MD_INVALID   _IO (MD_MAJOR, 4)
+#define MD_VALID     _IO (MD_MAJOR, 5)
+
+/*
+   personalities :
+   Byte 0 : Chunk size factor
+   Byte 1 : Fault tolerance count for each physical device
+            (   0 means no fault tolerance,
+             0xFF means always tolerate faults)
+   Byte 2 : Personality
+   Byte 3 : Reserved.
+ */
+
+#define FAULT_SHIFT       8
+#define PERSONALITY_SHIFT 16
+
+#define FACTOR_MASK       0xFFUL
+#define FAULT_MASK        0xFF00UL
+#define PERSONALITY_MASK  0xFF0000UL
+
+#define MD_RESERVED       0    /* Not used by now */
+#define LINEAR            (1UL << PERSONALITY_SHIFT)
+#define STRIPED           (2UL << PERSONALITY_SHIFT)
+#define STRIPPED          STRIPED /* Long lasting spelling mistake... */
+#define RAID0             STRIPED
+#define RAID1             (3UL << PERSONALITY_SHIFT)
+#define RAID5             (4UL << PERSONALITY_SHIFT)
+#define MAX_PERSONALITY   5
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <sys/types.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+
+#undef MD_COUNT_SIZE           /* Define this to have stats about
+                                  chunk size in /proc/mdstat */
+#define MAX_REAL     8         /* Max number of physical dev per md dev */
+#define MAX_MD_DEV   4         /* Max number of md dev */
+
+#define FACTOR(a)         ((a)->repartition & FACTOR_MASK)
+#define MAX_FAULT(a)      (((a)->repartition & FAULT_MASK)>>8)
+#define PERSONALITY(a)    ((a)->repartition & PERSONALITY_MASK)
+
+#define FACTOR_SHIFT(a) (PAGE_SHIFT + (a) - 10)
+
+/* Invalidation modes */
+#define VALID          0
+#define INVALID_NEXT   1
+#define INVALID_ALWAYS 2
+#define INVALID        3       /* Only useful to md_valid_device */
+
+/* Return values from personalities to md driver */
+#define REDIRECTED_BHREQ 0 /* Redirected individual buffers
+                             (shouldn't be used anymore since 0.31) */
+#define REDIRECTED_REQ   1 /* Redirected whole request */
+#define REDIRECT_FAILED -1 /* For RAID-1 */
+
+struct real_dev
+{
+  kdev_t dev;                  /* Device number */
+  int size;                    /* Device size (in blocks) */
+  int offset;                  /* Real device offset (in blocks) in md dev
+                                  (only used in linear mode) */
+  struct inode *inode;         /* Lock inode */
+  int fault_count;             /* Fault counter for invalidation */
+  int invalid;                 /* Indicate if the device is disabled :
+                                  VALID          - valid
+                                  INVALID_NEXT   - disabled for next access
+                                  INVALID_ALWAYS - permanently disabled
+                                  (for redundancy modes only) */
+};
+
+struct md_dev;
+
+struct md_personality
+{
+  char *name;
+  int (*map)(int minor, struct md_dev *md_dev, struct request *req);
+  int (*run)(int minor, struct md_dev *md_dev);
+  int (*stop)(int minor, struct md_dev *md_dev);
+  int (*status)(char *page, int minor, struct md_dev *md_dev);
+  int (*ioctl)(struct inode *inode, struct file *file,
+              unsigned int cmd, unsigned long arg);
+  int max_invalid_dev;
+};
+
+struct md_dev
+{
+  struct md_personality *pers;
+  int repartition;
+  int invalid_dev_count;
+  int busy;
+  int nb_dev;
+  void *private;
+#ifdef MD_COUNT_SIZE
+  unsigned int smallest_count;
+  unsigned int biggest_count;
+  unsigned int equal_count;
+#endif
+};
+
+extern struct real_dev devices[MAX_MD_DEV][MAX_REAL];
+extern struct md_dev md_dev[MAX_MD_DEV];
+extern int md_size[MAX_MD_DEV];
+
+extern void make_md_request(struct request *pending, int n);
+extern char *partition_name (kdev_t dev);
+
+#if defined(CONFIG_MD_SUPPORT_RAID1) || defined(CONFIG_MD_SUPPORT_RAID5)
+extern int md_valid_device (int minor, kdev_t dev, int mode);
+extern int md_can_reemit (int minor);
+#endif
+
+extern int register_md_personality (int p_num, struct md_personality *p);
+extern int unregister_md_personality (int p_num);
+
+#endif __KERNEL__
+#endif _MD_H
index d024f7dfbcd39029f5f746c7f4e72157de6e678a..cdf1aa6248dde1d704a87b71a9b0f7b7aa8654e4 100644 (file)
@@ -239,8 +239,8 @@ extern void truncate_inode_pages(struct inode *, unsigned long);
 
 #define GFP_LEVEL_MASK 0xf
 
-#define avl_empty      (struct vm_area_struct *) NULL
-
+/* vma is the first one with  address < vma->vm_end,
+ * and even  address < vma->vm_start. Have to extend vma. */
 static inline int expand_stack(struct vm_area_struct * vma, unsigned long address)
 {
        unsigned long grow;
@@ -257,6 +257,8 @@ static inline int expand_stack(struct vm_area_struct * vma, unsigned long addres
        return 0;
 }
 
+#define avl_empty      (struct vm_area_struct *) NULL
+
 /* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
 static inline struct vm_area_struct * find_vma (struct task_struct * task, unsigned long addr)
 {
index fb96ea20f87112ce44f25e5b4b3eae0e962745cd..448734bfc4c61b88ac6eacf0b12259540ea91279 100644 (file)
@@ -19,9 +19,6 @@
 
 /* enable / disable parts of driver by define / undef */
 #define        MULTISESSION            /* multisession support (ALPHA) */
-#define        PROBE_ISP16             /* ISP16 interface card probing */
-/* ISP16 probing can also be suppressed with kernel command line option
-   'noisp16', or with insmod parameter 'noisp16=1'. */
 
 
 /* Change 0 to 1 to debug various parts of the driver */
@@ -35,8 +32,9 @@
 #define        DEBUG_VFS       0       /* VFS interface */
 
 
-/* Various timeout loop repetition counts. Don't touch unless you know
-   what you're doing. */
+/* Don't touch these unless you know what you're doing. */
+
+/* Various timeout loop repetition counts. */
 #define BUSY_TIMEOUT           10000000        /* for busy wait */
 #define FAST_TIMEOUT           100000          /* ibid. for probing */
 #define SLEEP_TIMEOUT          3000            /* for timer wait */
 #define STOP_TIMEOUT           1000            /* for poll wait */
 #define RESET_WAIT             1000            /* busy wait at drive reset */
 
+/* # of buffer for block size conversion. 6 is optimal for my setup (P75),
+   giving 280 kb/s, with 0.4% CPU usage. Experiment to find your optimal
+   setting */
+#define N_BUFS         6
+
+
 #endif _LINUX_OPTCD_H
index 16e061cd0de0df027c4ca2d95a345ed6a39d26ec..f1f5a142fdc15b16a2afebb2a4b9a5bcf2558203 100644 (file)
@@ -9,6 +9,9 @@
  * Copyright 1995 Linus Torvalds
  */
 
+#include <linux/mm.h>
+#include <linux/fs.h>
+
 static inline unsigned long page_address(struct page * page)
 {
        return PAGE_OFFSET + PAGE_SIZE*(page - mem_map);
@@ -45,12 +48,13 @@ static inline struct page * find_page(struct inode * inode, unsigned long offset
 {
        struct page *page;
        unsigned long flags;
-       
+
        for (page = page_hash(inode, offset); page ; page = page->next_hash) {
                if (page->inode != inode)
                        continue;
                if (page->offset != offset)
                        continue;
+               /* Found the page. */
                save_flags(flags);
                cli();
                page->referenced = 1;
index 21b39d628a370c28693ff92b44d0240a428ca5af..6971535701f80a16ef23ec0508b3ad48526b39c2 100644 (file)
@@ -1,7 +1,9 @@
 #ifndef _LINUX_PIPE_FS_I_H
 #define _LINUX_PIPE_FS_I_H
+#include <linux/nfs_fs_i.h>
 
 struct pipe_inode_info {
+       struct nfs_inode_info dummy;   /* NFS/fifo conflict workaround */
        struct wait_queue * wait;
        char * base;
        unsigned int start;
index 84533d01140a807415003ba9b80216d6e547a59f..3b5104cc4c1d21737a93adf41a94f49de4131e81 100644 (file)
@@ -28,7 +28,7 @@
  */
 
 /*
- *  ==FILEVERSION 4==
+ *  ==FILEVERSION 5==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, increment the number above.
  * Protocol field values.
  */
 #define PPP_IP         0x21    /* Internet Protocol */
+#define PPP_IPX                0x2b    /* IPX protocol */
 #define        PPP_VJC_COMP    0x2d    /* VJ compressed TCP */
 #define        PPP_VJC_UNCOMP  0x2f    /* VJ uncompressed TCP */
 #define PPP_COMP       0xfd    /* compressed packet */
 #define PPP_IPCP       0x8021  /* IP Control Protocol */
+#define PPP_IPXCP      0x802b  /* IPX Control Protocol */
 #define PPP_CCP                0x80fd  /* Compression Control Protocol */
 #define PPP_LCP                0xc021  /* Link Control Protocol */
 #define PPP_PAP                0xc023  /* Password Authentication Protocol */
index cc674c9b9130ce8dba1d8da2dfc3c014f04aea29..973d460bdc51d027654a0cf9fd403f7f4bce901b 100644 (file)
@@ -41,7 +41,8 @@ enum root_directory_inos {
        PROC_PROFILE, /* whether enabled or not */
        PROC_CMDLINE,
        PROC_SYS,
-       PROC_MTAB
+       PROC_MTAB,
+       PROC_MD
 };
 
 enum pid_directory_inos {
diff --git a/include/linux/raid0.h b/include/linux/raid0.h
new file mode 100644 (file)
index 0000000..2e3e69b
--- /dev/null
@@ -0,0 +1,56 @@
+
+#ifndef _RAID0_H
+#define _RAID0_H
+
+struct strip_zone
+{
+  int zone_offset;             /* Zone offset in md_dev */
+  int dev_offset;              /* Zone offset in real dev */
+  int size;                    /* Zone size */
+  int nb_dev;                  /* Number of devices attached to the zone */
+  struct real_dev *dev[MAX_REAL]; /* Devices attached to the zone */
+};
+
+struct raid0_hash
+{
+  struct strip_zone *zone0, *zone1;
+};
+
+struct raid0_data
+{
+  struct raid0_hash *hash_table; /* Dynamically allocated */
+  struct strip_zone *strip_zone; /* This one too */
+  int nr_strip_zones;
+  struct strip_zone *smallest;
+  int nr_zones;
+};
+
+#endif
+
+#ifndef _RAID0_H
+#define _RAID0_H
+
+struct strip_zone
+{
+  int zone_offset;             /* Zone offset in md_dev */
+  int dev_offset;              /* Zone offset in real dev */
+  int size;                    /* Zone size */
+  int nb_dev;                  /* Number of devices attached to the zone */
+  struct real_dev *dev[MAX_REAL]; /* Devices attached to the zone */
+};
+
+struct raid0_hash
+{
+  struct strip_zone *zone0, *zone1;
+};
+
+struct raid0_data
+{
+  struct raid0_hash *hash_table; /* Dynamically allocated */
+  struct strip_zone *strip_zone; /* This one too */
+  int nr_strip_zones;
+  struct strip_zone *smallest;
+  int nr_zones;
+};
+
+#endif
index 0f0da6858a776e892413f45e5d4ce8041692cdf0..dfdc05aa60f5a87654cc3580aab0c0d7dab3409c 100644 (file)
@@ -24,8 +24,8 @@
  * SUCH DAMAGE.
  */
 
-#define SOUND_VERSION  301
-#define VOXWARE
+#define SOUND_VERSION  350
+#define UNIX_SOUND_SYSTEM
 
 #include <sys/ioctl.h>
 
@@ -597,6 +597,7 @@ typedef struct audio_buf_info {
                                                        /* Sometimes it's a DSP */
                                                        /* but usually not */
 #      define DSP_CAP_TRIGGER          0x00001000      /* Supports SETTRIGGER */
+#      define DSP_CAP_MMAP             0x00002000      /* Supports mmap() */
 
 #define SNDCTL_DSP_GETTRIGGER          _IOR ('P',16, int)
 #define SNDCTL_DSP_SETTRIGGER          _IOW ('P',16, int)
@@ -730,17 +731,17 @@ typedef struct copr_msg {
 /* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */
 #define SOUND_ONOFF_MIN                28
 #define SOUND_ONOFF_MAX                30
-#define SOUND_MIXER_MUTE       28      /* 0 or 1 */
-#define SOUND_MIXER_LOUD       30      /* 0 or 1 */
 
 /* Note!       Number 31 cannot be used since the sign bit is reserved */
 
 
 /*
- * SOUND_MIXER_ENHANCE is an unsupported and undocumented call which
- * will be removed from the API in future.
+ * The following unsupported macros will be removed from the API in near
+ * future.
  */
 #define SOUND_MIXER_ENHANCE    29      /* Enhanced stereo (0, 40, 60 or 80) */
+#define SOUND_MIXER_MUTE       28      /* 0 or 1 */
+#define SOUND_MIXER_LOUD       30      /* 0 or 1 */
 
 
 
index 87390b3c332982a0d7a733e2192c63c3551aa3e5..d58fe447ec4b4a8476d17c77d4c2ed29780ae103 100644 (file)
@@ -80,8 +80,6 @@ extern void swap_free(unsigned long);
  */
 #define SHM_SWP_TYPE 0x40
 
-extern void shm_no_page (ulong *);
-
 /*
  * swap cache stuff (in linux/mm/swap_state.c)
  */
index 9e22b52b5430b206c2716b76b71b3159a15c9960..cd65fa02c077f9bbbbe33c67dd942b99fc70abcb 100644 (file)
@@ -60,7 +60,8 @@ struct __sysctl_args {
 #define VM_SWAPCTL     1       /* struct: Set vm swapping control */
 #define VM_KSWAPD      2       /* struct: control background pagout */
 #define VM_FREEPG      3       /* struct: Set free page thresholds */
-#define VM_MAXID       4
+#define VM_BDFLUSH     4       /* struct: Control buffer cache flushing */
+#define VM_MAXID       5
 
 /* CTL_NET names: */
 
@@ -91,6 +92,8 @@ extern int proc_dostring(ctl_table *, int, struct file *,
                         void *, size_t *);
 extern int proc_dointvec(ctl_table *, int, struct file *,
                         void *, size_t *);
+extern int proc_dointvec_minmax(ctl_table *, int, struct file *,
+                               void *, size_t *);
 
 extern int do_sysctl (int *name, int nlen,
                      void *oldval, size_t *oldlenp,
@@ -102,6 +105,7 @@ extern int do_sysctl_strategy (ctl_table *table,
                               void *newval, size_t newlen, void ** context);
 
 extern ctl_handler sysctl_string;
+extern ctl_handler sysctl_intvec;
 
 extern int do_string (
        void *oldval, size_t *oldlenp, void *newval, size_t newlen,
@@ -163,6 +167,8 @@ struct ctl_table
        proc_handler *proc_handler;     /* Callback for text formatting */
        ctl_handler *strategy;          /* Callback function for all r/w */
        struct proc_dir_entry *de;      /* /proc control block */
+       void *extra1;
+       void *extra2;
 };
 
 /* struct ctl_table_header is used to maintain dynamic lists of
index cfe6b106eb74883c6f083c264d720e99c7b73151..f5409b70e1aeb169ea86af176cd1baf32dd65f6e 100644 (file)
@@ -316,8 +316,6 @@ struct sysv_dir_entry {
 #define SYSV_DIRSIZE   sizeof(struct sysv_dir_entry)   /* size of every directory entry */
 
 
-#ifdef __KERNEL__
-
 /* Operations */
 /* ========== */
 
@@ -335,6 +333,8 @@ struct sysv_dir_entry {
 #define SYSV2_SUPER_MAGIC      (SYSV_MAGIC_BASE+FSTYPE_SYSV2)
 #define COH_SUPER_MAGIC                (SYSV_MAGIC_BASE+FSTYPE_COH)
 
+#ifdef __KERNEL__
+
 /* sv_get_hash_table(sb,dev,block) is equivalent to  get_hash_table(dev,block,block_size)  */
 static inline struct buffer_head *
 sv_get_hash_table (struct super_block *sb, kdev_t dev, unsigned int block)
index c54e8c5e675af285126d24015152a96d88110f7c..7a89bae8d0dd8d23558e14925b80680550771688 100644 (file)
@@ -35,8 +35,6 @@
  *
  * GSCD_TIMER          Goldstar CD-ROM Timer
  *
- * OPTCD_TIMER         Optics Storage CD-ROM Timer
- *
  */
 
 #define BLANK_TIMER    0
@@ -56,7 +54,6 @@
 
 #define HD_TIMER2      24
 #define GSCD_TIMER     25
-#define OPTCD_TIMER    26
 
 struct timer_struct {
        unsigned long expires;
index 3e80057506f8980c90cb6fce91f8b4fa682c8f3a..667a2fad17efa000971de3187ea3c3cca37affe5 100644 (file)
@@ -29,6 +29,7 @@
  * SUCH DAMAGE.
  */
 
+
 /*
  *     Private events for Gravis Ultrasound (GUS)
  *
index dc7a4a90ac753d923783f4d56caf9fb04e7c8f23..dfe4ddfe6e7160001fc66aaf0cc9669d3f45f2d9 100644 (file)
@@ -86,7 +86,61 @@ struct inet_packet_opt
        char                    device_name[15];
 };
 
+/*
+ *     Once the IPX ncpd patches are in these are going into protinfo
+ */
+
+#ifdef CONFIG_IPX 
+struct ipx_opt
+{
+       ipx_address             dest_addr;
+       ipx_interface           *intrfc;
+       unsigned short          port;
+#ifdef CONFIG_IPX_INTERN
+       unsigned char           node[IPX_NODE_LEN];
+#endif
+       unsigned short          type;
+/* 
+ * To handle asynchronous messages from the NetWare server, we have to
+ * know the connection this socket belongs to. 
+ */
+       struct ncp_server       *ncp_server;
+       
+};
+#endif
 
+#ifdef CONFIG_NUTCP
+struct tcp_opt
+{
+/*
+ *     RFC793 variables by their proper names. This means you can
+ *     read the code and the spec side by side (and laugh ...)
+ *     See RFC793 and RFC1122. The RFC writes these in capitals.
+ */
+       __u32   rcv_nxt;        /* What we want to receive next         */
+       __u32   rcv_up;         /* The urgent point (may not be valid)  */
+       __u32   rcv_wnd;        /* Current receiver window              */
+       __u32   snd_nxt;        /* Next sequence we send                */
+       __u32   snd_una;        /* First byte we want an ack for        */
+       __u32   snd_up;         /* Outgoing urgent pointer              */
+       __u32   snd_wl1;        /* Sequence for window update           */
+       __u32   snd_wl2;        /* Ack sequence for update              */
+/*
+ *     Slow start and congestion control (see also Nagle, and Karn & Partridge)
+ */
+       __u32   snd_cwnd;       /* Sending congestion window            */
+       __u32   snd_ssthresh;   /* Slow start size threshold            */
+/*
+ *     Timers used by the TCP protocol layer
+ */
+       struct timer_list       delack_timer;           /* Ack delay    */
+       struct timer_list       idle_timer;             /* Idle watch   */
+       struct timer_list       completion_timer;       /* Up/Down timer */
+       struct timer_list       probe_timer;            /* Probes       */
+       struct timer_list       retransmit_timer;       /* Resend (no ack) */
+};
+#endif
+       
 /*
  * This structure really needs to be cleaned up.
  * Most of it is for TCP, and not used by any of
@@ -190,24 +244,6 @@ struct sock
        unsigned short          sndbuf;
        unsigned short          type;
        unsigned char           localroute;     /* Route locally only */
-#ifdef CONFIG_IPX
-/*
- *     Once the IPX ncpd patches are in these are going into protinfo
- */
-       ipx_address             ipx_dest_addr;
-       ipx_interface           *ipx_intrfc;
-       unsigned short          ipx_port;
-
-/* To handle asynchronous messages from the NetWare server, we have to
- * know the connection this socket belongs to. Sorry to blow up this
- * structure even more. */
-       struct ncp_server       *ipx_ncp_server;
-
-#ifdef CONFIG_IPX_INTERN
-       unsigned char           ipx_node[IPX_NODE_LEN];
-#endif
-       unsigned short          ipx_type;
-#endif
 #ifdef CONFIG_AX25
        ax25_cb                 *ax25;
 #ifdef CONFIG_NETROM
@@ -226,8 +262,14 @@ struct sock
 #ifdef CONFIG_ATALK
                struct atalk_sock       af_at;
 #endif
+#ifdef CONFIG_IPX
+               struct ipx_opt          af_ipx;
+#endif         
 #ifdef CONFIG_INET
                struct inet_packet_opt  af_packet;
+#ifdef CONFIG_NUTCP            
+               struct tcp_opt          af_tcp;
+#endif         
 #endif
        } protinfo;             
 
@@ -331,11 +373,12 @@ struct proto
 #define TIME_DESTROY   4
 #define TIME_DONE      5       /* Used to absorb those last few packets */
 #define TIME_PROBE0    6
+
 /*
  *     About 10 seconds 
  */
-#define SOCK_DESTROY_TIME (10*HZ)
 
+#define SOCK_DESTROY_TIME (10*HZ)
 
 /*
  *     Sockets 0-1023 can't be bound too unless you are superuser 
@@ -343,7 +386,6 @@ struct proto
  
 #define PROT_SOCK      1024
 
-
 #define SHUTDOWN_MASK  3
 #define RCV_SHUTDOWN   1
 #define SEND_SHUTDOWN  2
index 2b0913462b1605b9c42f419f135c64f959976573..0969d5b6a10cb7899f4b87ebdfbd4bd162ad25f8 100644 (file)
@@ -116,6 +116,12 @@ static void ramdisk_start_setup(char *str, int *ints);
 static void load_ramdisk(char *str, int *ints);
 static void prompt_ramdisk(char *str, int *ints);
 #endif CONFIG_BLK_DEV_RAM
+#ifdef CONFIG_ISDN_DRV_ICN
+extern void icn_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_ISDN_DRV_TELES
+extern void teles_setup(char *str, int *ints);
+#endif
 
 #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 extern void ipc_init(void);
@@ -297,6 +303,12 @@ struct {
 #endif CONFIG_ISP16_CDI
 #ifdef CONFIG_SOUND
        { "sound=", sound_setup },
+#endif
+#ifdef CONFIG_ISDN_DRV_ICN
+       { "icn=", icn_setup },
+#endif
+#ifdef CONFIG_ISDN_DRV_TELES
+       { "teles=", teles_setup },
 #endif
        { 0, 0 }
 };
index d7e7312248b8eadc8e963700042a45c3675a4c7a..994442692a09b08458aadf5c82e626c69abf8275 100644 (file)
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -189,14 +189,15 @@ static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgty
        while (!nmsg) {
                if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
                        return -EIDRM;
-               if ((msgflg & IPC_KERNELD) == 0)
+               if ((msgflg & IPC_KERNELD) == 0) {
                        /*
                         * Non-root processes may receive from kerneld! 
                         * i.e. no permission check if called from the kernel
                         * otoh we don't want user level non-root snoopers...
                         */
-               if (ipcperms (ipcp, S_IRUGO))
-                       return -EACCES;
+                       if (ipcperms (ipcp, S_IRUGO))
+                               return -EACCES;
+               }
                if (msgtyp == 0) 
                        nmsg = msq->msg_first;
                else if (msgtyp > 0) {
index 4da405a07cbfb59edada244f1bb212d65ec1f9cb..ef25cb1b951a423588fdaaf3415ce01e14cbe0a8 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -351,7 +351,7 @@ asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
  * The per process internal structure for managing segments is
  * `struct vm_area_struct'.
  * A shmat will add to and shmdt will remove from the list.
- * shmd->vm_task       the attacher
+ * shmd->vm_mm         the attacher
  * shmd->vm_start      virt addr of attach, multiple of SHMLBA
  * shmd->vm_end                multiple of SHMLBA
  * shmd->vm_next       next attach for task
@@ -765,7 +765,7 @@ int shm_swap (int prio, unsigned long limit)
                mem_map[MAP_NR(pte_page(pte))].count--;
                if (shmd->vm_mm->rss > 0)
                        shmd->vm_mm->rss--;
-               invalidate_range(shmd->vm_mm, shmd->vm_start, shmd->vm_end);
+               invalidate_page(shmd, tmp);
            /* continue looping through circular list */
            } while (0);
            if ((shmd = shmd->vm_next_share) == shp->attaches)
index 3708eff49182156ce0f12f2b77ce0fb9f7ec5ab5..f1c5e99adaf22c4b3f4539b0d39badd41343b742 100644 (file)
@@ -226,11 +226,11 @@ struct symbol_table symbol_table = {
        X(bmap),
        X(sync_dev),
        X(get_blkfops),
-       
+#ifdef CONFIG_SERIAL   
        /* Module creation of serial units */
        X(register_serial),
        X(unregister_serial),
-
+#endif
        /* tty routines */
        X(tty_hangup),
        X(tty_wait_until_sent),
index d934f0e3165f76c0edaa3fa0875d57d9ff15ede4..ab3bded440aeaf3edd735c65d5ef2a2fcecd892c 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Begun 24 March 1995, Stephen Tweedie
  * Added /proc support, Dec 1995
+ * Added bdflush entry and intvec min/max checking, 2/23/96, Tom Dyas.
  */
 
 #include <linux/config.h>
@@ -81,6 +82,8 @@ static void register_proc_table(ctl_table *, struct proc_dir_entry *);
 static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
 #endif
 
+extern int bdf_prm[], bdflush_min[], bdflush_max[];
+
 static int do_securelevel_strategy (ctl_table *, int *, int, void *, size_t *,
                                    void *, size_t, void **);
 
@@ -123,6 +126,9 @@ static ctl_table vm_table[] = {
         &kswapd_ctl, sizeof(kswapd_ctl), 0600, NULL, &proc_dointvec},
        {VM_FREEPG, "freepages", 
         &min_free_pages, 3*sizeof(int), 0600, NULL, &proc_dointvec},
+       {VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL,
+        &proc_dointvec_minmax, &sysctl_intvec, NULL,
+        &bdflush_min, &bdflush_max},
        {0}
 };
 
@@ -582,6 +588,88 @@ int proc_dointvec(ctl_table *table, int write, struct file *filp,
        return 0;
 }
 
+int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
+                 void *buffer, size_t *lenp)
+{
+       int *i, *min, *max, vleft, first=1, len, left, neg, val;
+       #define TMPBUFLEN 20
+       char buf[TMPBUFLEN], *p;
+       
+       if (!table->data || !table->maxlen || !*lenp ||
+           (filp->f_pos && !write)) {
+               *lenp = 0;
+               return 0;
+       }
+       
+       i = (int *) table->data;
+       min = (int *) table->extra1;
+       max = (int *) table->extra2;
+       vleft = table->maxlen / sizeof(int);
+       left = *lenp;
+       
+       for (; left && vleft--; i++, first=0) {
+               if (write) {
+                       while (left && isspace(get_user((char *) buffer)))
+                               left--, ((char *) buffer)++;
+                       if (!left)
+                               break;
+                       neg = 0;
+                       len = left;
+                       if (len > TMPBUFLEN-1)
+                               len = TMPBUFLEN-1;
+                       memcpy_fromfs(buf, buffer, len);
+                       buf[len] = 0;
+                       p = buf;
+                       if (*p == '-' && left > 1) {
+                               neg = 1;
+                               left--, p++;
+                       }
+                       if (*p < '0' || *p > '9')
+                               break;
+                       val = simple_strtoul(p, &p, 0);
+                       len = p-buf;
+                       if ((len < left) && *p && !isspace(*p))
+                               break;
+                       if (neg)
+                               val = -val;
+                       buffer += len;
+                       left -= len;
+
+                       if (min && val < *min++)
+                               continue;
+                       if (max && val > *max++)
+                               continue;
+                       *i = val;
+               } else {
+                       p = buf;
+                       if (!first)
+                               *p++ = '\t';
+                       sprintf(p, "%d", *i);
+                       len = strlen(buf);
+                       if (len > left)
+                               len = left;
+                       memcpy_tofs(buffer, buf, len);
+                       left -= len;
+                       buffer += len;
+               }
+       }
+
+       if (!write && !first && left) {
+               put_user('\n', (char *) buffer);
+               left--, buffer++;
+       }
+       if (write) {
+               p = (char *) buffer;
+               while (left && isspace(get_user(p++)))
+                       left--;
+       }
+       if (write && first)
+               return -EINVAL;
+       *lenp -= left;
+       filp->f_pos += *lenp;
+       return 0;
+}
+
 #else /* CONFIG_PROC_FS */
 
 int proc_dostring(ctl_table *table, int write, struct file *filp,
@@ -596,6 +684,12 @@ int proc_dointvec(ctl_table *table, int write, struct file *filp,
        return -ENOSYS;
 }
 
+int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
+                   void *buffer, size_t *lenp)
+{
+       return -ENOSYS;
+}
+
 #endif /* CONFIG_PROC_FS */
 
 
@@ -635,6 +729,43 @@ int sysctl_string(ctl_table *table, int *name, int nlen,
        return 0;
 }
 
+/*
+ * This function makes sure that all of the integers in the vector
+ * are between the minimum and maximum values given in the arrays
+ * table->extra1 and table->extra2, respectively.
+ */
+int sysctl_intvec(ctl_table *table, int *name, int nlen,
+               void *oldval, size_t *oldlenp,
+               void *newval, size_t newlen, void **context)
+{
+       int i, length, *vec, *min, *max;
+
+       if (newval && newlen) {
+               if (newlen % sizeof(int) != 0)
+                       return -EINVAL;
+
+               if (!table->extra1 && !table->extra2)
+                       return 0;
+
+               if (newlen > table->maxlen)
+                       newlen = table->maxlen;
+               length = newlen / sizeof(int);
+
+               vec = (int *) newval;
+               min = (int *) table->extra1;
+               max = (int *) table->extra2;
+
+               for (i = 0; i < length; i++) {
+                       int value = get_user(vec + i);
+                       if (min && value < min[i])
+                               return -EINVAL;
+                       if (max && value > max[i])
+                               return -EINVAL;
+               }
+       }
+       return 0;
+}
+
 int do_string (
        void *oldval, size_t *oldlenp, void *newval, size_t newlen,
        int rdwr, char *data, size_t max)
index e41524bc04814478f3d6839d0219222196c18c15..87129f6e2a5821c4d98472027b2ae208ae133111 100644 (file)
@@ -293,19 +293,24 @@ repeat:
  * This is a generic file read routine, and uses the
  * inode->i_op->readpage() function for the actual low-level
  * stuff.
+ *
+ * This is really ugly. But the goto's actually try to clarify some
+ * of the logic when it comes to error handling etc.
  */
 #define MAX_READAHEAD (PAGE_SIZE*4)
 int generic_file_read(struct inode * inode, struct file * filp, char * buf, int count)
 {
-       int read = 0;
-       unsigned long pos;
-       unsigned long page_cache = 0;
+       int error, read;
+       unsigned long pos, page_cache;
        
        if (count <= 0)
                return 0;
+       error = 0;
+       read = 0;
+       page_cache = 0;
 
        pos = filp->f_pos;
-       do {
+       for (;;) {
                struct page *page;
                unsigned long offset, addr, nr;
 
@@ -324,14 +329,14 @@ int generic_file_read(struct inode * inode, struct file * filp, char * buf, int
                 * Ok, it wasn't cached, so we need to create a new
                 * page..
                 */
-               if (!page_cache) {
-                       page_cache = __get_free_page(GFP_KERNEL);
-                       if (!page_cache) {
-                               if (!read)
-                                       read = -ENOMEM;
-                               break;
-                       }
-               }
+               if (page_cache)
+                       goto new_page;
+
+               error = -ENOMEM;
+               page_cache = __get_free_page(GFP_KERNEL);
+               if (!page_cache)
+                       break;
+               error = 0;
 
                /*
                 * That could have slept, so we need to check again..
@@ -339,22 +344,8 @@ int generic_file_read(struct inode * inode, struct file * filp, char * buf, int
                if (pos >= inode->i_size)
                        break;
                page = find_page(inode, pos & PAGE_MASK);
-               if (page)
-                       goto found_page;
-
-               /*
-                * Ok, add the new page to the hash-queues...
-                */
-               page = mem_map + MAP_NR(page_cache);
-               page_cache = 0;
-               page->count++;
-               page->uptodate = 0;
-               page->error = 0;
-               page->offset = pos & PAGE_MASK;
-               add_page_to_inode_queue(inode, page);
-               add_page_to_hash_queue(inode, page);
-
-               inode->i_op->readpage(inode, page);
+               if (!page)
+                       goto new_page;
 
 found_page:
                addr = page_address(page);
@@ -369,15 +360,20 @@ found_page:
                 *  - if "f_reada" is set
                 */
                if (page->locked) {
-                       if (nr < count || filp->f_reada) {
-                               unsigned long ahead = 0;
-                               do {
-                                       ahead += PAGE_SIZE;
-                                       page_cache = try_to_read_ahead(inode, pos + ahead, page_cache);
-                               } while (ahead < MAX_READAHEAD);
+                       unsigned long max_ahead, ahead;
+
+                       max_ahead = count - nr;
+                       if (filp->f_reada || max_ahead > MAX_READAHEAD)
+                               max_ahead = MAX_READAHEAD;
+                       ahead = 0;
+                       while (ahead < max_ahead) {
+                               ahead += PAGE_SIZE;
+                               page_cache = try_to_read_ahead(inode, pos + ahead, page_cache);
                        }
                        __wait_on_page(page);
                }
+               if (!page->uptodate)
+                       goto read_page;
                if (nr > inode->i_size - pos)
                        nr = inode->i_size - pos;
                memcpy_tofs(buf, (void *) (addr + offset), nr);
@@ -386,7 +382,40 @@ found_page:
                pos += nr;
                read += nr;
                count -= nr;
-       } while (count);
+               if (count)
+                       continue;
+               break;
+       
+
+new_page:
+               /*
+                * Ok, add the new page to the hash-queues...
+                */
+               addr = page_cache;
+               page = mem_map + MAP_NR(page_cache);
+               page_cache = 0;
+               page->count++;
+               page->uptodate = 0;
+               page->error = 0;
+               page->offset = pos & PAGE_MASK;
+               add_page_to_inode_queue(inode, page);
+               add_page_to_hash_queue(inode, page);
+
+               /*
+                * Error handling is tricky. If we get a read error,
+                * the cached page stays in the cache (but uptodate=0),
+                * and the next process that accesses it will try to
+                * re-read it. This is needed for NFS etc, where the
+                * identity of the reader can decide if we can read the
+                * page or not..
+                */
+read_page:
+               error = inode->i_op->readpage(inode, page);
+               if (!error)
+                       goto found_page;
+               free_page(addr);
+               break;
+       }
 
        filp->f_pos = pos;
        filp->f_reada = 1;
@@ -396,6 +425,8 @@ found_page:
                inode->i_atime = CURRENT_TIME;
                inode->i_dirt = 1;
        }
+       if (!read)
+               read = error;
        return read;
 }
 
index 2e73a927d58b464f6a7e8a886c7193b4173eb2ea..c1d762b47a4cca1aaa9964360778143c6d9f35c2 100644 (file)
@@ -19,6 +19,8 @@
 #include <asm/system.h>
 #include <asm/dma.h>
 
+/* Define this is you want slow routines that try to trip errors */
+#undef SADISTIC_KMALLOC
 
 /* Private flags. */
 
@@ -270,6 +272,9 @@ found_it:
        sizes[order].nbytesmalloced += size;
        p->bh_flags = type;     /* As of now this block is officially in use */
        p->bh_length = size;
+#ifdef SADISTIC_KMALLOC
+       memset(p+1, 0xf0, size);
+#endif
        return p + 1;           /* Pointer arithmetic: increments past header */
 }
 
@@ -302,6 +307,9 @@ void kfree(void *ptr)
        }
        size = p->bh_length;
        p->bh_flags = MF_FREE;  /* As of now this block is officially free */
+#ifdef SADISTIC_KMALLOC
+       memset(p+1, 0xe0, size);
+#endif
        save_flags(flags);
        cli();
        p->bh_next = page->firstfree;
index 9b2444488b53a1b04726dbcbfc059f6c6566e285..51234e0f1ba813a0cbc37d62ee7d17051f8c759d 100644 (file)
@@ -431,6 +431,7 @@ int zeromap_page_range(unsigned long address, unsigned long size, pgprot_t prot)
 {
        int error = 0;
        pgd_t * dir;
+       unsigned long beg = address;
        unsigned long end = address + size;
        pte_t zero_pte;
 
@@ -447,7 +448,7 @@ int zeromap_page_range(unsigned long address, unsigned long size, pgprot_t prot)
                address = (address + PGDIR_SIZE) & PGDIR_MASK;
                dir++;
        }
-       invalidate_range(current->mm, end - size, end);
+       invalidate_range(current->mm, beg, end);
        return error;
 }
 
@@ -502,6 +503,7 @@ int remap_page_range(unsigned long from, unsigned long offset, unsigned long siz
 {
        int error = 0;
        pgd_t * dir;
+       unsigned long beg = from;
        unsigned long end = from + size;
 
        offset -= from;
@@ -517,7 +519,7 @@ int remap_page_range(unsigned long from, unsigned long offset, unsigned long siz
                from = (from + PGDIR_SIZE) & PGDIR_MASK;
                dir++;
        }
-       invalidate_range(current->mm, from - size, from);
+       invalidate_range(current->mm, beg, from);
        return error;
 }
 
index 2879b955da8fe3c4c689d601bcd69eadf7fa6d87..8e3dd33d99f56ef433c8908f131ac5f3b960d4e7 100644 (file)
@@ -106,12 +106,6 @@ static int mlock_fixup(struct vm_area_struct * vma,
        if (newflags == vma->vm_flags)
                return 0;
 
-       /* keep track of amount of locked VM */
-       pages = (end - start) >> PAGE_SHIFT;
-       if (!(newflags & VM_LOCKED))
-               pages = -pages;
-       vma->vm_mm->locked_vm += pages;
-
        if (start == vma->vm_start) {
                if (end == vma->vm_end)
                        retval = mlock_fixup_all(vma, newflags);
@@ -123,12 +117,19 @@ static int mlock_fixup(struct vm_area_struct * vma,
                else
                        retval = mlock_fixup_middle(vma, start, end, newflags);
        }
-       if (!retval && (newflags & VM_LOCKED)) {
-               while (start < end) {
-                       char c = get_user((char *) start);
-                       __asm__ __volatile__("": :"r" (c));
-                       start += PAGE_SIZE;
-               }
+       if (!retval) {
+               /* keep track of amount of locked VM */
+               pages = (end - start) >> PAGE_SHIFT;
+               if (!(newflags & VM_LOCKED))
+                       pages = -pages;
+               vma->vm_mm->locked_vm += pages;
+
+               if (newflags & VM_LOCKED)
+                       while (start < end) {
+                               char c = get_user((char *) start);
+                               __asm__ __volatile__("": :"r" (c));
+                               start += PAGE_SIZE;
+                       }
        }
        return retval;
 }
index bf02b5dc23f2534e25ff14b06f768ade68cb610f..af307e5ba3a9929cf63bfa1c5c2647708d4ab8a1 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -208,18 +208,13 @@ unsigned long get_unmapped_area(unsigned long addr, unsigned long len)
                addr = TASK_SIZE / 3;
        addr = PAGE_ALIGN(addr);
 
-       for (vmm = current->mm->mmap; ; vmm = vmm->vm_next) {
+       for (vmm = find_vma(current, addr); ; vmm = vmm->vm_next) {
+               /* At this point:  (!vmm || addr < vmm->vm_end). */
                if (TASK_SIZE - len < addr)
                        return 0;
-               if (!vmm)
+               if (!vmm || addr + len <= vmm->vm_start)
                        return addr;
-               if (addr > vmm->vm_end)
-                       continue;
-               if (addr + len > vmm->vm_start) {
-                       addr = vmm->vm_end;
-                       continue;
-               }
-               return addr;
+               addr = vmm->vm_end;
        }
 }
 
index 8d2d792315aa9ac1bb51278b9a2e4e8c385eee65..9727e83b60d4f10c51f79287f15011de0adcf7ea 100644 (file)
@@ -109,7 +109,7 @@ void rw_swap_page(int rw, unsigned long entry, char * buf, int wait)
                }
                ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf);
        } else
-               printk("re_swap_page: no swap file or device\n");
+               printk("rw_swap_page: no swap file or device\n");
        if (offset && !clear_bit(offset,p->swap_lockmap))
                printk("rw_swap_page: lock already cleared\n");
        wake_up(&lock_queue);
index 37aab0bf42c8d65e0e6b1be3629982c556b90eb0..e17476e00dc3383b158976fe3d57c8afe32bb499 100644 (file)
@@ -346,7 +346,7 @@ int try_to_free_page(int priority, unsigned long limit, int wait)
                        if (swap_out(i, limit, wait))
                                return 1;
                        state = 0;
-               } while(i--);
+               } while (i--);
        }
        return 0;
 }
index defc9b3774df9b77dd7993cc0d34fda5bb34685f..186530b345074203c4cb2a92a36eef7bc0832840 100644 (file)
@@ -216,7 +216,7 @@ struct device *dev_get(const char *name)
 
 extern __inline__ void dev_load(const char *name)
 {
-        char *sptr;
+        const char *sptr;
  
         if(!dev_get(name)) {
 #ifdef CONFIG_NET_ALIAS
@@ -1317,6 +1317,7 @@ int dev_ioctl(unsigned int cmd, void *arg)
  *
  */
 extern int lance_init(void);
+extern int ni65_init(void);
 extern int pi_init(void);
 extern int dec21040_init(void);
 
@@ -1338,6 +1339,9 @@ int net_dev_init(void)
 #if defined(CONFIG_LANCE)
        lance_init();
 #endif
+#if defined(CONFIG_NI65)
+        ni65_init();
+#endif
 #if defined(CONFIG_PI)
        pi_init();
 #endif 
index 0cf40fea4e7550ac5e62e2a6f622a77c578a8e1b..56df49c390c032af1eb01fcc68b650cf3530d851 100644 (file)
@@ -520,8 +520,6 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigne
                        if (sk->wmem_alloc + size >= sk->sndbuf) 
 #endif
                        {
-                               if (sk->wmem_alloc < 0)
-                                 printk("sock.c: Look where I am %ld<%ld\n", tmp, sk->wmem_alloc);
                                sk->socket->flags &= ~SO_NOSPACE;
                                interruptible_sleep_on(sk->sleep);
                                if (current->signal & ~current->blocked) 
index 6b3503ed3872f7cdf39a03a9d7d36defb4532e69..e3cd375713c1b18edd0736e1635b6b105234975b 100644 (file)
@@ -1162,7 +1162,7 @@ static int inet_shutdown(struct socket *sock, int how)
                return(-EINVAL);
        if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED)
                sock->state = SS_CONNECTED;
-       if (!tcp_connected(sk->state)) 
+       if (!sk || !tcp_connected(sk->state)) 
                return(-ENOTCONN);
        sk->shutdown |= how;
        if (sk->prot->shutdown)
index 97d1725d0edce4f2815a4d4d51d08871a4277c01..e2e7ff61fd93cc0204b8bdc5d7b8e5025fa7a874 100644 (file)
@@ -51,6 +51,7 @@
  *                                     of host down events.
  *             Alan Cox        :       Missing unlock in device events.
  *             Eckes           :       ARP ioctl control errors.
+ *             Alexey Kuznetsov:       Arp free fix.
  */
 
 /* RFC1122 Status:
@@ -331,6 +332,7 @@ static void arp_free_entry(struct arp_table *entry)
        {
                next = hh->hh_next;
                hh->hh_arp = NULL;
+               hh->hh_uptodate = 0;
                if (!--hh->hh_refcnt)
                        kfree_s(hh, sizeof(struct(struct hh_cache)));
        }
index 44c8390fddf4e0b42d36c01adbef8db1af90e8e6..82491b9454de58efe7196410f5d57641b1acfbb9 100644 (file)
@@ -17,6 +17,7 @@
  *             Mike Shaver     :       RFC1122 checks.
  *             Alan Cox        :       Multicast ping reply as self.
  *             Alan Cox        :       Fix atomicity lockup in ip_build_xmit call
+ *             Alan Cox        :       Added 216,128 byte paths to the MTU code.
  *
  *
  *
@@ -356,8 +357,8 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, struct devi
                        case ICMP_HOST_UNREACH:
                                break;
                        case ICMP_PROT_UNREACH:
-                               printk(KERN_INFO "ICMP: %s:%d: protocol unreachable.\n",
-                                       in_ntoa(iph->daddr), ntohs(iph->protocol));
+/*                             printk(KERN_INFO "ICMP: %s:%d: protocol unreachable.\n",
+                                       in_ntoa(iph->daddr), ntohs(iph->protocol));*/
                                break;
                        case ICMP_PORT_UNREACH:
                                break;
@@ -396,7 +397,18 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, struct devi
                                                new_mtu = 576;
                                        else if (old_mtu > 296)
                                                new_mtu = 296;
+                                       /*
+                                        *      These two are not from the RFC but
+                                        *      are needed for AMPRnet AX.25 paths.
+                                        */
+                                       else if (old_mtu > 216)
+                                               old_mtu = 216;
+                                       else if (old_mtu > 128)
+                                               old_mtu = 128;
                                        else
+                                       /*
+                                        *      Despair..
+                                        */
                                                new_mtu = 68;
                                }
                                /*
index 0eef7c07956c150b4864f8fd19c6880506166760..3bca9ccde18af7dd387fa65cee903e8c1bfc0dd4 100644 (file)
@@ -240,10 +240,10 @@ static void packet_close(struct sock *sk, unsigned long timeout)
 }
 
 /*
- *     Attach a packer hook to a device.
+ *     Attach a packet hook to a device.
  */
 
-int packet_attach(struct sock *sk)
+int packet_attach(struct sock *sk, struct device *dev)
 {
        struct packet_type *p = (struct packet_type *) kmalloc(sizeof(*p), GFP_KERNEL);
        if (p == NULL) 
@@ -252,7 +252,7 @@ int packet_attach(struct sock *sk)
        p->func = packet_rcv;
        p->type = sk->num;
        p->data = (void *)sk;
-       p->dev = NULL;
+       p->dev = dev;
        dev_add_pack(p);
    
        /*
@@ -260,6 +260,7 @@ int packet_attach(struct sock *sk)
         */
    
        sk->protinfo.af_packet.prot_hook = p;
+       sk->protinfo.af_packet.bound_dev = dev;
        return 0;       
 }
  
@@ -269,7 +270,8 @@ int packet_attach(struct sock *sk)
 
 static int packet_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
-       char dev[15];
+       char name[15];
+       struct device *dev;
        
        /*
         *      Check legality
@@ -277,8 +279,8 @@ static int packet_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
         
        if(addr_len!=sizeof(struct sockaddr))
                return -EINVAL;
-       strncpy(dev,uaddr->sa_data,14);
-       dev[14]=0;
+       strncpy(name,uaddr->sa_data,14);
+       name[14]=0;
        
        /*
         *      Lock the device chain while we sanity check
@@ -286,12 +288,14 @@ static int packet_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
         */
         
        dev_lock_list();
-       if((sk->protinfo.af_packet.bound_dev=dev_get(dev))==NULL)
+       dev=dev_get(name);
+       if(dev==NULL)
        {
                dev_unlock_list();
                return -ENODEV;
        }
-       if(!(sk->protinfo.af_packet.bound_dev->flags&IFF_UP))
+       
+       if(!(dev->flags&IFF_UP))
        {
                dev_unlock_list();
                return -ENETDOWN;
@@ -301,20 +305,28 @@ static int packet_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
         *      Perform the request.
         */
         
-       memcpy(sk->protinfo.af_packet.device_name,dev,15);
+       memcpy(sk->protinfo.af_packet.device_name,name,15);
+       
+       /*
+        *      Rewrite an existing hook if present.
+        */
+        
        if(sk->protinfo.af_packet.prot_hook)
+       {
                dev_remove_pack(sk->protinfo.af_packet.prot_hook);
+               sk->protinfo.af_packet.prot_hook->dev=dev;
+               sk->protinfo.af_packet.bound_dev=dev;
+               dev_add_pack(sk->protinfo.af_packet.prot_hook);
+       }
        else
        {
-               int err=packet_attach(sk);
+               int err=packet_attach(sk, dev);
                if(err)
                {
                        dev_unlock_list();
                        return err;
                }
        }
-       sk->protinfo.af_packet.prot_hook->dev=sk->protinfo.af_packet.bound_dev;
-       dev_add_pack(sk->protinfo.af_packet.prot_hook);
        /*
         *      Now the notifier is set up right this lot is safe.
         */
@@ -354,7 +366,7 @@ static int packet_init(struct sock *sk)
         *      Attach a protocol block
         */
         
-       int err=packet_attach(sk);
+       int err=packet_attach(sk, NULL);
        if(err)
                return err;
                
index 0a2d10eabd6dd4b94a341b4c599149ae36b145d8..d77b280cf8208698bce9c1465547bc1493d5621f 100644 (file)
@@ -18,6 +18,9 @@
  *             Matthew Dillon, <dillon@apollo.west.oic.com>
  *             Arnt Gulbrandsen, <agulbra@nvg.unit.no>
  *             Jorge Cwik, <jorge@laser.satlink.net>
+ *
+ * FIXES
+ *             Pedro Roque     :       Double ACK bug
  */
 
 #include <linux/config.h>
@@ -1420,19 +1423,21 @@ static int tcp_data(struct sk_buff *skb, struct sock *sk,
                         *      - must send at least every 2 full sized packets
                         */
                        if (!sk->delay_acks ||
-                           sk->ack_backlog >= sk->max_ack_backlog || 
+                           /* sk->ack_backlog >= sk->max_ack_backlog || */
                            sk->bytes_rcv > sk->max_unacked || th->fin ||
                            sk->ato > HZ/2 ||
                            tcp_raise_window(sk)) {
-       /*                      tcp_send_ack(sk->sent_seq, sk->acked_seq,sk,th, saddr); */
+                               tcp_send_ack(sk->sent_seq, sk->acked_seq,sk,th, saddr);
                        }
                        else 
-                       {
+                       {       
                                sk->ack_backlog++;
-                               
+                       
                                if(sk->debug)                           
                                        printk("Ack queued.\n");
+                               
                                tcp_reset_xmit_timer(sk, TIME_WRITE, sk->ato);
+                               
                        }
                }
        }
@@ -1475,11 +1480,7 @@ static int tcp_data(struct sk_buff *skb, struct sock *sk,
                }
                tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
                sk->ack_backlog++;
-               tcp_reset_xmit_timer(sk, TIME_WRITE, min(sk->ato, 0.5 * HZ));
-       }
-       else
-       {
-               tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
+               tcp_reset_xmit_timer(sk, TIME_WRITE, min(sk->ato, HZ/2));
        }
 
        /*
index bbe3ba916b47e559cb1bc696c3adda51ef232337..6c833750e8ab342a01e4c6ed26b18e9bb5b3c3bf 100644 (file)
@@ -39,6 +39,8 @@
  *     Revision 0.31:  New sk_buffs. This still needs a lot of testing. <Alan Cox>
  *     Revision 0.32:  Using sock_alloc_send_skb, firewall hooks. <Alan Cox>
  *                     Supports sendmsg/recvmsg
+ *     Revision 0.33:  Internal network support, routing changes, uses a
+ *                     protocol private area for ipx data.
  *
  *     Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com>
  *     Neither Greg Page nor Caldera, Inc. admit liability nor provide 
@@ -143,7 +145,7 @@ ipx_remove_socket(ipx_socket *sk)
        cli();
        
        /* Determine interface with which socket is associated */
-       intrfc = sk->ipx_intrfc;
+       intrfc = sk->protinfo.af_ipx.intrfc;
        if (intrfc == NULL) {
                restore_flags(flags);
                return;
@@ -234,7 +236,7 @@ ipxitf_insert_socket(ipx_interface *intrfc, ipx_socket *sk)
 {
        ipx_socket      *s;
 
-       sk->ipx_intrfc = intrfc;
+       sk->protinfo.af_ipx.intrfc = intrfc;
        sk->next = NULL;
        if (intrfc->if_sklist == NULL) {
                intrfc->if_sklist = sk;
@@ -251,7 +253,7 @@ ipxitf_find_socket(ipx_interface *intrfc, unsigned short port)
        ipx_socket      *s;
 
        for (s=intrfc->if_sklist; 
-               (s != NULL) && (s->ipx_port != port); 
+               (s != NULL) && (s->protinfo.af_ipx.port != port); 
                s=s->next)
                ;
 
@@ -268,8 +270,8 @@ ipxitf_find_internal_socket(ipx_interface *intrfc,
 
        while (s != NULL)
        {
-               if (   (s->ipx_port == port)
-                   && (memcmp(node, s->ipx_node, IPX_NODE_LEN) == 0))
+               if (   (s->protinfo.af_ipx.port == port)
+                   && (memcmp(node, s->protinfo.af_ipx.node, IPX_NODE_LEN) == 0))
                {
                        break;
                }
@@ -294,8 +296,8 @@ ipxitf_down(ipx_interface *intrfc)
        for (s = intrfc->if_sklist; s != NULL; ) {
                s->err = ENOLINK;
                s->error_report(s);
-               s->ipx_intrfc = NULL;
-               s->ipx_port = 0;
+               s->protinfo.af_ipx.intrfc = NULL;
+               s->protinfo.af_ipx.port = 0;
                s->zapped=1;    /* Indicates it is no longer bound */
                t = s;
                s = s->next;
@@ -378,9 +380,9 @@ ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
 
        while (s != NULL)
        {
-               if (   (s->ipx_port == ipx->ipx_dest.sock)
+               if (   (s->protinfo.af_ipx.port == ipx->ipx_dest.sock)
                    && (   is_broadcast
-                       || (memcmp(ipx->ipx_dest.node, s->ipx_node,
+                       || (memcmp(ipx->ipx_dest.node, s->protinfo.af_ipx.node,
                                   IPX_NODE_LEN) == 0)))
                {
                        /* We found a socket to which to send */
@@ -1200,11 +1202,11 @@ static int ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, struc
        ipx->ipx_type=usipx->sipx_type;
        skb->h.raw = (unsigned char *)ipx;
 
-       ipx->ipx_source.net = sk->ipx_intrfc->if_netnum;
+       ipx->ipx_source.net = sk->protinfo.af_ipx.intrfc->if_netnum;
 #ifdef CONFIG_IPX_INTERN
-       memcpy(ipx->ipx_source.node, sk->ipx_node, IPX_NODE_LEN);
+       memcpy(ipx->ipx_source.node, sk->protinfo.af_ipx.node, IPX_NODE_LEN);
 #else
-       if ((err = ntohs(sk->ipx_port)) == 0x453 || err == 0x452)  
+       if ((err = ntohs(sk->protinfo.af_ipx.port)) == 0x453 || err == 0x452)  
        {
                /* RIP/SAP special handling for mars_nwe */
                ipx->ipx_source.net = intrfc->if_netnum;
@@ -1212,11 +1214,11 @@ static int ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, struc
        }
        else
        {
-               ipx->ipx_source.net = sk->ipx_intrfc->if_netnum;
-               memcpy(ipx->ipx_source.node, sk->ipx_intrfc->if_node, IPX_NODE_LEN);
+               ipx->ipx_source.net = sk->protinfo.af_ipx.intrfc->if_netnum;
+               memcpy(ipx->ipx_source.node, sk->protinfo.af_ipx.intrfc->if_node, IPX_NODE_LEN);
        }
 #endif
-       ipx->ipx_source.sock = sk->ipx_port;
+       ipx->ipx_source.sock = sk->protinfo.af_ipx.port;
        ipx->ipx_dest.net=usipx->sipx_network;
        memcpy(ipx->ipx_dest.node,usipx->sipx_node,IPX_NODE_LEN);
        ipx->ipx_dest.sock=usipx->sipx_port;
@@ -1384,25 +1386,31 @@ static int ipx_get_info(char *buffer, char **start, off_t offset,
                        len += sprintf(buffer+len,
                                       "%08lX:%02X%02X%02X%02X%02X%02X:%04X  ", 
                                       htonl(s->ipx_intrfc->if_netnum),
-                                      s->ipx_node[0], s->ipx_node[1], 
-                                      s->ipx_node[2], s->ipx_node[3], 
-                                      s->ipx_node[4], s->ipx_node[5],
-                                      htons(s->ipx_port));
+                                      s->protinfo.af_ipx.node[0],
+                                      s->protinfo.af_ipx.node[1], 
+                                      s->protinfo.af_ipx.node[2], 
+                                      s->protinfo.af_ipx.node[3], 
+                                      s->protinfo.af_ipx.node[4], 
+                                      s->protinfo.af_ipx.node[5],
+                                      htons(s->protinfo.af_ipx.port));
 #else
                        len += sprintf(buffer+len,"%08lX:%04X  ", 
                                       htonl(i->if_netnum),
-                                      htons(s->ipx_port));
+                                      htons(s->protinfo.af_ipx.port));
 #endif
                        if (s->state!=TCP_ESTABLISHED) {
                                len += sprintf(buffer+len, "%-28s", "Not_Connected");
                        } else {
                                len += sprintf (buffer+len,
                                        "%08lX:%02X%02X%02X%02X%02X%02X:%04X  ", 
-                                       htonl(s->ipx_dest_addr.net),
-                                       s->ipx_dest_addr.node[0], s->ipx_dest_addr.node[1], 
-                                       s->ipx_dest_addr.node[2], s->ipx_dest_addr.node[3], 
-                                       s->ipx_dest_addr.node[4], s->ipx_dest_addr.node[5],
-                                       htons(s->ipx_dest_addr.sock));
+                                       htonl(s->protinfo.af_ipx.dest_addr.net),
+                                       s->protinfo.af_ipx.dest_addr.node[0],
+                                       s->protinfo.af_ipx.dest_addr.node[1], 
+                                       s->protinfo.af_ipx.dest_addr.node[2],
+                                       s->protinfo.af_ipx.dest_addr.node[3], 
+                                       s->protinfo.af_ipx.dest_addr.node[4],
+                                       s->protinfo.af_ipx.dest_addr.node[5],
+                                       htons(s->protinfo.af_ipx.dest_addr.sock));
                        }
                        len += sprintf (buffer+len,"%08lX  %08lX  ", 
                                s->wmem_alloc, s->rmem_alloc);
@@ -1506,7 +1514,7 @@ static int ipx_setsockopt(struct socket *sock, int level, int optname, char *opt
                        switch(optname)
                        {
                                case IPX_TYPE:
-                                       sk->ipx_type=opt;
+                                       sk->protinfo.af_ipx.type=opt;
                                        return 0;
                                default:
                                        return -EOPNOTSUPP;
@@ -1537,7 +1545,7 @@ static int ipx_getsockopt(struct socket *sock, int level, int optname,
                        switch(optname)
                        {
                                case IPX_TYPE:
-                                       val=sk->ipx_type;
+                                       val=sk->protinfo.af_ipx.type;
                                        break;
                                default:
                                        return -ENOPROTOOPT;
@@ -1613,12 +1621,13 @@ ipx_create(struct socket *sock, int protocol)
        sk->state=TCP_CLOSE;
        sk->socket=sock;
        sk->type=sock->type;
-       sk->ipx_type=0;         /* General user level IPX */
-       sk->ipx_ncp_server = NULL;
+       sk->protinfo.af_ipx.type=0;             /* General user level IPX */
        sk->debug=0;
-       sk->ipx_intrfc = NULL;
-       memset(&sk->ipx_dest_addr,'\0',sizeof(sk->ipx_dest_addr));
-       sk->ipx_port = 0;
+       sk->protinfo.af_ipx.intrfc = NULL;
+       memset(&sk->protinfo.af_ipx.dest_addr,'\0',
+               sizeof(sk->protinfo.af_ipx.dest_addr));
+       sk->protinfo.af_ipx.port = 0;
+       sk->protinfo.af_ipx.ncp_server = 0;
        sk->mtu=IPX_MTU;
        
        if(sock!=NULL)
@@ -1699,7 +1708,7 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
        if(ntohs(addr->sipx_port)<IPX_MIN_EPHEMERAL_SOCKET && !suser())
                return -EPERM;  /* protect IPX system stuff like routing/sap */
 
-       sk->ipx_port=addr->sipx_port;
+       sk->protinfo.af_ipx.port=addr->sipx_port;
 
 #ifdef CONFIG_IPX_INTERN
        if (intrfc == ipx_internal_net)
@@ -1716,15 +1725,16 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
                }
                if (memcmp(addr->sipx_node, ipx_this_node, IPX_NODE_LEN) == 0)
                {
-                       memcpy(sk->ipx_node, intrfc->if_node,
+                       memcpy(sk->protinfo.af_ipx.node, intrfc->if_node,
                               IPX_NODE_LEN);
                }
                else
                {
-                       memcpy(sk->ipx_node, addr->sipx_node, IPX_NODE_LEN);
+                       memcpy(sk->protinfo.af_ipx.node, addr->sipx_node, IPX_NODE_LEN);
                }
-               if (ipxitf_find_internal_socket(intrfc, sk->ipx_node,
-                                               sk->ipx_port) != NULL)
+               if (ipxitf_find_internal_socket(intrfc, 
+                       sk->protinfo.af_ipx.node, 
+                       sk->protinfo.af_ipx.port) != NULL)
                {
                        if(sk->debug)
                                printk("IPX: bind failed because port %X in"
@@ -1739,7 +1749,8 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
                 * with the ipx routing ioctl()
                 */
 
-               memcpy(sk->ipx_node, intrfc->if_node, IPX_NODE_LEN);
+               memcpy(sk->protinfo.af_ipx.node, intrfc->if_node, 
+                       IPX_NODE_LEN);
                
                if(ipxitf_find_socket(intrfc, addr->sipx_port)!=NULL) {
                        if(sk->debug)
@@ -1783,7 +1794,7 @@ static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
                return(-EINVAL);
        addr=(struct sockaddr_ipx *)uaddr;
        
-       if(sk->ipx_port==0)
+       if(sk->protinfo.af_ipx.port==0)
        /* put the autobinding in */
        {
                struct sockaddr_ipx uaddr;
@@ -1792,7 +1803,7 @@ static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
                uaddr.sipx_port = 0;
                uaddr.sipx_network = 0L;
 #ifdef CONFIG_IPX_INTERN
-               memcpy(uaddr.sipx_node, sk->ipx_intrfc->if_node,
+               memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc->if_node,
                       IPX_NODE_LEN);
 #endif
                ret = ipx_bind (sock, (struct sockaddr *)&uaddr,
@@ -1802,10 +1813,11 @@ static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
        
        if(ipxrtr_lookup(addr->sipx_network)==NULL)
                return -ENETUNREACH;
-       sk->ipx_dest_addr.net=addr->sipx_network;
-       sk->ipx_dest_addr.sock=addr->sipx_port;
-       memcpy(sk->ipx_dest_addr.node,addr->sipx_node,IPX_NODE_LEN);
-       sk->ipx_type=addr->sipx_type;
+       sk->protinfo.af_ipx.dest_addr.net=addr->sipx_network;
+       sk->protinfo.af_ipx.dest_addr.sock=addr->sipx_port;
+       memcpy(sk->protinfo.af_ipx.dest_addr.node,
+               addr->sipx_node,IPX_NODE_LEN);
+       sk->protinfo.af_ipx.type=addr->sipx_type;
        sock->state = SS_CONNECTED;
        sk->state=TCP_ESTABLISHED;
        return 0;
@@ -1837,29 +1849,29 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
        if(peer) {
                if(sk->state!=TCP_ESTABLISHED)
                        return -ENOTCONN;
-               addr=&sk->ipx_dest_addr;
+               addr=&sk->protinfo.af_ipx.dest_addr;
                sipx.sipx_network = addr->net;
                memcpy(sipx.sipx_node,addr->node,IPX_NODE_LEN);
                sipx.sipx_port = addr->sock;
        } else {
-               if (sk->ipx_intrfc != NULL) {
-                       sipx.sipx_network = sk->ipx_intrfc->if_netnum;
+               if (sk->protinfo.af_ipx.intrfc != NULL) {
+                       sipx.sipx_network = sk->protinfo.af_ipx.intrfc->if_netnum;
 #ifdef CONFIG_IPX_INTERN
-                       memcpy(sipx.sipx_node, sk->ipx_node, IPX_NODE_LEN);
+                       memcpy(sipx.sipx_node, sk->protinfo.af_ipx.node, IPX_NODE_LEN);
 #else
-                       memcpy(sipx.sipx_node, sk->ipx_intrfc->if_node,
-                              IPX_NODE_LEN);
+                       memcpy(sipx.sipx_node, 
+                               sk->protinfo.af_ipx.intrfc->if_node, IPX_NODE_LEN);
 #endif
 
                } else {
                        sipx.sipx_network = 0L;
                        memset(sipx.sipx_node, '\0', IPX_NODE_LEN);
                }
-               sipx.sipx_port = sk->ipx_port;
+               sipx.sipx_port = sk->protinfo.af_ipx.port;
        }
                
        sipx.sipx_family = AF_IPX;
-       sipx.sipx_type = sk->ipx_type;
+       sipx.sipx_type = sk->protinfo.af_ipx.type;
        memcpy(uaddr,&sipx,sizeof(sipx));
        return 0;
 }
@@ -1974,7 +1986,7 @@ static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nob
                
        if(usipx) 
        {
-               if(sk->ipx_port == 0) 
+               if(sk->protinfo.af_ipx.port == 0) 
                {
                        struct sockaddr_ipx uaddr;
                        int ret;
@@ -1982,8 +1994,8 @@ static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nob
                        uaddr.sipx_port = 0;
                        uaddr.sipx_network = 0L; 
 #ifdef CONFIG_IPX_INTERN
-                       memcpy(uaddr.sipx_node, sk->ipx_intrfc->if_node,
-                              IPX_NODE_LEN);
+                       memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc
+                               ->if_node, IPX_NODE_LEN);
 #endif
                        ret = ipx_bind (sock, (struct sockaddr *)&uaddr,
                                        sizeof(struct sockaddr_ipx));
@@ -2001,10 +2013,10 @@ static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nob
                        return -ENOTCONN;
                usipx=&local_sipx;
                usipx->sipx_family=AF_IPX;
-               usipx->sipx_type=sk->ipx_type;
-               usipx->sipx_port=sk->ipx_dest_addr.sock;
-               usipx->sipx_network=sk->ipx_dest_addr.net;
-               memcpy(usipx->sipx_node,sk->ipx_dest_addr.node,IPX_NODE_LEN);
+               usipx->sipx_type=sk->protinfo.af_ipx.type;
+               usipx->sipx_port=sk->protinfo.af_ipx.dest_addr.sock;
+               usipx->sipx_network=sk->protinfo.af_ipx.dest_addr.net;
+               memcpy(usipx->sipx_node,sk->protinfo.af_ipx.dest_addr.node,IPX_NODE_LEN);
        }
        
        retval = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len);
index f83d5b4ba0c4c70e3925ec7a3bca70c218aa346f..72755eeaf8e3eefdeb9fc86fd18af4e46f72afb0 100644 (file)
@@ -56,8 +56,8 @@ function extract_help () {
      var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
      #now pick out the right help text:
      text=$(sed -n "/^$var[    ]*\$/,\${
-                        /^$var[        ]*\$/b
-                        /^#.*/b;/^[    ]*\$/q
+                        /^$var[        ]*\$/d
+                        /^#.*/d;/^[    ]*\$/q
                         p
                     }" Documentation/Configure.help)
 
index 3a7ce98dd09652e98db5b4af87b468cdb93708de..9ae9271de7cfdbc41c6f2c23fd41948c3206d4a2 100644 (file)
@@ -87,8 +87,7 @@ proc maybe_exit { w } {
        label $w.bm -bitmap questhead
        pack  $w.bm -pady 10 -side top -padx 10
        message $w.m -width 400 -aspect 300 \
-               -text "Changes will be lost.  Are you sure?"  -relief raised \
-               -fg black
+               -text "Changes will be lost.  Are you sure?"  -relief raised
        pack  $w.m -pady 10 -side top -padx 10
        wm title $w "Are you sure?" 
 
@@ -115,7 +114,7 @@ proc read_config_file { w } {
                toplevel $w -class Dialog
                message $w.m -width 400 -aspect 300 -text \
                        "Unable to read file $loadfile" \
-                        -relief raised -fg black
+                        -relief raised 
                label $w.bm -bitmap error
                pack $w.bm $w.m -pady 10 -side top -padx 10
                wm title $w "Oops" 
@@ -143,7 +142,7 @@ proc write_config_file  { w } {
                toplevel $w -class Dialog
                message $w.m -width 400 -aspect 300 -text \
                        "Unable to write file $loadfile" \
-                        -relief raised -fg black
+                        -relief raised 
                label $w.bm -bitmap error
                pack $w.bm $w.m -pady 10 -side top -padx 10
                wm title $w "Oops" 
@@ -303,17 +302,17 @@ proc dohelp {w varname }  {
        if { $found == 0 } then {
                if { $filefound == 0 } then {
                message $w.m -width 750 -aspect 300 -text \
-                       "No help available - unable to open file Documentation/Configure.help.  This file is available from http://math-www.uni-paderborn.de/~axel/config_help.html or ftp://sunsite.unc.edu/pub/Linux/kernel/config/krnl_cnfg_hlp_1.X.XX.tgz"  -relief raised -fg black
+                       "No help available - unable to open file Documentation/Configure.help.  This file is available from http://math-www.uni-paderborn.de/~axel/config_help.html or ftp://sunsite.unc.edu/pub/Linux/kernel/config/krnl_cnfg_hlp_1.X.XX.tgz"  -relief raised 
                } else {
                message $w.m -width 400 -aspect 300 -text \
-                       "No help available for $varname"  -relief raised -fg black
+                       "No help available for $varname"  -relief raised 
                }
                label $w.bm -bitmap error
                pack $w.bm $w.m -pady 10 -side top -padx 10
                wm title $w "RTFM" 
        } else {
                message $w.m -width 400 -aspect 300 -text $message \
-                        -relief raised -fg black
+                        -relief raised 
                label $w.bm -bitmap info
                pack $w.bm $w.m -pady 10 -side top -padx 10
                wm title $w "Configuration help" 
@@ -334,7 +333,7 @@ proc wrapup {w }  {
        catch {destroy $w}
        toplevel $w -class Dialog
        message $w.m -width 400 -aspect 300 -text \
-               "The linux kernel is now hopefully configured for your setup. Check the top-level Makefile for additional configuration, and do a 'make dep ; make clean' if you want to be sure all the files are correctly re-made."  -relief raised -fg black
+               "The linux kernel is now hopefully configured for your setup. Check the top-level Makefile for additional configuration, and do a 'make dep ; make clean' if you want to be sure all the files are correctly re-made."  -relief raised 
        label $w.bm -bitmap info
        pack $w.bm $w.m -pady 10 -side top -padx 10
        wm title $w "Kernel build instructions" 
index 216510442eec50dc5a2c06901a0c26b42cb558fe..8dea2bd8131e03e22d5e81f224a4c087381bb27d 100644 (file)
@@ -332,11 +332,7 @@ dialog_checklist (const char *title, const char *prompt, int height, int width,
                for (i = 0; i < item_no; i++) {
                    if (status[i]) {
                        if (flag == FLAG_CHECK) {
-                           if (separate_output) {
-                               fprintf (stderr, "%s\n", items[i * 3]);
-                           } else {
-                               fprintf (stderr, "\"%s\" ", items[i * 3]);
-                           }
+                           fprintf (stderr, "\"%s\" ", items[i * 3]);
                        } else {
                            fprintf (stderr, "%s", items[i * 3]);
                        }
index 85f5b5115f09b63360776ca7c71eb64e8d02fc98..63598e8b75556b2f859933db41f249665a14663f 100644 (file)
@@ -141,7 +141,7 @@ int dialog_menu (const char *title, const char *prompt, int height, int width,
                const char * const * items);
 int dialog_checklist (const char *title, const char *prompt, int height,
                int width, int list_height, int item_no,
-               const char * const * items, int flag, int separate_output);
+               const char * const * items, int flag);
 extern unsigned char dialog_input_result[];
 int dialog_inputbox (const char *title, const char *prompt, int height,
                int width, const char *init);
index b0ae3a31de0b62fa0f2a3d7ee8e6ec342a81beb2..94d75ded617d0cb94fa3dabb28d4273d79b9f445 100644 (file)
@@ -23,8 +23,6 @@
 
 static void Usage (const char *name);
 
-static int separate_output = 0;
-
 typedef int (jumperFn) (const char *title, int argc, const char * const * argv);
 
 struct Mode {
@@ -87,9 +85,6 @@ main (int argc, const char * const * argv)
                 backtitle = argv[offset + 2];
                 offset += 2;
             }
-       } else if (!strcmp (argv[offset + 1], "--separate-output")) {
-           separate_output = 1;
-           offset++;
        } else if (!strcmp (argv[offset + 1], "--clear")) {
            if (clear_screen) { /* Hey, "--clear" can't appear twice! */
                Usage (argv[0]);
@@ -153,7 +148,7 @@ Usage (const char *name)
 \n* Display dialog boxes from shell scripts *\
 \n\
 \nUsage: %s --clear\
-\n       %s [--title <title>] [--separate-output] [--backtitle <backtitle>] --clear <Box options>\
+\n       %s [--title <title>] [--backtitle <backtitle>] --clear <Box options>\
 \n\
 \nBox options:\
 \n\
@@ -182,14 +177,14 @@ int
 j_checklist (const char *t, int ac, const char * const * av)
 {
     return dialog_checklist (t, av[2], atoi (av[3]), atoi (av[4]),
-       atoi (av[5]), (ac - 6) / 3, av + 6, FLAG_CHECK, separate_output);
+       atoi (av[5]), (ac - 6) / 3, av + 6, FLAG_CHECK);
 }
 
 int
 j_radiolist (const char *t, int ac, const char * const * av)
 {
     return dialog_checklist (t, av[2], atoi (av[3]), atoi (av[4]),
-       atoi (av[5]), (ac - 6) / 3, av + 6, FLAG_RADIO, separate_output);
+       atoi (av[5]), (ac - 6) / 3, av + 6, FLAG_RADIO);
 }
 
 int
index 3e7d27c80f67d4788c810c2b77aedd4ef01c9eb1..317ba92eeeced1b010d0b4e2b82d691c7a89145c 100644 (file)
@@ -36,13 +36,16 @@ do
     fi
 
     echo "Applying $patch..."
-    gunzip -dc $patchdir/$patch | patch -p1 -s -E -d $sourcedir
-# used to be patch -p1 -s -E -f -d $sourcedir
-    if [ "`find $sourcedir -name \*.rej -print`" ]
+    if (gunzip -dc $patchdir/$patch | grep -v '^\\' | patch -p1 -s -E -d $sourcedir)
+    then
+        echo "Patch failed.  Clean up yourself."
+        break
+    fi
+    if [ "`find $sourcedir '(' -name '*.rej' -o -name '.*.rej' ')' -print`" ]
     then
         echo "Aborting.  Reject files found."
         break
     fi
     # Remove backup files
-    find $sourcedir -name \*.orig -print | xargs rm -f
+    find $sourcedir '(' -name '*.orig' -o -name '.*.orig' ')' -print | xargs rm -f
 done
index 316d0b859f56d8279f4c5c788527c8f56b49010c..4da2e2178714e2f6260ccba8da5893e522f2420d 100644 (file)
@@ -196,7 +196,7 @@ generate_if(struct kconfig * item,
       break;
     case tok_int:
       printf("} then { ");
-      printf(".menu%d.config.f.x%d.x configure -state normal -fore black; ", menu_num, line_num);
+      printf(".menu%d.config.f.x%d.x configure -state normal; ", menu_num, line_num);
       printf(".menu%d.config.f.x%d.l configure -state normal; ", menu_num, line_num);
       printf("} else { ");
       printf(".menu%d.config.f.x%d.x configure -state disabled -fore gray60;", menu_num, line_num );