diff -u --recursive --new-file v2.1.29/linux/CREDITS linux/CREDITS
--- v2.1.29/linux/CREDITS	Mon Mar 17 14:54:19 1997
+++ linux/CREDITS	Wed Mar 12 15:10:13 1997
@@ -142,8 +142,8 @@
 S: Cambridge, UK. CB2 1TQ
 
 N: Thomas Bogendoerfer
-E: tsbogend@bigbug.franken.de
-D: Lance32 driver
+E: tsbogend@alpha.franken.de
+D: PCnet32 driver
 D: strace for Linux/Alpha
 S: Baumgartenweg 5
 S: 91452 Wilhermsdorf
diff -u --recursive --new-file v2.1.29/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.29/linux/Documentation/Configure.help	Mon Mar 17 14:54:19 1997
+++ linux/Documentation/Configure.help	Thu Mar 20 17:11:51 1997
@@ -20,14 +20,18 @@
 # via ftp (user: anonymous) from sunsite.unc.edu in the directory
 # /pub/Linux/docs/HOWTO. 
 #
-# Format of this file: description<nl>variable<nl>helptext<nl><nl>. 
-# If the question being documented is of type "choice", we list
-# only the first occurring config variable. The help texts
-# must not contain empty lines. No variable should occur twice; if it
-# does, only the first occurrence will be used by Configure. The lines
-# in a help text should be indented two positions. Lines starting with
-# `#' are ignored. To be nice to menuconfig, limit your lines to 70
-# characters. Use emacs' kfill.el to edit this file or you lose.
+# Format of this file: description<nl>variable<nl>helptext<nl><nl>.
+# If the question being documented is of type "choice", we list only
+# the first occurring config variable. The help texts must not contain
+# empty lines. Order of the help texts does not matter, however, no
+# variable should be documented twice: if it is, only the first
+# occurrence will be used by Configure. It is not absolutely necessary
+# that the one-line descriptions of the variables used here are
+# exactly the same as the ones in the corresponding Config.in
+# scripts. The lines in a help text should be indented two
+# positions. Lines starting with `#' are ignored. To be nice to
+# menuconfig, limit your lines to 70 characters. Use emacs' kfill.el
+# to edit this file or you lose.
 #
 # If you add a help text to this file, please try to be as gentle as
 # possible. Don't use unexplained acronyms and generally write for the
@@ -187,8 +191,10 @@
   old harddisk driver instead, say Y.  If you want to compile this
   driver as a module ( = code which can be inserted in and removed
   from the running kernel whenever you want), say M here and read
-  Documentation/modules.txt. The module will be called ide-disk.o. If
-  unsure, say Y.
+  Documentation/modules.txt. The module will be called ide-disk.o. Do
+  not compile this driver as a module if your root filesystem (the one
+  containing the directory /) is located on the IDE disk. If unsure,
+  say Y.
 
 Include IDE/ATAPI CDROM support
 CONFIG_BLK_DEV_IDECD
@@ -287,10 +293,10 @@
   and includes the Intel Triton I/II IDE interface chipset (i82371FB
   or i82371SB), you will want to enable this option to allow use of
   bus-mastering DMA data transfers. Read the comments at the beginning
-  of drivers/block/triton.c.  You can get the latest version of the
-  hdparm utility via ftp (user: anonymous) from
-  sunsite.unc.edu/pub/Linux/kernel/patches/diskdrives/; it is used to
-  tune your harddisk. It is safe to say Y to this question.
+  of drivers/block/triton.c and Documentation/ide.txt.  You can get
+  the latest version of the hdparm utility via ftp (user: anonymous)
+  from sunsite.unc.edu/pub/Linux/kernel/patches/diskdrives/; it is
+  used to tune your harddisk. It is safe to say Y to this question.
 
 Other IDE chipset support
 CONFIG_IDE_CHIPSETS
@@ -527,6 +533,12 @@
   proxy server). Chances are that you should use this on every machine
   being run as a router and not on any regular host. If unsure, say N.
 
+Socket Security API Support (EXPERIMENTAL)
+CONFIG_NET_SECURITY
+  Enable use of the socket security API. Note that Linux does not include
+  any security protocols currently and that this option only really supports
+  security on IPv4 links at the moment.
+
 Sun floppy controller support
 CONFIG_BLK_DEV_SUNFD
   This is support for floppy drives on Sun Sparc workstations. Say Y
@@ -600,14 +612,22 @@
   Say Y here if you have any non-standard serial boards --- boards
   which aren't supported using the standard "dumb" serial driver.
   This includes intelligent serial boards such as Cyclades,
-  Digiboards, etc.
+  Digiboards, etc. These are usually used for systems that need many
+  serial ports because they serve many terminals or dial-in
+  connections.  Note that the answer to this question won't directly
+  affect the kernel: saying N will just cause this configure script to
+  skip all the questions about non-standard serial boards. Most people
+  can say N here.
 
 Extended dumb serial driver options
 CONFIG_SERIAL_EXTENDED  
   If you wish to use any non-standard features of the standard "dumb"
   driver, say Y here.  This includes HUB6 support, shared serial
   interrupts, special multiport support, support for more than the
-  four COM 1/2/3/4 boards, etc.
+  four COM 1/2/3/4 boards, etc.  Note that the answer to this question
+  won't directly affect the kernel: saying N will just cause this
+  configure script to skip all the questions about serial driver
+  options. If unsure, say N.
 
 Support more than 4 serial ports
 CONFIG_SERIAL_MANY_PORTS
@@ -670,8 +690,9 @@
 CONFIG_MCA
   MicroChannel Architecture is found in some IBM PS/2 machines and
   laptops. It is a bus system similar to PCI or ISA.  See
-  Documentation/mca.txt before attempting to build an MCA bus kernel.
-  Note that this is still experimental code.
+  Documentation/mca.txt (and especially the web page given there)
+  before attempting to build an MCA bus kernel.  Note that this is
+  still experimental code.
 
 System V IPC
 CONFIG_SYSVIPC
@@ -941,13 +962,12 @@
 CONFIG_IP_ACCT
   This keeps track of your IP network traffic and produces some
   statistics. Usually, you only want to say Y here if your box will be
-  a router or a firewall for some local network, in which case you
-  naturally should have said Y to IP forwarding/gatewaying resp. IP
-  firewalling. The data is accessible with "cat /proc/net/ip_acct", so
-  you want to say Y to the /proc filesystem below, if you say Y
-  here. To specify what exactly should be recorded, you need the tool
-  ipfwadm (available via ftp (user: anonymous) from
-  ftp.xos.nl/pub/linux/ipfwadm/).
+  a router or a firewall for some local network. For the latter, you
+  need to say Y to IP firewalling. The data is accessible with "cat
+  /proc/net/ip_acct", so you want to say Y to the /proc filesystem
+  below, if you say Y here. To specify what exactly should be
+  recorded, you need the tool ipfwadm (available via ftp (user:
+  anonymous) from ftp.xos.nl/pub/linux/ipfwadm/).
 
 IP: tunneling
 CONFIG_NET_IPIP
@@ -962,9 +982,9 @@
   http://anchor.cs.binghamton.edu/~mobileip/LJ/index.html). Saying Y
   to this option will produce two modules ( = code which can be
   inserted in and removed from the running kernel whenever you want),
-  one encapsulator and one decapsulator. The module will be called
+  one encapsulator called tunnel.o and one decapsulator called
   ipip.o. You can read details in drivers/net/README.tunnel. Most
-  people can say N.
+  people won't need this and can say N.
   
 IP: firewall packet logging
 CONFIG_IP_FIREWALL_VERBOSE
@@ -1259,7 +1279,7 @@
 CONFIG_IPDDP
   This allows IP networking for users who only have Appletalk
   networking available.  This feature is experimental. Please see
-  http://www.maths.unm.edu/~bradford/ltpc.html
+  http://www.maths.unm.edu/~bradford/ltpc.html for support software.
 
 LocalTalk PC card support
 CONFIG_LTPC
@@ -1443,7 +1463,10 @@
   available as a module ( = code which can be inserted in and removed
   from the running kernel whenever you want). The module will be
   called scsi_mod.o. If you want to compile it as a module, say M here
-  and read Documentation/modules.txt and Documentation/scsi.txt.
+  and read Documentation/modules.txt and
+  Documentation/scsi.txt. However, do not compile this as a module if
+  your root filesystem (the one containing the directory /) is located
+  on a SCSI disk.
 
 SCSI disk support
 CONFIG_BLK_DEV_SD
@@ -1455,7 +1478,10 @@
   be inserted in and removed from the running kernel whenever you
   want). The module will be called sd_mod.o. If you want to compile it
   as a module, say M here and read Documentation/modules.txt and
-  Documentation/scsi.txt.
+  Documentation/scsi.txt. Do not compile this driver as a module if
+  your root filesystem (the one containing the directory /) is located
+  on a SCSI disk. In this case, do not compile the driver for your
+  SCSI host adapter (below) as a module either.
   
 SCSI tape support
 CONFIG_CHR_DEV_ST
@@ -1572,28 +1598,36 @@
   drivers/scsi/README.aic7xxx and in the SCSI-HOWTO, available via ftp
   (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it
   doesn't work out of the box, you may have to change some settings in
-  drivers/scsi/aic7xxx.h.  If you want to compile this as a module ( =
-  code which can be inserted in and removed from the running kernel
-  whenever you want), say M here and read
-  Documentation/modules.txt. The module will be called aic7xxx.o.
+  drivers/scsi/aic7xxx.h. It has been reported that the "wide
+  negotiation" on these cards is not quite working and should be
+  disabled. Note that the AHA2920 SCSI host adapter is *not* supported
+  by this driver; choose "Future Domain 16xx SCSI support" instead. If
+  you want to compile this driver a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt. The module will be
+  called aic7xxx.o.
 
 BusLogic SCSI support
 CONFIG_SCSI_BUSLOGIC
-  This is support for BusLogic MultiMaster and FlashPoint SCSI Host Adapters.
-  Consult the SCSI-HOWTO, available via anonymous ftp from sunsite.unc.edu in
-  /pub/Linux/docs/HOWTO, and the files README.BusLogic and README.FlashPoint in
-  drivers/scsi for more information.  If this driver does not work correctly
-  without modification, please contact the author, Leonard N. Zubkoff, by email
-  to lnz@dandelion.com.  You can also build this driver as a module ( = code
-  which can be inserted in and removed from the running kernel whenever you
-  want), but only a single instance may be loaded.  If you want to compile it
-  as a module, say M here and read Documentation/modules.txt.
+  This is support for BusLogic MultiMaster and FlashPoint SCSI Host
+  Adapters.  Consult the SCSI-HOWTO, available via anonymous ftp from
+  sunsite.unc.edu in /pub/Linux/docs/HOWTO, and the files
+  README.BusLogic and README.FlashPoint in drivers/scsi for more
+  information.  If this driver does not work correctly without
+  modification, please contact the author, Leonard N. Zubkoff, by
+  email to lnz@dandelion.com.  You can also build this driver as a
+  module ( = code which can be inserted in and removed from the
+  running kernel whenever you want), but only a single instance may be
+  loaded.  If you want to compile it as a module, say M here and read
+  Documentation/modules.txt. The module will be called BusLogic.o.
 
 Omit BusLogic SCSI FlashPoint support
 CONFIG_SCSI_OMIT_FLASHPOINT
-  This option allows you to omit the FlashPoint support from the BusLogic
-  SCSI driver.  The FlashPoint SCCB Manager code is substantial, so users of
-  MultiMaster Host Adapters may wish to omit it.
+  This option allows you to omit the FlashPoint support from the
+  BusLogic SCSI driver.  The FlashPoint SCCB Manager code is
+  substantial, so users of MultiMaster Host Adapters may wish to omit
+  it.
+
 
 DTC3180/3280 SCSI support
 CONFIG_SCSI_DTC3280
@@ -1661,7 +1695,7 @@
   given SCSI device. Go with the default unless you know what you're
   doing. Minimum is 2 and maximum is 8.
 
-Future Domain 16xx SCSI support
+Future Domain 16xx SCSI/AHA 2920 support
 CONFIG_SCSI_FUTURE_DOMAIN
   This is support for Future Domain's 16-bit SCSI host adapters
   (TMC-1660/1680, TMC-1650/1670, TMC-3260, TMC-1610M/MER/MEX) and
@@ -1826,13 +1860,31 @@
   This option must be set to N if your system has at least one 53C8XX based  
   scsi board with a vendor-specific BIOS (example: Tekram DC-390/U/W/F).
 
+assume boards are SYMBIOS compatible
+CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+  This option allows you to enable some features depending on GPIO
+  wiring.  These General Purpose Input/Output pins can be used for
+  vendor specific features or implementation of the standard SYMBIOS
+  features.  Genuine SYMBIOS boards use GPIO0 in output for controller
+  LED and GPIO3 bit as a flag indicating singled-ended/differential
+  interface.
+  If all the boards of your system are genuine SYMBIOS boards or use
+  BIOS and drivers from SYMBIOS, you would want to enable this option,
+  obviously at your own risks.
+  The driver behaves correctly on my system with this option enabled.
+  (SDMS 4.0 + Promise SCSI ULTRA 875 rev 0x3 + ASUS SC200 810A rev
+  0x12).  This option must be set to N if your system has at least one
+  53C8XX based scsi board with a vendor-specific BIOS (example: Tekram
+  DC-390/U/W/F).  If unsure, say N.
+
 IBMMCA SCSI support
 CONFIG_SCSI_IBMMCA
   If your computer sports an MCA bus system architecture (IBM PS/2)
-  with an SCSI harddrive, say Y here. This driver is also available as
-  a module ( = code which can be inserted in and removed from the
-  running kernel whenever you want). The module will be called
-  ibmmca.o. If you want to compile it as a module, say M here and read
+  with an SCSI harddrive, say Y here. Please read
+  Documentation/mca.txt. This driver is also available as a module ( =
+  code which can be inserted in and removed from the running kernel
+  whenever you want). The module will be called ibmmca.o. If you want
+  to compile it as a module, say M here and read
   Documentation/modules.txt.
 
 Always IN2000 SCSI support
@@ -2273,12 +2325,33 @@
   compatible to popular modems using TCM3105 or AM7911. The demodulator
   requires about 12% of the CPU power of a Pentium 75 CPU per channel.
 
+Soundmodem 1200 baud AFSK using floating point
+CONFIG_SOUNDMODEM_AFSK1200_FP
+  This option enables floating point calculations to be used for the
+  AFSK1200 baud modem. The Intel Pentium is a perverted chip because
+  integer multiplications are, although easier to implement in silicon,
+  an order of magnitude slower than floating point calculations.
+  Enabling this option uses a highly optimized assembler routine for
+  correlations, modeled after the one published by Phil Karn, KA9Q.
+  This reduces the computing power needed on Intel Pentium chips to
+  about 50%. On the other hand, Pentium clones with faster integer
+  multiply and slower floating point multiply will probably take
+  longer with this option turned on. As a rule of thumb, enable it for
+  Intel Pentium and Pentium Pro processors, and disable it for
+  anything else.
+  I (sailer@ife.ee.ethz.ch) am very interested in figures. If you are
+  willing to give me a feedback, please compile the driver once with
+  this option enabled and once with it disabled, and send me the cycle
+  counter numbers obtained with both compilations, and your exact
+  chip. The cycle counter numbers can be obtained with a recent
+  sethdlc utility.
+
 Soundcard modem support for 4800 baud HAPN-1 modulation
 CONFIG_SOUNDMODEM_HAPN4800
-  This option enables the soundmodem driver 4800 baud HAPN-1 compatible
-  modem. This modulation seems to be widely used 'down under' and in
-  the netherlands. Here, nobody uses it, so I could not test if it works.
-  It is compatible to itself, however :-)
+  This option enables the soundmodem driver 4800 baud HAPN-1
+  compatible modem. This modulation seems to be widely used 'down
+  under' and in the Netherlands. Here, nobody uses it, so I could not
+  test if it works.  It is compatible to itself, however :-)
 
 Soundcard modem support for 9600 baud FSK G3RUH modulation
 CONFIG_SOUNDMODEM_FSK9600
@@ -2293,20 +2366,22 @@
 CONFIG_SOUNDMODEM_FLOAT
   This option enables floating point calculations to be used for the
   AFSK1200 baud modem. The Intel Pentium is a perverted chip because
-  integer multiplications are, altough easier to implement in silicon,
+  integer multiplications are, although easier to implement in silicon,
   an order of a magnitude slower than floating point calculations.
   Enabling this option uses a highly optimized assembler routine for
-  correlations, modelled after the one published by Phil Karn, KA9Q.
-  This reduces the computing power needed on Intel Pentium chips to about
-  50%. On the other hand, Pentium clones with faster integer multiply and
-  slower floating point multiply will probably take longer with this
-  option turned on. As a rule of thumb, enable it for Intel Pentium and
-  Pentium Pro processors, and disable it for anything else.
+  correlations, modeled after the one published by Phil Karn, KA9Q.
+  This reduces the computing power needed on Intel Pentium chips to
+  about 50%. On the other hand, Pentium clones with faster integer
+  multiply and slower floating point multiply will probably take
+  longer with this option turned on. As a rule of thumb, enable it for
+  Intel Pentium and Pentium Pro processors, and disable it for
+  anything else.
   I (sailer@ife.ee.ethz.ch) am very interested in figures. If you are
-  willing to give me a feedback, please compile the driver once with this
-  option enabled and once with it disabled, and send me the cycle counter
-  numbers obtained with both compilations, and your exact chip. The cycle
-  counter numbers can be optained by a recent sethdlc utility.
+  willing to give me a feedback, please compile the driver once with
+  this option enabled and once with it disabled, and send me the cycle
+  counter numbers obtained with both compilations, and your exact
+  chip. The cycle counter numbers can be obtained by a recent sethdlc
+  utility.
 
 Serial port KISS driver for AX.25
 CONFIG_MKISS
@@ -2429,18 +2504,19 @@
   achievable with commonly used asynchronous modem connections.
   Usually, a quite expensive external device called `WAN router' is
   needed to connect to a WAN.
-      As an alternative, WAN routing can be built into the Linux kernel. 
-  With relatively inexpensive WAN interface cards available on the
-  market, a perfectly usable router can be built for less than half the
-  price of an external router.  If you have one of those cards (with
-  appropriate WAN Link Driver) and wish to use your Linux box as a WAN
-  router, you may say 'Y' to this option.  You will also need a
-  wan-tools package available via FTP (user: anonymous) from
+      As an alternative, WAN routing can be built into the Linux
+  kernel.  With relatively inexpensive WAN interface cards available
+  on the market, a perfectly usable router can be built for less than
+  half the price of an external router.  If you have one of those
+  cards (with appropriate WAN Link Driver) and wish to use your Linux
+  box as a WAN router, you may say 'Y' to this option.  You will also
+  need a wan-tools package available via FTP (user: anonymous) from
   ftp.sangoma.com.  Read Documentation/networking/wan-router.txt for
   more information.
       WAN routing is always built as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
-  For general information about modules read Documentation/modules.txt.
+  The module will be called wanrouter.o.  For general information
+  about modules read Documentation/modules.txt.
 
 WAN Drivers
 CONFIG_WAN_DRIVERS
@@ -2457,35 +2533,39 @@
   is a family of intelligent multiprotocol WAN adapters with data
   transfer rates up to T1 (1.544 Mbps).  They are also known as
   Synchronous Data Link Adapters (SDLA) and designated S502E(A), S503
-  or S508.  If you have one of these cards, say 'Y' to this option.
-      WANPIPE driver is always built as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want).
-  For general information about modules read Documentation/modules.txt.
+  or S508. These cards support the X.25, Frame Relay, and PPP
+  protocols. If you have one or more of these cards, say 'Y' to this
+  option.  The next questions will ask you about the protocols you
+  want the driver to support. The driver will be compiled as a module
+  ( = code which can be inserted in and removed from the running
+  kernel whenever you want).  The module will be called wanpipe.o.
+  For general information about modules read
+  Documentation/modules.txt.
 
 Maximum number of cards
 CONFIG_WANPIPE_CARDS
       Enter number of WANPIPE adapters installed in your machine.  The
-  driver can support up to 8 cards.  You may enter more that you
+  driver can support up to 8 cards.  You may enter more than you
   actually have if you plan to add more cards in the future without
   re-compiling the driver, but remember that in this case you'll waste
   some kernel memory (about 1K per card).
 
 WANPIPE X.25 support
 CONFIG_WANPIPE_X25
-      Say 'Y' to this option, if you are planning to connect WANPIPE
+      Say 'Y' to this option, if you are planning to connect a WANPIPE
   card to an X.25 network.  If you say 'N', the X.25 support will not
   be included in the driver (saves about 16K of kernel memory).
 
 WANPIPE Frame Relay support
 CONFIG_WANPIPE_FR
-      Say 'Y' to this option, if you are planning to connect WANPIPE
+      Say 'Y' to this option, if you are planning to connect a WANPIPE
   card to a frame relay network.  If you say 'N', the frame relay
   support will not be included in the driver (saves about 16K of
   kernel memory).
 
 WANPIPE PPP support
 CONFIG_WANPIPE_PPP
-      Say 'Y' to this option, if you are planning to connect WANPIPE
+      Say 'Y' to this option, if you are planning to connect a WANPIPE
   card to a leased line using Point-to-Point protocol (PPP).  If you
   say 'N', the PPP support will not be included in the driver (saves
   about 16K of kernel memory).
@@ -2494,7 +2574,11 @@
 CONFIG_SUN_LANCE
   This is support for lance ethernet cards on Sun workstations such as
   the Sparcstation IPC (any Sparc with a network interface 'le0' under
-  SunOS basically).
+  SunOS basically). This driver is also available as a module ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). The module will be called lance.o. If you want
+  to compile it as a module, say M here and read
+  Documentation/modules.txt.
 
 Sun Intel Ethernet support
 CONFIG_SUN_INTEL
@@ -2768,16 +2852,17 @@
 
 CS89x0 support
 CONFIG_CS89x0
-  Support for CS89x0 chipset based ethernet cards. 
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  Support for CS89x0 chipset based ethernet cards.  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 as well as
-  drivers/net/depca.c.  If you want to compile this as a module ( =
-  code which can be inserted in and removed from the running kernel
-  whenever you want), 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
+  Documentation/networking/cs89x0.txt.  If you want to compile this as
+  a module ( = code which can be inserted in and removed from the
+  running kernel whenever you want), say M here and read
+  Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt. The module will be called
+  cs89x.o. 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.
 
 DEPCA support
@@ -3065,7 +3150,7 @@
   linux, read the Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
-DE425, DE434, DE435 support
+Generic DECchip & DIGITAL EtherWORKS PCI/EISA
 CONFIG_DE4X5
   This is support for the DIGITAL series of PCI/EISA ethernet
   cards. These include the DE425, DE434, DE435, DE450 and DE500
@@ -3088,9 +3173,9 @@
   cards and also works with cards based on the DECchip
   21040/21041/21140 (Tulip series) chips. Some LinkSys PCI cards are
   of this type. (If your card is NOT SMC EtherPower 10/100 PCI
-  (smc9332dst), you can also try the driver from "DE425, DE434, DE435
-  support", above.) However, most people with a network card of this
-  type will say Y here. Do read the Ethernet-HOWTO, available via ftp
+  (smc9332dst), you can also try the driver for "Generic DECchip"
+  cards, above. However, most people with a network card of this type
+  will say Y here.) Do read the Ethernet-HOWTO, available via ftp
   (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. More
   specific information is contained in
   Documentation/networking/tulip.txt. This driver is also available as
@@ -3914,7 +3999,7 @@
 CONFIG_STALLION
   If you have an EasyIO or EasyConnection 8/32 multiport Stallion
   card, then this is for you; say Y. Make sure to read
-  drivers/char/README.stallion. If you want to compile this as a
+  Documentation/stallion.txt. If you want to compile this as a
   module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
   Documentation/modules.txt. The module will be called stallion.o.
@@ -3923,7 +4008,7 @@
 CONFIG_ISTALLION
   If you have an EasyConnection 8/64, ONboard, Brumby or Stallion
   serial multiport card, say Y here. Make sure to read
-  drivers/char/README.stallion. To compile it as a module ( = code
+  Documentation/stallion.txt. To compile it as a module ( = code
   which can be inserted in and removed from the running kernel
   whenever you want), say M here and read
   Documentation/modules.txt. The module will be called istallion.o.
@@ -4134,14 +4219,14 @@
   things to try when experiencing seemingly random, "weird" problems:
    1) passing the "no-hlt" option to the kernel 
    2) passing the "no-387" option to the kernel 
-   3) passing the "mem=4M" option to the kernel (thereby disabling 
+   3) passing the "floppy=nodma" option to the kernel
+   4) passing the "mem=4M" option to the kernel (thereby disabling 
       all but the first 4M of RAM)
-   4) Reading the sig11 FAQ at http://www.bitwizard.nl/sig11/
-   5) disabling the cache from your BIOS settings
-   6) installing a better fan
-   7) exchanging RAM chips 
-   8) exchanging the motherboard.
-
+   5) reading the sig11 FAQ at http://www.bitwizard.nl/sig11/
+   6) disabling the cache from your BIOS settings
+   7) installing a better fan
+   8) exchanging RAM chips 
+   9) exchanging the motherboard.
 
 Ignore USER SUSPEND
 CONFIG_APM_IGNORE_USER_SUSPEND
@@ -4279,9 +4364,9 @@
   /proc/rtc and its behaviour is set by various ioctls on
   /dev/rtc. People running SMP (= multiprocessor) versions of Linux
   should say Y here to read and set the RTC clock in a SMP compatible
-  fashion. If you think you have a use for such a device (such as
-  periodic data sampling), then say Y here, and go read the file
-  Documentation/rtc.txt for details.
+  fashion. (They should also read Documentation/smp.) If you think you
+  have a use for such a device (such as periodic data sampling), then
+  say Y here, and go read the file Documentation/rtc.txt for details.
 
 Sound card support
 CONFIG_SOUND
@@ -4537,8 +4622,8 @@
   (mgetty+sendfax by gert@greenie.muc.de with an extension, available
   with the ISDN utility package for example), you will be able to use
   your Linux box as an ISDN-answering machine. Of course, this must be
-  supported by the lowlevel driver also. Currently, the Teles and HiSax
-  drivers are the only voice-supporting drivers. See
+  supported by the lowlevel driver also. Currently, the Teles and
+  HiSax drivers are the only voice-supporting drivers. See
   Documentation/isdn/README.audio for more information.
 
 ICN 2B and 4B support
@@ -4560,7 +4645,7 @@
   and many compatibles.
   There is a new, heavily improved driver called HiSax which can be
   enabled in the next section. This driver will be removed soon. Please
-  use this driver only if you cannot get the HiSax driver working.
+  use this driver only if you cannot get the HiSax driver to work.
   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
@@ -4569,13 +4654,15 @@
 HiSax SiemensChipSet driver support
 CONFIG_ISDN_DRV_HISAX
   This is an alternative driver supporting the Siemens chipset on
-  various ISDN-cards (like AVM A1, Elsa ISDN cards, Teles S0-16.0, 
-  Teles S0-16.3, Teles S0-8, Teles/Creatix PnP, ITK micro ix1 and 
-  many compatibles). It's a complete rewrite of the original Teles 
-  driver.  So you either say M or Y here and N in the above Teles 
-  section.
-  See Documentation/isdn/README.HiSax for further informations on
-  using this driver.
+  various ISDN-cards (like AVM A1, Elsa ISDN cards, Teles S0-16.0,
+  Teles S0-16.3, Teles S0-8, Teles/Creatix PnP, ITK micro ix1 and many
+  compatibles). It's a complete rewrite of the original Teles driver.
+  So you either say M or Y here and N in the above Teles section. If
+  you want to compile this as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want), say M
+  here and read Documentation/modules.txt. The module will be called
+  hisax.o.  See Documentation/isdn/README.HiSax for further
+  informations on using this driver.
 
 HiSax Support for Teles 16.0/8.0
 CONFIG_HISAX_16_0
@@ -4617,14 +4704,14 @@
 
 HiSax Support for EURO/DSS1
 CONFIG_HISAX_EURO
-  You should choose your D-channel protocol your local
+  You should choose the D-channel protocol your local
   telephone service provider uses here by saying Y or N.
   NOTE: This is mutually exclusive with HiSax Support for
-  german 1TR6 if you have only one ISDN card installed.
+  German 1TR6 if you have only one ISDN card installed.
 
-HiSax Support for german 1TR6
+HiSax Support for German 1TR6
 CONFIG_HISAX_1TR6
-  You should choose your D-channel protocol your local
+  You should choose the D-channel protocol your local
   telephone service provider uses here by saying Y or N.
   NOTE: This is mutually exclusive with HiSax Support for
   EURO/DSS1 if you have only one ISDN card installed.
@@ -4643,10 +4730,12 @@
 
 Spellcaster support (EXPERIMENTAL)
 CONFIG_ISDN_DRV_SC
-  This enables support for the Spellcaster BRI boards. This driver
-  currently builds in a modularized version only.
-  See Documentation/isdn/README.sc and http://www.spellcast.com
-  for more information.
+  This enables support for the Spellcaster BRI ISDN boards. This
+  driver currently builds only in a modularized version ( = code which
+  can be inserted in and removed from the running kernel whenever you
+  want, details in Documentation/modules.txt); the module will be
+  called sc.o.  See Documentation/isdn/README.sc and
+  http://www.spellcast.com for more information.
 
 Support for AP1000 multicomputer
 CONFIG_AP1000
@@ -5122,7 +5211,7 @@
 # LocalWords:  wdt hdb hdc bugfix SiS vlb Acculogic CSA DTC dtc Holtek ht QDI
 # LocalWords:  QD qd UMC umc ALI ali lena fnet fr homepage azstarnet axplinux
 # LocalWords:  Avanti XL AlphaStations Jensen DECpc AXPpci UDB Cabriolet MCA RC
-# LocalWords:  AlphaPC uwaterloo cpbeaure mca AOUT OUTput PPro sipx gwdg lo nwe
+# LocalWords:  AlphaPC mca AOUT OUTput PPro sipx gwdg lo nwe FourPort Boca unm
 # LocalWords:  Keepalive linefill RELCOM keepalive analogue CDR conf CDI INIT
 # LocalWords:  OPTi isp irq noisp VFAT vfat NTFS losetup dmsdosfs dosfs ISDN MP
 # LocalWords:  NOWAYOUT behaviour dialin isdn callback BTX Teles ICN EDSS Cisco
@@ -5177,5 +5266,10 @@
 # LocalWords:  ibmmca lapbether mkiss dlci sdla fmv eepro eexpress ni hp ne es
 # LocalWords:  ibmtr isofs ROMFS romfs pcxx cyclades istallion psaux msbusmouse
 # LocalWords:  atixlmouse sbin softdog pcwd USS Lite ACI miroSOUND PCM miroPCM
-# LocalWords:  microcontroller miro Voxware downloading teles acsi slm gvp
-# LocalWords:  atari ariadne amigamouse atarimouse builtin
+# LocalWords:  microcontroller miro Voxware downloading teles acsi slm gvp ltpc
+# LocalWords:  atari ariadne amigamouse atarimouse builtin IPDDP maths bradford
+# LocalWords:  LocalTalk AppleTalk Farallon PhoneNet Zubkoff lnz SCCB HAPN WANs
+# LocalWords:  wanrouter WANPIPE multiprotocol Mbps wanpipe EtherWORKS nodma SC
+# LocalWords:  smp HiSax SiemensChipSet Siemens AVM Elsa ITK hisax PCC MICROR
+# LocalWords:  Mircolink EURO DSS Spellcaster BRI sc spellcast Digiboards GPIO
+# LocalWords:  SYMBIOS COMPAT SDMS rev ASUS Tekram
diff -u --recursive --new-file v2.1.29/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.29/linux/MAINTAINERS	Mon Mar 17 14:54:20 1997
+++ linux/MAINTAINERS	Thu Mar 20 17:11:51 1997
@@ -266,6 +266,14 @@
 W:	http://www.uk.linux.org/NetNews.html
 S:	Maintained
 
+NETWORKING [IPv4/IPv6]:
+P:	David S. Miller
+M:	davem@caip.rutgers.edu
+P:	Eric Schenk
+M:	Eric.Schenk@dna.lth.se
+L:	netdev@roxanne.nuclecu.unam.mx
+S:	Maintained
+
 PPP PROTOCOL DRIVERS AND COMPRESSORS
 P:	Al Longyear
 M:	longyear@pobox.com
@@ -304,8 +312,8 @@
 S:	Maintained
 
 SPARC:
-P:	Eddie C. Dost
-M:	ecd@skynet.be
+P:	David S. Miller
+M:	davem@caip.rutgers.edu
 L:	sparclinux@vger.rutgers.edu
 S:	Maintained
 
@@ -369,9 +377,9 @@
 M:	jmaurer@cck.uni-kl.de
 S:	Maintained
 
-LANCE AND LANCE32 NETWORK DRIVER
+PCNET32 NETWORK DRIVER
 P:	Thomas Bogendoerfer
-M:	tsbogend@bigbug.franken.de
+M:	tsbogend@alpha.franken.de
 L:	linux-net@vger.rutgers.edu
 S:	Maintained
 
diff -u --recursive --new-file v2.1.29/linux/Makefile linux/Makefile
--- v2.1.29/linux/Makefile	Mon Mar 17 14:54:20 1997
+++ linux/Makefile	Mon Mar 10 15:51:29 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 29
+SUBLEVEL = 30
 
 ARCH = i386
 
diff -u --recursive --new-file v2.1.29/linux/Rules.make linux/Rules.make
--- v2.1.29/linux/Rules.make	Sun Jan 26 02:07:03 1997
+++ linux/Rules.make	Wed Mar 19 16:27:09 1997
@@ -82,8 +82,7 @@
 # This make dependencies quickly
 #
 fastdep: dummy
-	if [ -n "$(wildcard *.[chS])" ]; then \
-	$(TOPDIR)/scripts/mkdep *.[chS] > .depend; fi
+	$(TOPDIR)/scripts/mkdep *.[chS] > .depend
 ifdef ALL_SUB_DIRS
 	set -e; for i in $(ALL_SUB_DIRS); do $(MAKE) -C $$i fastdep; done
 endif
diff -u --recursive --new-file v2.1.29/linux/arch/alpha/kernel/alpha_ksyms.c linux/arch/alpha/kernel/alpha_ksyms.c
--- v2.1.29/linux/arch/alpha/kernel/alpha_ksyms.c	Sun Jan 26 03:40:45 1997
+++ linux/arch/alpha/kernel/alpha_ksyms.c	Thu Mar 20 17:11:51 1997
@@ -81,9 +81,11 @@
 EXPORT_SYMBOL(__kernel_thread);
 EXPORT_SYMBOL(start_thread);
 
+/* Networking helper routines. */
 EXPORT_SYMBOL(csum_tcpudp_magic);
 EXPORT_SYMBOL(ip_fast_csum);
 EXPORT_SYMBOL(ip_compute_csum);
+EXPORT_SYMBOL(csum_partial_copy);
 
 /*
  * The following are specially called from the uaccess assembly stubs.
diff -u --recursive --new-file v2.1.29/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S
--- v2.1.29/linux/arch/alpha/kernel/entry.S	Tue Mar  4 10:25:23 1997
+++ linux/arch/alpha/kernel/entry.S	Tue Mar 18 10:44:31 1997
@@ -526,14 +526,9 @@
 ret_from_sys_call:
 	cmovne	$26,0,$19		/* $19 = 0 => non-restartable */
 	/* check bottom half interrupts */
-	lda	$0,intr_count
-	ldl	$1,0($0)
 	bne	$1,ret_from_handle_bh
-	lda	$2,bh_active
-	ldq	$3,0($2)
-	lda	$2,bh_mask
-	ldq	$4,0($2)
-	addq	$1,1,$1
+	ldq	$3,bh_active
+	ldq	$4,bh_mask
 	and	$3,$4,$2
 	bne	$2,handle_bottom_half
 ret_from_handle_bh:
@@ -618,20 +613,13 @@
 
 	.align 3
 handle_bottom_half:
-	/*
-	 * We're called with $0 containing the address of
-	 * 'intr_count' and $1 containing 'intr_count+1'
-	 */
-	stl	$1,0($0)	/* intr_count = 1 */
 	subq	$30,16,$30
 	stq	$19,0($30)	/* save syscall nr */
 	stq	$20,8($30)	/* and error indication (a3) */
 	jsr	$26,do_bottom_half
-	lda	$0,intr_count
 	ldq	$19,0($30)
 	ldq	$20,8($30)
 	addq	$30,16,$30
-	stl	$31,0($0)	/* intr_count = 0 */
 	br	$31,ret_from_handle_bh
 
 	.align 3
diff -u --recursive --new-file v2.1.29/linux/arch/alpha/kernel/process.c linux/arch/alpha/kernel/process.c
--- v2.1.29/linux/arch/alpha/kernel/process.c	Sun Jan 26 04:26:39 1997
+++ linux/arch/alpha/kernel/process.c	Thu Mar 20 17:11:51 1997
@@ -27,6 +27,7 @@
 #include <linux/stat.h>
 #include <linux/mman.h>
 #include <linux/elfcore.h>
+#include <linux/reboot.h>
 
 #include <asm/reg.h>
 #include <asm/uaccess.h>
@@ -63,7 +64,7 @@
 	return ret;
 }
 
-void hard_reset_now(void)
+void machine_restart(char * __unused)
 {
 #if defined(CONFIG_ALPHA_SRM) && defined(CONFIG_ALPHA_ALCOR)
 	/* who said DEC engineer's have no sense of humor? ;-)) */
@@ -71,6 +72,14 @@
 	mb();
 #endif
 	halt();
+}
+
+void machine_halt(void)
+{
+}
+
+void machine_power_off(void)
+{
 }
 
 void show_regs(struct pt_regs * regs)
Binary files v2.1.29/linux/arch/i386/boot/map and linux/arch/i386/boot/map differ
diff -u --recursive --new-file v2.1.29/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.29/linux/arch/i386/defconfig	Tue Mar  4 10:25:23 1997
+++ linux/arch/i386/defconfig	Wed Mar 12 15:12:52 1997
@@ -152,6 +152,7 @@
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_ISA is not set
 CONFIG_NET_EISA=y
+# CONFIG_PCNET32 is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
 CONFIG_DE4X5=y
diff -u --recursive --new-file v2.1.29/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.1.29/linux/arch/i386/kernel/entry.S	Thu Feb 27 10:57:29 1997
+++ linux/arch/i386/kernel/entry.S	Mon Mar 24 16:38:07 1997
@@ -153,12 +153,8 @@
 	ALIGN
 	.globl	ret_from_smpfork
 ret_from_smpfork:
-	GET_CURRENT(%ebx)
-	movl	$NO_PROC_ID, SYMBOL_NAME(active_kernel_processor)
-	lock
-	btrl	$0, SYMBOL_NAME(kernel_flag)
-	sti
-	jmp	9f
+	btrl	$0, SYMBOL_NAME(scheduler_lock)
+	jmp	ret_from_sys_call
 #endif /* __SMP__ */
 
 	ALIGN
@@ -187,13 +183,7 @@
 ret_from_intr:
 ret_from_sys_call:
 	GET_CURRENT(%ebx)
-	cmpl $0,SYMBOL_NAME(intr_count)
-#ifdef __SMP__
-	jne 2f
-#else
-	jne 1f
-#endif
-9:	movl SYMBOL_NAME(bh_mask),%eax
+	movl SYMBOL_NAME(bh_mask),%eax
 	andl SYMBOL_NAME(bh_active),%eax
 	jne handle_bottom_half
 2:	movl EFLAGS(%esp),%eax		# mix EFLAGS and CS
diff -u --recursive --new-file v2.1.29/linux/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c
--- v2.1.29/linux/arch/i386/kernel/i386_ksyms.c	Sun Jan 26 02:07:04 1997
+++ linux/arch/i386/kernel/i386_ksyms.c	Thu Mar 20 17:11:51 1997
@@ -5,10 +5,12 @@
 #include <linux/elfcore.h>
 #include <linux/mca.h>
 #include <linux/sched.h>
+#include <linux/in6.h>
 
 #include <asm/semaphore.h>
 #include <asm/processor.h>
 #include <asm/uaccess.h>
+#include <asm/checksum.h>
 #include <asm/io.h>
 
 extern void dump_thread(struct pt_regs *, struct user *);
@@ -25,7 +27,11 @@
 EXPORT_SYMBOL(ioremap);
 EXPORT_SYMBOL(iounmap);
 EXPORT_SYMBOL_NOVERS(__down_failed);
+EXPORT_SYMBOL_NOVERS(__down_failed_interruptible);
 EXPORT_SYMBOL_NOVERS(__up_wakeup);
+
+/* Networking helper routines. */
+EXPORT_SYMBOL(csum_partial_copy);
 
 #ifdef __SMP__
 EXPORT_SYMBOL(apic_reg);	/* Needed internally for the I386 inlines */
diff -u --recursive --new-file v2.1.29/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v2.1.29/linux/arch/i386/kernel/irq.c	Tue Mar  4 10:25:23 1997
+++ linux/arch/i386/kernel/irq.c	Wed Mar 26 07:41:48 1997
@@ -33,6 +33,7 @@
 #include <asm/irq.h>
 #include <asm/bitops.h>
 #include <asm/smp.h>
+#include <asm/pgtable.h>
 
 #define CR0_NE 32
 
@@ -126,15 +127,14 @@
 BUILD_IRQ(SECOND,10,0x04)
 BUILD_IRQ(SECOND,11,0x08)
 BUILD_IRQ(SECOND,12,0x10)
-#ifdef __SMP__
-BUILD_MSGIRQ(SECOND,13,0x20)
-#else
 BUILD_IRQ(SECOND,13,0x20)
-#endif
 BUILD_IRQ(SECOND,14,0x40)
 BUILD_IRQ(SECOND,15,0x80)
+
 #ifdef __SMP__
-BUILD_RESCHEDIRQ(16)
+BUILD_SMP_INTERRUPT(reschedule_interrupt)
+BUILD_SMP_INTERRUPT(invalidate_interrupt)
+BUILD_SMP_INTERRUPT(stop_cpu_interrupt)
 #endif
 
 /*
@@ -146,9 +146,6 @@
 	IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt,
 	IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt,
 	IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt	
-#ifdef __SMP__	
-	,IRQ16_interrupt
-#endif
 };
 
 static void (*fast_interrupt[16])(void) = {
@@ -179,15 +176,6 @@
 
 static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
 
-#ifdef __SMP__
-
-/*
- * On SMP boards, irq13 is used for interprocessor interrupts (IPI's).
- */
-static struct irqaction irq13 = { smp_message_irq, SA_INTERRUPT, 0, "IPI", NULL, NULL };
-
-#else
-
 /*
  * Note that on a 486, we don't want to do a SIGFPE on an irq13
  * as the irq is unreliable, and exception 16 works correctly
@@ -211,8 +199,6 @@
 
 static struct irqaction irq13 = { math_error_irq, 0, 0, "math error", NULL, NULL };
 
-#endif
-
 /*
  * IRQ2 is cascade interrupt to second interrupt controller
  */
@@ -335,6 +321,174 @@
 #endif 
 
 
+/*
+ * Global interrupt locks for SMP. Allow interrupts to come in on any
+ * CPU, yet make cli/sti act globally to protect critical regions..
+ */
+#ifdef __SMP__
+unsigned char global_irq_holder = NO_PROC_ID;
+unsigned volatile int global_irq_lock;
+unsigned volatile int global_irq_count;
+unsigned int local_irq_count[NR_CPUS];
+
+#define irq_active(cpu) \
+	(global_irq_count != local_irq_count[cpu])
+
+#define INIT_STUCK 10000000
+
+#define STUCK(x) \
+if (!--stuck) {printk(#x " stuck at %08lx, waiting for %08lx\n", where, previous); stuck = INIT_STUCK;}
+
+/*
+ * "global_cli()" is a special case, in that it can hold the
+ * interrupts disabled for a longish time, and also because
+ * we may be doing TLB invalidates when holding the global
+ * IRQ lock for historical reasons. Thus we may need to check
+ * SMP invalidate events specially by hand here (but not in
+ * any normal spinlocks)
+ */
+static inline void check_smp_invalidate(int cpu)
+{
+	if (test_bit(cpu, &smp_invalidate_needed)) {
+		clear_bit(cpu, &smp_invalidate_needed);
+		local_flush_tlb();
+	}
+}
+
+static inline void get_irqlock(int cpu, unsigned long where)
+{
+static unsigned long previous;
+	int local_count;
+	int stuck = INIT_STUCK;
+
+	if (set_bit(0,&global_irq_lock)) {
+		/* do we already hold the lock? */
+		if ((unsigned char) cpu == global_irq_holder)
+			return;
+		/* Uhhuh.. Somebody else got it. Wait.. */
+		do {
+			do {
+				STUCK(irqlock1);
+				check_smp_invalidate(cpu);
+			} while (test_bit(0,&global_irq_lock));
+		} while (set_bit(0,&global_irq_lock));		
+	}
+	/*
+	 * Ok, we got the lock bit.
+	 * But that's actually just the easy part.. Now
+	 * we need to make sure that nobody else is running
+	 * in an interrupt context. 
+	 */
+	local_count = local_irq_count[cpu];
+
+	/* Are we the only one in an interrupt context? */
+	while (local_count != global_irq_count) {
+		/*
+		 * No such luck. Now we need to release the lock,
+		 * _and_ release our interrupt context, because
+		 * otherwise we'd have dead-locks and live-locks
+		 * and other fun things.
+		 */
+		atomic_sub(local_count, &global_irq_count);
+		global_irq_lock = 0;
+
+		/*
+		 * Wait for everybody else to go away and release
+		 * their things before trying to get the lock again.
+		 */
+		for (;;) {
+			STUCK(irqlock2);
+			check_smp_invalidate(cpu);
+			if (global_irq_count)
+				continue;
+			if (global_irq_lock)
+				continue;
+			if (!set_bit(0,&global_irq_lock))
+				break;
+		}
+		atomic_add(local_count, &global_irq_count);
+	}
+
+	/*
+	 * Finally.
+	 */
+	global_irq_holder = cpu;
+	previous = where;
+}
+
+void __global_cli(void)
+{
+	int cpu = smp_processor_id();
+	unsigned long where;
+
+	__asm__("movl 12(%%esp),%0":"=r" (where));
+	__cli();
+	get_irqlock(cpu, where);
+}
+
+void __global_sti(void)
+{
+	release_irqlock(smp_processor_id());
+	__sti();
+}
+
+unsigned long __global_save_flags(void)
+{
+	return global_irq_holder == (unsigned char) smp_processor_id();
+}
+
+void __global_restore_flags(unsigned long flags)
+{
+	switch (flags) {
+	case 0:
+		__global_sti();
+		break;
+	case 1:
+		__global_cli();
+		break;
+	default:
+		printk("global_restore_flags: %08lx (%08lx)\n",
+			flags, (&flags)[-1]);
+	}
+}
+
+#undef INIT_STUCK
+#define INIT_STUCK 200000000
+
+#undef STUCK
+#define STUCK \
+if (!--stuck) {printk("irq_enter stuck (irq=%d, cpu=%d, global=%d)\n",irq,cpu,global_irq_holder); stuck = INIT_STUCK;}
+
+static inline void irq_enter(int cpu, int irq)
+{
+	int stuck = INIT_STUCK;
+
+	hardirq_enter(cpu);
+	while (test_bit(0,&global_irq_lock)) {
+		if ((unsigned char) cpu == global_irq_holder) {
+			printk("BAD! Local interrupts enabled, global disabled\n");
+			break;
+		}
+		STUCK;
+		/* nothing */;
+	}
+	atomic_inc(&intr_count);
+}
+
+static inline void irq_exit(int cpu, int irq)
+{
+	__cli();
+	atomic_dec(&intr_count);
+	hardirq_exit(cpu);
+	release_irqlock(cpu);
+}
+
+#else
+
+#define irq_enter(cpu, irq)	do { } while (0)
+#define irq_exit(cpu, irq)	do { } while (0)
+
+#endif
 
 /*
  * do_IRQ handles IRQ's that have been installed without the
@@ -345,20 +499,16 @@
  */
 asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
 {
-	struct irqaction * action = *(irq + irq_action);
-	int do_random = 0;
-
-	lock_kernel();
-	atomic_inc(&intr_count);
-#ifdef __SMP__
-	if(smp_threads_ready && active_kernel_processor!=smp_processor_id())
-		panic("IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id());
-#endif
+	struct irqaction * action;
+	int do_random, cpu = smp_processor_id();
 
+	irq_enter(cpu, irq);
 	kstat.interrupts[irq]++;
-#ifdef __SMP_PROF__
-	int_count[smp_processor_id()][irq]++;
-#endif
+
+	/* slow interrupts run with interrupts enabled */
+	__sti();
+	action = *(irq + irq_action);
+	do_random = 0;
 	while (action) {
 		do_random |= action->flags;
 		action->handler(irq, action->dev_id, regs);
@@ -366,8 +516,7 @@
 	}
 	if (do_random & SA_SAMPLE_RANDOM)
 		add_interrupt_randomness(irq);
-	atomic_dec(&intr_count);
-	unlock_kernel();
+	irq_exit(cpu, irq);
 }
 
 /*
@@ -377,21 +526,13 @@
  */
 asmlinkage void do_fast_IRQ(int irq)
 {
-	struct irqaction * action = *(irq + irq_action);
-	int do_random = 0;
-	
-	lock_kernel();
-	intr_count++;
-#ifdef __SMP__
-	/* IRQ 13 is allowed - that's a flush tlb */
-	if(smp_threads_ready && active_kernel_processor!=smp_processor_id() && irq!=13)
-		panic("fast_IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id());
-#endif
+	struct irqaction * action;
+	int do_random, cpu = smp_processor_id();
 
+	irq_enter(cpu, irq);
 	kstat.interrupts[irq]++;
-#ifdef __SMP_PROF__
-	int_count[smp_processor_id()][irq]++;
-#endif
+	action = *(irq + irq_action);
+	do_random = 0;
 	while (action) {
 		do_random |= action->flags;
 		action->handler(irq, action->dev_id, NULL);
@@ -399,8 +540,7 @@
 	}
 	if (do_random & SA_SAMPLE_RANDOM)
 		add_interrupt_randomness(irq);
-	intr_count--;
-	unlock_kernel();
+	irq_exit(cpu, irq);
 }
 
 int setup_x86_irq(int irq, struct irqaction * new)
@@ -561,7 +701,9 @@
 	/* This bit is a hack because we don't send timer messages to all processors yet */
 	/* It has to be here .. it doesn't work if you put it down the bottom - assembler explodes 8) */
 #ifdef __SMP__	
-	set_intr_gate(0x20+i, interrupt[i]);	/* IRQ '16' - IPI for rescheduling */
+	set_intr_gate(0x20+i, reschedule_interrupt);	/* IRQ '16' - IPI for rescheduling */
+	set_intr_gate(0x21+i, invalidate_interrupt);	/* IRQ '17' - IPI for invalidation */
+	set_intr_gate(0x22+i, stop_cpu_interrupt);	/* IRQ '18' - IPI for CPU halt */
 #endif	
 	request_region(0x20,0x20,"pic1");
 	request_region(0xa0,0x20,"pic2");
diff -u --recursive --new-file v2.1.29/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.1.29/linux/arch/i386/kernel/process.c	Sun Jan 26 03:40:45 1997
+++ linux/arch/i386/kernel/process.c	Thu Mar 20 17:11:51 1997
@@ -29,6 +29,10 @@
 #include <linux/unistd.h>
 #include <linux/delay.h>
 #include <linux/smp.h>
+#include <linux/reboot.h>
+#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
+#include <linux/apm_bios.h>
+#endif
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -122,6 +126,7 @@
 			if (hlt_works_ok && !hlt_counter && !need_resched)
 		        	__asm__("hlt");
 		}
+		run_task_queue(&tq_scheduler);
 		if (need_resched) 
 			start_idle = 0;
 		schedule();
@@ -135,30 +140,6 @@
 #else
 
 /*
- *	In the SMP world we hlt outside of kernel syscall rather than within
- *	so as to get the right locking semantics.
- */
- 
-asmlinkage int sys_idle(void)
-{
-	int ret = -EPERM;
-
-	lock_kernel();
-	if(current->pid != 0)
-		goto out;
-#ifdef __SMP_PROF__
-	smp_spins_sys_idle[smp_processor_id()]+=
-	  smp_spins_syscall_cur[smp_processor_id()];
-#endif
-	current->counter= -100;
-	schedule();
-	ret = 0;
-out:
-	unlock_kernel();
-	return ret;
-}
-
-/*
  *	This is being executed in task 0 'user space'.
  */
 
@@ -168,32 +149,17 @@
 	{
 		if(cpu_data[smp_processor_id()].hlt_works_ok && !hlt_counter && !need_resched)
 			__asm("hlt");
-                if(0==(read_smp_counter(&smp_process_available))) 
-                	continue;
-                while(0x80000000 & smp_process_available)
-			;
-	        cli();
-                while(set_bit(31,&smp_process_available))
-                	while(test_bit(31,&smp_process_available))
-                {
-                	/*
-                	 *	Oops.. This is kind of important in some cases...
-                	 */
-                	if(clear_bit(smp_processor_id(), &smp_invalidate_needed))
-                		local_flush_tlb();
-                }
-                if (0==(read_smp_counter(&smp_process_available))) {
-                        clear_bit(31,&smp_process_available);
-                        sti();
-                        continue;
-                }
-                smp_process_available--;
-                clear_bit(31,&smp_process_available);
-                sti();
-		idle();
+		run_task_queue(&tq_scheduler);
+		schedule();
 	}
 }
 
+asmlinkage int sys_idle(void)
+{
+	cpu_idle(NULL);
+	return 0;
+}
+
 #endif
 
 /*
@@ -297,11 +263,13 @@
 			break;
 }
 
-void hard_reset_now (void)
+void machine_restart(char * __unused)
 {
 
 	if(!reboot_thru_bios) {
+#if 0
 		sti();
+#endif
 		/* rebooting needs to touch the page at absolute addr 0 */
 		*((unsigned short *)__va(0x472)) = reboot_mode;
 		for (;;) {
@@ -398,6 +366,18 @@
 				:
 				: "i" ((void *) (0x1000 - sizeof (real_mode_switch))));
 }
+
+void machine_halt(void)
+{
+}
+
+void machine_power_off(void)
+{
+#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
+	apm_set_power_state(APM_STATE_OFF);
+#endif
+}
+
 
 void show_regs(struct pt_regs * regs)
 {
diff -u --recursive --new-file v2.1.29/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.1.29/linux/arch/i386/kernel/smp.c	Sun Feb  2 05:18:30 1997
+++ linux/arch/i386/kernel/smp.c	Tue Mar 25 11:25:03 1997
@@ -44,6 +44,8 @@
 #include <asm/smp.h>
 #include <asm/io.h>
 
+extern unsigned long start_kernel, _etext;
+
 /*
  *	Some notes on processor bugs:
  *
@@ -122,9 +124,6 @@
 unsigned char *kernel_stacks[NR_CPUS];			/* Kernel stack pointers for CPU's (debugging)		*/
 
 static volatile unsigned char smp_cpu_in_msg[NR_CPUS];	/* True if this processor is sending an IPI		*/
-static volatile unsigned long smp_msg_data;		/* IPI data pointer					*/
-static volatile int smp_src_cpu;			/* IPI sender processor					*/
-static volatile int smp_msg_id;				/* Message being sent					*/
 
 volatile unsigned long kernel_flag=0;			/* Kernel spinlock 					*/
 volatile unsigned char active_kernel_processor = NO_PROC_ID;	/* Processor holding kernel spinlock		*/
@@ -491,6 +490,7 @@
 				 */
 				nlong = boot_cpu_id<<24;	/* Dummy 'self' for bootup */
 				cpu_logical_map[0] = boot_cpu_id;
+				global_irq_holder = boot_cpu_id;
 
 				printk("Processors: %d\n", num_processors);
 				/*
@@ -1051,7 +1051,6 @@
 	SMP_PRINTK(("Boot done.\n"));
 }
 
-
 /*
  *	A non wait message cannot pass data or cpu source info. This current setup
  *	is only safe because the kernel lock owner is the only person who can send a message.
@@ -1070,9 +1069,8 @@
 	unsigned long cfg;
 	unsigned long target_map;
 	int p=smp_processor_id();
-	int irq=0x2d;								/* IRQ 13 */
+	int irq;
 	int ct=0;
-	static volatile int message_cpu = NO_PROC_ID;
 
 	/*
 	 *	During boot up send no messages
@@ -1087,11 +1085,24 @@
 	 *	message at this time. The reschedule cannot wait
 	 *	but is not critical.
 	 */
-	
-	if(msg==MSG_RESCHEDULE)							/* Reschedules we do via trap 0x30 */
-	{
-		irq=0x30;
-		if(smp_cpu_in_msg[p])
+
+	switch (msg) {
+		case MSG_RESCHEDULE:	
+			irq = 0x30;
+			if (smp_cpu_in_msg[p])
+				return;
+			break;
+
+		case MSG_INVALIDATE_TLB:
+			irq = 0x31;
+			break;
+
+		case MSG_STOP_CPU:
+			irq = 0x32;
+			break;
+
+		default:
+			printk("Unknown SMP message %d\n", msg);
 			return;
 	}
 
@@ -1103,31 +1114,12 @@
 	 *	I got to notice this bug...
 	 */
 	 
-	if(message_cpu!=NO_PROC_ID && msg!=MSG_STOP_CPU && msg!=MSG_RESCHEDULE)
-	{
-		panic("CPU #%d: Message pass %d but pass in progress by %d of %d\n",
-			smp_processor_id(),msg,message_cpu, smp_msg_id);
-	}
-
-	message_cpu=smp_processor_id();
-	
 	/*
 	 *	We are busy
 	 */
 	 	
 	smp_cpu_in_msg[p]++;
 	
-	/*
-	 *	Reschedule is currently special
-	 */
-	 
-	if(msg!=MSG_RESCHEDULE)
-	{
-		smp_src_cpu=p;
-		smp_msg_id=msg;
-		smp_msg_data=data;
-	}
-	
 /*	printk("SMP message pass #%d to %d of %d\n",
 		p, msg, target);*/
 	
@@ -1150,7 +1142,7 @@
 	 */
 	 
 	if(ct==1000)
-		printk("CPU #%d: previous IPI still not cleared after 10mS", smp_processor_id());
+		printk("CPU #%d: previous IPI still not cleared after 10mS", p);
 		
 	/*
 	 *	Program the APIC to deliver the IPI
@@ -1171,7 +1163,7 @@
 	{
 		cfg|=APIC_DEST_ALLBUT;
 		target_map=cpu_present_map;
-		cpu_callin_map[0]=(1<<smp_src_cpu);
+		cpu_callin_map[0]=(1<<p);
 	}
 	else if(target==MSG_ALL)
 	{
@@ -1197,11 +1189,30 @@
 	 
 	switch(wait)
 	{
+		int stuck;
 		case 1:
-			while(cpu_callin_map[0]!=target_map);		/* Spin on the pass		*/
+			stuck = 50000000;
+			while(cpu_callin_map[0]!=target_map) {
+				--stuck;
+				if (!stuck) {
+					printk("stuck on target_map IPI wait\n");
+					break;
+				}
+			}
 			break;
 		case 2:
-			while(smp_invalidate_needed);			/* Wait for invalidate map to clear */
+			stuck = 50000000;
+			/* Wait for invalidate map to clear */
+			while (smp_invalidate_needed) {
+				/* Take care of "crossing" invalidates */
+				if (test_bit(p, &smp_invalidate_needed))
+					clear_bit(p, &smp_invalidate_needed);
+				--stuck;
+				if (!stuck) {
+					printk("stuck on smp_invalidate_needed IPI wait\n");
+					break;
+				}
+			}
 			break;
 	}
 	
@@ -1210,7 +1221,6 @@
 	 */
 	 
 	smp_cpu_in_msg[p]--;
-	message_cpu=NO_PROC_ID;
 }
 
 /*
@@ -1233,15 +1243,15 @@
 	 *	bus locked or.
 	 */
 	
-	smp_invalidate_needed=cpu_present_map&~(1<<smp_processor_id());
+	smp_invalidate_needed=cpu_present_map;
 	
 	/*
 	 *	Processors spinning on the lock will see this IRQ late. The smp_invalidate_needed map will
 	 *	ensure they don't do a spurious flush tlb or miss one.
 	 */
 	 
-	save_flags(flags);
-	cli();
+	__save_flags(flags);
+	__cli();
 	smp_message_pass(MSG_ALL_BUT_SELF, MSG_INVALIDATE_TLB, 0L, 2);
 	
 	/*
@@ -1250,7 +1260,7 @@
 	 
 	local_flush_tlb();
 	
-	restore_flags(flags);
+	__restore_flags(flags);
 	
 	/*
 	 *	Completed.
@@ -1262,69 +1272,34 @@
 /*	
  *	Reschedule call back
  */
-
-void smp_reschedule_irq(int cpl, struct pt_regs *regs)
+asmlinkage void smp_reschedule_interrupt(void)
 {
-	lock_kernel();
-	intr_count++;
-	if(smp_processor_id()!=active_kernel_processor)
-		panic("SMP Reschedule on CPU #%d, but #%d is active.\n",
-			smp_processor_id(), active_kernel_processor);
-
 	need_resched=1;
 
 	/* Clear the IPI */
 	apic_read(APIC_SPIV);		/* Dummy read */
 	apic_write(APIC_EOI, 0);	/* Docs say use 0 for future compatibility */
-
-	intr_count--;
-	unlock_kernel();
-}	
+}
 
 /*
- *	Message call back.
+ * Invalidate call-back
  */
- 
-void smp_message_irq(int cpl, void *dev_id, struct pt_regs *regs)
+asmlinkage void smp_invalidate_interrupt(void)
 {
-	int i=smp_processor_id();
-/*	static int n=0;
-	if(n++<NR_CPUS)
-		printk("IPI %d->%d(%d,%ld)\n",smp_src_cpu,i,smp_msg_id,smp_msg_data);*/
-	switch(smp_msg_id)
-	{
-		case 0:	/* IRQ 13 testing - boring */
-			return;
-			
-		/*
-		 *	A TLB flush is needed.
-		 */
-		 
-		case MSG_INVALIDATE_TLB:
-			if(clear_bit(i,(unsigned long *)&smp_invalidate_needed))
-				local_flush_tlb();
-			set_bit(i, (unsigned long *)&cpu_callin_map[0]);
-		/*	cpu_callin_map[0]|=1<<smp_processor_id();*/
-			break;
-			
-		/*
-		 *	Halt other CPU's for a panic or reboot
-		 */
-		case MSG_STOP_CPU:
-			while(1)
-			{
-				if(cpu_data[smp_processor_id()].hlt_works_ok)
-					__asm__("hlt");
-			}
-		default:
-			printk("CPU #%d sent invalid cross CPU message to CPU #%d: %X(%lX).\n",
-				smp_src_cpu,smp_processor_id(),smp_msg_id,smp_msg_data);
-			break;
-	}
-	/*
-	 *	Clear the IPI, so we can receive future IPI's
-	 */
-	 
+	if (clear_bit(smp_processor_id(), &smp_invalidate_needed))
+		local_flush_tlb();
+
+	/* Clear the IPI */
 	apic_read(APIC_SPIV);		/* Dummy read */
 	apic_write(APIC_EOI, 0);	/* Docs say use 0 for future compatibility */
+}	
+
+/*
+ *	CPU halt call-back
+ */
+asmlinkage void smp_stop_cpu_interrupt(void)
+{
+	if (cpu_data[smp_processor_id()].hlt_works_ok)
+		for(;;) __asm__("hlt");
+	for  (;;) ;
 }
diff -u --recursive --new-file v2.1.29/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- v2.1.29/linux/arch/i386/kernel/time.c	Thu Feb 27 10:57:29 1997
+++ linux/arch/i386/kernel/time.c	Sun Mar 16 17:13:52 1997
@@ -388,7 +388,7 @@
 	   basically because we don't yet share IRQ's around. This message is
 	   rigged to be safe on the 386 - basically it's a hack, so don't look
 	   closely for now.. */
-	/*smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); */
+	smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0);
 	    
 #ifdef CONFIG_MCA
 	if( MCA_bus ) {
diff -u --recursive --new-file v2.1.29/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.1.29/linux/arch/i386/kernel/traps.c	Fri Feb  7 05:54:54 1997
+++ linux/arch/i386/kernel/traps.c	Wed Mar 26 11:56:41 1997
@@ -231,7 +231,6 @@
 
 asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
 {
-	lock_kernel();
 #ifdef CONFIG_SMP_NMI_INVAL
 	smp_flush_tlb_rcv();
 #else
@@ -241,7 +240,6 @@
 	printk("power saving mode enabled.\n");
 #endif	
 #endif
-	unlock_kernel();
 }
 
 asmlinkage void do_debug(struct pt_regs * regs, long error_code)
@@ -305,18 +303,14 @@
 
 asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code)
 {
-	lock_kernel();
 	ignore_irq13 = 1;
 	math_error();
-	unlock_kernel();
 }
 
 asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs,
 					  long error_code)
 {
-	lock_kernel();
 	printk("Ignoring P6 Local APIC Spurious Interrupt Bug...\n");
-	unlock_kernel();
 }
 
 /*
@@ -328,7 +322,6 @@
  */
 asmlinkage void math_state_restore(void)
 {
-	lock_kernel();
 	__asm__ __volatile__("clts");		/* Allow maths ops (or we recurse) */
 
 /*
@@ -341,7 +334,7 @@
  */
 #ifndef __SMP__
 	if (last_task_used_math == current)
-		goto out;
+		return;
 	if (last_task_used_math)
 		__asm__("fnsave %0":"=m" (last_task_used_math->tss.i387));
 	else
@@ -360,10 +353,6 @@
 		current->used_math = 1;
 	}
 	current->flags|=PF_USEDFPU;		/* So we fnsave on switch_to() */
-#ifndef __SMP__
-out:
-#endif
-	unlock_kernel();
 }
 
 #ifndef CONFIG_MATH_EMULATION
diff -u --recursive --new-file v2.1.29/linux/arch/m68k/kernel/m68k_ksyms.c linux/arch/m68k/kernel/m68k_ksyms.c
--- v2.1.29/linux/arch/m68k/kernel/m68k_ksyms.c	Sun Jan 26 02:07:05 1997
+++ linux/arch/m68k/kernel/m68k_ksyms.c	Thu Mar 20 17:11:51 1997
@@ -48,4 +48,5 @@
 EXPORT_SYMBOL_NOVERS(memset);
 
 EXPORT_SYMBOL_NOVERS(__down_failed);
+EXPORT_SYMBOL_NOVERS(__down_failed_interruptible);
 EXPORT_SYMBOL_NOVERS(__up_wakeup);
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
--- v2.1.29/linux/arch/sparc/defconfig	Mon Mar 17 14:54:20 1997
+++ linux/arch/sparc/defconfig	Thu Mar 20 16:41:59 1997
@@ -78,6 +78,7 @@
 CONFIG_NETLINK=y
 CONFIG_RTNETLINK=y
 CONFIG_FIREWALL=y
+# CONFIG_NET_SECURITY is not set
 CONFIG_NET_ALIAS=y
 CONFIG_INET=y
 CONFIG_IP_MULTICAST=y
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/kernel/setup.c linux/arch/sparc/kernel/setup.c
--- v2.1.29/linux/arch/sparc/kernel/setup.c	Mon Mar 17 14:54:21 1997
+++ linux/arch/sparc/kernel/setup.c	Thu Mar 20 16:41:59 1997
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.81 1997/01/29 10:32:55 davem Exp $
+/*  $Id: setup.c,v 1.82 1997/03/08 08:27:04 ecd Exp $
  *  linux/arch/sparc/kernel/setup.c
  *
  *  Copyright (C) 1995  David S. Miller (davem@caip.rutgers.edu)
@@ -115,6 +115,7 @@
 extern char *console_fb_path;
 static int console_fb = 0;
 #endif
+static unsigned long memory_size = 0;
 
 void kernel_enter_debugger(void)
 {
@@ -215,8 +216,23 @@
 					console_fb = 1;
 					console_fb_path = commands;
 				}
-			}
+			} else
 #endif
+			if (!strncmp(commands, "mem=", 4)) {
+				/*
+				 * "mem=XXX[kKmM] overrides the PROM-reported
+				 * memory size.
+				 */
+				memory_size = simple_strtoul(commands + 4,
+							     &commands, 0);
+				if (*commands == 'K' || *commands == 'k') {
+					memory_size <<= 10;
+					commands++;
+				} else if (*commands=='M' || *commands=='m') {
+					memory_size <<= 20;
+					commands++;
+				}
+			}
 			while (*commands && *commands != ' ')
 				commands++;
 		}
@@ -337,15 +353,35 @@
 	*memory_start_p = (((unsigned long) &end));
 
 	if(!packed) {
-		for(i=0; sp_banks[i].num_bytes != 0; i++)
+		for(i=0; sp_banks[i].num_bytes != 0; i++) {
 			end_of_phys_memory = sp_banks[i].base_addr +
-				sp_banks[i].num_bytes;
+					     sp_banks[i].num_bytes;
+			if (memory_size) {
+				if (end_of_phys_memory > memory_size) {
+					sp_banks[i].num_bytes -=
+					    (end_of_phys_memory - memory_size);
+					end_of_phys_memory = memory_size;
+					sp_banks[++i].base_addr = 0xdeadbeef;
+					sp_banks[i].num_bytes = 0;
+				}
+			}
+		}
 	} else {
 		unsigned int sum = 0;
 
-		for(i = 0; sp_banks[i].num_bytes != 0; i++)
+		for(i = 0; sp_banks[i].num_bytes != 0; i++) {
 			sum += sp_banks[i].num_bytes;
-
+			if (memory_size) {
+				if (sum > memory_size) {
+					sp_banks[i].num_bytes -=
+							(sum - memory_size);
+					sum = memory_size;
+					sp_banks[++i].base_addr = 0xdeadbeef;
+					sp_banks[i].num_bytes = 0;
+					break;
+				}
+			}
+		}
 		end_of_phys_memory = sum;
 	}
 
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/kernel/sparc_ksyms.c linux/arch/sparc/kernel/sparc_ksyms.c
--- v2.1.29/linux/arch/sparc/kernel/sparc_ksyms.c	Mon Mar 17 14:54:21 1997
+++ linux/arch/sparc/kernel/sparc_ksyms.c	Thu Mar 20 16:41:59 1997
@@ -1,4 +1,4 @@
-/* $Id: sparc_ksyms.c,v 1.47 1997/03/03 16:51:41 jj Exp $
+/* $Id: sparc_ksyms.c,v 1.49 1997/03/15 07:47:45 davem Exp $
  * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/string.h>
+#include <linux/in6.h>
 
 #include <asm/oplib.h>
 #include <asm/delay.h>
@@ -27,6 +28,7 @@
 #include <asm/ptrace.h>
 #include <asm/user.h>
 #include <asm/uaccess.h>
+#include <asm/checksum.h>
 #ifdef CONFIG_SBUS
 #include <asm/sbus.h>
 #include <asm/dma.h>
@@ -52,7 +54,6 @@
 extern void *__memscan_generic(void *, int, size_t);
 extern int __memcmp(const void *, const void *, __kernel_size_t);
 extern int __strncmp(const char *, const char *, __kernel_size_t);
-extern unsigned int __csum_partial_copy_sparc_generic (const char *, char *);
 
 extern void bcopy (const char *, char *, int);
 extern int __ashrdi3(int, int);
@@ -183,11 +184,13 @@
 EXPORT_SYMBOL(__strncmp);
 EXPORT_SYMBOL(__memmove);
 
-EXPORT_SYMBOL(__csum_partial_copy_sparc_generic);
-
 /* Moving data to/from userspace. */
 EXPORT_SYMBOL(__copy_user);
 EXPORT_SYMBOL(__strncpy_from_user);
+
+/* Networking helper routines. */
+/* XXX This is NOVERS because C_LABEL_STR doesn't get the version number. -DaveM */
+EXPORT_SYMBOL_NOVERS(__csum_partial_copy_sparc_generic);
 
 /* No version information on this, heavily used in inline asm,
  * and will always be 'void __ret_efault(void)'.
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/kernel/unaligned.c linux/arch/sparc/kernel/unaligned.c
--- v2.1.29/linux/arch/sparc/kernel/unaligned.c	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc/kernel/unaligned.c	Thu Mar 20 16:41:59 1997
@@ -1,4 +1,4 @@
-/* $Id: unaligned.c,v 1.15 1997/01/16 14:14:42 davem Exp $
+/* $Id: unaligned.c,v 1.16 1997/03/18 17:53:44 jj Exp $
  * unaligned.c: Unaligned load/store trap handling with special
  *              cases for the kernel to do them more quickly.
  *
@@ -18,8 +18,6 @@
 #include <linux/smp_lock.h>
 
 /* #define DEBUG_MNA */
-
-extern void die_if_kernel(char *, struct pt_regs *);
 
 enum direction {
 	load,    /* ld, ldd, ldh, ldsh */
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/lib/Makefile linux/arch/sparc/lib/Makefile
--- v2.1.29/linux/arch/sparc/lib/Makefile	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc/lib/Makefile	Thu Mar 20 16:42:00 1997
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.21 1997/01/25 06:48:43 davem Exp $
+# $Id: Makefile,v 1.22 1997/03/14 21:04:17 jj Exp $
 # Makefile for Sparc library files..
 #
 
@@ -7,7 +7,7 @@
 OBJS  = mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o memcpy.o memset.o \
         strlen.o checksum.o blockops.o memscan.o memcmp.o strncmp.o \
 	strncpy_from_user.o divdi3.o udivdi3.o strlen_user.o \
-	copy_user.o clear_user.o locks.o atomic.o bitops.o
+	copy_user.o locks.o atomic.o bitops.o
 
 lib.a: $(OBJS)
 	$(AR) rcs lib.a $(OBJS)
@@ -36,9 +36,6 @@
 
 copy_user.o: copy_user.S
 	$(CC) -D__ASSEMBLY__ -ansi -c -o copy_user.o copy_user.S
-
-clear_user.o: clear_user.S
-	$(CC) -D__ASSEMBLY__ -ansi -c -o clear_user.o clear_user.S
 
 blockops.o: blockops.S
 	$(CC) -ansi -c -o blockops.o blockops.S
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/lib/clear_user.S linux/arch/sparc/lib/clear_user.S
--- v2.1.29/linux/arch/sparc/lib/clear_user.S	Fri Dec 13 01:37:31 1996
+++ linux/arch/sparc/lib/clear_user.S	Wed Dec 31 16:00:00 1969
@@ -1,185 +0,0 @@
-/* linux/arch/sparc/lib/clear_user.S: Sparc optimized clear_user code
- *
- * Zero %o1 bytes at user %o0, handling exceptions as we go.
- * Returns 0 if successfull, # of bytes still not cleared otherwise
- *
- * Copyright (C) 1991,1996 Free Software Foundation
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
- */
-
-#include <asm/cprefix.h>
-#include <asm/ptrace.h>
-
-#define EX(x,y,a,b,z) 				\
-98: 	x,y;					\
-	.section .fixup,z##alloc,z##execinstr;	\
-	.align	4;				\
-99:	retl;					\
-	 a, b, %o0;				\
-	.section __ex_table,z##alloc;		\
-	.align	4;				\
-	.word	98b, 99b;			\
-	.text;					\
-	.align	4
-
-#define EXT(start,end,handler,z) 		\
-	.section __ex_table,z##alloc;		\
-	.align	4;				\
-	.word	start, 0, end, handler;		\
-	.text;					\
-	.align	4
-
-/* Please don't change these macros, unless you change the login
- * in the .fixup section below as well */
-/* Store 64 bytes at (BASE + OFFSET) using value SOURCE. */
-#define ZERO_BIG_BLOCK(base, offset, source)    \
-	std	source, [base + offset + 0x00]; \
-	std	source, [base + offset + 0x08]; \
-	std	source, [base + offset + 0x10]; \
-	std	source, [base + offset + 0x18]; \
-	std	source, [base + offset + 0x20]; \
-	std	source, [base + offset + 0x28]; \
-	std	source, [base + offset + 0x30]; \
-	std	source, [base + offset + 0x38];
-
-#define ZERO_LAST_BLOCKS(base, offset, source)	\
-	std	source, [base - offset - 0x38]; \
-	std	source, [base - offset - 0x30]; \
-	std	source, [base - offset - 0x28]; \
-	std	source, [base - offset - 0x20]; \
-	std	source, [base - offset - 0x18]; \
-	std	source, [base - offset - 0x10]; \
-	std	source, [base - offset - 0x08]; \
-	std	source, [base - offset - 0x00];
-
-	.text
-	.align 4
-
-	.globl	C_LABEL(__clear_user)
-3:
-	cmp	%o2, 3
-	be	2f
-	 EX(stb	%g0, [%o0], sub %o1, 0,#)
-
-	cmp	%o2, 2
-	be	2f
-	 EX(stb	%g0, [%o0 + 0x01], sub %o1, 1,#)
-
-	EX(stb	%g0, [%o0 + 0x02], sub %o1, 2,#)
-2:
-	sub	%o2, 4, %o2
-	add	%o1, %o2, %o1
-	b	4f
-	 sub	%o0, %o2, %o0
-
-C_LABEL(__clear_user):
-	cmp	%o1, 7
-	bleu	7f
-	 andcc	%o0, 3, %o2
-	bne	3b
-4:
-	 andcc	%o0, 4, %g0
-
-	be	2f
-	 mov	%g0, %g1
-
-	EX(st	%g0, [%o0], sub %o1, 0,#)
-	sub	%o1, 4, %o1
-	add	%o0, 4, %o0
-2:
-	andcc	%o1, 0xffffff80, %o3	! Now everything is 8 aligned and o1 is len to run
-	be	9f
-	 andcc	%o1, 0x78, %o2
-10:
-	ZERO_BIG_BLOCK(%o0, 0x00, %g0)
-	subcc	%o3, 128, %o3
-	ZERO_BIG_BLOCK(%o0, 0x40, %g0)
-11:
-	EXT(10b, 11b, 20f,#)
-	bne	10b
-	 add	%o0, 128, %o0
-
-	orcc	%o2, %g0, %g0
-9:
-	be	13f
-	 andcc	%o1, 7, %o1
-
-	srl	%o2, 1, %o3
-	set	13f, %o4
-	sub	%o4, %o3, %o4
-	jmp	%o4
-	 add	%o0, %o2, %o0
-
-12:
-	ZERO_LAST_BLOCKS(%o0, 0x48, %g0)
-	ZERO_LAST_BLOCKS(%o0, 0x08, %g0)
-13:
-	EXT(12b, 13b, 21f,#)
-	be	8f
-	 andcc	%o1, 4, %g0
-
-	be	1f
-	 andcc	%o1, 2, %g0
-
-	EX(st	%g0, [%o0], and %o1, 7,#)
-	add	%o0, 4, %o0
-1:
-	be	1f
-	 andcc	%o1, 1, %g0
-
-	EX(sth	%g0, [%o0], and %o1, 3,#)
-	add	%o0, 2, %o0
-1:
-	bne,a	8f
-	 EX(stb	%g0, [%o0], add %g0, 1,#)
-8:
-	retl
-	 clr	%o0
-7:
-	be	13b
-	 orcc	%o1, 0, %g0
-
-	andcc	%o1, 4, %g0
-	be	1f
-	 andcc	%o1, 2, %g0
-
-	EX(stb	%g0, [%o0 + 0], sub %o1, 0,#)
-	EX(stb	%g0, [%o0 + 1], sub %o1, 1,#)
-	EX(stb	%g0, [%o0 + 2], sub %o1, 2,#)
-	EX(stb	%g0, [%o0 + 3], sub %o1, 3,#)
-	add	%o0, 4, %o0
-1:
-	be	1f
-	 andcc	%o1, 1, %o2
-
-	EX(stb	%g0, [%o0 + 0], add %o2, 2,#)
-	EX(stb	%g0, [%o0 + 1], add %o2, 1,#)
-	add	%o0, 2, %o0
-1:
-	bne,a	8b
-	 EX(stb	%g0, [%o0], add %g0, 1,#)
-
-	retl
-	 clr	%o0
-
-	.section .fixup,#alloc,#execinstr
-	.align	4
-20:
-	cmp	%g2, 8
-	bleu	1f
-	 and	%o1, 0x7f, %o1
-	sub	%g2, 9, %g2
-	add	%o3, 64, %o3
-1:
-	sll	%g2, 3, %g2
-	add	%o3, %o1, %o0
-	retl
-	 sub	%o0, %g2, %o0
-21:
-	mov	8, %o0
-	and	%o1, 7, %o1
-	sub	%o0, %g2, %o0
-	sll	%o0, 3, %o0
-	retl
-	 add	%o0, %o1, %o0
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/mm/Makefile linux/arch/sparc/mm/Makefile
--- v2.1.29/linux/arch/sparc/mm/Makefile	Fri Dec 13 01:37:31 1996
+++ linux/arch/sparc/mm/Makefile	Thu Mar 20 16:42:00 1997
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.22 1996/11/22 11:57:03 ecd Exp $
+# $Id: Makefile,v 1.23 1997/03/10 09:16:52 davem Exp $
 # Makefile for the linux Sparc-specific parts of the memory manager.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -8,7 +8,10 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := mm.o
-O_OBJS   := fault.o init.o sun4c.o srmmu.o loadmmu.o generic.o asyncd.o \
-	    extable.o
+O_OBJS   := fault.o init.o sun4c.o srmmu.o hypersparc.o loadmmu.o \
+	    generic.o asyncd.o extable.o
 
 include $(TOPDIR)/Rules.make
+
+hypersparc.o: hypersparc.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o hypersparc.o hypersparc.S
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/mm/fault.c linux/arch/sparc/mm/fault.c
--- v2.1.29/linux/arch/sparc/mm/fault.c	Mon Mar 17 14:54:22 1997
+++ linux/arch/sparc/mm/fault.c	Thu Mar 20 16:42:00 1997
@@ -1,4 +1,4 @@
-/* $Id: fault.c,v 1.89 1997/03/04 16:26:46 jj Exp $
+/* $Id: fault.c,v 1.91 1997/03/18 17:56:00 jj Exp $
  * fault.c:  Page fault handlers for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -36,8 +36,6 @@
 extern struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
 extern int prom_node_root;
 
-extern void die_if_kernel(char *,struct pt_regs *);
-
 struct linux_romvec *romvec;
 
 /* At boot time we determine these two values necessary for setting
@@ -134,7 +132,10 @@
 	prom_halt();
 }
 
-void unhandled_fault(unsigned long address, struct task_struct *tsk,
+static void unhandled_fault(unsigned long, struct task_struct *,
+		struct pt_regs *) __attribute__ ((noreturn));
+
+static void unhandled_fault(unsigned long address, struct task_struct *tsk,
                      struct pt_regs *regs)
 {
 	if((unsigned long) address < PAGE_SIZE) {
@@ -165,10 +166,10 @@
 	case 3: return 3;
 	/* store will be handled by fixup, load will bump out */
 	/* for _to_ macros */
-	case 1: insn = (unsigned *)pc; if ((insn >> 21) & 1) return 1; break;
+	case 1: insn = (unsigned)pc; if ((insn >> 21) & 1) return 1; break;
 	/* load will be handled by fixup, store will bump out */
 	/* for _from_ macros */
-	case 2: insn = (unsigned *)pc; 
+	case 2: insn = (unsigned)pc; 
 		if (!((insn >> 21) & 1) || ((insn>>19)&0x3f) == 15) return 2; 
 		break; 
 	default: break;
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/mm/hypersparc.S linux/arch/sparc/mm/hypersparc.S
--- v2.1.29/linux/arch/sparc/mm/hypersparc.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/mm/hypersparc.S	Thu Mar 20 16:42:00 1997
@@ -0,0 +1,335 @@
+/* $Id: hypersparc.S,v 1.1 1997/03/10 09:16:52 davem Exp $
+ * hypersparc.S: High speed Hypersparc mmu/cache operations.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+#include <asm/asi.h>
+#include <asm/page.h>
+#include <asm/pgtsrmmu.h>
+
+#define WINDOW_FLUSH(tmp1, tmp2)					\
+	mov	0, tmp1;						\
+98:	ld	[%g6 + AOFF_task_tss + AOFF_thread_uwinmask], tmp2;	\
+	orcc	%g0, tmp2, %g0;						\
+	add	tmp1, 1, tmp1;						\
+	bne	98b;							\
+	 save	%sp, -64, %sp;						\
+99:	subcc	tmp1, 1, tmp1;						\
+	bne	99b;							\
+	 restore %g0, %g0, %g0;
+
+	.text
+	.align	4
+
+	.globl	hypersparc_flush_cache_all, hypersparc_flush_cache_mm
+	.globl	hypersparc_flush_cache_range, hypersparc_flush_cache_page
+	.globl	hypersparc_flush_page_to_ram, hypersparc_flush_chunk
+	.globl	hypersparc_flush_page_for_dma, hypersparc_flush_sig_insns
+	.globl	hypersparc_flush_tlb_all, hypersparc_flush_tlb_mm
+	.globl	hypersparc_flush_tlb_range, hypersparc_flush_tlb_page
+
+	/* Verified... */
+hypersparc_flush_cache_all:
+	WINDOW_FLUSH(%g4, %g5)
+	sethi	%hi(vac_cache_size), %g4
+	ld	[%g4 + %lo(vac_cache_size)], %g5
+	sethi	%hi(vac_line_size), %g1
+	ld	[%g1 + %lo(vac_line_size)], %g2
+1:	
+	subcc	%g5, %g2, %g5			! hyper_flush_unconditional_combined
+	bne	1b
+	 sta	%g0, [%g5] ASI_M_FLUSH_CTX
+	retl
+	 sta	%g0, [%g0] ASI_M_FLUSH_IWHOLE	! hyper_flush_whole_icache
+
+	/* We expand the window flush to get maximum performance. */
+	/* Verified... */
+hypersparc_flush_cache_mm:
+#ifndef __SMP__
+	ld	[%o0 + AOFF_mm_context], %g1
+	cmp	%g1, -1
+	be	hypersparc_flush_cache_mm_out
+#endif
+	WINDOW_FLUSH(%g4, %g5)
+
+	sethi	%hi(vac_line_size), %g1
+	ld	[%g1 + %lo(vac_line_size)], %o1
+	sethi	%hi(vac_cache_size), %g2
+	ld	[%g2 + %lo(vac_cache_size)], %o0
+	add	%o1, %o1, %g1
+	add	%o1, %g1, %g2
+	add	%o1, %g2, %g3
+	add	%o1, %g3, %g4
+	add	%o1, %g4, %g5
+	add	%o1, %g5, %o4
+	add	%o1, %o4, %o5
+
+	/* BLAMMO! */
+1:
+	subcc	%o0, %o5, %o0				! hyper_flush_cache_user
+	sta	%g0, [%o0 + %g0] ASI_M_FLUSH_USER
+	sta	%g0, [%o0 + %o1] ASI_M_FLUSH_USER
+	sta	%g0, [%o0 + %g1] ASI_M_FLUSH_USER
+	sta	%g0, [%o0 + %g2] ASI_M_FLUSH_USER
+	sta	%g0, [%o0 + %g3] ASI_M_FLUSH_USER
+	sta	%g0, [%o0 + %g4] ASI_M_FLUSH_USER
+	sta	%g0, [%o0 + %g5] ASI_M_FLUSH_USER
+	bne	1b
+	 sta	%g0, [%o0 + %o4] ASI_M_FLUSH_USER
+hypersparc_flush_cache_mm_out:
+	retl
+	 sta	%g0, [%g0 + %g0] ASI_M_FLUSH_IWHOLE	! hyper_flush_whole_icache
+
+	/* The things we do for performance... */
+	/* Verified... */
+hypersparc_flush_cache_range:
+#ifndef __SMP__
+	ld	[%o0 + AOFF_mm_context], %g1
+	cmp	%g1, -1
+	be	hypersparc_flush_cache_range_out
+#endif
+	WINDOW_FLUSH(%g4, %g5)
+
+	sethi	%hi(vac_line_size), %g1
+	ld	[%g1 + %lo(vac_line_size)], %o4
+	sethi	%hi(vac_cache_size), %g2
+	ld	[%g2 + %lo(vac_cache_size)], %o3
+
+	/* Here comes the fun part... */
+	add	%o2, (PAGE_SIZE - 1), %o2
+	andn	%o1, (PAGE_SIZE - 1), %o1
+	add	%o4, %o4, %o5
+	andn	%o2, (PAGE_SIZE - 1), %o2
+	add	%o4, %o5, %g1
+	sub	%o2, %o1, %g4
+	add	%o4, %g1, %g2
+	sll	%o3, 2, %g5
+	add	%o4, %g2, %g3
+	cmp	%g4, %g5
+	add	%o4, %g3, %g4
+	blu	0f
+	 add	%o4, %g4, %g5
+	add	%o4, %g5, %g7
+
+	/* Flush entire user space, believe it or not this is quicker
+	 * than page at a time flushings for range > (cache_size<<2).
+	 */
+1:
+	subcc	%o3, %g7, %o3
+	sta	%g0, [%o3 + %g0] ASI_M_FLUSH_USER
+	sta	%g0, [%o3 + %o4] ASI_M_FLUSH_USER
+	sta	%g0, [%o3 + %o5] ASI_M_FLUSH_USER
+	sta	%g0, [%o3 + %g1] ASI_M_FLUSH_USER
+	sta	%g0, [%o3 + %g2] ASI_M_FLUSH_USER
+	sta	%g0, [%o3 + %g3] ASI_M_FLUSH_USER
+	sta	%g0, [%o3 + %g4] ASI_M_FLUSH_USER
+	bne	1b
+	 sta	%g0, [%o3 + %g5] ASI_M_FLUSH_USER
+	retl
+	 sta	%g0, [%g0 + %g0] ASI_M_FLUSH_IWHOLE
+
+	/* Below our threshold, flush one page at a time. */
+0:
+	ld	[%o0 + AOFF_mm_context], %o0
+	mov	SRMMU_CTX_REG, %g7
+	lda	[%g7] ASI_M_MMUREGS, %o3
+	sta	%o0, [%g7] ASI_M_MMUREGS
+	sethi	%hi(PAGE_SIZE), %g7		/* XXX ick, stupid stalls... */
+	sub	%o2, %g7, %o0
+1:
+	or	%o0, 0x400, %g7
+	lda	[%g7] ASI_M_FLUSH_PROBE, %g7
+	orcc	%g7, 0, %g0
+	be,a	3f
+	 mov	%o0, %o2
+	add	%o4, %g5, %g7
+2:
+	sub	%o2, %g7, %o2
+	sta	%g0, [%o2 + %g0] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o2 + %o4] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o2 + %o5] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o2 + %g1] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o2 + %g2] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o2 + %g3] ASI_M_FLUSH_PAGE
+	andcc	%o2, 0xffc, %g0
+	sta	%g0, [%o2 + %g4] ASI_M_FLUSH_PAGE
+	bne	2b
+	 sta	%g0, [%o2 + %g5] ASI_M_FLUSH_PAGE
+3:
+	sethi	%hi(PAGE_SIZE), %g7
+	cmp	%o2, %o1
+	bne	1b
+	 sub	%o2, %g7, %o0
+	mov	SRMMU_FAULT_STATUS, %g5
+	lda	[%g5] ASI_M_MMUREGS, %g0
+	mov	SRMMU_CTX_REG, %g7
+	sta	%o3, [%g7] ASI_M_MMUREGS
+hypersparc_flush_cache_range_out:
+	retl
+	 sta	%g0, [%g0 + %g0] ASI_M_FLUSH_IWHOLE
+
+	/* HyperSparc requires a valid mapping where we are about to flush
+	 * in order to check for a physical tag match during the flush.
+	 */
+	/* Verified... */
+hypersparc_flush_cache_page:
+	ld	[%o0 + 0x0], %o0		/* XXX vma->vm_mm, GROSS XXX */
+#ifndef __SMP__
+	ld	[%o0 + AOFF_mm_context], %g1
+	cmp	%g1, -1
+	be	hypersparc_flush_cache_page_out
+#endif
+	WINDOW_FLUSH(%g4, %g5)
+
+	sethi	%hi(vac_line_size), %g1
+	ld	[%g1 + %lo(vac_line_size)], %o4
+	mov	SRMMU_CTX_REG, %o3
+	andn	%o1, (PAGE_SIZE - 1), %o1
+	lda	[%o3] ASI_M_MMUREGS, %o2
+	sta	%g1, [%o3] ASI_M_MMUREGS
+	or	%o1, 0x400, %o5
+	lda	[%o5] ASI_M_FLUSH_PROBE, %g1
+	orcc	%g0, %g1, %g0
+	sethi	%hi(PAGE_SIZE), %g7
+	be	2f
+	 add	%o4, %o4, %o5
+	add	%o1, %g7, %o1
+	add	%o4, %o5, %g1
+	add	%o4, %g1, %g2
+	add	%o4, %g2, %g3
+	add	%o4, %g3, %g4
+	add	%o4, %g4, %g5
+	add	%o4, %g5, %g7
+
+	/* BLAMMO! */
+1:
+	sub	%o1, %g7, %o1
+	sta	%g0, [%o1 + %g0] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o4] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o5] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g1] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g2] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g3] ASI_M_FLUSH_PAGE
+	andcc	%o1, 0xffc, %g0
+	sta	%g0, [%o1 + %g4] ASI_M_FLUSH_PAGE
+	bne	1b
+	 sta	%g0, [%o1 + %g5] ASI_M_FLUSH_PAGE
+2:
+	mov	SRMMU_FAULT_STATUS, %g7
+	mov	SRMMU_CTX_REG, %g4
+	lda	[%g7] ASI_M_MMUREGS, %g0
+	sta	%o2, [%g4] ASI_M_MMUREGS
+hypersparc_flush_cache_page_out:
+hypersparc_flush_sig_insns:		/* This is "neat"... */
+	retl
+	 sta	%g0, [%g0 + %g0] ASI_M_FLUSH_IWHOLE
+
+	/* HyperSparc is copy-back. */
+	/* Verified... */
+hypersparc_flush_page_to_ram:
+hypersparc_flush_chunk:
+	sethi	%hi(vac_line_size), %g1
+	ld	[%g1 + %lo(vac_line_size)], %o4
+	andn	%o0, (PAGE_SIZE - 1), %o0
+	add	%o4, %o4, %o5
+	or	%o0, 0x400, %g7
+	lda	[%g7] ASI_M_FLUSH_PROBE, %g5
+	add	%o4, %o5, %g1
+	orcc	%g5, 0, %g0
+	be	2f
+	 add	%o4, %g1, %g2
+	sethi	%hi(PAGE_SIZE), %g5
+	add	%o4, %g2, %g3
+	add	%o0, %g5, %o0
+	add	%o4, %g3, %g4
+	add	%o4, %g4, %g5
+	add	%o4, %g5, %g7
+
+	/* BLAMMO! */
+1:
+	sub	%o0, %g7, %o0
+	sta	%g0, [%o0 + %g0] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o0 + %o4] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o0 + %o5] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o0 + %g1] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o0 + %g2] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o0 + %g3] ASI_M_FLUSH_PAGE
+	andcc	%o0, 0xffc, %g0
+	sta	%g0, [%o0 + %g4] ASI_M_FLUSH_PAGE
+	bne	1b
+	 sta	%g0, [%o0 + %g5] ASI_M_FLUSH_PAGE
+2:
+	mov	SRMMU_FAULT_STATUS, %g1
+	retl
+	 lda	[%g1] ASI_M_MMUREGS, %g0
+
+	/* HyperSparc is IO cache coherent. */
+hypersparc_flush_page_for_dma:
+	retl
+	 nop
+
+	/* Verified... */
+hypersparc_flush_tlb_all:
+	mov	0x400, %g1
+	retl
+	 sta	%g0, [%g1] ASI_M_FLUSH_PROBE
+
+	/* Verified... */
+hypersparc_flush_tlb_mm:
+	mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o1
+	lda	[%g1] ASI_M_MMUREGS, %g5
+#ifndef __SMP__
+	cmp	%o1, -1
+	be	hypersparc_flush_tlb_mm_out
+#endif
+	mov	0x300, %g2
+	sta	%o1, [%g1] ASI_M_MMUREGS
+	sta	%g0, [%g2] ASI_M_FLUSH_PROBE
+hypersparc_flush_tlb_mm_out:
+	retl
+	 sta	%g5, [%g1] ASI_M_MMUREGS
+
+	/* Verified... */
+hypersparc_flush_tlb_range:
+	mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o3
+	lda	[%g1] ASI_M_MMUREGS, %g5
+#ifndef __SMP__
+	cmp	%o3, -1
+	be	hypersparc_flush_tlb_range_out
+#endif
+	srl	%o1, SRMMU_PGDIR_SHIFT, %o1
+	sta	%o3, [%g1] ASI_M_MMUREGS
+	sll	%o1, SRMMU_PGDIR_SHIFT, %o1
+	sethi	%hi(1 << SRMMU_PGDIR_SHIFT), %o4
+	add	%o1, 0x200, %o1
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+1:
+	add	%o1, %o4, %o1
+	cmp	%o1, %o2
+	blu,a	1b
+	 sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+hypersparc_flush_tlb_range_out:
+	retl
+	 sta	%g5, [%g1] ASI_M_MMUREGS
+
+	/* Verified... */
+hypersparc_flush_tlb_page:
+	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
+	mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o3
+	andn	%o1, (PAGE_SIZE - 1), %o1
+	lda	[%g1] ASI_M_MMUREGS, %g5
+#ifndef __SMP__
+	cmp	%o3, -1
+	be	hypersparc_flush_tlb_page_out
+#endif
+	sta	%o3, [%g1] ASI_M_MMUREGS
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+hypersparc_flush_tlb_page_out:
+	retl
+	 sta	%g5, [%g1] ASI_M_MMUREGS
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
--- v2.1.29/linux/arch/sparc/mm/srmmu.c	Mon Mar 17 14:54:22 1997
+++ linux/arch/sparc/mm/srmmu.c	Thu Mar 20 16:42:00 1997
@@ -1,4 +1,4 @@
-/* $Id: srmmu.c,v 1.130 1997/02/10 23:33:49 davem Exp $
+/* $Id: srmmu.c,v 1.132 1997/03/18 17:56:47 jj Exp $
  * srmmu.c:  SRMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
@@ -53,20 +53,13 @@
 #define FLUSH_END	}
 #endif
 
-#define USE_CHUNK_ALLOC 1
-
-static unsigned long (*mmu_getpage)(void);
 static void (*ctxd_set)(ctxd_t *ctxp, pgd_t *pgdp);
 static void (*pmd_set)(pmd_t *pmdp, pte_t *ptep);
 
 static void (*flush_page_for_dma)(unsigned long page);
-static void (*flush_cache_page_to_uncache)(unsigned long page);
-static void (*flush_tlb_page_for_cbit)(unsigned long page);
 static void (*flush_chunk)(unsigned long chunk);
 #ifdef __SMP__
 static void (*local_flush_page_for_dma)(unsigned long page);
-static void (*local_flush_cache_page_to_uncache)(unsigned long page);
-static void (*local_flush_tlb_page_for_cbit)(unsigned long page);
 #endif
 
 static struct srmmu_stats {
@@ -282,18 +275,11 @@
 	}
 }
 
-static unsigned long srmmu_getpage(void)
-{
-	return get_free_page(GFP_KERNEL);
-}
-
 static inline void srmmu_putpage(unsigned long page)
 {
 	free_page(page);
 }
 
-#ifdef USE_CHUNK_ALLOC
-
 #define LC_HIGH_WATER	128
 #define BC_HIGH_WATER	32
 
@@ -536,18 +522,6 @@
 #define FREE_PMD(chunk) free_small_chunk((unsigned long *)(chunk))
 #define FREE_PTE(chunk) free_small_chunk((unsigned long *)(chunk))
 
-#else
-
-/* The easy versions. */
-#define NEW_PGD() (pgd_t *) mmu_getpage()
-#define NEW_PMD() (pmd_t *) mmu_getpage()
-#define NEW_PTE() (pte_t *) mmu_getpage()
-#define FREE_PGD(chunk) srmmu_putpage((unsigned long)(chunk))
-#define FREE_PMD(chunk) srmmu_putpage((unsigned long)(chunk))
-#define FREE_PTE(chunk) srmmu_putpage((unsigned long)(chunk))
-
-#endif
-
 /*
  * Allocate and free page tables. The xxx_kernel() versions are
  * used to allocate a kernel page table - this turns on ASN bits
@@ -753,7 +727,6 @@
 	       smp_processor_id(), address);
 	while (1) ;
 #else
-	extern void die_if_kernel(char *str, struct pt_regs *regs);
 	printk("Kernel faults at addr=0x%08lx\n", address);
 	printk("PTE=%08lx\n", srmmu_hwprobe((address & PAGE_MASK)));
 	die_if_kernel("SRMMU bolixed...", current->tss.kregs);
@@ -948,11 +921,6 @@
 	FLUSH_END
 }
 
-static void tsunami_flush_cache_page_to_uncache(unsigned long page)
-{
-	tsunami_flush_dcache();
-}
-
 /* Tsunami does not have a Copy-back style virtual cache. */
 static void tsunami_flush_page_to_ram(unsigned long page)
 {
@@ -1018,11 +986,6 @@
 	FLUSH_END
 }
 
-static void tsunami_flush_tlb_page_for_cbit(unsigned long page)
-{
-	srmmu_flush_tlb_page(page);
-}
-
 /* Swift flushes.  It has the recommended SRMMU specification flushing
  * facilities, so we can do things in a more fine grained fashion than we
  * could on the tsunami.  Let's watch out for HARDWARE BUGS...
@@ -1080,11 +1043,6 @@
 	swift_flush_icache();
 }
 
-static void swift_flush_cache_page_to_uncache(unsigned long page)
-{
-	swift_flush_dcache();
-}
-
 static void swift_flush_chunk(unsigned long chunk)
 {
 }
@@ -1119,11 +1077,6 @@
 	FLUSH_END
 }
 
-static void swift_flush_tlb_page_for_cbit(unsigned long page)
-{
-	srmmu_flush_whole_tlb();
-}
-
 /* The following are all MBUS based SRMMU modules, and therefore could
  * be found in a multiprocessor configuration.  On the whole, these
  * chips seems to be much more touchy about DVMA and page tables
@@ -1267,14 +1220,6 @@
 {
 }
 
-static unsigned long viking_no_mxcc_getpage(void)
-{
-	unsigned long page = get_free_page(GFP_KERNEL);
-
-	viking_no_mxcc_flush_page(page);
-	return page;
-}
-
 static void viking_flush_tlb_all(void)
 {
 	register int ctr asm("g5");
@@ -1387,11 +1332,6 @@
 	FLUSH_END
 }
 
-static void viking_flush_tlb_page_for_cbit(unsigned long page)
-{
-	srmmu_flush_tlb_page(page);
-}
-
 /* Cypress flushes. */
 static void cypress_flush_cache_all(void)
 {
@@ -1573,62 +1513,6 @@
 {
 }
 
-static void cypress_flush_page_to_uncache(unsigned long page)
-{
-	register unsigned long a, b, c, d, e, f, g;
-	unsigned long line;
-
-	a = 0x20; b = 0x40; c = 0x60; d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
-	page &= PAGE_MASK;
-	line = (page + PAGE_SIZE) - 0x100;
-	goto inside;
-	do {
-		line -= 0x100;
-	inside:
-		__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
-				     "sta %%g0, [%0 + %2] %1\n\t"
-				     "sta %%g0, [%0 + %3] %1\n\t"
-				     "sta %%g0, [%0 + %4] %1\n\t"
-				     "sta %%g0, [%0 + %5] %1\n\t"
-				     "sta %%g0, [%0 + %6] %1\n\t"
-				     "sta %%g0, [%0 + %7] %1\n\t"
-				     "sta %%g0, [%0 + %8] %1\n\t" : :
-				     "r" (line),
-				     "i" (ASI_M_FLUSH_PAGE),
-				     "r" (a), "r" (b), "r" (c), "r" (d),
-				     "r" (e), "r" (f), "r" (g));
-	} while(line != page);
-}
-
-static unsigned long cypress_getpage(void)
-{
-	register unsigned long a, b, c, d, e, f, g;
-	unsigned long page = get_free_page(GFP_KERNEL);
-	unsigned long line;
-
-	a = 0x20; b = 0x40; c = 0x60; d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
-	page &= PAGE_MASK;
-	line = (page + PAGE_SIZE) - 0x100;
-	goto inside;
-	do {
-		line -= 0x100;
-	inside:
-		__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
-				     "sta %%g0, [%0 + %2] %1\n\t"
-				     "sta %%g0, [%0 + %3] %1\n\t"
-				     "sta %%g0, [%0 + %4] %1\n\t"
-				     "sta %%g0, [%0 + %5] %1\n\t"
-				     "sta %%g0, [%0 + %6] %1\n\t"
-				     "sta %%g0, [%0 + %7] %1\n\t"
-				     "sta %%g0, [%0 + %8] %1\n\t" : :
-				     "r" (line),
-				     "i" (ASI_M_FLUSH_PAGE),
-				     "r" (a), "r" (b), "r" (c), "r" (d),
-				     "r" (e), "r" (f), "r" (g));
-	} while(line != page);
-	return page;
-}
-
 static void cypress_flush_tlb_all(void)
 {
 	srmmu_flush_whole_tlb();
@@ -1692,456 +1576,19 @@
 	FLUSH_END
 }
 
-static void cypress_flush_tlb_page_for_cbit(unsigned long page)
-{
-	srmmu_flush_tlb_page(page);
-}
-
-/* Hypersparc flushes.  Very nice chip... */
-static void hypersparc_flush_cache_all(void)
-{
-	register int ctr asm("g5");
-	unsigned long tmp1;
-
-	ctr = 0;
-	__asm__ __volatile__("
-	1:	ld	[%%g6 + %6], %%g4	! flush user windows
-		orcc	%%g0, %%g4, %%g0
-		add	%1, 1, %1
-		bne	1b
-		 save	%%sp, -64, %%sp
-	2:	subcc	%1, 1, %1
-		bne	2b
-		 restore %%g0, %%g0, %%g0
-	1:	subcc	%0, %3, %0		! hyper_flush_unconditional_combined
-		bne	1b
-		 sta	%%g0, [%0] %4
-		sta	%%g0, [%%g0] %5		! hyper_flush_whole_icache"
-	: "=&r" (tmp1), "=&r" (ctr)
-	: "0" (vac_cache_size), "r" (vac_line_size),
-	  "i" (ASI_M_FLUSH_CTX), "i" (ASI_M_FLUSH_IWHOLE),
-	  "i" (UWINMASK_OFFSET), "1" (ctr)
-	: "g4");
-}
-
-static void hypersparc_flush_cache_mm(struct mm_struct *mm)
-{
-	register int ctr asm("g5");
-
-	FLUSH_BEGIN(mm)
-	ctr = 0;
-	__asm__ __volatile__("
-	1:	ld	[%%g6 + %2], %%g4	! flush user windows
-		orcc	%%g0, %%g4, %%g0
-		add	%0, 1, %0
-		bne	1b
-		 save	%%sp, -64, %%sp
-	2:	subcc	%0, 1, %0
-		bne	2b
-		 restore %%g0, %%g0, %%g0"
-	: "=&r" (ctr)
-	: "0" (ctr), "i" (UWINMASK_OFFSET)
-	: "g4");
-
-	/* Gcc can bite me... */
-#if !defined(__svr4__) && !defined(__ELF__)
-	__asm__ __volatile__("
-	sethi	%hi(_vac_line_size), %g1
-	ld	[%g1 + %lo(_vac_line_size)], %o1
-	sethi	%hi(_vac_cache_size), %g2
-	ld	[%g2 + %lo(_vac_cache_size)], %o0");
-#else
-	__asm__ __volatile__("
-	sethi	%hi(vac_line_size), %g1
-	ld	[%g1 + %lo(vac_line_size)], %o1
-	sethi	%hi(vac_cache_size), %g2
-	ld	[%g2 + %lo(vac_cache_size)], %o0");
-#endif
-
-	__asm__ __volatile__("
-		add	%%o1, %%o1, %%g1
-		add	%%o1, %%g1, %%g2
-		add	%%o1, %%g2, %%g3
-		add	%%o1, %%g3, %%g4
-		add	%%o1, %%g4, %%g5
-		add	%%o1, %%g5, %%o4
-		add	%%o1, %%o4, %%o5
-	1:	subcc	%%o0, %%o5, %%o0		! hyper_flush_cache_user
-		sta	%%g0, [%%o0 + %%g0] %0
-		sta	%%g0, [%%o0 + %%o1] %0
-		sta	%%g0, [%%o0 + %%g1] %0
-		sta	%%g0, [%%o0 + %%g2] %0
-		sta	%%g0, [%%o0 + %%g3] %0
-		sta	%%g0, [%%o0 + %%g4] %0
-		sta	%%g0, [%%o0 + %%g5] %0
-		bne	1b
-		 sta	%%g0, [%%o0 + %%o4] %0
-		sta	%%g0, [%%g0 + %%g0] %1		! hyper_flush_whole_icache"
-	: : "i" (ASI_M_FLUSH_USER), "i" (ASI_M_FLUSH_IWHOLE));
-	FLUSH_END
-}
-
-/* The things we do for performance... */
-static void hypersparc_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
-{
-	FLUSH_BEGIN(mm)
-	__asm__ __volatile__("
-	mov	0, %%g5
-1:	ld	[%%g6 + %0], %%g4
-	orcc	%%g0, %%g4, %%g0
-	add	%%g5, 1, %%g5
-	bne	1b
-	 save	%%sp, -64, %%sp
-2:	subcc	%%g5, 1, %%g5
-	bne	2b
-	 restore %%g0, %%g0, %%g0
-	" : : "i" (UWINMASK_OFFSET));
-#if !defined(__svr4__) && !defined(__ELF__)
-	__asm__ __volatile__("
-	sethi	%hi(_vac_line_size), %g1
-	ld	[%g1 + %lo(_vac_line_size)], %o4
-	sethi	%hi(_vac_cache_size), %g2
-	ld	[%g2 + %lo(_vac_cache_size)], %o3");
-#else
-	__asm__ __volatile__("
-	sethi	%hi(vac_line_size), %g1
-	ld	[%g1 + %lo(vac_line_size)], %o4
-	sethi	%hi(vac_cache_size), %g2
-	ld	[%g2 + %lo(vac_cache_size)], %o3");
-#endif
-	/* Close your eyes... */
-	__asm__ __volatile__("
-	add	%%o2, (%0 - 1), %%o2
-	andn	%%o1, (%0 - 1), %%o1
-	add	%%o4, %%o4, %%o5
-	andn	%%o2, (%0 - 1), %%o2
-	add	%%o4, %%o5, %%g1
-	sub	%%o2, %%o1, %%g4
-	add	%%o4, %%g1, %%g2
-	sll	%%o3, 2, %%g5
-	add	%%o4, %%g2, %%g3
-	cmp	%%g4, %%g5
-	add	%%o4, %%g3, %%g4
-	blu	0f
-	 add	%%o4, %%g4, %%g5
-	add	%%o4, %%g5, %%g7
-1:	subcc	%%o3, %%g7, %%o3
-	sta	%%g0, [%%o3 + %%g0] %1
-	sta	%%g0, [%%o3 + %%o4] %1
-	sta	%%g0, [%%o3 + %%o5] %1
-	sta	%%g0, [%%o3 + %%g1] %1
-	sta	%%g0, [%%o3 + %%g2] %1
-	sta	%%g0, [%%o3 + %%g3] %1
-	sta	%%g0, [%%o3 + %%g4] %1
-	bne	1b
-	 sta	%%g0, [%%o3 + %%g5] %1
-	b,a	9f
-0:	ld	[%%o0 + %3], %%o0
-	mov	%2, %%g7
-	lda	[%%g7] %4, %%o3
-	sta	%%o0, [%%g7] %4
-	sethi	%%hi(%0), %%g7
-	sub	%%o2, %%g7, %%o0
-1:	or	%%o0, 0x400, %%g7
-	lda	[%%g7] %5, %%g7
-	orcc	%%g7, 0x0, %%g0
-	be,a	3f
-	 mov	%%o0, %%o2
-	add	%%o4, %%g5, %%g7
-2:	sub	%%o2, %%g7, %%o2
-	sta	%%g0, [%%o2 + %%g0] %6
-	sta	%%g0, [%%o2 + %%o4] %6
-	sta	%%g0, [%%o2 + %%o5] %6
-	sta	%%g0, [%%o2 + %%g1] %6
-	sta	%%g0, [%%o2 + %%g2] %6
-	sta	%%g0, [%%o2 + %%g3] %6
-	andcc	%%o2, 0xffc, %%g0
-	sta	%%g0, [%%o2 + %%g4] %6
-	bne	2b
-	 sta	%%g0, [%%o2 + %%g5] %6
-3:	sethi	%%hi(%0), %%g7
-	cmp	%%o2, %%o1
-	bne	1b
-	 sub	%%o2, %%g7, %%o0
-	mov	%8, %%g5
-	lda	[%%g5] %4, %%g0
-	mov	%2, %%g7
-	sta	%%o3, [%%g7] %4
-9:	sta	%%g0, [%%g0 + %%g0] %7
-"	: : "i" (PAGE_SIZE), "i" (ASI_M_FLUSH_USER), "i" (SRMMU_CTX_REG),
-	    "i" ((const unsigned long)(&(((struct mm_struct *)0)->context))),
-	    "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE), "i" (ASI_M_FLUSH_PAGE),
-	    "i" (ASI_M_FLUSH_IWHOLE), "i" (SRMMU_FAULT_STATUS));
-	FLUSH_END
-}
-
-/* HyperSparc requires a valid mapping where we are about to flush
- * in order to check for a physical tag match during the flush.
- */
-static void hypersparc_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
-{
-	__asm__ __volatile__("
-	ld	[%%o0 + %0], %%g4
-	ld	[%%g4 + %1], %%o0
-"	: : "i" ((const unsigned long)(&(((struct vm_area_struct *)0)->vm_mm))),
-	    "i" ((const unsigned long)(&(((struct mm_struct *)0)->context))));
-#ifndef __SMP__
-	__asm__ __volatile__("
-	cmp	%o0, -1
-	bne	1f
-	 mov	0, %g5
-	retl");
-#else
-	__asm__ __volatile__("mov	0, %g5");	/* else we die a horrible death */
-#endif
-	__asm__ __volatile__("
-1:	 ld	[%%g6 + %0], %%g4	! flush user windows
-	orcc	%%g0, %%g4, %%g0
-	add	%%g5, 1, %%g5
-	bne	1b
-	 save	%%sp, -64, %%sp
-2:	subcc	%%g5, 1, %%g5
-	bne	2b
-	 restore %%g0, %%g0, %%g0"
-	: : "i" (UWINMASK_OFFSET));
-#if !defined(__svr4__) && !defined(__ELF__)
-	__asm__ __volatile__("
-	sethi	%hi(_vac_line_size), %g1
-	ld	[%g1 + %lo(_vac_line_size)], %o4");
-#else
-	__asm__ __volatile__("
-	sethi	%hi(vac_line_size), %g1
-	ld	[%g1 + %lo(vac_line_size)], %o4");
-#endif
-	__asm__ __volatile__("
-	mov	%0, %%o3
-	andn	%%o1, (%4 - 1), %%o1
-	lda	[%%o3] %1, %%o2
-	sta	%%o0, [%%o3] %1
-	or	%%o1, 0x400, %%o5
-	lda	[%%o5] %3, %%o5
-	orcc	%%o5, 0x0, %%g0
-	be	2f
-	 sethi	%%hi(%4), %%g7
-	add	%%o4, %%o4, %%o5
-	add	%%o1, %%g7, %%o1
-	add	%%o4, %%o5, %%g1
-	add	%%o4, %%g1, %%g2
-	add	%%o4, %%g2, %%g3
-	add	%%o4, %%g3, %%g4
-	add	%%o4, %%g4, %%g5
-	add	%%o4, %%g5, %%g7
-1:	sub	%%o1, %%g7, %%o1		! hyper_flush_cache_page
-	sta	%%g0, [%%o1 + %%g0] %5
-	sta	%%g0, [%%o1 + %%o4] %5
-	sta	%%g0, [%%o1 + %%o5] %5
-	sta	%%g0, [%%o1 + %%g1] %5
-	sta	%%g0, [%%o1 + %%g2] %5
-	sta	%%g0, [%%o1 + %%g3] %5
-	andcc	%%o1, 0xffc, %%g0
-	sta	%%g0, [%%o1 + %%g4] %5
-	bne	1b
-	 sta	%%g0, [%%o1 + %%g5] %5
-	sta	%%g0, [%%g0 + %%g0] %6
-2:	mov	%0, %%g4
-	sta	%%o2, [%%g4] %1
-	mov	%2, %%g7
-	retl
-	 lda	[%%g7] %1, %%g0
-"	: : "i" (SRMMU_CTX_REG), "i" (ASI_M_MMUREGS), "i" (SRMMU_FAULT_STATUS),
-	    "i" (ASI_M_FLUSH_PROBE), "i" (PAGE_SIZE), "i" (ASI_M_FLUSH_PAGE),
-	    "i" (ASI_M_FLUSH_IWHOLE));
-}
-
-/* HyperSparc is copy-back. */
-static void hypersparc_flush_page_to_ram(unsigned long page)
-{
-#if !defined(__svr4__) && !defined(__ELF__)
-	__asm__ __volatile__("
-	sethi	%hi(_vac_line_size), %g1
-	ld	[%g1 + %lo(_vac_line_size)], %o4");
-#else
-	__asm__ __volatile__("
-	sethi	%hi(vac_line_size), %g1
-	ld	[%g1 + %lo(vac_line_size)], %o4");
-#endif
-	__asm__ __volatile__("
-	andn	%%o0, (%0 - 1), %%o0
-	add	%%o4, %%o4, %%o5
-	or	%%o0, 0x400, %%g7
-	lda	[%%g7] %2, %%g5
-	add	%%o4, %%o5, %%g1
-	orcc	%%g5, 0x0, %%g0
-	be	2f
-	 add	%%o4, %%g1, %%g2
-	sethi	%%hi(%0), %%g5
-	add	%%o4, %%g2, %%g3
-	add	%%o0, %%g5, %%o0
-	add	%%o4, %%g3, %%g4
-	add	%%o4, %%g4, %%g5
-	add	%%o4, %%g5, %%g7
-1:	sub	%%o0, %%g7, %%o0
-	sta	%%g0, [%%o0 + %%g0] %1
-	sta	%%g0, [%%o0 + %%o4] %1
-	sta	%%g0, [%%o0 + %%o5] %1
-	sta	%%g0, [%%o0 + %%g1] %1
-	sta	%%g0, [%%o0 + %%g2] %1
-	sta	%%g0, [%%o0 + %%g3] %1
-	andcc	%%o0, 0xffc, %%g0
-	sta	%%g0, [%%o0 + %%g4] %1
-	bne	1b
-	 sta	%%g0, [%%o0 + %%g5] %1
-2:	mov	%3, %%g1
-	lda	[%%g1] %4, %%g0"
-	: /* no outputs */
-	: "i" (PAGE_SIZE), "i" (ASI_M_FLUSH_PAGE), "i" (ASI_M_FLUSH_PROBE),
-	  "i" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS));
-}
-
-static void hypersparc_flush_chunk(unsigned long chunk)
-{
-	hypersparc_flush_page_to_ram(chunk);
-}
-
-/* HyperSparc is IO cache coherent. */
-static void hypersparc_flush_page_for_dma(unsigned long page)
-{
-}
-
-/* HyperSparc has unified I/D L2 cache, however it posseses a small on-chip
- * ICACHE which must be flushed for the new style signals.
- */
-static void hypersparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr)
-{
-	hyper_flush_whole_icache();
-}
-
-static void hypersparc_flush_cache_page_to_uncache(unsigned long page)
-{
-	page &= PAGE_MASK;
-	__asm__ __volatile__("
-	lda	[%0] %2, %%g4
-	orcc	%%g4, 0x0, %%g0
-	be	2f
-	 sethi	%%hi(%7), %%g5
-1:	subcc	%%g5, %6, %%g5		! hyper_flush_cache_page
-	bne	1b
-	 sta	%%g0, [%1 + %%g5] %3
-2:	lda	[%4] %5, %%g0"
-	: /* no outputs */
-	: "r" (page | 0x400), "r" (page), "i" (ASI_M_FLUSH_PROBE),
-	  "i" (ASI_M_FLUSH_PAGE), "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS),
-	  "r" (vac_line_size), "i" (PAGE_SIZE)
-	: "g4", "g5");
-}
-
-static unsigned long hypersparc_getpage(void)
-{
-	register unsigned long page asm("i0");
-
-	page = get_free_page(GFP_KERNEL);
-#if !defined(__svr4__) && !defined(__ELF__)
-	__asm__ __volatile__("
-	sethi	%hi(_vac_line_size), %g1
-	ld	[%g1 + %lo(_vac_line_size)], %o4");
-#else
-	__asm__ __volatile__("
-	sethi	%hi(vac_line_size), %g1
-	ld	[%g1 + %lo(vac_line_size)], %o4");
-#endif
-	__asm__ __volatile__("
-	sethi	%%hi(%0), %%g5
-	add	%%o4, %%o4, %%o5
-	add	%%o4, %%o5, %%g1
-	add	%%o4, %%g1, %%g2
-	add	%%o4, %%g2, %%g3
-	add	%%i0, %%g5, %%o0
-	add	%%o4, %%g3, %%g4
-	add	%%o4, %%g4, %%g5
-	add	%%o4, %%g5, %%g7
-1:	sub	%%o0, %%g7, %%o0
-	sta	%%g0, [%%o0 + %%g0] %1
-	sta	%%g0, [%%o0 + %%o4] %1
-	sta	%%g0, [%%o0 + %%o5] %1
-	sta	%%g0, [%%o0 + %%g1] %1
-	sta	%%g0, [%%o0 + %%g2] %1
-	sta	%%g0, [%%o0 + %%g3] %1
-	andcc	%%o0, 0xffc, %%g0
-	sta	%%g0, [%%o0 + %%g4] %1
-	bne	1b
-	 sta	%%g0, [%%o0 + %%g5] %1"
-	: /* no outputs */
-	: "i" (PAGE_SIZE), "i" (ASI_M_FLUSH_PAGE));
-	return page;
-}
-
-static void hypersparc_flush_tlb_all(void)
-{
-	srmmu_flush_whole_tlb();
-	module_stats.invall++;
-}
-
-static void hypersparc_flush_tlb_mm(struct mm_struct *mm)
-{
-	FLUSH_BEGIN(mm)
-	__asm__ __volatile__("
-	lda	[%0] %3, %%g5
-	sta	%2, [%0] %3
-	sta	%%g0, [%1] %4
-	sta	%%g5, [%0] %3"
-	: /* no outputs */
-	: "r" (SRMMU_CTX_REG), "r" (0x300), "r" (mm->context),
-	  "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE)
-	: "g5");
-	module_stats.invmm++;
-	FLUSH_END
-}
-
-static void hypersparc_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
-{
-	unsigned long size;
-
-	FLUSH_BEGIN(mm)
-	start &= SRMMU_PGDIR_MASK;
-	size = SRMMU_PGDIR_ALIGN(end) - start;
-	__asm__ __volatile__("
-		lda	[%0] %5, %%g5
-		sta	%1, [%0] %5
-	1:	subcc	%3, %4, %3
-		bne	1b
-		 sta	%%g0, [%2 + %3] %6
-		sta	%%g5, [%0] %5"
-	: /* no outputs */
-	: "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (start | 0x200),
-	  "r" (size), "r" (SRMMU_PGDIR_SIZE), "i" (ASI_M_MMUREGS),
-	  "i" (ASI_M_FLUSH_PROBE)
-	: "g5");
-	module_stats.invrnge++;
-	FLUSH_END
-}
-
-static void hypersparc_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
-{
-	struct mm_struct *mm = vma->vm_mm;
-
-	FLUSH_BEGIN(mm)
-	__asm__ __volatile__("
-	lda	[%0] %3, %%g5
-	sta	%1, [%0] %3
-	sta	%%g0, [%2] %4
-	sta	%%g5, [%0] %3"
-	: /* no outputs */
-	: "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (page & PAGE_MASK),
-	  "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE)
-	: "g5");
-	module_stats.invpg++;
-	FLUSH_END
-}
-
-static void hypersparc_flush_tlb_page_for_cbit(unsigned long page)
-{
-	srmmu_flush_tlb_page(page);
-}
+/* hypersparc.S */
+extern void hypersparc_flush_cache_all(void);
+extern void hypersparc_flush_cache_mm(struct mm_struct *mm);
+extern void hypersparc_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end);
+extern void hypersparc_flush_cache_page(struct vm_area_struct *vma, unsigned long page);
+extern void hypersparc_flush_page_to_ram(unsigned long page);
+extern void hypersparc_flush_chunk(unsigned long chunk);
+extern void hypersparc_flush_page_for_dma(unsigned long page);
+extern void hypersparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr);
+extern void hypersparc_flush_tlb_all(void);
+extern void hypersparc_flush_tlb_mm(struct mm_struct *mm);
+extern void hypersparc_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end);
+extern void hypersparc_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
 
 static void hypersparc_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp)
 {
@@ -3234,7 +2681,6 @@
 	init_vac_layout();
 
 	set_pte = srmmu_set_pte_nocache_hyper;
-	mmu_getpage = hypersparc_getpage;
 	flush_cache_all = hypersparc_flush_cache_all;
 	flush_cache_mm = hypersparc_flush_cache_mm;
 	flush_cache_range = hypersparc_flush_cache_range;
@@ -3248,8 +2694,6 @@
 	flush_page_to_ram = hypersparc_flush_page_to_ram;
 	flush_sig_insns = hypersparc_flush_sig_insns;
 	flush_page_for_dma = hypersparc_flush_page_for_dma;
-	flush_cache_page_to_uncache = hypersparc_flush_cache_page_to_uncache;
-	flush_tlb_page_for_cbit = hypersparc_flush_tlb_page_for_cbit;
 
 	flush_chunk = hypersparc_flush_chunk; /* local flush _only_ */
 
@@ -3306,7 +2750,6 @@
 	init_vac_layout();
 
 	set_pte = srmmu_set_pte_nocache_cypress;
-	mmu_getpage = cypress_getpage;
 	flush_cache_all = cypress_flush_cache_all;
 	flush_cache_mm = cypress_flush_cache_mm;
 	flush_cache_range = cypress_flush_cache_range;
@@ -3322,8 +2765,6 @@
 	flush_page_to_ram = cypress_flush_page_to_ram;
 	flush_sig_insns = cypress_flush_sig_insns;
 	flush_page_for_dma = cypress_flush_page_for_dma;
-	flush_cache_page_to_uncache = cypress_flush_page_to_uncache;
-	flush_tlb_page_for_cbit = cypress_flush_tlb_page_for_cbit;
 	sparc_update_rootmmu_dir = cypress_update_rootmmu_dir;
 
 	update_mmu_cache = srmmu_vac_update_mmu_cache;
@@ -3437,8 +2878,6 @@
 	flush_page_to_ram = swift_flush_page_to_ram;
 	flush_sig_insns = swift_flush_sig_insns;
 	flush_page_for_dma = swift_flush_page_for_dma;
-	flush_cache_page_to_uncache = swift_flush_cache_page_to_uncache;
-	flush_tlb_page_for_cbit = swift_flush_tlb_page_for_cbit;
 
 	/* Are you now convinced that the Swift is one of the
 	 * biggest VLSI abortions of all time?  Bravo Fujitsu!
@@ -3485,8 +2924,6 @@
 	flush_page_to_ram = tsunami_flush_page_to_ram;
 	flush_sig_insns = tsunami_flush_sig_insns;
 	flush_page_for_dma = tsunami_flush_page_for_dma;
-	flush_cache_page_to_uncache = tsunami_flush_cache_page_to_uncache;
-	flush_tlb_page_for_cbit = tsunami_flush_tlb_page_for_cbit;
 
 	poke_srmmu = poke_tsunami;
 }
@@ -3539,9 +2976,6 @@
 	flush_page_to_ram = local_flush_page_to_ram;
 	flush_sig_insns = local_flush_sig_insns;
 	flush_page_for_dma = local_flush_page_for_dma;
-	if (viking_mxcc_present) {
-		flush_cache_page_to_uncache = local_flush_cache_page_to_uncache;
-	}
 #endif
 }
 
@@ -3562,12 +2996,9 @@
 
 		msi_set_sync();
 
-		mmu_getpage = viking_no_mxcc_getpage;
 		set_pte = srmmu_set_pte_nocache_nomxccvik;
 		sparc_update_rootmmu_dir = viking_no_mxcc_update_rootmmu_dir;
 
-		flush_cache_page_to_uncache = viking_no_mxcc_flush_page;
-
 		flush_chunk = viking_nomxcc_flush_chunk; /* local flush _only_ */
 
 		/* We need this to make sure old viking takes no hits
@@ -3580,7 +3011,6 @@
 	} else {
 		srmmu_name = "TI Viking/MXCC";
 		viking_mxcc_present = 1;
-		flush_cache_page_to_uncache = viking_mxcc_flush_page;
 
 		flush_chunk = viking_mxcc_flush_chunk; /* local flush _only_ */
 
@@ -3600,7 +3030,6 @@
 
 	flush_page_to_ram = viking_flush_page_to_ram;
 	flush_sig_insns = viking_flush_sig_insns;
-	flush_tlb_page_for_cbit = viking_flush_tlb_page_for_cbit;
 
 	poke_srmmu = poke_viking;
 }
@@ -3708,15 +3137,6 @@
 	xc1((smpfunc_t) local_flush_page_for_dma, page);
 }
 
-static void smp_flush_cache_page_to_uncache(unsigned long page)
-{
-	xc1((smpfunc_t) local_flush_cache_page_to_uncache, page);
-}
-
-static void smp_flush_tlb_page_for_cbit(unsigned long page)
-{
-	xc1((smpfunc_t) local_flush_tlb_page_for_cbit, page);
-}
 #endif
 
 /* Load up routines and constants for sun4m mmu */
@@ -3742,7 +3162,6 @@
 	pg_iobits = SRMMU_VALID | SRMMU_WRITE | SRMMU_REF;
 	    
 	/* Functions */
-	mmu_getpage = srmmu_getpage;
 	set_pte = srmmu_set_pte_cacheable;
 	init_new_context = srmmu_init_new_context;
 	switch_to_context = srmmu_switch_to_context;
@@ -3845,8 +3264,6 @@
 	local_flush_page_to_ram = flush_page_to_ram;
 	local_flush_sig_insns = flush_sig_insns;
 	local_flush_page_for_dma = flush_page_for_dma;
-	local_flush_cache_page_to_uncache = flush_cache_page_to_uncache;
-	local_flush_tlb_page_for_cbit = flush_tlb_page_for_cbit;
 
 	flush_cache_all = smp_flush_cache_all;
 	flush_cache_mm = smp_flush_cache_mm;
@@ -3859,7 +3276,5 @@
 	flush_page_to_ram = smp_flush_page_to_ram;
 	flush_sig_insns = smp_flush_sig_insns;
 	flush_page_for_dma = smp_flush_page_for_dma;
-	flush_cache_page_to_uncache = smp_flush_cache_page_to_uncache;
-	flush_tlb_page_for_cbit = smp_flush_tlb_page_for_cbit;
 #endif
 }
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/prom/console.c linux/arch/sparc/prom/console.c
--- v2.1.29/linux/arch/sparc/prom/console.c	Thu Dec 19 01:03:33 1996
+++ linux/arch/sparc/prom/console.c	Thu Mar 20 16:42:00 1997
@@ -1,4 +1,4 @@
-/* $Id: console.c,v 1.10 1996/12/18 06:46:54 tridge Exp $
+/* $Id: console.c,v 1.11 1997/03/18 17:58:10 jj Exp $
  * console.c: Routines that deal with sending and receiving IO
  *            to/from the current console device using the PROM.
  *
@@ -31,7 +31,6 @@
 		break;
 	case PROM_V2:
 	case PROM_V3:
-	case PROM_P1275:
 		if( (*(romvec->pv_v2devops).v2_dev_read)(*romvec->pv_v2bootargs.fd_stdin , &inc, 0x1) == 1) {
 			i = inc;
 		} else {
@@ -66,7 +65,6 @@
 		break;
 	case PROM_V2:
 	case PROM_V3:
-	case PROM_P1275:
 		outc = c;
 		if( (*(romvec->pv_v2devops).v2_dev_write)(*romvec->pv_v2bootargs.fd_stdout, &outc, 0x1) == 1)
 			i = 0;
@@ -129,7 +127,6 @@
 			return PROMDEV_I_UNK;
 		};
 	case PROM_V3:
-	case PROM_P1275:
 		save_flags(flags); cli();
 		st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdin);
 		__asm__ __volatile__("ld [%0], %%g6\n\t" : :
@@ -177,7 +174,6 @@
 		break;
 	case PROM_V2:
 	case PROM_V3:
-	case PROM_P1275:
 		save_flags(flags); cli();
 		st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdout);
 		__asm__ __volatile__("ld [%0], %%g6\n\t" : :
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/prom/devops.c linux/arch/sparc/prom/devops.c
--- v2.1.29/linux/arch/sparc/prom/devops.c	Sat Nov  9 00:12:25 1996
+++ linux/arch/sparc/prom/devops.c	Thu Mar 20 16:42:00 1997
@@ -1,4 +1,4 @@
-/* $Id: devops.c,v 1.6 1996/10/12 12:37:38 davem Exp $
+/* $Id: devops.c,v 1.7 1997/03/18 17:58:19 jj Exp $
  * devops.c:  Device operations using the PROM.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -27,7 +27,6 @@
 		break;
 	case PROM_V2:
 	case PROM_V3:
-	case PROM_P1275:
 		handle = (*(romvec->pv_v2devops.v2_dev_open))(dstr);
 		break;
         case PROM_AP1000:
@@ -55,7 +54,6 @@
 		break;
 	case PROM_V2:
 	case PROM_V3:
-	case PROM_P1275:
 		(*(romvec->pv_v2devops.v2_dev_close))(dhandle);
 		break;
         case PROM_AP1000:
@@ -82,7 +80,6 @@
 		break;
 	case PROM_V2:
 	case PROM_V3:
-	case PROM_P1275:
 		(*(romvec->pv_v2devops.v2_dev_seek))(dhandle, seekhi, seeklo);
 		break;
         case PROM_AP1000:
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/prom/init.c linux/arch/sparc/prom/init.c
--- v2.1.29/linux/arch/sparc/prom/init.c	Thu Dec 19 01:03:33 1996
+++ linux/arch/sparc/prom/init.c	Thu Mar 20 16:42:00 1997
@@ -1,4 +1,4 @@
-/* $Id: init.c,v 1.9 1996/12/18 06:46:55 tridge Exp $
+/* $Id: init.c,v 1.11 1997/03/18 17:58:24 jj Exp $
  * init.c:  Initialize internal variables used by the PROM
  *          library functions.
  *
@@ -19,6 +19,8 @@
 /* The root node of the prom device tree. */
 int prom_root_node;
 
+int prom_stdin, prom_stdout;
+
 /* Pointer to the device tree operations structure. */
 struct linux_nodeops *prom_nodeops;
 
@@ -49,11 +51,6 @@
 	case 3:
 		prom_vers = PROM_V3;
 		break;
-	case 4:
-		prom_vers = PROM_P1275;
-		prom_printf("PROMLIB: Sun IEEE Prom not supported yet\n");
-		prom_halt();
-		break;
 	case 42: /* why not :-) */
 		prom_vers = PROM_AP1000;
 		break;
@@ -77,6 +74,11 @@
 	   (((unsigned long) prom_nodeops) == -1))
 		prom_halt();
 
+	if(prom_vers == PROM_V2 || prom_vers == PROM_V3) {
+		prom_stdout = *romvec->pv_v2bootargs.fd_stdout;
+		prom_stdin  = *romvec->pv_v2bootargs.fd_stdin;
+	}
+	
 	prom_meminit();
 
 	prom_ranges_init();
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/prom/memory.c linux/arch/sparc/prom/memory.c
--- v2.1.29/linux/arch/sparc/prom/memory.c	Fri Dec 13 01:37:32 1996
+++ linux/arch/sparc/prom/memory.c	Thu Mar 20 16:42:00 1997
@@ -1,4 +1,4 @@
-/* $Id: memory.c,v 1.9 1996/11/13 05:10:09 davem Exp $
+/* $Id: memory.c,v 1.10 1997/03/18 17:58:27 jj Exp $
  * memory.c: Prom routine for acquiring various bits of information
  *           about RAM on the machine, both virtual and physical.
  *
@@ -107,7 +107,6 @@
 		break;
 	case PROM_V2:
 	case PROM_V3:
-	case PROM_P1275:
 		/* Grrr, have to traverse the prom device tree ;( */
 		node = prom_getchild(prom_root_node);
 		node = prom_searchsiblings(node, "memory");
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/prom/mp.c linux/arch/sparc/prom/mp.c
--- v2.1.29/linux/arch/sparc/prom/mp.c	Sat Nov  9 00:12:27 1996
+++ linux/arch/sparc/prom/mp.c	Thu Mar 20 16:42:00 1997
@@ -1,4 +1,4 @@
-/* $Id: mp.c,v 1.6 1996/09/19 20:27:25 davem Exp $
+/* $Id: mp.c,v 1.7 1997/03/18 17:58:23 jj Exp $
  * mp.c:  OpenBoot Prom Multiprocessor support routines.  Don't call
  *        these on a UP or else you will halt and catch fire. ;)
  *
@@ -32,7 +32,6 @@
 		ret = -1;
 		break;
 	case PROM_V3:
-	case PROM_P1275:
 		ret = (*(romvec->v3_cpustart))(cpunode, (int) ctable_reg, ctx, pc);
 		break;
 	};
@@ -62,7 +61,6 @@
 		ret = -1;
 		break;
 	case PROM_V3:
-	case PROM_P1275:
 		ret = (*(romvec->v3_cpustop))(cpunode);
 		break;
 	};
@@ -92,7 +90,6 @@
 		ret = -1;
 		break;
 	case PROM_V3:
-	case PROM_P1275:
 		ret = (*(romvec->v3_cpuidle))(cpunode);
 		break;
 	};
@@ -122,7 +119,6 @@
 		ret = -1;
 		break;
 	case PROM_V3:
-	case PROM_P1275:
 		ret = (*(romvec->v3_cpuresume))(cpunode);
 		break;
 	};
diff -u --recursive --new-file v2.1.29/linux/arch/sparc/prom/tree.c linux/arch/sparc/prom/tree.c
--- v2.1.29/linux/arch/sparc/prom/tree.c	Mon Mar 17 14:54:22 1997
+++ linux/arch/sparc/prom/tree.c	Thu Mar 20 16:42:00 1997
@@ -1,4 +1,4 @@
-/* $Id: tree.c,v 1.15 1997/01/31 00:17:04 tdyas Exp $
+/* $Id: tree.c,v 1.16 1997/03/19 14:53:16 davem Exp $
  * tree.c: Basic device tree traversal/scanning for the Linux
  *         prom library.
  *
@@ -257,6 +257,33 @@
 	restore_current();
 	restore_flags(flags);
 	return ret;
+}
+
+int prom_finddevice(char *name)
+{
+	int topnd = prom_getchild(prom_root_node);
+	int srch;
+
+	if(name[0] == '/')
+		name++;
+	if(sparc_cpu_model == sun4d) {
+		if(!strcmp(name, "sbus"))
+			name = "sbi";
+		if((srch = prom_searchsiblings(topnd, "io-unit")) == 0 ||
+		   (srch = prom_getchild(srch)) == 0 ||
+		   (srch = prom_searchsiblings(srch, name)) == 0) {
+			prom_printf("%s prom node not found.\n", name);
+			prom_halt();
+		}
+	} else if((srch = prom_searchsiblings(topnd, name)) == 0) {
+		if((srch = prom_searchsiblings(topnd, "iommu")) == 0 ||
+		   (srch = prom_getchild(srch)) == 0 ||
+		   (srch = prom_searchsiblings(srch, name)) == 0) {
+			prom_printf("Cannot find node %s\n", name);
+			prom_halt();
+		}
+	}
+	return srch;
 }
 
 int prom_node_has_property(int node, char *prop)
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/Makefile linux/arch/sparc64/Makefile
--- v2.1.29/linux/arch/sparc64/Makefile	Mon Mar 17 14:54:22 1997
+++ linux/arch/sparc64/Makefile	Thu Mar 20 16:43:31 1997
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.6 1997/03/04 16:27:18 jj Exp $
+# $Id: Makefile,v 1.9 1997/03/14 21:04:39 jj Exp $
 # sparc64/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
@@ -40,16 +40,6 @@
 archclean:
 
 archdep:
-
-# <hack> Temporary hack, until we get a clean compile of everything...
-vmlinux64: $(CONFIGURATION) init/main.o init/version.o
-	set -e; for i in arch/sparc64/kernel arch/sparc64/lib arch/sparc64/prom lib; do $(MAKE) -C $$i; done
-	$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \
-		arch/sparc64/kernel/kernel.o \
-		lib/lib.a arch/sparc64/prom/promlib.a arch/sparc64/lib/lib.a \
-		-o vmlinux
-	$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)\|\(\.\.ng$$\)' | sort > System.map
-# </hack>
 
 check_asm:
 	$(MAKE) -C arch/sparc64/kernel check_asm
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- v2.1.29/linux/arch/sparc64/defconfig	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc64/defconfig	Thu Mar 20 16:43:31 1997
@@ -10,7 +10,9 @@
 #
 # Loadable module support
 #
-# CONFIG_MODULES is not set
+CONFIG_MODULES=y
+CONFIG_MODVERSIONS=y
+CONFIG_KERNELD=y
 
 #
 # General setup
@@ -38,22 +40,26 @@
 SUN_FB_BWTWO=y
 SUN_FB_LEO=y
 TADPOLE_FB_WEITEK=y
-SUN_FB_FAST_ONE=y
-SUN_FB_FAST_TWO=y
-SUN_FB_FAST_MONO=y
-SUN_FB_GENERIC=y
 
 #
 # Misc Linux/SPARC drivers
 #
-CONFIG_SUN_OPENPROMIO=y
+CONFIG_SUN_OPENPROMIO=m
 CONFIG_SUN_MOSTEK_RTC=y
-CONFIG_SUN_OPENPROMFS=y
+# CONFIG_SUN_BPP is not set
+
+#
+# Linux/SPARC audio subsystem (EXPERIMENTAL)
+#
+# CONFIG_SPARCAUDIO is not set
+# CONFIG_SPARCAUDIO_AMD7930 is not set
+# CONFIG_SPARCAUDIO_CS4231 is not set
+CONFIG_SUN_OPENPROMFS=m
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
 CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
-CONFIG_BINFMT_JAVA=y
+CONFIG_BINFMT_JAVA=m
 
 #
 # Floppy, IDE, and other block devices
@@ -64,7 +70,7 @@
 CONFIG_MD_STRIPED=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
-CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP=m
 
 #
 # Networking options
@@ -74,7 +80,6 @@
 CONFIG_FIREWALL=y
 CONFIG_NET_ALIAS=y
 CONFIG_INET=y
-CONFIG_IP_FORWARD=y
 CONFIG_IP_MULTICAST=y
 CONFIG_IP_FIREWALL=y
 # CONFIG_IP_FIREWALL_NETLINK is not set
@@ -88,32 +93,35 @@
 # CONFIG_IP_ALWAYS_DEFRAG is not set
 # CONFIG_IP_ACCT is not set
 # CONFIG_IP_ROUTER is not set
-CONFIG_NET_IPIP=y
+# CONFIG_NET_IPIP is not set
 # CONFIG_IP_MROUTE is not set
-CONFIG_IP_ALIAS=y
+CONFIG_IP_ALIAS=m
 # CONFIG_ARPD is not set
 
 #
 # (it is safe to leave these untouched)
 #
 # CONFIG_INET_PCTCP is not set
-CONFIG_INET_RARP=y
+CONFIG_INET_RARP=m
 # CONFIG_PATH_MTU_DISCOVERY is not set
 CONFIG_IP_NOSR=y
 CONFIG_SKB_LARGE=y
-CONFIG_IPV6=y
+CONFIG_IPV6=m
 
 #
 #  
 #
-CONFIG_IPX=y
+CONFIG_IPX=m
 # CONFIG_IPX_INTERN is not set
 # CONFIG_IPX_PPROP_ROUTING is not set
-CONFIG_ATALK=y
+CONFIG_ATALK=m
+# CONFIG_IPDDP is not set
 # CONFIG_AX25 is not set
-CONFIG_X25=y
+CONFIG_X25=m
+# CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_LLC is not set
+# CONFIG_WAN_ROUTER is not set
 
 #
 # SCSI support
@@ -139,51 +147,50 @@
 # SCSI low-level drivers
 #
 CONFIG_SCSI_SUNESP=y
-CONFIG_SCSI_QLOGICPTI=y
+CONFIG_SCSI_QLOGICPTI=m
 
 #
 # Network device support
 #
 CONFIG_NETDEVICES=y
-CONFIG_DUMMY=y
-CONFIG_PPP=y
+CONFIG_DUMMY=m
+CONFIG_PPP=m
 
 #
 # CCP compressors for PPP are only built as modules.
 #
-CONFIG_SLIP=y
+CONFIG_SLIP=m
 CONFIG_SLIP_COMPRESSED=y
 CONFIG_SLIP_SMART=y
 # CONFIG_SLIP_MODE_SLIP6 is not set
 CONFIG_SUNLANCE=y
-CONFIG_HAPPYMEAL=y
-CONFIG_SUNQE=y
-CONFIG_MYRI_SBUS=y
+CONFIG_HAPPYMEAL=m
+CONFIG_SUNQE=m
+CONFIG_MYRI_SBUS=m
 
 #
 # Filesystems
 #
 CONFIG_QUOTA=y
-CONFIG_MINIX_FS=y
-CONFIG_EXT_FS=y
+CONFIG_MINIX_FS=m
 CONFIG_EXT2_FS=y
-CONFIG_XIA_FS=y
-CONFIG_FAT_FS=y
-CONFIG_MSDOS_FS=y
-CONFIG_VFAT_FS=y
-CONFIG_UMSDOS_FS=y
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_UMSDOS_FS=m
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
 CONFIG_ROOT_NFS=y
 CONFIG_RNFS_BOOTP=y
 CONFIG_RNFS_RARP=y
-CONFIG_SMB_FS=y
+CONFIG_SMB_FS=m
 CONFIG_SMB_WIN95=y
-CONFIG_NCP_FS=y
+CONFIG_NCP_FS=m
 CONFIG_ISO9660_FS=y
-CONFIG_HPFS_FS=y
-CONFIG_SYSV_FS=y
-CONFIG_AFFS_FS=y
+CONFIG_HPFS_FS=m
+CONFIG_SYSV_FS=m
+CONFIG_AFFS_FS=m
+CONFIG_ROMFS_FS=m
 CONFIG_AMIGA_PARTITION=y
 CONFIG_UFS_FS=y
 CONFIG_BSD_DISKLABEL=y
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/Makefile linux/arch/sparc64/kernel/Makefile
--- v2.1.29/linux/arch/sparc64/kernel/Makefile	Mon Mar 17 14:54:22 1997
+++ linux/arch/sparc64/kernel/Makefile	Thu Mar 20 16:43:31 1997
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.5 1997/03/04 16:26:54 jj Exp $
+# $Id: Makefile,v 1.7 1997/03/18 17:59:15 jj Exp $
 # Makefile for the linux kernel.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -16,7 +16,7 @@
 all: kernel.o head.o
 
 O_TARGET := kernel.o
-O_OBJS   := etrap.o rtrap.o hack.o process.o # signal32.o
+O_OBJS   := etrap.o rtrap.o hack.o process.o setup.o cpu.o idprom.o systbls.o traps.o entry.o devices.o auxio.o ioport.o # signal32.o
 OX_OBJS  := sparc64_ksyms.o
 
 head.o: head.S
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/auxio.c linux/arch/sparc64/kernel/auxio.c
--- v2.1.29/linux/arch/sparc64/kernel/auxio.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/kernel/auxio.c	Thu Mar 20 16:43:31 1997
@@ -0,0 +1,42 @@
+/* auxio.c: Probing for the Sparc AUXIO register at boot time.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <asm/oplib.h>
+#include <asm/io.h>
+#include <asm/auxio.h>
+#include <asm/sbus.h>
+
+/* Probe and map in the Auxiliary I/O register */
+unsigned char *auxio_register;
+
+__initfunc(void auxio_probe(void))
+{
+        struct linux_sbus *bus;
+        struct linux_sbus_device *sdev = 0;
+	struct linux_prom_registers auxregs[1];
+
+        for_each_sbus(bus) {
+                for_each_sbusdev(sdev, bus) {
+                        if(!strcmp(sdev->prom_name, "auxio")) {
+				break;
+                        }
+                }
+        }
+
+	if (!sdev) {
+		prom_printf("Cannot find auxio node, cannot continue...\n");
+		prom_halt();
+	}
+	prom_getproperty(sdev->prom_node, "reg", (char *) auxregs, sizeof(auxregs));
+	prom_apply_sbus_ranges(sdev->my_bus, auxregs, 0x1, sdev);
+	/* Map the register both read and write */
+	auxio_register = (unsigned char *) sparc_alloc_io(auxregs[0].phys_addr, 0,
+							  auxregs[0].reg_size,
+							  "auxiliaryIO",
+							  auxregs[0].which_io, 0x0);
+	TURN_ON_LED;
+}
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/cpu.c linux/arch/sparc64/kernel/cpu.c
--- v2.1.29/linux/arch/sparc64/kernel/cpu.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/kernel/cpu.c	Thu Mar 20 16:43:31 1997
@@ -0,0 +1,86 @@
+/* cpu.c: Dinky routines to look for the kind of Sparc cpu
+ *        we are on.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/system.h>
+
+struct cpu_iu_info {
+  short manuf;
+  short impl;
+  char* cpu_name;   /* should be enough I hope... */
+};
+
+struct cpu_fp_info {
+  short manuf;
+  short impl;
+  char fpu_vers;
+  char* fp_name;
+};
+
+/* In order to get the fpu type correct, you need to take the IDPROM's
+ * machine type value into consideration too.  I will fix this.
+ */
+struct cpu_fp_info linux_sparc_fpu[] = {
+  { 0x17, 0x10, 0, "FIXME: UltraSparc I FPU"},
+};
+
+#define NSPARCFPU  (sizeof(linux_sparc_fpu)/sizeof(struct cpu_fp_info))
+
+struct cpu_iu_info linux_sparc_chips[] = {
+  { 0x17, 0x10, "FIXME: UltraSparc I"},
+};
+
+#define NSPARCCHIPS  (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info))
+
+char *sparc_cpu_type[NCPUS] = { "cpu-oops", "cpu-oops1", "cpu-oops2", "cpu-oops3" };
+char *sparc_fpu_type[NCPUS] = { "fpu-oops", "fpu-oops1", "fpu-oops2", "fpu-oops3" };
+
+unsigned int fsr_storage;
+
+__initfunc(void cpu_probe(void))
+{
+	int manuf, impl;
+	unsigned i, cpuid;
+	long ver, fpu_vers;
+
+	cpuid = get_cpuid();
+	
+	__asm__ __volatile__ ("rdpr %%ver, %0; stx %%fsr, [%1]" : "=r" (ver) : "r" (&fpu_vers));
+
+	manuf = ((ver >> 48)&0xffff);
+	impl = ((ver >> 32)&0xffff);
+
+	fpu_vers = ((fpu_vers>>17)&0x7);
+
+	for(i = 0; i<NSPARCCHIPS; i++) {
+		if(linux_sparc_chips[i].manuf == manuf)
+			if(linux_sparc_chips[i].impl == impl) {
+				sparc_cpu_type[cpuid] = linux_sparc_chips[i].cpu_name;
+				break;
+			}
+	}
+
+	if(i==NSPARCCHIPS) {
+		printk("DEBUG: manuf = 0x%x   impl = 0x%x\n", manuf, 
+			    impl);
+		sparc_cpu_type[cpuid] = "Unknow CPU";
+	}
+
+	for(i = 0; i<NSPARCFPU; i++) {
+		if(linux_sparc_fpu[i].manuf == manuf && linux_sparc_fpu[i].impl == impl)
+			if(linux_sparc_fpu[i].fpu_vers == fpu_vers) {
+				sparc_fpu_type[cpuid] = linux_sparc_fpu[i].fp_name;
+				break;
+			}
+	}
+
+	if(i == NSPARCFPU) {
+		printk("DEBUG: manuf = 0x%x  impl = 0x%x fsr.vers = 0x%x\n", manuf, impl,
+			    (unsigned)fpu_vers);
+		sparc_fpu_type[cpuid] = "Unknown FPU";
+	}
+}
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/devices.c linux/arch/sparc64/kernel/devices.c
--- v2.1.29/linux/arch/sparc64/kernel/devices.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/kernel/devices.c	Thu Mar 20 16:43:31 1997
@@ -0,0 +1,65 @@
+/* devices.c: Initial scan of the prom device tree for important
+ *            Sparc device nodes which we need to find.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+#include <linux/tasks.h>
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <asm/page.h>
+#include <asm/oplib.h>
+#include <asm/smp.h>
+#include <asm/system.h>
+
+struct prom_cpuinfo linux_cpus[NCPUS];
+int linux_num_cpus;
+
+extern void cpu_probe(void);
+
+__initfunc(unsigned long
+device_scan(unsigned long mem_start))
+{
+	char node_str[128];
+	int nd, prom_node_cpu, thismid;
+	int cpu_nds[NCPUS];  /* One node for each cpu */
+	int cpu_ctr = 0;
+
+	prom_getstring(prom_root_node, "device_type", node_str, sizeof(node_str));
+
+	if(strcmp(node_str, "cpu") == 0) {
+		cpu_nds[0] = prom_root_node;
+		cpu_ctr++;
+	} else {
+		int scan;
+		scan = prom_getchild(prom_root_node);
+		prom_printf("root child is %08x\n", (unsigned) scan);
+		nd = 0;
+		while((scan = prom_getsibling(scan)) != 0) {
+			prom_getstring(scan, "device_type", node_str, sizeof(node_str));
+			if(strcmp(node_str, "cpu") == 0) {
+				cpu_nds[cpu_ctr] = scan;
+				linux_cpus[cpu_ctr].prom_node = scan;
+				prom_getproperty(scan, "mid", (char *) &thismid, sizeof(thismid));
+				linux_cpus[cpu_ctr].mid = thismid;
+				prom_printf("Found CPU %d <node=%08x,mid=%d>\n",
+					    cpu_ctr, (unsigned) scan,
+					    thismid);
+				cpu_ctr++;
+			}
+		};
+		if(cpu_ctr == 0) {
+			printk("No CPU nodes found, cannot continue.\n");
+			halt();
+		}
+		printk("Found %d CPU prom device tree node(s).\n", cpu_ctr);
+	};
+	prom_node_cpu = cpu_nds[0];
+
+	linux_num_cpus = cpu_ctr;
+
+	cpu_probe();
+	return mem_start;
+}
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/entry.S linux/arch/sparc64/kernel/entry.S
--- v2.1.29/linux/arch/sparc64/kernel/entry.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/kernel/entry.S	Thu Mar 20 16:43:31 1997
@@ -0,0 +1,130 @@
+/* $Id: entry.S,v 1.1 1997/03/18 17:58:59 jj Exp $
+ * arch/sparc64/kernel/entry.S:  Sparc64 trap low-level entry points.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
+ * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1996,1997 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+
+#include <asm/head.h>
+#include <asm/asi.h>
+#include <asm/smp.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/signal.h>
+
+#define curptr      g6
+
+#define NR_SYSCALLS 256      /* Each OS is different... */
+
+	.text
+        .align  4
+linux_sparc_ni_syscall:
+	sethi	%hi(sys_ni_syscall), %l7
+	or	%l7, %lo(sys_ni_syscall), %l7
+	ba,pt	%xcc,syscall_is_too_hard
+	 add	%l7, %g4, %l7
+
+linux_fast_syscall:
+	andn	%l7, 3, %l7
+	mov	%i0, %o0
+	mov	%i1, %o1
+	mov 	%i2, %o2
+	jmpl	%l7 + %g0, %g0
+	 mov	%i3, %o3
+
+linux_syscall_trace:
+	call	syscall_trace
+	 nop
+	mov	%i0, %o0
+	mov	%i1, %o1
+	mov	%i2, %o2
+	mov	%i3, %o3
+	ba,pt	%xcc, 2f
+	 mov	%i4, %o4
+
+	.globl	ret_from_syscall
+ret_from_syscall:
+	ba,pt	%xcc, ret_sys_call
+	 ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_I0], %o0
+
+	/* Linux native and SunOS system calls enter here... */
+	.align	4
+	.globl	linux_sparc_syscall
+linux_sparc_syscall:
+	/* Direct access to user regs, must faster. */
+	cmp	%g1, NR_SYSCALLS
+	add	%l7, %g4, %l7
+	bgeu,pn	%xcc, linux_sparc_ni_syscall
+	 sll	%g1, 3, %l4
+	ldx	[%l7 + %l4], %l7
+	andcc	%l7, 1, %g0
+	bne,pn	%icc, linux_fast_syscall
+	 /* Just do the next insn in the delay slot */
+
+	.globl	syscall_is_too_hard
+syscall_is_too_hard:
+	mov	%i0, %o0
+	mov	%i1, %o1
+	mov	%i2, %o2
+
+	ldx	[%curptr + AOFF_task_flags], %l5
+	mov	%i3, %o3
+	mov	%i4, %o4
+	andcc	%l5, 0x20, %g0
+	bne,pn	%icc, linux_syscall_trace
+	 mov	%i0, %l5
+2:
+	call	%l7
+	 mov	%i5, %o5
+
+	stx	%o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_I0]
+
+	.globl	ret_sys_call
+ret_sys_call:
+	ldx	[%curptr + AOFF_task_flags], %l6
+	mov	%ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_TSTATE], %g3
+	cmp	%o0, -ENOIOCTLCMD
+	sllx	%g2, 32, %g2
+	bgeu,pn	%xcc, 1f
+	 andcc	%l6, 0x20, %l6	
+
+	/* System call success, clear Carry condition code. */
+	andn	%g3, %g2, %g3
+	clr	%l6
+	stx	%g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_TSTATE]	
+	bne,pn	%icc, linux_syscall_trace2
+	 ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_TNPC], %l1 /* pc = npc */
+	add	%l1, 0x4, %l2				      /* npc = npc+4 */
+	stx	%l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_TPC]
+	ba,pt	%xcc, rtrap
+	 stx	%l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_TNPC]
+1:
+	/* System call failure, set Carry condition code.
+	 * Also, get abs(errno) to return to the process.
+	 */
+	sub	%g0, %o0, %o0
+	or	%g3, %g2, %g3
+	stx	%o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_I0]
+	mov	1, %l6
+	stx	%g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_TSTATE]
+	bne,pn	%icc, linux_syscall_trace2
+	 ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_TNPC], %l1 /* pc = npc */
+	add	%l1, 0x4, %l2				      /* npc = npc+4 */
+	stx	%l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_TPC]
+	ba,pt	%xcc, rtrap
+	 stx	%l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_TNPC]
+
+linux_syscall_trace2:
+	call	syscall_trace
+	 add	%l1, 0x4, %l2			/* npc = npc+4 */
+	stx	%l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_TPC]
+	ba,pt	%xcc, rtrap
+	 stx	%l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_TNPC]
+
+/* End of entry.S */
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/etrap.S linux/arch/sparc64/kernel/etrap.S
--- v2.1.29/linux/arch/sparc64/kernel/etrap.S	Mon Mar 17 14:54:23 1997
+++ linux/arch/sparc64/kernel/etrap.S	Thu Mar 20 16:43:31 1997
@@ -1,4 +1,4 @@
-/* $Id: etrap.S,v 1.4 1997/03/04 16:26:58 jj Exp $
+/* $Id: etrap.S,v 1.5 1997/03/13 08:24:01 jj Exp $
  * etrap.S: Preparing for entry into the kernel on Sparc V9.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -18,16 +18,17 @@
 	rdpr	%tstate, %g1
 	be,pn	%xcc, 1f		/* What happens more often? etrap when already in priv or from userland? */
 	 sllx	%g6, 32, %g6
+	/* Just when going from userland to privileged mode, we have to change this stuff. */
 	sll	%g2, 3, %g2
 	wrpr	%g2, %wstate
+	rdpr	%canrestore, %g5
+	wrpr	%g0, 0, %canrestore
+	wrpr	%g5, 0, %otherwin
 1:
 	sethi	%hi(current_set), %g4
 	or	%g4, %lo(current_set), %g4
 	rdpr	%tpc, %g2
-	rdpr	%canrestore, %g5
 	rdpr	%tnpc, %g3
-	wrpr	%g5, 0, %otherwin
-	wrpr	%g0, 0, %canrestore
 	ldx	[%g6 + %g4], %g6
 #ifdef __SMP__
 /* FIXME: Fix the above insn for SMP */
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/hack.S linux/arch/sparc64/kernel/hack.S
--- v2.1.29/linux/arch/sparc64/kernel/hack.S	Mon Mar 17 14:54:23 1997
+++ linux/arch/sparc64/kernel/hack.S	Thu Mar 20 16:43:31 1997
@@ -3,396 +3,344 @@
    Hopefully will disappear quickly, once we get everything
    to compile... */
 	.text
-	.globl ROOT_DEV
-ROOT_DEV:
-	nop
-	.globl ___lock_kernel
-___lock_kernel:
-	nop
-	.globl ___unlock_kernel
-___unlock_kernel:
-	nop
-	.globl __memcpy
-__memcpy:
-	nop
-	.globl __strncmp
-__strncmp:
-	nop
-	.globl _ctype
-_ctype:
-	nop
-	.globl _stext
-_stext:
-	nop
-	.globl bad_trap
-bad_trap:
-	nop
-	.globl bad_trap_tl1
-bad_trap_tl1:
-	nop
-	.globl bdflush
-bdflush:
-	nop
-	.globl breakpoint_trap
-breakpoint_trap:
-	nop
-	.globl buff_setup
-buff_setup:
-	nop
-	.globl buffer_init
-buffer_init:
-	nop
-	.globl change_root
-change_root:
-	nop
-	.globl console_init
-console_init:
-	nop
-	.globl console_loglevel
-console_loglevel:
-	nop
-	.globl console_restore_palette
-console_restore_palette:
-	nop
-	.globl current_set
-current_set:
-	nop
-	.globl do_cee
-do_cee:
-	nop
-	.globl do_cee_tl1
-do_cee_tl1:
-	nop
-	.globl do_dae
-do_dae:
-	nop
-	.globl do_dae_tl1
-do_dae_tl1:
-	nop
-	.globl do_dax
-do_dax:
-	nop
-	.globl do_dax_tl1
-do_dax_tl1:
-	nop
-	.globl do_div0
-do_div0:
-	nop
-	.globl do_div0_tl1
-do_div0_tl1:
-	nop
-	.globl do_fpdis
-do_fpdis:
-	nop
-	.globl do_fpdis_tl1
-do_fpdis_tl1:
-	nop
-	.globl do_fpieee
-do_fpieee:
-	nop
-	.globl do_fpieee_tl1
-do_fpieee_tl1:
-	nop
-	.globl do_fpother
-do_fpother:
-	nop
-	.globl do_fpother_tl1
-do_fpother_tl1:
-	nop
-	.globl do_iae
-do_iae:
-	nop
-	.globl do_iae_tl1
-do_iae_tl1:
-	nop
-	.globl do_iax
-do_iax:
-	nop
-	.globl do_iax_tl1
-do_iax_tl1:
-	nop
-	.globl do_ill
-do_ill:
-	nop
-	.globl do_ill_tl1
-do_ill_tl1:
-	nop
-	.globl do_irq
-do_irq:
-	nop
-	.globl do_irq_tl1
-do_irq_tl1:
-	nop
-	.globl do_ivec
-do_ivec:
-	nop
-	.globl do_ivec_tl1
-do_ivec_tl1:
-	nop
-	.globl do_lddfmna
-do_lddfmna:
-	nop
-	.globl do_lddfmna_tl1
-do_lddfmna_tl1:
-	nop
-	.globl do_mna
-do_mna:
-	nop
-	.globl do_mna_tl1
-do_mna_tl1:
-	nop
-	.globl do_paw
-do_paw:
-	nop
-	.globl do_paw_tl1
-do_paw_tl1:
-	nop
-	.globl do_privact
-do_privact:
-	nop
-	.globl do_privop
-do_privop:
-	nop
-	.globl do_stdfmna
-do_stdfmna:
-	nop
-	.globl do_stdfmna_tl1
-do_stdfmna_tl1:
-	nop
-	.globl do_tof
-do_tof:
-	nop
-	.globl do_tof_tl1
-do_tof_tl1:
-	nop
-	.globl do_vaw
-do_vaw:
-	nop
-	.globl do_vaw_tl1
-do_vaw_tl1:
-	nop
-	.globl dquot_init
-dquot_init:
-	nop
-	.globl errno
-errno:
-	nop
-	.globl eth_setup
-eth_setup:
-	nop
-	.globl file_table_init
-file_table_init:
-	nop
-	.globl floppy_setup
-floppy_setup:
-	nop
-	.globl getcc
-getcc:
-	nop
-	.globl indirect_syscall
-indirect_syscall:
-	nop
-	.globl init_IRQ
-init_IRQ:
-	nop
-	.globl init_task
-init_task:
-	nop
-	.globl initrd_below_start_ok
-initrd_below_start_ok:
-	nop
-	.globl initrd_start
-initrd_start:
-	nop
-	.globl inode_init
-inode_init:
-	nop
-	.globl install_linux_ticker
-install_linux_ticker:
-	nop
-	.globl install_obp_ticker
-install_obp_ticker:
-	nop
-	.globl ipc_init
-ipc_init:
-	nop
-	.globl jiffies
-jiffies:
-	nop
-	.globl kernel_enter_debugger
-kernel_enter_debugger:
-	nop
-	.globl kmalloc_init
-kmalloc_init:
-	nop
-	.globl kmem_cache_init
-kmem_cache_init:
-	nop
-	.globl kmem_cache_sizes_init
-kmem_cache_sizes_init:
-	nop
-	.globl kswapd
-kswapd:
-	nop
-	.globl linux32_syscall
-linux32_syscall:
-	nop
-	.globl linux64_syscall
-linux64_syscall:
-	nop
-	.globl lookup_fault
-lookup_fault:
-	nop
-	.globl mem_init
-mem_init:
-	nop
-	.globl memcpy
-memcpy:
-	nop
-	.globl mount_initrd
-mount_initrd:
-	nop
-	.globl name_cache_init
-name_cache_init:
-	nop
-	.globl netbsd_syscall
-netbsd_syscall:
-	nop
-	.globl no_scroll
-no_scroll:
-	nop
-	.globl paging_init
-paging_init:
-	nop
-	.globl panic
-panic:
-	nop
-	.globl panic_setup
-panic_setup:
-	nop
-	.globl printk
-printk:
-	nop
-	.globl prof_buffer
-prof_buffer:
-	nop
-	.globl prof_len
-prof_len:
-	nop
-	.globl prof_shift
-prof_shift:
-	nop
-	.globl pseudo_root
-pseudo_root:
-	nop
-	.globl rd_doload
-rd_doload:
-	nop
-	.globl rd_image_start
-rd_image_start:
-	nop
-	.globl rd_prompt
-rd_prompt:
-	nop
-	.globl rd_size
-rd_size:
-	nop
-	.globl reboot_command
-reboot_command:
-	nop
-	.globl reserve_setup
-reserve_setup:
-	nop
-	.globl sbus_init
-sbus_init:
-	nop
-	.globl sched_init
-sched_init:
-	nop
-	.globl scsi_luns_setup
-scsi_luns_setup:
-	nop
-	.globl serial_console
-serial_console:
-	nop
-	.globl set_palette
-set_palette:
-	nop
-	.globl setcc
-setcc:
-	nop
-	.globl setup_arch
-setup_arch:
-	nop
-	.globl simple_strtoul
-simple_strtoul:
-	nop
-	.globl sock_init
-sock_init:
-	nop
-	.globl solaris_syscall
-solaris_syscall:
-	nop
-	.globl sparc64_dtlb_fault
-sparc64_dtlb_fault:
-	nop
-	.globl sparc64_dtlb_refbit_catch
-sparc64_dtlb_refbit_catch:
-	nop
-	.globl sparc64_itlb_refbit_catch
-sparc64_itlb_refbit_catch:
-	nop
-	.globl sprintf
-sprintf:
-	nop
-	.globl st_setup
-st_setup:
-	nop
-	.globl strchr
-strchr:
-	nop
-	.globl strcmp
-strcmp:
-	nop
-	.globl strcpy
-strcpy:
-	nop
-	.globl strlen
-strlen:
-	nop
-	.globl strncpy
-strncpy:
-	nop
-	.globl sunos_syscall
-sunos_syscall:
-	nop
-	.globl swap_setup
-swap_setup:
-	nop
-	.globl sysctl_init
-sysctl_init:
-	nop
-	.globl time_init
-time_init:
-	nop
-	.globl trap_init
-trap_init:
-	nop
-	.globl vma_init
-vma_init:
-	nop
-	.globl vsprintf
-vsprintf:
-	nop
-	.globl schedule
-schedule:
-	nop
-	.globl getname
-getname:
-	nop
-	.globl do_execve
-do_execve:
-	nop
-	.globl putname
-putname:
-	nop
+	.align	8
+	.globl	__copy_user
+__copy_user:	retl;nop
+	.globl	__csum_partial_copy_sparc_generic
+__csum_partial_copy_sparc_generic:	retl;nop
+	.globl	_sigpause_common
+_sigpause_common:	retl;nop
+	.globl	_stext
+_stext:	retl;nop
+	.globl	auxio_register
+auxio_register:	retl;nop
+	.globl	bad_trap
+bad_trap:	retl;nop
+	.globl	bad_trap_tl1
+bad_trap_tl1:	retl;nop
+	.globl	breakpoint
+breakpoint:	retl;nop
+	.globl	breakpoint_trap
+breakpoint_trap:	retl;nop
+	.globl	csum_partial
+csum_partial:	retl;nop
+	.globl	ctx_free
+ctx_free:	.skip	32
+	.globl	ctx_list_pool
+ctx_list_pool:	.skip	32
+	.globl	ctx_used
+ctx_used:	.skip	32
+	.globl	destroy_context
+destroy_context:	retl;nop
+	.globl	disable_irq
+disable_irq:	retl;nop
+	.globl	do_cee
+do_cee:	retl;nop
+	.globl	do_cee_tl1
+do_cee_tl1:	retl;nop
+	.globl	do_dae
+do_dae:	retl;nop
+	.globl	do_dae_tl1
+do_dae_tl1:	retl;nop
+	.globl	do_dax
+do_dax:	retl;nop
+	.globl	do_dax_tl1
+do_dax_tl1:	retl;nop
+	.globl	do_div0
+do_div0:	retl;nop
+	.globl	do_div0_tl1
+do_div0_tl1:	retl;nop
+	.globl	do_fpdis
+do_fpdis:	retl;nop
+	.globl	do_fpdis_tl1
+do_fpdis_tl1:	retl;nop
+	.globl	do_fpieee
+do_fpieee:	retl;nop
+	.globl	do_fpieee_tl1
+do_fpieee_tl1:	retl;nop
+	.globl	do_fpother
+do_fpother:	retl;nop
+	.globl	do_fpother_tl1
+do_fpother_tl1:	retl;nop
+	.globl	do_gettimeofday
+do_gettimeofday:	retl;nop
+	.globl	do_iae
+do_iae:	retl;nop
+	.globl	do_iae_tl1
+do_iae_tl1:	retl;nop
+	.globl	do_iax
+do_iax:	retl;nop
+	.globl	do_iax_tl1
+do_iax_tl1:	retl;nop
+	.globl	do_ill
+do_ill:	retl;nop
+	.globl	do_ill_tl1
+do_ill_tl1:	retl;nop
+	.globl	do_irq
+do_irq:	retl;nop
+	.globl	do_irq_tl1
+do_irq_tl1:	retl;nop
+	.globl	do_ivec
+do_ivec:	retl;nop
+	.globl	do_ivec_tl1
+do_ivec_tl1:	retl;nop
+	.globl	do_lddfmna
+do_lddfmna:	retl;nop
+	.globl	do_lddfmna_tl1
+do_lddfmna_tl1:	retl;nop
+	.globl	do_mna
+do_mna:	retl;nop
+	.globl	do_mna_tl1
+do_mna_tl1:	retl;nop
+	.globl	do_paw
+do_paw:	retl;nop
+	.globl	do_paw_tl1
+do_paw_tl1:	retl;nop
+	.globl	do_privact
+do_privact:	retl;nop
+	.globl	do_privop
+do_privop:	retl;nop
+	.globl	do_settimeofday
+do_settimeofday:	retl;nop
+	.globl	do_signal
+do_signal:	retl;nop
+	.globl	do_stdfmna
+do_stdfmna:	retl;nop
+	.globl	do_stdfmna_tl1
+do_stdfmna_tl1:	retl;nop
+	.globl	do_tof
+do_tof:	retl;nop
+	.globl	do_tof_tl1
+do_tof_tl1:	retl;nop
+	.globl	do_vaw
+do_vaw:	retl;nop
+	.globl	do_vaw_tl1
+do_vaw_tl1:	retl;nop
+	.globl	enable_irq
+enable_irq:	retl;nop
+	.globl	floppy_hardint
+floppy_hardint:	retl;nop
+	.globl	flush_user_windows
+flush_user_windows:	retl;nop
+	.globl	free_irq
+free_irq:	retl;nop
+	.globl	get_cpuid
+get_cpuid:	retl;nop
+	.globl	get_irq_list
+get_irq_list:	retl;nop
+	.globl	getcc
+getcc:	retl;nop
+	.globl	halt
+halt:	retl;nop
+	.globl	indirect_syscall
+indirect_syscall:	retl;nop
+	.globl	init_IRQ
+init_IRQ:	retl;nop
+	.globl	install_linux_ticker
+install_linux_ticker:	retl;nop
+	.globl	install_obp_ticker
+install_obp_ticker:	retl;nop
+	.globl	iommu_init
+iommu_init:	retl;nop
+	.globl	linux32_syscall
+linux32_syscall:	retl;nop
+	.globl	linux64_syscall
+linux64_syscall:	retl;nop
+	.globl	linux_dbvec
+linux_dbvec:	retl;nop
+	.globl	linux_num_cpus
+linux_num_cpus:	retl;nop
+	.globl	load_mmu
+load_mmu:	retl;nop
+	.globl	mmu_get_scsi_one
+mmu_get_scsi_one:	retl;nop
+	.globl	mmu_get_scsi_sgl
+mmu_get_scsi_sgl:	retl;nop
+	.globl	mmu_info
+mmu_info:	retl;nop
+	.globl	mmu_lockarea
+mmu_lockarea:	retl;nop
+	.globl	mmu_release_scsi_one
+mmu_release_scsi_one:	retl;nop
+	.globl	mmu_release_scsi_sgl
+mmu_release_scsi_sgl:	retl;nop
+	.globl	mmu_unlockarea
+mmu_unlockarea:	retl;nop
+	.globl	mstk48t02_regs
+mstk48t02_regs:	retl;nop
+	.globl	netbsd_syscall
+netbsd_syscall:	retl;nop
+	.globl	probe_irq_off
+probe_irq_off:	retl;nop
+	.globl	probe_irq_on
+probe_irq_on:	retl;nop
+	.globl	request_fast_irq
+request_fast_irq:	retl;nop
+	.globl	request_irq
+request_irq:	retl;nop
+	.globl	setcc
+setcc:	retl;nop
+	.globl	solaris_syscall
+solaris_syscall:	retl;nop
+	.globl	sparc64_dtlb_fault
+sparc64_dtlb_fault:	retl;nop
+	.globl	sparc64_dtlb_refbit_catch
+sparc64_dtlb_refbit_catch:	retl;nop
+	.globl	sparc64_itlb_refbit_catch
+sparc64_itlb_refbit_catch:	retl;nop
+	.globl	spitfire_get_new_context
+spitfire_get_new_context:	retl;nop
+	.globl	sunos_mmap
+sunos_mmap:	retl;nop
+	.globl	sunos_syscall
+sunos_syscall:	retl;nop
+	.globl	svr4_getcontext
+svr4_getcontext:	retl;nop
+	.globl	svr4_setcontext
+svr4_setcontext:	retl;nop
+	.globl	swapper_pg_dir
+swapper_pg_dir:	retl;nop
+	.globl	switch_to
+switch_to:	retl;nop
+	.globl	sys_call_table
+sys_call_table:	retl;nop
+	.globl	time_init
+time_init:	retl;nop
+	.globl	translate_namei
+translate_namei:	retl;nop
+	.globl	translate_open_namei
+translate_open_namei:	retl;nop
+	.globl	sparc_brk
+sparc_brk:	retl;nop
+	.globl	sparc_sigaction
+sparc_sigaction:	retl;nop
+	.globl	sunos_accept
+sunos_accept:	retl;nop
+	.globl	sunos_audit
+sunos_audit:	retl;nop
+	.globl	sunos_brk
+sunos_brk:	retl;nop
+	.globl	sunos_execv
+sunos_execv:	retl;nop
+	.globl	sunos_fpathconf
+sunos_fpathconf:	retl;nop
+	.globl	sunos_getdents
+sunos_getdents:	retl;nop
+	.globl	sunos_getdirentries
+sunos_getdirentries:	retl;nop
+	.globl	sunos_getdomainname
+sunos_getdomainname:	retl;nop
+	.globl	sunos_getdtablesize
+sunos_getdtablesize:	retl;nop
+	.globl	sunos_getgid
+sunos_getgid:	retl;nop
+	.globl	sunos_gethostid
+sunos_gethostid:	retl;nop
+	.globl	sunos_getpid
+sunos_getpid:	retl;nop
+	.globl	sunos_getsockopt
+sunos_getsockopt:	retl;nop
+	.globl	sunos_getuid
+sunos_getuid:	retl;nop
+	.globl	sunos_indir
+sunos_indir:	retl;nop
+	.globl	sunos_ioctl
+sunos_ioctl:	retl;nop
+	.globl	sunos_killpg
+sunos_killpg:	retl;nop
+	.globl	sunos_madvise
+sunos_madvise:	retl;nop
+	.globl	sunos_mctl
+sunos_mctl:	retl;nop
+	.globl	sunos_mincore
+sunos_mincore:	retl;nop
+	.globl	sunos_mount
+sunos_mount:	retl;nop
+	.globl	sunos_nop
+sunos_nop:	retl;nop
+	.globl	sunos_nosys
+sunos_nosys:	retl;nop
+	.globl	sunos_open
+sunos_open:	retl;nop
+	.globl	sunos_pathconf
+sunos_pathconf:	retl;nop
+	.globl	sunos_poll
+sunos_poll:	retl;nop
+	.globl	sunos_read
+sunos_read:	retl;nop
+	.globl	sunos_readv
+sunos_readv:	retl;nop
+	.globl	sunos_recv
+sunos_recv:	retl;nop
+	.globl	sunos_sbrk
+sunos_sbrk:	retl;nop
+	.globl	sunos_select
+sunos_select:	retl;nop
+	.globl	sunos_semsys
+sunos_semsys:	retl;nop
+	.globl	sunos_send
+sunos_send:	retl;nop
+	.globl	sunos_setpgrp
+sunos_setpgrp:	retl;nop
+	.globl	sunos_setsockopt
+sunos_setsockopt:	retl;nop
+	.globl	sunos_shmsys
+sunos_shmsys:	retl;nop
+	.globl	sunos_sigaction
+sunos_sigaction:	retl;nop
+	.globl	sunos_sigblock
+sunos_sigblock:	retl;nop
+	.globl	sunos_sigsetmask
+sunos_sigsetmask:	retl;nop
+	.globl	sunos_sstk
+sunos_sstk:	retl;nop
+	.globl	sunos_sysconf
+sunos_sysconf:	retl;nop
+	.globl	sunos_uname
+sunos_uname:	retl;nop
+	.globl	sunos_vadvise
+sunos_vadvise:	retl;nop
+	.globl	sunos_wait4
+sunos_wait4:	retl;nop
+	.globl	sunos_write
+sunos_write:	retl;nop
+	.globl	sunos_writev
+sunos_writev:	retl;nop
+	.globl	sys_aplib
+sys_aplib:	retl;nop
+	.globl	sys_clone
+sys_clone:	retl;nop
+	.globl	sys_execve
+sys_execve:	retl;nop
+	.globl	sys_fork
+sys_fork:	retl;nop
+	.globl	sys_getpagesize
+sys_getpagesize:	retl;nop
+	.globl	sys_ipc
+sys_ipc:	retl;nop
+	.globl	sys_mmap
+sys_mmap:	retl;nop
+	.globl	sys_nis_syscall
+sys_nis_syscall:	retl;nop
+	.globl	sys_pipe
+sys_pipe:	retl;nop
+	.globl	sys_ptrace
+sys_ptrace:	retl;nop
+	.globl	sys_sigpause
+sys_sigpause:	retl;nop
+	.globl	sys_sigreturn
+sys_sigreturn:	retl;nop
+	.globl	sys_sigstack
+sys_sigstack:	retl;nop
+	.globl	sys_sigsuspend
+sys_sigsuspend:	retl;nop
+	.globl	sys_vfork
+sys_vfork:	retl;nop
+	.globl	syscall_trace
+syscall_trace:	retl;nop
+	.globl	sparc_ultra_mapioaddr
+sparc_ultra_mapioaddr:	retl;nop
+	.globl	sparc_ultra_unmapioaddr
+sparc_ultra_unmapioaddr:	retl;nop
+	.globl	mmu_map_dma_area
+mmu_map_dma_area:	retl;nop
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/head.S linux/arch/sparc64/kernel/head.S
--- v2.1.29/linux/arch/sparc64/kernel/head.S	Mon Mar 17 14:54:23 1997
+++ linux/arch/sparc64/kernel/head.S	Thu Mar 20 16:43:32 1997
@@ -1,8 +1,10 @@
-/* $Id: head.S,v 1.9 1997/02/26 11:09:25 jj Exp $
+/* $Id: head.S,v 1.17 1997/03/18 17:59:37 jj Exp $
  * head.S: Initial boot code for the Sparc64 port of Linux.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 David Sitsky (David.Sitsky@anu.edu.au)
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
  */
 
 #include <linux/version.h>
@@ -12,12 +14,14 @@
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/errno.h>
-
+#include <asm/lsu.h>
+	
 /* This section from from _start to sparc64_boot_end should fit into
-   0xfffff80000004000 to 0xfffff80000008000 and will be sharing space
-   with bootup_user_stack, which is from 0xfffff80000004000 to
-   0xfffff80000006000 and bootup_kernel_stack, which is from
-   0xfffff80000006000 to 0xfffff80000008000. */
+ * 0xffff.f800.0000.4000 to 0xffff.f800.0000.8000 and will be sharing space
+ * with bootup_user_stack, which is from 0xffff.f800.0000.4000 to
+ * 0xffff.f800.0000.6000 and bootup_kernel_stack, which is from
+ * 0xffff.f800.0000.6000 to 0xffff.f800.0000.8000. 
+ */
 
 	.text
 	.globl	start, _start
@@ -26,12 +30,15 @@
 bootup_user_stack:
 ! 0xfffff80000004000
 	b	sparc64_boot
-	 rdpr	%ver, %g1			/* Get VERSION register.	*/
+	 flushw					/* Flush register file.      */
 
 /* This stuff has to be in sync with SILO and other potential boot loaders
  * Fields should be kept upward compatible and whenever any change is made,
  * HdrS version should be incremented.
  */
+        .global root_flags, ram_flags, root_dev
+        .global ramdisk_image, ramdisk_size
+
         .ascii  "HdrS"
         .word   LINUX_VERSION_CODE
         .half   0x0201          /* HdrS version */
@@ -47,59 +54,80 @@
         .word   0
         .word   reboot_command
 
-sparc64_boot:
 	/* We must be careful, 32-bit OpenBOOT will get confused if it
 	 * tries to save away a register window to a 64-bit kernel
 	 * stack address.  Flush all windows, disable interrupts,
 	 * remap if necessary, jump onto kernel trap table, then kernel
 	 * stack, or else we die.
+	 *
+	 * PROM entry point is on %o4
 	 */
-	flushw					/* Flush register file.      */
+sparc64_boot:
+	mov	(LSU_CONTROL_IC|LSU_CONTROL_DC|LSU_CONTROL_IM|LSU_CONTROL_DM), %g1
+	stxa	%g1, [%g0] ASI_LSU_CONTROL
+
+	/*
+	 * Make sure we are in privileged mode, have address masking,
+         * using the ordinary globals and have enabled floating
+         * point.
+         */                                                                                
+
 	wrpr	%g0, 0xf, %pil			/* Interrupts off.           */
+	wrpr    %g0, (PSTATE_PRIV|PSTATE_PEF), %pstate
 
-	/* Remap ourselves to upper 64-bit addresses if necessary.
+	/* Check if we are mapped where we expect to be in virtual
+	 * memory.  The Solaris /boot elf format bootloader
+	 * will peek into our elf header and load us where
+	 * we want to be, otherwise we have to re-map.
 	 */
-	sethi	%uhi(PAGE_OFFSET), %g4
 current_pc:
-	rd	%pc, %g2
+	rd	%pc, %g3
+	sethi	%uhi(KERNBASE), %g4
 	sllx	%g4, 32, %g4
-	sethi	%hi(current_pc), %g3
-	or	%g3, %lo(current_pc), %g3
-	add	%g4, %g3, %g3
-	cmp	%g3, %g2
-	be	go_to_highmem
-	 nop
 
-	/* Remap ourselves into high addresses. */
-	sethi	%uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5
-	sllx	%g5, 32, %g5
-	or	%g5, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W | _PAGE_G | _PAGE_L), %g5
+	/* Check the run time program counter. */
 
-	/* Be real fucking anal... */
-	stxa	%g0, [%g4] ASI_IMMU_DEMAP
-	stxa	%g0, [%g4] ASI_DMMU_DEMAP
-	membar	#Sync
-	flush	%g4
+	set	current_pc, %g5
+	add	%g5, %g4, %g5
+	cmp	%g3, %g5
+	be	%xcc,sun4u_init
+	 nop
 
-	mov	TLB_TAG_ACCESS, %g6
-	stxa	%g4, [%g6] ASI_IMMU
-	stxa	%g5, [%g0] ASI_ITLB_DATA_IN
-	membar	#Sync
-	flush	%g4
+create_mappings:
+	/* %g5 holds the tlb data */
+        sethi   %uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5
+        sllx    %g5, 32, %g5
+        or      %g5, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W | _PAGE_G), %g5
+
+	/* We aren't mapped in at KERNBASE, so we need to create
+	 * an I and D tlb entry to map KERNBASE to 0.  Both entries
+	 * are 4 Megs mappings and are locked in.
+	 */
+	set	TLB_TAG_ACCESS, %g3           /* 0x30 */
 
-	stxa	%g4, [%g6] ASI_DMMU
-	stxa	%g5, [%g0] ASI_DTLB_DATA_IN
-	membar	#Sync
-	flush	%g4
+	stxa	%g4, [%g3] ASI_IMMU           /* 0x50 */
+	stxa	%g5, [%g0] ASI_ITLB_DATA_IN   /* 0x54 */
+	membar  #Sync
+
+	/* Put KERNBASE into the I/D Tag Access Register (TAR) */
+	stxa	%g4, [%g3] ASI_DMMU           /* 0x58 */
+	stxa    %g5, [%g0] ASI_DTLB_DATA_IN
+	membar  #Sync
 
-/* FIXME: Should clean here the page @ phys. 0 and map one page @ */
+	nop
+	nop	
+	membar	#Sync
+	
+	ba,pt	%xcc, go_to_highmem
+	 nop
 
+/* Now do a non-relative jump so that PC is in high-memory */
 go_to_highmem:
-	jmpl	%g3 + (execute_in_high_mem - current_pc), %g0
+	set	sun4u_init, %g1
+	jmpl     %g1 + %g4, %g0
 	 nop
 
-execute_in_high_mem:
-
+sun4u_init:
 	/* Remap our prom interface code */
 	sethi	%hi(__p1275_loc), %g7
 	or	%g7, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W | _PAGE_G | _PAGE_L), %g7
@@ -114,29 +142,40 @@
 	stxa	%g3, [%g6] ASI_IMMU
 	stxa	%g5, [%g0] ASI_ITLB_DATA_IN
 	membar	#Sync
-	flush	%g3
+
 	stxa	%g3, [%g6] ASI_DMMU
 	stxa	%g5, [%g0] ASI_DTLB_DATA_IN
 	membar	#Sync
 	flush	%g3
 
-	sethi	%hi(nwindows), %g7
-	and	%g1, VERS_MAXWIN, %g5
-	add	%g7, %lo(nwindows), %g7
+	nop
+	nop
+	membar	#Sync
+	
+	/* Compute the number of windows in this machine
+	 * store this in nwindows and nwindowsm1
+	 */
+	rdpr	%ver, %g1			/* Get VERSION register.	*/
+	sethi   %hi(nwindows), %g2
+	and     %g1, VERS_MAXWIN, %g5
+	or	%g2,%lo(nwindows),%g2
 	add	%g5, 1, %g6
-	add	%g7, (nwindows - nwindowsm1), %g3
-	stx	%g6, [%g7 + %g4]
-	stx	%g5, [%g3 + %g4]
-	mov	%sp, %o1			! second argument to prom_init
+	add	%g2, (nwindows - nwindowsm1), %g3
+	stx     %g6, [%g2 + %g4]
+	stx     %g5, [%g3 + %g4]
+
 	sethi	%hi(init_task), %g6
 	or	%g6, %lo(init_task), %g6
 	add	%g6, %g4, %g6			! g6 usage is fixed as well
+	mov	%sp, %l6
+	mov	%o4, %l7
 
-/* FIXME: Initialize MMU globals??? */
-
+#if 0
+/* This is to dangerous for now... To many traps unfilled yet... */
 	sethi	%hi(sparc64_ttable_tl0), %g5
 	add	%g5, %g4, %g5
 	wrpr	%g5, %tba
+#endif
 
 	sethi	%hi(bootup_kernel_stack + 0x2000 - STACK_BIAS - REGWIN_SZ), %g5
 	or	%g5, %lo(bootup_kernel_stack + 0x2000 - STACK_BIAS - REGWIN_SZ), %g5
@@ -145,9 +184,36 @@
 	wrpr	%g0, PSTATE_PEF | PSTATE_PRIV, %pstate
 	wrpr	%g0, 0, %wstate
 	wrpr	%g0, 0x0, %tl
+	fzero	%f48
+	fzero	%f50
+	fzero	%f52
+	fzero	%f54
+	fzero	%f56
+	fzero	%f58
+	fzero	%f60
+	fzero	%f62
+
+	/* Clear the bss */
+	sethi	%hi(8191), %l2
+	or	%l2, %lo(8191), %l2
+	sethi	%hi(__bss_start), %l0
+	or	%l0, %lo(__bss_start), %l0
+	sethi	%hi(_end), %l1
+	or	%l1, %lo(_end), %l1
+	add	%l1, %l2, %l1
+	andn	%l1, %l2, %l1
+	add	%l2, 1, %l2
+	add	%l0, %g4, %o0
+1:
+	call	bzero_1page
+	 add	%l0, %l2, %l0
+	cmp	%l0, %l1
+	blu,pt	%xcc, 1b
+	 add	%l0, %g4, %o0
 
+	mov	%l6, %o1			! OpenPROM stack
 	call	prom_init
-	 mov	%o4, %o0			! OpenPROM cif handler
+	 mov	%l7, %o0			! OpenPROM cif handler
 
 	/* Off we go.... */
 	call	start_kernel
@@ -165,18 +231,11 @@
 
 #include "ttable.S"
 
-        .global root_flags
-        .global ram_flags
-        .global root_dev
-        .global ramdisk_image
-        .global ramdisk_size
-
 	.data
 	.align	8
 	.globl	nwindows, nwindowsm1 
 nwindows:	.xword	0
 nwindowsm1:	.xword	0
-
 	.section	".fixup",#alloc,#execinstr
 	.globl	__ret_efault
 __ret_efault:
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/ioport.c linux/arch/sparc64/kernel/ioport.c
--- v2.1.29/linux/arch/sparc64/kernel/ioport.c	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/kernel/ioport.c	Thu Mar 20 16:43:32 1997
@@ -1,4 +1,4 @@
-/* $Id: ioport.c,v 1.1 1996/12/28 18:39:39 davem Exp $
+/* $Id: ioport.c,v 1.2 1997/03/18 17:59:31 jj Exp $
  * ioport.c:  Simple io mapping allocator.
  *
  * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu)
@@ -29,21 +29,21 @@
  * to use your own mapping, but in practice this should not be used.
  *
  * Input:
- *  address: the obio address to map
+ *  address: Physical address to map
  *  virtual: if non zero, specifies a fixed virtual address where
  *           the mapping should take place.
  *  len:     the length of the mapping
- *  bus_type: The bus on which this io area sits.
+ *  bus_type: Optional high word of physical address.
  *
  * Returns:
  *  The virtual address where the mapping actually took place.
  */
 
 void *sparc_alloc_io (void *address, void *virtual, int len, char *name,
-		      int bus_type, int rdonly)
+		      unsigned bus_type, int rdonly)
 {
 	unsigned long vaddr, base_address;
-	unsigned long addr = (unsigned long) address;
+	unsigned long addr = ((unsigned long) address) + (((unsigned long) bus_type) << 32);
 	unsigned long offset = (addr & (~PAGE_MASK));
 
 	if (virtual) {
@@ -74,7 +74,7 @@
 	base_address = vaddr;
 	/* Do the actual mapping */
 	for (; len > 0; len -= PAGE_SIZE) {
-		mapioaddr(addr, vaddr, bus_type, rdonly);
+		mapioaddr(addr, vaddr, rdonly);
 		vaddr += PAGE_SIZE;
 		addr += PAGE_SIZE;
 	}
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/rtrap.S linux/arch/sparc64/kernel/rtrap.S
--- v2.1.29/linux/arch/sparc64/kernel/rtrap.S	Mon Mar 17 14:54:23 1997
+++ linux/arch/sparc64/kernel/rtrap.S	Thu Mar 20 16:43:32 1997
@@ -1,7 +1,7 @@
-/* $Id: rtrap.S,v 1.2 1997/02/26 11:09:25 jj Exp $
- * rtrap.S: Preparing for entry into the kernel on Sparc V9.
+/* $Id: rtrap.S,v 1.4 1997/03/13 16:24:55 jj Exp $
+ * rtrap.S: Preparing for return from trap on Sparc V9.
  *
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <asm/pstate.h>
@@ -11,4 +11,84 @@
 	.align	4
 	.globl	rtrap
 rtrap:
-	/*not*/ done /*yet*/
+	sethi	%hi(intr_count), %l0
+	or	%l0, %lo(intr_count), %l0
+	ld	[%l0 + %g4], %l1
+	sethi	%hi(bh_active), %l2
+	brz,pt	%l1, 2f
+	 or	%l2, %lo(bh_active), %l2
+	sethi	%hi(bh_mask), %l1
+	or	%l1, %lo(bh_mask), %l1
+1:
+	ldx	[%l2 + %g4], %l3
+	ldx	[%l1 + %g4], %l4
+	andcc	%l3, %l4, %g0
+	be,pt	%xcc, 2f
+	 mov	1, %l7
+	call	do_bottom_half
+	 st	%l7, [%l0 + %g4]
+	ba,pt	%xcc, 1b
+	 st	%g0, [%l0 + %g4]	
+2:
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_TSTATE], %l1
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_TPC], %l2
+	andcc	%l1, TSTATE_PRIV, %l3
+	rdpr	%pstate, %l7
+	be,pt	%icc, to_user
+	 andn	%l7, PSTATE_IE, %l7
+3:
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_G1], %g1
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_G2], %g2
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_G3], %g3
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_G4], %g4
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_G5], %g5
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_G6], %g6
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_G7], %g7
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_I0], %i0
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_I1], %i1
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_I2], %i2
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_I3], %i3
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_I4], %i4
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_I5], %i5
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_I6], %i6
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_I7], %i7
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_Y], %o3
+	ldx	[%sp + STACK_BIAS + REGWIN_SZ + PT_TNPC], %o2
+	wr	%o3, %g0, %y
+	wrpr	%l7, %g0, %pstate
+	wrpr	%g0, 1, %tl
+	wrpr	%l1, %g0, %tstate
+	wrpr	%l2, %g0, %tpc
+	brnz,pn	%l3, 1f
+	 wrpr	%o2, %g0, %tnpc
+	/* we came here from to_user, ie. we have now AG */
+	restore
+	rdpr	%wstate, %g1
+	rdpr	%otherwin, %g2
+	srl	%g1, 3, %g1
+	wrpr	%g2, %g0, %canrestore
+	wrpr	%g1, %g0, %wstate
+	wrpr	%g0, %g0, %otherwin
+	retry
+1:
+	restore
+	retry
+to_user:
+	sethi	%hi(need_resched), %l0
+	or	%l0, %lo(need_resched), %l0
+	ld	[%l0 + %g4], %l0
+	wrpr	%o4, PSTATE_IE, %pstate
+	brz,pt	%l0, 1f
+	 ldx	[%g6 + AOFF_task_signal], %l0
+	call	schedule
+	 nop
+1:
+	ldx	[%g6 + AOFF_task_blocked], %o0
+	or	%l7, PSTATE_AG, %l7		! Will need this for setting back wstate
+	andcc	%l0, %o0, %g0
+	be,pt	%xcc, 3b
+	 mov	%l5, %o2  
+	mov	%l6, %o3
+	add	%sp, STACK_BIAS + REGWIN_SZ, %o1
+	call	do_signal
+	 add	%o7, 3b-.-4, %o7
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/setup.c linux/arch/sparc64/kernel/setup.c
--- v2.1.29/linux/arch/sparc64/kernel/setup.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/kernel/setup.c	Thu Mar 20 16:43:32 1997
@@ -0,0 +1,416 @@
+/*  $Id: setup.c,v 1.1 1997/03/11 17:37:04 jj Exp $
+ *  linux/arch/sparc64/kernel/setup.c
+ *
+ *  Copyright (C) 1995,1996  David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1997       Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <asm/smp.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+#include <linux/delay.h>
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+#include <linux/init.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/oplib.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/idprom.h>
+
+struct screen_info screen_info = {
+	0, 0,			/* orig-x, orig-y */
+	{ 0, 0, },		/* unused */
+	0,			/* orig-video-page */
+	0,			/* orig-video-mode */
+	128,			/* orig-video-cols */
+	0,0,0,			/* ega_ax, ega_bx, ega_cx */
+	54,			/* orig-video-lines */
+	0,                      /* orig-video-isVGA */
+	16                      /* orig-video-points */
+};
+
+unsigned int phys_bytes_of_ram, end_of_phys_memory;
+
+unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)
+{
+	return memory_start;
+}
+
+/* Typing sync at the prom prompt calls the function pointed to by
+ * the sync callback which I set to the following function.
+ * This should sync all filesystems and return, for now it just
+ * prints out pretty messages and returns.
+ */
+
+extern unsigned long sparc64_ttable_tl0;
+extern void breakpoint(void);
+#if CONFIG_SUN_CONSOLE
+extern void console_restore_palette(void);
+#endif
+asmlinkage void sys_sync(void);	/* it's really int */
+
+/* Pretty sick eh? */
+void prom_sync_me(long *args)
+{
+	unsigned long prom_tba, flags;
+
+	save_and_cli(flags);
+	__asm__ __volatile__("flushw; rdpr %%tba, %0\n\t" : "=r" (prom_tba));
+	__asm__ __volatile__("wrpr %0, 0x0, %%tba\n\t" : : "r" (&sparc64_ttable_tl0));
+
+#ifdef CONFIG_SUN_CONSOLE
+        console_restore_palette ();
+#endif
+	prom_printf("PROM SYNC COMMAND...\n");
+	show_free_areas();
+	if(current->pid != 0) {
+		sti();
+		sys_sync();
+		cli();
+	}
+	prom_printf("Returning to prom\n");
+
+	__asm__ __volatile__("flushw; wrpr %0, 0x0, %%tba\n\t" : : "r" (prom_tba));
+	restore_flags(flags);
+
+	return;
+}
+
+extern void rs_kgdb_hook(int tty_num); /* sparc/serial.c */
+
+unsigned int boot_flags;
+#define BOOTME_DEBUG  0x1
+#define BOOTME_SINGLE 0x2
+#define BOOTME_KGDB   0x4
+
+#ifdef CONFIG_SUN_CONSOLE
+extern char *console_fb_path;
+static int console_fb = 0;
+#endif
+static unsigned long memory_size = 0;
+
+void kernel_enter_debugger(void)
+{
+	if (boot_flags & BOOTME_KGDB) {
+		printk("KGDB: Entered\n");
+		breakpoint();
+	}
+}
+
+int obp_system_intr(void)
+{
+	if (boot_flags & BOOTME_KGDB) {
+		printk("KGDB: system interrupted\n");
+		breakpoint();
+		return 1;
+	}
+	if (boot_flags & BOOTME_DEBUG) {
+		printk("OBP: system interrupted\n");
+		prom_halt();
+		return 1;
+	}
+	return 0;
+}
+
+/* 
+ * Process kernel command line switches that are specific to the
+ * SPARC or that require special low-level processing.
+ */
+__initfunc(static void process_switch(char c))
+{
+	switch (c) {
+	case 'd':
+		boot_flags |= BOOTME_DEBUG;
+		break;
+	case 's':
+		boot_flags |= BOOTME_SINGLE;
+		break;
+	case 'h':
+		prom_printf("boot_flags_init: Halt!\n");
+		halt();
+		break;
+	default:
+		printk("Unknown boot switch (-%c)\n", c);
+		break;
+	}
+}
+
+__initfunc(static void boot_flags_init(char *commands))
+{
+	while (*commands) {
+		/* Move to the start of the next "argument". */
+		while (*commands && *commands == ' ')
+			commands++;
+
+		/* Process any command switches, otherwise skip it. */
+		if (*commands == '\0')
+			break;
+		else if (*commands == '-') {
+			commands++;
+			while (*commands && *commands != ' ')
+				process_switch(*commands++);
+		} else if (strlen(commands) >= 9
+			   && !strncmp(commands, "kgdb=tty", 8)) {
+			boot_flags |= BOOTME_KGDB;
+			switch (commands[8]) {
+#ifdef CONFIG_SUN_SERIAL
+			case 'a':
+				rs_kgdb_hook(0);
+				prom_printf("KGDB: Using serial line /dev/ttya.\n");
+				break;
+			case 'b':
+				rs_kgdb_hook(1);
+				prom_printf("KGDB: Using serial line /dev/ttyb.\n");
+				break;
+#endif
+			default:
+				printk("KGDB: Unknown tty line.\n");
+				boot_flags &= ~BOOTME_KGDB;
+				break;
+			}
+			commands += 9;
+		} else {
+#if CONFIG_SUN_CONSOLE
+			if (!strncmp(commands, "console=", 8)) {
+				commands += 8;
+				if (!strncmp (commands, "ttya", 4)) {
+					console_fb = 2;
+					prom_printf ("Using /dev/ttya as console.\n");
+				} else if (!strncmp (commands, "ttyb", 4)) {
+					console_fb = 3;
+					prom_printf ("Using /dev/ttyb as console.\n");
+				} else {
+					console_fb = 1;
+					console_fb_path = commands;
+				}
+			} else
+#endif
+			if (!strncmp(commands, "mem=", 4)) {
+				/*
+				 * "mem=XXX[kKmM] overrides the PROM-reported
+				 * memory size.
+				 */
+				memory_size = simple_strtoul(commands + 4,
+							     &commands, 0);
+				if (*commands == 'K' || *commands == 'k') {
+					memory_size <<= 10;
+					commands++;
+				} else if (*commands=='M' || *commands=='m') {
+					memory_size <<= 20;
+					commands++;
+				}
+			}
+			while (*commands && *commands != ' ')
+				commands++;
+		}
+	}
+}
+
+extern int prom_probe_memory(void);
+extern unsigned long start, end;
+extern void panic_setup(char *, int *);
+extern unsigned long sun_serial_setup(unsigned long);
+
+extern unsigned short root_flags;
+extern unsigned short root_dev;
+extern unsigned short ram_flags;
+extern unsigned ramdisk_image;
+extern unsigned ramdisk_size;
+#define RAMDISK_IMAGE_START_MASK	0x07FF
+#define RAMDISK_PROMPT_FLAG		0x8000
+#define RAMDISK_LOAD_FLAG		0x4000
+
+extern int root_mountflags;
+
+extern void register_console(void (*proc)(const char *));
+
+char saved_command_line[256];
+char reboot_command[256];
+
+__initfunc(void setup_arch(char **cmdline_p,
+	unsigned long * memory_start_p, unsigned long * memory_end_p))
+{
+	int total, i;
+
+	/* Initialize PROM console and command line. */
+	*cmdline_p = prom_getbootargs();
+	strcpy(saved_command_line, *cmdline_p);
+
+	printk("ARCH: SUN4U\n");
+
+	boot_flags_init(*cmdline_p);
+#if 0	
+	if((boot_flags&BOOTME_DEBUG) && (linux_dbvec!=0) && 
+	   ((*(short *)linux_dbvec) != -1)) {
+		printk("Booted under KADB. Syncing trap table.\n");
+		(*(linux_dbvec->teach_debugger))();
+	}
+	if((boot_flags & BOOTME_KGDB)) {
+		set_debug_traps();
+		prom_printf ("Breakpoint!\n");
+		breakpoint();
+	}
+#endif	
+
+	idprom_init();
+	load_mmu();
+	total = prom_probe_memory();
+	*memory_start_p = (((unsigned long) &end));
+
+	for(i=0; sp_banks[i].num_bytes != 0; i++) {
+		end_of_phys_memory = sp_banks[i].base_addr +
+			sp_banks[i].num_bytes;
+		if (memory_size) {
+			if (end_of_phys_memory > memory_size) {
+				sp_banks[i].num_bytes -=
+					(end_of_phys_memory - memory_size);
+				end_of_phys_memory = memory_size;
+				sp_banks[++i].base_addr = 0xdeadbeef;
+				sp_banks[i].num_bytes = 0;
+			}
+		}
+	}
+	prom_setsync(prom_sync_me);
+
+	*memory_end_p = (end_of_phys_memory + KERNBASE);
+	if (!root_flags)
+		root_mountflags &= ~MS_RDONLY;
+	ROOT_DEV = to_kdev_t(root_dev);
+#ifdef CONFIG_BLK_DEV_RAM
+	rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK;
+	rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
+	rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);	
+#endif
+#ifdef CONFIG_BLK_DEV_INITRD
+	if (ramdisk_image) {
+		initrd_start = ramdisk_image;
+		if (initrd_start < KERNBASE) initrd_start += KERNBASE;
+		initrd_end = initrd_start + ramdisk_size;
+		if (initrd_end > *memory_end_p) {
+			printk(KERN_CRIT "initrd extends beyond end of memory "
+		                 	 "(0x%016lx > 0x%016lx)\ndisabling initrd\n",
+		       			 initrd_end,*memory_end_p);
+			initrd_start = 0;
+		}
+		if (initrd_start >= *memory_start_p && initrd_start < *memory_start_p + 2 * PAGE_SIZE) {
+			initrd_below_start_ok = 1;
+			*memory_start_p = PAGE_ALIGN (initrd_end);
+		}
+	}
+#endif	
+
+	/* Due to stack alignment restrictions and assumptions... */
+#if 0	
+	init_task.mm->mmap->vm_page_prot = PAGE_SHARED;
+	init_task.mm->mmap->vm_start = KERNBASE;
+	init_task.mm->mmap->vm_end = *memory_end_p;
+	init_task.mm->context = (unsigned long) NO_CONTEXT;
+#endif	
+
+#ifdef CONFIG_SUN_SERIAL
+	*memory_start_p = sun_serial_setup(*memory_start_p); /* set this up ASAP */
+#endif
+	{
+		extern int serial_console;  /* in console.c, of course */
+#if !CONFIG_SUN_SERIAL
+		serial_console = 0;
+#else
+		switch (console_fb) {
+		case 0: /* Let get our io devices from prom */
+			{
+				int idev = prom_query_input_device();
+				int odev = prom_query_output_device();
+				if (idev == PROMDEV_IKBD && odev == PROMDEV_OSCREEN) {
+					serial_console = 0;
+				} else if (idev == PROMDEV_ITTYA && odev == PROMDEV_OTTYA) {
+					serial_console = 1;
+				} else if (idev == PROMDEV_ITTYB && odev == PROMDEV_OTTYB) {
+					serial_console = 2;
+				} else {
+					prom_printf("Inconsistent console\n");
+					prom_halt();
+				}
+			}
+			break;
+		case 1: serial_console = 0; break; /* Force one of the framebuffers as console */
+		case 2: serial_console = 1; break; /* Force ttya as console */
+		case 3: serial_console = 2; break; /* Force ttyb as console */
+		}
+#endif
+	}
+}
+
+asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on)
+{
+	return -EIO;
+}
+
+/* BUFFER is PAGE_SIZE bytes long. */
+
+extern char *sparc_cpu_type[];
+extern char *sparc_fpu_type[];
+
+extern char *smp_info(void);
+extern char *mmu_info(void);
+
+int get_cpuinfo(char *buffer)
+{
+	int cpuid=get_cpuid();
+
+	return sprintf(buffer, "cpu\t\t: %s\n"
+            "fpu\t\t: %s\n"
+            "promlib\t\t: Version 3 Revision %d\n"
+            "prom\t\t: %d.%d.%d\n"
+            "type\t\t: sun4u\n"
+	    "ncpus probed\t: %d\n"
+	    "ncpus active\t: %d\n"
+#ifndef __SMP__
+            "BogoMips\t: %lu.%02lu\n"
+#else
+	    "Cpu0Bogo\t: %lu.%02lu\n"
+	    "Cpu1Bogo\t: %lu.%02lu\n"
+	    "Cpu2Bogo\t: %lu.%02lu\n"
+	    "Cpu3Bogo\t: %lu.%02lu\n"
+#endif
+	    "%s"
+#ifdef __SMP__
+	    "%s"
+#endif
+	    ,
+            sparc_cpu_type[cpuid],
+            sparc_fpu_type[cpuid],
+            prom_rev, prom_prev >> 16, (prom_prev >> 8) & 0xff, prom_prev & 0xff,
+	    linux_num_cpus, smp_num_cpus,
+#ifndef __SMP__
+            loops_per_sec/500000, (loops_per_sec/5000) % 100,
+#else
+	    cpu_data[0].udelay_val/500000, (cpu_data[0].udelay_val/5000)%100,
+	    cpu_data[1].udelay_val/500000, (cpu_data[1].udelay_val/5000)%100,
+	    cpu_data[2].udelay_val/500000, (cpu_data[2].udelay_val/5000)%100,
+	    cpu_data[3].udelay_val/500000, (cpu_data[3].udelay_val/5000)%100,
+#endif
+	    mmu_info()
+#ifdef __SMP__
+	    , smp_info()
+#endif
+            );
+
+}
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/sparc64_ksyms.c linux/arch/sparc64/kernel/sparc64_ksyms.c
--- v2.1.29/linux/arch/sparc64/kernel/sparc64_ksyms.c	Mon Mar 17 14:54:23 1997
+++ linux/arch/sparc64/kernel/sparc64_ksyms.c	Thu Mar 20 16:43:32 1997
@@ -1,4 +1,4 @@
-/* $Id: sparc64_ksyms.c,v 1.1 1997/03/03 16:51:45 jj Exp $
+/* $Id: sparc64_ksyms.c,v 1.3 1997/03/18 17:59:10 jj Exp $
  * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -74,24 +74,25 @@
 #endif
 EXPORT_SYMBOL_PRIVATE(_lock_kernel);
 EXPORT_SYMBOL_PRIVATE(_unlock_kernel);
-EXPORT_SYMBOL(page_offset);
-EXPORT_SYMBOL(stack_top);
 
 EXPORT_SYMBOL(mstk48t02_regs);
 EXPORT_SYMBOL(request_fast_irq);
 EXPORT_SYMBOL(sparc_alloc_io);
 EXPORT_SYMBOL(sparc_free_io);
+#if 0
 EXPORT_SYMBOL(io_remap_page_range);
-EXPORT_SYMBOL(mmu_v2p);
 EXPORT_SYMBOL(mmu_unlockarea);
 EXPORT_SYMBOL(mmu_lockarea);
 EXPORT_SYMBOL(mmu_get_scsi_sgl);
 EXPORT_SYMBOL(mmu_get_scsi_one);
 EXPORT_SYMBOL(mmu_release_scsi_sgl);
 EXPORT_SYMBOL(mmu_release_scsi_one);
+#endif
 EXPORT_SYMBOL(sparc_dvma_malloc);
+#if 0
 EXPORT_SYMBOL(sun4c_unmapioaddr);
 EXPORT_SYMBOL(srmmu_unmapioaddr);
+#endif
 #if CONFIG_SBUS
 EXPORT_SYMBOL(SBus_chain);
 EXPORT_SYMBOL(dma_chain);
@@ -119,14 +120,12 @@
 EXPORT_SYMBOL(prom_node_has_property);
 EXPORT_SYMBOL(prom_setprop);
 EXPORT_SYMBOL(prom_getbootargs);
-EXPORT_SYMBOL(prom_apply_obio_ranges);
 EXPORT_SYMBOL(prom_getname);
 EXPORT_SYMBOL(prom_feval);
 EXPORT_SYMBOL(prom_getstring);
 EXPORT_SYMBOL(prom_apply_sbus_ranges);
 EXPORT_SYMBOL(prom_getint);
 EXPORT_SYMBOL(prom_getintdefault);
-EXPORT_SYMBOL(romvec);
 EXPORT_SYMBOL(__prom_getchild);
 EXPORT_SYMBOL(__prom_getsibling);
 
@@ -176,4 +175,3 @@
 EXPORT_SYMBOL_NOVERS(memcpy);
 EXPORT_SYMBOL_NOVERS(memset);
 EXPORT_SYMBOL_NOVERS(memmove);
-EXPORT_SYMBOL_NOVERS(__ashrdi3);
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/systbls.S linux/arch/sparc64/kernel/systbls.S
--- v2.1.29/linux/arch/sparc64/kernel/systbls.S	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/kernel/systbls.S	Thu Mar 20 16:43:32 1997
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.1 1996/12/28 18:39:36 davem Exp $
+/* $Id: systbls.S,v 1.2 1997/03/18 17:59:03 jj Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -14,309 +14,207 @@
 
 	/* First, the 32-bit Linux native syscall table. */
 
-	.globl C_LABEL(sys_call_table32)
-C_LABEL(sys_call_table32):
-/*0*/	.xword C_LABEL(sys_setup), C_LABEL(sys_exit), C_LABEL(sys_fork)
-	.xword C_LABEL(sys_read), C_LABEL(sys_write)
-/*5*/	.xword C_LABEL(sys_open), C_LABEL(sys_close), C_LABEL(sys_wait4)
-	.xword C_LABEL(sys_creat), C_LABEL(sys_link)
-/*10*/  .xword C_LABEL(sys_unlink), C_LABEL(sunos_execv), C_LABEL(sys_chdir)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_mknod)
-/*15*/	.xword C_LABEL(sys_chmod), C_LABEL(sys_chown), C_LABEL(sparc_brk)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_lseek)
-/*20*/	.xword C_LABEL(sys_getpid), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_setuid), C_LABEL(sys_getuid)
-/*25*/	.xword C_LABEL(sys_time), C_LABEL(sys_ptrace), C_LABEL(sys_alarm)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_pause)
-/*30*/	.xword C_LABEL(sys_utime), C_LABEL(sys_stty), C_LABEL(sys_gtty)
-	.xword C_LABEL(sys_access), C_LABEL(sys_nice), C_LABEL(sys_ftime)
-	.xword C_LABEL(sys_sync), C_LABEL(sys_kill), C_LABEL(sys_newstat)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_newlstat), C_LABEL(sys_dup)
-	.xword C_LABEL(sys_pipe), C_LABEL(sys_times), C_LABEL(sys_profil)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_setgid), C_LABEL(sys_getgid)
-	.xword C_LABEL(sys_signal), C_LABEL(sys_geteuid)
-/*50*/	.xword C_LABEL(sys_getegid), C_LABEL(sys_acct), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_ioctl), C_LABEL(sys_reboot)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_symlink), C_LABEL(sys_readlink)
-	.xword C_LABEL(sys_execve), C_LABEL(sys_umask), C_LABEL(sys_chroot)
-	.xword C_LABEL(sys_newfstat), C_LABEL(sys_nis_syscall), C_LABEL(sys_getpagesize)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_vfork), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_mmap), C_LABEL(sys_nis_syscall), C_LABEL(sys_munmap)
-	.xword C_LABEL(sys_mprotect), C_LABEL(sys_nis_syscall), C_LABEL(sys_vhangup)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_getgroups)
-	.xword C_LABEL(sys_setgroups), C_LABEL(sys_getpgrp), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_setitimer), C_LABEL(sys_nis_syscall), C_LABEL(sys_swapon)
-	.xword C_LABEL(sys_getitimer), C_LABEL(sys_nis_syscall), C_LABEL(sys_sethostname)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_dup2), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_fcntl), C_LABEL(sys_select), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-/*100*/	.xword C_LABEL(sys_getpriority), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_gettimeofday), C_LABEL(sys_getrusage)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_readv)
-	.xword C_LABEL(sys_writev), C_LABEL(sys_settimeofday), C_LABEL(sys_fchown)
-	.xword C_LABEL(sys_fchmod), C_LABEL(sys_nis_syscall), C_LABEL(sys_setreuid)
-	.xword C_LABEL(sys_setregid), C_LABEL(sys_rename), C_LABEL(sys_truncate)
-	.xword C_LABEL(sys_ftruncate), C_LABEL(sys_flock), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_mkdir), C_LABEL(sys_rmdir), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_getrlimit)
-	.xword C_LABEL(sys_setrlimit), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-/*150*/	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_statfs), C_LABEL(sys_fstatfs)
-	.xword C_LABEL(sys_umount), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_setdomainname)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_quotactl), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_mount), C_LABEL(sys_ustat), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_getdents), C_LABEL(sys_setsid)
-	.xword C_LABEL(sys_fchdir), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_sigpending), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_setpgid), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_newuname), C_LABEL(sys_init_module)
-	.xword C_LABEL(sys_personality), C_LABEL(sys_prof), C_LABEL(sys_break)
-	.xword C_LABEL(sys_lock), C_LABEL(sys_mpx), C_LABEL(sys_ulimit)
-	.xword C_LABEL(sys_getppid), C_LABEL(sparc_sigaction), C_LABEL(sys_sgetmask)
-/*200*/	.xword C_LABEL(sys_ssetmask), C_LABEL(sys_sigsuspend), C_LABEL(sys_newlstat)
-	.xword C_LABEL(sys_uselib), C_LABEL(old_readdir), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_socketcall), C_LABEL(sys_syslog), C_LABEL(sys_olduname)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_idle), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_waitpid), C_LABEL(sys_swapoff), C_LABEL(sys_sysinfo)
-	.xword C_LABEL(sys_ipc), C_LABEL(sys_sigreturn), C_LABEL(sys_clone)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_adjtimex), C_LABEL(sys_sigprocmask)
-	.xword C_LABEL(sys_create_module), C_LABEL(sys_delete_module)
-	.xword C_LABEL(sys_get_kernel_syms), C_LABEL(sys_getpgid), C_LABEL(sys_bdflush)
-	.xword C_LABEL(sys_sysfs), C_LABEL(sys_nis_syscall), C_LABEL(sys_setfsuid)
-	.xword C_LABEL(sys_setfsgid), C_LABEL(sys_llseek), C_LABEL(sys_time)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_stime), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_llseek)
-	/* "We are the Knights of the Forest of Ni!!" */
-	.xword C_LABEL(sys_mlock), C_LABEL(sys_munlock), C_LABEL(sys_mlockall)
-	.xword C_LABEL(sys_munlockall), C_LABEL(sys_sched_setparam)
-	.xword C_LABEL(sys_sched_getparam), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_sched_get_priority_max), C_LABEL(sys_sched_get_priority_min)
-	.xword C_LABEL(sys_sched_rr_get_interval), C_LABEL(sys_nanosleep)
-/*250*/	.xword C_LABEL(sys_mremap)
-	.xword C_LABEL(sys_sysctl)
-	.xword C_LABEL(sys_getsid), C_LABEL(sys_fdatasync), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_aplib), C_LABEL(sys_nis_syscall)
+	.globl sys_call_table32
+sys_call_table32:
+/*0*/	.xword sys_setup, sys_exit, sys_fork, sys_read, sys_write
+/*5*/	.xword sys_open, sys_close, sys_wait4, sys_creat, sys_link
+/*10*/  .xword sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys_mknod
+/*15*/	.xword sys_chmod, sys_chown, sparc_brk, sys_nis_syscall, sys_lseek
+/*20*/	.xword sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid
+/*25*/	.xword sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_pause
+/*30*/	.xword sys_utime, sys_stty, sys_gtty, sys_access, sys_nice
+	.xword sys_ftime, sys_sync, sys_kill, sys_newstat, sys_nis_syscall
+/*40*/	.xword sys_newlstat, sys_dup, sys_pipe, sys_times, sys_profil
+	.xword sys_nis_syscall, sys_setgid, sys_getgid, sys_signal, sys_geteuid
+/*50*/	.xword sys_getegid, sys_acct, sys_nis_syscall, sys_nis_syscall, sys_ioctl
+	.xword sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys_execve
+/*60*/	.xword sys_umask, sys_chroot, sys_newfstat, sys_nis_syscall, sys_getpagesize
+	.xword sys_nis_syscall, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*70*/	.xword sys_nis_syscall, sys_mmap, sys_nis_syscall, sys_munmap, sys_mprotect
+	.xword sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys_getgroups
+/*80*/	.xword sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall
+	.xword sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall
+/*90*/	.xword sys_dup2, sys_nis_syscall, sys_fcntl, sys_select, sys_nis_syscall
+	.xword sys_fsync, sys_setpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*100*/	.xword sys_getpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*110*/	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+	.xword sys_nis_syscall, sys_gettimeofday, sys_getrusage, sys_nis_syscall, sys_nis_syscall
+/*120*/	.xword sys_readv, sys_writev, sys_settimeofday, sys_fchown, sys_fchmod
+	.xword sys_nis_syscall, sys_setreuid, sys_setregid, sys_rename, sys_truncate
+/*130*/	.xword sys_ftruncate, sys_flock, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+	.xword sys_nis_syscall, sys_mkdir, sys_rmdir, sys_nis_syscall, sys_nis_syscall
+/*140*/	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getrlimit
+	.xword sys_setrlimit, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*150*/	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+	.xword sys_nis_syscall, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_umount
+/*160*/	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_setdomainname, sys_nis_syscall
+	.xword sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_nis_syscall
+/*170*/	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getdents
+	.xword sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*180*/	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_nis_syscall
+	.xword sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_newuname
+/*190*/	.xword sys_init_module, sys_personality, sys_prof, sys_break, sys_lock
+	.xword sys_mpx, sys_ulimit, sys_getppid, sparc_sigaction, sys_sgetmask
+/*200*/	.xword sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, old_readdir
+	.xword sys_nis_syscall, sys_socketcall, sys_syslog, sys_olduname, sys_nis_syscall
+/*210*/	.xword sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo
+	.xword sys_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys_adjtimex
+/*220*/	.xword sys_sigprocmask, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid
+	.xword sys_bdflush, sys_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid
+/*230*/	.xword sys_llseek, sys_time, sys_nis_syscall, sys_stime, sys_nis_syscall
+	.xword sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall
+/*240*/	.xword sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_nis_syscall, sys_nis_syscall
+	.xword sys_nis_syscall, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep
+/*250*/	.xword sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nis_syscall
+	.xword sys_aplib, sys_nis_syscall
 
 	/* Now the 64-bit native Linux syscall table. */
 
-	.globl C_LABEL(sys_call_table64)
-C_LABEL(sys_call_table64):
-/*0*/	.xword C_LABEL(sys_setup), C_LABEL(sys_exit), C_LABEL(sys_fork)
-	.xword C_LABEL(sys_read), C_LABEL(sys_write)
-/*5*/	.xword C_LABEL(sys_open), C_LABEL(sys_close), C_LABEL(sys_wait4)
-	.xword C_LABEL(sys_creat), C_LABEL(sys_link)
-/*10*/  .xword C_LABEL(sys_unlink), C_LABEL(sunos_execv), C_LABEL(sys_chdir)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_mknod)
-/*15*/	.xword C_LABEL(sys_chmod), C_LABEL(sys_chown), C_LABEL(sparc_brk)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_lseek)
-/*20*/	.xword C_LABEL(sys_getpid), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_setuid), C_LABEL(sys_getuid)
-/*25*/	.xword C_LABEL(sys_time), C_LABEL(sys_ptrace), C_LABEL(sys_alarm)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_pause)
-/*30*/	.xword C_LABEL(sys_utime), C_LABEL(sys_stty), C_LABEL(sys_gtty)
-	.xword C_LABEL(sys_access), C_LABEL(sys_nice), C_LABEL(sys_ftime)
-	.xword C_LABEL(sys_sync), C_LABEL(sys_kill), C_LABEL(sys_newstat)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_newlstat), C_LABEL(sys_dup)
-	.xword C_LABEL(sys_pipe), C_LABEL(sys_times), C_LABEL(sys_profil)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_setgid), C_LABEL(sys_getgid)
-	.xword C_LABEL(sys_signal), C_LABEL(sys_geteuid)
-/*50*/	.xword C_LABEL(sys_getegid), C_LABEL(sys_acct), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_ioctl), C_LABEL(sys_reboot)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_symlink), C_LABEL(sys_readlink)
-	.xword C_LABEL(sys_execve), C_LABEL(sys_umask), C_LABEL(sys_chroot)
-	.xword C_LABEL(sys_newfstat), C_LABEL(sys_nis_syscall), C_LABEL(sys_getpagesize)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_vfork), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_mmap), C_LABEL(sys_nis_syscall), C_LABEL(sys_munmap)
-	.xword C_LABEL(sys_mprotect), C_LABEL(sys_nis_syscall), C_LABEL(sys_vhangup)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_getgroups)
-	.xword C_LABEL(sys_setgroups), C_LABEL(sys_getpgrp), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_setitimer), C_LABEL(sys_nis_syscall), C_LABEL(sys_swapon)
-	.xword C_LABEL(sys_getitimer), C_LABEL(sys_nis_syscall), C_LABEL(sys_sethostname)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_dup2), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_fcntl), C_LABEL(sys_select), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-/*100*/	.xword C_LABEL(sys_getpriority), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_gettimeofday), C_LABEL(sys_getrusage)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_readv)
-	.xword C_LABEL(sys_writev), C_LABEL(sys_settimeofday), C_LABEL(sys_fchown)
-	.xword C_LABEL(sys_fchmod), C_LABEL(sys_nis_syscall), C_LABEL(sys_setreuid)
-	.xword C_LABEL(sys_setregid), C_LABEL(sys_rename), C_LABEL(sys_truncate)
-	.xword C_LABEL(sys_ftruncate), C_LABEL(sys_flock), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_mkdir), C_LABEL(sys_rmdir), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_getrlimit)
-	.xword C_LABEL(sys_setrlimit), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-/*150*/	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_statfs), C_LABEL(sys_fstatfs)
-	.xword C_LABEL(sys_umount), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_setdomainname)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_quotactl), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_mount), C_LABEL(sys_ustat), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_getdents), C_LABEL(sys_setsid)
-	.xword C_LABEL(sys_fchdir), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_sigpending), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_setpgid), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_newuname), C_LABEL(sys_init_module)
-	.xword C_LABEL(sys_personality), C_LABEL(sys_prof), C_LABEL(sys_break)
-	.xword C_LABEL(sys_lock), C_LABEL(sys_mpx), C_LABEL(sys_ulimit)
-	.xword C_LABEL(sys_getppid), C_LABEL(sparc_sigaction), C_LABEL(sys_sgetmask)
-/*200*/	.xword C_LABEL(sys_ssetmask), C_LABEL(sys_sigsuspend), C_LABEL(sys_newlstat)
-	.xword C_LABEL(sys_uselib), C_LABEL(old_readdir), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_socketcall), C_LABEL(sys_syslog), C_LABEL(sys_olduname)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_idle), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_waitpid), C_LABEL(sys_swapoff), C_LABEL(sys_sysinfo)
-	.xword C_LABEL(sys_ipc), C_LABEL(sys_sigreturn), C_LABEL(sys_clone)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_adjtimex), C_LABEL(sys_sigprocmask)
-	.xword C_LABEL(sys_create_module), C_LABEL(sys_delete_module)
-	.xword C_LABEL(sys_get_kernel_syms), C_LABEL(sys_getpgid), C_LABEL(sys_bdflush)
-	.xword C_LABEL(sys_sysfs), C_LABEL(sys_nis_syscall), C_LABEL(sys_setfsuid)
-	.xword C_LABEL(sys_setfsgid), C_LABEL(sys_llseek), C_LABEL(sys_time)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_stime), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_llseek)
-	/* "We are the Knights of the Forest of Ni!!" */
-	.xword C_LABEL(sys_mlock), C_LABEL(sys_munlock), C_LABEL(sys_mlockall)
-	.xword C_LABEL(sys_munlockall), C_LABEL(sys_sched_setparam)
-	.xword C_LABEL(sys_sched_getparam), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_sched_get_priority_max), C_LABEL(sys_sched_get_priority_min)
-	.xword C_LABEL(sys_sched_rr_get_interval), C_LABEL(sys_nanosleep)
-/*250*/	.xword C_LABEL(sys_mremap)
-	.xword C_LABEL(sys_sysctl)
-	.xword C_LABEL(sys_getsid), C_LABEL(sys_fdatasync), C_LABEL(sys_nis_syscall)
-	.xword C_LABEL(sys_aplib), C_LABEL(sys_nis_syscall)
+	.globl sys_call_table64
+sys_call_table64:
+/*0*/	.xword sys_setup, sys_exit, sys_fork, sys_read, sys_write
+/*5*/	.xword sys_open, sys_close, sys_wait4, sys_creat, sys_link
+/*10*/  .xword sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys_mknod
+/*15*/	.xword sys_chmod, sys_chown, sparc_brk, sys_nis_syscall, sys_lseek
+/*20*/	.xword sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid
+/*25*/	.xword sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_pause
+/*30*/	.xword sys_utime, sys_stty, sys_gtty, sys_access, sys_nice
+	.xword sys_ftime, sys_sync, sys_kill, sys_newstat, sys_nis_syscall
+/*40*/	.xword sys_newlstat, sys_dup, sys_pipe, sys_times, sys_profil
+	.xword sys_nis_syscall, sys_setgid, sys_getgid, sys_signal, sys_geteuid
+/*50*/	.xword sys_getegid, sys_acct, sys_nis_syscall, sys_nis_syscall, sys_ioctl
+	.xword sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys_execve
+/*60*/	.xword sys_umask, sys_chroot, sys_newfstat, sys_nis_syscall, sys_getpagesize
+	.xword sys_nis_syscall, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*70*/	.xword sys_nis_syscall, sys_mmap, sys_nis_syscall, sys_munmap, sys_mprotect
+	.xword sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys_getgroups
+/*80*/	.xword sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall
+	.xword sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall
+/*90*/	.xword sys_dup2, sys_nis_syscall, sys_fcntl, sys_select, sys_nis_syscall
+	.xword sys_fsync, sys_setpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*100*/	.xword sys_getpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*110*/	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+	.xword sys_nis_syscall, sys_gettimeofday, sys_getrusage, sys_nis_syscall, sys_nis_syscall
+/*120*/	.xword sys_readv, sys_writev, sys_settimeofday, sys_fchown, sys_fchmod
+	.xword sys_nis_syscall, sys_setreuid, sys_setregid, sys_rename, sys_truncate
+/*130*/	.xword sys_ftruncate, sys_flock, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+	.xword sys_nis_syscall, sys_mkdir, sys_rmdir, sys_nis_syscall, sys_nis_syscall
+/*140*/	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getrlimit
+	.xword sys_setrlimit, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*150*/	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+	.xword sys_nis_syscall, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_umount
+/*160*/	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_setdomainname, sys_nis_syscall
+	.xword sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_nis_syscall
+/*170*/	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getdents
+	.xword sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*180*/	.xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_nis_syscall
+	.xword sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_newuname
+/*190*/	.xword sys_init_module, sys_personality, sys_prof, sys_break, sys_lock
+	.xword sys_mpx, sys_ulimit, sys_getppid, sparc_sigaction, sys_sgetmask
+/*200*/	.xword sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, old_readdir
+	.xword sys_nis_syscall, sys_socketcall, sys_syslog, sys_olduname, sys_nis_syscall
+/*210*/	.xword sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo
+	.xword sys_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys_adjtimex
+/*220*/	.xword sys_sigprocmask, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid
+	.xword sys_bdflush, sys_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid
+/*230*/	.xword sys_llseek, sys_time, sys_nis_syscall, sys_stime, sys_nis_syscall
+	.xword sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall
+/*240*/	.xword sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_nis_syscall, sys_nis_syscall
+	.xword sys_nis_syscall, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep
+/*250*/	.xword sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nis_syscall
+	.xword sys_aplib, sys_nis_syscall
 
 	/* Now the 32-bit SunOS syscall table. */
 
 	.align 4
-	.globl C_LABEL(sunos_sys_table)
-C_LABEL(sunos_sys_table):
-/*0*/	.xword C_LABEL(sunos_indir), C_LABEL(sys_exit), C_LABEL(sys_fork)
-	.xword C_LABEL(sunos_read), C_LABEL(sunos_write), C_LABEL(sunos_open)
-	.xword C_LABEL(sys_close), C_LABEL(sunos_wait4), C_LABEL(sys_creat)
-	.xword C_LABEL(sys_link), C_LABEL(sys_unlink), C_LABEL(sunos_execv)
-	.xword C_LABEL(sys_chdir), C_LABEL(sunos_nosys), C_LABEL(sys_mknod)
-	.xword C_LABEL(sys_chmod), C_LABEL(sys_chown), C_LABEL(sunos_brk)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sys_lseek), C_LABEL(sunos_getpid)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_getuid), C_LABEL(sunos_nosys), C_LABEL(sys_ptrace)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sys_access), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sys_sync), C_LABEL(sys_kill), C_LABEL(sys_newstat)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sys_newlstat), C_LABEL(sys_dup)
-	.xword C_LABEL(sys_pipe), C_LABEL(sunos_nosys), C_LABEL(sys_profil)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_getgid)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-/*50*/	.xword C_LABEL(sunos_nosys), C_LABEL(sys_acct), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_mctl), C_LABEL(sunos_ioctl), C_LABEL(sys_reboot)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sys_symlink), C_LABEL(sys_readlink)
-	.xword C_LABEL(sys_execve), C_LABEL(sys_umask), C_LABEL(sys_chroot)
-	.xword C_LABEL(sys_newfstat), C_LABEL(sunos_nosys), C_LABEL(sys_getpagesize)
-	.xword C_LABEL(sys_msync), C_LABEL(sys_vfork), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_sbrk), C_LABEL(sunos_sstk)
-	.xword C_LABEL(sunos_mmap), C_LABEL(sunos_vadvise), C_LABEL(sys_munmap)
-	.xword C_LABEL(sys_mprotect), C_LABEL(sunos_madvise), C_LABEL(sys_vhangup)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_mincore), C_LABEL(sys_getgroups)
-	.xword C_LABEL(sys_setgroups), C_LABEL(sys_getpgrp), C_LABEL(sunos_setpgrp)
-	.xword C_LABEL(sys_setitimer), C_LABEL(sunos_nosys), C_LABEL(sys_swapon)
-	.xword C_LABEL(sys_getitimer), C_LABEL(sys_gethostname), C_LABEL(sys_sethostname)
-	.xword C_LABEL(sunos_getdtablesize), C_LABEL(sys_dup2), C_LABEL(sunos_nop)
-	.xword C_LABEL(sys_fcntl), C_LABEL(sunos_select), C_LABEL(sunos_nop)
-	.xword C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sys_socket)
-	.xword C_LABEL(sys_connect), C_LABEL(sunos_accept)
-/*100*/	.xword C_LABEL(sys_getpriority), C_LABEL(sunos_send), C_LABEL(sunos_recv)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sys_bind), C_LABEL(sunos_setsockopt)
-	.xword C_LABEL(sys_listen), C_LABEL(sunos_nosys), C_LABEL(sunos_sigaction)
-	.xword C_LABEL(sunos_sigblock), C_LABEL(sunos_sigsetmask), C_LABEL(sys_sigpause)
-	.xword C_LABEL(sys_sigstack), C_LABEL(sys_recvmsg), C_LABEL(sys_sendmsg)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sys_gettimeofday), C_LABEL(sys_getrusage)
-	.xword C_LABEL(sunos_getsockopt), C_LABEL(sunos_nosys), C_LABEL(sunos_readv)
-	.xword C_LABEL(sunos_writev), C_LABEL(sys_settimeofday), C_LABEL(sys_fchown)
-	.xword C_LABEL(sys_fchmod), C_LABEL(sys_recvfrom), C_LABEL(sys_setreuid)
-	.xword C_LABEL(sys_setregid), C_LABEL(sys_rename), C_LABEL(sys_truncate)
-	.xword C_LABEL(sys_ftruncate), C_LABEL(sys_flock), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sys_sendto), C_LABEL(sys_shutdown), C_LABEL(sys_socketpair)
-	.xword C_LABEL(sys_mkdir), C_LABEL(sys_rmdir), C_LABEL(sys_utimes)
-	.xword C_LABEL(sys_sigreturn), C_LABEL(sunos_nosys), C_LABEL(sys_getpeername)
-	.xword C_LABEL(sunos_gethostid), C_LABEL(sunos_nosys), C_LABEL(sys_getrlimit)
-	.xword C_LABEL(sys_setrlimit), C_LABEL(sunos_killpg), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-/*150*/	.xword C_LABEL(sys_getsockname), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_poll), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_getdirentries), C_LABEL(sys_statfs), C_LABEL(sys_fstatfs)
-	.xword C_LABEL(sys_umount), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_getdomainname), C_LABEL(sys_setdomainname)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sys_quotactl), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_mount), C_LABEL(sys_ustat), C_LABEL(sunos_semsys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_shmsys), C_LABEL(sunos_audit)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_getdents), C_LABEL(sys_setsid)
-	.xword C_LABEL(sys_fchdir), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sys_sigpending), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sys_setpgid), C_LABEL(sunos_pathconf), C_LABEL(sunos_fpathconf)
-	.xword C_LABEL(sunos_sysconf), C_LABEL(sunos_uname), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-/*200*/	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-/*250*/	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
-	.xword C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sys_aplib)
+	.globl sunos_sys_table
+sunos_sys_table:
+/*0*/	.xword sunos_indir, sys_exit, sys_fork
+	.xword sunos_read, sunos_write, sunos_open
+	.xword sys_close, sunos_wait4, sys_creat
+	.xword sys_link, sys_unlink, sunos_execv
+	.xword sys_chdir, sunos_nosys, sys_mknod
+	.xword sys_chmod, sys_chown, sunos_brk
+	.xword sunos_nosys, sys_lseek, sunos_getpid
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_getuid, sunos_nosys, sys_ptrace
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sys_access, sunos_nosys, sunos_nosys
+	.xword sys_sync, sys_kill, sys_newstat
+	.xword sunos_nosys, sys_newlstat, sys_dup
+	.xword sys_pipe, sunos_nosys, sys_profil
+	.xword sunos_nosys, sunos_nosys, sunos_getgid
+	.xword sunos_nosys, sunos_nosys
+/*50*/	.xword sunos_nosys, sys_acct, sunos_nosys
+	.xword sunos_mctl, sunos_ioctl, sys_reboot
+	.xword sunos_nosys, sys_symlink, sys_readlink
+	.xword sys_execve, sys_umask, sys_chroot
+	.xword sys_newfstat, sunos_nosys, sys_getpagesize
+	.xword sys_msync, sys_vfork, sunos_nosys
+	.xword sunos_nosys, sunos_sbrk, sunos_sstk
+	.xword sunos_mmap, sunos_vadvise, sys_munmap
+	.xword sys_mprotect, sunos_madvise, sys_vhangup
+	.xword sunos_nosys, sunos_mincore, sys_getgroups
+	.xword sys_setgroups, sys_getpgrp, sunos_setpgrp
+	.xword sys_setitimer, sunos_nosys, sys_swapon
+	.xword sys_getitimer, sys_gethostname, sys_sethostname
+	.xword sunos_getdtablesize, sys_dup2, sunos_nop
+	.xword sys_fcntl, sunos_select, sunos_nop
+	.xword sys_fsync, sys_setpriority, sys_socket
+	.xword sys_connect, sunos_accept
+/*100*/	.xword sys_getpriority, sunos_send, sunos_recv
+	.xword sunos_nosys, sys_bind, sunos_setsockopt
+	.xword sys_listen, sunos_nosys, sunos_sigaction
+	.xword sunos_sigblock, sunos_sigsetmask, sys_sigpause
+	.xword sys_sigstack, sys_recvmsg, sys_sendmsg
+	.xword sunos_nosys, sys_gettimeofday, sys_getrusage
+	.xword sunos_getsockopt, sunos_nosys, sunos_readv
+	.xword sunos_writev, sys_settimeofday, sys_fchown
+	.xword sys_fchmod, sys_recvfrom, sys_setreuid
+	.xword sys_setregid, sys_rename, sys_truncate
+	.xword sys_ftruncate, sys_flock, sunos_nosys
+	.xword sys_sendto, sys_shutdown, sys_socketpair
+	.xword sys_mkdir, sys_rmdir, sys_utimes
+	.xword sys_sigreturn, sunos_nosys, sys_getpeername
+	.xword sunos_gethostid, sunos_nosys, sys_getrlimit
+	.xword sys_setrlimit, sunos_killpg, sunos_nosys
+	.xword sunos_nosys, sunos_nosys
+/*150*/	.xword sys_getsockname, sunos_nosys, sunos_nosys
+	.xword sunos_poll, sunos_nosys, sunos_nosys
+	.xword sunos_getdirentries, sys_statfs, sys_fstatfs
+	.xword sys_umount, sunos_nosys, sunos_nosys
+	.xword sunos_getdomainname, sys_setdomainname
+	.xword sunos_nosys, sys_quotactl, sunos_nosys
+	.xword sunos_mount, sys_ustat, sunos_semsys
+	.xword sunos_nosys, sunos_shmsys, sunos_audit
+	.xword sunos_nosys, sunos_getdents, sys_setsid
+	.xword sys_fchdir, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sys_sigpending, sunos_nosys
+	.xword sys_setpgid, sunos_pathconf, sunos_fpathconf
+	.xword sunos_sysconf, sunos_uname, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+/*200*/	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys
+/*250*/	.xword sunos_nosys, sunos_nosys, sunos_nosys
+	.xword sunos_nosys, sunos_nosys, sys_aplib
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/kernel/traps.c linux/arch/sparc64/kernel/traps.c
--- v2.1.29/linux/arch/sparc64/kernel/traps.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/kernel/traps.c	Thu Mar 20 16:43:32 1997
@@ -0,0 +1,138 @@
+/* $Id: traps.c,v 1.1 1997/03/18 17:59:12 jj Exp $
+ * arch/sparc/kernel/traps.c
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+/*
+ * I hate traps on the sparc, grrr...
+ */
+
+#include <linux/sched.h>  /* for jiffies */
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <asm/delay.h>
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/oplib.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/unistd.h>
+
+/* #define TRAP_DEBUG */
+
+struct trap_trace_entry {
+	unsigned long pc;
+	unsigned long type;
+};
+
+int trap_curbuf = 0;
+struct trap_trace_entry trapbuf[1024];
+
+void syscall_trace_entry(struct pt_regs *regs)
+{
+	printk("%s[%d]: ", current->comm, current->pid);
+	printk("scall<%ld> (could be %ld)\n", (long) regs->u_regs[UREG_G1],
+	       (long) regs->u_regs[UREG_I0]);
+}
+
+void syscall_trace_exit(struct pt_regs *regs)
+{
+}
+
+void instruction_dump (unsigned int *pc)
+{
+	int i;
+	
+	if((((unsigned long) pc) & 3))
+                return;
+
+	for(i = -3; i < 6; i++)
+		printk("%c%08x%c",i?' ':'<',pc[i],i?' ':'>');
+	printk("\n");
+}
+
+void die_if_kernel(char *str, struct pt_regs *regs)
+{
+	/* Amuse the user. */
+	printk(
+"              \\|/ ____ \\|/\n"
+"              \"@'/ .` \\`@\"\n"
+"              /_| \\__/ |_\\\n"
+"                 \\__U_/\n");
+
+	printk("%s(%d): %s\n", current->comm, current->pid, str);
+	show_regs(regs);
+	printk("Instruction DUMP:");
+	instruction_dump ((unsigned int *) regs->tpc);
+	if(regs->tstate & TSTATE_PRIV)
+		do_exit(SIGKILL);
+	do_exit(SIGSEGV);
+}
+
+void do_illegal_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+			    unsigned long tstate)
+{
+	lock_kernel();
+	if(tstate & TSTATE_PRIV)
+		die_if_kernel("Kernel illegal instruction", regs);
+#ifdef TRAP_DEBUG
+	printk("Ill instr. at pc=%016lx instruction is %08x\n",
+	       regs->tpc, *(unsigned int *)regs->tpc);
+#endif
+	current->tss.sig_address = pc;
+	current->tss.sig_desc = SUBSIG_ILLINST;
+	send_sig(SIGILL, current, 1);
+	unlock_kernel();
+}
+
+void do_priv_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+			 unsigned long tstate)
+{
+	lock_kernel();
+	if(tstate & TSTATE_PRIV)
+		die_if_kernel("Penguin instruction from Penguin mode??!?!", regs);
+	current->tss.sig_address = pc;
+	current->tss.sig_desc = SUBSIG_PRIVINST;
+	send_sig(SIGILL, current, 1);
+	unlock_kernel();
+}
+
+/* XXX User may want to be allowed to do this. XXX */
+
+void do_memaccess_unaligned(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+			    unsigned long tstate)
+{
+	lock_kernel();
+	if(regs->tstate & TSTATE_PRIV) {
+		printk("KERNEL MNA at pc %016lx npc %016lx called by %016lx\n", pc, npc,
+		       regs->u_regs[UREG_RETPC]);
+		die_if_kernel("BOGUS", regs);
+		/* die_if_kernel("Kernel MNA access", regs); */
+	}
+	current->tss.sig_address = pc;
+	current->tss.sig_desc = SUBSIG_PRIVINST;
+#if 0
+	show_regs (regs);
+	instruction_dump ((unsigned long *) regs->tpc);
+	printk ("do_MNA!\n");
+#endif
+	send_sig(SIGBUS, current, 1);
+	unlock_kernel();
+}
+
+void handle_hw_divzero(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+		       unsigned long psr)
+{
+	lock_kernel();
+	send_sig(SIGILL, current, 1);
+	unlock_kernel();
+}
+
+void trap_init(void)
+{
+}
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/lib/Makefile linux/arch/sparc64/lib/Makefile
--- v2.1.29/linux/arch/sparc64/lib/Makefile	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/lib/Makefile	Thu Mar 20 16:43:32 1997
@@ -1,10 +1,11 @@
-# $Id: Makefile,v 1.1 1996/12/27 17:28:35 davem Exp $
+# $Id: Makefile,v 1.5 1997/03/14 21:04:27 jj Exp $
 # Makefile for Sparc library files..
 #
 
 CFLAGS := $(CFLAGS) -ansi
 
-OBJS  = memset.o blockops.o
+OBJS  = memset.o blockops.o locks.o memcpy.o strlen.o strncmp.o \
+	memscan.o strncpy_from_user.o strlen_user.o memcmp.o
 
 lib.a: $(OBJS)
 	$(AR) rcs lib.a $(OBJS)
@@ -15,6 +16,30 @@
 
 memset.o: memset.S
 	$(CC) -D__ASSEMBLY__ -ansi -c -o memset.o memset.S
+
+memcpy.o: memcpy.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o memcpy.o memcpy.S
+
+strlen.o: strlen.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o strlen.o strlen.S
+
+strncmp.o: strncmp.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o strncmp.o strncmp.S
+
+memcmp.o: memcmp.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o memcmp.o memcmp.S
+
+locks.o: locks.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o locks.o locks.S
+
+memscan.o: memscan.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o memscan.o memscan.S
+
+strncpy_from_user.o: strncpy_from_user.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o strncpy_from_user.o strncpy_from_user.S
+
+strlen_user.o: strlen_user.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o strlen_user.o strlen_user.S
 
 dep:
 
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/lib/checksum.S linux/arch/sparc64/lib/checksum.S
--- v2.1.29/linux/arch/sparc64/lib/checksum.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/lib/checksum.S	Thu Mar 20 16:43:32 1997
@@ -0,0 +1,567 @@
+/* checksum.S: Sparc V9 optimized checksum code.
+ *
+ *  Copyright(C) 1995 Linus Torvalds
+ *  Copyright(C) 1995 Miguel de Icaza
+ *  Copyright(C) 1996 David S. Miller
+ *  Copyright(C) 1997 Jakub Jelinek
+ *
+ * derived from:
+ *	Linux/Alpha checksum c-code
+ *      Linux/ix86 inline checksum assembly
+ *      RFC1071 Computing the Internet Checksum (esp. Jacobsons m68k code)
+ *	David Mosberger-Tang for optimized reference c-code
+ *	BSD4.4 portable checksum routine
+ */
+
+#include <asm/errno.h>
+#include <asm/head.h>
+
+#define CSUM_BIGCHUNK(buf, offset, sum, t0, t1, t2, t3, t4, t5)	\
+	ldd	[buf + offset + 0x00], t0;			\
+	ldd	[buf + offset + 0x08], t2;			\
+	addccc	t0, sum, sum;					\
+	addccc	t1, sum, sum;					\
+	ldd	[buf + offset + 0x10], t4;			\
+	addccc	t2, sum, sum;					\
+	addccc	t3, sum, sum;					\
+	ldd	[buf + offset + 0x18], t0;			\
+	addccc	t4, sum, sum;					\
+	addccc	t5, sum, sum;					\
+	addccc	t0, sum, sum;					\
+	addccc	t1, sum, sum;
+
+#define CSUM_LASTCHUNK(buf, offset, sum, t0, t1, t2, t3)	\
+	ldd	[buf - offset - 0x08], t0;			\
+	ldd	[buf - offset - 0x00], t2;			\
+	addccc	t0, sum, sum;					\
+	addccc	t1, sum, sum;					\
+	addccc	t2, sum, sum;					\
+	addccc	t3, sum, sum;
+
+	/* Do end cruft out of band to get better cache patterns. */
+csum_partial_end_cruft:
+	andcc	%o1, 8, %g0			! check how much
+	be,pn	%icc, 1f			! caller asks %o1 & 0x8
+	 and	%o1, 4, %g3			! nope, check for word remaining
+	ldd	[%o0], %g2			! load two
+	addcc	%g2, %o2, %o2			! add first word to sum
+	addccc	%g3, %o2, %o2			! add second word as well
+	add	%o0, 8, %o0			! advance buf ptr
+	addc	%g0, %o2, %o2			! add in final carry
+1:	brz,pn	%g3, 1f				! nope, skip this code
+	 andcc	%o1, 3, %o1			! check for trailing bytes
+	ld	[%o0], %g2			! load it
+	addcc	%g2, %o2, %o2			! add to sum
+	add	%o0, 4, %o0			! advance buf ptr
+	addc	%g0, %o2, %o2			! add in final carry
+1:	brz,pn	%o1, 1f				! no trailing bytes, return
+	 addcc	%o1, -1, %g0			! only one byte remains?
+	bne,pn	%icc, 2f			! at least two bytes more
+	 subcc	%o1, 2, %o1			! only two bytes more?
+	ba,pt	%xcc, 4f			! only one byte remains
+	 clr	%o4				! clear fake hword value
+2:	lduh	[%o0], %o4			! get hword
+	be,pn	%icc, 6f			! jmp if only hword remains
+	 add	%o0, 2, %o0			! advance buf ptr either way
+	sll	%o4, 16, %o4			! create upper hword
+4:	ldub	[%o0], %o5			! get final byte
+	sll	%o5, 8, %o5			! put into place
+	or	%o5, %o4, %o4			! coalese with hword (if any)
+6:	addcc	%o4, %o2, %o2			! add to sum
+1:	sllx	%g4, 32, %g4			! give gfp back
+	retl					! get outta here
+	 addc	%g0, %o2, %o0			! add final carry into retval
+
+	/* Also do alignment out of band to get better cache patterns. */
+csum_partial_fix_alignment:
+
+	/* The common case is to get called with a nicely aligned
+	 * buffer of size 0x20.  Follow the code path for that case.
+	 */
+	.globl	C_LABEL(csum_partial)
+C_LABEL(csum_partial):			/* %o0=buf, %o1=len, %o2=sum */
+	andcc	%o0, 0x7, %g0				! alignment problems?
+	be,pt	%icc, csum_partial_fix_aligned		! yep, handle it
+	 andn	%o1, 0x7f, %o3				! num loop iterations
+	cmp	%o1, 6
+	bl,pn	%icc, cpte - 0x4
+	 andcc	%o0, 0x2, %g0
+	be,pn	%icc, 1f
+	 and	%o0, 0x4, %g7
+	lduh	[%o0 + 0x00], %g2
+	sub	%o1, 2, %o1
+	add	%o0, 2, %o0
+	sll	%g2, 16, %g2
+	addcc	%g2, %o2, %o2
+	srl	%o2, 16, %g3
+	addc	%g0, %g3, %g2
+	sll	%o2, 16, %o2
+	sll	%g2, 16, %g3
+	srl	%o2, 16, %o2
+	or	%g3, %o2, %o2
+1:	brz,pn	%g7, csum_partial_fix_aligned
+	 nop
+	ld	[%o0 + 0x00], %g2
+	sub	%o1, 4, %o1
+	addcc	%g2, %o2, %o2
+	add	%o0, 4, %o0
+	addc	%g0, %o2, %o2
+csum_partial_fix_aligned:
+	brz,pt	%o3, 3f					! none to do
+	 andcc	%o1, 0x70, %g1				! clears carry flag too
+5:	CSUM_BIGCHUNK(%o0, 0x00, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+	CSUM_BIGCHUNK(%o0, 0x20, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+	CSUM_BIGCHUNK(%o0, 0x40, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+	CSUM_BIGCHUNK(%o0, 0x60, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+	sub	%o3, 128, %o3				! detract from loop iters
+	addc	%g0, %o2, %o2				! sink in final carry
+	brnz,pt	%o3, 5b					! more to do
+	 add	%o0, 128, %o0				! advance buf ptr
+3:	brz,pn	%g1, cpte				! nope
+	 andcc	%o1, 0xf, %o3				! anything left at all?
+10:	rd	%pc, %g7				! get pc
+	srl	%g1, 1, %o4				! compute offset
+	sub	%g7, %g1, %g7				! adjust jmp ptr
+	sub	%g7, %o4, %g7				! final jmp ptr adjust
+	jmp	%g7 + (cpte - 8 - 10b)			! enter the table
+	 add	%o0, %g1, %o0				! advance buf ptr
+cptbl:	CSUM_LASTCHUNK(%o0, 0x68, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x58, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x48, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x38, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x28, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x18, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x08, %o2, %g2, %g3, %g4, %g5)
+	addc	%g0, %o2, %o2				! fetch final carry
+	andcc	%o1, 0xf, %g0				! anything left at all?
+cpte:	brnz,pn	%o3, csum_partial_end_cruft		! yep, handle it
+	 sethi	%uhi(KERNBASE), %g4
+	mov	%o2, %o0				! return computed csum
+	retl						! get outta here
+	 sllx	%g4, 32, %g4				! give gfp back
+
+	.globl C_LABEL(__csum_partial_copy_start), C_LABEL(__csum_partial_copy_end)
+C_LABEL(__csum_partial_copy_start):
+
+#define EX(x,y,a,b,z)                           \
+98:     x,y;                                    \
+        .section .fixup,z##alloc,z##execinstr;  \
+        .align  4;                              \
+99:     ba,pt %xcc, 30f;                        \
+         a, b, %o3;                             \
+        .section __ex_table,z##alloc;           \
+        .align  4;                              \
+        .word   98b, 99b;                       \
+        .text;                                  \
+        .align  4
+
+#define EX2(x,y,z)                          	\
+98:     x,y;                                    \
+        .section __ex_table,z##alloc;           \
+        .align  4;                              \
+        .word   98b, 30f;                       \
+        .text;                                  \
+        .align  4
+
+#define EX3(x,y,z)                          	\
+98:     x,y;                                    \
+        .section __ex_table,z##alloc;           \
+        .align  4;                              \
+        .word   98b, 96f;                       \
+        .text;                                  \
+        .align  4
+
+#define EXT(start,end,handler,z)                \
+        .section __ex_table,z##alloc;           \
+        .align  4;                              \
+        .word   start, 0, end, handler;         \
+        .text;                                  \
+        .align  4
+
+	/* This aligned version executes typically in 8.5 superscalar cycles, this
+	 * is the best I can do.  I say 8.5 because the final add will pair with
+	 * the next ldd in the main unrolled loop.  Thus the pipe is always full.
+	 * If you change these macros (including order of instructions),
+	 * please check the fixup code below as well.
+	 */
+#define CSUMCOPY_BIGCHUNK_ALIGNED(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7)	\
+	ldd	[src + off + 0x00], t0;							\
+	ldd	[src + off + 0x08], t2;							\
+	addccc	t0, sum, sum;								\
+	ldd	[src + off + 0x10], t4;							\
+	addccc	t1, sum, sum;								\
+	ldd	[src + off + 0x18], t6;							\
+	addccc	t2, sum, sum;								\
+	std	t0, [dst + off + 0x00];							\
+	addccc	t3, sum, sum;								\
+	std	t2, [dst + off + 0x08];							\
+	addccc	t4, sum, sum;								\
+	std	t4, [dst + off + 0x10];							\
+	addccc	t5, sum, sum;								\
+	std	t6, [dst + off + 0x18];							\
+	addccc	t6, sum, sum;								\
+	addccc	t7, sum, sum;
+
+	/* 12 superscalar cycles seems to be the limit for this case,
+	 * because of this we thus do all the ldd's together to get
+	 * Viking MXCC into streaming mode.  Ho hum...
+	 */
+#define CSUMCOPY_BIGCHUNK(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7)	\
+	ldd	[src + off + 0x00], t0;						\
+	ldd	[src + off + 0x08], t2;						\
+	ldd	[src + off + 0x10], t4;						\
+	ldd	[src + off + 0x18], t6;						\
+	st	t0, [dst + off + 0x00];						\
+	addccc	t0, sum, sum;							\
+	st	t1, [dst + off + 0x04];						\
+	addccc	t1, sum, sum;							\
+	st	t2, [dst + off + 0x08];						\
+	addccc	t2, sum, sum;							\
+	st	t3, [dst + off + 0x0c];						\
+	addccc	t3, sum, sum;							\
+	st	t4, [dst + off + 0x10];						\
+	addccc	t4, sum, sum;							\
+	st	t5, [dst + off + 0x14];						\
+	addccc	t5, sum, sum;							\
+	st	t6, [dst + off + 0x18];						\
+	addccc	t6, sum, sum;							\
+	st	t7, [dst + off + 0x1c];						\
+	addccc	t7, sum, sum;
+
+	/* Yuck, 6 superscalar cycles... */
+#define CSUMCOPY_LASTCHUNK(src, dst, sum, off, t0, t1, t2, t3)	\
+	ldd	[src - off - 0x08], t0;				\
+	ldd	[src - off - 0x00], t2;				\
+	addccc	t0, sum, sum;					\
+	st	t0, [dst - off - 0x08];				\
+	addccc	t1, sum, sum;					\
+	st	t1, [dst - off - 0x04];				\
+	addccc	t2, sum, sum;					\
+	st	t2, [dst - off - 0x00];				\
+	addccc	t3, sum, sum;					\
+	st	t3, [dst - off + 0x04];
+
+	/* Handle the end cruft code out of band for better cache patterns. */
+cc_end_cruft:
+	andcc	%o3, 8, %g0		! begin checks for that code
+	be,pn	%icc, 1f
+	 and	%o3, 4, %g5
+	EX(ldd	[%o0 + 0x00], %g2, and %o3, 0xf,#)
+	add	%o1, 8, %o1
+	addcc	%g2, %g7, %g7
+	add	%o0, 8, %o0
+	addccc	%g3, %g7, %g7
+	EX2(st	%g2, [%o1 - 0x08],#)
+	addc	%g0, %g7, %g7
+	EX2(st	%g3, [%o1 - 0x04],#)
+1:	brz,pt	%g5, 1f
+	 andcc	%o3, 3, %o3
+	EX(ld	[%o0 + 0x00], %g2, add %o3, 4,#)
+	add	%o1, 4, %o1
+	addcc	%g2, %g7, %g7
+	EX2(st	%g2, [%o1 - 0x04],#)
+	addc	%g0, %g7, %g7
+	add	%o0, 4, %o0
+1:	brz,pn	%o3, 1f
+	 addcc	%o3, -1, %g0
+	bne,pn	%icc, 2f
+	 subcc	%o3, 2, %o3
+	ba,pt	%xcc, 4f
+	 clr	%o4
+2:	EX(lduh	[%o0 + 0x00], %o4, add %o3, 2,#)
+	add	%o0, 2, %o0
+	EX2(sth	%o4, [%o1 + 0x00],#)
+	be,pn	%icc, 6f
+	 add	%o1, 2, %o1
+	sll	%o4, 16, %o4
+4:	EX(ldub	[%o0 + 0x00], %o5, add %g0, 1,#)
+	EX2(stb	%o5, [%o1 + 0x00],#)
+	sll	%o5, 8, %o5
+	or	%o5, %o4, %o4
+6:	addcc	%o4, %g7, %g7
+1:	sllx	%g4, 32, %g4
+	retl
+	 addc	%g0, %g7, %o0
+
+	/* Sun, you just can't beat me, you just can't.  Stop trying,
+	 * give up.  I'm serious, I am going to kick the living shit
+	 * out of you, game over, lights out.
+	 */
+	.align	8
+	.globl	C_LABEL(__csum_partial_copy_sparc_generic)
+C_LABEL(__csum_partial_copy_sparc_generic):
+					/* %o0=src, %o1=dest, %g1=len, %g7=sum */
+	xor	%o0, %o1, %o4		! get changing bits
+	andcc	%o4, 3, %g0		! check for mismatched alignment
+	bne,pn	%icc, ccslow		! better this than unaligned/fixups
+	 andcc	%o0, 7, %g0		! need to align things?
+	be,pt	%icc, cc_dword_aligned	! yes, we check for short lengths there
+	 andn	%g1, 0x7f, %g2		! can we use unrolled loop?
+	cmp	%g1, 6
+	bl,a,pn	%icc, ccte
+	 andcc	%g1, 0xf, %o3
+	andcc	%o0, 0x1, %g0
+	bne,pn	%icc, ccslow
+	 andcc	%o0, 0x2, %g0
+	be,pn	%icc, 1f
+	 andcc	%o0, 0x4, %g0
+	EX(lduh	[%o0 + 0x00], %g4, add %g1, 0,#)
+	sub	%g1, 2, %g1
+	EX2(sth	%g4, [%o1 + 0x00],#)
+	add	%o0, 2, %o0
+	sll	%g4, 16, %g4
+	addcc	%g4, %g7, %g7
+	add	%o1, 2, %o1
+	srl	%g7, 16, %g3
+	addc	%g0, %g3, %g4
+	sll	%g7, 16, %g7
+	sll	%g4, 16, %g3
+	srl	%g7, 16, %g7
+	andcc	%o0, 0x4, %g0
+	or	%g3, %g7, %g7
+1:	be,pt	%icc, 3f
+	 andn	%g1, 0x7f, %g0
+	EX(ld	[%o0 + 0x00], %g4, add %g1, 0,#)
+	sub	%g1, 4, %g1
+	EX2(st	%g4, [%o1 + 0x00],#)
+	add	%o0, 4, %o0
+	addcc	%g4, %g7, %g7
+	add	%o1, 4, %o1
+	addc	%g0, %g7, %g7
+cc_dword_aligned:
+3:	brz,pn	%g2, 3f			! nope, less than one loop remains
+	 andcc	%o1, 4, %g0		! dest aligned on 4 or 8 byte boundry?
+	be,pn	%icc, ccdbl + 4		! 8 byte aligned, kick ass
+5:	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+10:	EXT(5b, 10b, 20f,#)		! note for exception handling
+	sub	%g1, 128, %g1		! detract from length
+	addc	%g0, %g7, %g7		! add in last carry bit
+	andncc	%g1, 0x7f, %g0		! more to csum?
+	add	%o0, 128, %o0		! advance src ptr
+	bne,pt	%icc, 5b		! we did not go negative, continue looping
+	 add	%o1, 128, %o1		! advance dest ptr
+3:	andcc	%g1, 0x70, %o2		! can use table?
+ccmerge:be,pn	%icc, ccte		! nope, go and check for end cruft
+	 andcc	%g1, 0xf, %o3		! get low bits of length (clears carry btw)
+	srl	%o2, 1, %o4		! begin negative offset computation
+13:	rd	%pc, %o5		! set up table ptr end
+	add	%o0, %o2, %o0		! advance src ptr
+	sub	%o5, %o4, %o5		! continue table calculation
+	sll	%o2, 1, %g2		! constant multiplies are fun...
+	sub	%o5, %g2, %o5		! some more adjustments
+	jmpl	%o5 + (12f-13b), %g0	! jump into it, duff style, wheee...
+	 add	%o1, %o2, %o1		! advance dest ptr (carry is clear btw)
+cctbl:	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x68,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x58,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x48,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x38,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x28,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x18,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x08,%g2,%g3,%g4,%g5)
+12:	EXT(cctbl, 12b, 22f,#)		! note for exception table handling
+	addc	%g0, %g7, %g7
+	andcc	%o3, 0xf, %g0		! check for low bits set
+ccte:	bne,pn	%icc, cc_end_cruft	! something left, handle it out of band
+	 sethi	%uhi(KERNBASE), %g4	! restore gfp
+	mov	%g7, %o0		! give em the computed checksum
+	retl				! return
+	 sllx	%g4, 32, %g4		! finish gfp restoration
+ccdbl:	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+11:	EXT(ccdbl, 11b, 21f,#)		! note for exception table handling
+	sub	%g1, 128, %g1		! detract from length
+	addc	%g0, %g7, %g7		! add in last carry bit
+	andncc	%g1, 0x7f, %g0		! more to csum?
+	add	%o0, 128, %o0		! advance src ptr
+	bne,pt	%icc, ccdbl		! we did not go negative, continue looping
+	 add	%o1, 128, %o1		! advance dest ptr
+	ba,pt	%xcc, ccmerge		! finish it off, above
+	 andcc	%g1, 0x70, %o2		! can use table? (clears carry btw)
+
+ccslow:	mov	0, %g5
+	brlez,pn %g1, 4f
+	 andcc	%o0, 1, %o5		
+	be,a,pt	%icc, 1f
+	 srl	%g1, 1, %o3		
+	sub	%g1, 1, %g1	
+	EX(ldub	[%o0], %g5, add %g1, 1,#)
+	add	%o0, 1, %o0	
+	EX2(stb	%g5, [%o1],#)
+	srl	%g1, 1, %o3
+	add	%o1, 1, %o1
+1:	brz,a,pn %o3, 3f
+	 andcc	%g1, 1, %g0
+	andcc	%o0, 2, %g0	
+	be,a,pt	%icc, 1f
+	 srl	%o3, 1, %o3
+	EX(lduh	[%o0], %o4, add %g1, 0,#)
+	sub	%g1, 2, %g1	
+	srl	%o4, 8, %g2
+	sub	%o3, 1, %o3	
+	EX2(stb	%g2, [%o1],#)
+	add	%o4, %g5, %g5
+	EX2(stb	%o4, [%o1 + 1],#)
+	add	%o0, 2, %o0	
+	srl	%o3, 1, %o3
+	add	%o1, 2, %o1
+1:	brz,a,pn %o3, 2f		
+	 andcc	%g1, 2, %g0
+	EX3(ld	[%o0], %o4,#)
+5:	srl	%o4, 24, %g2
+	srl	%o4, 16, %g3
+	EX2(stb	%g2, [%o1],#)
+	srl	%o4, 8, %g2
+	EX2(stb	%g3, [%o1 + 1],#)
+	add	%o0, 4, %o0
+	EX2(stb	%g2, [%o1 + 2],#)
+	addcc	%o4, %g5, %g5
+	EX2(stb	%o4, [%o1 + 3],#)
+	addc	%g5, %g0, %g5	! I am now to lazy to optimize this (question is if it
+	add	%o1, 4, %o1	! is worthy). Maybe some day - with the sll/srl
+	subcc	%o3, 1, %o3	! tricks
+	bne,a,pt %icc, 5b
+	 EX3(ld	[%o0], %o4,#)
+	sll	%g5, 16, %g2
+	srl	%g5, 16, %g5
+	srl	%g2, 16, %g2
+	andcc	%g1, 2, %g0
+	add	%g2, %g5, %g5 
+2:	be,a,pt	%icc, 3f		
+	 andcc	%g1, 1, %g0
+	EX(lduh	[%o0], %o4, and %g1, 3,#)
+	andcc	%g1, 1, %g0
+	srl	%o4, 8, %g2
+	add	%o0, 2, %o0	
+	EX2(stb	%g2, [%o1],#)
+	add	%g5, %o4, %g5
+	EX2(stb	%o4, [%o1 + 1],#)
+	add	%o1, 2, %o1
+3:	be,a,pt	%icc, 1f		
+	 sll	%g5, 16, %o4
+	EX(ldub	[%o0], %g2, add %g0, 1,#)
+	sll	%g2, 8, %o4	
+	EX2(stb	%g2, [%o1],#)
+	add	%g5, %o4, %g5
+	sll	%g5, 16, %o4
+1:	addcc	%o4, %g5, %g5
+	srl	%g5, 16, %o4
+	addc	%g0, %o4, %g5
+	brz,pt	%o5, 4f
+	 srl	%g5, 8, %o4
+	and	%g5, 0xff, %g2
+	and	%o4, 0xff, %o4
+	sll	%g2, 8, %g2
+	or	%g2, %o4, %g5
+4:	addcc	%g7, %g5, %g7
+	retl	
+	 addc	%g0, %g7, %o0
+C_LABEL(__csum_partial_copy_end):
+
+        .section .fixup,#alloc,#execinstr
+        .align  4
+/* We do these strange calculations for the csum_*_from_user case only, ie.
+ * we only bother with faults on loads... */
+
+/* o2 = ((g2%20)&3)*8
+ * o3 = g1 - (g2/20)*32 - o2 */
+20:
+	cmp	%g2, 20
+	blu,a,pn %icc, 1f
+	 and	%g2, 3, %o2
+	sub	%g1, 32, %g1
+	ba,pt	%xcc, 20b
+	 sub	%g2, 20, %g2
+1:
+	sll	%o2, 3, %o2
+	ba,pt	%xcc, 31f
+	 sub	%g1, %o2, %o3
+
+/* o2 = (!(g2 & 15) ? 0 : (((g2 & 15) + 1) & ~1)*8)
+ * o3 = g1 - (g2/16)*32 - o2 */
+21:
+	andcc	%g2, 15, %o3
+	srl	%g2, 4, %g2
+	be,a,pn	%icc, 1f
+	 clr	%o2
+	add	%o3, 1, %o3
+	and	%o3, 14, %o3
+	sll	%o3, 3, %o2
+1:
+	sll	%g2, 5, %g2
+	sub	%g1, %g2, %o3
+	ba,pt	%xcc, 31f
+	 sub	%o3, %o2, %o3
+
+/* o0 += (g2/10)*16 - 0x70
+ * 01 += (g2/10)*16 - 0x70
+ * o2 = (g2 % 10) ? 8 : 0
+ * o3 += 0x70 - (g2/10)*16 - o2 */
+22:
+	cmp	%g2, 10
+	blu,a,pt %xcc, 1f
+	 sub	%o0, 0x70, %o0
+	add	%o0, 16, %o0
+	add	%o1, 16, %o1
+	sub	%o3, 16, %o3
+	ba,pt	%xcc, 22b
+	 sub	%g2, 10, %g2
+1:
+	sub	%o1, 0x70, %o1
+	add	%o3, 0x70, %o3
+	clr	%o2
+	movne	%g2, 8, %o2
+	ba,pt	%xcc, 31f
+	 sub	%o3, %o2, %o3
+96:
+	and	%g1, 3, %g1
+	sll	%o3, 2, %o3
+	add	%g1, %o3, %o3
+30:
+/* %o1 is dst
+ * %o3 is # bytes to zero out
+ * %o4 is faulting address
+ * %o5 is %pc where fault occured */
+	clr	%o2
+31:
+/* %o0 is src
+ * %o1 is dst
+ * %o2 is # of bytes to copy from src to dst
+ * %o3 is # bytes to zero out
+ * %o4 is faulting address
+ * %o5 is %pc where fault occured */
+	save	%sp, -136, %sp
+        mov     %i5, %o0
+        mov     %i7, %o1
+        mov	%i4, %o2
+        call    C_LABEL(lookup_fault)
+	 mov	%g7, %i4
+	cmp	%o0, 2
+	bne,pn	%icc, 1f
+	 add	%g0, -EFAULT, %i5
+	brz,pn	%i2, 2f
+	 mov	%i0, %o1
+	mov	%i1, %o0
+5:
+	call	C_LABEL(__memcpy)
+	 mov	%i2, %o2
+	brnz,a,pn %o0, 2f
+	 add	%i3, %i2, %i3
+	add	%i1, %i2, %i1
+2:
+	mov	%i1, %o0
+	call	C_LABEL(__bzero)
+	 mov	%i3, %o1
+1:
+	ldx	[%sp + 264], %o2		! struct_ptr of parent
+	st	%i5, [%o2]
+	ret
+	 restore
+
+        .section __ex_table,#alloc
+        .align 4
+        .word 5b,2
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/lib/locks.S linux/arch/sparc64/lib/locks.S
--- v2.1.29/linux/arch/sparc64/lib/locks.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/lib/locks.S	Thu Mar 20 16:43:32 1997
@@ -0,0 +1,77 @@
+/* $Id: locks.S,v 1.2 1997/03/10 12:28:02 jj Exp $
+ * locks.S: SMP low-level lock primitives on Sparc64.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/ptrace.h>
+
+	.text
+	.align	4
+
+	.globl	__spinlock_waitfor
+__spinlock_waitfor:
+1:	orcc	%g2, 0x0, %g0
+	bne	1b
+	 ldub	[%g1], %g2
+	ldstub	[%g1], %g2
+	jmpl	%o7 - 12, %g0
+	 mov	%g5, %o7
+
+	.globl	___become_idt
+___become_idt:
+#if 0 /* Don't know how to do this on the Ultra yet... */
+#endif
+	jmpl	%o7 + 8, %g0
+	 mov	%g5, %o7
+
+___lk_busy_spin:
+	orcc	%g2, 0, %g0
+	bne	___lk_busy_spin
+	 ldub	[%g1 + 0], %g2
+	b	1f
+	 ldstub	[%g1 + 0], %g2
+
+	.globl	___lock_kernel
+___lock_kernel:
+	addcc	%g2, -1, %g2
+	rdpr	%pil, %g3
+	bcs,a	9f
+	 st	%g2, [%g6 + AOFF_task_lock_depth]
+	wrpr	15, %pil
+	ldstub	[%g1 + 0], %g2
+1:	orcc	%g2, 0, %g0
+	bne,a	___lk_busy_spin
+	 ldub	[%g1 + 0], %g2
+	ldub	[%g1 + 2], %g2
+	cmp	%g2, %g5
+	be	2f
+	 stb	%g5, [%g1 + 1]
+	stb	%g5, [%g1 + 2]
+#ifdef __SMP__
+	/* XXX Figure out how to become interrupt receiver in SMP system. */
+#endif
+2:	mov	-1, %g2
+	st	%g2, [%g6 + AOFF_task_lock_depth]
+	wrpr	%g3, %pil
+9:	jmpl	%o7 + 0x8, %g0
+	 mov	%g5, %o7
+
+#undef NO_PROC_ID
+#define NO_PROC_ID	0xff
+
+	.globl	___unlock_kernel
+___unlock_kernel:
+	addcc	%g2, 1, %g2
+	rdpr	%pil, %g3
+	bne,a	1f
+	 st	%g2, [%g6 + AOFF_task_lock_depth]
+	wrpr	15, %pil
+	mov	NO_PROC_ID, %g2
+	stb	%g2, [%g1 + 1]
+	stb	%g0, [%g1 + 0]
+	st	%g0, [%g6 + AOFF_task_lock_depth]
+	wrpr	%g3, %pil
+1:	jmpl	%o7 + 0x8, %g0
+	 mov	%g5, %o7
+	
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/lib/memcmp.S linux/arch/sparc64/lib/memcmp.S
--- v2.1.29/linux/arch/sparc64/lib/memcmp.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/lib/memcmp.S	Thu Mar 20 16:43:32 1997
@@ -0,0 +1,29 @@
+/* $Id: memcmp.S,v 1.1 1997/03/14 21:04:23 jj Exp $
+ * Sparc64 optimized memcmp code.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+	.text
+	.align 4
+	.globl __memcmp, memcmp
+__memcmp:
+memcmp:
+	brlez,pn %o2, 2f
+	 sub	%g0, %o2, %o3
+	add	%o0, %o2, %o0
+	add	%o1, %o2, %o1
+	ldub	[%o0 + %o3], %o4
+1:
+	ldub	[%o1 + %o3], %o5
+	sub	%o4, %o5, %o4
+	brnz,pn	%o4, 3f
+	 addcc	%o3, 1, %o3
+	bne,a,pt %xcc, 1b
+	 ldub	[%o0 + %o3], %o5
+2:
+	retl
+	 clr	%o0
+3:
+	retl
+	 mov	%o4, %o0
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/lib/memcpy.S linux/arch/sparc64/lib/memcpy.S
--- v2.1.29/linux/arch/sparc64/lib/memcpy.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/lib/memcpy.S	Thu Mar 20 16:43:32 1997
@@ -0,0 +1,526 @@
+/* memcpy.S: Sparc optimized memcpy, bcopy and memmove code
+ * Hand optimized from GNU libc's memcpy, bcopy and memmove
+ * for UltraSparc
+ * Copyright (C) 1991,1996 Free Software Foundation
+ * Copyright (C) 1995 Linus Torvalds (Linus.Torvalds@helsinki.fi)
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <asm/asi.h>
+#include <asm/head.h>
+
+#ifdef __KERNEL__
+
+#define FUNC(x) 											\
+	.globl	x;											\
+	.type	x,@function;										\
+	.align	4;											\
+x:
+
+#define FASTER_ALIGNED
+
+/* In kernel these functions don't return a value.
+ * One should use macros in asm/string.h for that purpose.
+ * We return 0, so that bugs are more apparent.
+ */
+#define SETUP_RETL
+#define PRE_RETL	sethi	%uhi(KERNBASE), %g4; clr %o0
+#define RETL_INSN	sllx	%g4, 32, %g4
+
+#else
+
+/* libc */
+
+#define FASTER_ALIGNED
+
+#ifdef DEBUG
+#define FUNC(x) 											\
+	.globl	jj##x##1;										\
+	.type	jj##x##1,@function;									\
+	.align	4;											\
+jj##x##1:
+#else
+#include "DEFS.h"
+#endif
+
+#define SETUP_RETL	mov	%o0, %g6
+#define PRE_RETL
+#define RETL_INSN	mov	%g6, %o0
+
+#endif
+
+#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) 				\
+	ldd	[%src + offset + 0x00], %t0; 								\
+	ldd	[%src + offset + 0x08], %t2; 								\
+	ldd	[%src + offset + 0x10], %t4; 								\
+	ldd	[%src + offset + 0x18], %t6; 								\
+	stw	%t0, [%dst + offset + 0x00]; 								\
+	stw	%t1, [%dst + offset + 0x04]; 								\
+	stw	%t2, [%dst + offset + 0x08]; 								\
+	stw	%t3, [%dst + offset + 0x0c]; 								\
+	stw	%t4, [%dst + offset + 0x10]; 								\
+	stw	%t5, [%dst + offset + 0x14]; 								\
+	stw	%t6, [%dst + offset + 0x18]; 								\
+	stw	%t7, [%dst + offset + 0x1c];
+
+#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) 				\
+	ldx	[%src + offset + 0x00], %t0; 								\
+	ldx	[%src + offset + 0x08], %t1; 								\
+	ldx	[%src + offset + 0x10], %t2; 								\
+	ldx	[%src + offset + 0x18], %t3; 								\
+	ldx	[%src + offset + 0x20], %t4; 								\
+	ldx	[%src + offset + 0x28], %t5; 								\
+	ldx	[%src + offset + 0x30], %t6; 								\
+	ldx	[%src + offset + 0x38], %t7; 								\
+	stx	%t0, [%dst + offset + 0x00]; 								\
+	stx	%t1, [%dst + offset + 0x08]; 								\
+	stx	%t2, [%dst + offset + 0x10]; 								\
+	stx	%t3, [%dst + offset + 0x18]; 								\
+	stx	%t4, [%dst + offset + 0x20]; 								\
+	stx	%t5, [%dst + offset + 0x28]; 								\
+	stx	%t6, [%dst + offset + 0x30]; 								\
+	stx	%t7, [%dst + offset + 0x38];
+
+#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) 						\
+	ldd	[%src - offset - 0x10], %t0; 								\
+	ldd	[%src - offset - 0x08], %t2; 								\
+	stw	%t0, [%dst - offset - 0x10]; 								\
+	stw	%t1, [%dst - offset - 0x0c]; 								\
+	stw	%t2, [%dst - offset - 0x08]; 								\
+	stw	%t3, [%dst - offset - 0x04];
+
+#define MOVE_LASTALIGNCHUNK(src, dst, offset, t0, t1) 							\
+	ldx	[%src - offset - 0x10], %t0; 								\
+	ldx	[%src - offset - 0x08], %t1; 								\
+	stx	%t0, [%dst - offset - 0x10]; 								\
+	stx	%t1, [%dst - offset - 0x08];
+
+#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) 							\
+	ldub	[%src - offset - 0x02], %t0; 								\
+	ldub	[%src - offset - 0x01], %t1; 								\
+	stb	%t0, [%dst - offset - 0x02]; 								\
+	stb	%t1, [%dst - offset - 0x01];
+
+	.text
+	.align	4
+
+FUNC(bcopy)
+
+	mov		%o0, %o3
+	mov		%o1, %o0
+	mov		%o3, %o1
+	brgez,a,pt	%o2, 1f
+	 cmp		%o0, %o1
+
+	retl
+	 nop		! Only bcopy returns here and it retuns void...
+
+#ifdef __KERNEL__
+FUNC(amemmove)
+FUNC(__memmove)
+#endif
+FUNC(memmove)
+
+	cmp		%o0, %o1
+1:
+	SETUP_RETL
+	bleu,pt		%xcc, 9f
+	 sub		%o0, %o1, %o4
+
+	add		%o1, %o2, %o3
+	cmp		%o3, %o0
+	bleu,pt		%xcc, 0f
+	 andcc		%o4, 3, %o5
+
+	add		%o1, %o2, %o1
+	add		%o0, %o2, %o0
+	sub		%o1, 1, %o1
+	sub		%o0, 1, %o0
+	
+1:
+	ldub		[%o1], %o4
+	subcc		%o2, 1, %o2
+	sub		%o1, 1, %o1
+	stb		%o4, [%o0]
+	bne,pt		%icc, 1b
+	 sub		%o0, 1, %o0
+
+	PRE_RETL
+	retl
+	 RETL_INSN
+
+#ifdef __KERNEL__
+FUNC(__memcpy)
+#endif
+FUNC(memcpy)	/* %o0=dst %o1=src %o2=len */
+
+	sub		%o0, %o1, %o4
+	SETUP_RETL
+9:
+	andcc		%o4, 3, %o5
+0:
+	bne,pn		%icc, 86f
+	 cmp		%o2, 15
+
+	bleu,pn		%xcc, 90f
+	 andcc		%o1, 3, %g0
+
+	be,a,pt		%icc, 3f		! check if we need to align
+	 andcc		%o1, 4, %g0
+
+	andcc		%o1, 1, %g0
+	be,pn		%icc, 4f
+	 andcc		%o1, 2, %g0
+
+	ldub		[%o1], %g2
+	add		%o1, 1, %o1
+	sub		%o2, 1, %o2
+	stb		%g2, [%o0]
+	bne,pn		%icc, 5f
+	 add		%o0, 1, %o0
+4:
+	lduh		[%o1], %g2
+	add		%o1, 2, %o1
+	sub		%o2, 2, %o2
+	sth		%g2, [%o0]
+	add		%o0, 2, %o0
+5:
+	andcc		%o1, 4, %g0
+3:
+	be,pn		%icc, 2f
+	 mov		%o2, %g1
+
+	lduw		[%o1], %o4
+	sub		%g1, 4, %g1
+	stw		%o4, [%o0]
+	add		%o1, 4, %o1
+	add		%o0, 4, %o0
+2:
+	andcc		%g1, -128, %g7
+	be,pn		%xcc, 3f
+	 andcc		%o0, 4, %g0
+
+	be,a,pn		%icc, 82f + 4
+	 ldx		[%o1], %o2
+5:
+	MOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
+	subcc		%g7, 128, %g7
+	add		%o1, 128, %o1
+	bne,pt		%xcc, 5b
+	 add		%o0, 128, %o0
+3:
+	andcc		%g1, 0x70, %g7
+	be,pn		%icc, 80f
+	 andcc		%g1, 8, %g0
+79:
+	rd		%pc, %o5
+	srl		%g7, 1, %o4
+	add		%g7, %o4, %o4
+	add		%o1, %g7, %o1
+	sub		%o5, %o4, %o5
+	jmpl		%o5 + %lo(80f-79b), %g0
+	 add		%o0, %g7, %o0
+
+	MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5)
+
+80:	/* memcpy_table_end */
+	be,pt		%icc, 81f
+	 andcc		%g1, 4, %g0
+
+	ldd		[%o1], %g2
+	add		%o0, 8, %o0
+	stw		%g2, [%o0 - 0x08]
+	add		%o1, 8, %o1
+	stw		%g3, [%o0 - 0x04]
+
+81:	/* memcpy_last7 */
+
+	be,pt		%icc, 1f
+	 andcc		%g1, 2, %g0
+
+	lduw		[%o1], %g2
+	add		%o1, 4, %o1
+	stw		%g2, [%o0]
+	add		%o0, 4, %o0
+1:
+	be,pt		%icc, 1f
+	 andcc		%g1, 1, %g0
+
+	lduh		[%o1], %g2
+	add		%o1, 2, %o1
+	sth		%g2, [%o0]
+	add		%o0, 2, %o0
+1:
+	be,pt		%icc, 1f
+	 nop
+
+	ldub		[%o1], %g2
+	stb		%g2, [%o0]
+1:
+	PRE_RETL
+	retl
+ 	 RETL_INSN
+
+82:	/* ldx_stx */
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
+	subcc		%g7, 128, %g7
+	add		%o1, 128, %o1
+	bne,pt		%xcc, 82b
+	 add		%o0, 128, %o0
+
+#ifndef FASTER_ALIGNED
+
+	andcc		%g1, 0x70, %g7
+	be,pn		%icc, 80b
+	 andcc		%g1, 8, %g0
+83:
+	rd		%pc, %o5
+	srl		%g7, 1, %o4
+	add		%g7, %o4, %o4
+	add		%o1, %g7, %o1
+	sub		%o5, %o4, %o5
+	jmpl		%o5 + %lo(80b - 83b), %g0
+	 add		%o0, %g7, %o0
+
+#else /* FASTER_ALIGNED */
+
+	andcc		%g1, 0x70, %g7
+	be,pn		%icc, 84f
+	 andcc		%g1, 8, %g0
+83:
+	rd		%pc, %o5
+	add		%o1, %g7, %o1
+	sub		%o5, %g7, %o5
+	jmpl		%o5 + %lo(84f - 83b), %g0
+	 add		%o0, %g7, %o0
+
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x60, g2, g3)
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x50, g2, g3)
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x40, g2, g3)
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x30, g2, g3)
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x20, g2, g3)
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x10, g2, g3)
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x00, g2, g3)
+
+84:	/* amemcpy_table_end */
+	be,pt		%icc, 85f
+	 andcc		%g1, 4, %g0
+
+	ldx		[%o1], %g2
+	add		%o1, 8, %o1
+	stx		%g2, [%o0]
+	add		%o0, 8, %o0
+85:	/* amemcpy_last7 */
+	be,pt		%icc, 1f
+	 andcc		%g1, 2, %g0
+
+	lduw		[%o1], %g2
+	add		%o1, 4, %o1
+	stw		%g2, [%o0]
+	add		%o0, 4, %o0
+1:
+	be,pt		%icc, 1f
+	 andcc		%g1, 1, %g0
+
+	lduh		[%o1], %g2
+	add		%o1, 2, %o1
+	sth		%g2, [%o0]
+	add		%o0, 2, %o0
+1:
+	be,pt		%icc, 1f
+	 nop
+
+	ldub		[%o1], %g2
+	stb		%g2, [%o0]
+1:
+	PRE_RETL
+	retl
+ 	 RETL_INSN
+
+#endif /* FASTER_ALIGNED */
+
+86:	/* non_aligned */
+	cmp		%o2, 15
+	bleu,pn		%xcc, 88f
+
+	 andcc		%o0, 3, %g0
+	be,pn		%icc, 61f
+	 andcc		%o0, 1, %g0
+	be,pn		%icc, 60f
+	 andcc		%o0, 2, %g0
+
+	ldub		[%o1], %g5
+	add		%o1, 1, %o1
+	stb		%g5, [%o0]
+	sub		%o2, 1, %o2
+	bne,pn		%icc, 61f
+	 add		%o0, 1, %o0
+60:
+	ldub		[%o1], %g3
+	add		%o1, 2, %o1
+	stb		%g3, [%o0]
+	sub		%o2, 2, %o2
+	ldub		[%o1 - 1], %g3
+	add		%o0, 2, %o0
+	stb		%g3, [%o0 - 1]
+61:
+	and		%o1, 3, %g2
+	and		%o2, 0xc, %g3
+	and		%o1, -4, %o1
+	cmp		%g3, 4
+	sll		%g2, 3, %g4
+	mov		32, %g2
+	be,pn		%icc, 4f
+	 sub		%g2, %g4, %g7
+	
+	blu,pn		%icc, 3f
+	 cmp		%g3, 0x8
+
+	be,pn		%icc, 2f
+	 srl		%o2, 2, %g3
+
+	lduw		[%o1], %o3
+	add		%o0, -8, %o0
+	lduw		[%o1 + 4], %o4
+	b		8f
+	 add		%g3, 1, %g3
+2:
+	lduw		[%o1], %o4
+	add		%o0, -12, %o0
+	lduw		[%o1 + 4], %o5
+	add		%g3, 2, %g3
+	b		9f
+	 add		%o1, -4, %o1
+3:
+	lduw		[%o1], %g1
+	add		%o0, -4, %o0
+	lduw		[%o1 + 4], %o3
+	srl		%o2, 2, %g3
+	b		7f
+	 add		%o1, 4, %o1
+4:
+	lduw		[%o1], %o5
+	cmp		%o2, 7
+	lduw		[%o1 + 4], %g1
+	srl		%o2, 2, %g3
+	bleu,pn		%xcc, 10f
+	 add		%o1, 8, %o1
+
+	lduw		[%o1], %o3
+	add		%g3, -1, %g3
+5:
+	sll		%o5, %g4, %g2
+	srl		%g1, %g7, %g5
+	or		%g2, %g5, %g2
+	stw		%g2, [%o0]
+7:
+	lduw		[%o1 + 4], %o4
+	sll		%g1, %g4, %g2
+	srl		%o3, %g7, %g5
+	or		%g2, %g5, %g2
+	stw		%g2, [%o0 + 4]
+8:
+	lduw		[%o1 + 8], %o5
+	sll		%o3, %g4, %g2
+	srl		%o4, %g7, %g5
+	or		%g2, %g5, %g2
+	stw		%g2, [%o0 + 8]
+9:
+	lduw		[%o1 + 12], %g1
+	sll		%o4, %g4, %g2
+	srl		%o5, %g7, %g5
+	addcc		%g3, -4, %g3
+	or		%g2, %g5, %g2
+	add		%o1, 16, %o1
+	stw		%g2, [%o0 + 12]
+	add		%o0, 16, %o0
+	bne,a,pt	%xcc, 5b
+	 lduw		[%o1], %o3
+10:
+	sll		%o5, %g4, %g2
+	srl		%g1, %g7, %g5
+	srl		%g7, 3, %g3
+	or		%g2, %g5, %g2
+	sub		%o1, %g3, %o1
+	andcc		%o2, 2, %g0
+	stw		%g2, [%o0]
+	be,pt		%icc, 1f
+	 andcc		%o2, 1, %g0
+
+	ldub		[%o1], %g2
+	add		%o1, 2, %o1
+	stb		%g2, [%o0 + 4]
+	add		%o0, 2, %o0
+	ldub		[%o1 - 1], %g2
+	stb		%g2, [%o0 + 3]
+1:
+	be,pt		%icc, 1f
+	 nop
+
+	ldub		[%o1], %g2
+	stb		%g2, [%o0 + 4]
+1:
+	PRE_RETL
+	retl
+	 RETL_INSN
+
+88:	/* short_end */
+
+	and		%o2, 0xe, %o3
+20:
+	rd		%pc, %o5
+	sll		%o3, 3, %o4
+	add		%o0, %o3, %o0
+	sub		%o5, %o4, %o5
+	add		%o1, %o3, %o1
+	jmpl		%o5 + %lo(89f - 20b), %g0
+	 andcc		%o2, 1, %g0
+
+	MOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3)
+
+89:	/* short_table_end */
+
+	be,pt		%icc, 1f
+	 nop
+
+	ldub		[%o1], %g2
+	stb		%g2, [%o0]
+1:
+	PRE_RETL
+	retl
+ 	 RETL_INSN
+
+90:	/* short_aligned_end */
+	bne,pn		%xcc, 88b
+	 andcc		%o2, 8, %g0
+
+	be,pt		%icc, 1f
+	 andcc		%o2, 4, %g0
+
+	lduw		[%o1 + 0x00], %g2
+	lduw		[%o1 + 0x04], %g3
+	add		%o1, 8, %o1
+	stw		%g2, [%o0 + 0x00]
+	stw		%g3, [%o0 + 0x04]
+	add		%o0, 8, %o0
+1:
+	b		81b
+	 mov		%o2, %g1
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/lib/memscan.S linux/arch/sparc64/lib/memscan.S
--- v2.1.29/linux/arch/sparc64/lib/memscan.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/lib/memscan.S	Thu Mar 20 16:43:32 1997
@@ -0,0 +1,116 @@
+/* $Id: memscan.S,v 1.1 1997/03/14 21:04:24 jj Exp $
+ * memscan.S: Optimized memscan for the Sparc64.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+/* In essence, this is just a fancy strlen. */
+
+#define LO_MAGIC 0x01010101
+#define HI_MAGIC 0x80808080
+
+	.text
+	.align	4
+	.globl	__memscan_zero, __memscan_generic
+	.globl	memscan
+__memscan_zero:
+	/* %o0 = addr, %o1 = size */
+	brlez,pn %o1, 0f
+	 andcc	%o0, 3, %g0
+	be,pt	%icc, 9f
+	 sethi	%hi(HI_MAGIC), %o4
+	ldub	[%o0], %o5
+	subcc	%o1, 1, %o1
+	brz,pn	%o5, 10f
+	 add	%o0, 1, %o0
+	be,pn	%xcc, 0f
+	 andcc	%o0, 3, %g0
+	be,pn	%icc, 4f
+	 or	%o4, %lo(HI_MAGIC), %o3
+	ldub	[%o0], %o5
+	subcc	%o1, 1, %o1
+	brz,pn	%o5, 10f
+	 add	%o0, 1, %o0
+	be,pn	%xcc, 0f
+	 andcc	%o0, 3, %g0
+	be,pt	%icc, 5f
+	 sethi	%hi(LO_MAGIC), %o4
+	ldub	[%o0], %o5
+	subcc	%o1, 1, %o1
+	brz,pn	%o5, 10f
+	 add	%o0, 1, %o0
+	be,pn	%xcc, 0f
+	 or	%o4, %lo(LO_MAGIC), %o2
+	ba,pt	%xcc, 2f
+	 ld	[%o0], %o5
+9:
+	or	%o4, %lo(HI_MAGIC), %o3
+4:
+	sethi	%hi(LO_MAGIC), %o4
+5:
+	or	%o4, %lo(LO_MAGIC), %o2
+	ld	[%o0], %o5
+2:
+	sub	%o5, %o2, %o4
+	sub	%o1, 4, %o1
+	andcc	%o4, %o3, %g0
+	be,pn	%icc, 1f
+	 add	%o0, 4, %o0
+	brgz,pt	%o1, 2b
+	 ld	[%o0], %o5
+
+	retl
+	 add	%o0, %o1, %o0
+1:
+	/* Check every byte. */
+	srl	%o5, 24, %g5
+	andcc	%g5, 0xff, %g0
+	be,pn	%icc, 1f
+	 add	%o0, -4, %o4
+	srl	%o5, 16, %g5
+	andcc	%g5, 0xff, %g0
+	be,pn	%icc, 1f
+	 add	%o4, 1, %o4
+	srl	%o5, 8, %g5
+	andcc	%g5, 0xff, %g0
+	be,pn	%icc, 1f
+	 add	%o4, 1, %o4
+	andcc	%o5, 0xff, %g0
+	be,pn	%icc, 1f
+	 add	%o4, 1, %o4
+	brgz,pt	%o1, 2b
+	 ld	[%o0], %o5
+1:
+	add	%o0, %o1, %o0
+	cmp	%o4, %o0
+	retl
+	 movle	%xcc, %o4, %o0
+0:
+	retl
+	 nop
+10:
+	retl
+	 sub	%o0, 1, %o0
+
+memscan:
+__memscan_generic:
+	/* %o0 = addr, %o1 = c, %o2 = size */
+	brz,pn	%o2, 3f
+	 add	%o0, %o2, %o3
+	ldub	[%o0], %o5
+	sub	%g0, %o2, %o4
+1:
+	cmp	%o5, %o1
+	be,pn	%icc, 2f
+	 addcc	%o4, 1, %o4
+	bne,a,pt %xcc, 1b
+	 ldub	[%o3 + %o4], %o5
+	retl
+	/* The delay slot is the same as the next insn, this is just to make it look more awful */
+2:
+	 add	%o3, %o4, %o0
+	retl
+	 sub	%o0, 1, %o0
+3:
+	retl
+	 nop
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/lib/memset.S linux/arch/sparc64/lib/memset.S
--- v2.1.29/linux/arch/sparc64/lib/memset.S	Mon Mar 17 14:54:23 1997
+++ linux/arch/sparc64/lib/memset.S	Thu Mar 20 16:43:32 1997
@@ -54,7 +54,7 @@
 	.text
 	.align 4
 
-	.globl	__bzero, __memset, 
+	.globl	__bzero, __memset
 	.globl	memset, __memset_start, __memset_end
 __memset_start:
 __memset:
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/lib/strlen.S linux/arch/sparc64/lib/strlen.S
--- v2.1.29/linux/arch/sparc64/lib/strlen.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/lib/strlen.S	Thu Mar 20 16:43:32 1997
@@ -0,0 +1,77 @@
+/* strlen.S: Sparc64 optimized strlen code
+ * Hand optimized from GNU libc's strlen
+ * Copyright (C) 1991,1996 Free Software Foundation
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#define LO_MAGIC 0x01010101
+#define HI_MAGIC 0x80808080
+
+	.align 4
+	.global strlen
+strlen:
+	mov	%o0, %o1
+	andcc	%o0, 3, %g0
+	be,pt	%icc, 9f
+	 sethi	%hi(HI_MAGIC), %o4
+	ldub	[%o0], %o5
+	brz,pn	%o5, 11f
+	 add	%o0, 1, %o0
+	andcc	%o0, 3, %g0
+	be,pn	%icc, 4f
+	 or	%o4, %lo(HI_MAGIC), %o3
+	ldub	[%o0], %o5
+	brz,pn	%o5, 12f
+	 add	%o0, 1, %o0
+	andcc	%o0, 3, %g0
+	be,pt	%icc, 5f
+	 sethi	%hi(LO_MAGIC), %o4
+	ldub	[%o0], %o5
+	brz,pn	%o5, 13f
+	 add	%o0, 1, %o0
+	ba,pt	%icc, 8f
+	 or	%o4, %lo(LO_MAGIC), %o2
+9:
+	or	%o4, %lo(HI_MAGIC), %o3
+4:
+	sethi	%hi(LO_MAGIC), %o4
+5:
+	or	%o4, %lo(LO_MAGIC), %o2
+8:
+	ld	[%o0], %o5
+2:
+	sub	%o5, %o2, %o4
+	andcc	%o4, %o3, %g0
+	be,pt	%icc, 8b
+	 add	%o0, 4, %o0
+
+	/* Check every byte. */
+	srl	%o5, 24, %g5
+	andcc	%g5, 0xff, %g0
+	be,pn	%icc, 1f
+	 add	%o0, -4, %o4
+	srl	%o5, 16, %g5
+	andcc	%g5, 0xff, %g0
+	be,pn	%icc, 1f
+	 add	%o4, 1, %o4
+	srl	%o5, 8, %g5
+	andcc	%g5, 0xff, %g0
+	be,pn	%icc, 1f
+	 add	%o4, 1, %o4
+	andcc	%o5, 0xff, %g0
+	bne,a,pt %icc, 2b
+	 ld	[%o0], %o5
+	add	%o4, 1, %o4
+1:
+	retl
+	 sub	%o4, %o1, %o0
+11:
+	retl
+	 mov	0, %o0
+12:
+	retl
+	 mov	1, %o0
+13:
+	retl
+	 mov	2, %o0
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/lib/strlen_user.S linux/arch/sparc64/lib/strlen_user.S
--- v2.1.29/linux/arch/sparc64/lib/strlen_user.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/lib/strlen_user.S	Thu Mar 20 16:43:32 1997
@@ -0,0 +1,99 @@
+/* strlen_user.S: Sparc64 optimized strlen_user code
+ *
+ * Return length of string in userspace including terminating 0
+ * or 0 for error
+ *
+ * Copyright (C) 1991,1996 Free Software Foundation
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#define LO_MAGIC 0x01010101
+#define HI_MAGIC 0x80808080
+
+	.align 4
+	.global __strlen_user
+__strlen_user:
+	mov	%o0, %o1
+	andcc	%o0, 3, %g0
+	be,pt	%icc, 9f
+	 sethi	%hi(HI_MAGIC), %o4
+10:
+	ldub	[%o0], %o5
+	brz,pn	%o5, 21f
+	 add	%o0, 1, %o0
+	andcc	%o0, 3, %g0
+	be,pn	%icc, 4f
+	 or	%o4, %lo(HI_MAGIC), %o3
+11:
+	ldub	[%o0], %o5
+	brz,pn	%o5, 22f
+	 add	%o0, 1, %o0
+	andcc	%o0, 3, %g0
+	be,pt	%icc, 5f
+	 sethi	%hi(LO_MAGIC), %o4
+12:
+	ldub	[%o0], %o5
+	brz,pn	%o5, 23f
+	 add	%o0, 1, %o0
+	ba,pt	%icc, 13f
+	 or	%o4, %lo(LO_MAGIC), %o2
+9:
+	or	%o4, %lo(HI_MAGIC), %o3
+4:
+	sethi	%hi(LO_MAGIC), %o4
+5:
+	or	%o4, %lo(LO_MAGIC), %o2
+13:
+	ld	[%o0], %o5
+2:
+	sub	%o5, %o2, %o4
+	andcc	%o4, %o3, %g0
+	be,pt	%icc, 13b
+	 add	%o0, 4, %o0
+
+	/* Check every byte. */
+	srl	%o5, 24, %g5
+	andcc	%g5, 0xff, %g0
+	be,pn	%icc, 1f
+	 add	%o0, -3, %o4
+	srl	%o5, 16, %g5
+	andcc	%g5, 0xff, %g0
+	be,pn	%icc, 1f
+	 add	%o4, 1, %o4
+	srl	%o5, 8, %g5
+	andcc	%g5, 0xff, %g0
+	be,pn	%icc, 1f
+	 add	%o4, 1, %o4
+	andcc	%o5, 0xff, %g0
+	bne,a,pt %icc, 2b
+14:
+	 ld	[%o0], %o5
+	add	%o4, 1, %o4
+1:
+	retl
+	 sub	%o4, %o1, %o0
+21:
+	retl
+	 mov	1, %o0
+22:
+	retl
+	 mov	2, %o0
+23:
+	retl
+	 mov	3, %o0
+
+        .section .fixup,#alloc,#execinstr
+        .align  4
+30:
+        retl
+         clr    %o0
+
+	.section __ex_table,#alloc
+	.align	4
+
+	.word	10b, 30b
+	.word	11b, 30b
+	.word	12b, 30b
+	.word	13b, 30b
+	.word	14b, 30b
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/lib/strncmp.S linux/arch/sparc64/lib/strncmp.S
--- v2.1.29/linux/arch/sparc64/lib/strncmp.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/lib/strncmp.S	Thu Mar 20 16:43:32 1997
@@ -0,0 +1,31 @@
+/* $Id: strncmp.S,v 1.2 1997/03/11 17:51:44 jj Exp $
+ * Sparc64 optimized strncmp code.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <asm/asi.h>
+
+	.text
+	.align 4
+	.global __strncmp, strncmp
+__strncmp:
+strncmp:
+	brlez,pn %o2, 3f
+	 lduba	[%o0] (ASI_PNF), %o3
+1:
+	add	%o0, 1, %o0
+	ldub	[%o1], %o4
+	brz,pn	%o3, 2f
+	 add	%o1, 1, %o1
+	cmp	%o3, %o4
+	bne,pn	%icc, 2f
+	 subcc	%o2, 1, %o2
+	bne,a,pt %xcc, 1b
+	 ldub	[%o0], %o3
+2:
+	retl
+	 sub	%o3, %o4, %o0
+3:
+	retl
+	 clr	%o0
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/lib/strncpy_from_user.S linux/arch/sparc64/lib/strncpy_from_user.S
--- v2.1.29/linux/arch/sparc64/lib/strncpy_from_user.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/lib/strncpy_from_user.S	Thu Mar 20 16:43:32 1997
@@ -0,0 +1,54 @@
+/* strncpy_from_user.S: Sparc64 strncpy from userspace.
+ *
+ *  Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <asm/asi.h>
+#include <asm/errno.h>
+
+	.text
+	.align	4
+
+	/* Must return:
+	 *
+	 * -EFAULT		for an exception
+	 * count		if we hit the buffer limit
+	 * bytes copied		if we hit a null byte
+	 */
+
+	.globl	__strncpy_from_user
+__strncpy_from_user:
+	/* %o0=dest, %o1=src, %o2=count */
+	brlez,pn %o2, 3f
+	 add	%o1, %o2, %o1
+	sub	%g0, %o2, %o3
+	add	%o0, %o2, %o0
+10:
+	ldub	[%o1 + %o3], %o4
+1:
+	brz,pn	%o4, 2f
+	 stb	%o4, [%o0 + %o3]
+	addcc	%o3, 1, %o3
+	bne,pt	%xcc, 1b
+11:
+	 ldub	[%o1 + %o3], %o4
+	retl
+	 mov	%o2, %o0
+2:
+	add	%o3, 1, %o3
+	retl
+	 add	%o2, %o3, %o0
+3:
+	retl
+	 clr	%o0
+
+	.section .fixup,#alloc,#execinstr
+	.align	4
+4:
+	retl
+	 mov	-EFAULT, %o0
+
+	.section __ex_table,#alloc
+	.align	4
+	.word	10b, 4b
+	.word	11b, 4b
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/mm/extable.c linux/arch/sparc64/mm/extable.c
--- v2.1.29/linux/arch/sparc64/mm/extable.c	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/mm/extable.c	Thu Mar 20 16:43:32 1997
@@ -1,5 +1,5 @@
-/* $Id: extable.c,v 1.1 1996/12/26 10:24:24 davem Exp $
- * linux/arch/sparc/mm/extable.c
+/*
+ * linux/arch/sparc64/mm/extable.c
  */
 
 #include <linux/config.h>
@@ -31,12 +31,13 @@
                 else
                         last = mid-1;
         }
-        if (last->insn < value && !last->fixup && (last + 1)->insn > value) {
+        if (last->insn < value && !last->fixup && last[1].insn > value) {
         	*g2 = (value - last->insn)/4;
-        	return (last + 1)->fixup;
+        	return last[1].fixup;
         }
-        if (first > start && (first-1)->insn < value && !(first-1)->fixup && first->insn < value) {
-        	*g2 = (value - (first-1)->insn)/4;
+        if (first > start && first[-1].insn < value
+	    && !first[-1].fixup && first->insn < value) {
+        	*g2 = (value - first[-1].insn)/4;
         	return first->fixup;
         }
         return 0;
@@ -46,25 +47,23 @@
 search_exception_table(unsigned long addr, unsigned long *g2)
 {
 	unsigned long ret;
-#ifdef CONFIG_MODULES
-	struct module *mp;
-#endif
 
-	/* Search the kernel's table first.  */
+#ifndef CONFIG_MODULES
+	/* There is only the kernel to search.  */
 	ret = search_one_table(__start___ex_table,
 			       __stop___ex_table-1, addr, g2);
-	if (ret)
-		return ret;
-
-#ifdef CONFIG_MODULES
+	if (ret) return ret;
+#else
+	/* The kernel is the last "module" -- no need to treat it special.  */
+	struct module *mp;
 	for (mp = module_list; mp != NULL; mp = mp->next) {
-		if (mp->exceptinfo.start != NULL) {
-			ret = search_one_table(mp->exceptinfo.start,
-				mp->exceptinfo.stop-1, addr, g2);
-			if (ret)
-				return ret;
-		}
+		if (mp->ex_table_start == NULL)
+			continue;
+		ret = search_one_table(mp->ex_table_start,
+				       mp->ex_table_end-1, addr, g2);
+		if (ret) return ret;
 	}
 #endif
+
 	return 0;
 }
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/mm/fault.c linux/arch/sparc64/mm/fault.c
--- v2.1.29/linux/arch/sparc64/mm/fault.c	Mon Mar 17 14:54:23 1997
+++ linux/arch/sparc64/mm/fault.c	Thu Mar 20 16:43:32 1997
@@ -1,24 +1,39 @@
-/* $Id: fault.c,v 1.3 1997/03/04 16:27:02 jj Exp $
+/* $Id: fault.c,v 1.4 1997/03/11 17:37:07 jj Exp $
  * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
+#include <asm/head.h>
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/uaccess.h>
+
 #define ELEMENTS(arr) (sizeof (arr)/sizeof (arr[0]))
 
 extern struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
-extern int prom_node_root;
 
 /* Nice, simple, prom library does all the sweating for us. ;) */
-unsigned int prom_probe_memory (void)
+unsigned long prom_probe_memory (void)
 {
-	register struct linux_mlist_v0 *mlist;
-	register unsigned int bytes, base_paddr, tally;
+	register struct linux_mlist_p1275 *mlist;
+	register unsigned long bytes, base_paddr, tally;
 	register int i;
 
 	i = 0;
-	mlist= *prom_meminfo()->v0_available;
+	mlist = *prom_meminfo()->p1275_available;
 	bytes = tally = mlist->num_bytes;
 	base_paddr = (unsigned int) mlist->start_adr;
   
@@ -40,7 +55,7 @@
 			break;
 		}
     
-		sp_banks[i].base_addr = (unsigned int) mlist->start_adr;
+		sp_banks[i].base_addr = (unsigned long) mlist->start_adr;
 		sp_banks[i].num_bytes = mlist->num_bytes;
 	}
 
@@ -60,10 +75,10 @@
 /* Traverse the memory lists in the prom to see how much physical we
  * have.
  */
-unsigned int
+unsigned long
 probe_memory(void)
 {
-	unsigned int total;
+	unsigned long total;
 
 	total = prom_probe_memory();
 
@@ -102,17 +117,17 @@
 	case 3: return 3;
 	/* store will be handled by fixup, load will bump out */
 	/* for _to_ macros */
-	case 1: insn = (unsigned *)pc; if ((insn >> 21) & 1) return 1; break;
+	case 1: insn = *(unsigned *)pc; if ((insn >> 21) & 1) return 1; break;
 	/* load will be handled by fixup, store will bump out */
 	/* for _from_ macros */
-	case 2: insn = (unsigned *)pc; 
+	case 2: insn = *(unsigned *)pc; 
 		if (!((insn >> 21) & 1) || ((insn>>19)&0x3f) == 15) return 2; 
 		break; 
 	default: break;
 	}
 	memset (&regs, 0, sizeof (regs));
-	regs.pc = pc;
-	regs.npc = pc + 4;
+	regs.tpc = pc;
+	regs.tnpc = pc + 4;
 	/* FIXME: Should set up regs->tstate? */
 	unhandled_fault (address, current, &regs);
 	/* Not reached */
@@ -125,7 +140,7 @@
 	struct vm_area_struct *vma;
 	struct task_struct *tsk = current;
 	struct mm_struct *mm = tsk->mm;
-	unsigned int fixup;
+	unsigned long fixup;
 	unsigned long g2;
 	int from_user = !(regs->tstate & TSTATE_PRIV);
 
@@ -165,20 +180,16 @@
 	/* Is this in ex_table? */
 	
 	g2 = regs->u_regs[UREG_G2];
-	if (!from_user && (fixup = search_exception_table (regs->pc, &g2))) {
-		printk("Exception: PC<%016lx> faddr<%016lx>\n", regs->pc, address);
+	if (!from_user && (fixup = search_exception_table (regs->tpc, &g2))) {
+		printk("Exception: PC<%016lx> faddr<%016lx>\n", regs->tpc, address);
 		printk("EX_TABLE: insn<%016lx> fixup<%016lx> g2<%016lx>\n",
-			regs->pc, fixup, g2);
-		regs->pc = fixup;
-		regs->npc = regs->pc + 4;
+			regs->tpc, fixup, g2);
+		regs->tpc = fixup;
+		regs->tnpc = regs->tpc + 4;
 		regs->u_regs[UREG_G2] = g2;
 		goto out;
 	}
 	if(from_user) {
-#if 0
-		printk("Fault whee %s [%d]: segfaults at %08lx pc=%08lx\n",
-		       tsk->comm, tsk->pid, address, regs->pc);
-#endif
 		tsk->tss.sig_address = address;
 		tsk->tss.sig_desc = SUBSIG_NOMAPPING;
 		send_sig(SIGSEGV, tsk, 1);
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/mm/init.c linux/arch/sparc64/mm/init.c
--- v2.1.29/linux/arch/sparc64/mm/init.c	Sun Jan 26 02:07:10 1997
+++ linux/arch/sparc64/mm/init.c	Thu Mar 20 16:43:33 1997
@@ -1,10 +1,21 @@
-/*  $Id: init.c,v 1.2 1997/01/02 14:14:42 jj Exp $
+/*  $Id: init.c,v 1.4 1997/03/18 17:59:48 jj Exp $
  *  arch/sparc64/mm/init.c
  *
  *  Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
+ 
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+#include <linux/swap.h>
+
+#include <asm/system.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
 
 extern void show_net_buffers(void);
+extern unsigned long device_scan(unsigned long);
 
 struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
 
@@ -21,10 +32,10 @@
  * ZERO_PAGE is a special page that is used for zero-initialized
  * data and COW.
  */
-pte_t *__bad_pagetable(void)
+pmd_t *__bad_pagetable(void)
 {
 	memset((void *) EMPTY_PGT, 0, PAGE_SIZE);
-	return (pte_t *) EMPTY_PGT;
+	return (pmd_t *) EMPTY_PGT;
 }
 
 pte_t __bad_page(void)
@@ -67,7 +78,8 @@
 
 	ctx_list_pool = (struct ctx_list *) start_mem;
 	start_mem += (numctx * sizeof(struct ctx_list));
-	for(ctx = 0; ctx < numctx; ctx++) {
+	/* Context 0 is reserved for PROM and uaccess stuff */
+	for(ctx = 1; ctx < numctx; ctx++) {
 		struct ctx_list *clist;
 
 		clist = (ctx_list_pool + ctx);
@@ -76,7 +88,7 @@
 	}
 	ctx_free.next = ctx_free.prev = &ctx_free;
 	ctx_used.next = ctx_used.prev = &ctx_used;
-	for(ctx = 0; ctx < numctx; ctx++)
+	for(ctx = 1; ctx < numctx; ctx++)
 		add_to_free_ctxlist(ctx_list_pool + ctx);
 	return start_mem;
 }
@@ -86,6 +98,7 @@
 __initfunc(unsigned long 
 paging_init(unsigned long start_mem, unsigned long end_mem))
 {
+	return device_scan (start_mem);
 }
 
 extern int min_free_pages;
@@ -182,7 +195,7 @@
 		mem_map[MAP_NR(addr)].count = 1;
 		free_page(addr);
 	}
-	printk ("Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10);
+	printk ("Freeing unused kernel memory: %dk freed\n", (unsigned)((&__init_end - &__init_begin) >> 10));
 }
 
 void si_meminfo(struct sysinfo *val)
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/prom/console.c linux/arch/sparc64/prom/console.c
--- v2.1.29/linux/arch/sparc64/prom/console.c	Mon Mar 17 14:54:23 1997
+++ linux/arch/sparc64/prom/console.c	Thu Mar 20 16:43:33 1997
@@ -1,4 +1,4 @@
-/* $Id: console.c,v 1.4 1997/03/04 16:27:07 jj Exp $
+/* $Id: console.c,v 1.6 1997/03/18 17:59:59 jj Exp $
  * console.c: Routines that deal with sending and receiving IO
  *            to/from the current console device using the PROM.
  *
@@ -43,7 +43,7 @@
 	outc = c;
 	if (p1275_cmd("write", P1275_ARG(1,P1275_ARG_IN_BUF)|
 			       P1275_INOUT(3,1),
-			       prom_stdin, &outc, P1275_SIZE(1)) == 1)
+			       prom_stdout, &outc, P1275_SIZE(1)) == 1)
 		return 0;
 	else
 		return -1;
@@ -62,8 +62,16 @@
 void
 prom_putchar(char c)
 {
-	while(prom_nbputchar(c) == -1) ;
+	prom_nbputchar(c);
 	return;
+}
+
+void
+prom_puts(char *s, int len)
+{
+	p1275_cmd("write", P1275_ARG(1,P1275_ARG_IN_BUF)|
+			   P1275_INOUT(3,1),
+			   prom_stdout, s, P1275_SIZE(len));
 }
 
 /* Query for input device type */
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/prom/init.c linux/arch/sparc64/prom/init.c
--- v2.1.29/linux/arch/sparc64/prom/init.c	Mon Mar 17 14:54:23 1997
+++ linux/arch/sparc64/prom/init.c	Thu Mar 20 16:43:33 1997
@@ -1,4 +1,4 @@
-/* $Id: init.c,v 1.4 1997/03/04 16:27:09 jj Exp $
+/* $Id: init.c,v 1.6 1997/03/11 17:37:11 jj Exp $
  * init.c:  Initialize internal variables used by the PROM
  *          library functions.
  *
@@ -55,17 +55,22 @@
 		prom_halt();
 		
 	prom_getstring (node, "version", buffer, sizeof (buffer));
-	if (strncmp (buffer, "OPB ", 4) || buffer[5] != '.' || buffer[7] != '.') {
-		prom_printf ("Strange OPB version.\n");
+	
+	prom_printf ("\n");
+	
+	if (strncmp (buffer, "OBP ", 4) || buffer[5] != '.' || buffer[7] != '.') {
+		prom_printf ("Strange OBP version `%s'.\n", buffer);
 		prom_halt ();
 	}
-	/* Version field is expected to be 'OPB x.y.z date...' */
+	/* Version field is expected to be 'OBP x.y.z date...' */
 	
 	prom_rev = buffer[6] - '0';
 	prom_prev = ((buffer[4] - '0') << 16) | 
 		    ((buffer[6] - '0') << 8) |
 		    (buffer[8] - '0');
 		    
+	prom_printf ("PROMLIB: Sun IEEE Boot Prom %s\n", buffer + 4);
+	
 	prom_meminit();
 
 	prom_ranges_init();
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/prom/p1275.c linux/arch/sparc64/prom/p1275.c
--- v2.1.29/linux/arch/sparc64/prom/p1275.c	Mon Mar 17 14:54:24 1997
+++ linux/arch/sparc64/prom/p1275.c	Thu Mar 20 16:43:33 1997
@@ -1,4 +1,4 @@
-/* $Id: p1275.c,v 1.4 1997/03/04 16:27:12 jj Exp $
+/* $Id: p1275.c,v 1.7 1997/03/18 17:59:55 jj Exp $
  * p1275.c: Sun IEEE 1275 PROM low level interface routines
  *
  * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -12,37 +12,52 @@
 #include <asm/oplib.h>
 #include <asm/spitfire.h>
 #include <asm/system.h>
+#include <asm/pstate.h>
 
 /* If you change layout of this structure, please change the prom_doit
    function below as well. */
 typedef struct {
-	unsigned prom_doit_code [16];
-	void (*prom_cif_handler)(long *);
-	unsigned long prom_cif_stack;
-	unsigned long prom_args [23];
-	char prom_buffer [7928];
+	unsigned prom_doit_code [24];		/* 0x8000 */
+	long prom_sync_routine;			/* 0x8060 */
+	void (*prom_cif_handler)(long *);	/* 0x8068 */
+	unsigned long prom_cif_stack;		/* 0x8070 */
+	unsigned long prom_args [23];		/* 0x8078 */
+	char prom_buffer [7888];
 } at0x8000;
 
 static void (*prom_do_it)(void);
 
 void prom_cif_interface (void) __attribute__ ((__section__ (".p1275")));
 
-/* At most 16 insns (including the retl; nop; afterwards) */
+/* At most 14 insns */
 void prom_cif_interface (void)
 {
 	__asm__ __volatile__ ("
-	sethi	%hi(0x8000), %o0
-	ldx	[%o0 + 0x048], %o1	! prom_cif_stack
-	save	%o1, -0xc0, %sp
-	ldx	[%i0 + 0x040], %l2	! prom_cif_handler
-	mov	%g4, %l0
-	mov	%g6, %l1
-	call	%l2
-	 or	%i0, 0x050, %o0		! prom_args
-	mov	%l0, %g4
-	mov	%l1, %g6
-	restore
-	");
+	sethi	%%hi(0x8000), %%o0
+	ldx	[%%o0 + 0x070], %%o1	! prom_cif_stack
+	save	%%o1, -0xc0, %%sp
+	ldx	[%%i0 + 0x068], %%l2	! prom_cif_handler
+	rdpr	%%pstate, %%l4
+	mov	%%g4, %%l0
+	mov	%%g6, %%l1
+	wrpr	%%l4, %0, %%pstate	! turn on address masking
+	call	%%l2
+	 or	%%i0, 0x078, %%o0	! prom_args
+	wrpr	%%l4, 0, %%pstate	! put pstate back
+	mov	%%l0, %%g4
+	ret
+	 restore %%l1, 0, %%g6
+	save	%%sp, -0xc0, %%sp	! If you change the offset of the save 
+	rdpr	%%pstate, %%l4		! here, please change the 0x8038 
+	andn	%%l4, %0, %%l3		! constant below as well
+	wrpr	%%l3, %%pstate
+	ldx	[%%o0 + 0x060], %%l2
+	call	%%l2
+	 nop
+	wrpr	%%l4, 0, %%pstate
+	ret
+	 restore
+	" : : "i" (PSTATE_AM));
 }
 
 long p1275_cmd (char *service, long fmt, ...)
@@ -64,26 +79,26 @@
 	}
 	low->prom_args[0] = (unsigned long)p;			/* service */
 	strcpy (p, service);
-	p = strchr (p, 0) + 1;
+	p = (char *)(((long)(strchr (p, 0) + 8)) & ~7);
 	low->prom_args[1] = nargs = (fmt & 0x0f);		/* nargs */
 	low->prom_args[2] = nrets = ((fmt & 0xf0) >> 4); 	/* nrets */
-	attrs = (fmt & (~0xff));
+	attrs = fmt >> 8;
 	va_start(list, fmt);
 	for (i = 0; i < nargs; i++, attrs >>= 3) {
 		switch (attrs & 0x7) {
 		case P1275_ARG_NUMBER:
-			low->prom_args[i + 3] = va_arg(list, long); break;
+			low->prom_args[i + 3] = (unsigned)va_arg(list, long); break;
 		case P1275_ARG_IN_STRING:
 			strcpy (p, va_arg(list, char *));
 			low->prom_args[i + 3] = (unsigned long)p;
-			p = strchr (p, 0) + 1;
+			p = (char *)(((long)(strchr (p, 0) + 8)) & ~7);
 			break;
 		case P1275_ARG_OUT_BUF:
 			(void) va_arg(list, char *);
 			low->prom_args[i + 3] = (unsigned long)p;
 			x = va_arg(list, long);
 			i++; attrs >>= 3;
-			p += (int)x;
+			p = (char *)(((long)(p + (int)x + 7)) & ~7);
 			low->prom_args[i + 3] = x;
 			break;
 		case P1275_ARG_IN_BUF:
@@ -91,8 +106,8 @@
 			low->prom_args[i + 3] = (unsigned long)p;
 			x = va_arg(list, long);
 			i++; attrs >>= 3;
-			p += (int)x;
 			memcpy (p, q, (int)x);
+			p = (char *)(((long)(p + (int)x + 7)) & ~7);
 			low->prom_args[i + 3] = x;
 			break;
 		case P1275_ARG_OUT_32B:
@@ -101,23 +116,28 @@
 			p += 32;
 			break;
 		case P1275_ARG_IN_FUNCTION:
-			/* FIXME: This should make a function in our <4G
-			 * section, which will call the argument,
-			 * so that PROM can call it.
-			 */
-			low->prom_args[i + 3] = va_arg(list, long); break;
+			low->prom_args[i + 3] = 0x8038;
+			low->prom_sync_routine = va_arg(list, long); break;
 		}
 	}
 	va_end(list);
+	
 	(*prom_do_it)();
-	attrs = fmt & (~0xff);
+
+	attrs = fmt >> 8;
 	va_start(list, fmt);
 	for (i = 0; i < nargs; i++, attrs >>= 3) {
 		switch (attrs & 0x7) {
 		case P1275_ARG_NUMBER:
+			(void) va_arg(list, long); break;
 		case P1275_ARG_IN_STRING:
+			(void) va_arg(list, char *); break;
 		case P1275_ARG_IN_FUNCTION:
+			(void) va_arg(list, long); break;
 		case P1275_ARG_IN_BUF:
+			(void) va_arg(list, char *);
+			(void) va_arg(list, long);
+			i++; attrs >>= 3;
 			break;
 		case P1275_ARG_OUT_BUF:
 			p = va_arg(list, char *);
@@ -133,6 +153,7 @@
 	}
 	va_end(list);
 	x = low->prom_args [nargs + 3];
+	
 	if (ctx)
 		spitfire_set_primary_context (ctx);
 	restore_flags(flags);
@@ -142,7 +163,7 @@
 void prom_cif_init(void *cif_handler, void *cif_stack)
 {
 	at0x8000 *low = (at0x8000 *)(0x8000);
-	
+
 	low->prom_cif_handler = (void (*)(long *))cif_handler;
 	low->prom_cif_stack = (unsigned long)cif_stack;
         prom_do_it = (void (*)(void))(0x8000);
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/prom/printf.c linux/arch/sparc64/prom/printf.c
--- v2.1.29/linux/arch/sparc64/prom/printf.c	Mon Mar 17 14:54:24 1997
+++ linux/arch/sparc64/prom/printf.c	Thu Mar 20 16:43:33 1997
@@ -1,7 +1,8 @@
-/* $Id: printf.c,v 1.2 1997/03/04 16:27:13 jj Exp $
+/* $Id: printf.c,v 1.3 1997/03/18 18:00:00 jj Exp $
  * printf.c:  Internal prom library printf facility.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 /* This routine is internal to the prom library, no one else should know
@@ -15,24 +16,31 @@
 
 static char ppbuf[1024];
 
+extern void prom_puts (char *, int);
+
 void
 prom_printf(char *fmt, ...)
 {
 	va_list args;
-	char ch, *bptr;
+	char ch, *bptr, *last;
 	int i;
 
 	va_start(args, fmt);
 	i = vsprintf(ppbuf, fmt, args);
 
 	bptr = ppbuf;
+	last = ppbuf;
 
 	while((ch = *(bptr++)) != 0) {
-		if(ch == '\n')
+		if(ch == '\n') {
+			if (last < bptr - 1)
+				prom_puts (last, bptr - 1 - last);
 			prom_putchar('\r');
-
-		prom_putchar(ch);
+			last = bptr - 1;
+		}
 	}
+	if (last < bptr - 1)
+		prom_puts (last, bptr - 1 - last);
 	va_end(args);
 	return;
 }
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/prom/ranges.c linux/arch/sparc64/prom/ranges.c
--- v2.1.29/linux/arch/sparc64/prom/ranges.c	Mon Mar 17 14:54:24 1997
+++ linux/arch/sparc64/prom/ranges.c	Thu Mar 20 16:43:33 1997
@@ -1,4 +1,4 @@
-/* $Id: ranges.c,v 1.1 1997/02/25 12:40:28 jj Exp $
+/* $Id: ranges.c,v 1.2 1997/03/18 17:59:57 jj Exp $
  * ranges.c: Handle ranges in newer proms for obio/sbus.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -66,7 +66,7 @@
 {
 }
 
-__initfunc(void prom_sbus_ranges_init(int parentnd, struct linux_sbus *sbus))
+__initfunc(void prom_sbus_ranges_init(struct linux_sbus *sbus))
 {
 	int success;
 	
diff -u --recursive --new-file v2.1.29/linux/arch/sparc64/vmlinux.lds linux/arch/sparc64/vmlinux.lds
--- v2.1.29/linux/arch/sparc64/vmlinux.lds	Mon Mar 17 14:54:24 1997
+++ linux/arch/sparc64/vmlinux.lds	Thu Mar 20 16:43:33 1997
@@ -5,6 +5,8 @@
 
 SECTIONS
 {
+  empty_zero_page = 0xfffff80000000000;
+  empty_bad_page =  0xfffff80000002000;
   . = 0x4000;
   .text 0xfffff80000004000 :
   {
@@ -30,6 +32,7 @@
   __start___ksymtab = .;
   __ksymtab  : { *(__ksymtab) }
   __stop___ksymtab = .;
+  __kstrtab  : { *(.kstrtab) }
   . = ALIGN(8192);
   __init_begin = .;
   .text.init : { *(.text.init) }
@@ -49,6 +52,9 @@
    *(.dynbss)
    *(.bss)
    *(COMMON)
+   . = ALIGN(8192);
+   empty_bad_page_table = .;
+   . += 8192;
   }
   _end = . ;
   PROVIDE (end = .);
diff -u --recursive --new-file v2.1.29/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v2.1.29/linux/drivers/block/Config.in	Wed Dec 18 05:57:28 1996
+++ linux/drivers/block/Config.in	Tue Mar 11 14:19:11 1997
@@ -23,7 +23,7 @@
     fi
     if [ "$CONFIG_PCI" = "y" ]; then
       bool '   RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000
-      bool '   Intel 82371 PIIX (Triton I/II) DMA support' CONFIG_BLK_DEV_TRITON
+      bool '   Intel PIIX/PIIX3 (Triton 430FX/HX/VX, 440FX) DMA support' CONFIG_BLK_DEV_TRITON
     fi
     bool '   Other IDE chipset support' CONFIG_IDE_CHIPSETS
     if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
diff -u --recursive --new-file v2.1.29/linux/drivers/block/README.hd linux/drivers/block/README.hd
--- v2.1.29/linux/drivers/block/README.hd	Tue Feb 27 22:42:32 1996
+++ linux/drivers/block/README.hd	Wed Dec 31 16:00:00 1969
@@ -1,8 +0,0 @@
-This hard disk driver (hd.c) includes support for IDE Multiple Mode
-and IRQ-unmasking during I/O (both disabled by default), as well as
-support for drives where the BIOS reports "more than 16 heads".
-
-The hdparm.c program for controlling various features is now packaged
-separately.  Look for it on your favorite linux FTP site.
-
--mlord@pobox.com
diff -u --recursive --new-file v2.1.29/linux/drivers/block/README.ide linux/drivers/block/README.ide
--- v2.1.29/linux/drivers/block/README.ide	Sun May 12 11:21:04 1996
+++ linux/drivers/block/README.ide	Wed Dec 31 16:00:00 1969
@@ -1,3 +0,0 @@
-The IDE driver Documentation is now in the linux/Documentation directory.
-
--mlord@pobox.com
diff -u --recursive --new-file v2.1.29/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v2.1.29/linux/drivers/block/floppy.c	Mon Mar 17 14:54:24 1997
+++ linux/drivers/block/floppy.c	Thu Mar 20 11:37:10 1997
@@ -260,7 +260,7 @@
  * current disk size is unknown.
  * [Now it is rather a minimum]
  */
-#define MAX_DISK_SIZE 2 /* 3984*/
+#define MAX_DISK_SIZE 4 /* 3984*/
 
 #define K_64	0x10000		/* 64KB */
 
@@ -681,7 +681,7 @@
 				DPRINT("Disk type is undefined after "
 				       "disk change\n");
 			current_type[drive] = NULL;
-			floppy_sizes[TOMINOR(current_drive)] = MAX_DISK_SIZE;
+			floppy_sizes[TOMINOR(drive)] = MAX_DISK_SIZE;
 		}
 
 		/*USETF(FD_DISK_NEWCHANGE);*/
@@ -905,7 +905,7 @@
 static void schedule_bh( void (*handler)(void*) )
 {
 	floppy_tq.routine = (void *)(void *) handler;
-	queue_task_irq(&floppy_tq, &tq_immediate);
+	queue_task(&floppy_tq, &tq_immediate);
 	mark_bh(IMMEDIATE_BH);
 }
 
@@ -1042,7 +1042,7 @@
 	floppy_disable_hlt();
 }
 
-void show_floppy(void);
+static void show_floppy(void);
 
 /* waits until the fdc becomes ready */
 static int wait_til_ready(void)
@@ -1631,7 +1631,7 @@
 	printk("\n");
 }
 
-/* interrupt handler */
+/* interrupt handler. Note that this can be called externally on the Sparc */
 void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 {
 	void (*handler)(void) = DEVICE_INTR;
@@ -1734,7 +1734,7 @@
 	}
 }
 
-void show_floppy(void)
+static void show_floppy(void)
 {
 	int i;
 
@@ -3152,7 +3152,7 @@
 }
 
 /* handle obsolete ioctl's */
-int ioctl_table[]= {
+static int ioctl_table[]= {
 	FDCLRPRM,
 	FDSETPRM,
 	FDDEFPRM,
@@ -3800,7 +3800,7 @@
 
 /* lilo configuration */
 
-void floppy_set_flags(int *ints,int param, int param2)
+static void floppy_set_flags(int *ints,int param, int param2)
 {
 	int i;
 
diff -u --recursive --new-file v2.1.29/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.1.29/linux/drivers/block/ide-cd.c	Sun Dec 29 01:37:34 1996
+++ linux/drivers/block/ide-cd.c	Tue Mar 11 14:19:11 1997
@@ -395,14 +395,6 @@
 {
 	struct request *rq = HWGROUP(drive)->rq;
 
-	/* The code in blk.h can screw us up on error recovery if the block
-	   size is larger than 1k.  Fix that up here. */
-	if (!uptodate && rq->bh != 0) {
-		int adj = rq->current_nr_sectors - 1;
-		rq->current_nr_sectors -= adj;
-		rq->sector += adj;
-	}
-
 	if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate) {
 		struct packet_command *pc = (struct packet_command *)
 			                      rq->buffer;
@@ -737,10 +729,8 @@
 	/* Check for errors. */
 	if (dma) {
 		info->dma = 0;
-		if ((dma_error = HWIF(drive)->dmaproc(ide_dma_status_bad, drive))) {
-			printk ("%s: disabled DMA\n", drive->name);
-			drive->using_dma = 0;
-		}
+		if ((dma_error = HWIF(drive)->dmaproc(ide_dma_status_bad, drive)))
+			HWIF(drive)->dmaproc(ide_dma_off, drive);
 		(void) (HWIF(drive)->dmaproc(ide_dma_abort, drive));
 	}
 
diff -u --recursive --new-file v2.1.29/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c
--- v2.1.29/linux/drivers/block/ide-floppy.c	Sat Jan 25 10:51:17 1997
+++ linux/drivers/block/ide-floppy.c	Tue Mar 11 14:19:11 1997
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/ide-floppy.c	Version 0.4 - ALPHA	Jan  26, 1997
+ * linux/drivers/block/ide-floppy.c	Version 0.5 - ALPHA	Feb  21, 1997
  *
  * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
  */
@@ -17,6 +17,10 @@
  * Ver 0.2   Oct 31 96   Minor changes.
  * Ver 0.3   Dec  2 96   Fixed error recovery bug.
  * Ver 0.4   Jan 26 97   Add support for the HDIO_GETGEO ioctl.
+ * Ver 0.5   Feb 21 97   Add partitions support.
+ *                       Use the minimum of the LBA and CHS capacities.
+ *                       Avoid hwgroup->rq == NULL on the last irq.
+ *                       Fix potential null dereferencing with DEBUG_LOG.
  */
 
 #include <linux/config.h>
@@ -481,25 +485,23 @@
 	struct request *rq = pc->rq;
 	struct buffer_head *bh = rq->bh;
 	int count;
-	
+
 	while (bcount) {
-#if IDEFLOPPY_DEBUG_BUGS
+		if (pc->b_count == bh->b_size) {
+			rq->sector += rq->current_nr_sectors;
+			rq->nr_sectors -= rq->current_nr_sectors;
+			idefloppy_end_request (1, HWGROUP(drive));
+			if ((bh = rq->bh) != NULL)
+				pc->b_count = 0;
+		}
 		if (bh == NULL) {
 			printk (KERN_ERR "%s: bh == NULL in idefloppy_input_buffers, bcount == %d\n", drive->name, bcount);
 			idefloppy_discard_data (drive, bcount);
 			return;
 		}
-#endif /* IDEFLOPPY_DEBUG_BUGS */
 		count = IDEFLOPPY_MIN (bh->b_size - pc->b_count, bcount);
 		atapi_input_bytes (drive, bh->b_data + pc->b_count, count);
 		bcount -= count; pc->b_count += count;
-		if (pc->b_count == bh->b_size) {
-			rq->sector += rq->current_nr_sectors;
-			rq->nr_sectors -= rq->current_nr_sectors;
-			idefloppy_end_request (1, HWGROUP(drive));
-			if ((bh = rq->bh) != NULL)
-				pc->b_count = 0;
-		}
 	}
 }
 
@@ -510,16 +512,6 @@
 	int count;
 	
 	while (bcount) {
-#if IDEFLOPPY_DEBUG_BUGS
-		if (bh == NULL) {
-			printk (KERN_ERR "%s: bh == NULL in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount);
-			idefloppy_write_zeros (drive, bcount);
-			return;
-		}
-#endif /* IDEFLOPPY_DEBUG_BUGS */
-		count = IDEFLOPPY_MIN (pc->b_count, bcount);
-		atapi_output_bytes (drive, pc->b_data, count);
-		bcount -= count; pc->b_data += count; pc->b_count -= count;
 		if (!pc->b_count) {
 			rq->sector += rq->current_nr_sectors;
 			rq->nr_sectors -= rq->current_nr_sectors;
@@ -529,6 +521,14 @@
 				pc->b_count = bh->b_size;
 			}
 		}
+		if (bh == NULL) {
+			printk (KERN_ERR "%s: bh == NULL in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount);
+			idefloppy_write_zeros (drive, bcount);
+			return;
+		}
+		count = IDEFLOPPY_MIN (pc->b_count, bcount);
+		atapi_output_bytes (drive, pc->b_data, count);
+		bcount -= count; pc->b_data += count; pc->b_count -= count;
 	}
 }
 
@@ -584,7 +584,10 @@
 
 	floppy->sense_key = result->sense_key; floppy->asc = result->asc; floppy->ascq = result->ascq;
 #if IDEFLOPPY_DEBUG_LOG
-	printk (KERN_INFO "ide-floppy: pc = %x, sense key = %x, asc = %x, ascq = %x\n",floppy->failed_pc->c[0],result->sense_key,result->asc,result->ascq);
+	if (floppy->failed_pc)
+		printk (KERN_INFO "ide-floppy: pc = %x, sense key = %x, asc = %x, ascq = %x\n",floppy->failed_pc->c[0],result->sense_key,result->asc,result->ascq);
+	else
+		printk (KERN_INFO "ide-floppy: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq);
 #endif /* IDEFLOPPY_DEBUG_LOG */
 }
 
@@ -726,7 +729,7 @@
 	if (clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
 		printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n");
 		printk (KERN_ERR "ide-floppy: DMA disabled, reverting to PIO\n");
-		drive->using_dma=0;
+		HWIF(drive)->dmaproc(ide_dma_off, drive);
 		ide_do_reset (drive);
 		return;
 	}
@@ -841,7 +844,7 @@
 #ifdef CONFIG_BLK_DEV_TRITON
 	if (clear_bit (PC_DMA_ERROR, &pc->flags)) {
 		printk (KERN_WARNING "ide-floppy: DMA disabled, reverting to PIO\n");
-		drive->using_dma=0;
+		HWIF(drive)->dmaproc(ide_dma_off, drive);
 	}
 	if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
 		dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
@@ -875,6 +878,7 @@
 	printk (KERN_INFO "ide-floppy: Reached idefloppy_rw_callback\n");
 #endif /* IDEFLOPPY_DEBUG_LOG */
 
+	idefloppy_end_request(1, HWGROUP(drive));
 	return;
 }
 
@@ -930,9 +934,9 @@
 	pc->c[4] = start;
 }
 
-static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq)
+static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, unsigned long sector)
 {
-	int block = rq->sector / floppy->bs_factor;
+	int block = sector / floppy->bs_factor;
 	int blocks = rq->nr_sectors / floppy->bs_factor;
 	
 #if IDEFLOPPY_DEBUG_LOG
@@ -991,7 +995,7 @@
 				return;
 			}
 			pc = idefloppy_next_pc_storage (drive);
-			idefloppy_create_rw_cmd (floppy, pc, rq);
+			idefloppy_create_rw_cmd (floppy, pc, rq, block);
 			break;
 		case IDEFLOPPY_PC_RQ:
 			pc = (idefloppy_pc_t *) rq->buffer;
@@ -1029,7 +1033,7 @@
 	idefloppy_pc_t pc;
 	idefloppy_mode_parameter_header_t *header;
 	idefloppy_flexible_disk_page_t *page;
-	int capacity;
+	int capacity, lba_capacity;
 
 	idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE, MODE_SENSE_CURRENT);
 	if (idefloppy_queue_pc_tail (drive,&pc)) {
@@ -1044,17 +1048,21 @@
 	page->cyls = ntohs (page->cyls);
 	page->rpm = ntohs (page->rpm);
 	capacity = page->cyls * page->heads * page->sectors * page->sector_size;
-	if (memcmp (page, &floppy->flexible_disk_page, sizeof (idefloppy_flexible_disk_page_t))) {
+	if (memcmp (page, &floppy->flexible_disk_page, sizeof (idefloppy_flexible_disk_page_t)))
 		printk (KERN_INFO "%s: %dkB, %d/%d/%d CHS, %d kBps, %d sector size, %d rpm\n",
 			drive->name, capacity / 1024, page->cyls, page->heads, page->sectors,
 			page->transfer_rate / 8, page->sector_size, page->rpm);
-		floppy->flexible_disk_page = *page;
-		drive->bios_cyl = page->cyls;
-		drive->bios_head = page->heads;
-		drive->bios_sect = page->sectors;
-		if (capacity != floppy->blocks * floppy->block_size)
-			printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
-				drive->name, capacity, floppy->blocks * floppy->block_size);
+
+	floppy->flexible_disk_page = *page;
+	drive->bios_cyl = page->cyls;
+	drive->bios_head = page->heads;
+	drive->bios_sect = page->sectors;
+	lba_capacity = floppy->blocks * floppy->block_size;
+	if (capacity != lba_capacity) {
+		printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
+			drive->name, capacity, lba_capacity);
+		capacity = IDEFLOPPY_MIN(capacity, lba_capacity);
+		floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0;
 	}
 	return 0;
 }
@@ -1071,6 +1079,11 @@
 	idefloppy_capacity_descriptor_t *descriptor;
 	int i, descriptors, rc = 1, blocks, length;
 	
+	drive->bios_cyl = 0;
+	drive->bios_head = drive->bios_sect = 0;
+	floppy->blocks = floppy->bs_factor = 0;
+	drive->part[0].nr_sects = 0;
+
 	idefloppy_create_read_capacity_cmd (&pc);
 	if (idefloppy_queue_pc_tail (drive, &pc)) {
 		printk (KERN_ERR "ide-floppy: Can't get floppy parameters\n");
@@ -1083,10 +1096,9 @@
 		blocks = descriptor->blocks = ntohl (descriptor->blocks);
 		length = descriptor->length = ntohs (descriptor->length);
 		if (!i && descriptor->dc == CAPACITY_CURRENT) {
-			if (memcmp (descriptor, &floppy->capacity, sizeof (idefloppy_capacity_descriptor_t))) {
+			if (memcmp (descriptor, &floppy->capacity, sizeof (idefloppy_capacity_descriptor_t)))
 				printk (KERN_INFO "%s: %dkB, %d blocks, %d sector size\n", drive->name, blocks * length / 1024, blocks, length);
-				floppy->capacity = *descriptor;
-			}
+			floppy->capacity = *descriptor;
 			if (!length || length % 512)
 				printk (KERN_ERR "%s: %d bytes block size not supported\n", drive->name, length);
 			else {
@@ -1094,9 +1106,6 @@
 				floppy->block_size = length;
 				if ((floppy->bs_factor = length / 512) != 1)
 					printk (KERN_NOTICE "%s: warning: non 512 bytes block size not fully supported\n", drive->name);
-				drive->part[0].nr_sects = blocks * floppy->bs_factor;
-				if (length > BLOCK_SIZE)
-					blksize_size[HWIF(drive)->major][drive->select.b.unit << PARTN_BITS] = length;
 				rc = 0;
 			}
 		}
@@ -1106,6 +1115,7 @@
 #endif /* IDEFLOPPY_DEBUG_INFO */
 	}
 	(void) idefloppy_get_flexible_disk_page (drive);
+	drive->part[0].nr_sects = floppy->blocks * floppy->bs_factor;
 	return rc;
 }
 
@@ -1183,7 +1193,7 @@
 	idefloppy_floppy_t *floppy = drive->driver_data;
 	unsigned long capacity = floppy->blocks * floppy->bs_factor;
 
-	return capacity ? capacity : 0x7fffffff;
+	return capacity;
 }
 
 /*
diff -u --recursive --new-file v2.1.29/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v2.1.29/linux/drivers/block/ide-tape.c	Sun Jan 26 02:07:10 1997
+++ linux/drivers/block/ide-tape.c	Tue Mar 11 14:19:12 1997
@@ -1780,7 +1780,7 @@
 	if (clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
 		printk (KERN_ERR "ide-tape: The tape wants to issue more interrupts in DMA mode\n");
 		printk (KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n");
-		drive->using_dma=0;
+		HWIF(drive)->dmaproc(ide_dma_off, drive);
 		ide_do_reset (drive);
 		return;
 	}
@@ -1918,7 +1918,7 @@
 #ifdef CONFIG_BLK_DEV_TRITON
 	if (clear_bit (PC_DMA_ERROR, &pc->flags)) {
 		printk (KERN_WARNING "ide-tape: DMA disabled, reverting to PIO\n");
-		drive->using_dma=0;
+		HWIF(drive)->dmaproc(ide_dma_off, drive);
 	}
 	if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
 		dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
diff -u --recursive --new-file v2.1.29/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.1.29/linux/drivers/block/ide.c	Sun Jan 26 02:07:10 1997
+++ linux/drivers/block/ide.c	Tue Mar 11 14:19:12 1997
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 6.01  Jan  26, 1997
+ *  linux/drivers/block/ide.c	Version 6.02  Mar  11, 1997
  *
  *  Copyright (C) 1994-1997  Linus Torvalds & authors (see below)
  */
@@ -278,6 +278,8 @@
  *			don't flush leftover data for ATAPI devices
  * Version 6.01		clear hwgroup->active while the hwgroup sleeps
  *			support HDIO_GETGEO for floppies
+ * Version 6.02		fix ide_ack_intr() call
+ *			check partition table on floppies
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -632,7 +634,8 @@
 		ide_drive_t *drive = &hwif->drives[unit];
 
 		drive->part[0].nr_sects = current_capacity(drive);
-		if (!drive->present || drive->media != ide_disk || drive->driver == NULL)
+		if (!drive->present || (drive->media != ide_disk && drive->media != ide_floppy) ||
+		    drive->driver == NULL || !drive->part[0].nr_sects)
 			drive->part[0].start_sect = -1; /* skip partition check */
 	}
 }
@@ -722,10 +725,8 @@
 	if (!drive->keep_settings) {
 		drive->unmask = 0;
 		drive->io_32bit = 0;
-		if (drive->using_dma) {
-			drive->using_dma = 0;
-			printk("%s: disabled DMA\n", drive->name);
-		}
+		if (drive->using_dma)
+			HWIF(drive)->dmaproc(ide_dma_off, drive);
 	}
 	if (drive->driver != NULL)
 		DRIVER(drive)->pre_reset(drive);
@@ -1449,7 +1450,7 @@
 	ide_hwgroup_t *hwgroup = dev_id;
 	ide_handler_t *handler;
 
-	if (!ide_ack_intr (hwgroup->hwif->io_ports[IDE_DATA_OFFSET],
+	if (!ide_ack_intr (hwgroup->hwif->io_ports[IDE_STATUS_OFFSET],
 			   hwgroup->hwif->io_ports[IDE_IRQ_OFFSET]))
 		return;
 
@@ -1620,7 +1621,8 @@
 	};
 
 	drive->part[0].nr_sects = current_capacity(drive);
-	if (drive->media != ide_disk || drive->driver == NULL)
+	if ((drive->media != ide_disk && drive->media != ide_floppy) ||
+	     drive->driver == NULL || !drive->part[0].nr_sects)
 		drive->part[0].start_sect = -1;
 	resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit);
 
@@ -1969,7 +1971,10 @@
 						restore_flags(flags);
 						return -EPERM;
 					}
-					drive->using_dma = arg;
+					if (HWIF(drive)->dmaproc(arg ? ide_dma_on : ide_dma_off, drive)) {
+						restore_flags(flags);
+						return -EIO;
+					}
 					break;
 				case HDIO_SET_KEEPSETTINGS:
 					drive->keep_settings = arg;
diff -u --recursive --new-file v2.1.29/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.1.29/linux/drivers/block/ide.h	Thu Feb  6 02:54:13 1997
+++ linux/drivers/block/ide.h	Wed Mar 26 10:58:41 1997
@@ -266,7 +266,8 @@
 typedef enum {	ide_dma_read = 0,	ide_dma_write = 1,
 		ide_dma_abort = 2,	ide_dma_check = 3,
 		ide_dma_status_bad = 4,	ide_dma_transferred = 5,
-		ide_dma_begin = 6 }
+		ide_dma_begin = 6,	ide_dma_on = 7,
+		ide_dma_off = 8 }
 	ide_dma_action_t;
 
 typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *);
@@ -583,6 +584,7 @@
 extern struct file_operations ide_fops[];
 #endif
 
+#ifdef _IDE_C
 #ifdef CONFIG_BLK_DEV_IDEDISK
 int idedisk_init (void);
 #endif /* CONFIG_BLK_DEV_IDEDISK */
@@ -598,6 +600,7 @@
 #ifdef CONFIG_BLK_DEV_IDESCSI
 int idescsi_init (void);
 #endif /* CONFIG_BLK_DEV_IDESCSI */
+#endif /* _IDE_C */
 
 int ide_register_module (ide_module_t *module);
 void ide_unregister_module (ide_module_t *module);
diff -u --recursive --new-file v2.1.29/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.1.29/linux/drivers/block/ll_rw_blk.c	Thu Feb 27 10:57:29 1997
+++ linux/drivers/block/ll_rw_blk.c	Tue Mar 18 14:13:45 1997
@@ -124,7 +124,7 @@
 	if (dev->current_request)
 		return;
 	dev->current_request = &dev->plug;
-	queue_task_irq_off(&dev->plug_tq, &tq_disk);
+	queue_task(&dev->plug_tq, &tq_disk);
 }
 
 /*
diff -u --recursive --new-file v2.1.29/linux/drivers/block/triton.c linux/drivers/block/triton.c
--- v2.1.29/linux/drivers/block/triton.c	Mon Jan 27 23:32:47 1997
+++ linux/drivers/block/triton.c	Tue Mar 11 14:19:12 1997
@@ -1,28 +1,34 @@
 /*
- *  linux/drivers/block/triton.c	Version 1.20  Jan 27, 1997
+ *  linux/drivers/block/triton.c	Version 2.00  March 9, 1997
  *
  *  Copyright (c) 1995-1997  Mark Lord
  *  May be copied or modified under the terms of the GNU General Public License
  */
 
 /*
- * This module provides support for the Bus Master IDE DMA function
- * of the Intel PCI Triton I/II chipsets (i82371FB or i82371SB).
+ * This module provides support for the bus-master IDE DMA function
+ * of the Intel PCI Triton chipset families, which use the PIIX (i82371FB,
+ * for the 430 FX chipset), and the enhanced PIIX3 (i82371SB for the 430 HX/VX
+ * and 440 chipsets).
  *
- * Pretty much the same code will work for the OPTi "Viper" chipset.
- * Look for DMA support for this in linux kernel 2.1.xx, when it appears.
+ * "PIIX" stands for "PCI ISA IDE Xcellerator".
  *
- * Up to four drives may be enabled for DMA, and the Triton chipset will
- * (hopefully) arbitrate the PCI bus among them.  Note that the i82371 chip
+ * Pretty much the same code could work for other IDE PCI bus-mastering chipsets.
+ * Look for DMA support for this someday in the not too distant future.
+ *
+ * DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies).
+ *
+ * Up to four drives may be enabled for DMA, and the PIIX/PIIX3 chips
+ * will arbitrate the PCI bus among them.  Note that the PIIX/PIIX3
  * provides a single "line buffer" for the BM IDE function, so performance of
  * multiple (two) drives doing DMA simultaneously will suffer somewhat,
  * as they contest for that resource bottleneck.  This is handled transparently
- * inside the i82371 chip.
+ * inside the PIIX/PIIX3.
  *
  * By default, DMA support is prepared for use, but is currently enabled only
- * for drives which support multi-word DMA mode2 (mword2), or which are
+ * for drives which support DMA mode2 (multi/single word), or which are
  * recognized as "good" (see table below).  Drives with only mode0 or mode1
- * (single or multi) DMA should also work with this chipset/driver (eg. MC2112A)
+ * (multi/single word) DMA should also work with this chipset/driver (eg. MC2112A)
  * but are not enabled by default.  Use "hdparm -i" to view modes supported
  * by a given drive.
  *
@@ -39,55 +45,11 @@
  * the "hdparm -X" feature.  There is seldom a need to do this, as drives
  * normally power-up with their "best" PIO/DMA modes enabled.
  *
- * Testing was done with an ASUS P55TP4XE/100 system and the following drives:
+ * Testing has been done with a rather extensive number of drives,
+ * with Quantum & Western Digital models generally outperforming the pack,
+ * and Fujitsu & Conner (and some Seagate which are really Conner) drives
+ * showing more lackluster throughput.
  *
- *   Quantum Fireball 1080A (1Gig w/83kB buffer), DMA mode2, PIO mode4.
- *	- DMA mode2 works well (7.4MB/sec), despite the tiny on-drive buffer.
- *	- This drive also does PIO mode4, at about the same speed as DMA mode2.
- *	  An awesome drive for the price!
- *
- *   Fujitsu M1606TA (1Gig w/256kB buffer), DMA mode2, PIO mode4.
- *	- DMA mode2 gives horrible performance (1.6MB/sec), despite the good
- *	  size of the on-drive buffer and a boasted 10ms average access time.
- *	- PIO mode4 was better, but peaked at a mere 4.5MB/sec.
- *
- *   Micropolis MC2112A (1Gig w/508kB buffer), drive pre-dates EIDE and ATA2.
- *	- DMA works fine (2.2MB/sec), probably due to the large on-drive buffer.
- *	- This older drive can also be tweaked for fastPIO (3.7MB/sec) by using
- *	  maximum clock settings (5,4) and setting all flags except prefetch.
- *
- *   Western Digital AC31000H (1Gig w/128kB buffer), DMA mode1, PIO mode3.
- *	- DMA does not work reliably.  The drive appears to be somewhat tardy
- *	  in deasserting DMARQ at the end of a sector.  This is evident in
- *	  the observation that WRITEs work most of the time, depending on
- *	  cache-buffer occupancy, but multi-sector reads seldom work.
- *
- * Testing was done with a Gigabyte GA-586 ATE system and the following drive:
- * (Uwe Bonnes - bon@elektron.ikp.physik.th-darmstadt.de)
- *
- *   Western Digital AC31600H (1.6Gig w/128kB buffer), DMA mode2, PIO mode4.
- *	- much better than its 1Gig cousin, this drive is reported to work
- *	  very well with DMA (7.3MB/sec).
- *
- * Other drives:
- *
- *   Maxtor 7540AV (515Meg w/32kB buffer), DMA modes mword0/sword2, PIO mode3.
- *	- a budget drive, with budget performance, around 3MB/sec.
- *
- *   Western Digital AC2850F (814Meg w/64kB buffer), DMA mode1, PIO mode3.
- *	- another "caviar" drive, similar to the AC31000, except that this one
- *	  worked with DMA in at least one system.  Throughput is about 3.8MB/sec
- *	  for both DMA and PIO.
- *
- *   Conner CFS850A (812Meg w/64kB buffer), DMA mode2, PIO mode4.
- *	- like most Conner models, this drive proves that even a fast interface
- *	  cannot improve slow media.  Both DMA and PIO peak around 3.5MB/sec.
- *
- *   Maxtor 71260AT (1204Meg w/256kB buffer), DMA mword0/sword2, PIO mode3.
- *	- works with DMA, on some systems (but not always on others, eg. Dell),
- *	giving 3-4MB/sec performance, about the same as mode3.
- *
- * If you have any drive models to add, email your results to:  mlord@pobox.com
  * Keep an eye on /var/adm/messages for "DMA disabled" messages.
  *
  * Some people have reported trouble with Intel Zappa motherboards.
@@ -98,7 +60,7 @@
  * Thanks to "Christopher J. Reimer" <reimer@doe.carleton.ca> for fixing the
  * problem with some (all?) ACER motherboards/BIOSs.
  *
- * And, yes, Intel Zappa boards really *do* use the Triton IDE ports.
+ * And, yes, Intel Zappa boards really *do* use both PIIX IDE ports.
  */
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -115,17 +77,17 @@
 #include <asm/dma.h>
 
 #include "ide.h"
+#include "ide_modes.h"
 
-#undef DISPLAY_TRITON_TIMINGS	/* define this to display timings */
+#define DISPLAY_PIIX_TIMINGS	/* define this to display timings */
 
 /*
  * good_dma_drives() lists the model names (from "hdparm -i")
- * of drives which do not support mword2 DMA but which are
+ * of drives which do not support mode2 DMA but which are
  * known to work fine with this interface under Linux.
  */
 const char *good_dma_drives[] = {"Micropolis 2112A",
 				 "CONNER CTMA 4000",
-				 "CONNER CTT8000-A",
 				 NULL};
 
 /*
@@ -152,6 +114,37 @@
  */
 static unsigned int piix_key;
 
+#define PIIX_FLAGS_FAST_PIO	1
+#define PIIX_FLAGS_USE_IORDY	2
+#define PIIX_FLAGS_PREFETCH	4
+#define PIIX_FLAGS_FAST_DMA	8
+
+typedef struct {
+	unsigned d0_flags	:4;
+	unsigned d1_flags	:4;
+	unsigned recovery	:2;
+	unsigned reserved	:2;
+	unsigned sample		:2;
+	unsigned sidetim_enabled:1;
+	unsigned ports_enabled	:1;
+} piix_timing_t;
+
+typedef struct {
+	unsigned pri_recovery	:2;
+	unsigned pri_sample	:2;
+	unsigned sec_recovery	:2;
+	unsigned sec_sample	:2;
+} piix_sidetim_t;
+
+
+/*
+ * We currently can handle only one PIIX chip here
+ */
+static piix_pci_bus = 0;
+static piix_pci_fn  = 0;
+
+static int config_drive_for_dma (ide_drive_t *);
+
 /*
  * dma_intr() is the handler for disk read/write DMA interrupts
  */
@@ -240,31 +233,8 @@
 	return 1;	/* let the PIO routines handle this weirdness */
 }
 
-static int config_drive_for_dma (ide_drive_t *drive)
-{
-	const char **list;
-
-	struct hd_driveid *id = drive->id;
-	if (id && (id->capability & 1)) {
-		/* Enable DMA on any drive that supports mword2 DMA */
-		if ((id->field_valid & 2) && (id->dma_mword & 0x404) == 0x404) {
-			drive->using_dma = 1;
-			return 0;		/* DMA enabled */
-		}
-		/* Consult the list of known "good" drives */
-		list = good_dma_drives;
-		while (*list) {
-			if (!strcmp(*list++,id->model)) {
-				drive->using_dma = 1;
-				return 0;	/* DMA enabled */
-			}
-		}
-	}
-	return 1;	/* DMA not enabled */
-}
-
 /*
- * triton_dmaproc() initiates/aborts DMA read/write operations on a drive.
+ * piix_dmaproc() initiates/aborts DMA read/write operations on a drive.
  *
  * The caller is assumed to have selected the drive and programmed the drive's
  * sector address using CHS or LBA.  All that remains is to prepare for DMA
@@ -278,12 +248,40 @@
  * Returns 1 if DMA read/write could not be started, in which case
  * the caller should revert to PIO for the current request.
  */
-static int triton_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+static int piix_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
 {
 	unsigned long dma_base = HWIF(drive)->dma_base;
 	unsigned int reading = (1 << 3);
+	piix_timing_t timing;
+	unsigned short reg;
+	byte dflags;
 
 	switch (func) {
+		case ide_dma_off:
+			printk("%s: DMA disabled\n", drive->name);
+		case ide_dma_on:
+			drive->using_dma = (func == ide_dma_on);
+			reg = (HWIF(drive)->io_ports[IDE_DATA_OFFSET] == 0x170) ? 0x42 : 0x40;
+			if (pcibios_read_config_word(piix_pci_bus, piix_pci_fn, reg, (short *)&timing)) {
+				printk("%s: pcibios read failed\n", HWIF(drive)->name);
+				return 1;
+			}
+			dflags = drive->select.b.unit ? timing.d1_flags : timing.d0_flags;
+			if (dflags & PIIX_FLAGS_FAST_PIO) {
+				if (func == ide_dma_on && drive->media == ide_disk)
+					dflags |= PIIX_FLAGS_FAST_DMA;
+				else
+					dflags &= ~PIIX_FLAGS_FAST_DMA;
+				if (drive->select.b.unit == 0)
+					timing.d0_flags = dflags;
+				else
+					timing.d1_flags = dflags;
+				if (pcibios_write_config_word(piix_pci_bus, piix_pci_fn, reg, *(short *)&timing)) {
+					printk("%s: pcibios write failed\n", HWIF(drive)->name);
+					return 1;
+				}
+			}
+			return 0;
 		case ide_dma_abort:
 			outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
 			return 0;
@@ -305,7 +303,7 @@
 			outb(inb(dma_base)|1, dma_base);	/* begin DMA */
 			return 0;
 		default:
-			printk("triton_dmaproc: unsupported func: %d\n", func);
+			printk("piix_dmaproc: unsupported func: %d\n", func);
 			return 1;
 	}
 	if (build_dmatable (drive))
@@ -321,31 +319,42 @@
 	return 0;
 }
 
-#ifdef DISPLAY_TRITON_TIMINGS
+static int config_drive_for_dma (ide_drive_t *drive)
+{
+	const char **list;
+
+	struct hd_driveid *id = drive->id;
+	if (id && (id->capability & 1)) {
+		/* Enable DMA on any drive that supports mode2 (multi/single word) DMA */
+		if (id->field_valid & 2)
+			if  ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
+				return piix_dmaproc(ide_dma_on, drive);
+		/* Consult the list of known "good" drives */
+		list = good_dma_drives;
+		while (*list) {
+			if (!strcmp(*list++,id->model))
+				return piix_dmaproc(ide_dma_on, drive);
+		}
+	}
+	return piix_dmaproc(ide_dma_off, drive);
+}
+
+#ifdef DISPLAY_PIIX_TIMINGS
 /*
- * print_triton_drive_flags() displays the currently programmed options
- * in the i82371 (Triton) for a given drive.
- *
- *	If fastDMA  is "no", then slow ISA timings are used for DMA data xfers.
- *	If fastPIO  is "no", then slow ISA timings are used for PIO data xfers.
- *	If IORDY    is "no", then IORDY is assumed to always be asserted.
- *	If PreFetch is "no", then data pre-fetch/post are not used.
- *
- * When "fastPIO" and/or "fastDMA" are "yes", then faster PCI timings and
- * back-to-back 16-bit data transfers are enabled, using the sample_CLKs
- * and recovery_CLKs (PCI clock cycles) timing parameters for that interface.
+ * print_piix_drive_flags() displays the currently programmed options
+ * in the PIIX/PIIX3 for a given drive.
  */
-static void print_triton_drive_flags (unsigned int unit, byte flags)
+static void print_piix_drive_flags (const char *unit, byte dflags)
 {
-	printk("         %s ", unit ? "slave :" : "master:");
-	printk( "fastDMA=%s",	(flags&9)	? "on " : "off");
-	printk(" PreFetch=%s",	(flags&4)	? "on " : "off");
-	printk(" IORDY=%s",	(flags&2)	? "on " : "off");
-	printk(" fastPIO=%s\n",	((flags&9)==1)	? "on " : "off");
+	printk("         %s ", unit);
+	printk( "fastDMA=%s",	(dflags & PIIX_FLAGS_FAST_PIO)	? "yes" : "no ");
+	printk(" PreFetch=%s",	(dflags & PIIX_FLAGS_PREFETCH)	? "on " : "off");
+	printk(" IORDY=%s",	(dflags & PIIX_FLAGS_USE_IORDY)	? "on " : "off");
+	printk(" fastPIO=%s\n",	((dflags & (PIIX_FLAGS_FAST_PIO|PIIX_FLAGS_FAST_DMA)) == PIIX_FLAGS_FAST_PIO) ? "on " : "off");
 }
-#endif /* DISPLAY_TRITON_TIMINGS */
+#endif /* DISPLAY_PIIX_TIMINGS */
 
-static void init_triton_dma (ide_hwif_t *hwif, unsigned short base)
+static void init_piix_dma (ide_hwif_t *hwif, unsigned short base)
 {
 	static unsigned long dmatable = 0;
 
@@ -367,7 +376,7 @@
 			hwif->dmatable = (unsigned long *) dmatable;
 			dmatable += (PRD_ENTRIES * PRD_BYTES);
 			outl(virt_to_bus(hwif->dmatable), base + 4);
-			hwif->dmaproc  = &triton_dmaproc;
+			hwif->dmaproc  = &piix_dmaproc;
 		}
 	}
 	printk("\n");
@@ -429,10 +438,16 @@
 {
 	int rc = 0, h;
 	int dma_enabled = 0;
-	unsigned short pcicmd;
-	unsigned int bmiba, timings;
+	unsigned short pcicmd, devid;
+	unsigned int bmiba;
+	const char *chipset = "ide";
+	piix_timing_t timings[2];
+
+	if (pcibios_read_config_word(piix_pci_bus, piix_pci_fn, 0x02, &devid))
+		goto quit;
+	chipset = (devid == PCI_DEVICE_ID_INTEL_82371SB_1) ? "PIIX3" : "PIIX";
 
-	printk("ide: i82371 PIIX (Triton) on PCI bus %d function %d\n", bus, fn);
+	printk("%s: bus-master IDE device on PCI bus %d function %d\n", chipset, bus, fn);
 
 	/*
 	 * See if IDE ports are enabled
@@ -440,21 +455,26 @@
 	if ((rc = pcibios_read_config_word(bus, fn, 0x04, &pcicmd)))
 		goto quit;
 	if ((pcicmd & 1) == 0)  {
-		printk("ide: ports are not enabled (BIOS)\n");
+		printk("%s: IDE ports are not enabled (BIOS)\n", chipset);
 		goto quit;
 	}
-	if ((rc = pcibios_read_config_dword(bus, fn, 0x40, &timings)))
+	if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0])))
 		goto quit;
-	if (!(timings & 0x80008000)) {
-		printk("ide: neither port is enabled\n");
+	if ((rc = pcibios_read_config_word(bus, fn, 0x42, (short *)&timings[1])))
+		goto quit;
+	if ((!timings[0].ports_enabled) || (!timings[1].ports_enabled)) {
+		printk("%s: neither IDE port is enabled\n", chipset);
 		goto quit;
 	}
 
+	piix_pci_bus = bus;
+	piix_pci_fn  = fn;
+
 	/*
 	 * See if Bus-Mastered DMA is enabled
 	 */
 	if ((pcicmd & 4) == 0) {
-		printk("ide: BM-DMA feature is not enabled (BIOS)\n");
+		printk("%s: bus-master DMA feature is not enabled (BIOS)\n", chipset);
 	} else {
 		/*
 		 * Get the bmiba base address
@@ -466,10 +486,10 @@
 			dma_enabled = 1;
 		} else {
 			unsigned short base;
-		        printk("ide: BM-DMA base register is invalid (0x%04x, PnP BIOS problem)\n", bmiba);
+		        printk("%s: bus-master base address is invalid (0x%04x, BIOS problem)\n", chipset, bmiba);
 			base = find_free_region(16);
 		        if (base) {
-				printk("ide: bypassing BIOS; setting BMIBA to 0x%04x\n", base);
+				printk("%s: bypassing BIOS; setting bus-master base address to 0x%04x\n", chipset, base);
 				piix_key = 0x80000000 + (fn * 0x100);
 				put_piix_reg(0x04,get_piix_reg(0x04)&~5);
 				put_piix_reg(0x20,(get_piix_reg(0x20)&0xFFFF000F)|base|1);
@@ -478,8 +498,10 @@
 				if (bmiba == base && (get_piix_reg(0x04) & 5) == 5)
 					dma_enabled = 1;
 				else
-					printk("ide: no such luck; DMA disabled\n");
+					printk("%s: operation failed\n", chipset);
 			}
+			if (!dma_enabled)
+				printk("%s: DMA is disabled (BIOS)\n", chipset);
 		}
 	}
 
@@ -487,56 +509,51 @@
 	 * Save the dma_base port addr for each interface
 	 */
 	for (h = 0; h < MAX_HWIFS; ++h) {
-#ifdef DISPLAY_TRITON_TIMINGS
-		byte s_clks, r_clks;
-		unsigned short devid;
-#endif /* DISPLAY_TRITON_TIMINGS */
+		unsigned int pri_sec;
+		piix_timing_t timing;
 		ide_hwif_t *hwif = &ide_hwifs[h];
-		unsigned short time;
-		if (hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0) {
-			time = timings & 0xffff;
-			if ((time & 0x8000) == 0)	/* interface enabled? */
-				continue;
-			hwif->chipset = ide_triton;
-			if (dma_enabled)
-				init_triton_dma(hwif, bmiba);
-		} else if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170) {
-			time = timings >> 16;
-			if ((time & 0x8000) == 0)	/* interface enabled? */
-				continue;
-			hwif->chipset = ide_triton;
-			if (dma_enabled)
-				init_triton_dma(hwif, bmiba + 8);
-		} else
+		switch (hwif->io_ports[IDE_DATA_OFFSET]) {
+			case 0x1f0:	pri_sec = 0; break;
+			case 0x170:	pri_sec = 1; break;
+			default:	continue;
+		}
+		timing = timings[pri_sec];
+		if (!timing.ports_enabled)	/* interface disabled? */
 			continue;
-#ifdef DISPLAY_TRITON_TIMINGS
-		s_clks = ((~time >> 12) & 3) + 2;
-		r_clks = ((~time >>  8) & 3) + 1;
-		printk("    %s timing: (0x%04x) sample_CLKs=%d, recovery_CLKs=%d\n",
-		 hwif->name, time, s_clks, r_clks);
-		if ((time & 0x40) && !pcibios_read_config_word(bus, fn, 0x02, &devid)
-		 && devid == PCI_DEVICE_ID_INTEL_82371SB_1)
+		hwif->chipset = ide_triton;
+		if (dma_enabled)
+			init_piix_dma(hwif, bmiba + (pri_sec ? 8 : 0));
+#ifdef DISPLAY_PIIX_TIMINGS
+		/*
+		 * Display drive timings/modes
+		 */
 		{
-			byte stime;
-			if (pcibios_read_config_byte(bus, fn, 0x44, &stime)) {
-				if (hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0) {
-					s_clks = ~stime >> 6;
-					r_clks = ~stime >> 4;
+			const char *slave;
+			piix_sidetim_t sidetim;
+			byte sample   = 5 - timing.sample;
+			byte recovery = 4 - timing.recovery;
+			if (devid == PCI_DEVICE_ID_INTEL_82371SB_1
+			 && timing.sidetim_enabled
+			 && !pcibios_read_config_byte(piix_pci_bus, piix_pci_fn, 0x44, (byte *) &sidetim))
+				slave = "";		/* PIIX3 */
+			else
+				slave = "/slave";	/* PIIX, or PIIX3 in compatibility mode */
+			printk("    %s master%s: sample_CLKs=%d, recovery_CLKs=%d\n", hwif->name, slave, sample, recovery);
+			print_piix_drive_flags ("master:", timing.d0_flags);
+			if (!*slave) {
+				if (pri_sec == 0) {
+					sample   = 5 - sidetim.pri_sample;
+					recovery = 4 - sidetim.pri_recovery;
 				} else {
-					s_clks = ~stime >> 2;
-					r_clks = ~stime;
+					sample   = 5 - sidetim.sec_sample;
+					recovery = 4 - sidetim.sec_recovery;
 				}
-				s_clks = (s_clks & 3) + 2;
-				r_clks = (r_clks & 3) + 1;
-				printk("                   slave: sample_CLKs=%d, recovery_CLKs=%d\n",
-		 			s_clks, r_clks);
+				printk("         slave : sample_CLKs=%d, recovery_CLKs=%d\n", sample, recovery);
 			}
+			print_piix_drive_flags ("slave :", timing.d1_flags);
 		}
-		print_triton_drive_flags (0, time & 0xf);
-		print_triton_drive_flags (1, (time >> 4) & 0xf);
-#endif /* DISPLAY_TRITON_TIMINGS */
+#endif /* DISPLAY_PIIX_TIMINGS */
 	}
 
-quit: if (rc) printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc));
+quit: if (rc) printk("%s: pcibios access failed - %s\n", chipset, pcibios_strerror(rc));
 }
-
diff -u --recursive --new-file v2.1.29/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- v2.1.29/linux/drivers/char/keyboard.c	Mon Mar 17 14:54:25 1997
+++ linux/drivers/char/keyboard.c	Wed Mar 12 15:13:56 1997
@@ -1,4 +1,3 @@
-extern void allow_interrupts(void);
 /*
  * linux/drivers/char/keyboard.c
  *
@@ -1081,7 +1080,6 @@
 		reply_expected = 1;
 		outb_p(data, 0x60);
 		for(i=0; i<0x200000; i++) {
-			allow_interrupts();
 			inb_p(0x64);		/* just as a delay */
 			if (acknowledge)
 				return 1;
diff -u --recursive --new-file v2.1.29/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.1.29/linux/drivers/char/misc.c	Mon Mar 17 14:54:25 1997
+++ linux/drivers/char/misc.c	Thu Mar 20 17:11:51 1997
@@ -80,9 +80,10 @@
 	struct miscdevice *p;
 
 	len=0;
-	for (p = misc_list.next; p != &misc_list; p = p->next)
+	for (p = misc_list.next; p != &misc_list && len < 4000; p = p->next)
 		len += sprintf(buf+len, "%3i %s\n",p->minor, p->name ?: "");
-	return len;
+	*start = buf + offset;
+	return len > offset ? len - offset : 0;
 }
 
 #endif /* PROC_FS */
diff -u --recursive --new-file v2.1.29/linux/drivers/char/random.c linux/drivers/char/random.c
--- v2.1.29/linux/drivers/char/random.c	Tue Mar  4 10:25:23 1997
+++ linux/drivers/char/random.c	Tue Mar 25 11:25:22 1997
@@ -1162,7 +1162,7 @@
 		p += bytes;
 		ret += bytes;
 		
-		i = (c+sizeof(__u32)-1) / sizeof(__u32);
+		i = (bytes+sizeof(__u32)-1) / sizeof(__u32);
 		while (i--)
 			add_entropy_word(&random_state, buf[i]);
 	}
diff -u --recursive --new-file v2.1.29/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.1.29/linux/drivers/char/serial.c	Mon Mar 17 14:54:25 1997
+++ linux/drivers/char/serial.c	Tue Mar 18 15:05:27 1997
@@ -458,7 +458,7 @@
 				  int event)
 {
 	info->event |= 1 << event;
-	queue_task_irq_off(&info->tqueue, &tq_serial);
+	queue_task(&info->tqueue, &tq_serial);
 	mark_bh(SERIAL_BH);
 }
 
@@ -540,7 +540,7 @@
 	ignore_char:
 		*status = serial_inp(info, UART_LSR);
 	} while (*status & UART_LSR_DR);
-	queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+	queue_task(&tty->flip.tqueue, &tq_timer);
 }
 
 static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
@@ -625,7 +625,7 @@
 #ifdef SERIAL_DEBUG_OPEN
 			printk("scheduling hangup...");
 #endif
-			queue_task_irq_off(&info->tqueue_hangup,
+			queue_task(&info->tqueue_hangup,
 					   &tq_scheduler);
 		}
 	}
diff -u --recursive --new-file v2.1.29/linux/drivers/char/softdog.c linux/drivers/char/softdog.c
--- v2.1.29/linux/drivers/char/softdog.c	Sun Feb  2 05:18:35 1997
+++ linux/drivers/char/softdog.c	Thu Mar 20 17:11:51 1997
@@ -33,6 +33,7 @@
 #include <linux/mm.h>
 #include <linux/miscdevice.h>
 #include <linux/watchdog.h>
+#include <linux/reboot.h>
 #include <asm/uaccess.h>
 
 #define WATCHDOG_MINOR	130
@@ -54,12 +55,11 @@
  
 static void watchdog_fire(unsigned long data)
 {
-	extern void hard_reset_now(void);
 #ifdef ONLY_TESTING
 		printk(KERN_CRIT "SOFTDOG: Would Reboot.\n");
 #else
 	printk(KERN_CRIT "SOFTDOG: Initiating system reboot.\n");
-	hard_reset_now();
+	machine_restart(NULL);
 	printk("WATCHDOG: Reboot didn't ?????\n");
 #endif
 }
diff -u --recursive --new-file v2.1.29/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.29/linux/drivers/char/tty_io.c	Tue Mar  4 10:25:23 1997
+++ linux/drivers/char/tty_io.c	Tue Mar 18 13:30:03 1997
@@ -520,12 +520,27 @@
 
 /*
  * Sleeps until a vt is activated, or the task is interrupted. Returns
- * 0 if activation, -1 if interrupted.
+ * 0 if activation, -EINTR if interrupted.
  */
-int vt_waitactive(void)
+int vt_waitactive(int vt)
 {
-	interruptible_sleep_on(&vt_activate_queue);
-	return (current->signal & ~current->blocked) ? -1 : 0;
+	int retval;
+	struct wait_queue wait = { current, NULL };
+
+	add_wait_queue(&vt_activate_queue, &wait);
+	for (;;) {
+		current->state = TASK_INTERRUPTIBLE;
+		retval = 0;
+		if (vt == fg_console)
+			break;
+		retval = -EINTR;
+		if (current->signal & ~current->blocked)
+			break;
+		schedule();
+	}
+	remove_wait_queue(&vt_activate_queue, &wait);
+	current->state = TASK_RUNNING;
+	return retval;
 }
 
 #define vt_wake_waitactive() wake_up(&vt_activate_queue)
@@ -1210,7 +1225,7 @@
 	 * Make sure that the tty's task queue isn't activated.  If it
 	 * is, take it out of the linked list.
 	 */
-	cli();
+	spin_lock_irq(&tqueue_lock);
 	if (tty->flip.tqueue.sync) {
 		struct tq_struct *tq, *prev;
 
@@ -1224,7 +1239,7 @@
 			}
 		}
 	}
-	sti();
+	spin_unlock_irq(&tqueue_lock);
 	tty->magic = 0;
 	(*tty->driver.refcount)--;
 	free_page((unsigned long) tty);
diff -u --recursive --new-file v2.1.29/linux/drivers/char/tty_ioctl.c linux/drivers/char/tty_ioctl.c
--- v2.1.29/linux/drivers/char/tty_ioctl.c	Mon Mar 17 14:54:25 1997
+++ linux/drivers/char/tty_ioctl.c	Wed Mar 12 15:33:58 1997
@@ -157,16 +157,12 @@
 		return retval;
 
 	if (opt & TERMIOS_TERMIO) {
-		retval = verify_area(VERIFY_READ, (void *) arg, sizeof(struct termio));
-		if (retval)
-			return retval;
 		memcpy(&tmp_termios, tty->termios, sizeof(struct termios));
-		user_termio_to_kernel_termios(&tmp_termios, (struct termio *) arg);
+		if (user_termio_to_kernel_termios(&tmp_termios, (struct termio *) arg))
+			return -EFAULT;
 	} else {
-		retval = user_termios_to_kernel_termios
-			(&tmp_termios, (struct termios *) arg);
-		if (retval)
-			return retval;
+		if (user_termios_to_kernel_termios(&tmp_termios, (struct termios *) arg))
+			return -EFAULT;
 	}
 
 	if ((opt & TERMIOS_FLUSH) && tty->ldisc.flush_buffer)
@@ -184,12 +180,8 @@
 
 static int get_termio(struct tty_struct * tty, struct termio * termio)
 {
-	int i;
-
-	i = verify_area(VERIFY_WRITE, termio, sizeof (struct termio));
-	if (i)
-		return i;
-	kernel_termios_to_user_termio(termio, tty->termios);
+	if (kernel_termios_to_user_termio(termio, tty->termios))
+		return -EFAULT;
 	return 0;
 }
 
@@ -411,9 +403,9 @@
 			return set_ltchars(real_tty, (struct ltchars *) arg);
 #endif
 		case TCGETS:
-			return kernel_termios_to_user_termios
-				((struct termios *)arg,
-				 real_tty->termios);
+			if (kernel_termios_to_user_termios((struct termios *)arg, real_tty->termios))
+				return -EFAULT;
+			return 0;
 		case TCSETSF:
 			opt |= TERMIOS_FLUSH;
 		case TCSETSW:
@@ -488,15 +480,17 @@
 				retval = inq_canon(tty);
 			return put_user(retval, (unsigned int *) arg);
 		case TIOCGLCKTRMIOS:
-			return kernel_termios_to_user_termios
-				((struct termios *)arg,
-				 real_tty->termios_locked);
+			if (kernel_termios_to_user_termios((struct termios *)arg, real_tty->termios_locked))
+				return -EFAULT;
+			return 0;
+
 		case TIOCSLCKTRMIOS:
 			if (!suser())
 				return -EPERM;
-			return user_termios_to_kernel_termios
-				(real_tty->termios_locked,
-				 (struct termios *) arg);
+			if (user_termios_to_kernel_termios(real_tty->termios_locked, (struct termios *) arg))
+				return -EFAULT;
+			return 0;
+
 		case TIOCPKT:
 		{
 			int pktmode;
diff -u --recursive --new-file v2.1.29/linux/drivers/char/vt.c linux/drivers/char/vt.c
--- v2.1.29/linux/drivers/char/vt.c	Sun Jan 26 02:07:17 1997
+++ linux/drivers/char/vt.c	Tue Mar 18 12:18:42 1997
@@ -58,7 +58,7 @@
 extern int setkeycode(unsigned int scancode, unsigned int keycode);
 extern void compute_shiftstate(void);
 extern void complete_change_console(unsigned int new_console);
-extern int vt_waitactive(void);
+extern int vt_waitactive(int vt);
 extern void do_blank_screen(int nopowersave);
 
 extern unsigned int keymap_count;
@@ -807,13 +807,7 @@
 			return -EPERM;
 		if (arg == 0 || arg > MAX_NR_CONSOLES)
 			return -ENXIO;
-		arg--;
-		while (fg_console != arg)
-		{
-			if (vt_waitactive() < 0)
-				return -EINTR;
-		}
-		return 0;
+		return vt_waitactive(arg-1);
 
 	/*
 	 * If a vt is under process control, the kernel will not switch to it
diff -u --recursive --new-file v2.1.29/linux/drivers/char/wdt.c linux/drivers/char/wdt.c
--- v2.1.29/linux/drivers/char/wdt.c	Sun Jan 19 05:47:24 1997
+++ linux/drivers/char/wdt.c	Thu Mar 20 17:11:51 1997
@@ -42,6 +42,7 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <linux/notifier.h>
+#include <linux/reboot.h>
 
 static int wdt_is_open=0;
 
@@ -145,7 +146,7 @@
 		printk(KERN_CRIT "Would Reboot.\n");
 #else		
 		printk(KERN_CRIT "Initiating system reboot.\n");
-		hard_reset_now();
+		machine_restart(NULL);
 #endif		
 #else
 		printk(KERN_CRIT "Reset in 5ms.\n");
diff -u --recursive --new-file v2.1.29/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v2.1.29/linux/drivers/net/3c509.c	Sun Feb  2 05:18:35 1997
+++ linux/drivers/net/3c509.c	Tue Mar 11 13:24:57 1997
@@ -53,9 +53,9 @@
 #include <asm/io.h>
 
 #ifdef EL3_DEBUG
-int el3_debug = EL3_DEBUG;
+static int el3_debug = EL3_DEBUG;
 #else
-int el3_debug = 2;
+static int el3_debug = 2;
 #endif
 
 /* To minimize the size of the driver source I only define operating
diff -u --recursive --new-file v2.1.29/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v2.1.29/linux/drivers/net/3c59x.c	Thu Feb 27 10:57:30 1997
+++ linux/drivers/net/3c59x.c	Thu Mar 20 17:11:51 1997
@@ -24,7 +24,7 @@
    Tx process.  Bus master transfers are always disabled by default, but
    iff this is set they may be turned on using 'options'. */
 #define VORTEX_BUS_MASTER
-
+#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/kernel.h>
@@ -78,9 +78,9 @@
 #endif
 
 #ifdef VORTEX_DEBUG
-int vortex_debug = VORTEX_DEBUG;
+static int vortex_debug = VORTEX_DEBUG;
 #else
-int vortex_debug = 1;
+static int vortex_debug = 1;
 #endif
 
 #ifdef CONFIG_PCI
diff -u --recursive --new-file v2.1.29/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.29/linux/drivers/net/Config.in	Thu Feb 27 10:57:30 1997
+++ linux/drivers/net/Config.in	Wed Mar 12 15:10:13 1997
@@ -30,9 +30,6 @@
     tristate '3c590 series (592/595/597) "Vortex" support' CONFIG_VORTEX
   fi
   bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE
-  if [ "$CONFIG_LANCE" = "y" ]; then
-    bool 'AMD PCInet32 (VLB and PCI) support' CONFIG_LANCE32
-  fi  
   bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC
   if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
     tristate 'WD80*3 support' CONFIG_WD80x3
@@ -72,9 +69,11 @@
   fi
   bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA
   if [ "$CONFIG_NET_EISA" = "y" ]; then
+    bool 'AMD PCnet32 (VLB and PCI) support' CONFIG_PCNET32
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate 'Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200
     fi
+    
     tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT
     tristate 'CS89x0 support' CONFIG_CS89x0
     tristate 'Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5
diff -u --recursive --new-file v2.1.29/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.29/linux/drivers/net/Makefile	Thu Feb 27 10:57:30 1997
+++ linux/drivers/net/Makefile	Wed Mar 12 15:10:13 1997
@@ -255,15 +255,16 @@
 L_OBJS += lance.o
 endif
 
-ifeq ($(CONFIG_DEFXX),y)
-L_OBJS += defxx.o
-endif
-
 ifeq ($(CONFIG_LANCE),y)
 L_OBJS += lance.o
-  ifeq ($(CONFIG_LANCE32),y)
-  L_OBJS += lance32.o
-  endif
+endif
+
+ifeq ($(CONFIG_PCNET32),y)
+L_OBJS += pcnet32.o
+endif
+
+ifeq ($(CONFIG_DEFXX),y)
+L_OBJS += defxx.o
 endif
 
 ifeq ($(CONFIG_SUNLANCE),y)
diff -u --recursive --new-file v2.1.29/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.1.29/linux/drivers/net/Space.c	Sun Feb  2 05:18:36 1997
+++ linux/drivers/net/Space.c	Wed Mar 12 15:10:13 1997
@@ -52,6 +52,7 @@
 extern int eepro_probe(struct device *);
 extern int el3_probe(struct device *);
 extern int at1500_probe(struct device *);
+extern int pcnet32_probe(struct device *);
 extern int at1700_probe(struct device *);
 extern int fmv18x_probe(struct device *);
 extern int eth16i_probe(struct device *);
@@ -153,6 +154,9 @@
 #ifdef CONFIG_CS89x0
  	&& cs89x0_probe(dev)
 #endif
+#ifdef CONFIG_PCNET32
+	&& pcnet32_probe(dev)
+#endif	
 #ifdef CONFIG_AT1700
 	&& at1700_probe(dev)
 #endif
diff -u --recursive --new-file v2.1.29/linux/drivers/net/bpqether.c linux/drivers/net/bpqether.c
--- v2.1.29/linux/drivers/net/bpqether.c	Sun Feb  2 05:18:36 1997
+++ linux/drivers/net/bpqether.c	Thu Mar 20 17:11:51 1997
@@ -325,7 +325,9 @@
 	skb->dev = dev;
 	dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0);
 
-	return dev->hard_start_xmit(skb, dev);
+	dev_queue_xmit(skb);
+
+	return 0;
 }
 
 /*
diff -u --recursive --new-file v2.1.29/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.1.29/linux/drivers/net/de4x5.c	Sun Feb  2 05:18:37 1997
+++ linux/drivers/net/de4x5.c	Wed Mar 26 09:51:23 1997
@@ -1380,6 +1380,8 @@
 		   dev->name, sts);
 	    return;
 	}
+if (sts & (STS_AIS | STS_UNF | STS_SE | STS_LNF | STS_RWT | STS_RU | STS_TJT))
+	printk("STS=%08x\n",sts);
     }
 
     /* Load the TX ring with any locally stored packets */
@@ -1563,7 +1565,10 @@
 	if ((omr & OMR_TR) < OMR_TR) {
 	    omr += 0x4000;
 	} else {
-	    omr |= OMR_SF;
+	    if (omr & OMR_TTM)
+		omr &= ~OMR_TTM;
+	    else
+		omr |= OMR_SF;
 	}
 	outl(omr | OMR_ST | OMR_SR, DE4X5_OMR);
     }
diff -u --recursive --new-file v2.1.29/linux/drivers/net/hdlcdrv.c linux/drivers/net/hdlcdrv.c
--- v2.1.29/linux/drivers/net/hdlcdrv.c	Thu Feb 27 10:57:30 1997
+++ linux/drivers/net/hdlcdrv.c	Thu Mar 20 17:11:51 1997
@@ -31,6 +31,7 @@
  *        18.10.96  Changed to new user space access routines 
  *                  (copy_{to,from}_user)
  *   0.2  21.11.96  various small changes
+ *   0.3  03.03.97  fixed (hopefully) IP not working with ax.25 as a module
  */
 
 /*****************************************************************************/
@@ -50,10 +51,10 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/hdlcdrv.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 /* prototypes for ax25_encapsulate and ax25_rebuild_header */
 #include <net/ax25.h> 
-#endif /* CONFIG_AX25 */
+#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
 
 /* make genksyms happy */
 #include <linux/ip.h>
@@ -745,7 +746,19 @@
 		bi.data.cs.ptt = hdlcdrv_ptt(s);
 		bi.data.cs.dcd = s->hdlcrx.dcd;
 		bi.data.cs.ptt_keyed = s->ptt_keyed;
-		bi.data.cs.stats = s->stats;
+		bi.data.cs.tx_packets = s->stats.tx_packets;
+		bi.data.cs.tx_errors = s->stats.tx_errors;
+		bi.data.cs.rx_packets = s->stats.rx_packets;
+		bi.data.cs.rx_errors = s->stats.rx_errors;
+		break;		
+
+	case HDLCDRVCTL_OLDGETSTAT:
+		bi.data.ocs.ptt = hdlcdrv_ptt(s);
+		bi.data.ocs.dcd = s->hdlcrx.dcd;
+		bi.data.ocs.ptt_keyed = s->ptt_keyed;
+#if LINUX_VERSION_CODE < 0x20100
+		bi.data.ocs.stats = s->stats;
+#endif
 		break;		
 
 	case HDLCDRVCTL_CALIBRATE:
@@ -858,13 +871,13 @@
 
 	skb_queue_head_init(&s->send_queue);
 	
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 	dev->hard_header = ax25_encapsulate;
 	dev->rebuild_header = ax25_rebuild_header;
-#else /* CONFIG_AX25 */
+#else /* CONFIG_AX25 || CONFIG_AX25_MODULE */
 	dev->hard_header = NULL;
 	dev->rebuild_header = NULL;
-#endif /* CONFIG_AX25 */
+#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
 	dev->set_mac_address = hdlcdrv_set_mac_address;
 	
 	dev->type = ARPHRD_AX25;           /* AF_AX25 device */
@@ -989,8 +1002,7 @@
 int init_module(void)
 {
 	printk(KERN_INFO "hdlcdrv: (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
-	printk(KERN_INFO "hdlcdrv: version 0.2 compiled %s %s\n", 
-	       __TIME__, __DATE__);
+	printk(KERN_INFO "hdlcdrv: version 0.3 compiled " __TIME__ " " __DATE__ "\n");
 #if LINUX_VERSION_CODE < 0x20115
         register_symtab(&hdlcdrv_syms);
 #endif
diff -u --recursive --new-file v2.1.29/linux/drivers/net/lance.c linux/drivers/net/lance.c
--- v2.1.29/linux/drivers/net/lance.c	Sun Feb  2 05:18:40 1997
+++ linux/drivers/net/lance.c	Wed Mar 12 15:10:14 1997
@@ -162,11 +162,13 @@
 
 /*
  * Changes:
- *	Thomas Bogendoerfer (tsbogend@bigbug.franken.de):
+ *	Thomas Bogendoerfer (tsbogend@alpha.franken.de):
  *	- added support for Linux/Alpha, but removed most of it, because
  *        it worked only for the PCI chip. 
  *      - added hook for the 32bit lance driver
  *      - added PCnetPCI II (79C970A) to chip table
+ *      - made 32bit driver standalone
+ *      - changed setting of autoselect bit
  *
  *	Paul Gortmaker (gpg109@rsphy1.anu.edu.au):
  *	- hopefully fix above so Linux/Alpha can use ISA cards too.
@@ -245,6 +247,7 @@
 #define LANCE_MUST_REINIT_RING  0x00000004
 #define LANCE_MUST_UNRESET      0x00000008
 #define LANCE_HAS_MISSED_FRAME  0x00000010
+#define PCNET32_POSSIBLE        0x00000020
 
 /* A mapping from the chip ID number to the part number and features.
    These are from the datasheets -- in real life the '970 version
@@ -264,15 +267,15 @@
 			LANCE_HAS_MISSED_FRAME},
 	{0x2420, "PCnet/PCI 79C970",		/* 79C970 or 79C974 PCnet-SCSI, PCI. */
 		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
-			LANCE_HAS_MISSED_FRAME},
+			LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
 	/* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call
 		it the PCnet32. */
 	{0x2430, "PCnet32",					/* 79C965 PCnet for VL bus. */
 		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
-			LANCE_HAS_MISSED_FRAME},
+			LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
         {0x2621, "PCnet/PCI-II 79C970A",        /* 79C970A PCInetPCI II. */
                 LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
-                        LANCE_HAS_MISSED_FRAME},
+                        LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
 	{0x0, 	 "PCnet (unknown)",
 		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
 			LANCE_HAS_MISSED_FRAME},
@@ -312,7 +315,7 @@
 	if (virt_to_bus(high_memory) <= 16*1024*1024)
 		lance_need_isa_bounce_buffers = 0;
 
-#if defined(CONFIG_PCI)
+#if defined(CONFIG_PCI) && !defined(CONFIG_PCNET32)
     if (pcibios_present()) {
 	    int pci_index;
 		if (lance_debug > 1)
@@ -427,6 +430,15 @@
 				break;
 		}
 	}
+    
+#ifdef CONFIG_PCNET32
+        /*
+	 * if pcnet32 is configured and the chip is capable of 32bit mode
+	 * leave the card alone
+	 */
+        if (chip_table[lance_version].flags & PCNET32_POSSIBLE)
+          return;
+#endif    
 
 	dev = init_etherdev(0, 0);
 	dev->open = lance_open_fail;
@@ -441,15 +453,6 @@
 	dev->base_addr = ioaddr;
 	request_region(ioaddr, LANCE_TOTAL_SIZE, chip_table[lance_version].name);
 
-#ifdef CONFIG_LANCE32
-        /* look if it's a PCI or VLB chip */
-        if (lance_version == PCNET_PCI || lance_version == PCNET_VLB || lance_version == PCNET_PCI_II) {
-	    extern void lance32_probe1 (struct device *dev, const char *chipname, int pci_irq_line);
-	    
-	    lance32_probe1 (dev, chipname, pci_irq_line);
-	    return;
-	}
-#endif    
 	/* Make certain the data structures used by the LANCE are aligned and DMAble. */
 	lp = (struct lance_private *)(((unsigned long)kmalloc(sizeof(*lp)+7,
 										   GFP_DMA | GFP_KERNEL)+7) & ~7);
@@ -606,7 +609,8 @@
 		/* Turn on auto-select of media (10baseT or BNC) so that the user
 		   can watch the LEDs even if the board isn't opened. */
 		outw(0x0002, ioaddr+LANCE_ADDR);
-		outw(0x0002, ioaddr+LANCE_BUS_IF);
+		/* set autoselect and clean xmausel */
+		outw(inw(ioaddr+LANCE_BUS_IF) & 0xfffe | 0x0002, ioaddr+LANCE_BUS_IF);
 	}
 
 	if (lance_debug > 0  &&  did_version++ == 0)
@@ -663,7 +667,8 @@
 	if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
 		/* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */
 		outw(0x0002, ioaddr+LANCE_ADDR);
-		outw(0x0002, ioaddr+LANCE_BUS_IF);
+		/* set autoselect and clean xmausel */
+		outw(inw(ioaddr+LANCE_BUS_IF) & 0xfffe | 0x0002, ioaddr+LANCE_BUS_IF);
 	}
 
 	if (lance_debug > 1)
diff -u --recursive --new-file v2.1.29/linux/drivers/net/lance32.c linux/drivers/net/lance32.c
--- v2.1.29/linux/drivers/net/lance32.c	Sun Feb  2 05:18:40 1997
+++ linux/drivers/net/lance32.c	Wed Dec 31 16:00:00 1969
@@ -1,841 +0,0 @@
-/* lance32.c: An AMD PCnet32 ethernet driver for linux. */
-/*
- *      Copyright 1996 Thomas Bogendoerfer
- * 
- * 	Derived from the lance driver written 1993,1994,1995 by Donald Becker.
- * 
- * 	Copyright 1993 United States Government as represented by the
- * 	Director, National Security Agency.
- * 
- * 	This software may be used and distributed according to the terms
- * 	of the GNU Public License, incorporated herein by reference.
- *
- * 	This driver is for PCnet32 and PCnetPCI based ethercards
- */
-
-static const char *version = "lance32.c:v0.10 28.4.96 tsbogend@bigbug.franken.de\n";
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/ptrace.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/malloc.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/bios32.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-
-#ifdef LANCE32_DEBUG
-int lance32_debug = LANCE32_DEBUG;
-#else
-int lance32_debug = 1;
-#endif
-
-/*
- * 				Theory of Operation
- * 
- * This driver uses the same software structure as the normal lance
- * driver. So look for a verbose description in lance.c. The differences
- * to the normal lance driver is the use of the 32bit mode of PCnet32
- * and PCnetPCI chips. Because these chips are 32bit chips, there is no
- * 16MB limitation and we don't need bounce buffers.
- */
- 
-/*
- * History:
- * v0.01:  Initial version
- *         only tested on Alpha Noname Board
- * v0.02:  changed IRQ handling for new interrupt scheme (dev_id)
- *         tested on a ASUS SP3G
- * v0.10:  fixed an odd problem with the 79C794 in a Compaq Deskpro XL
- *         looks like the 974 doesn't like stopping and restarting in a
- *         short period of time; now we do a reinit of the lance; the
- *         bug was triggered by doing ifconfig eth0 <ip> broadcast <addr>
- *         and hangs the machine (thanks to Klaus Liedl for debugging)
- */
-
-
-/*
- * Set the number of Tx and Rx buffers, using Log_2(# buffers).
- * Reasonable default values are 4 Tx buffers, and 16 Rx buffers.
- * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4).
- */
-#ifndef LANCE_LOG_TX_BUFFERS
-#define LANCE_LOG_TX_BUFFERS 4
-#define LANCE_LOG_RX_BUFFERS 4
-#endif
-
-#define TX_RING_SIZE			(1 << (LANCE_LOG_TX_BUFFERS))
-#define TX_RING_MOD_MASK		(TX_RING_SIZE - 1)
-#define TX_RING_LEN_BITS		((LANCE_LOG_TX_BUFFERS) << 12)
-
-#define RX_RING_SIZE			(1 << (LANCE_LOG_RX_BUFFERS))
-#define RX_RING_MOD_MASK		(RX_RING_SIZE - 1)
-#define RX_RING_LEN_BITS		((LANCE_LOG_RX_BUFFERS) << 4)
-
-#define PKT_BUF_SZ		1544
-
-/* Offsets from base I/O address. */
-#define LANCE_DATA 0x10
-#define LANCE_ADDR 0x12
-#define LANCE_RESET 0x14
-#define LANCE_BUS_IF 0x16
-#define LANCE_TOTAL_SIZE 0x18
-
-/* The LANCE Rx and Tx ring descriptors. */
-struct lance32_rx_head {
-	u32 base;
-	s16 buf_length;
-        s16 status;    
-	u32 msg_length;
-	u32 reserved;
-};
-	
-struct lance32_tx_head {
-	u32 base;
-	s16 length;
-        s16 status;
-	u32 misc;
-	u32 reserved;
-};
-
-
-/* The LANCE 32-Bit initialization block, described in databook. */
-struct lance32_init_block {
-	u16 mode;
-	u16 tlen_rlen;
-	u8  phys_addr[6];
-	u16 reserved;
-	u32 filter[2];
-	/* Receive and transmit ring base, along with extra bits. */    
-	u32 rx_ring;
-	u32 tx_ring;
-};
-
-struct lance32_private {
-	/* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */
-	struct lance32_rx_head   rx_ring[RX_RING_SIZE];
-	struct lance32_tx_head   tx_ring[TX_RING_SIZE];
-	struct lance32_init_block	init_block;
-	const char *name;
-	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
-	struct sk_buff* tx_skbuff[TX_RING_SIZE];
-	unsigned long rx_buffs;			/* Address of Rx and Tx buffers. */
-	int cur_rx, cur_tx;			/* The next free ring entry */
-	int dirty_rx, dirty_tx;		        /* The ring entries to be free()ed. */
-	int dma;
-	struct net_device_stats stats;
-	char tx_full;
-	unsigned long lock;
-};
-
-static int lance32_open(struct device *dev);
-static void lance32_init_ring(struct device *dev);
-static int lance32_start_xmit(struct sk_buff *skb, struct device *dev);
-static int lance32_rx(struct device *dev);
-static void lance32_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static int lance32_close(struct device *dev);
-static struct net_device_stats *lance32_get_stats(struct device *dev);
-static void lance32_set_multicast_list(struct device *dev);
-
-
-
-/* lance32_probe1 */
-void lance32_probe1(struct device *dev, char *chipname, int pci_irq_line)
-{
-	struct lance32_private *lp;
-        int ioaddr = dev->base_addr;
-	short dma_channels;					/* Mark spuriously-busy DMA channels */    
-        int i;
-    
-	/* Make certain the data structures used by the LANCE are 16byte aligned and DMAble. */
-	lp = (struct lance32_private *) (((unsigned long)kmalloc(sizeof(*lp)+15, GFP_DMA | GFP_KERNEL)+15) & ~15);
-      
-	memset(lp, 0, sizeof(*lp));
-	dev->priv = lp;
-	lp->name = chipname;
-	lp->rx_buffs = (unsigned long) kmalloc(PKT_BUF_SZ*RX_RING_SIZE, GFP_DMA | GFP_KERNEL);
-
-	lp->init_block.mode = 0x0003;		/* Disable Rx and Tx. */
-        lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS;    
-	for (i = 0; i < 6; i++)
-		lp->init_block.phys_addr[i] = dev->dev_addr[i];
-	lp->init_block.filter[0] = 0x00000000;
-	lp->init_block.filter[1] = 0x00000000;
-	lp->init_block.rx_ring = (u32)virt_to_bus(lp->rx_ring);
-	lp->init_block.tx_ring = (u32)virt_to_bus(lp->tx_ring);
-    
-	/* switch lance to 32bit mode */
-	outw(0x0014, ioaddr+LANCE_ADDR);
-	outw(0x0002, ioaddr+LANCE_BUS_IF);
-
-	outw(0x0001, ioaddr+LANCE_ADDR);
-	inw(ioaddr+LANCE_ADDR);
-	outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
-	outw(0x0002, ioaddr+LANCE_ADDR);
-	inw(ioaddr+LANCE_ADDR);
-	outw(((u32)virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
-	outw(0x0000, ioaddr+LANCE_ADDR);
-	inw(ioaddr+LANCE_ADDR);
-    
-	if (pci_irq_line) {
-		dev->dma = 4;			/* Native bus-master, no DMA channel needed. */
-		dev->irq = pci_irq_line;
-	} else {
-		/* The DMA channel may be passed in PARAM1. */
-		if (dev->mem_start & 0x07)
-			dev->dma = dev->mem_start & 0x07;
-	}
-
-	if (dev->dma == 0) {
-		/* Read the DMA channel status register, so that we can avoid
-		   stuck DMA channels in the DMA detection below. */
-		dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) |
-			(inb(DMA2_STAT_REG) & 0xf0);
-	}
-	if (dev->irq >= 2)
-		printk(" assigned IRQ %d", dev->irq);
-	else {
-		/* To auto-IRQ we enable the initialization-done and DMA error
-		   interrupts. For ISA boards we get a DMA error, but VLB and PCI
-		   boards will work. */
-		autoirq_setup(0);
-
-		/* Trigger an initialization just for the interrupt. */
-		outw(0x0041, ioaddr+LANCE_DATA);
-
-		dev->irq = autoirq_report(1);
-		if (dev->irq)
-			printk(", probed IRQ %d", dev->irq);
-		else {
-			printk(", failed to detect IRQ line.\n");
-			return;
-		}
-
-		/* Check for the initialization done bit, 0x0100, which means
-		   that we don't need a DMA channel. */
-		if (inw(ioaddr+LANCE_DATA) & 0x0100)
-			dev->dma = 4;
-	}
-
-	if (dev->dma == 4) {
-		printk(", no DMA needed.\n");
-	} else if (dev->dma) {
-		if (request_dma(dev->dma, chipname)) {
-			printk("DMA %d allocation failed.\n", dev->dma);
-			return;
-		} else
-			printk(", assigned DMA %d.\n", dev->dma);
-	} else {			/* OK, we have to auto-DMA. */
-		for (i = 0; i < 4; i++) {
-			static const char dmas[] = { 5, 6, 7, 3 };
-			int dma = dmas[i];
-			int boguscnt;
-
-			/* Don't enable a permanently busy DMA channel, or the machine
-			   will hang. */
-			if (test_bit(dma, &dma_channels))
-				continue;
-			outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */
-			if (request_dma(dma, chipname))
-				continue;
-			set_dma_mode(dma, DMA_MODE_CASCADE);
-			enable_dma(dma);
-
-			/* Trigger an initialization. */
-			outw(0x0001, ioaddr+LANCE_DATA);
-			for (boguscnt = 100; boguscnt > 0; --boguscnt)
-				if (inw(ioaddr+LANCE_DATA) & 0x0900)
-					break;
-			if (inw(ioaddr+LANCE_DATA) & 0x0100) {
-				dev->dma = dma;
-				printk(", DMA %d.\n", dev->dma);
-				break;
-			} else {
-				disable_dma(dma);
-				free_dma(dma);
-			}
-		}
-		if (i == 4) {			/* Failure: bail. */
-			printk("DMA detection failed.\n");
-			return;
-		}
-	}
-    
-        outw(0x0002, ioaddr+LANCE_ADDR);
-	outw(0x0002, ioaddr+LANCE_BUS_IF);
-
-	if (lance32_debug > 0)
-		printk(version);
-
-	/* The LANCE-specific entries in the device structure. */
-	dev->open = &lance32_open;
-	dev->hard_start_xmit = &lance32_start_xmit;
-	dev->stop = &lance32_close;
-	dev->get_stats = &lance32_get_stats;
-	dev->set_multicast_list = &lance32_set_multicast_list;
-
-	return;
-}
-
-
-static int
-lance32_open(struct device *dev)
-{
-	struct lance32_private *lp = (struct lance32_private *)dev->priv;
-	int ioaddr = dev->base_addr;
-	int i;
-
-	if (dev->irq == 0 ||
-		request_irq(dev->irq, &lance32_interrupt, 0, lp->name, (void *)dev)) {
-		return -EAGAIN;
-	}
-
-	irq2dev_map[dev->irq] = dev;
-
-	/* Reset the LANCE */
-	inw(ioaddr+LANCE_RESET);
-
-	/* switch lance to 32bit mode */
-	outw(0x0014, ioaddr+LANCE_ADDR);
-	outw(0x0002, ioaddr+LANCE_BUS_IF);
-
-	/* The DMA controller is used as a no-operation slave, "cascade mode". */
-	if (dev->dma != 4) {
-		enable_dma(dev->dma);
-		set_dma_mode(dev->dma, DMA_MODE_CASCADE);
-	}
-
-	/* Turn on auto-select of media (AUI, BNC). */
-	outw(0x0002, ioaddr+LANCE_ADDR);
-	outw(0x0002, ioaddr+LANCE_BUS_IF);
-
-	if (lance32_debug > 1)
-		printk("%s: lance32_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n",
-			   dev->name, dev->irq, dev->dma,
-		           (u32) virt_to_bus(lp->tx_ring),
-		           (u32) virt_to_bus(lp->rx_ring),
-			   (u32) virt_to_bus(&lp->init_block));
-
-	lp->init_block.mode = 0x0000;
-	lp->init_block.filter[0] = 0x00000000;
-	lp->init_block.filter[1] = 0x00000000;
-	lance32_init_ring(dev);
-    
-	/* Re-initialize the LANCE, and start it when done. */
-	outw(0x0001, ioaddr+LANCE_ADDR);
-	outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
-	outw(0x0002, ioaddr+LANCE_ADDR);
-	outw(((u32)virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
-
-	outw(0x0004, ioaddr+LANCE_ADDR);
-	outw(0x0915, ioaddr+LANCE_DATA);
-
-	outw(0x0000, ioaddr+LANCE_ADDR);
-	outw(0x0001, ioaddr+LANCE_DATA);
-
-	dev->tbusy = 0;
-	dev->interrupt = 0;
-	dev->start = 1;
-	i = 0;
-	while (i++ < 100)
-		if (inw(ioaddr+LANCE_DATA) & 0x0100)
-			break;
-	/* 
-	 * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
-	 * reports that doing so triggers a bug in the '974.
-	 */
- 	outw(0x0042, ioaddr+LANCE_DATA);
-
-	if (lance32_debug > 2)
-		printk("%s: LANCE32 open after %d ticks, init block %#x csr0 %4.4x.\n",
-			   dev->name, i, (u32) virt_to_bus(&lp->init_block), inw(ioaddr+LANCE_DATA));
-
-	return 0;					/* Always succeed */
-}
-
-/*
- * The LANCE has been halted for one reason or another (busmaster memory
- * arbitration error, Tx FIFO underflow, driver stopped it to reconfigure,
- * etc.).  Modern LANCE variants always reload their ring-buffer
- * configuration when restarted, so we must reinitialize our ring
- * context before restarting.  As part of this reinitialization,
- * find all packets still on the Tx ring and pretend that they had been
- * sent (in effect, drop the packets on the floor) - the higher-level
- * protocols will time out and retransmit.  It'd be better to shuffle
- * these skbs to a temp list and then actually re-Tx them after
- * restarting the chip, but I'm too lazy to do so right now.  dplatt@3do.com
- */
-
-static void 
-lance32_purge_tx_ring(struct device *dev)
-{
-	struct lance32_private *lp = (struct lance32_private *)dev->priv;
-	int i;
-
-	for (i = 0; i < TX_RING_SIZE; i++) {
-		if (lp->tx_skbuff[i]) {
-			dev_kfree_skb(lp->tx_skbuff[i],FREE_WRITE);
-			lp->tx_skbuff[i] = NULL;
-		}
-	}
-}
-
-
-/* Initialize the LANCE Rx and Tx rings. */
-static void
-lance32_init_ring(struct device *dev)
-{
-	struct lance32_private *lp = (struct lance32_private *)dev->priv;
-	int i;
-
-	lp->lock = 0, lp->tx_full = 0;
-	lp->cur_rx = lp->cur_tx = 0;
-	lp->dirty_rx = lp->dirty_tx = 0;
-
-	for (i = 0; i < RX_RING_SIZE; i++) {
-		lp->rx_ring[i].base = (u32)virt_to_bus((char *)lp->rx_buffs + i*PKT_BUF_SZ);
-		lp->rx_ring[i].buf_length = -PKT_BUF_SZ;
-	        lp->rx_ring[i].status = 0x8000;	    
-	}
-	/* The Tx buffer address is filled in as needed, but we do need to clear
-	   the upper ownership bit. */
-	for (i = 0; i < TX_RING_SIZE; i++) {
-	        lp->tx_ring[i].base = 0;
-	        lp->tx_ring[i].status = 0;
-	}
-
-        lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS;
-	for (i = 0; i < 6; i++)
-		lp->init_block.phys_addr[i] = dev->dev_addr[i];
-	lp->init_block.rx_ring = (u32)virt_to_bus(lp->rx_ring);
-	lp->init_block.tx_ring = (u32)virt_to_bus(lp->tx_ring);
-}
-
-static void
-lance32_restart(struct device *dev, unsigned int csr0_bits, int must_reinit)
-{
-        int i;
-	int ioaddr = dev->base_addr;
-    
-	lance32_purge_tx_ring(dev);
-	lance32_init_ring(dev);
-    
-	outw(0x0000, ioaddr + LANCE_ADDR);
-        /* ReInit Ring */
-        outw(0x0001, ioaddr + LANCE_DATA);
-	i = 0;
-	while (i++ < 100)
-		if (inw(ioaddr+LANCE_DATA) & 0x0100)
-			break;
-
-	outw(csr0_bits, ioaddr + LANCE_DATA);
-}
-
-static int
-lance32_start_xmit(struct sk_buff *skb, struct device *dev)
-{
-	struct lance32_private *lp = (struct lance32_private *)dev->priv;
-	int ioaddr = dev->base_addr;
-	int entry;
-	unsigned long flags;
-
-	/* Transmitter timeout, serious problems. */
-	if (dev->tbusy) {
-		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 20)
-			return 1;
-		outw(0, ioaddr+LANCE_ADDR);
-		printk("%s: transmit timed out, status %4.4x, resetting.\n",
-			   dev->name, inw(ioaddr+LANCE_DATA));
-		outw(0x0004, ioaddr+LANCE_DATA);
-		lp->stats.tx_errors++;
-#ifndef final_version
-		{
-			int i;
-			printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
-				   lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
-				   lp->cur_rx);
-			for (i = 0 ; i < RX_RING_SIZE; i++)
-				printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
-					   lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
-					   lp->rx_ring[i].msg_length);
-			for (i = 0 ; i < TX_RING_SIZE; i++)
-				printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
-					   lp->tx_ring[i].base, -lp->tx_ring[i].length,
-					   lp->tx_ring[i].misc);
-			printk("\n");
-		}
-#endif
-		lance32_restart(dev, 0x0042, 1);
-
-		dev->tbusy=0;
-		dev->trans_start = jiffies;
-
-		return 0;
-	}
-
-	if (skb == NULL) {
-		dev_tint(dev);
-		return 0;
-	}
-
-	if (skb->len <= 0)
-		return 0;
-
-	if (lance32_debug > 3) {
-		outw(0x0000, ioaddr+LANCE_ADDR);
-		printk("%s: lance32_start_xmit() called, csr0 %4.4x.\n", dev->name,
-			   inw(ioaddr+LANCE_DATA));
-		outw(0x0000, ioaddr+LANCE_DATA);
-	}
-
-	/* Block a timer-based transmit from overlapping.  This could better be
-	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
-	if (set_bit(0, (void*)&dev->tbusy) != 0) {
-		printk("%s: Transmitter access conflict.\n", dev->name);
-		return 1;
-	}
-
-	if (set_bit(0, (void*)&lp->lock) != 0) {
-		if (lance32_debug > 0)
-			printk("%s: tx queue lock!.\n", dev->name);
-		/* don't clear dev->tbusy flag. */
-		return 1;
-	}
-
-	/* Fill in a Tx ring entry */
-
-	/* Mask to ring buffer boundary. */
-	entry = lp->cur_tx & TX_RING_MOD_MASK;
-
-	/* Caution: the write order is important here, set the base address
-	   with the "ownership" bits last. */
-
-	lp->tx_ring[entry].length = -skb->len;
-
-	lp->tx_ring[entry].misc = 0x00000000;
-
-	lp->tx_skbuff[entry] = skb;
-	lp->tx_ring[entry].base = (u32)virt_to_bus(skb->data);
-        lp->tx_ring[entry].status = 0x8300;
-
-	lp->cur_tx++;
-
-	/* Trigger an immediate send poll. */
-	outw(0x0000, ioaddr+LANCE_ADDR);
-	outw(0x0048, ioaddr+LANCE_DATA);
-
-	dev->trans_start = jiffies;
-
-	save_flags(flags);
-	cli();
-	lp->lock = 0;
-	if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
-		dev->tbusy=0;
-	else
-		lp->tx_full = 1;
-	restore_flags(flags);
-
-	return 0;
-}
-
-/* The LANCE32 interrupt handler. */
-static void
-lance32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
-{
-	struct device *dev = (struct device *)dev_id;
-	struct lance32_private *lp;
-	int csr0, ioaddr, boguscnt=10;
-	int must_restart;
-
-	if (dev == NULL) {
-		printk ("lance32_interrupt(): irq %d for unknown device.\n", irq);
-		return;
-	}
-
-	ioaddr = dev->base_addr;
-	lp = (struct lance32_private *)dev->priv;
-	if (dev->interrupt)
-		printk("%s: Re-entering the interrupt handler.\n", dev->name);
-
-	dev->interrupt = 1;
-
-	outw(0x00, dev->base_addr + LANCE_ADDR);
-	while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600
-		   && --boguscnt >= 0) {
-		/* Acknowledge all of the current interrupt sources ASAP. */
-		outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA);
-
-		must_restart = 0;
-
-		if (lance32_debug > 5)
-			printk("%s: interrupt  csr0=%#2.2x new csr=%#2.2x.\n",
-				   dev->name, csr0, inw(dev->base_addr + LANCE_DATA));
-
-		if (csr0 & 0x0400)			/* Rx interrupt */
-			lance32_rx(dev);
-
-		if (csr0 & 0x0200) {		/* Tx-done interrupt */
-			int dirty_tx = lp->dirty_tx;
-
-			while (dirty_tx < lp->cur_tx) {
-				int entry = dirty_tx & TX_RING_MOD_MASK;
-				int status = lp->tx_ring[entry].status;
-			
-				if (status < 0)
-					break;			/* It still hasn't been Txed */
-
-				lp->tx_ring[entry].base = 0;
-
-				if (status & 0x4000) {
-					/* There was an major error, log it. */
-					int err_status = lp->tx_ring[entry].misc;
-					lp->stats.tx_errors++;
-					if (err_status & 0x04000000) lp->stats.tx_aborted_errors++;
-					if (err_status & 0x08000000) lp->stats.tx_carrier_errors++;
-					if (err_status & 0x10000000) lp->stats.tx_window_errors++;
-					if (err_status & 0x40000000) {
-						/* Ackk!  On FIFO errors the Tx unit is turned off! */
-						lp->stats.tx_fifo_errors++;
-						/* Remove this verbosity later! */
-						printk("%s: Tx FIFO error! Status %4.4x.\n",
-							   dev->name, csr0);
-						/* Restart the chip. */
-						must_restart = 1;
-					}
-				} else {
-					if (status & 0x1800)
-						lp->stats.collisions++;
-					lp->stats.tx_packets++;
-				}
-
-				/* We must free the original skb */
-				if (lp->tx_skbuff[entry]) {
-					dev_kfree_skb(lp->tx_skbuff[entry],FREE_WRITE);
-					lp->tx_skbuff[entry] = 0;
-				}
-				dirty_tx++;
-			}
-
-#ifndef final_version
-			if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
-				printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
-					   dirty_tx, lp->cur_tx, lp->tx_full);
-				dirty_tx += TX_RING_SIZE;
-			}
-#endif
-
-			if (lp->tx_full && dev->tbusy
-				&& dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
-				/* The ring is no longer full, clear tbusy. */
-				lp->tx_full = 0;
-				dev->tbusy = 0;
-				mark_bh(NET_BH);
-			}
-
-			lp->dirty_tx = dirty_tx;
-		}
-
-		/* Log misc errors. */
-		if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */
-		if (csr0 & 0x1000) lp->stats.rx_errors++; /* Missed a Rx frame. */
-		if (csr0 & 0x0800) {
-			printk("%s: Bus master arbitration failure, status %4.4x.\n",
-				   dev->name, csr0);
-			/* Restart the chip. */
-			must_restart = 1;
-		}
-
-		if (must_restart) {
-			/* stop the chip to clear the error condition, then restart */
-			outw(0x0000, dev->base_addr + LANCE_ADDR);
-			outw(0x0004, dev->base_addr + LANCE_DATA);
-			lance32_restart(dev, 0x0002, 0);
-		}
-	}
-
-    /* Clear any other interrupt, and set interrupt enable. */
-    outw(0x0000, dev->base_addr + LANCE_ADDR);
-    outw(0x7940, dev->base_addr + LANCE_DATA);
-
-	if (lance32_debug > 4)
-		printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
-			   dev->name, inw(ioaddr + LANCE_ADDR),
-			   inw(dev->base_addr + LANCE_DATA));
-
-	dev->interrupt = 0;
-	return;
-}
-
-static int
-lance32_rx(struct device *dev)
-{
-	struct lance32_private *lp = (struct lance32_private *)dev->priv;
-	int entry = lp->cur_rx & RX_RING_MOD_MASK;
-	int i;
-		
-	/* If we own the next entry, it's a new packet. Send it up. */
-	while (lp->rx_ring[entry].status >= 0) {
-		int status = lp->rx_ring[entry].status >> 8;
-
-		if (status != 0x03) {			/* There was an error. */
-			/* There is a tricky error noted by John Murphy,
-			   <murf@perftech.com> to Russ Nelson: Even with full-sized
-			   buffers it's possible for a jabber packet to use two
-			   buffers, with only the last correctly noting the error. */
-			if (status & 0x01)	/* Only count a general error at the */
-				lp->stats.rx_errors++; /* end of a packet.*/
-			if (status & 0x20) lp->stats.rx_frame_errors++;
-			if (status & 0x10) lp->stats.rx_over_errors++;
-			if (status & 0x08) lp->stats.rx_crc_errors++;
-			if (status & 0x04) lp->stats.rx_fifo_errors++;
-			lp->rx_ring[entry].status &= 0x03ff;
-		}
-		else 
-		{
-			/* Malloc up new buffer, compatible with net-2e. */
-			short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4;
-			struct sk_buff *skb;
-			
-			if(pkt_len<60)
-			{
-				printk("%s: Runt packet!\n",dev->name);
-				lp->stats.rx_errors++;
-			}
-			else
-			{
-				skb = dev_alloc_skb(pkt_len+2);
-				if (skb == NULL) 
-				{
-					printk("%s: Memory squeeze, deferring packet.\n", dev->name);
-					for (i=0; i < RX_RING_SIZE; i++)
-						if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status < 0)
-							break;
-
-					if (i > RX_RING_SIZE -2) 
-					{
-						lp->stats.rx_dropped++;
-						lp->rx_ring[entry].status |= 0x8000;
-						lp->cur_rx++;
-					}
-					break;
-				}
-				skb->dev = dev;
-				skb_reserve(skb,2);	/* 16 byte align */
-				skb_put(skb,pkt_len);	/* Make room */
-				eth_copy_and_sum(skb,
-					(unsigned char *)bus_to_virt(lp->rx_ring[entry].base),
-					pkt_len,0);
-				skb->protocol=eth_type_trans(skb,dev);
-				netif_rx(skb);
-				lp->stats.rx_packets++;
-			}
-		}
-		/* The docs say that the buffer length isn't touched, but Andrew Boyd
-		   of QNX reports that some revs of the 79C965 clear it. */
-		lp->rx_ring[entry].buf_length = -PKT_BUF_SZ;
-		lp->rx_ring[entry].status |= 0x8000;
-		entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
-	}
-
-	/* We should check that at least two ring entries are free.	 If not,
-	   we should free one and mark stats->rx_dropped++. */
-
-	return 0;
-}
-
-static int
-lance32_close(struct device *dev)
-{
-	int ioaddr = dev->base_addr;
-	struct lance32_private *lp = (struct lance32_private *)dev->priv;
-
-	dev->start = 0;
-	dev->tbusy = 1;
-
-	outw(112, ioaddr+LANCE_ADDR);
-	lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
-
-	outw(0, ioaddr+LANCE_ADDR);
-
-	if (lance32_debug > 1)
-		printk("%s: Shutting down ethercard, status was %2.2x.\n",
-			   dev->name, inw(ioaddr+LANCE_DATA));
-
-	/* We stop the LANCE here -- it occasionally polls
-	   memory if we don't. */
-	outw(0x0004, ioaddr+LANCE_DATA);
-
-	if (dev->dma != 4)
-		disable_dma(dev->dma);
-
-	free_irq(dev->irq, dev);
-
-	irq2dev_map[dev->irq] = 0;
-
-	return 0;
-}
-
-static struct net_device_stats *lance32_get_stats(struct device *dev)
-{
-	struct lance32_private *lp = (struct lance32_private *)dev->priv;
-	int ioaddr = dev->base_addr;
-	unsigned short saved_addr;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	saved_addr = inw(ioaddr+LANCE_ADDR);
-	outw(112, ioaddr+LANCE_ADDR);
-	lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
-	outw(saved_addr, ioaddr+LANCE_ADDR);
-	restore_flags(flags);
-
-	return &lp->stats;
-}
-
-/* Set or clear the multicast filter for this adaptor.
- */
-
-static void lance32_set_multicast_list(struct device *dev)
-{
-	int ioaddr = dev->base_addr;
-	struct lance32_private *lp = (struct lance32_private *)dev->priv;    
-
-	if (dev->flags&IFF_PROMISC) {
-		/* Log any net taps. */
-		printk("%s: Promiscuous mode enabled.\n", dev->name);
-	        lp->init_block.mode = 0x8000;
-	} else {
-		int num_addrs=dev->mc_count;
-		if(dev->flags&IFF_ALLMULTI)
-			num_addrs=1;
-		/* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */
-		memset(lp->init_block.filter , (num_addrs == 0) ? 0 : -1, sizeof(lp->init_block.filter));
-	        lp->init_block.mode = 0x0000;	        
-	}
-    
-	outw(0, ioaddr+LANCE_ADDR);
-	outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance.	 */
-
-	lance32_restart(dev, 0x0042, 0); /*  Resume normal operation */
-
-}
-
-
-/*
- * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c lance32.c"
- *  c-indent-level: 4
- *  tab-width: 4
- * End:
- */
diff -u --recursive --new-file v2.1.29/linux/drivers/net/lapbether.c linux/drivers/net/lapbether.c
--- v2.1.29/linux/drivers/net/lapbether.c	Sun Feb  2 05:18:40 1997
+++ linux/drivers/net/lapbether.c	Thu Mar 20 17:11:51 1997
@@ -280,7 +280,7 @@
 
 	dev->hard_header(skb, dev, ETH_P_DEC, bcast_addr, NULL, 0);
 
-	dev->hard_start_xmit(skb, dev);
+	dev_queue_xmit(skb);
 }
 
 static void lapbeth_connected(void *token, int reason)
diff -u --recursive --new-file v2.1.29/linux/drivers/net/ltpc.c linux/drivers/net/ltpc.c
--- v2.1.29/linux/drivers/net/ltpc.c	Thu Feb 27 10:57:30 1997
+++ linux/drivers/net/ltpc.c	Thu Mar 20 17:11:51 1997
@@ -316,9 +316,9 @@
 	int i;
 	int timeout;
 
-	/* ten second or so total */
+	/* twenty second or so total */
 
-	for(i=0;i<10000;i++) {
+	for(i=0;i<20000;i++) {
 		if ( c != inb_p(dev->base_addr+6) ) return 0;
 		for(timeout=loops_per_sec/1000; timeout > 0; timeout--) ;
 	}
diff -u --recursive --new-file v2.1.29/linux/drivers/net/mkiss.c linux/drivers/net/mkiss.c
--- v2.1.29/linux/drivers/net/mkiss.c	Sun Feb  2 05:18:40 1997
+++ linux/drivers/net/mkiss.c	Thu Mar 20 17:11:51 1997
@@ -833,6 +833,7 @@
 	/* Fill in our line protocol discipline, and register it */
 	memset(&ax_ldisc, 0, sizeof(ax_ldisc));
 	ax_ldisc.magic  = TTY_LDISC_MAGIC;
+	ax_ldisc.name   = "mkiss";
 	ax_ldisc.flags  = 0;
 	ax_ldisc.open   = ax25_open;
 	ax_ldisc.close  = ax25_close;
diff -u --recursive --new-file v2.1.29/linux/drivers/net/myri_sbus.c linux/drivers/net/myri_sbus.c
--- v2.1.29/linux/drivers/net/myri_sbus.c	Mon Mar 17 14:54:25 1997
+++ linux/drivers/net/myri_sbus.c	Thu Mar 20 17:11:52 1997
@@ -1,4 +1,4 @@
-/* myri_sbus.h: MyriCOM Gigabit Ethernet SBUS card driver.
+/* myri_sbus.h: MyriCOM MyriNET SBUS card driver.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  */
@@ -43,11 +43,6 @@
 #include <net/sock.h>
 #include <net/ipv6.h>
 
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-#include <linux/in6.h>
-#include <net/ndisc.h>
-#endif
-
 #include <asm/checksum.h>
 
 #include "myri_sbus.h"
@@ -707,6 +702,7 @@
 	unsigned char *pad = (unsigned char *)skb->data;
 	struct ethhdr *eth = (struct ethhdr *)(pad + MYRI_PAD_LEN);
 	struct device *dev = skb->dev;
+ 	struct neighbour *neigh = NULL;
 
 #ifdef DEBUG_HEADER
 	DHDR(("myri_rebuild_header: pad[%02x,%02x] ", pad[0], pad[1]));
@@ -721,29 +717,19 @@
 	 *	Only ARP/IP and NDISC/IPv6 are currently supported
 	 */
 	
+ 	if (skb->dst)
+ 		neigh = skb->dst->neighbour;
+ 
+ 	if (neigh)
+ 		return neigh->ops->resolve(eth->h_dest, skb);
+ 
 	switch (eth->h_proto)
 	{
 #ifdef CONFIG_INET
 	case __constant_htons(ETH_P_IP):
-
-		/*
-		 *	Try to get ARP to resolve the header.
-		 */
-
-		return arp_find(eth->h_dest, skb) ? 1 : 0;
-		break;
+ 		return arp_find(eth->h_dest, skb);
 #endif
 
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-	case __constant_htons(ETH_P_IPV6):
-#ifdef CONFIG_IPV6
-		return (ndisc_eth_resolv(eth->h_dest, dev, skb));
-#else
-		if (ndisc_eth_hook)
-			return (ndisc_eth_hook(eth->h_dest, dev, skb));
-#endif
-		break;
-#endif	
 	default:
 		printk(KERN_DEBUG 
 		       "%s: unable to resolve type %X addresses.\n", 
@@ -757,7 +743,7 @@
 	return 0;	
 }
 
-int myri_header_cache(struct dst_entry *dst, struct dst_entry *neigh,
+int myri_header_cache(struct dst_entry *dst, struct neighbour *neigh,
 		       struct hh_cache *hh)
 {
 	unsigned short type = hh->hh_type;
@@ -920,7 +906,7 @@
 	if(version_printed++ == 0)
 		printk(version);
 
-	printk("%s: MyriCOM Gigabit Ethernet ", dev->name);
+	printk("%s: MyriCOM MyriNET Ethernet ", dev->name);
 	dev->base_addr = (long) sdev;
 
 	mp = (struct myri_eth *) dev->priv;
@@ -1112,6 +1098,7 @@
 	myri_load_lanai(mp);
 
 #ifdef MODULE
+	dev->ifindex = dev_new_index();
 	mp->next_module = root_myri_dev;
 	root_myri_dev = mp;
 #endif
@@ -1135,7 +1122,7 @@
 			if(!strcmp(sdev->prom_name, "MYRICOM,mlanai") ||
 			   !strcmp(sdev->prom_name, "myri")) {
 				cards++;
-				DET(("Found myricom gigabit as %s\n", sdev->prom_name));
+				DET(("Found myricom myrinet as %s\n", sdev->prom_name));
 				if((v = myri_ether_init(dev, sdev, (cards - 1))))
 					return v;
 			}
diff -u --recursive --new-file v2.1.29/linux/drivers/net/myri_sbus.h linux/drivers/net/myri_sbus.h
--- v2.1.29/linux/drivers/net/myri_sbus.h	Sun Feb  2 05:18:40 1997
+++ linux/drivers/net/myri_sbus.h	Thu Mar 20 17:11:52 1997
@@ -1,4 +1,4 @@
-/* myri_sbus.h: Defines for MyriCOM Gigabit Ethernet SBUS card driver.
+/* myri_sbus.h: Defines for MyriCOM MyriNET SBUS card driver.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  */
diff -u --recursive --new-file v2.1.29/linux/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c
--- v2.1.29/linux/drivers/net/pcnet32.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/pcnet32.c	Wed Mar 12 15:10:14 1997
@@ -0,0 +1,931 @@
+/* pcnet32.c: An AMD PCnet32 ethernet driver for linux. */
+/*
+ *      Copyright 1996,97 Thomas Bogendoerfer
+ * 
+ * 	Derived from the lance driver written 1993,1994,1995 by Donald Becker.
+ * 
+ * 	Copyright 1993 United States Government as represented by the
+ * 	Director, National Security Agency.
+ * 
+ * 	This software may be used and distributed according to the terms
+ * 	of the GNU Public License, incorporated herein by reference.
+ *
+ * 	This driver is for PCnet32 and PCnetPCI based ethercards
+ */
+
+static const char *version = "pcnet32.c:v0.23 8.2.97 tsbogend@alpha.franken.de\n";
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+static unsigned int pcnet32_portlist[] = {0x300, 0x320, 0x340, 0x360, 0};
+
+#ifdef PCNET32_DEBUG
+int pcnet32_debug = PCNET32_DEBUG;
+#else
+int pcnet32_debug = 1;
+#endif
+
+/*
+ * 				Theory of Operation
+ * 
+ * This driver uses the same software structure as the normal lance
+ * driver. So look for a verbose description in lance.c. The differences
+ * to the normal lance driver is the use of the 32bit mode of PCnet32
+ * and PCnetPCI chips. Because these chips are 32bit chips, there is no
+ * 16MB limitation and we don't need bounce buffers.
+ */
+ 
+/*
+ * History:
+ * v0.01:  Initial version
+ *         only tested on Alpha Noname Board
+ * v0.02:  changed IRQ handling for new interrupt scheme (dev_id)
+ *         tested on a ASUS SP3G
+ * v0.10:  fixed an odd problem with the 79C794 in a Compaq Deskpro XL
+ *         looks like the 974 doesn't like stopping and restarting in a
+ *         short period of time; now we do a reinit of the lance; the
+ *         bug was triggered by doing ifconfig eth0 <ip> broadcast <addr>
+ *         and hangs the machine (thanks to Klaus Liedl for debugging)
+ * v0.12:  by suggestion from Donald Becker: Renamed driver to pcnet32,
+ *         made it standalone (no need for lance.c)
+ * v0.13:  added additional PCI detecting for special PCI devices (Compaq)
+ * v0.14:  stripped down additional PCI probe (thanks to David C Niemi
+ *         and sveneric@xs4all.nl for testing this on their Compaq boxes)
+ * v0.15:  added 79C965 (VLB) probe
+ *         added interrupt sharing for PCI chips
+ * v0.16:  fixed set_multicast_list on Alpha machines
+ * v0.17:  removed hack from dev.c; now pcnet32 uses ethif_probe in Space.c
+ * v0.19:  changed setting of autoselect bit
+ * v0.20:  removed additional Compaq PCI probe; there is now a working one
+ *	   in arch/i386/bios32.c
+ * v0.21:  added endian conversion for ppc, from work by cort@cs.nmt.edu
+ * v0.22:  added printing of status to ring dump
+ * v0.23:  changed enet_statistics to net_devive_stats
+ */
+
+
+/*
+ * Set the number of Tx and Rx buffers, using Log_2(# buffers).
+ * Reasonable default values are 4 Tx buffers, and 16 Rx buffers.
+ * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4).
+ */
+#ifndef PCNET32_LOG_TX_BUFFERS
+#define PCNET32_LOG_TX_BUFFERS 4
+#define PCNET32_LOG_RX_BUFFERS 4
+#endif
+
+#define TX_RING_SIZE			(1 << (PCNET32_LOG_TX_BUFFERS))
+#define TX_RING_MOD_MASK		(TX_RING_SIZE - 1)
+#define TX_RING_LEN_BITS		((PCNET32_LOG_TX_BUFFERS) << 12)
+
+#define RX_RING_SIZE			(1 << (PCNET32_LOG_RX_BUFFERS))
+#define RX_RING_MOD_MASK		(RX_RING_SIZE - 1)
+#define RX_RING_LEN_BITS		((PCNET32_LOG_RX_BUFFERS) << 4)
+
+#define PKT_BUF_SZ		1544
+
+/* Offsets from base I/O address. */
+#define PCNET32_DATA 0x10
+#define PCNET32_ADDR 0x12
+#define PCNET32_RESET 0x14
+#define PCNET32_BUS_IF 0x16
+#define PCNET32_TOTAL_SIZE 0x18
+
+/* The PCNET32 Rx and Tx ring descriptors. */
+struct pcnet32_rx_head {
+	u32 base;
+	s16 buf_length;
+        s16 status;    
+	u32 msg_length;
+	u32 reserved;
+};
+	
+struct pcnet32_tx_head {
+	u32 base;
+	s16 length;
+        s16 status;
+	u32 misc;
+	u32 reserved;
+};
+
+
+/* The PCNET32 32-Bit initialization block, described in databook. */
+struct pcnet32_init_block {
+	u16 mode;
+	u16 tlen_rlen;
+	u8  phys_addr[6];
+	u16 reserved;
+	u32 filter[2];
+	/* Receive and transmit ring base, along with extra bits. */    
+	u32 rx_ring;
+	u32 tx_ring;
+};
+
+struct pcnet32_private {
+	/* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */
+	struct pcnet32_rx_head   rx_ring[RX_RING_SIZE];
+	struct pcnet32_tx_head   tx_ring[TX_RING_SIZE];
+	struct pcnet32_init_block	init_block;
+	const char *name;
+	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
+	struct sk_buff* tx_skbuff[TX_RING_SIZE];
+	unsigned long rx_buffs;			/* Address of Rx and Tx buffers. */
+	int cur_rx, cur_tx;			/* The next free ring entry */
+	int dirty_rx, dirty_tx;		        /* The ring entries to be free()ed. */
+	struct net_device_stats stats;
+	char tx_full;
+	unsigned long lock;
+        char shared_irq;                         /* shared irq possible */
+};
+
+int  pcnet32_probe(struct device *dev);
+static int  pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line, int shared);
+static int  pcnet32_open(struct device *dev);
+static void pcnet32_init_ring(struct device *dev);
+static int  pcnet32_start_xmit(struct sk_buff *skb, struct device *dev);
+static int  pcnet32_rx(struct device *dev);
+static void pcnet32_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int  pcnet32_close(struct device *dev);
+static struct net_device_stats *pcnet32_get_stats(struct device *dev);
+static void pcnet32_set_multicast_list(struct device *dev);
+
+
+
+int pcnet32_probe (struct device *dev)
+{
+    unsigned int  ioaddr = dev ? dev->base_addr: 0;
+    unsigned char irq_line = dev ? dev->irq : 0;
+    int *port;
+    
+    if (ioaddr > 0x1ff)
+      return pcnet32_probe1(dev, ioaddr, irq_line, 0);
+    else if(ioaddr != 0)
+      return ENXIO;
+    
+#if defined(CONFIG_PCI)
+    if (pcibios_present()) {
+	int pci_index;
+	
+	printk("pcnet32.c: PCI bios is present, checking for devices...\n");
+	for (pci_index = 0; pci_index < 8; pci_index++) {
+	    unsigned char pci_bus, pci_device_fn;
+	    unsigned short pci_command;
+
+	    if (pcibios_find_device (PCI_VENDOR_ID_AMD,
+				     PCI_DEVICE_ID_AMD_LANCE, pci_index,
+				     &pci_bus, &pci_device_fn) != 0)
+	      break;
+	    pcibios_read_config_byte(pci_bus, pci_device_fn,
+				     PCI_INTERRUPT_LINE, &irq_line);
+	    pcibios_read_config_dword(pci_bus, pci_device_fn,
+				      PCI_BASE_ADDRESS_0, &ioaddr);
+	    /* Remove I/O space marker in bit 0. */
+	    ioaddr &= ~3;
+	    /* PCI Spec 2.1 states that it is either the driver or PCI card's
+	     * responsibility to set the PCI Master Enable Bit if needed.
+	     *	(From Mark Stockton <marks@schooner.sys.hou.compaq.com>)
+	     */
+	    pcibios_read_config_word(pci_bus, pci_device_fn,
+				     PCI_COMMAND, &pci_command);
+	    
+	    /* Avoid already found cards from previous pcnet32_probe() calls */
+	    if (check_region(ioaddr, PCNET32_TOTAL_SIZE))
+	      continue;
+
+	    if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+		printk("PCI Master Bit has not been set. Setting...\n");
+		pci_command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO;
+		pcibios_write_config_word(pci_bus, pci_device_fn,
+					  PCI_COMMAND, pci_command);
+	    }
+#ifdef __powerpc__
+	    irq_line = 15;
+#endif
+	    
+	    printk("Found PCnet/PCI at %#x, irq %d.\n",
+		   ioaddr, irq_line);
+	    
+	    if (pcnet32_probe1(dev, ioaddr, irq_line, 1) != 0) {	/* Shouldn't happen. */
+		printk(KERN_ERR "pcnet32.c: Probe of PCI card at %#x failed.\n", ioaddr);
+		break;
+	    }
+	    return 0;
+	}
+    } else 
+#endif  /* defined(CONFIG_PCI) */
+    
+    /* now look for PCnet32 VLB cards */
+    for (port = pcnet32_portlist; *port; port++) {
+	unsigned int ioaddr = *port;
+	
+	if ( check_region(ioaddr, PCNET32_TOTAL_SIZE) == 0) {
+	    if (pcnet32_probe1(dev, ioaddr, 0, 0) == 0)
+	      return 0;
+	}
+    }
+    
+    return ENODEV;
+}
+
+
+/* pcnet32_probe1 */
+int pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line, int shared)
+{
+    struct pcnet32_private *lp;
+    int i;
+    char *chipname;
+
+    /* check if there is really a pcnet chip on that ioaddr */
+    if ((inb(ioaddr + 14) != 0x57) || (inb(ioaddr + 15) != 0x57))
+      return ENODEV; 
+    
+    inw(ioaddr+PCNET32_RESET); /* Reset the PCNET32 */
+
+    outw(0x0000, ioaddr+PCNET32_ADDR); /* Switch to window 0 */
+    if (inw(ioaddr+PCNET32_DATA) != 0x0004)
+      return ENODEV;
+
+    /* Get the version of the chip. */
+    outw(88, ioaddr+PCNET32_ADDR);
+    if (inw(ioaddr+PCNET32_ADDR) != 88) {
+	/* should never happen */
+	return ENODEV;
+    } else {			/* Good, it's a newer chip. */
+	int chip_version = inw(ioaddr+PCNET32_DATA);
+	outw(89, ioaddr+PCNET32_ADDR);
+	chip_version |= inw(ioaddr+PCNET32_DATA) << 16;
+	if (pcnet32_debug > 2)
+	  printk("  PCnet chip version is %#x.\n", chip_version);
+	if ((chip_version & 0xfff) != 0x003)
+	  return ENODEV;
+	chip_version = (chip_version >> 12) & 0xffff;
+	switch (chip_version) {
+	 case 0x2420:
+	    chipname = "PCnet/PCI 79C970";
+	    break;
+	 case 0x2430:
+	    chipname = "PCnet32";
+	    break;
+	 case 0x2621:
+	    chipname = "PCnet/PCI II 79C970A";
+	    break;
+	 default:
+	    printk("pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version);
+	    return ENODEV;
+	}
+    }
+    
+    /* We should have a "dev" from Space.c or the static module table. */
+    if (dev == NULL) {
+	printk(KERN_ERR "pcnet32.c: Passed a NULL device.\n");
+	dev = init_etherdev(0, 0);
+    }
+
+    printk("%s: %s at %#3x,", dev->name, chipname, ioaddr);
+
+    /* There is a 16 byte station address PROM at the base address.
+     The first six bytes are the station address. */
+    for (i = 0; i < 6; i++)
+      printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
+
+    dev->base_addr = ioaddr;
+    request_region(ioaddr, PCNET32_TOTAL_SIZE, chipname);
+    
+    /* Make certain the data structures used by the PCnet32 are 16byte aligned and DMAble. */
+    lp = (struct pcnet32_private *) (((unsigned long)kmalloc(sizeof(*lp)+15, GFP_DMA | GFP_KERNEL)+15) & ~15);
+      
+    memset(lp, 0, sizeof(*lp));
+    dev->priv = lp;
+    lp->name = chipname;
+    lp->shared_irq = shared;
+    lp->rx_buffs = (unsigned long) kmalloc(PKT_BUF_SZ*RX_RING_SIZE, GFP_DMA | GFP_KERNEL);
+
+    lp->init_block.mode = le16_to_cpu(0x0003);	/* Disable Rx and Tx. */
+    lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); 
+    for (i = 0; i < 6; i++)
+      lp->init_block.phys_addr[i] = dev->dev_addr[i];
+    lp->init_block.filter[0] = 0x00000000;
+    lp->init_block.filter[1] = 0x00000000;
+    lp->init_block.rx_ring = (u32)le32_to_cpu(virt_to_bus(lp->rx_ring));
+    lp->init_block.tx_ring = (u32)le32_to_cpu(virt_to_bus(lp->tx_ring));
+    
+    /* switch pcnet32 to 32bit mode */
+    outw(0x0014, ioaddr+PCNET32_ADDR);
+    outw(0x0002, ioaddr+PCNET32_BUS_IF);
+
+    outw(0x0001, ioaddr+PCNET32_ADDR);
+    inw(ioaddr+PCNET32_ADDR);
+    outw(virt_to_bus(&lp->init_block) & 0xffff, ioaddr+PCNET32_DATA);
+    outw(0x0002, ioaddr+PCNET32_ADDR);
+    inw(ioaddr+PCNET32_ADDR);
+    outw(virt_to_bus(&lp->init_block) >> 16, ioaddr+PCNET32_DATA);
+    outw(0x0000, ioaddr+PCNET32_ADDR);
+    inw(ioaddr+PCNET32_ADDR);
+    
+    if (irq_line) {
+	dev->irq = irq_line;
+    }
+    
+    if (dev->irq >= 2)
+      printk(" assigned IRQ %d.\n", dev->irq);
+    else {
+	/*
+	 * To auto-IRQ we enable the initialization-done and DMA error
+	 * interrupts. For ISA boards we get a DMA error, but VLB and PCI
+	 * boards will work.
+	 */
+	autoirq_setup(0);
+	
+	/* Trigger an initialization just for the interrupt. */
+	outw(0x0041, ioaddr+PCNET32_DATA);
+
+	dev->irq = autoirq_report(1);
+	if (dev->irq)
+	  printk(", probed IRQ %d.\n", dev->irq);
+	else {
+	    printk(", failed to detect IRQ line.\n");
+	    return ENODEV;
+	}
+    }
+
+    outw(0x0002, ioaddr+PCNET32_ADDR);
+    /* only touch autoselect bit */
+    outw(inw(ioaddr+PCNET32_BUS_IF) | 0x0002, ioaddr+PCNET32_BUS_IF);
+
+
+    if (pcnet32_debug > 0)
+      printk(version);
+    
+    /* The PCNET32-specific entries in the device structure. */
+    dev->open = &pcnet32_open;
+    dev->hard_start_xmit = &pcnet32_start_xmit;
+    dev->stop = &pcnet32_close;
+    dev->get_stats = &pcnet32_get_stats;
+    dev->set_multicast_list = &pcnet32_set_multicast_list;
+
+    /* Fill in the generic fields of the device structure. */
+    ether_setup(dev);
+    return 0;
+}
+
+
+static int
+pcnet32_open(struct device *dev)
+{
+	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+	unsigned int ioaddr = dev->base_addr;
+	int i;
+
+	if (dev->irq == 0 ||
+		request_irq(dev->irq, &pcnet32_interrupt,
+			    lp->shared_irq ? SA_SHIRQ : 0, lp->name, (void *)dev)) {
+		return -EAGAIN;
+	}
+
+	/* Reset the PCNET32 */
+	inw(ioaddr+PCNET32_RESET);
+
+	/* switch pcnet32 to 32bit mode */
+	outw(0x0014, ioaddr+PCNET32_ADDR);
+	outw(0x0002, ioaddr+PCNET32_BUS_IF);
+
+	/* Turn on auto-select of media (AUI, BNC). */
+	outw(0x0002, ioaddr+PCNET32_ADDR);
+	/* only touch autoselect bit */
+	outw(inw(ioaddr+PCNET32_BUS_IF) | 0x0002, ioaddr+PCNET32_BUS_IF);
+
+	if (pcnet32_debug > 1)
+		printk("%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n",
+			   dev->name, dev->irq,
+		           (u32) virt_to_bus(lp->tx_ring),
+		           (u32) virt_to_bus(lp->rx_ring),
+			   (u32) virt_to_bus(&lp->init_block));
+
+	lp->init_block.mode = 0x0000;
+	lp->init_block.filter[0] = 0x00000000;
+	lp->init_block.filter[1] = 0x00000000;
+	pcnet32_init_ring(dev);
+    
+	/* Re-initialize the PCNET32, and start it when done. */
+	outw(0x0001, ioaddr+PCNET32_ADDR);
+	outw(virt_to_bus(&lp->init_block) &0xffff, ioaddr+PCNET32_DATA);
+	outw(0x0002, ioaddr+PCNET32_ADDR);
+	outw(virt_to_bus(&lp->init_block) >> 16, ioaddr+PCNET32_DATA);
+
+	outw(0x0004, ioaddr+PCNET32_ADDR);
+	outw(0x0915, ioaddr+PCNET32_DATA);
+
+	outw(0x0000, ioaddr+PCNET32_ADDR);
+	outw(0x0001, ioaddr+PCNET32_DATA);
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+	i = 0;
+	while (i++ < 100)
+		if (inw(ioaddr+PCNET32_DATA) & 0x0100)
+			break;
+	/* 
+	 * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
+	 * reports that doing so triggers a bug in the '974.
+	 */
+ 	outw(0x0042, ioaddr+PCNET32_DATA);
+
+	if (pcnet32_debug > 2)
+		printk("%s: PCNET32 open after %d ticks, init block %#x csr0 %4.4x.\n",
+			   dev->name, i, (u32) virt_to_bus(&lp->init_block), inw(ioaddr+PCNET32_DATA));
+
+	return 0;					/* Always succeed */
+}
+
+/*
+ * The LANCE has been halted for one reason or another (busmaster memory
+ * arbitration error, Tx FIFO underflow, driver stopped it to reconfigure,
+ * etc.).  Modern LANCE variants always reload their ring-buffer
+ * configuration when restarted, so we must reinitialize our ring
+ * context before restarting.  As part of this reinitialization,
+ * find all packets still on the Tx ring and pretend that they had been
+ * sent (in effect, drop the packets on the floor) - the higher-level
+ * protocols will time out and retransmit.  It'd be better to shuffle
+ * these skbs to a temp list and then actually re-Tx them after
+ * restarting the chip, but I'm too lazy to do so right now.  dplatt@3do.com
+ */
+
+static void 
+pcnet32_purge_tx_ring(struct device *dev)
+{
+	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+	int i;
+
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		if (lp->tx_skbuff[i]) {
+			dev_kfree_skb(lp->tx_skbuff[i],FREE_WRITE);
+			lp->tx_skbuff[i] = NULL;
+		}
+	}
+}
+
+
+/* Initialize the PCNET32 Rx and Tx rings. */
+static void
+pcnet32_init_ring(struct device *dev)
+{
+	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+	int i;
+
+	lp->lock = 0, lp->tx_full = 0;
+	lp->cur_rx = lp->cur_tx = 0;
+	lp->dirty_rx = lp->dirty_tx = 0;
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		lp->rx_ring[i].base = (u32)le32_to_cpu(virt_to_bus((char *)lp->rx_buffs + i*PKT_BUF_SZ));
+		lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ);
+	        lp->rx_ring[i].status = le16_to_cpu(0x8000);	    
+	}
+	/* The Tx buffer address is filled in as needed, but we do need to clear
+	   the upper ownership bit. */
+	for (i = 0; i < TX_RING_SIZE; i++) {
+	        lp->tx_ring[i].base = 0;
+	        lp->tx_ring[i].status = 0;
+	}
+
+        lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS;
+	for (i = 0; i < 6; i++)
+		lp->init_block.phys_addr[i] = dev->dev_addr[i];
+	lp->init_block.rx_ring = (u32)le32_to_cpu(virt_to_bus(lp->rx_ring));
+	lp->init_block.tx_ring = (u32)le32_to_cpu(virt_to_bus(lp->tx_ring));
+}
+
+static void
+pcnet32_restart(struct device *dev, unsigned int csr0_bits, int must_reinit)
+{
+        int i;
+	unsigned int ioaddr = dev->base_addr;
+    
+	pcnet32_purge_tx_ring(dev);
+	pcnet32_init_ring(dev);
+    
+	outw(0x0000, ioaddr + PCNET32_ADDR);
+        /* ReInit Ring */
+        outw(0x0001, ioaddr + PCNET32_DATA);
+	i = 0;
+	while (i++ < 100)
+		if (inw(ioaddr+PCNET32_DATA) & 0x0100)
+			break;
+
+	outw(csr0_bits, ioaddr + PCNET32_DATA);
+}
+
+static int
+pcnet32_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+	unsigned int ioaddr = dev->base_addr;
+	int entry;
+	unsigned long flags;
+
+	/* Transmitter timeout, serious problems. */
+	if (dev->tbusy) {
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 20)
+			return 1;
+		outw(0, ioaddr+PCNET32_ADDR);
+		printk("%s: transmit timed out, status %4.4x, resetting.\n",
+			   dev->name, inw(ioaddr+PCNET32_DATA));
+		outw(0x0004, ioaddr+PCNET32_DATA);
+		lp->stats.tx_errors++;
+#ifndef final_version
+		{
+			int i;
+			printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
+				   lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
+				   lp->cur_rx);
+			for (i = 0 ; i < RX_RING_SIZE; i++)
+				printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
+					   lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
+					   lp->rx_ring[i].msg_length, (unsigned)lp->rx_ring[i].status);
+			for (i = 0 ; i < TX_RING_SIZE; i++)
+				printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
+					   lp->tx_ring[i].base, -lp->tx_ring[i].length,
+					   lp->tx_ring[i].misc, (unsigned)lp->tx_ring[i].status);
+			printk("\n");
+		}
+#endif
+		pcnet32_restart(dev, 0x0042, 1);
+
+		dev->tbusy=0;
+		dev->trans_start = jiffies;
+
+		return 0;
+	}
+
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+
+	if (skb->len <= 0)
+		return 0;
+
+	if (pcnet32_debug > 3) {
+		outw(0x0000, ioaddr+PCNET32_ADDR);
+		printk("%s: pcnet32_start_xmit() called, csr0 %4.4x.\n", dev->name,
+			   inw(ioaddr+PCNET32_DATA));
+		outw(0x0000, ioaddr+PCNET32_DATA);
+	}
+
+	/* Block a timer-based transmit from overlapping.  This could better be
+	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+	if (set_bit(0, (void*)&dev->tbusy) != 0) {
+		printk("%s: Transmitter access conflict.\n", dev->name);
+		return 1;
+	}
+
+	if (set_bit(0, (void*)&lp->lock) != 0) {
+		if (pcnet32_debug > 0)
+			printk("%s: tx queue lock!.\n", dev->name);
+		/* don't clear dev->tbusy flag. */
+		return 1;
+	}
+
+	/* Fill in a Tx ring entry */
+
+	/* Mask to ring buffer boundary. */
+	entry = lp->cur_tx & TX_RING_MOD_MASK;
+
+	/* Caution: the write order is important here, set the base address
+	   with the "ownership" bits last. */
+
+	lp->tx_ring[entry].length = le16_to_cpu(-skb->len);
+
+	lp->tx_ring[entry].misc = 0x00000000;
+
+	lp->tx_skbuff[entry] = skb;
+	lp->tx_ring[entry].base = (u32)le32_to_cpu(virt_to_bus(skb->data));
+        lp->tx_ring[entry].status = le16_to_cpu(0x8300);
+
+	lp->cur_tx++;
+
+	/* Trigger an immediate send poll. */
+	outw(0x0000, ioaddr+PCNET32_ADDR);
+	outw(0x0048, ioaddr+PCNET32_DATA);
+
+	dev->trans_start = jiffies;
+
+	save_flags(flags);
+	cli();
+	lp->lock = 0;
+	if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
+		dev->tbusy=0;
+	else
+		lp->tx_full = 1;
+	restore_flags(flags);
+
+	return 0;
+}
+
+/* The PCNET32 interrupt handler. */
+static void
+pcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct pcnet32_private *lp;
+	unsigned int csr0, ioaddr;
+	int boguscnt=10;
+	int must_restart;
+
+	if (dev == NULL) {
+		printk ("pcnet32_interrupt(): irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	ioaddr = dev->base_addr;
+	lp = (struct pcnet32_private *)dev->priv;
+	if (dev->interrupt)
+		printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+	dev->interrupt = 1;
+
+	outw(0x00, dev->base_addr + PCNET32_ADDR);
+	while ((csr0 = inw(dev->base_addr + PCNET32_DATA)) & 0x8600
+		   && --boguscnt >= 0) {
+		/* Acknowledge all of the current interrupt sources ASAP. */
+		outw(csr0 & ~0x004f, dev->base_addr + PCNET32_DATA);
+
+		must_restart = 0;
+
+		if (pcnet32_debug > 5)
+			printk("%s: interrupt  csr0=%#2.2x new csr=%#2.2x.\n",
+				   dev->name, csr0, inw(dev->base_addr + PCNET32_DATA));
+
+		if (csr0 & 0x0400)			/* Rx interrupt */
+			pcnet32_rx(dev);
+
+		if (csr0 & 0x0200) {		/* Tx-done interrupt */
+			int dirty_tx = lp->dirty_tx;
+
+			while (dirty_tx < lp->cur_tx) {
+				int entry = dirty_tx & TX_RING_MOD_MASK;
+				int status = (short)le16_to_cpu(lp->tx_ring[entry].status);
+			
+				if (status < 0)
+					break;			/* It still hasn't been Txed */
+
+				lp->tx_ring[entry].base = 0;
+
+				if (status & 0x4000) {
+					/* There was an major error, log it. */
+					int err_status = le16_to_cpu(lp->tx_ring[entry].misc);
+					lp->stats.tx_errors++;
+					if (err_status & 0x04000000) lp->stats.tx_aborted_errors++;
+					if (err_status & 0x08000000) lp->stats.tx_carrier_errors++;
+					if (err_status & 0x10000000) lp->stats.tx_window_errors++;
+					if (err_status & 0x40000000) {
+						/* Ackk!  On FIFO errors the Tx unit is turned off! */
+						lp->stats.tx_fifo_errors++;
+						/* Remove this verbosity later! */
+						printk("%s: Tx FIFO error! Status %4.4x.\n",
+							   dev->name, csr0);
+						/* Restart the chip. */
+						must_restart = 1;
+					}
+				} else {
+					if (status & 0x1800)
+						lp->stats.collisions++;
+					lp->stats.tx_packets++;
+				}
+
+				/* We must free the original skb */
+				if (lp->tx_skbuff[entry]) {
+					dev_kfree_skb(lp->tx_skbuff[entry],FREE_WRITE);
+					lp->tx_skbuff[entry] = 0;
+				}
+				dirty_tx++;
+			}
+
+#ifndef final_version
+			if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
+				printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+					   dirty_tx, lp->cur_tx, lp->tx_full);
+				dirty_tx += TX_RING_SIZE;
+			}
+#endif
+
+			if (lp->tx_full && dev->tbusy
+				&& dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
+				/* The ring is no longer full, clear tbusy. */
+				lp->tx_full = 0;
+				dev->tbusy = 0;
+				mark_bh(NET_BH);
+			}
+
+			lp->dirty_tx = dirty_tx;
+		}
+
+		/* Log misc errors. */
+		if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */
+		if (csr0 & 0x1000) lp->stats.rx_errors++; /* Missed a Rx frame. */
+		if (csr0 & 0x0800) {
+			printk("%s: Bus master arbitration failure, status %4.4x.\n",
+				   dev->name, csr0);
+			/* Restart the chip. */
+			must_restart = 1;
+		}
+
+		if (must_restart) {
+			/* stop the chip to clear the error condition, then restart */
+			outw(0x0000, dev->base_addr + PCNET32_ADDR);
+			outw(0x0004, dev->base_addr + PCNET32_DATA);
+			pcnet32_restart(dev, 0x0002, 0);
+		}
+	}
+
+    /* Clear any other interrupt, and set interrupt enable. */
+    outw(0x0000, dev->base_addr + PCNET32_ADDR);
+    outw(0x7940, dev->base_addr + PCNET32_DATA);
+
+	if (pcnet32_debug > 4)
+		printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
+			   dev->name, inw(ioaddr + PCNET32_ADDR),
+			   inw(dev->base_addr + PCNET32_DATA));
+
+	dev->interrupt = 0;
+	return;
+}
+
+static int
+pcnet32_rx(struct device *dev)
+{
+	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+	int entry = lp->cur_rx & RX_RING_MOD_MASK;
+	int i;
+		
+	/* If we own the next entry, it's a new packet. Send it up. */
+	while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) {
+		int status = (short)le16_to_cpu(lp->rx_ring[entry].status) >> 8;
+
+		if (status != 0x03) {			/* There was an error. */
+			/* There is a tricky error noted by John Murphy,
+			   <murf@perftech.com> to Russ Nelson: Even with full-sized
+			   buffers it's possible for a jabber packet to use two
+			   buffers, with only the last correctly noting the error. */
+			if (status & 0x01)	/* Only count a general error at the */
+				lp->stats.rx_errors++; /* end of a packet.*/
+			if (status & 0x20) lp->stats.rx_frame_errors++;
+			if (status & 0x10) lp->stats.rx_over_errors++;
+			if (status & 0x08) lp->stats.rx_crc_errors++;
+			if (status & 0x04) lp->stats.rx_fifo_errors++;
+			lp->rx_ring[entry].status &= le16_to_cpu(0x03ff);
+		}
+		else 
+		{
+			/* Malloc up new buffer, compatible with net-2e. */
+			short pkt_len = (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)-4;
+			struct sk_buff *skb;
+			
+			if(pkt_len<60)
+			{
+				printk("%s: Runt packet!\n",dev->name);
+				lp->stats.rx_errors++;
+			}
+			else
+			{
+				skb = dev_alloc_skb(pkt_len+2);
+				if (skb == NULL) 
+				{
+					printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+					for (i=0; i < RX_RING_SIZE; i++)
+						if ((short)le16_to_cpu(lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status) < 0)
+							break;
+
+					if (i > RX_RING_SIZE -2) 
+					{
+						lp->stats.rx_dropped++;
+						lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
+						lp->cur_rx++;
+					}
+					break;
+				}
+				skb->dev = dev;
+				skb_reserve(skb,2);	/* 16 byte align */
+				skb_put(skb,pkt_len);	/* Make room */
+				eth_copy_and_sum(skb,
+					(unsigned char *)bus_to_virt(le32_to_cpu(lp->rx_ring[entry].base)),
+					pkt_len,0);
+				skb->protocol=eth_type_trans(skb,dev);
+				netif_rx(skb);
+				lp->stats.rx_packets++;
+			}
+		}
+		/* The docs say that the buffer length isn't touched, but Andrew Boyd
+		   of QNX reports that some revs of the 79C965 clear it. */
+		lp->rx_ring[entry].buf_length = le16_to_cpu(-PKT_BUF_SZ);
+		lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
+		entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
+	}
+
+	/* We should check that at least two ring entries are free.	 If not,
+	   we should free one and mark stats->rx_dropped++. */
+
+	return 0;
+}
+
+static int
+pcnet32_close(struct device *dev)
+{
+	unsigned int ioaddr = dev->base_addr;
+	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	outw(112, ioaddr+PCNET32_ADDR);
+	lp->stats.rx_missed_errors = inw(ioaddr+PCNET32_DATA);
+
+	outw(0, ioaddr+PCNET32_ADDR);
+
+	if (pcnet32_debug > 1)
+		printk("%s: Shutting down ethercard, status was %2.2x.\n",
+			   dev->name, inw(ioaddr+PCNET32_DATA));
+
+	/* We stop the PCNET32 here -- it occasionally polls
+	   memory if we don't. */
+	outw(0x0004, ioaddr+PCNET32_DATA);
+
+	free_irq(dev->irq, dev);
+
+	return 0;
+}
+
+static struct net_device_stats *
+pcnet32_get_stats(struct device *dev)
+{
+	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+	unsigned int ioaddr = dev->base_addr;
+	unsigned short saved_addr;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	saved_addr = inw(ioaddr+PCNET32_ADDR);
+	outw(112, ioaddr+PCNET32_ADDR);
+	lp->stats.rx_missed_errors = inw(ioaddr+PCNET32_DATA);
+	outw(saved_addr, ioaddr+PCNET32_ADDR);
+	restore_flags(flags);
+
+	return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ */
+
+static void pcnet32_set_multicast_list(struct device *dev)
+{
+	unsigned int ioaddr = dev->base_addr;
+	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;    
+
+	if (dev->flags&IFF_PROMISC) {
+		/* Log any net taps. */
+		printk("%s: Promiscuous mode enabled.\n", dev->name);
+	        lp->init_block.mode = 0x8000;
+	} else {
+		int num_addrs=dev->mc_count;
+		if(dev->flags&IFF_ALLMULTI)
+			num_addrs=1;
+		/* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */
+		memset(lp->init_block.filter , (num_addrs == 0) ? 0 : -1, sizeof(lp->init_block.filter));
+	        lp->init_block.mode = 0x0000;	        
+	}
+    
+	outw(0, ioaddr+PCNET32_ADDR);
+	outw(0x0004, ioaddr+PCNET32_DATA); /* Temporarily stop the lance.	 */
+
+	pcnet32_restart(dev, 0x0042, 0); /*  Resume normal operation */
+
+}
+
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c pcnet32.c"
+ *  c-indent-level: 4
+ *  tab-width: 4
+ * End:
+ */
diff -u --recursive --new-file v2.1.29/linux/drivers/net/pi2.c linux/drivers/net/pi2.c
--- v2.1.29/linux/drivers/net/pi2.c	Sun Feb  2 05:18:40 1997
+++ linux/drivers/net/pi2.c	Thu Mar 20 17:11:52 1997
@@ -111,7 +111,7 @@
 #include <linux/skbuff.h>
 #include <linux/timer.h>
 #include <linux/if_arp.h>
-#include "pi2.h"
+#include <linux/pi2.h>
 #include "z8530.h"
 #include <net/ax25.h>
 
diff -u --recursive --new-file v2.1.29/linux/drivers/net/pi2.h linux/drivers/net/pi2.h
--- v2.1.29/linux/drivers/net/pi2.h	Fri Feb  7 05:54:54 1997
+++ linux/drivers/net/pi2.h	Wed Dec 31 16:00:00 1969
@@ -1,130 +0,0 @@
-
-#define DMA_BUFF_SIZE 2200
-
-#define ON 1
-#define OFF 0
-
-
-/* Register offset info, specific to the PI
- * E.g., to read the data port on channel A, use
- *  inportb(pichan[dev].base + CHANA + DATA)
- */
-#define CHANB   0   /* Base of channel B regs */
-#define CHANA   2   /* Base of channel A regs */
-
-/* 8530 ports on each channel */
-#define CTL 0
-#define DATA    1
-
-#define DMAEN   0x4 /* Offset off DMA Enable register */
-
-/* Timer chip offsets */
-#define TMR0    0x8 /* Offset of timer 0 register */
-#define TMR1    0x9 /* Offset of timer 1 register */
-#define TMR2    0xA /* Offset of timer 2 register */
-#define TMRCMD  0xB /* Offset of timer command register */
-
-/* Timer chip equates */
-#define SC0 0x00 /* Select counter 0 */
-#define SC1 0x40 /* Select counter 1 */
-#define SC2 0x80 /* Select counter 2 */
-#define CLATCH  0x00 /* Counter latching operation */
-#define MSB 0x20 /* Read/load MSB only */
-#define LSB 0x10 /* Read/load LSB only */
-#define LSB_MSB 0x30 /* Read/load LSB, then MSB */
-#define MODE0   0x00 /* Interrupt on terminal count */
-#define MODE1   0x02 /* Programmable one shot */
-#define MODE2   0x04 /* Rate generator */
-#define MODE3   0x06 /* Square wave rate generator */
-#define MODE4   0x08 /* Software triggered strobe */
-#define MODE5   0x0a /* Hardware triggered strobe */
-#define BCD 0x01 /* BCD counter */
-
-/* DMA controller registers */
-#define DMA_STAT    8   /* DMA controller status register */
-#define DMA_CMD     8   /* DMA controller command register */
-#define DMA_MASK        10  /* DMA controller mask register */
-#define DMA_MODE        11  /* DMA controller mode register */
-#define DMA_RESETFF 12  /* DMA controller first/last flip flop  */
-/* DMA data */
-#define DMA_DISABLE (0x04)  /* Disable channel n */
-#define DMA_ENABLE  (0x00)  /* Enable channel n */
-/* Single transfers, incr. address, auto init, writes, ch. n */
-#define DMA_RX_MODE (0x54)
-/* Single transfers, incr. address, no auto init, reads, ch. n */
-#define DMA_TX_MODE (0x48)
-
-#define SINGLE 3686400
-#define DOUBLE 7372800
-
-#define SIOCGPIPARAM		0x5000	/* get PI parameters */
-#define SIOCSPIPARAM		0x5001	/* set */
-#define SIOCGPIBAUD		0x5002	/* get only baud rate */
-#define SIOCSPIBAUD		0x5003	
-#define SIOCGPIDMA		0x5004	/* get only DMA */
-#define SIOCSPIDMA		0x5005	
-#define SIOCGPIIRQ		0x5006	/* get only IRQ */
-#define SIOCSPIIRQ		0x5007	
-
-struct pi_req  {
-    int cmd;
-    int speed;
-    int clockmode;
-    int txdelay;
-    unsigned char persist;
-    int slotime; 
-    int squeldelay;
-    int dmachan;    
-    int irq;    
-};
-
-#ifdef __KERNEL__
-
-/* Information that needs to be kept for each channel. */
-struct pi_local {
-    struct net_device_stats stats; 
-    long open_time;             /* Useless example local info. */
-    unsigned long xtal; 
-
-    struct mbuf *rcvbuf;/* Buffer for current rx packet */
-    struct mbuf *rxdmabuf1; /* DMA rx buffer */
-    struct mbuf *rxdmabuf2; /* DMA rx buffer */
-
-    int bufsiz;         /* Size of rcvbuf */
-    char *rcp;          /* Pointer into rcvbuf */
-
-    struct sk_buff_head sndq;  /* Packets awaiting transmission */
-    int sndcnt;         /* Number of packets on sndq */
-    struct sk_buff *sndbuf;	/* Current buffer being transmitted */
-    char *txdmabuf;     /* Transmit DMA buffer */
-	char *txptr;		/* Used by B port tx */
-	int txcnt;			
-    char tstate;        /* Transmitter state */
-#define IDLE    0       /* Transmitter off, no data pending */
-#define ACTIVE  1       /* Transmitter on, sending data */
-#define UNDERRUN 2      /* Transmitter on, flushing CRC */
-#define FLAGOUT 3       /* CRC sent - attempt to start next frame */
-#define DEFER 4         /* Receive Active - DEFER Transmit */
-#define ST_TXDELAY 5    /* Sending leading flags */
-#define CRCOUT 6
-    char rstate;        /* Set when !DCD goes to 0 (TRUE) */
-/* Normal state is ACTIVE if Receive enabled */
-#define RXERROR 2       /* Error -- Aborting current Frame */
-#define RXABORT 3       /* ABORT sequence detected */
-#define TOOBIG 4        /* too large a frame to store */
-    int dev;            /* Device number */
-    int base;       /* Base of I/O registers */
-    int cardbase;     /* Base address of card */
-    int stata;        /* address of Channel A status regs */
-    int statb;        /* address of Channel B status regs */
-    int speed;        /* Line speed, bps */
-    int clockmode;    /* tapr 9600 modem clocking option */
-    int txdelay;      /* Transmit Delay 10 ms/cnt */
-    unsigned char persist;       /* Persistence (0-255) as a % */
-    int slotime;      /* Delay to wait on persistence hit */
-    int squeldelay;   /* Delay after XMTR OFF for squelch tail */
-    struct iface *iface;    /* Associated interface */
-    int dmachan;           /* DMA channel for this port */
-};
-
-#endif
diff -u --recursive --new-file v2.1.29/linux/drivers/net/pt.c linux/drivers/net/pt.c
--- v2.1.29/linux/drivers/net/pt.c	Sun Feb  2 05:18:41 1997
+++ linux/drivers/net/pt.c	Thu Mar 20 17:11:52 1997
@@ -91,7 +91,7 @@
 #include <linux/skbuff.h>
 #include <linux/timer.h>
 #include <linux/if_arp.h>
-#include "pt.h"
+#include <linux/pt.h>
 #include "z8530.h"
 #include <net/ax25.h>
 
diff -u --recursive --new-file v2.1.29/linux/drivers/net/pt.h linux/drivers/net/pt.h
--- v2.1.29/linux/drivers/net/pt.h	Sun Feb  2 05:18:41 1997
+++ linux/drivers/net/pt.h	Wed Dec 31 16:00:00 1969
@@ -1,174 +0,0 @@
-/*
- * pt.h: Linux device driver for the Gracilis PackeTwin
- * Copyright (C) 1995 Craig Small VK2XLZ (vk2xlz@vk2xlz.ampr.org.)
- *
- * Please read the notice appearing at the top of the file pt.c
- */
-#define DMA_BUFF_SIZE 2200
-
-#define ON 1
-#define OFF 0
-
-
-/* Register offset info, specific to the PT
- * E.g., to read the data port on channel A, use
- *  inportb(pichan[dev].base + CHANA + DATA)
- */
-#define CHANB   0   /* Base of channel B regs */
-#define CHANA   2   /* Base of channel A regs */
-
-/* 8530 ports on each channel */
-#define CTL 0
-#define DATA    1
-
-#define DMAEN   0x8 /* Offset off DMA Enable register */
-
-/* Timer chip offsets */
-#define TMR0    0x4 /* Offset of timer 0 register */
-#define TMR1    0x5 /* Offset of timer 1 register */
-#define TMR2    0x6 /* Offset of timer 2 register */
-#define TMRCMD  0x7 /* Offset of timer command register */
-#define INT_REG	0x8
-#define TMR1CLR 0x9
-#define TMR2CLR 0xa
-
-/* Interrupt register equates */
-#define PT_SCC_MSK	0x1
-#define PT_TMR1_MSK	0x2
-#define PT_TMR2_MSK	0x4
-
-/* Serial/interrupt register equates */
-#define PT_DTRA_ON	0x1
-#define PT_DTRB_ON	0x2
-#define PT_EXTCLKA	0x4
-#define PT_EXTCLKB	0x8
-#define PT_LOOPA_ON	0x10
-#define PT_LOOPB_ON	0x20
-#define PT_EI		0x80
-
-/* Timer chip equates */
-#define SC0 0x00 /* Select counter 0 */
-#define SC1 0x40 /* Select counter 1 */
-#define SC2 0x80 /* Select counter 2 */
-#define CLATCH  0x00 /* Counter latching operation */
-#define MSB 0x20 /* Read/load MSB only */
-#define LSB 0x10 /* Read/load LSB only */
-#define LSB_MSB 0x30 /* Read/load LSB, then MSB */
-#define MODE0   0x00 /* Interrupt on terminal count */
-#define MODE1   0x02 /* Programmable one shot */
-#define MODE2   0x04 /* Rate generator */
-#define MODE3   0x06 /* Square wave rate generator */
-#define MODE4   0x08 /* Software triggered strobe */
-#define MODE5   0x0a /* Hardware triggered strobe */
-#define BCD 0x01 /* BCD counter */
-
-/* DMA controller registers */
-#define DMA_STAT    8   /* DMA controller status register */
-#define DMA_CMD     8   /* DMA controller command register */
-#define DMA_MASK        10  /* DMA controller mask register */
-#define DMA_MODE        11  /* DMA controller mode register */
-#define DMA_RESETFF 12  /* DMA controller first/last flip flop  */
-/* DMA data */
-#define DMA_DISABLE (0x04)  /* Disable channel n */
-#define DMA_ENABLE  (0x00)  /* Enable channel n */
-/* Single transfers, incr. address, auto init, writes, ch. n */
-#define DMA_RX_MODE (0x54)
-/* Single transfers, incr. address, no auto init, reads, ch. n */
-#define DMA_TX_MODE (0x48)
-
-/* Write registers */
-#define DMA_CFG		0x08
-#define SERIAL_CFG	0x09
-#define INT_CFG		0x09	/* shares with serial config */
-#define DMA_CLR_FF	0x0a
-
-#define SINGLE 3686400
-#define DOUBLE 7372800
-#define XTAL   ((long) 6144000L)
-
-#define SIOCGPIPARAM		0x5000	/* get PI parameters */
-#define SIOCSPIPARAM		0x5001	/* set */
-#define SIOCGPIBAUD		0x5002	/* get only baud rate */
-#define SIOCSPIBAUD		0x5003	
-#define SIOCGPIDMA		0x5004	/* get only DMA */
-#define SIOCSPIDMA		0x5005	
-#define SIOCGPIIRQ		0x5006	/* get only IRQ */
-#define SIOCSPIIRQ		0x5007	
-
-struct pt_req
-{
-	int cmd;
-	int speed;
-	int clockmode;
-	int txdelay;
-	unsigned char persist;
-	int slotime; 
-	int squeldelay;
-	int dmachan;    
-	int irq;    
-};
-
-/* SCC Interrupt vectors, if we have set 'status low' */
-#define CHBTxIV		0x00
-#define CHBEXTIV	0x02
-#define CHBRxIV		0x04
-#define CHBSRCIV	0x06
-#define CHATxIV		0x08
-#define CHAEXTIV	0x0a
-#define CHARxIV		0x0c
-#define CHASRCIV	0x0e
-
-
-#ifdef __KERNEL__
-
-/* Information that needs to be kept for each channel. */
-struct pt_local
-{
-	struct net_device_stats stats; /* %%%dp*/
-	long open_time;             /* Useless example local info. */
-	unsigned long xtal; 
-
-	struct mbuf *rcvbuf;/* Buffer for current rx packet */
-	struct mbuf *rxdmabuf1; /* DMA rx buffer */
-	struct mbuf *rxdmabuf2; /* DMA rx buffer */
-
-	int bufsiz;			/* Size of rcvbuf 			*/
-	char *rcp;			/* Pointer into rcvbuf 			*/
-	struct sk_buff_head sndq;	/* Packets awaiting transmission 	*/
-	int sndcnt;			/* Number of packets on sndq 		*/
-	struct sk_buff *sndbuf;		/* Current buffer being transmitted	*/
-	char *txdmabuf; 		/* Transmit DMA buffer 			*/
-	char *txptr;			/* Used by B port tx 			*/
-	int txcnt;			
-	char tstate;			/* Transmitter state 			*/
-#define IDLE    0       /* Transmitter off, no data pending 		*/
-#define ACTIVE  1       /* Transmitter on, sending data 		*/
-#define UNDERRUN 2      /* Transmitter on, flushing CRC			*/
-#define FLAGOUT 3       /* CRC sent - attempt to start next frame	*/
-#define DEFER 4         /* Receive Active - DEFER Transmit		*/
-#define ST_TXDELAY 5    /* Sending leading flags			*/
-#define CRCOUT 6
-	char rstate;        /* Set when !DCD goes to 0 (TRUE)		*/
-/* Normal state is ACTIVE if Receive enabled */
-#define RXERROR 2       /* Error -- Aborting current Frame		*/
-#define RXABORT 3       /* ABORT sequence detected			*/
-#define TOOBIG 4        /* too large a frame to store			*/
-	
-	int dev;		/* Device number */
-	int base;		/* Base of I/O registers */
-	int cardbase;		/* Base address of card */
-	int stata;		/* address of Channel A status regs */
-	int statb;		/* address of Channel B status regs */
-	int speed;        	/* Line speed, bps */
-	int clockmode;    	/* tapr 9600 modem clocking option */
-	int txdelay;      	/* Transmit Delay 10 ms/cnt */
-	unsigned char persist;  /* Persistence (0-255) as a % */
-	int slotime;		/* Delay to wait on persistence hit */
-	int squeldelay; 	/* Delay after XMTR OFF for squelch tail */
-	struct iface *iface;    /* Associated interface */
-	int dmachan;		/* DMA channel for this port */
-	char saved_RR0;		/* The saved version of RR) that we compare with */
-	int nrzi;		/* Do we use NRZI (or NRZ) */
-};
-
-#endif
diff -u --recursive --new-file v2.1.29/linux/drivers/net/sk_g16.c linux/drivers/net/sk_g16.c
--- v2.1.29/linux/drivers/net/sk_g16.c	Fri Feb  7 05:54:54 1997
+++ linux/drivers/net/sk_g16.c	Tue Mar 11 14:54:24 1997
@@ -16,6 +16,8 @@
  * Description    : Schneider & Koch G16 Ethernet Device Driver for
  *                  Linux Kernel >= 1.1.22
  * Update History :
+ *                  Paul Gortmaker, 03/97: Fix for v2.1.x to use read{b,w}
+ *                  write{b,w} and memcpy -> memcpy_{to,from}io
  *
 -*/
 
@@ -64,6 +66,7 @@
 #include <linux/interrupt.h>
 #include <linux/malloc.h>
 #include <linux/string.h> 
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/bitops.h> 
@@ -754,7 +757,7 @@
     /* Read in station address */
     for (i = 0, j = 0; i < ETH_ALEN; i++, j+=2)
     {
-	dev->dev_addr[i] = board->rom[j];          
+	dev->dev_addr[i] = readb(board->rom+j);          
     }
 
     /* Check for manufacturer code */
@@ -1253,8 +1256,7 @@
 
 	/* Copy data into dual ported ram */
 
-	memcpy((char *) (tmdp->u.buffer & 0x00ffffff), (char *)skb->data,
-	       skb->len);
+	memcpy_toio((tmdp->u.buffer & 0x00ffffff), skb->data, skb->len);
 
 	tmdp->blen = -len;            /* set length to transmit */
 
@@ -1598,8 +1600,7 @@
 	     * ignore status fields) 
 	     */
 
-	    memcpy(skb_put(skb,len), (unsigned char *) (rmdp->u.buffer & 0x00ffffff),
-		   len);
+	    memcpy_fromio(skb_put(skb,len), (rmdp->u.buffer & 0x00ffffff), len);
 
 
 	    /* 
@@ -1775,7 +1776,7 @@
     int rom_found = 0;
     unsigned int rom_location[] = SK_BOOT_ROM_LOCATIONS;
     unsigned char rom_id[] = SK_BOOT_ROM_ID;
-    unsigned char *test_byte;
+    unsigned char test_byte;
 
     /* Autodetect Boot_ROM */
     PRINTK(("## %s: Autodetection of Boot_ROM\n", SK_NAME));
@@ -1788,10 +1789,10 @@
 	rom_found = 1; 
 	for (j = 0; j < 6; j++)
 	{
-	    test_byte = (unsigned char *) (rom_location[i]+j);
+	    test_byte = readb(rom_location[i]+j);
 	    PRINTK((" %02x ", *test_byte));
 
-	    if(!(*test_byte == rom_id[j]))
+	    if(test_byte != rom_id[j])
 	    {
 		rom_found = 0;
 	    } 
@@ -1842,12 +1843,9 @@
 
 void SK_reset_board(void)
 {
-    int i;
-
-    SK_PORT = 0x00;           /* Reset active */
-    for (i = 0; i < 10 ; i++) /* Delay min 5ms */
-	;
-    SK_PORT = SK_RESET;       /* Set back to normal operation */
+    writeb(0x00, SK_PORT);       /* Reset active */
+    udelay(5000);                /* Delay min 5ms */
+    writeb(SK_RESET, SK_PORT);   /* Set back to normal operation */
 
 } /* End of SK_reset_board() */
 
@@ -1870,12 +1868,12 @@
 
 void SK_set_RAP(int reg_number)
 {
-    SK_IOREG = reg_number;
-    SK_PORT  = SK_RESET | SK_RAP | SK_WREG;
-    SK_IOCOM = SK_DOIO;
+    writew(reg_number, SK_IOREG);
+    writeb(SK_RESET | SK_RAP | SK_WREG, SK_PORT);
+    writeb(SK_DOIO, SK_IOCOM);
 
-    while (SK_PORT & SK_IORUN) 
-	;
+    while (readb(SK_PORT) & SK_IORUN) 
+	barrier();
 } /* End of SK_set_RAP() */
 
 
@@ -1898,12 +1896,12 @@
 {
     SK_set_RAP(reg_number);
 
-    SK_PORT  = SK_RESET | SK_RDATA | SK_RREG;
-    SK_IOCOM = SK_DOIO;
+    writeb(SK_RESET | SK_RDATA | SK_RREG, SK_PORT);
+    writeb(SK_DOIO, SK_IOCOM);
 
-    while (SK_PORT & SK_IORUN)
-	;
-    return (SK_IOREG);
+    while (readb(SK_PORT) & SK_IORUN)
+	barrier();
+    return (readw(SK_IOREG));
 
 } /* End of SK_read_reg() */
 
@@ -1927,13 +1925,13 @@
 
 int SK_rread_reg(void)
 {
-    SK_PORT  = SK_RESET | SK_RDATA | SK_RREG;
+    writeb(SK_RESET | SK_RDATA | SK_RREG, SK_PORT);
 
-    SK_IOCOM = SK_DOIO;
+    writeb(SK_DOIO, SK_IOCOM);
 
-    while (SK_PORT & SK_IORUN)
-	;
-    return (SK_IOREG);
+    while (readb(SK_PORT) & SK_IORUN)
+	barrier();
+    return (readw(SK_IOREG));
 
 } /* End of SK_rread_reg() */
 
@@ -1961,12 +1959,12 @@
 {
     SK_set_RAP(reg_number);
 
-    SK_IOREG = value;
-    SK_PORT  = SK_RESET | SK_RDATA | SK_WREG;
-    SK_IOCOM = SK_DOIO;
+    writew(value, SK_IOREG);
+    writeb(SK_RESET | SK_RDATA | SK_WREG, SK_PORT);
+    writeb(SK_DOIO, SK_IOCOM);
 
-    while (SK_PORT & SK_IORUN)
-	;
+    while (readb(SK_PORT) & SK_IORUN)
+	barrier();
 } /* End of SK_write_reg */
 
 
diff -u --recursive --new-file v2.1.29/linux/drivers/net/soundmodem/sm.c linux/drivers/net/soundmodem/sm.c
--- v2.1.29/linux/drivers/net/soundmodem/sm.c	Thu Feb 27 10:57:31 1997
+++ linux/drivers/net/soundmodem/sm.c	Thu Mar 20 17:11:52 1997
@@ -37,6 +37,7 @@
  *   0.1  21.09.96  Started
  *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
  *   0.4  21.01.97  Separately compileable soundcard/modem modules
+ *   0.5  03.03.97  fixed LPT probing (check_lpt result was interpreted the wrong way round)
  */
 
 /*****************************************************************************/
@@ -101,13 +102,13 @@
 
 /* --------------------------------------------------------------------- */
 
-static const char sm_drvname[] = "soundmodem";
+const char sm_drvname[] = "soundmodem";
 static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "soundmodem: version 0.4 compiled " __TIME__ " " __DATE__ "\n";
+KERN_INFO "soundmodem: version 0.5 compiled " __TIME__ " " __DATE__ "\n";
 
 /* --------------------------------------------------------------------- */
 
-static const struct modem_tx_info *sm_modem_tx_table[] = {
+const struct modem_tx_info *sm_modem_tx_table[] = {
 #ifdef CONFIG_SOUNDMODEM_AFSK1200
 	&sm_afsk1200_tx,
 #endif /* CONFIG_SOUNDMODEM_AFSK1200 */
@@ -130,7 +131,7 @@
 	NULL
 };
 
-static const struct modem_rx_info *sm_modem_rx_table[] = {
+const struct modem_rx_info *sm_modem_rx_table[] = {
 #ifdef CONFIG_SOUNDMODEM_AFSK1200
 	&sm_afsk1200_rx,
 #endif /* CONFIG_SOUNDMODEM_AFSK1200 */
@@ -229,6 +230,11 @@
  * ===================== port checking routines ========================
  */
 
+/*
+ * returns 0 if ok and != 0 on error;
+ * the same behaviour as par96_check_lpt in baycom.c
+ */
+
 static int check_lpt(unsigned int iobase)
 {
 	unsigned char b1,b2;
@@ -367,7 +373,7 @@
 	}
 	if (sm->hdrv.ptt_out.pariobase > 0 &&
 	    sm->hdrv.ptt_out.pariobase <= 0x1000-LPT_EXTENT &&
-	    check_lpt(sm->hdrv.ptt_out.pariobase)) {
+	    !check_lpt(sm->hdrv.ptt_out.pariobase)) {
 		sm->hdrv.ptt_out.flags |= SP_PAR;
 		request_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT, "sm par ptt");
 	}
diff -u --recursive --new-file v2.1.29/linux/drivers/net/soundmodem/sm_afsk1200.c linux/drivers/net/soundmodem/sm_afsk1200.c
--- v2.1.29/linux/drivers/net/soundmodem/sm_afsk1200.c	Thu Feb 27 10:57:31 1997
+++ linux/drivers/net/soundmodem/sm_afsk1200.c	Thu Mar 20 17:11:52 1997
@@ -81,8 +81,6 @@
  */
 
 
-
-
 #if defined (CONFIG_SOUNDMODEM__AFSK1200_FP) && (defined(CONFIG_M586) || defined(CONFIG_M686))
 
 
@@ -143,10 +141,10 @@
 	memmove(st->filt.f+1, st->filt.f,sizeof(st->filt.f) - sizeof(st->filt.f[0]));
 	st->filt.f[0] = (((int)newval)-0x80);
 
-	sum = convolution8(st->filt.f, afsk12_tx_lo_i);
-	sum += convolution8(st->filt.f, afsk12_tx_lo_q);
-	sum -= convolution8(st->filt.f, afsk12_tx_hi_i);
-	sum -= convolution8(st->filt.f, afsk12_tx_hi_q);
+	sum = convolution8(st->filt.f, afsk12_tx_lo_i_f);
+	sum += convolution8(st->filt.f, afsk12_tx_lo_q_f);
+	sum -= convolution8(st->filt.f, afsk12_tx_hi_i_f);
+	sum -= convolution8(st->filt.f, afsk12_tx_hi_q_f);
 	return sum;
 }
 
diff -u --recursive --new-file v2.1.29/linux/drivers/net/soundmodem/sm_sbc.c linux/drivers/net/soundmodem/sm_sbc.c
--- v2.1.29/linux/drivers/net/soundmodem/sm_sbc.c	Thu Feb 27 10:57:31 1997
+++ linux/drivers/net/soundmodem/sm_sbc.c	Thu Mar 20 17:11:52 1997
@@ -456,6 +456,7 @@
 		i = 0;
 		bi.data.mix.sample_rate = sm->mode_rx->srate;
 		bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
+		bi.data.mix.mixer_type = SM_MIXER_INVALID;
 		switch (SCSTATE->revhi) {
 		case 2:
 			bi.data.mix.mixer_type = SM_MIXER_CT1335;
diff -u --recursive --new-file v2.1.29/linux/drivers/net/sunhme.c linux/drivers/net/sunhme.c
--- v2.1.29/linux/drivers/net/sunhme.c	Mon Mar 17 14:54:25 1997
+++ linux/drivers/net/sunhme.c	Thu Mar 20 17:11:52 1997
@@ -2190,6 +2190,7 @@
 	/* We are home free at this point, link us in to the happy
 	 * module device list.
 	 */
+	dev->ifindex = dev_new_index();
 	hp->next_module = root_happy_dev;
 	root_happy_dev = hp;
 #endif
diff -u --recursive --new-file v2.1.29/linux/drivers/net/sunlance.c linux/drivers/net/sunlance.c
--- v2.1.29/linux/drivers/net/sunlance.c	Mon Mar 17 14:54:26 1997
+++ linux/drivers/net/sunlance.c	Thu Mar 20 17:11:52 1997
@@ -1,4 +1,4 @@
-/* $Id: sunlance.c,v 1.55 1997/02/07 09:41:07 ecd Exp $
+/* $Id: sunlance.c,v 1.56 1997/03/14 21:04:45 jj Exp $
  * lance.c: Linux/Sparc/Lance driver
  *
  *	Written 1995, 1996 by Miguel de Icaza
@@ -255,7 +255,7 @@
  * zero on the lebuffer PIO area. -davem
  */
 
-#define LANCE_ADDR(x) ((int)(x) & ~0xff000000)
+#define LANCE_ADDR(x) ((long)(x) & ~0xff000000)
 
 #ifdef MODULE
 static struct lance_private *root_lance_dev = NULL;
diff -u --recursive --new-file v2.1.29/linux/drivers/net/sunqe.c linux/drivers/net/sunqe.c
--- v2.1.29/linux/drivers/net/sunqe.c	Mon Mar 17 14:54:26 1997
+++ linux/drivers/net/sunqe.c	Thu Mar 20 17:11:52 1997
@@ -1116,6 +1116,8 @@
 	/* We are home free at this point, link the qe's into
 	 * the master list for later module unloading.
 	 */
+	for(i = 0; i < 4; i++)
+		qe_devs[i]->ifindex = dev_new_index();
 	qecp->next_module = root_qec_dev;
 	root_qec_dev = qecp;
 #endif
diff -u --recursive --new-file v2.1.29/linux/drivers/sbus/char/bpp.c linux/drivers/sbus/char/bpp.c
--- v2.1.29/linux/drivers/sbus/char/bpp.c	Mon Mar 17 14:54:28 1997
+++ linux/drivers/sbus/char/bpp.c	Thu Mar 20 17:11:52 1997
@@ -10,37 +10,38 @@
  */
 
 
-# include  <linux/kernel.h>
-# include  <linux/module.h>
-# include  <linux/version.h>
-# include  <linux/fs.h>
-# include  <linux/errno.h>
-# include  <linux/sched.h>
-# include  <linux/timer.h>
-# include  <linux/ioport.h>
-
-# include  <asm/uaccess.h>
-
-# if defined(__i386__)
-# include  <asm/io.h>
-# include  <asm/system.h>
-# include  <asm/segment.h>
-# endif
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/major.h>
 
-# if defined(__sparc__)
+#include  <asm/uaccess.h>
+
+#if defined(__i386__)
+# include <asm/io.h>
+# include <asm/system.h>
+# include <asm/segment.h>
+#endif
+
+#if defined(__sparc__)
 # include <linux/delay.h>         /* udelay() */
 
 # include <asm/oplib.h>           /* OpenProm Library */
 # include <asm/sbus.h>            /* struct linux_sbus *SBus_chain */
 # include <asm/io.h>              /* sparc_alloc_io() */
-# endif
+#endif
 
-# include <asm/bpp.h>
+#include <asm/bpp.h>
 
 #define BPP_PROBE_CODE 0x55
 #define BPP_DELAY 100
 
-static const unsigned  BPP_MAJOR = 32;
+static const unsigned  BPP_MAJOR = LP_MAJOR;
 static const char* dev_name = "bpp";
 
 /* When switching from compatability to a mode where I can read, try
@@ -324,7 +325,7 @@
 
 static void snooze(unsigned long snooze_time, unsigned minor)
 {
-      instances[minor].timer_list.expires = snooze_time+1;
+      instances[minor].timer_list.expires = jiffies + snooze_time + 1;
       instances[minor].timer_list.data    = minor;
       add_timer(&instances[minor].timer_list);
       sleep_on (&instances[minor].wait_queue);
@@ -457,7 +458,7 @@
 /*
  * Allow only one process to open the device at a time.
  */
-static int open(struct inode *inode, struct file *f)
+static int bpp_open(struct inode *inode, struct file *f)
 {
       unsigned minor = MINOR(inode->i_rdev);
       if (minor >= BPP_NO) return -ENODEV;
@@ -475,7 +476,7 @@
  * mode as this is a reasonable place to clean up from messes made by
  * ioctls, or other mayhem.
  */
-static void release(struct inode *inode, struct file *f)
+static void bpp_release(struct inode *inode, struct file *f)
 {
       unsigned minor = MINOR(inode->i_rdev);
       instances[minor].opened = 0;
@@ -629,7 +630,7 @@
       return cnt - remaining;
 }
 
-static long read(struct inode *inode, struct file *f,
+static long bpp_read(struct inode *inode, struct file *f,
 		 char *c, unsigned long cnt)
 {
       long rc;
@@ -779,7 +780,7 @@
  * that. Otherwise, terminate and do my writing in compat mode. This
  * is the safest course as any device can handle it.
  */
-static long write(struct inode *inode, struct file *f,
+static long bpp_write(struct inode *inode, struct file *f,
 		  const char *c, unsigned long cnt)
 {
       long errno = 0;
@@ -804,7 +805,7 @@
       return errno;
 }
 
-static int ioctl(struct inode *inode, struct file *f, unsigned int cmd,
+static int bpp_ioctl(struct inode *inode, struct file *f, unsigned int cmd,
 		 unsigned long arg)
 {
       int errno = 0;
@@ -856,19 +857,15 @@
 }
 
 static struct file_operations bpp_fops = {
-      0,
-      read, /* read */
-      write, /* write */
-      0,
-      0,
-      ioctl,
-      0, /* mmap */
-      open,
-      release,
-      0,
-      0,
-      0,
-      0
+	NULL,		/* bpp_lseek */
+	bpp_read,
+	bpp_write,
+	NULL,		/* bpp_readdir */
+	NULL,		/* bpp_select */
+	bpp_ioctl,
+	NULL,		/* bpp_mmap */
+	bpp_open,
+	bpp_release,
 };
 
 #if defined(__i386__)
diff -u --recursive --new-file v2.1.29/linux/drivers/sbus/char/bwtwo.c linux/drivers/sbus/char/bwtwo.c
--- v2.1.29/linux/drivers/sbus/char/bwtwo.c	Mon Dec 30 02:00:04 1996
+++ linux/drivers/sbus/char/bwtwo.c	Thu Mar 20 17:11:52 1997
@@ -1,7 +1,8 @@
-/* $Id: bwtwo.c,v 1.9 1996/12/23 10:15:57 ecd Exp $
+/* $Id: bwtwo.c,v 1.10 1997/03/12 23:25:15 ecd Exp $
  * bwtwo.c: bwtwo console driver
  *
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1997 Eddie C. Dost   (ecd@skynet.be)
  */
 
 #include <linux/kd.h>
@@ -29,9 +30,30 @@
 	__volatile__ __u8		status;
 	__volatile__ __u8		cursor_start;
 	__volatile__ __u8		cursor_end;
-	__volatile__ __u8		vcontrol[12];
+	__volatile__ __u8		h_blank_start;
+	__volatile__ __u8		h_blank_end;
+	__volatile__ __u8		h_sync_start;
+	__volatile__ __u8		h_sync_end;
+	__volatile__ __u8		comp_sync_end;
+	__volatile__ __u8		v_blank_start_high;
+	__volatile__ __u8		v_blank_start_low;
+	__volatile__ __u8		v_blank_end;
+	__volatile__ __u8		v_sync_start;
+	__volatile__ __u8		v_sync_end;
+	__volatile__ __u8		xfer_holdoff_start;
+	__volatile__ __u8		xfer_holdoff_end;
 };
 
+/* Status Register Constants */
+#define BWTWO_SR_RES_MASK	0x70
+#define BWTWO_SR_1600_1280	0x50
+#define BWTWO_SR_1152_900_76_A	0x40
+#define BWTWO_SR_1152_900_76_B	0x60
+#define BWTWO_SR_ID_MASK	0x0f
+#define BWTWO_SR_ID_MONO	0x02
+#define BWTWO_SR_ID_MONO_ECL	0x03
+#define BWTWO_SR_ID_MSYNC	0x04
+
 /* Control Register Constants */
 #define BWTWO_CTL_ENABLE_INTS   0x80
 #define BWTWO_CTL_ENABLE_VIDEO  0x40
@@ -85,7 +107,44 @@
 	fb->info.bwtwo.regs->control |= BWTWO_CTL_ENABLE_VIDEO;
 }
 
-__initfunc(void bwtwo_setup (fbinfo_t *fb, int slot, uint bwtwo, int bw2_io))
+
+static u8 bw2regs_1600[] __initdata = {
+	0x14, 0x8b,	0x15, 0x28,	0x16, 0x03,	0x17, 0x13,
+	0x18, 0x7b,	0x19, 0x05,	0x1a, 0x34,	0x1b, 0x2e,
+	0x1c, 0x00,	0x1d, 0x0a,	0x1e, 0xff,	0x1f, 0x01,
+	0x10, 0x21,	0
+};
+
+static u8 bw2regs_ecl[] __initdata = {
+	0x14, 0x65,	0x15, 0x1e,	0x16, 0x04,	0x17, 0x0c,
+	0x18, 0x5e,	0x19, 0x03,	0x1a, 0xa7,	0x1b, 0x23,
+	0x1c, 0x00,	0x1d, 0x08,	0x1e, 0xff,	0x1f, 0x01,
+	0x10, 0x20,	0
+};
+
+static u8 bw2regs_analog[] __initdata = {
+	0x14, 0xbb,	0x15, 0x2b,	0x16, 0x03,	0x17, 0x13,
+	0x18, 0xb0,	0x19, 0x03,	0x1a, 0xa6,	0x1b, 0x22,
+	0x1c, 0x01,	0x1d, 0x05,	0x1e, 0xff,	0x1f, 0x01,
+	0x10, 0x20,	0
+};
+
+static u8 bw2regs_76hz[] __initdata = {
+	0x14, 0xb7,	0x15, 0x27,	0x16, 0x03,	0x17, 0x0f,
+	0x18, 0xae,	0x19, 0x03,	0x1a, 0xae,	0x1b, 0x2a,
+	0x1c, 0x01,	0x1d, 0x09,	0x1e, 0xff,	0x1f, 0x01,
+	0x10, 0x24,	0
+};
+
+static u8 bw2regs_66hz[] __initdata = {
+	0x14, 0xbb,	0x15, 0x2b,	0x16, 0x04,	0x17, 0x14,
+	0x18, 0xae,	0x19, 0x03,	0x1a, 0xa8,	0x1b, 0x24,
+	0x1c, 0x01,	0x1d, 0x05,	0x1e, 0xff,	0x1f, 0x01,
+	0x10, 0x20,	0
+};
+
+__initfunc(void bwtwo_setup (fbinfo_t *fb, int slot, uint bwtwo, int bw2_io,
+			     struct linux_sbus_device *sbdp))
 {
 	printk ("bwtwo%d at 0x%8.8x\n", slot, bwtwo);
 	fb->type.fb_cmsize = 0;
@@ -95,9 +154,51 @@
 	fb->reset = 0;
 	fb->blank = bwtwo_blank;
 	fb->unblank = bwtwo_unblank;
+
 	fb->info.bwtwo.regs = sparc_alloc_io ((void *)bwtwo +
 		BWTWO_REGISTER_OFFSET, 0, sizeof (struct bwtwo_regs),
 		"bwtwo_regs", bw2_io, 0);
+
+	if (!prom_getbool(sbdp->prom_node, "width")) {
+		/* Ugh, broken PROM didn't initialize us.
+		 * Let's deal with this ourselves.
+		 */
+		u8 status, mon;
+		u8 *p;
+
+		status = fb->info.bwtwo.regs->status;
+		mon = status & BWTWO_SR_RES_MASK;
+		switch (status & BWTWO_SR_ID_MASK) {
+			case BWTWO_SR_ID_MONO_ECL:
+				if (mon == BWTWO_SR_1600_1280) {
+					p = bw2regs_1600;
+					fb->type.fb_width = 1600;
+					fb->type.fb_height = 1280;
+				} else {
+					p = bw2regs_ecl;
+				}
+				break;
+			case BWTWO_SR_ID_MONO:
+				p = bw2regs_analog;
+				break;
+			case BWTWO_SR_ID_MSYNC:
+				if (mon == BWTWO_SR_1152_900_76_A ||
+				    mon == BWTWO_SR_1152_900_76_B) {
+					p = bw2regs_76hz;
+				} else {
+					p = bw2regs_66hz;
+				}
+				break;
+			default:
+				prom_printf("bwtwo: can't handle SR %02x\n",
+					    status);
+				prom_halt();
+				return; /* fool gcc. */
+		}
+		for ( ; *p; p += 2)
+			((u8 *)fb->info.bwtwo.regs)[p[0]] = p[1];
+	}
+
 	if(!fb->base)
 		fb->base = (unsigned long) sparc_alloc_io((void *)bwtwo, 0,
 		  ((fb->type.fb_depth*fb->type.fb_height*fb->type.fb_width)/8),
diff -u --recursive --new-file v2.1.29/linux/drivers/sbus/char/cgthree.c linux/drivers/sbus/char/cgthree.c
--- v2.1.29/linux/drivers/sbus/char/cgthree.c	Mon Dec 30 02:00:04 1996
+++ linux/drivers/sbus/char/cgthree.c	Thu Mar 20 17:11:52 1997
@@ -1,7 +1,9 @@
-/* $Id: cgthree.c,v 1.10 1996/12/23 10:16:07 ecd Exp $
+/* $Id: cgthree.c,v 1.12 1997/03/11 23:44:24 ecd Exp $
  * cgtree.c: cg3 frame buffer driver
  *
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1996 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Eddie C. Dost   (ecd@skynet.be)
  *
  * Support for cgRDI added, Nov/96, jj.
  */
@@ -22,22 +24,55 @@
 #include "fb.h"
 #include "cg_common.h"
 
+
+#define CG3_SR_RES_MASK		0x70
+#define CG3_SR_1152_900_76_A	0x40
+#define CG3_SR_1152_900_76_B	0x60
+#define CG3_SR_ID_MASK		0x0f
+#define CG3_SR_ID_COLOR		0x01
+#define CG3_SR_ID_MONO		0x02
+#define CG3_SR_ID_MONO_ECL	0x03
+
+
+enum cg3_type {
+	CG3_AT_66HZ = 0,
+	CG3_AT_76HZ,
+	CG3_RDI
+};
+
+
+struct cg3_regs {
+	struct bt_regs	cmap;
+	volatile u8	control;
+	volatile u8	status;
+	volatile u8	cursor_start;
+	volatile u8	cursor_end;
+	volatile u8	h_blank_start;
+	volatile u8	h_blank_end;
+	volatile u8	h_sync_start;
+	volatile u8	h_sync_end;
+	volatile u8	comp_sync_end;
+	volatile u8	v_blank_start_high;
+	volatile u8	v_blank_start_low;
+	volatile u8	v_blank_end;
+	volatile u8	v_sync_start;
+	volatile u8	v_sync_end;
+	volatile u8	xfer_holdoff_start;
+	volatile u8	xfer_holdoff_end;
+};
+
 /* The cg3 palette is loaded with 4 color values at each time  */
 /* so you end up with: (rgb)(r), (gb)(rg), (b)(rgb), and so on */
 static void
 cg3_loadcmap (fbinfo_t *fb, int index, int count)
 {
-	struct bt_regs *bt = fb->info.cg3.bt;
+	struct bt_regs *bt = &fb->info.cg3.regs->cmap;
 	int *i, steps;
 
 	i = (((int *)fb->color_map) + D4M3(index));
 	steps = D4M3(index+count-1) - D4M3(index)+3;
-#if 0	
-	if (fb->info.cg3.cgrdi) {
-		*(volatile u8 *)bt->addr = (u8)(D4M4(index));
-	} else
-#endif	
-	bt->addr = D4M4(index);
+
+	*(volatile u8 *)&bt->addr = (u8)D4M4(index);
 	while (steps--)
 		bt->color_map = *i++;
 }
@@ -90,7 +125,38 @@
         return 0;
 }
 
-__initfunc(void cg3_setup (fbinfo_t *fb, int slot, uint cg3, int cg3_io, struct linux_sbus_device *sbdp))
+static u8 cg3regvals_66hz[] __initdata = {	/* 1152 x 900, 66 Hz */
+	0x14, 0xbb,	0x15, 0x2b,	0x16, 0x04,	0x17, 0x14,
+	0x18, 0xae,	0x19, 0x03,	0x1a, 0xa8,	0x1b, 0x24,
+	0x1c, 0x01,	0x1d, 0x05,	0x1e, 0xff,	0x1f, 0x01,
+	0x10, 0x20,	0
+};
+
+static u8 cg3regvals_76hz[] __initdata = {	/* 1152 x 900, 76 Hz */
+	0x14, 0xb7,	0x15, 0x27,	0x16, 0x03,	0x17, 0x0f,
+	0x18, 0xae,	0x19, 0x03,	0x1a, 0xae,	0x1b, 0x2a,
+	0x1c, 0x01,	0x1d, 0x09,	0x1e, 0xff,	0x1f, 0x01,
+	0x10, 0x24,	0
+};
+
+static u8 cg3regvals_rdi[] __initdata = {	/* 640 x 480, cgRDI */
+	0x14, 0x70,	0x15, 0x20,	0x16, 0x08,	0x17, 0x10,
+	0x18, 0x06,	0x19, 0x02,	0x1a, 0x31,	0x1b, 0x51,
+	0x1c, 0x06,	0x1d, 0x0c,	0x1e, 0xff,	0x1f, 0x01,
+	0x10, 0x22,	0
+};
+
+static u8 *cg3_regvals[] __initdata = {
+	cg3regvals_66hz, cg3regvals_76hz, cg3regvals_rdi
+};
+
+static u_char cg3_dacvals[] __initdata = {
+	4, 0xff,	5, 0x00,	6, 0x70,	7, 0x00,	0
+};
+
+
+__initfunc(void cg3_setup (fbinfo_t *fb, int slot, uint cg3, int cg3_io,
+			   struct linux_sbus_device *sbdp))
 {
 	struct cg3_info *cg3info = (struct cg3_info *) &fb->info.cg3;
 
@@ -99,7 +165,8 @@
 		char *p;
 		int w, h;
 		
-		prom_getstring (sbdp->prom_node, "params", buffer, sizeof(buffer));
+		prom_getstring (sbdp->prom_node, "params",
+				buffer, sizeof(buffer));
 		if (*buffer) {
 			w = simple_strtoul (buffer, &p, 10);
 			if (w && *p == 'x') {
@@ -126,11 +193,45 @@
 	fb->reset = 0;
 
 	/* Map the card registers */
-	cg3info->bt = sparc_alloc_io ((void *) cg3+CG3_REGS, 0,
-		 sizeof (struct bt_regs),"cg3_bt", cg3_io, 0);
-	/* cgRDI actually has 32 bytes of regs, but I don't understand
-	   those bitfields yet (guess it is some interrupt stuff etc. */
-	
+	cg3info->regs = sparc_alloc_io ((void *) cg3+CG3_REGS, 0,
+		 sizeof (struct cg3_regs),"cg3_regs", cg3_io, 0);
+
+	if (!prom_getbool(sbdp->prom_node, "width")) {
+		/* Ugh, broken PROM didn't initialize us.
+		 * Let's deal with this ourselves.
+		 */
+		u8 status, mon;
+		enum cg3_type type;
+		u8 *p;
+
+		if (cg3info->cgrdi) {
+			type = CG3_RDI;
+		} else {
+			status = cg3info->regs->status;
+			if ((status & CG3_SR_ID_MASK) == CG3_SR_ID_COLOR) {
+				mon = status & CG3_SR_RES_MASK;
+				if (mon == CG3_SR_1152_900_76_A ||
+				    mon == CG3_SR_1152_900_76_B)
+					type = CG3_AT_76HZ;
+				else
+					type = CG3_AT_66HZ;
+			} else {
+				prom_printf("cgthree: can't handle SR %02x\n",
+					    status);
+				prom_halt();
+				return; /* fool gcc. */
+			}
+		}
+
+		for (p = cg3_regvals[type]; *p; p += 2)
+			((u8 *)cg3info->regs)[p[0]] = p[1];
+
+		for (p = cg3_dacvals; *p; p += 2) {
+			*(volatile u8 *)&cg3info->regs->cmap.addr = p[0];
+			*(volatile u8 *)&cg3info->regs->cmap.control = p[1];
+		}
+	}
+
 	if (!fb->base){
 		fb->base=(uint) sparc_alloc_io ((void*) cg3+CG3_RAM, 0,
 				    fb->type.fb_size, "cg3_ram", cg3_io, 0);
diff -u --recursive --new-file v2.1.29/linux/drivers/sbus/char/fb.h linux/drivers/sbus/char/fb.h
--- v2.1.29/linux/drivers/sbus/char/fb.h	Mon Mar 17 14:54:28 1997
+++ linux/drivers/sbus/char/fb.h	Thu Mar 20 17:11:52 1997
@@ -1,4 +1,4 @@
-/* $Id: fb.h,v 1.21 1997/02/02 02:12:43 ecd Exp $
+/* $Id: fb.h,v 1.23 1997/03/12 23:25:16 ecd Exp $
  * fb.h: contains the definitions of the structures that various sun
  *       frame buffer can use to do console driver stuff.
  *
@@ -77,7 +77,7 @@
 };
 
 struct cg3_info {
-	struct bt_regs *bt;	/* brooktree (color) registers */
+	struct cg3_regs *regs;	/* brooktree (color) registers, and more */
 	int cgrdi;		/* 1 if this is a cgRDI */
 };
 
@@ -200,7 +200,8 @@
 extern void cg3_setup (fbinfo_t *, int, uint, int, struct linux_sbus_device *);
 extern void cg6_setup (fbinfo_t *, int, uint, int);
 extern void cg14_setup (fbinfo_t *, int, int, uint, int);
-extern void bwtwo_setup (fbinfo_t *, int, uint, int);
+extern void bwtwo_setup (fbinfo_t *, int, uint, int,
+			 struct linux_sbus_device *);
 extern void leo_setup (fbinfo_t *, int, uint, int);
 extern void tcx_setup (fbinfo_t *, int, int, uint, struct linux_sbus_device *);
 
diff -u --recursive --new-file v2.1.29/linux/drivers/sbus/char/suncons.c linux/drivers/sbus/char/suncons.c
--- v2.1.29/linux/drivers/sbus/char/suncons.c	Mon Mar 17 14:54:28 1997
+++ linux/drivers/sbus/char/suncons.c	Thu Mar 20 17:11:52 1997
@@ -1,4 +1,4 @@
-/* $Id: suncons.c,v 1.50 1997/02/26 08:55:22 ecd Exp $
+/* $Id: suncons.c,v 1.53 1997/03/15 07:47:50 davem Exp $
  *
  * suncons.c: Sun SparcStation console support.
  *
@@ -758,26 +758,16 @@
 	        (*fb_restore_palette) (&fbinfo[0]);
 }
 
-/* This routine should be moved to srmmu.c */
-static __inline__ uint
-srmmu_get_pte (unsigned long addr)
-{
-	register unsigned long entry;
-
-	__asm__ __volatile__("\n\tlda [%1] %2,%0\n\t" :
-			     "=r" (entry):
-			     "r" ((addr & 0xfffff000) | 0x400), "i" (ASI_M_FLUSH_PROBE));
-	return entry;
-}
-
-uint
-get_phys (uint addr)
+unsigned int
+get_phys (unsigned int addr)
 {
 	switch (sparc_cpu_model){
 	case sun4c:
 		return sun4c_get_pte (addr) << PAGE_SHIFT;
 	case sun4m:
 		return ((srmmu_get_pte (addr) & 0xffffff00) << 4);
+	case sun4u:
+		/* FIXME: */ return 0;
 	default:
 		panic ("get_phys called for unsupported cpu model\n");
 		return 0;
@@ -785,13 +775,15 @@
 }
 
 int
-get_iospace (uint addr)
+get_iospace (unsigned int addr)
 {
 	switch (sparc_cpu_model){
 	case sun4c:
 		return -1; /* Don't check iospace on sun4c */
 	case sun4m:
 		return (srmmu_get_pte (addr) >> 28);
+	case sun4u:
+		/* FIXME: */ return 0;
 	default:
 		panic ("get_iospace called for unsupported cpu model\n");
 		return -1;
@@ -927,7 +919,7 @@
 #endif
 #ifdef SUN_FB_BWTWO
 	case FBTYPE_SUN2BW:
-		bwtwo_setup (&fbinfo [n], n, base, io);
+		bwtwo_setup (&fbinfo [n], n, base, io, sbdp);
 		break;
 #endif
 #ifdef SUN_FB_CGFOURTEEN
@@ -1082,13 +1074,13 @@
 			default_node = prom_pathtoinode(console_fb_path);
 			if (default_node) {
 				prom_printf ("Using %s for console\n", console_fb_path);
-				prom_console_node = prom_inst2pkg(*romvec->pv_v2bootargs.fd_stdout);
+				prom_console_node = prom_inst2pkg(prom_stdout);
 				if (prom_console_node == default_node)
 					prom_console_node = 0;
 			}
 		}
 		if (!default_node)
-			default_node = prom_inst2pkg(*romvec->pv_v2bootargs.fd_stdout);
+			default_node = prom_inst2pkg(prom_stdout);
 		propl = prom_getproperty(default_node, "device_type",
 					 prop, sizeof (prop));
 		if (propl < 0) {
diff -u --recursive --new-file v2.1.29/linux/drivers/sbus/char/sunfb.c linux/drivers/sbus/char/sunfb.c
--- v2.1.29/linux/drivers/sbus/char/sunfb.c	Sun Jan 26 02:07:18 1997
+++ linux/drivers/sbus/char/sunfb.c	Thu Mar 20 17:11:52 1997
@@ -1,4 +1,4 @@
-/* $Id: sunfb.c,v 1.20 1997/01/26 07:13:40 davem Exp $
+/* $Id: sunfb.c,v 1.21 1997/03/14 21:04:53 jj Exp $
  * sunfb.c: Sun generic frame buffer support.
  *
  * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -32,7 +32,6 @@
 #include <asm/sbus.h>
 #include <asm/fbio.h>
 #include <asm/io.h>
-#include <asm/pgtsun4c.h>	/* for the sun4c_nocache */
 
 #include "../../char/kbd_kern.h"
 #include "../../char/vt_kern.h"
diff -u --recursive --new-file v2.1.29/linux/drivers/sbus/char/sunkbd.c linux/drivers/sbus/char/sunkbd.c
--- v2.1.29/linux/drivers/sbus/char/sunkbd.c	Sun Jan 26 02:07:18 1997
+++ linux/drivers/sbus/char/sunkbd.c	Thu Mar 20 17:11:52 1997
@@ -96,6 +96,8 @@
 static struct kbd_struct * kbd = kbd_table;
 static struct tty_struct * tty = NULL;
 static int compose_led_on = 0;
+static int kbd_delay_ticks = HZ / 5;
+static int kbd_rate_ticks = HZ / 20;
 
 extern void compute_shiftstate(void);
 
@@ -393,8 +395,10 @@
 	/* Auto repeat: send regs = 0 to indicate autorepeat */
 	sunkbd_inchar (last_keycode, 0, 0);
 	del_timer (&auto_repeat_timer);
-	auto_repeat_timer.expires = jiffies + HZ/20;
-	add_timer (&auto_repeat_timer);
+	if (kbd_rate_ticks) {
+		auto_repeat_timer.expires = jiffies + kbd_rate_ticks;
+		add_timer (&auto_repeat_timer);
+	}
 	restore_flags(flags);
 }
 
@@ -470,8 +474,11 @@
 		clear_bit(keycode, key_down);
 	} else {
 		if (!norepeat_keys[keycode]) {
-			auto_repeat_timer.expires = jiffies+HZ/5;
-			add_timer (&auto_repeat_timer);
+			if (kbd_rate_ticks) {
+				auto_repeat_timer.expires =
+						jiffies + kbd_delay_ticks;
+				add_timer (&auto_repeat_timer);
+			}
 		}
 		rep = set_bit(keycode, key_down);
 	}
@@ -1177,6 +1184,7 @@
 static Firm_event kbd_queue [KBD_QSIZE];
 static int kbd_head, kbd_tail;
 char kbd_opened;
+static int kbd_active = 0;
 static struct wait_queue *kbd_wait;
 static struct fasync_struct *kb_fasync;
 
@@ -1311,6 +1319,41 @@
 	case KIOCGLED:
 		put_user_ret(vcleds_to_sunkbd(getleds()), (unsigned char *) arg, -EFAULT);
 		break;
+	case KIOCGRATE:
+	{
+		struct kbd_rate rate;
+
+		rate.delay = kbd_delay_ticks;
+		if (kbd_rate_ticks)
+			rate.rate = HZ / kbd_rate_ticks;
+		else
+			rate.rate = 0;
+
+		copy_to_user_ret((struct kbd_rate *)arg, &rate,
+				 sizeof(struct kbd_rate), -EFAULT);
+
+		return 0;
+	}
+	case KIOCSRATE:
+	{
+		struct kbd_rate rate;
+
+		if (verify_area(VERIFY_READ, (void *)arg,
+				sizeof(struct kbd_rate)))
+			return -EFAULT;
+		copy_from_user(&rate, (struct kbd_rate *)arg,
+			       sizeof(struct kbd_rate));
+
+		if (rate.rate > 50)
+			return -EINVAL;
+		if (rate.rate == 0)
+			kbd_rate_ticks = 0;
+		else
+			kbd_rate_ticks = HZ / rate.rate;
+		kbd_delay_ticks = rate.delay;
+
+		return 0;
+	}
 	case FIONREAD:		/* return number of bytes in kbd queue */
 	{
 		int count;
@@ -1329,8 +1372,11 @@
 static int
 kbd_open (struct inode *i, struct file *f)
 {
+	kbd_active++;
+
 	if (kbd_opened)
 		return 0;
+
 	kbd_opened = fg_console + 1;
 	kbd_head = kbd_tail = 0;
 	return 0;
@@ -1339,8 +1385,12 @@
 static void
 kbd_close (struct inode *i, struct file *f)
 {
+	if (--kbd_active)
+		return;
+
 	if (kbd_redirected)
 		kbd_table [kbd_opened-1].kbdmode = VC_XLATE;
+
 	kbd_redirected = 0;
 	kbd_opened = 0;
 
diff -u --recursive --new-file v2.1.29/linux/drivers/sbus/dvma.c linux/drivers/sbus/dvma.c
--- v2.1.29/linux/drivers/sbus/dvma.c	Mon Mar 17 14:54:29 1997
+++ linux/drivers/sbus/dvma.c	Thu Mar 20 17:11:52 1997
@@ -8,24 +8,19 @@
 #include <linux/init.h>
 
 #include <asm/oplib.h>
-#include <asm/contregs.h>
-#include <asm/sysen.h>
 #include <asm/delay.h>
-#include <asm/machines.h>
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <asm/sbus.h>
-#include <asm/vac-ops.h>
-#include <asm/vaddrs.h>
 
 struct Linux_SBus_DMA *dma_chain;
 
 /* Print out the current values in the DMA control registers */
-void
+static __inline__ void
 dump_dma_regs(struct sparc_dma_registers *dregs)
 {
 	printk("DMA CONTROL<%08lx>  ADDR<%08lx> CNT<%08lx> TEST<%08lx>\n",
-	       dregs->cond_reg,
+	       (unsigned long) dregs->cond_reg,
 	       (unsigned long) dregs->st_addr,
 	       (unsigned long) dregs->cnt,
 	       (unsigned long) dregs->dma_test);
diff -u --recursive --new-file v2.1.29/linux/drivers/sbus/sbus.c linux/drivers/sbus/sbus.c
--- v2.1.29/linux/drivers/sbus/sbus.c	Mon Mar 17 14:54:29 1997
+++ linux/drivers/sbus/sbus.c	Thu Mar 20 17:11:52 1997
@@ -149,9 +149,6 @@
 #ifdef CONFIG_SUN_MOSTEK_RTC
 extern int rtc_init(void);
 #endif
-#ifdef CONFIG_SPARCAUDIO
-extern int sparcaudio_init(void);
-#endif
 
 __initfunc(static unsigned long 
 sbus_do_child_siblings(unsigned long memory_start, int start_node,
@@ -338,9 +335,6 @@
 #endif
 #ifdef CONFIG_SUN_MOSTEK_RTC
 	rtc_init();
-#endif
-#ifdef CONFIG_SPARCAUDIO
-	sparcaudio_init();
 #endif
 #ifdef CONFIG_SUN_BPP
 	bpp_init();
diff -u --recursive --new-file v2.1.29/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c
--- v2.1.29/linux/drivers/scsi/BusLogic.c	Tue Mar  4 10:25:23 1997
+++ linux/drivers/scsi/BusLogic.c	Mon Mar 17 00:11:00 1997
@@ -27,8 +27,8 @@
 */
 
 
-#define BusLogic_DriverVersion		"2.0.7"
-#define BusLogic_DriverDate		"23 February 1997"
+#define BusLogic_DriverVersion		"2.0.8"
+#define BusLogic_DriverDate		"17 March 1997"
 
 
 #include <linux/module.h>
@@ -1086,9 +1086,9 @@
   interrogating the PCI Configuration Space on PCI machines as well as from the
   list of standard BusLogic MultiMaster ISA I/O Addresses.  By default, if both
   FlashPoint and PCI MultiMaster Host Adapters are present, this driver will
-  probe for PCI MultiMaster Host Adapters first unless the BIOS primary disk is
-  not controlled by the first PCI MultiMaster Host Adapter, in which case
-  FlashPoint Host Adapters will be probed first.  The Kernel Command Line 
+  probe for FlashPoint Host Adapters first unless the BIOS primary disk is
+  controlled by the first PCI MultiMaster Host Adapter, in which case
+  MultiMaster Host Adapters will be probed first.  The Kernel Command Line
   options "MultiMasterFirst" and "FlashPointFirst" can be used to force a
   particular probe order.
 */
@@ -1118,11 +1118,12 @@
 	}
       else
 	{
-	  int PCIMultiMasterCount = BusLogic_InitializeMultiMasterProbeInfo();
 	  int FlashPointCount = BusLogic_InitializeFlashPointProbeInfo();
-	  if (PCIMultiMasterCount > 0 && FlashPointCount > 0)
+	  int PCIMultiMasterCount = BusLogic_InitializeMultiMasterProbeInfo();
+	  if (FlashPointCount > 0 && PCIMultiMasterCount > 0)
 	    {
-	      BusLogic_ProbeInfo_T *ProbeInfo = &BusLogic_ProbeInfoList[0];
+	      BusLogic_ProbeInfo_T *ProbeInfo =
+		&BusLogic_ProbeInfoList[FlashPointCount];
 	      BusLogic_HostAdapter_T HostAdapterPrototype;
 	      BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype;
 	      BusLogic_FetchHostAdapterLocalRAMRequest_T
@@ -1142,27 +1143,26 @@
 			       &Drive0MapByte, sizeof(Drive0MapByte));
 	      /*
 		If the Map Byte for BIOS Drive 0 indicates that BIOS Drive 0
-		is not controlled by this PCI MultiMaster Host Adapter, then
-		reverse the probe order so that FlashPoint Host Adapters are
-		probed before MultiMaster Host Adapters.
+		is controlled by this PCI MultiMaster Host Adapter, then
+		reverse the probe order so that MultiMaster Host Adapters are
+		probed before FlashPoint Host Adapters.
 	      */
-	      if (Drive0MapByte.DiskGeometry ==
+	      if (Drive0MapByte.DiskGeometry !=
 		  BusLogic_BIOS_Disk_Not_Installed)
 		{
 		  BusLogic_ProbeInfo_T
 		    SavedProbeInfo[BusLogic_MaxHostAdapters];
 		  int MultiMasterCount =
 		    BusLogic_ProbeInfoCount - FlashPointCount;
-		  memcpy(&SavedProbeInfo[0],
-			 &BusLogic_ProbeInfoList[0],
-			 BusLogic_ProbeInfoCount
-			 * sizeof(BusLogic_ProbeInfo_T));
+		  memcpy(SavedProbeInfo,
+			 BusLogic_ProbeInfoList,
+			 sizeof(BusLogic_ProbeInfoList));
 		  memcpy(&BusLogic_ProbeInfoList[0],
-			 &SavedProbeInfo[MultiMasterCount],
-			 FlashPointCount * sizeof(BusLogic_ProbeInfo_T));
-		  memcpy(&BusLogic_ProbeInfoList[FlashPointCount],
-			 &SavedProbeInfo[0],
+			 &SavedProbeInfo[FlashPointCount],
 			 MultiMasterCount * sizeof(BusLogic_ProbeInfo_T));
+		  memcpy(&BusLogic_ProbeInfoList[MultiMasterCount],
+			 &SavedProbeInfo[0],
+			 FlashPointCount * sizeof(BusLogic_ProbeInfo_T));
 		}
 	    }
 	}
@@ -1223,9 +1223,14 @@
 	    FlashPointInfo->Present))
 	{
 	  scsi_init_free((char *) FlashPointInfo, sizeof(FlashPoint_Info_T));
-	  if (BusLogic_GlobalOptions.Bits.TraceProbe)
-	    BusLogic_Notice("BusLogic_Probe(0x%X): FlashPoint Not Found\n",
-			    HostAdapter, HostAdapter->IO_Address);
+	  BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at "
+			 "PCI Bus %d Device %d\n", HostAdapter,
+			 HostAdapter->Bus, HostAdapter->Device);
+	  BusLogic_Error("BusLogic: I/O Address 0x%X PCI Address 0x%X, "
+			 "but FlashPoint\n", HostAdapter,
+			 HostAdapter->IO_Address, HostAdapter->PCI_Address);
+	  BusLogic_Error("BusLogic: Probe Function failed to validate it.\n",
+			 HostAdapter);
 	  return false;
 	}
       HostAdapter->FlashPointInfo = FlashPointInfo;
@@ -3006,6 +3011,7 @@
 			  .BusDeviceResetsCompleted);
 	  HostAdapter->CommandsSinceReset[TargetID] = 0;
 	  HostAdapter->TaggedQueuingActive[TargetID] = false;
+	  HostAdapter->LastResetCompleted[TargetID] = jiffies;
 	  /*
 	    Place CCB back on the Host Adapter's free list.
 	  */
@@ -3325,9 +3331,13 @@
       int Segment;
       CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather;
       CCB->DataLength = SegmentCount * sizeof(BusLogic_ScatterGatherSegment_T);
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
       if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
 	CCB->DataPointer = Virtual_to_Bus(CCB->ScatterGatherList);
       else CCB->DataPointer = (BusLogic_BusAddress_T) CCB->ScatterGatherList;
+#else
+      CCB->DataPointer = Virtual_to_Bus(CCB->ScatterGatherList);
+#endif
       for (Segment = 0; Segment < SegmentCount; Segment++)
 	{
 	  CCB->ScatterGatherList[Segment].SegmentByteCount =
@@ -3739,7 +3749,10 @@
 	  }
       }
   for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
-    HostAdapter->LastResetTime[TargetID] = jiffies;
+    {
+      HostAdapter->LastResetAttempted[TargetID] = jiffies;
+      HostAdapter->LastResetCompleted[TargetID] = jiffies;
+    }
   Result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
   /*
     Release exclusive access to Host Adapter.
@@ -3907,7 +3920,7 @@
   BusLogic_IncrementErrorCounter(
     &HostAdapter->TargetDeviceStatistics[TargetID].BusDeviceResetsAttempted);
   HostAdapter->BusDeviceResetPendingCCB[TargetID] = CCB;
-  HostAdapter->LastResetTime[TargetID] = jiffies;
+  HostAdapter->LastResetAttempted[TargetID] = jiffies;
   for (XCCB = HostAdapter->All_CCBs; XCCB != NULL; XCCB = XCCB->NextAll)
     if (XCCB->Status == BusLogic_CCB_Active && XCCB->TargetID == TargetID)
       XCCB->Status = BusLogic_CCB_Reset;
@@ -3955,7 +3968,7 @@
     the system was initialized if no prior resets have occurred.
   */
   if (HostAdapter->TaggedQueuingActive[TargetID] &&
-      jiffies - HostAdapter->LastResetTime[TargetID] < 10*60*HZ)
+      jiffies - HostAdapter->LastResetCompleted[TargetID] < 10*60*HZ)
     {
       HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID);
       HostAdapter->TaggedQueuingActive[TargetID] = false;
@@ -3980,7 +3993,7 @@
 	clear the error condition.
       */
       if (HostAdapter->CommandSuccessfulFlag[TargetID] ||
-	  jiffies - HostAdapter->LastResetTime[TargetID] < HZ/10)
+	  jiffies - HostAdapter->LastResetAttempted[TargetID] < HZ/10)
 	{
 	  HostAdapter->CommandSuccessfulFlag[TargetID] = false;
 	  return BusLogic_SendBusDeviceReset(HostAdapter, Command, ResetFlags);
@@ -4428,19 +4441,19 @@
 
   MultiMasterFirst	By default, if both FlashPoint and PCI MultiMaster
 			Host Adapters are present, this driver will probe for
-			PCI MultiMaster Host Adapters first unless the BIOS
-			primary disk is not controlled by the first PCI
-			MultiMaster Host Adapter, in which case FlashPoint
-			Host Adapters will be probed first.  This option
-			forces MultiMaster Host Adapters to be probed first.
+			FlashPoint Host Adapters first unless the BIOS primary
+			disk is controlled by the first PCI MultiMaster Host
+			Adapter, in which case MultiMaster Host Adapters will
+			be probed first.  This option forces MultiMaster Host
+			Adapters to be probed first.
 
   FlashPointFirst	By default, if both FlashPoint and PCI MultiMaster
 			Host Adapters are present, this driver will probe for
-			PCI MultiMaster Host Adapters first unless the BIOS
-			primary disk is not controlled by the first PCI
-			MultiMaster Host Adapter, in which case FlashPoint
-			Host Adapters will be probed first.  This option
-			forces FlashPoint Host Adapters to be probed first.
+			FlashPoint Host Adapters first unless the BIOS primary
+			disk is controlled by the first PCI MultiMaster Host
+			Adapter, in which case MultiMaster Host Adapters will
+			be probed first.  This option forces FlashPoint Host
+			Adapters to be probed first.
 
   Debug			Sets all the tracing bits in BusLogic_GlobalOptions.
 
diff -u --recursive --new-file v2.1.29/linux/drivers/scsi/BusLogic.h linux/drivers/scsi/BusLogic.h
--- v2.1.29/linux/drivers/scsi/BusLogic.h	Tue Mar  4 10:25:24 1997
+++ linux/drivers/scsi/BusLogic.h	Mon Mar 17 00:11:00 1997
@@ -1346,7 +1346,8 @@
   unsigned char ActiveCommands[BusLogic_MaxTargetDevices];
   unsigned int CommandsSinceReset[BusLogic_MaxTargetDevices];
   unsigned long LastSequencePoint[BusLogic_MaxTargetDevices];
-  unsigned long LastResetTime[BusLogic_MaxTargetDevices];
+  unsigned long LastResetAttempted[BusLogic_MaxTargetDevices];
+  unsigned long LastResetCompleted[BusLogic_MaxTargetDevices];
   BusLogic_OutgoingMailbox_T *FirstOutgoingMailbox;
   BusLogic_OutgoingMailbox_T *LastOutgoingMailbox;
   BusLogic_OutgoingMailbox_T *NextOutgoingMailbox;
@@ -1604,7 +1605,20 @@
 
 #undef CONFIG_SCSI_OMIT_FLASHPOINT
 #define CONFIG_SCSI_OMIT_FLASHPOINT
+
 #define BusLogic_InitializeProbeInfoListISA BusLogic_InitializeProbeInfoList
+
+#endif
+
+
+/*
+  FlashPoint support is only available for the Intel x86 Architecture.
+*/
+
+#ifndef __i386__
+
+#undef CONFIG_SCSI_OMIT_FLASHPOINT
+#define CONFIG_SCSI_OMIT_FLASHPOINT
 
 #endif
 
diff -u --recursive --new-file v2.1.29/linux/drivers/scsi/README.BusLogic linux/drivers/scsi/README.BusLogic
--- v2.1.29/linux/drivers/scsi/README.BusLogic	Tue Mar  4 10:25:24 1997
+++ linux/drivers/scsi/README.BusLogic	Mon Mar 17 00:11:00 1997
@@ -1,10 +1,10 @@
 	   BusLogic MultiMaster and FlashPoint SCSI Driver for Linux
 
-			  Version 2.0.7 for Linux 2.0
+			  Version 2.0.8 for Linux 2.0
 
 			      PRODUCTION RELEASE
 
-			       23 February 1997
+				 17 March 1997
 
 			       Leonard N. Zubkoff
 			       Dandelion Digital
@@ -319,17 +319,17 @@
   "BusLogic=MultiMasterFirst"
 
     By default, if both FlashPoint and PCI MultiMaster Host Adapters are
-    present, this driver will probe for PCI MultiMaster Host Adapters first
-    unless the BIOS primary disk is not controlled by the first PCI MultiMaster
-    Host Adapter, in which case FlashPoint Host Adapters will be probed first.
+    present, this driver will probe for FlashPoint Host Adapters first unless
+    the BIOS primary disk is controlled by the first PCI MultiMaster Host
+    Adapter, in which case MultiMaster Host Adapters will be probed first.
     This option forces MultiMaster Host Adapters to be probed first.
 
   "BusLogic=FlashPointFirst"
 
     By default, if both FlashPoint and PCI MultiMaster Host Adapters are
-    present, this driver will probe for PCI MultiMaster Host Adapters first
-    unless the BIOS primary disk is not controlled by the first PCI MultiMaster
-    Host Adapter, in which case FlashPoint Host Adapters will be probed first.
+    present, this driver will probe for FlashPoint Host Adapters first unless
+    the BIOS primary disk is controlled by the first PCI MultiMaster Host
+    Adapter, in which case MultiMaster Host Adapters will be probed first.
     This option forces FlashPoint Host Adapters to be probed first.
 
   "BusLogic=0x330"
@@ -380,7 +380,7 @@
 replacing "/usr/src" with wherever you keep your Linux kernel source tree:
 
   cd /usr/src
-  tar -xvzf BusLogic-2.0.7.tar.gz
+  tar -xvzf BusLogic-2.0.8.tar.gz
   mv README.* LICENSE.* BusLogic.[ch] FlashPoint.c linux/drivers/scsi
   patch -p < BusLogic.patch
   cd linux
diff -u --recursive --new-file v2.1.29/linux/drivers/scsi/aha1740.c linux/drivers/scsi/aha1740.c
--- v2.1.29/linux/drivers/scsi/aha1740.c	Fri Dec 27 02:03:23 1996
+++ linux/drivers/scsi/aha1740.c	Wed Mar 12 09:01:52 1997
@@ -13,6 +13,11 @@
  *  for proper handling of multiple devices courteously
  *  provided by Michael Weller, March, 1993
  *
+ *  Multiple adapter support, extended translation detection,
+ *  update to current scsi subsystem changes, proc fs support,
+ *  working (!) module support based on patches from Andreas Arens,
+ *  by Andreas Degert <ad@papyrus.hamburg.com>, 2/1997
+ *
  * aha1740_makecode may still need even more work
  * if it doesn't work for your devices, take a look.
  */
@@ -59,12 +64,52 @@
 static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/aha1740.c,v 1.1 1992/07/24 06:27:38 root Exp root $";
 */
 
-static unsigned int slot, base;
-static unsigned char irq_level;
+struct aha1740_hostdata {
+    unsigned int slot;
+    unsigned int translation;
+    unsigned int last_ecb_used;
+    struct ecb ecb[AHA1740_ECBS];
+};
+
+#define HOSTDATA(host) ((struct aha1740_hostdata *) &host->hostdata)
 
-static struct ecb ecb[AHA1740_ECBS];	/* One for each queued operation */
+/* One for each IRQ level (9-15) */
+static struct Scsi_Host * aha_host[8] = {NULL, };
+
+int aha1740_proc_info(char *buffer, char **start, off_t offset,
+		      int length, int hostno, int inout)
+{
+    int len;
+    struct Scsi_Host * shpnt;
+    struct aha1740_hostdata *host;
+
+    if (inout)
+	return(-ENOSYS);
+
+    for (len = 0; len < 8; len++) {
+	shpnt = aha_host[len];
+	if (shpnt && shpnt->host_no == hostno)
+	    break;
+    }
+    host = HOSTDATA(shpnt);
+
+    len = sprintf(buffer, "aha174x at IO:%x, IRQ %d, SLOT %d.\n"
+		  "Extended translation %sabled.\n",
+		  shpnt->io_port, shpnt->irq, host->slot,
+		  host->translation ? "en" : "dis");
+
+    if (offset > len) {
+	*start = buffer;
+	return 0;
+    }
+
+    *start = buffer + offset;
+    len -= offset;
+    if (len > length)
+	len = length;
+    return len;
+}
 
-static int aha1740_last_ecb_used  = 0;	/* optimization */
 
 int aha1740_makecode(unchar *sense, unchar *status)
 {
@@ -88,8 +133,9 @@
 
     status_word = * (struct statusword *) status;
 #ifdef DEBUG
-printk("makecode from %x,%x,%x,%x %x,%x,%x,%x",status[0],status[1],status[2],status[3],
-sense[0],sense[1],sense[2],sense[3]);
+    printk("makecode from %x,%x,%x,%x %x,%x,%x,%x",
+	   status[0], status[1], status[2], status[3],
+	   sense[0], sense[1], sense[2], sense[3]);
 #endif
     if (!status_word.don) /* Anything abnormal was detected */
     {
@@ -113,8 +159,8 @@
 		break;
 	    case 0x04:
 	    case 0x05:
-		retval=DID_ABORT; /* Either by this driver or the AHA1740
-					 itself */
+		retval=DID_ABORT;
+		/* Either by this driver or the AHA1740 itself */
 		break;
 	    default:
 		retval=DID_ERROR; /* No further diagnostics possible */
@@ -141,35 +187,35 @@
     return status[3] | retval << 16;
 }
 
-int aha1740_test_port(void)
+int aha1740_test_port(unsigned int base)
 {
-    char    name[4],tmp;
+    char name[4], tmp;
 
     /* Okay, look for the EISA ID's */
-    name[0]= 'A' -1 + ((tmp = inb(HID0)) >> 2); /* First character */
+    name[0]= 'A' -1 + ((tmp = inb(HID0(base))) >> 2); /* First character */
     name[1]= 'A' -1 + ((tmp & 3) << 3);
-    name[1]+= ((tmp = inb(HID1)) >> 5)&0x7;	/* Second Character */
+    name[1]+= ((tmp = inb(HID1(base))) >> 5)&0x7;	/* Second Character */
     name[2]= 'A' -1 + (tmp & 0x1f);		/* Third Character */
     name[3]=0;
-    tmp = inb(HID2);
-    if ( strcmp ( name, HID_MFG ) || inb(HID2) != HID_PRD )
+    tmp = inb(HID2(base));
+    if ( strcmp ( name, HID_MFG ) || inb(HID2(base)) != HID_PRD )
 	return 0;   /* Not an Adaptec 174x */
 
-/*  if ( inb(HID3) != HID_REV )
-	printk("aha1740: Warning; board revision of %d; expected %d\n",
-	    inb(HID3),HID_REV); */
+/*  if ( inb(HID3(base)) != HID_REV )
+	printk("aha174x: Warning; board revision of %d; expected %d\n",
+	    inb(HID3(base)),HID_REV); */
 
-    if ( inb(EBCNTRL) != EBCNTRL_VALUE )
+    if ( inb(EBCNTRL(base)) != EBCNTRL_VALUE )
     {
-	printk("aha1740: Board detected, but EBCNTRL = %x, so disabled it.\n",
-	    inb(EBCNTRL));
+	printk("aha174x: Board detected, but EBCNTRL = %x, so disabled it.\n",
+	    inb(EBCNTRL(base)));
 	return 0;
     }
 
-    if ( inb(PORTADR) & PORTADDR_ENH )
+    if ( inb(PORTADR(base)) & PORTADDR_ENH )
 	return 1;   /* Okay, we're all set */
 	
-    printk("aha1740: Board detected, but not in enhanced mode, so disabled it.\n");
+    printk("aha174x: Board detected, but not in enhanced mode, so disabled it.\n");
     return 0;
 }
 
@@ -181,49 +227,58 @@
     int number_serviced;
     struct ecb *ecbptr;
     Scsi_Cmnd *SCtmp;
+    unsigned int base;
 
+    if (!aha_host[irq - 9])
+	panic("aha1740.c: Irq from unknown host!\n");
+    base = aha_host[irq - 9]->io_port;
     number_serviced = 0;
 
-    while(inb(G2STAT) & G2STAT_INTPEND)
+    while(inb(G2STAT(base)) & G2STAT_INTPEND)
     {
 	DEB(printk("aha1740_intr top of loop.\n"));
-	adapstat = inb(G2INTST);
-	ecbptr = (struct ecb *) bus_to_virt(inl(MBOXIN0));
-	outb(G2CNTRL_IRST,G2CNTRL); /* interrupt reset */
+	adapstat = inb(G2INTST(base));
+	ecbptr = (struct ecb *) bus_to_virt(inl(MBOXIN0(base)));
+	outb(G2CNTRL_IRST,G2CNTRL(base)); /* interrupt reset */
       
 	switch ( adapstat & G2INTST_MASK )
 	{
 	case	G2INTST_CCBRETRY:
 	case	G2INTST_CCBERROR:
 	case	G2INTST_CCBGOOD:
-	    outb(G2CNTRL_HRDY,G2CNTRL); /* Host Ready -> Mailbox in complete */
+	    /* Host Ready -> Mailbox in complete */
+	    outb(G2CNTRL_HRDY,G2CNTRL(base));
 	    if (!ecbptr)
 	    {
 		printk("Aha1740 null ecbptr in interrupt (%x,%x,%x,%d)\n",
-			inb(G2STAT),adapstat,inb(G2INTST),number_serviced++);
+		       inb(G2STAT(base)),adapstat,
+		       inb(G2INTST(base)), number_serviced++);
 		continue;
 	    }
 	    SCtmp = ecbptr->SCpnt;
 	    if (!SCtmp)
 	    {
 		printk("Aha1740 null SCtmp in interrupt (%x,%x,%x,%d)\n",
-			inb(G2STAT),adapstat,inb(G2INTST),number_serviced++);
+		       inb(G2STAT(base)),adapstat,
+		       inb(G2INTST(base)), number_serviced++);
 		continue;
 	    }
 	    if (SCtmp->host_scribble)
 		scsi_free(SCtmp->host_scribble, 512);
-	  /* Fetch the sense data, and tuck it away, in the required slot.  The
-	     Adaptec automatically fetches it, and there is no guarantee that
-	     we will still have it in the cdb when we come back */
+	    /* Fetch the sense data, and tuck it away, in the required slot.
+	       The Adaptec automatically fetches it, and there is no
+	       guarantee that we will still have it in the cdb when we come
+	       back */
 	    if ( (adapstat & G2INTST_MASK) == G2INTST_CCBERROR )
-	      {
+	    {
 		memcpy(SCtmp->sense_buffer, ecbptr->sense, 
 		       sizeof(SCtmp->sense_buffer));
 		errstatus = aha1740_makecode(ecbptr->sense,ecbptr->status);
-	      }
+	    }
 	    else
 		errstatus = 0;
-	    DEB(if (errstatus) printk("aha1740_intr_handle: returning %6x\n", errstatus));
+	    DEB(if (errstatus) printk("aha1740_intr_handle: returning %6x\n",
+				      errstatus));
 	    SCtmp->result = errstatus;
 	    my_done = ecbptr->done;
 	    memset(ecbptr,0,sizeof(struct ecb)); 
@@ -231,12 +286,14 @@
 		my_done(SCtmp);
 	    break;
 	case	G2INTST_HARDFAIL:
-	    printk("aha1740 hardware failure!\n");
+	    printk(KERN_ALERT "aha1740 hardware failure!\n");
 	    panic("aha1740.c");	/* Goodbye */
 	case	G2INTST_ASNEVENT:
-	    printk("aha1740 asynchronous event: %02x %02x %02x %02x %02x\n",adapstat,
-		inb(MBOXIN0),inb(MBOXIN1),inb(MBOXIN2),inb(MBOXIN3)); /* Say What? */
-	    outb(G2CNTRL_HRDY,G2CNTRL); /* Host Ready -> Mailbox in complete */
+	    printk("aha1740 asynchronous event: %02x %02x %02x %02x %02x\n",
+		   adapstat, inb(MBOXIN0(base)), inb(MBOXIN1(base)),
+		   inb(MBOXIN2(base)), inb(MBOXIN3(base))); /* Say What? */
+	    /* Host Ready -> Mailbox in complete */
+	    outb(G2CNTRL_HRDY,G2CNTRL(base));
 	    break;
 	case	G2INTST_CMDGOOD:
 	    /* set immediate command success flag here: */
@@ -245,7 +302,7 @@
 	    /* Set immediate command failure flag here: */
 	    break;
 	}
-      number_serviced++;
+	number_serviced++;
     }
 }
 
@@ -254,18 +311,19 @@
     unchar direction;
     unchar *cmd = (unchar *) SCpnt->cmnd;
     unchar target = SCpnt->target;
+    struct aha1740_hostdata *host = HOSTDATA(SCpnt->host);
     unsigned long flags;
     void *buff = SCpnt->request_buffer;
     int bufflen = SCpnt->request_bufflen;
     int ecbno;
     DEB(int i);
 
-    
     if(*cmd == REQUEST_SENSE)
     {
 	if (bufflen != sizeof(SCpnt->sense_buffer))
 	{
-	    printk("Wrong buffer length supplied for request sense (%d)\n",bufflen);
+	    printk("Wrong buffer length supplied for request sense (%d)\n",
+		   bufflen);
 	}
 	SCpnt->result = 0;
 	done(SCpnt); 
@@ -279,39 +337,41 @@
 	i = scsi2int(cmd+2);
     else
 	i = -1;
-    printk("aha1740_queuecommand: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen);
+    printk("aha1740_queuecommand: dev %d cmd %02x pos %d len %d ",
+	   target, *cmd, i, bufflen);
     printk("scsi cmd:");
     for (i = 0; i < SCpnt->cmd_len; i++) printk("%02x ", cmd[i]);
     printk("\n");
 #endif
 
     /* locate an available ecb */
-
     save_flags(flags);
     cli();
-    ecbno = aha1740_last_ecb_used + 1;		/* An optimization */
-    if (ecbno >= AHA1740_ECBS) ecbno = 0;
-
-    do{
-      if( ! ecb[ecbno].cmdw )
-	break;
-      ecbno++;
-      if (ecbno >= AHA1740_ECBS ) ecbno = 0;
-    } while (ecbno != aha1740_last_ecb_used);
+    ecbno = host->last_ecb_used + 1;		/* An optimization */
+    if (ecbno >= AHA1740_ECBS)
+	ecbno = 0;
+    do {
+	if (!host->ecb[ecbno].cmdw)
+	    break;
+	ecbno++;
+	if (ecbno >= AHA1740_ECBS)
+	    ecbno = 0;
+    } while (ecbno != host->last_ecb_used);
 
-    if( ecb[ecbno].cmdw )
-      panic("Unable to find empty ecb for aha1740.\n");
+    if (host->ecb[ecbno].cmdw)
+	panic("Unable to find empty ecb for aha1740.\n");
 
-    ecb[ecbno].cmdw = AHA1740CMD_INIT;	/* SCSI Initiator Command doubles as reserved flag */
+    host->ecb[ecbno].cmdw = AHA1740CMD_INIT;	/* SCSI Initiator Command
+						   doubles as reserved flag */
 
-    aha1740_last_ecb_used = ecbno;    
+    host->last_ecb_used = ecbno;    
     restore_flags(flags);
 
 #ifdef DEBUG
-    printk("Sending command (%d %x)...",ecbno, done);
+    printk("Sending command (%d %x)...", ecbno, done);
 #endif
 
-    ecb[ecbno].cdblen = SCpnt->cmd_len;	/* SCSI Command Descriptor Block Length */
+    host->ecb[ecbno].cdblen = SCpnt->cmd_len;	/* SCSI Command Descriptor Block Length */
 
     direction = 0;
     if (*cmd == READ_10 || *cmd == READ_6)
@@ -319,17 +379,16 @@
     else if (*cmd == WRITE_10 || *cmd == WRITE_6)
 	direction = 0;
 
-    memcpy(ecb[ecbno].cdb, cmd, ecb[ecbno].cdblen);
+    memcpy(host->ecb[ecbno].cdb, cmd, SCpnt->cmd_len);
 
     if (SCpnt->use_sg)
     {
 	struct scatterlist * sgpnt;
 	struct aha1740_chain * cptr;
 	int i;
-#ifdef DEBUG
-	unsigned char * ptr;
-#endif
-	ecb[ecbno].sg = 1;	  /* SCSI Initiator Command  w/scatter-gather*/
+	DEB(unsigned char * ptr);
+
+	host->ecb[ecbno].sg = 1;  /* SCSI Initiator Command  w/scatter-gather*/
 	SCpnt->host_scribble = (unsigned char *) scsi_malloc(512);
 	sgpnt = (struct scatterlist *) SCpnt->request_buffer;
 	cptr = (struct aha1740_chain *) SCpnt->host_scribble; 
@@ -339,8 +398,8 @@
 	    cptr[i].datalen = sgpnt[i].length;
 	    cptr[i].dataptr = virt_to_bus(sgpnt[i].address);
 	}
-	ecb[ecbno].datalen = SCpnt->use_sg * sizeof(struct aha1740_chain);
-	ecb[ecbno].dataptr = virt_to_bus(cptr);
+	host->ecb[ecbno].datalen = SCpnt->use_sg * sizeof(struct aha1740_chain);
+	host->ecb[ecbno].dataptr = virt_to_bus(cptr);
 #ifdef DEBUG
 	printk("cptr %x: ",cptr);
 	ptr = (unsigned char *) cptr;
@@ -350,144 +409,161 @@
     else
     {
 	SCpnt->host_scribble = NULL;
-	ecb[ecbno].datalen = bufflen;
-	ecb[ecbno].dataptr = virt_to_bus(buff);
+	host->ecb[ecbno].datalen = bufflen;
+	host->ecb[ecbno].dataptr = virt_to_bus(buff);
     }
-    ecb[ecbno].lun = SCpnt->lun;
-    ecb[ecbno].ses = 1;	/* Suppress underrun errors */
-    ecb[ecbno].dir= direction;
-    ecb[ecbno].ars=1;  /* Yes, get the sense on an error */
-    ecb[ecbno].senselen = 12;
-    ecb[ecbno].senseptr = virt_to_bus(ecb[ecbno].sense);
-    ecb[ecbno].statusptr = virt_to_bus(ecb[ecbno].status);
-    ecb[ecbno].done = done;
-    ecb[ecbno].SCpnt = SCpnt;
+    host->ecb[ecbno].lun = SCpnt->lun;
+    host->ecb[ecbno].ses = 1;	/* Suppress underrun errors */
+    host->ecb[ecbno].dir = direction;
+    host->ecb[ecbno].ars = 1;  /* Yes, get the sense on an error */
+    host->ecb[ecbno].senselen = 12;
+    host->ecb[ecbno].senseptr = virt_to_bus(host->ecb[ecbno].sense);
+    host->ecb[ecbno].statusptr = virt_to_bus(host->ecb[ecbno].status);
+    host->ecb[ecbno].done = done;
+    host->ecb[ecbno].SCpnt = SCpnt;
 #ifdef DEBUG
     {
 	int i;
 	printk("aha1740_command: sending.. ");
-	for (i = 0; i < sizeof(ecb[ecbno])-10; i++)
-	    printk("%02x ", ((unchar *)&ecb[ecbno])[i]);
+	for (i = 0; i < sizeof(host->ecb[ecbno]) - 10; i++)
+	    printk("%02x ", ((unchar *)&host->ecb[ecbno])[i]);
     }
     printk("\n");
 #endif
     if (done)
-    { /*  You may question the code below, which contains potentially
-	  non-terminating while loops with interrupts disabled.  So did
-	  I when I wrote it, but the Adaptec Spec says the card is so fast,
-	  that this problem virtually never occurs so I've kept it.  We
-	  do printk a warning first, so that you'll know if it happens.
-	  In practice the only time we've seen this message is when some-
-	  thing else is in the driver was broken, like _makecode(), or
-	  when a scsi device hung the scsi bus.  Even under these conditions,
-	  The loop actually only cycled < 3 times (we instrumented it). */
-
+    { /*  The Adaptec Spec says the card is so fast that the loops will
+	  only be executed once in the code below. Even if this was true
+	  with the fastest processors when the spec was written, it doesn't
+	  seem to be true with todays fast processors. We print a warning
+	  if the code is executed more often than LOOPCNT_WARN. If this
+	  happens, it should be investigated. If the count reaches
+	  LOOPCNT_MAX, we assume something is broken; since there is no
+	  way to return an error (the return value is ignored by the
+	  mid-level scsi layer) we have to panic (and maybe that's the
+	  best thing we can do then anyhow). */
+
+#define LOOPCNT_WARN 10		/* excessive mbxout wait -> syslog-msg */
+#define LOOPCNT_MAX 1000000	/* mbxout deadlock -> panic() after ~ 2 sec. */
+	int loopcnt;
+	unsigned int base = SCpnt->host->io_port;
 	DEB(printk("aha1740[%d] critical section\n",ecbno));
 	save_flags(flags);
 	cli();
-	if ( ! (inb(G2STAT) & G2STAT_MBXOUT) )
-	{
-	    printk("aha1740[%d]_mbxout wait!\n",ecbno);
-	    cli(); /* printk may have done a sti()! */
+	for (loopcnt = 0; ; loopcnt++) {
+	    if (inb(G2STAT(base)) & G2STAT_MBXOUT) break;
+	    if (loopcnt == LOOPCNT_WARN) {
+		printk("aha1740[%d]_mbxout wait!\n",ecbno);
+		cli(); /* printk may have done a sti()! */
+	    }
+	    if (loopcnt == LOOPCNT_MAX)
+		panic("aha1740.c: mbxout busy!\n");
 	}
-	mb();
-	while ( ! (inb(G2STAT) & G2STAT_MBXOUT) );	/* Oh Well. */
-	outl(virt_to_bus(ecb+ecbno), MBOXOUT0);
-	if ( inb(G2STAT) & G2STAT_BUSY )
-	{
-	    printk("aha1740[%d]_attn wait!\n",ecbno);
-	    cli();
+	outl(virt_to_bus(host->ecb + ecbno), MBOXOUT0(base));
+	for (loopcnt = 0; ; loopcnt++) {
+	    if (! (inb(G2STAT(base)) & G2STAT_BUSY)) break;
+	    if (loopcnt == LOOPCNT_WARN) {
+		printk("aha1740[%d]_attn wait!\n",ecbno);
+		cli();
+	    }
+	    if (loopcnt == LOOPCNT_MAX)
+		panic("aha1740.c: attn wait failed!\n");
 	}
-	while ( inb(G2STAT) & G2STAT_BUSY );		/* And Again! */
-	outb(ATTN_START | (target & 7), ATTN);	/* Start it up */
+	outb(ATTN_START | (target & 7), ATTN(base)); /* Start it up */
 	restore_flags(flags);
 	DEB(printk("aha1740[%d] request queued.\n",ecbno));
     }
     else
-      printk("aha1740_queuecommand: done can't be NULL\n");
-    
+	printk(KERN_ALERT "aha1740_queuecommand: done can't be NULL\n");
     return 0;
 }
 
-static volatile int internal_done_flag = 0;
-static volatile int internal_done_errcode = 0;
-
 static void internal_done(Scsi_Cmnd * SCpnt)
 {
-    internal_done_errcode = SCpnt->result;
-    ++internal_done_flag;
+    SCpnt->SCp.Status++;
 }
 
 int aha1740_command(Scsi_Cmnd * SCpnt)
 {
     aha1740_queuecommand(SCpnt, internal_done);
-
-    while (!internal_done_flag);
-    internal_done_flag = 0;
-    return internal_done_errcode;
+    SCpnt->SCp.Status = 0;
+    while (!SCpnt->SCp.Status)
+	barrier();
+    return SCpnt->result;
 }
 
 /* Query the board for its irq_level.  Nothing else matters
    in enhanced mode on an EISA bus. */
 
-void aha1740_getconfig(void)
+void aha1740_getconfig(unsigned int base, unsigned int *irq_level,
+		       unsigned int *translation)
 {
-  static int intab[] = { 9,10,11,12,0,14,15,0 };
+    static int intab[] = { 9, 10, 11, 12, 0, 14, 15, 0 };
 
-  irq_level = intab [ inb(INTDEF)&0x7 ];
-  outb(inb(INTDEF) | 0x10, INTDEF);
+    *irq_level = intab[inb(INTDEF(base)) & 0x7];
+    *translation = inb(RESV1(base)) & 0x1;
+    outb(inb(INTDEF(base)) | 0x10, INTDEF(base));
 }
 
 int aha1740_detect(Scsi_Host_Template * tpnt)
 {
-    tpnt->proc_dir = &proc_scsi_aha1740;
+    int count = 0, slot;
 
-    memset(&ecb, 0, sizeof(struct ecb));
     DEB(printk("aha1740_detect: \n"));
-    
+
     for ( slot=MINEISA; slot <= MAXEISA; slot++ )
     {
-	base = SLOTBASE(slot);
+	int slotbase;
+	unsigned int irq_level, translation;
+	struct Scsi_Host *shpnt;
+	struct aha1740_hostdata *host;
+	slotbase = SLOTBASE(slot);
 	/*
 	 * The ioports for eisa boards are generally beyond that used in the
 	 * check/allocate region code, but this may change at some point,
 	 * so we go through the motions.
 	 */
-	if(check_region(base, 0x5c)) continue;  /* See if in use */
-	if ( aha1740_test_port())  break;
-    }
-    if ( slot > MAXEISA )
-	return 0;
-
-    aha1740_getconfig();
-
-    if ( (inb(G2STAT) & (G2STAT_MBXOUT | G2STAT_BUSY) ) != G2STAT_MBXOUT )
-    {	/* If the card isn't ready, hard reset it */
-	outb(G2CNTRL_HRST,G2CNTRL);
-	outb(0,G2CNTRL);    
-    }
-
-    printk("Configuring Adaptec at IO:%x, IRQ %d\n",base,
-	   irq_level);
-
-    DEB(printk("aha1740_detect: enable interrupt channel %d\n", irq_level));
-
-    if (request_irq(irq_level,aha1740_intr_handle, 0, "aha1740", NULL))
-    {
-	printk("Unable to allocate IRQ for adaptec controller.\n");
-	return 0;
+	if (check_region(slotbase, SLOTSIZE))  /* See if in use */
+	    continue;
+	if (!aha1740_test_port(slotbase))
+	    continue;
+	aha1740_getconfig(slotbase,&irq_level,&translation);
+	if ((inb(G2STAT(slotbase)) &
+	     (G2STAT_MBXOUT|G2STAT_BUSY)) != G2STAT_MBXOUT)
+	{	/* If the card isn't ready, hard reset it */
+	    outb(G2CNTRL_HRST, G2CNTRL(slotbase));
+	    outb(0, G2CNTRL(slotbase));
+	}
+	printk("Configuring aha174x at IO:%x, IRQ %d\n", slotbase, irq_level);
+	printk("aha174x: Extended translation %sabled.\n",
+	       translation ? "en" : "dis");
+	DEB(printk("aha1740_detect: enable interrupt channel %d\n",irq_level));
+	if (request_irq(irq_level,aha1740_intr_handle,0,"aha1740",NULL)) {
+	    printk("Unable to allocate IRQ for adaptec controller.\n");
+	    continue;
+	}
+	shpnt = scsi_register(tpnt, sizeof(struct aha1740_hostdata));
+	request_region(slotbase, SLOTSIZE, "aha1740");
+	shpnt->base = 0;
+	shpnt->io_port = slotbase;
+	shpnt->n_io_port = SLOTSIZE;
+	shpnt->irq = irq_level;
+	shpnt->dma_channel = 0xff;
+	host = HOSTDATA(shpnt);
+	host->slot = slot;
+	host->translation = translation;
+	aha_host[irq_level - 9] = shpnt;
+	count++;
     }
-    request_region(base, 0x5c,"aha1740");  /* Reserve the space that we need to use */
-    return 1;
+    return count;
 }
 
 /* Note:  They following two functions do not apply very well to the Adaptec,
-which basically manages its own affairs quite well without our interference,
-so I haven't put anything into them.  I can faintly imagine someone with a
-*very* badly behaved SCSI target (perhaps an old tape?) wanting the abort(),
-but it hasn't happened yet, and doing aborts brings the Adaptec to its
-knees.  I cannot (at this moment in time) think of any reason to reset the
-card once it's running.  So there. */
+   which basically manages its own affairs quite well without our interference,
+   so I haven't put anything into them.  I can faintly imagine someone with a
+   *very* badly behaved SCSI target (perhaps an old tape?) wanting the abort(),
+   but it hasn't happened yet, and doing aborts brings the Adaptec to its
+   knees.  I cannot (at this moment in time) think of any reason to reset the
+   card once it's running.  So there. */
 
 int aha1740_abort(Scsi_Cmnd * SCpnt)
 {
@@ -507,13 +583,23 @@
 
 int aha1740_biosparam(Disk * disk, kdev_t dev, int* ip)
 {
-  int size = disk->capacity;
-DEB(printk("aha1740_biosparam\n"));
-  ip[0] = 64;
-  ip[1] = 32;
-  ip[2] = size >> 11;
-/*  if (ip[2] >= 1024) ip[2] = 1024; */
-  return 0;
+    int size = disk->capacity;
+    int extended = HOSTDATA(disk->device->host)->translation;
+
+    DEB(printk("aha1740_biosparam\n"));
+    if (extended && (ip[2] > 1024))
+    {
+	ip[0] = 255;
+	ip[1] = 63;
+	ip[2] = size / (255 * 63);
+    }
+    else
+    {
+	ip[0] = 64;
+	ip[1] = 32;
+	ip[2] = size >> 11;
+    }
+    return 0;
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.1.29/linux/drivers/scsi/aha1740.h linux/drivers/scsi/aha1740.h
--- v2.1.29/linux/drivers/scsi/aha1740.h	Fri Dec 27 02:03:23 1996
+++ linux/drivers/scsi/aha1740.h	Wed Mar 12 09:01:52 1997
@@ -17,23 +17,23 @@
 #define MINEISA 1   /* I don't have an EISA Spec to know these ranges, so I */
 #define MAXEISA 8   /* Just took my machine's specifications.  Adjust to fit.*/
 		    /* I just saw an ad, and bumped this from 6 to 8 */
-#define	SLOTBASE(x)	((x << 12)+ 0xc80 )
-#define	BASE		(base)
+#define	SLOTBASE(x)	((x << 12) + 0xc80)
+#define SLOTSIZE	0x5c
 
 /* EISA configuration registers & values */
-#define	HID0	(base + 0x0)
-#define	HID1	(base + 0x1)
-#define HID2	(base + 0x2)
-#define	HID3	(base + 0x3)
-#define	EBCNTRL	(base + 0x4)
-#define	PORTADR	(base + 0x40)
-#define BIOSADR (base + 0x41)
-#define INTDEF	(base + 0x42)
-#define SCSIDEF (base + 0x43)
-#define BUSDEF	(base + 0x44)
-#define	RESV0	(base + 0x45)
-#define RESV1	(base + 0x46)
-#define	RESV2	(base + 0x47)
+#define	HID0(base)	(base + 0x0)
+#define	HID1(base)	(base + 0x1)
+#define HID2(base)	(base + 0x2)
+#define	HID3(base)	(base + 0x3)
+#define	EBCNTRL(base)	(base + 0x4)
+#define	PORTADR(base)	(base + 0x40)
+#define BIOSADR(base)	(base + 0x41)
+#define INTDEF(base)	(base + 0x42)
+#define SCSIDEF(base)	(base + 0x43)
+#define BUSDEF(base)	(base + 0x44)
+#define	RESV0(base)	(base + 0x45)
+#define RESV1(base)	(base + 0x46)
+#define	RESV2(base)	(base + 0x47)
 
 #define	HID_MFG	"ADP"
 #define	HID_PRD 0
@@ -41,13 +41,13 @@
 #define EBCNTRL_VALUE 1
 #define PORTADDR_ENH 0x80
 /* READ */
-#define	G2INTST	(BASE + 0x56)
-#define G2STAT	(BASE + 0x57)
-#define	MBOXIN0	(BASE + 0x58)
-#define	MBOXIN1	(BASE + 0x59)
-#define	MBOXIN2	(BASE + 0x5a)
-#define	MBOXIN3	(BASE + 0x5b)
-#define G2STAT2	(BASE + 0x5c)
+#define	G2INTST(base)	(base + 0x56)
+#define G2STAT(base)	(base + 0x57)
+#define	MBOXIN0(base)	(base + 0x58)
+#define	MBOXIN1(base)	(base + 0x59)
+#define	MBOXIN2(base)	(base + 0x5a)
+#define	MBOXIN3(base)	(base + 0x5b)
+#define G2STAT2(base)	(base + 0x5c)
 
 #define G2INTST_MASK		0xf0	/* isolate the status */
 #define	G2INTST_CCBGOOD		0x10	/* CCB Completed */
@@ -65,12 +65,12 @@
 #define G2STAT2_READY	0	/* Host Ready Bit */
 
 /* WRITE (and ReadBack) */
-#define	MBOXOUT0	(BASE + 0x50)
-#define	MBOXOUT1	(BASE + 0x51)
-#define	MBOXOUT2	(BASE + 0x52)
-#define	MBOXOUT3	(BASE + 0x53)
-#define	ATTN		(BASE + 0x54)
-#define G2CNTRL		(BASE + 0x55)
+#define	MBOXOUT0(base)	(base + 0x50)
+#define	MBOXOUT1(base)	(base + 0x51)
+#define	MBOXOUT2(base)	(base + 0x52)
+#define	MBOXOUT3(base)	(base + 0x53)
+#define	ATTN(base)	(base + 0x54)
+#define G2CNTRL(base)	(base + 0x55)
 
 #define	ATTN_IMMED	0x10	/* Immediate Command */
 #define	ATTN_START	0x40	/* Start CCB */
@@ -159,18 +159,22 @@
 int aha1740_abort(Scsi_Cmnd *);
 int aha1740_reset(Scsi_Cmnd *, unsigned int);
 int aha1740_biosparam(Disk *, kdev_t, int*);
+int aha1740_proc_info(char *buffer, char **start, off_t offset,
+                               int length, int hostno, int inout);
 
 #define AHA1740_ECBS 32
 #define AHA1740_SCATTER 16
+#define AHA1740_CMDLUN 1
 
 #ifndef NULL
-#define NULL 0
+	#define NULL 0
 #endif
 
+extern struct proc_dir_entry proc_scsi_aha1740;
 
 #define AHA1740 {NULL,	NULL,				\
-                   NULL,                                \
-		   NULL,		                \
+                   &proc_scsi_aha1740,			\
+		   aha1740_proc_info,	                \
 		   "Adaptec 174x (EISA)",		\
 		   aha1740_detect,			\
 		   NULL,				\
@@ -184,10 +188,9 @@
 		   AHA1740_ECBS, 			\
 		   7, 					\
 		   AHA1740_SCATTER, 			\
-		   1, 					\
+		   AHA1740_CMDLUN, 			\
 		   0, 					\
 		   0, 					\
 		   ENABLE_CLUSTERING}
 
 #endif
-
diff -u --recursive --new-file v2.1.29/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.1.29/linux/drivers/scsi/aic7xxx.c	Mon Oct 28 14:21:09 1996
+++ linux/drivers/scsi/aic7xxx.c	Tue Mar 11 13:24:58 1997
@@ -79,7 +79,7 @@
  */
 #define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a))
 
-struct proc_dir_entry proc_scsi_aic7xxx = {
+static struct proc_dir_entry proc_scsi_aic7xxx = {
     PROC_SCSI_AIC7XXX, 7, "aic7xxx",
     S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
@@ -1089,7 +1089,7 @@
  *   Return a string containing just the RCS version number from either
  *   an Id or Revision RCS clause.
  *-F*************************************************************************/
-const char *
+static const char *
 rcs_version(const char *version_info)
 {
   static char buf[10];
diff -u --recursive --new-file v2.1.29/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- v2.1.29/linux/drivers/scsi/seagate.c	Fri Jan 24 09:35:51 1997
+++ linux/drivers/scsi/seagate.c	Tue Mar 11 13:24:58 1997
@@ -100,7 +100,7 @@
 #include "sd.h"
 #include <scsi/scsi_ioctl.h>
 
-struct proc_dir_entry proc_scsi_seagate =
+static struct proc_dir_entry proc_scsi_seagate =
 {
   PROC_SCSI_SEAGATE, 7, "seagate",
   S_IFDIR | S_IRUGO | S_IXUGO, 2
diff -u --recursive --new-file v2.1.29/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.29/linux/fs/buffer.c	Tue Mar  4 10:25:26 1997
+++ linux/fs/buffer.c	Wed Mar 12 15:13:46 1997
@@ -1,4 +1,3 @@
-extern void allow_interrupts(void);
 /*
  *  linux/fs/buffer.c
  *
@@ -626,7 +625,6 @@
 	}
 
 repeat:
-	allow_interrupts();
 	/* OK, we cannot grow the buffer cache, now try to get some
 	   from the lru list */
 
@@ -708,7 +706,6 @@
 	   now so as to ensure that there are still clean buffers available
 	   for user processes to use (and dirty) */
 repeat:
-	allow_interrupts();
 	bh = get_hash_table(dev, block, size);
 	if (bh) {
 		if (!buffer_dirty(bh)) {
@@ -721,7 +718,6 @@
 	}
 
 	while(!free_list[isize]) {
-		allow_interrupts();
 		refill_freelist(size);
 	}
 	
@@ -1516,7 +1512,6 @@
 		ndirty = 0;
 		nwritten = 0;
 	repeat:
-		allow_interrupts();
 	
 		bh = lru_list[nlist];
 		if(bh) 
@@ -1659,7 +1654,6 @@
 			 ndirty = 0;
 			 refilled = 0;
 		 repeat:
-		 	allow_interrupts();
 		 	
 			 bh = lru_list[nlist];
 			 if(bh) 
diff -u --recursive --new-file v2.1.29/linux/fs/inode.c linux/fs/inode.c
--- v2.1.29/linux/fs/inode.c	Sat Nov 30 02:24:00 1996
+++ linux/fs/inode.c	Mon Mar 17 17:18:41 1997
@@ -489,7 +489,7 @@
 	inode = first_inode;
 	best = NULL;
 	badness = 1000;
-	for (i = nr_inodes/2; i > 0; i--,inode = inode->i_next) {
+	for (i = nr_inodes/2; i > 0; i--,inode = inode->i_prev) {
 		if (!inode->i_count) {
 			unsigned long i = 999;
 			if (!(inode->i_lock | inode->i_dirt))
diff -u --recursive --new-file v2.1.29/linux/fs/msdos/msdosfs_syms.c linux/fs/msdos/msdosfs_syms.c
--- v2.1.29/linux/fs/msdos/msdosfs_syms.c	Fri Jan  3 01:33:26 1997
+++ linux/fs/msdos/msdosfs_syms.c	Tue Mar 11 13:24:58 1997
@@ -29,7 +29,7 @@
 EXPORT_SYMBOL(msdos_put_super);
 
 
-struct file_system_type msdos_fs_type = {
+static struct file_system_type msdos_fs_type = {
 	msdos_read_super, "msdos", 1, NULL
 };
 
diff -u --recursive --new-file v2.1.29/linux/fs/namei.c linux/fs/namei.c
--- v2.1.29/linux/fs/namei.c	Sun Jan 26 02:07:44 1997
+++ linux/fs/namei.c	Mon Mar 17 17:28:41 1997
@@ -652,7 +652,10 @@
 	}
 	if (dir->i_sb && dir->i_sb->dq_op)
 		dir->i_sb->dq_op->initialize(dir, -1);
-	return dir->i_op->rmdir(dir,basename,namelen);
+	down(&dir->i_sem);
+	error = dir->i_op->rmdir(dir,basename,namelen);
+	up(&dir->i_sem);
+	return error;
 }
 
 asmlinkage int sys_rmdir(const char * pathname)
@@ -705,7 +708,10 @@
 	}
 	if (dir->i_sb && dir->i_sb->dq_op)
 		dir->i_sb->dq_op->initialize(dir, -1);
-	return dir->i_op->unlink(dir,basename,namelen);
+	down(&dir->i_sem);
+	error = dir->i_op->unlink(dir,basename,namelen);
+	up(&dir->i_sem);
+	return error;
 }
 
 asmlinkage int sys_unlink(const char * pathname)
diff -u --recursive --new-file v2.1.29/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.1.29/linux/fs/proc/array.c	Tue Jan 28 00:02:45 1997
+++ linux/fs/proc/array.c	Thu Mar 20 17:11:52 1997
@@ -505,6 +505,9 @@
 	      eip = ((struct pt_regs *) (tsk)->tss.esp0)->pc;	 \
         eip; })
 #define	KSTK_ESP(tsk)	((tsk) == current ? rdusp() : (tsk)->tss.usp)
+#elif defined (__sparc_v9__)
+# define KSTK_EIP(tsk)  ((tsk)->tss.kregs->tpc)
+# define KSTK_ESP(tsk)  ((tsk)->tss.kregs->u_regs[UREG_FP])
 #elif defined(__sparc__)
 # define KSTK_EIP(tsk)  ((tsk)->tss.kregs->pc)
 # define KSTK_ESP(tsk)  ((tsk)->tss.kregs->u_regs[UREG_FP])
diff -u --recursive --new-file v2.1.29/linux/fs/proc/fd.c linux/fs/proc/fd.c
--- v2.1.29/linux/fs/proc/fd.c	Sun Jan 26 02:07:45 1997
+++ linux/fs/proc/fd.c	Thu Mar 20 17:11:52 1997
@@ -106,7 +106,13 @@
 	if (!pid || i >= NR_TASKS)
 		return -ENOENT;
 
-	if (fd >= NR_OPEN || !p->files->fd[fd] || !p->files->fd[fd]->f_inode)
+	/*
+	 *	File handle is invalid if it is out of range, if the process
+	 *	has no files (Zombie) if the file is closed, or if its inode
+	 *	is NULL
+	 */
+
+ 	if (fd >= NR_OPEN || !p->files || !p->files->fd[fd] || !p->files->fd[fd]->f_inode)
 	  return -ENOENT;
 
 	ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd;
diff -u --recursive --new-file v2.1.29/linux/fs/proc/generic.c linux/fs/proc/generic.c
--- v2.1.29/linux/fs/proc/generic.c	Mon Mar 17 14:54:30 1997
+++ linux/fs/proc/generic.c	Wed Mar 19 17:00:10 1997
@@ -14,7 +14,6 @@
 #include <linux/sched.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
-#include <linux/config.h>
 #include <asm/bitops.h>
 
 static long proc_file_read(struct inode * inode, struct file * file,
diff -u --recursive --new-file v2.1.29/linux/fs/proc/proc_tty.c linux/fs/proc/proc_tty.c
--- v2.1.29/linux/fs/proc/proc_tty.c	Mon Mar 17 14:54:30 1997
+++ linux/fs/proc/proc_tty.c	Wed Mar 19 17:00:21 1997
@@ -11,7 +11,6 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <linux/tty.h>
-#include <linux/config.h>
 #include <asm/bitops.h>
 
 extern struct tty_driver *tty_drivers;	/* linked list of tty drivers */
diff -u --recursive --new-file v2.1.29/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c
--- v2.1.29/linux/fs/smbfs/sock.c	Sun Feb  2 05:18:47 1997
+++ linux/fs/smbfs/sock.c	Thu Mar 20 17:11:53 1997
@@ -27,30 +27,28 @@
 
 static int
 _recvfrom(struct socket *sock, unsigned char *ubuf, int size,
-	  int noblock, unsigned flags, struct sockaddr_in *sa)
+	  int noblock, unsigned flags)
 {
 	struct iovec iov;
 	struct msghdr msg;
 	struct scm_cookie scm;
 
-	memset(&scm, 0, sizeof(scm));
-	
-	iov.iov_base = ubuf;
-	iov.iov_len = size;
-
-	msg.msg_name = (void *) sa;
+	msg.msg_name = NULL;
 	msg.msg_namelen = 0;
-	if (sa)
-		msg.msg_namelen = sizeof(struct sockaddr_in);
-	msg.msg_control = NULL;
 	msg.msg_iov = &iov;
 	msg.msg_iovlen = 1;
-
+	msg.msg_control = NULL;
+	iov.iov_base = ubuf;
+	iov.iov_len = size;
+	
 	if (noblock) {
 		flags |= MSG_DONTWAIT;
 	}
-
-	return sock->ops->recvmsg(sock, &msg, size, flags, &scm);
+	memset(&scm, 0,sizeof(scm));
+	size=sock->ops->recvmsg(sock, &msg, size, flags, &scm);
+	if(size>=0)
+		scm_recv(sock,&msg,&scm,flags);
+	return size;
 }
 
 static int
@@ -61,27 +59,26 @@
 	struct msghdr msg;
 	struct scm_cookie scm;
 	int err;
-	
-
-	iov.iov_base = (void *) buff;
-	iov.iov_len = len;
 
 	msg.msg_name = NULL;
 	msg.msg_namelen = 0;
-	msg.msg_control = NULL;
 	msg.msg_iov = &iov;
 	msg.msg_iovlen = 1;
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	
+	iov.iov_base = (void *)buff;
+	iov.iov_len = len;
+	
 
-	if (noblock) {
+	if (noblock) 
 		flags |= MSG_DONTWAIT;
-	}
 
 	msg.msg_flags = flags;
 
 	err = scm_send(sock, &msg, &scm);
         if (err < 0)
                 return err;
-	
 	err = sock->ops->sendmsg(sock, &msg, len, &scm);
 	scm_destroy(&scm);
 	return err;
@@ -101,14 +98,12 @@
 		fs = get_fs();
 		set_fs(get_ds());
 
-		result = _recvfrom(sock, (void *) peek_buf, 1, 1,
-				   MSG_PEEK, NULL);
+		result = _recvfrom(sock, (void *) peek_buf, 1, 1, MSG_PEEK);
 
 		while ((result != -EAGAIN) && (peek_buf[0] == 0x85))
 		{
 			/* got SESSION KEEP ALIVE */
-			result = _recvfrom(sock, (void *) peek_buf,
-					   4, 1, 0, NULL);
+			result = _recvfrom(sock, (void *) peek_buf, 4, 1, 0);
 
 			DDPRINTK("smb_data_callback:"
 				 " got SESSION KEEP ALIVE\n");
@@ -118,7 +113,7 @@
 				break;
 			}
 			result = _recvfrom(sock, (void *) peek_buf,
-					   1, 1, MSG_PEEK, NULL);
+					   1, 1, MSG_PEEK);
 		}
 		set_fs(fs);
 
@@ -265,7 +260,7 @@
 	{
 		result = _recvfrom(sock,
 				   (void *) (target + already_read),
-				   length - already_read, 0, 0, NULL);
+				   length - already_read, 0, 0);
 
 		if (result == 0)
 		{
@@ -665,6 +660,15 @@
 	*p++ = 'D';		/* this was added because OS/2 does it */
 	*p++ = ' ';
 
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 4;
+	msg.msg_flags = 0;
+	
 	iov[0].iov_base = (void *) server->packet;
 	iov[0].iov_len = oparam;
 	iov[1].iov_base = (param == NULL) ? padding : param;
@@ -673,12 +677,6 @@
 	iov[2].iov_len = odata - oparam - lparam;
 	iov[3].iov_base = (data == NULL) ? padding : data;
 	iov[3].iov_len = ldata;
-
-	msg.msg_name = NULL;
-	msg.msg_namelen = 0;
-	msg.msg_control = NULL;
-	msg.msg_iov = iov;
-	msg.msg_iovlen = 4;
 
 	err = scm_send(sock, &msg, &scm);
         if (err < 0)
diff -u --recursive --new-file v2.1.29/linux/fs/super.c linux/fs/super.c
--- v2.1.29/linux/fs/super.c	Sun Jan 26 02:07:45 1997
+++ linux/fs/super.c	Thu Mar 20 17:11:53 1997
@@ -760,6 +760,7 @@
 	if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY))
 		if (!fs_may_remount_ro(sb->s_dev))
 			return -EBUSY;
+	sb->s_flags = (flags & ~MS_RDONLY) | (sb->s_flags & MS_RDONLY);
 	if (sb->s_op && sb->s_op->remount_fs) {
 		retval = sb->s_op->remount_fs(sb, &flags, data);
 		if (retval)
diff -u --recursive --new-file v2.1.29/linux/fs/umsdos/emd.c linux/fs/umsdos/emd.c
--- v2.1.29/linux/fs/umsdos/emd.c	Mon Oct 28 04:29:27 1996
+++ linux/fs/umsdos/emd.c	Thu Mar 20 17:11:53 1997
@@ -64,8 +64,31 @@
 	unsigned long count)
 {
 	int written;
+#ifdef __BIG_ENDIAN
+	struct umsdos_dirent *d = (struct umsdos_dirent *)buf;
+#endif
 	filp->f_flags = 0;
+#ifdef __BIG_ENDIAN	
+	d->nlink = cpu_to_le16 (d->nlink);
+	d->uid = cpu_to_le16 (d->uid);
+	d->gid = cpu_to_le16 (d->gid);
+	d->atime = cpu_to_le32 (d->atime);
+	d->mtime = cpu_to_le32 (d->mtime);
+	d->ctime = cpu_to_le32 (d->ctime);
+	d->rdev = cpu_to_le16 (d->rdev);
+	d->mode = cpu_to_le16 (d->mode);
+#endif	
 	written = umsdos_file_write_kmem (emd_dir,filp,buf,count);
+#ifdef __BIG_ENDIAN	
+	d->nlink = le16_to_cpu (d->nlink);
+	d->uid = le16_to_cpu (d->uid);
+	d->gid = le16_to_cpu (d->gid);
+	d->atime = le32_to_cpu (d->atime);
+	d->mtime = le32_to_cpu (d->mtime);
+	d->ctime = le32_to_cpu (d->ctime);
+	d->rdev = le16_to_cpu (d->rdev);
+	d->mode = le16_to_cpu (d->mode);
+#endif	
 	return written != count ? -EIO : 0;
 }
 /*
@@ -80,6 +103,9 @@
 {
 	long int ret = 0;
 	int sizeread;
+#ifdef __BIG_ENDIAN
+	struct umsdos_dirent *d = (struct umsdos_dirent *)buf;
+#endif
 	filp->f_flags = 0;
 	sizeread = umsdos_file_read_kmem (emd_dir,filp,buf,count);
 	if (sizeread != count){
@@ -87,6 +113,16 @@
 			,filp->f_pos,sizeread,count);
 		ret = -EIO;
 	}
+#ifdef __BIG_ENDIAN	
+	d->nlink = le16_to_cpu (d->nlink);
+	d->uid = le16_to_cpu (d->uid);
+	d->gid = le16_to_cpu (d->gid);
+	d->atime = le32_to_cpu (d->atime);
+	d->mtime = le32_to_cpu (d->mtime);
+	d->ctime = le32_to_cpu (d->ctime);
+	d->rdev = le16_to_cpu (d->rdev);
+	d->mode = le16_to_cpu (d->mode);
+#endif	
 	return ret;
 
 }
diff -u --recursive --new-file v2.1.29/linux/include/asm-alpha/ide.h linux/include/asm-alpha/ide.h
--- v2.1.29/linux/include/asm-alpha/ide.h	Wed Nov  6 04:49:33 1996
+++ linux/include/asm-alpha/ide.h	Tue Mar 11 14:19:12 1997
@@ -97,7 +97,7 @@
 /*
  * The following are not needed for the non-m68k ports
  */
-static __inline__ int ide_ack_intr (ide_ioreg_t base_port, ide_ioreg_t irq_port)
+static __inline__ int ide_ack_intr (ide_ioreg_t status_port, ide_ioreg_t irq_port)
 {
 	return(1);
 }
diff -u --recursive --new-file v2.1.29/linux/include/asm-alpha/socket.h linux/include/asm-alpha/socket.h
--- v2.1.29/linux/include/asm-alpha/socket.h	Sun Dec 15 09:41:56 1996
+++ linux/include/asm-alpha/socket.h	Thu Mar 20 17:11:53 1997
@@ -40,4 +40,9 @@
 #define SO_PASSCRED	17
 #define SO_PEERCRED	18
 
+/* Security levels - as per NRL IPv6 - don't actually do anything */
+#define SO_SECURITY_AUTHENTICATION		19
+#define SO_SECURITY_ENCRYPTION_TRANSPORT	20
+#define SO_SECURITY_ENCRYPTION_NETWORK		21
+
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v2.1.29/linux/include/asm-i386/atomic.h linux/include/asm-i386/atomic.h
--- v2.1.29/linux/include/asm-i386/atomic.h	Thu Apr  4 02:04:45 1996
+++ linux/include/asm-i386/atomic.h	Mon Mar 24 16:39:39 1997
@@ -21,7 +21,7 @@
 
 typedef int atomic_t;
 
-static __inline__ void atomic_add(atomic_t i, atomic_t *v)
+static __inline__ void atomic_add(atomic_t i, volatile atomic_t *v)
 {
 	__asm__ __volatile__(
 		LOCK "addl %1,%0"
@@ -29,7 +29,7 @@
 		:"ir" (i), "m" (__atomic_fool_gcc(v)));
 }
 
-static __inline__ void atomic_sub(atomic_t i, atomic_t *v)
+static __inline__ void atomic_sub(atomic_t i, volatile atomic_t *v)
 {
 	__asm__ __volatile__(
 		LOCK "subl %1,%0"
@@ -37,7 +37,7 @@
 		:"ir" (i), "m" (__atomic_fool_gcc(v)));
 }
 
-static __inline__ void atomic_inc(atomic_t *v)
+static __inline__ void atomic_inc(volatile atomic_t *v)
 {
 	__asm__ __volatile__(
 		LOCK "incl %0"
@@ -45,7 +45,7 @@
 		:"m" (__atomic_fool_gcc(v)));
 }
 
-static __inline__ void atomic_dec(atomic_t *v)
+static __inline__ void atomic_dec(volatile atomic_t *v)
 {
 	__asm__ __volatile__(
 		LOCK "decl %0"
@@ -53,7 +53,7 @@
 		:"m" (__atomic_fool_gcc(v)));
 }
 
-static __inline__ int atomic_dec_and_test(atomic_t *v)
+static __inline__ int atomic_dec_and_test(volatile atomic_t *v)
 {
 	unsigned char c;
 
@@ -63,5 +63,14 @@
 		:"m" (__atomic_fool_gcc(v)));
 	return c != 0;
 }
+
+/* These are x86-specific, used by some header files */
+#define atomic_clear_mask(mask, addr) \
+__asm__ __volatile__(LOCK "andl %0,%1" \
+: : "r" (~(mask)),"m" (__atomic_fool_gcc(addr)) : "memory")
+
+#define atomic_set_mask(mask, addr) \
+__asm__ __volatile__(LOCK "orl %0,%1" \
+: : "r" (mask),"m" (__atomic_fool_gcc(addr)) : "memory")
 
 #endif
diff -u --recursive --new-file v2.1.29/linux/include/asm-i386/bitops.h linux/include/asm-i386/bitops.h
--- v2.1.29/linux/include/asm-i386/bitops.h	Fri Dec 20 01:24:38 1996
+++ linux/include/asm-i386/bitops.h	Mon Mar 24 16:40:01 1997
@@ -15,10 +15,8 @@
 
 #ifdef __SMP__
 #define LOCK_PREFIX "lock ; "
-#define SMPVOL volatile
 #else
 #define LOCK_PREFIX ""
-#define SMPVOL
 #endif
 
 /*
@@ -28,7 +26,7 @@
 #define ADDR (*(struct __dummy *) addr)
 #define CONST_ADDR (*(const struct __dummy *) addr)
 
-extern __inline__ int set_bit(int nr, SMPVOL void * addr)
+extern __inline__ int set_bit(int nr, volatile void * addr)
 {
 	int oldbit;
 
@@ -39,7 +37,7 @@
 	return oldbit;
 }
 
-extern __inline__ int clear_bit(int nr, SMPVOL void * addr)
+extern __inline__ int clear_bit(int nr, volatile void * addr)
 {
 	int oldbit;
 
@@ -50,7 +48,7 @@
 	return oldbit;
 }
 
-extern __inline__ int change_bit(int nr, SMPVOL void * addr)
+extern __inline__ int change_bit(int nr, volatile void * addr)
 {
 	int oldbit;
 
@@ -64,10 +62,26 @@
 /*
  * This routine doesn't need to be atomic.
  */
-extern __inline__ int test_bit(int nr, const SMPVOL void * addr)
+extern __inline__ int __constant_test_bit(int nr, const volatile void * addr)
 {
-	return ((1UL << (nr & 31)) & (((const unsigned int *) addr)[nr >> 5])) != 0;
+	return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0;
 }
+
+extern __inline__ int __test_bit(int nr, volatile void * addr)
+{
+	int oldbit;
+
+	__asm__ __volatile__(
+		"btl %2,%1\n\tsbbl %0,%0"
+		:"=r" (oldbit)
+		:"m" (ADDR),"ir" (nr));
+	return oldbit;
+}
+
+#define test_bit(nr,addr) \
+(__builtin_constant_p(nr) ? \
+ __constant_test_bit((nr),(addr)) : \
+ __test_bit((nr),(addr)))
 
 /*
  * Find-bit routines..
diff -u --recursive --new-file v2.1.29/linux/include/asm-i386/hardirq.h linux/include/asm-i386/hardirq.h
--- v2.1.29/linux/include/asm-i386/hardirq.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/hardirq.h	Wed Mar 26 09:53:06 1997
@@ -0,0 +1,68 @@
+#ifndef __ASM_HARDIRQ_H
+#define __ASM_HARDIRQ_H
+
+extern unsigned int local_irq_count[NR_CPUS];
+#define in_interrupt()	(local_irq_count[smp_processor_id()] != 0)
+
+#ifndef __SMP__
+
+#define hardirq_trylock(cpu)	((cpu)==0)	/* always true */
+#define hardirq_endlock(cpu)	do { } while (0)
+
+#define hardirq_enter(cpu)	(local_irq_count[cpu]++)
+#define hardirq_exit(cpu)	(local_irq_count[cpu]--)
+
+#else
+
+extern unsigned char global_irq_holder;
+extern unsigned volatile int global_irq_lock;
+extern unsigned volatile int global_irq_count;
+
+static inline void release_irqlock(int cpu)
+{
+	/* if we didn't own the irq lock, just ignore.. */
+	if (global_irq_holder == (unsigned char) cpu) {
+		global_irq_holder = NO_PROC_ID;
+		global_irq_lock = 0;
+	}
+}
+
+static inline void hardirq_enter(int cpu)
+{
+	++local_irq_count[cpu];
+	atomic_inc(&global_irq_count);
+}
+
+static inline void hardirq_exit(int cpu)
+{
+	atomic_dec(&global_irq_count);
+	--local_irq_count[cpu];
+}
+
+static inline int hardirq_trylock(int cpu)
+{
+	unsigned long flags;
+
+	__save_flags(flags);
+	__cli();
+	atomic_inc(&global_irq_count);
+	if (global_irq_count != 1 || test_bit(0,&global_irq_lock)) {
+		atomic_dec(&global_irq_count);
+		__restore_flags(flags);
+		return 0;
+	}
+	++local_irq_count[cpu];
+	__sti();
+	return 1;
+}
+
+static inline void hardirq_endlock(int cpu)
+{
+	__cli();
+	hardirq_exit(cpu);
+	__sti();
+}	
+
+#endif /* __SMP__ */
+
+#endif /* __ASM_HARDIRQ_H */
diff -u --recursive --new-file v2.1.29/linux/include/asm-i386/ide.h linux/include/asm-i386/ide.h
--- v2.1.29/linux/include/asm-i386/ide.h	Wed Nov  6 04:49:33 1996
+++ linux/include/asm-i386/ide.h	Tue Mar 11 14:19:12 1997
@@ -97,7 +97,7 @@
 /*
  * The following are not needed for the non-m68k ports
  */
-static __inline__ int ide_ack_intr (ide_ioreg_t base_port, ide_ioreg_t irq_port)
+static __inline__ int ide_ack_intr (ide_ioreg_t status_port, ide_ioreg_t irq_port)
 {
 	return(1);
 }
diff -u --recursive --new-file v2.1.29/linux/include/asm-i386/irq.h linux/include/asm-i386/irq.h
--- v2.1.29/linux/include/asm-i386/irq.h	Thu Feb 27 10:57:31 1997
+++ linux/include/asm-i386/irq.h	Tue Mar 25 10:44:52 1997
@@ -143,58 +143,17 @@
 #ifdef __SMP__
 
 /*
- *	Message pass must be a fast IRQ..
+ *	SMP has a few special interrupts for IPI messages
  */
 
-#define BUILD_MSGIRQ(chip,nr,mask) \
-asmlinkage void IRQ_NAME(nr); \
-asmlinkage void FAST_IRQ_NAME(nr); \
-asmlinkage void BAD_IRQ_NAME(nr); \
-__asm__( \
-"\n"__ALIGN_STR"\n" \
-SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \
-	"pushl $-"#nr"-2\n\t" \
-	SAVE_ALL \
-	ACK_##chip(mask,(nr&7)) \
-	"sti\n\t" \
-	"movl %esp,%eax\n\t" \
-	"pushl %eax\n\t" \
-	"pushl $" #nr "\n\t" \
-	"call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \
-	"addl $8,%esp\n\t" \
-	"cli\n\t" \
-	UNBLK_##chip(mask) \
-	"jmp ret_from_intr\n" \
-"\n"__ALIGN_STR"\n" \
-SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \
-	SAVE_MOST \
-	ACK_##chip(mask,(nr&7)) \
-	"pushl $" #nr "\n\t" \
-	"call "SYMBOL_NAME_STR(do_fast_IRQ)"\n\t" \
-	"addl $4,%esp\n\t" \
-	"cli\n\t" \
-	UNBLK_##chip(mask) \
-	RESTORE_MOST \
-"\n"__ALIGN_STR"\n" \
-SYMBOL_NAME_STR(bad_IRQ) #nr "_interrupt:\n\t" \
-	SAVE_MOST \
-	ACK_##chip(mask,(nr&7)) \
-	RESTORE_MOST);
-
-#define BUILD_RESCHEDIRQ(nr) \
-asmlinkage void IRQ_NAME(nr); \
+#define BUILD_SMP_INTERRUPT(x) \
+asmlinkage void x(void); \
 __asm__( \
 "\n"__ALIGN_STR"\n" \
-SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \
-	"pushl $-"#nr"-2\n\t" \
+SYMBOL_NAME_STR(x) ":\n\t" \
+	"pushl $-1\n\t" \
 	SAVE_ALL \
-	"sti\n\t" \
-	"movl %esp,%eax\n\t" \
-	"pushl %eax\n\t" \
-	"pushl $" #nr "\n\t" \
-	"call "SYMBOL_NAME_STR(smp_reschedule_irq)"\n\t" \
-	"addl $8,%esp\n\t" \
-	"cli\n\t" \
+	"call "SYMBOL_NAME_STR(smp_##x)"\n\t" \
 	"jmp ret_from_intr\n");
 
 #endif /* __SMP__ */
@@ -209,13 +168,11 @@
 	"pushl $-"#nr"-2\n\t" \
 	SAVE_ALL \
 	ACK_##chip(mask,(nr&7)) \
-	"sti\n\t" \
 	"movl %esp,%eax\n\t" \
 	"pushl %eax\n\t" \
 	"pushl $" #nr "\n\t" \
 	"call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \
 	"addl $8,%esp\n\t" \
-	"cli\n\t" \
 	UNBLK_##chip(mask) \
 	"jmp ret_from_intr\n" \
 "\n"__ALIGN_STR"\n" \
@@ -225,7 +182,6 @@
 	"pushl $" #nr "\n\t" \
 	"call "SYMBOL_NAME_STR(do_fast_IRQ)"\n\t" \
 	"addl $4,%esp\n\t" \
-	"cli\n\t" \
 	UNBLK_##chip(mask) \
 	RESTORE_MOST \
 "\n"__ALIGN_STR"\n" \
@@ -251,7 +207,6 @@
 	"pushl $" #nr "\n\t" \
 	"call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \
 	"addl $8,%esp\n\t" \
-	"cli\n\t" \
 	UNBLK_##chip(mask) \
 	"jmp ret_from_intr\n");
 
diff -u --recursive --new-file v2.1.29/linux/include/asm-i386/smp_lock.h linux/include/asm-i386/smp_lock.h
--- v2.1.29/linux/include/asm-i386/smp_lock.h	Thu Feb  6 02:53:45 1997
+++ linux/include/asm-i386/smp_lock.h	Wed Mar 26 10:58:26 1997
@@ -3,33 +3,58 @@
 
 #ifndef __SMP__
 
-#define lock_kernel()		do { } while(0)
-#define unlock_kernel()		do { } while(0)
+#define lock_kernel()				do { } while(0)
+#define unlock_kernel()				do { } while(0)
+#define release_kernel_lock(task, cpu, depth)	((depth) = 1)
+#define reaquire_kernel_lock(task, cpu, depth)	do { } while(0)
 
-typedef struct { } spinlock_t;
-#define SPIN_LOCK_UNLOCKED
-
-#define spin_lock_init(lock)	do { } while(0)
-#define spin_lock(lock)		do { } while(0)
-#define spin_trylock(lock)	do { } while(0)
-#define spin_unlock(lock)	do { } while(0)
-
-#define spin_lock_cli(lock)		\
-({	unsigned long flags;		\
-	save_flags(flags); cli();	\
-	return flags;			\
-})
+#else
 
-#define spin_unlock_restore(lock, flags)	restore_flags(flags)
+#include <asm/hardirq.h>
 
-#else
-#include <asm/pgtable.h>
+/* Release global kernel lock and global interrupt lock */
+#define release_kernel_lock(task, cpu, depth) \
+do { \
+	if ((depth = (task)->lock_depth) != 0) { \
+		__cli(); \
+		(task)->lock_depth = 0; \
+		active_kernel_processor = NO_PROC_ID; \
+		clear_bit(0,&kernel_flag); \
+	} \
+	release_irqlock(cpu); \
+	__sti(); \
+} while (0)
+
+/* Re-aquire the kernel lock */
+#define reaquire_kernel_lock(task, cpu, depth) \
+do { if (depth) __asm__ __volatile__( \
+	"cli\n\t" \
+	"movl $0f,%%eax\n\t" \
+	"jmp __lock_kernel\n" \
+	"0:\t" \
+	"movl %2,%0\n\t" \
+	"sti" \
+	: "=m" (task->lock_depth) \
+	: "d" (cpu), "c" (depth) \
+	: "ax"); \
+} while (0)
+	
 
 /* Locking the kernel */
 extern __inline__ void lock_kernel(void)
 {
 	int cpu = smp_processor_id();
 
+	if (local_irq_count[cpu]) {
+		__label__ l1;
+l1:		printk("lock from interrupt context at %p\n", &&l1);
+	}
+	if (cpu == global_irq_holder) {
+		__label__ l2;
+l2:		printk("Ugh at %p\n", &&l2);
+		sti();
+	}
+
 	__asm__ __volatile__("
 	pushfl
 	cli
@@ -61,92 +86,6 @@
 	: "m" (current->lock_depth), "i" (NO_PROC_ID)
 	: "ax", "memory");
 }
-
-/* Simple spin lock operations.  There are two variants, one clears IRQ's
- * on the local processor, one does not.
- *
- * We make no fairness assumptions. They have a cost.
- *
- *	NOT YET TESTED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- */
-
-typedef unsigned char spinlock_t;
-
-/* Arse backwards is faster for us on Intel (trylock is a clock faster) */
-
-#define SPIN_LOCK_UNLOCKED	1
-
-extern __inline__ void __spinlock_waitfor(spinlock_t *lock)
-{
-	int cpu=smp_processor_id();
-	do
-	{
-		/* Spin reading and let the MESI cache do the right stuff
-		   without us thrashing the bus */
-		while(lock)
-		{
-			/*
-			 *	Not a race, the interrupt will pick up
-			 *	the exiting case that looks suspicious.
-			 *	(The test_bit is not locked so won't
-			 *	 thrash the bus either).
-			 */
-			if(test_bit(cpu,&smp_invalidate_needed))
-			{
-				local_flush_tlb();
-				clear_bit(cpu,&smp_invalidate_needed);
-			}
-		}
-	}
-	while(clear_bit(0,lock));
-}
-
-extern __inline__ void spin_lock_init(spinlock_t *lock)
-{
-	*lock = 1;	/* We assume init does not need to be itself SMP safe */
-}
-
-extern __inline__ void spin_lock(spinlock_t *lock)
-{
-	/* Returns the old value. If we get 1 then we got the lock */
-	if(clear_bit(0,lock))
-	{
-		__spinlock_waitfor(lock);
-	}
-}
-
-extern __inline__ int spin_trylock(spinlock_t *lock)
-{
-	return clear_bit(0,lock);
-}
-
-extern __inline__ void spin_unlock(spinlock_t *lock)
-{
-	set_bit(0,lock);
-}
-
-/* These variants clear interrupts and return save_flags() style flags
- * to the caller when acquiring a lock.  To release the lock you must
- * pass the lock pointer as well as the flags returned from the acquisition
- * routine when releasing the lock.
- */
-extern __inline__ unsigned long spin_lock_cli(spinlock_t *lock)
-{
-	unsigned long flags;
-	save_flags(flags);
-	cli();
-	if(clear_bit(0,lock))
-		__spinlock_waitfor(lock);
-	return flags;
-}
-
-extern __inline__ void spin_unlock_restore(spinlock_t *lock, unsigned long flags)
-{
-	set_bit(0,lock);	/* Locked operation to keep it serialized with
-				   the popfl */
-	restore_flags(flags);
-}
-
 
 #endif /* __SMP__ */
 
diff -u --recursive --new-file v2.1.29/linux/include/asm-i386/socket.h linux/include/asm-i386/socket.h
--- v2.1.29/linux/include/asm-i386/socket.h	Thu Dec 12 06:54:20 1996
+++ linux/include/asm-i386/socket.h	Thu Mar 20 17:11:53 1997
@@ -28,4 +28,9 @@
 #define SO_RCVTIMEO	20
 #define SO_SNDTIMEO	21
 
+/* Security levels - as per NRL IPv6 - don't actually do anything */
+#define SO_SECURITY_AUTHENTICATION		22
+#define SO_SECURITY_ENCRYPTION_TRANSPORT	23
+#define SO_SECURITY_ENCRYPTION_NETWORK		24
+
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v2.1.29/linux/include/asm-i386/softirq.h linux/include/asm-i386/softirq.h
--- v2.1.29/linux/include/asm-i386/softirq.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/softirq.h	Tue Mar 25 12:55:07 1997
@@ -0,0 +1,34 @@
+#ifndef __ASM_SOFTIRQ_H
+#define __ASM_SOFTIRQ_H
+
+/*
+ * Software interrupts..
+ */
+#define get_active_bhs()	(bh_mask & bh_active)
+#define clear_active_bhs(x)	atomic_clear_mask((x),&bh_active)
+
+#ifdef __SMP__
+
+/* These are for the irq's testing the lock */
+static inline int softirq_trylock(void)
+{
+	atomic_inc(&intr_count);
+	if (intr_count != 1) {
+		atomic_dec(&intr_count);
+		return 0;
+	}
+	return 1;
+}
+
+#define softirq_endlock()	atomic_dec(&intr_count)
+
+#else
+
+/* These are for the irq's testing the lock */
+#define softirq_trylock()	(intr_count ? 0 : ((intr_count=1),1))
+#define softirq_endlock()	(intr_count = 0)
+
+
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.29/linux/include/asm-i386/spinlock.h linux/include/asm-i386/spinlock.h
--- v2.1.29/linux/include/asm-i386/spinlock.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/spinlock.h	Thu Mar 20 11:06:29 1997
@@ -0,0 +1,96 @@
+#ifndef __ASM_SPINLOCK_H
+#define __ASM_SPINLOCK_H
+
+#ifndef __SMP__
+
+typedef struct { } spinlock_t;
+#define SPIN_LOCK_UNLOCKED { }
+
+#define spin_lock_init(lock)	do { } while(0)
+#define spin_lock(lock)		do { } while(0)
+#define spin_trylock(lock)	do { } while(0)
+#define spin_unlock(lock)	do { } while(0)
+#define spin_lock_irq(lock)	cli()
+#define spin_unlock_irq(lock)	sti()
+
+#define spin_lock_irqsave(lock, flags) \
+	do { save_flags(flags); cli(); } while (0)
+#define spin_unlock_irqrestore(lock, flags) \
+	restore_flags(flags)
+
+#else
+
+/* Simple spin lock operations.  There are two variants, one clears IRQ's
+ * on the local processor, one does not.
+ *
+ * We make no fairness assumptions. They have a cost.
+ */
+
+typedef struct {
+	volatile unsigned int lock;
+	unsigned long previous;
+} spinlock_t;
+
+#define SPIN_LOCK_UNLOCKED { 0, 0 }
+
+typedef struct { unsigned long a[100]; } __dummy_lock_t;
+#define __dummy_lock(lock) (*(__dummy_lock_t *)(lock))
+
+#define spin_lock(lock) \
+__asm__ __volatile__( \
+	"jmp 2f\n" \
+	"1:\t" \
+	"testb $1,%0\n\t" \
+	"jne 1b\n" \
+	"2:\t" \
+	"lock ; btsl $0,%0\n\t" \
+	"jc 1b" \
+	:"=m" (__dummy_lock(lock)))
+
+#define spin_unlock(lock) \
+__asm__ __volatile__( \
+	"lock ; btrl $0,%0" \
+	:"=m" (__dummy_lock(lock)))
+
+#undef spin_lock
+static inline void spin_lock(spinlock_t * lock)
+{
+	__label__ l1;
+	int stuck = 10000000;
+l1:
+	__asm__ __volatile__(
+		"jmp 2f\n"
+		"1:\t"
+		"decl %1\n\t"
+		"je 3f\n\t"
+		"testb $1,%0\n\t"
+		"jne 1b\n"
+		"2:\t"
+		"lock ; btsl $0,%0\n\t"
+		"jc 1b\n"
+		"3:"
+		:"=m" (__dummy_lock(lock)),
+		 "=r" (stuck)
+		:"1" (stuck));
+	if (!stuck) {
+		printk("spinlock stuck at %p (%lx)\n",&&l1,lock->previous);
+	} else
+		lock->previous = (unsigned long) &&l1;
+}
+
+#define spin_trylock(lock) (!set_bit(0,(lock)))
+
+#define spin_lock_irq(lock) \
+	do { __cli(); spin_lock(lock); } while (0)
+
+#define spin_unlock_irq(lock) \
+	do { spin_unlock(lock); __sti(); } while (0)
+
+#define spin_lock_irqsave(lock, flags) \
+	do { __save_flags(flags); __cli(); spin_lock(lock); } while (0)
+
+#define spin_unlock_irqrestore(lock, flags) \
+	do { spin_unlock(lock); __restore_flags(flags); } while (0)
+
+#endif /* SMP */
+#endif /* __ASM_SPINLOCK_H */
diff -u --recursive --new-file v2.1.29/linux/include/asm-i386/system.h linux/include/asm-i386/system.h
--- v2.1.29/linux/include/asm-i386/system.h	Sun Jan 26 02:07:46 1997
+++ linux/include/asm-i386/system.h	Wed Mar 19 09:34:23 1997
@@ -67,22 +67,15 @@
 	 */
 
 #define switch_to(prev,next) do { \
-	cli();\
 	if(prev->flags&PF_USEDFPU) \
 	{ \
 		__asm__ __volatile__("fnsave %0":"=m" (prev->tss.i387.hard)); \
 		__asm__ __volatile__("fwait"); \
 		prev->flags&=~PF_USEDFPU;	 \
 	} \
-__asm__("pushl %%edx\n\t" \
-	"movl "SYMBOL_NAME_STR(apic_reg)",%%edx\n\t" \
-	"movl 0x20(%%edx), %%edx\n\t" \
-	"shrl $22,%%edx\n\t" \
-	"and  $0x3C,%%edx\n\t" \
-	"movl %%ecx,"SYMBOL_NAME_STR(current_set)"(,%%edx)\n\t" \
-	"popl %%edx\n\t" \
-	"ljmp %0\n\t" \
-	"sti\n\t" \
+	prev->processor = NO_PROC_ID; \
+	current_set[this_cpu] = next; \
+__asm__("ljmp %0\n\t" \
 	: /* no output */ \
 	:"m" (*(((char *)&next->tss.tr)-4)), \
 	 "c" (next)); \
@@ -220,16 +213,35 @@
 }
 
 #define mb()  __asm__ __volatile__ (""   : : :"memory")
-#define sti() __asm__ __volatile__ ("sti": : :"memory")
-#define cli() __asm__ __volatile__ ("cli": : :"memory")
 
-#define save_flags(x) \
+/* interrupt control.. */
+#define __sti() __asm__ __volatile__ ("sti": : :"memory")
+#define __cli() __asm__ __volatile__ ("cli": : :"memory")
+#define __save_flags(x) \
 __asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */ :"memory")
-
-#define restore_flags(x) \
+#define __restore_flags(x) \
 __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory")
 
-#define iret() __asm__ __volatile__ ("iret": : :"memory")
+
+#ifdef __SMP__
+
+extern void __global_cli(void);
+extern void __global_sti(void);
+extern unsigned long __global_save_flags(void);
+extern void __global_restore_flags(unsigned long);
+#define cli() __global_cli()
+#define sti() __global_sti()
+#define save_flags(x) ((x)=__global_save_flags())
+#define restore_flags(x) __global_restore_flags(x)
+
+#else
+
+#define cli() __cli()
+#define sti() __sti()
+#define save_flags(x) __save_flags(x)
+#define restore_flags(x) __restore_flags(x)
+
+#endif
 
 #define _set_gate(gate_addr,type,dpl,addr) \
 __asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
diff -u --recursive --new-file v2.1.29/linux/include/asm-i386/termios.h linux/include/asm-i386/termios.h
--- v2.1.29/linux/include/asm-i386/termios.h	Tue Mar  4 10:25:26 1997
+++ linux/include/asm-i386/termios.h	Tue Mar 11 14:08:47 1997
@@ -66,26 +66,26 @@
 }
 
 #define user_termio_to_kernel_termios(termios, termio) \
-do { \
+({ \
 	SET_LOW_TERMIOS_BITS(termios, termio, c_iflag); \
 	SET_LOW_TERMIOS_BITS(termios, termio, c_oflag); \
 	SET_LOW_TERMIOS_BITS(termios, termio, c_cflag); \
 	SET_LOW_TERMIOS_BITS(termios, termio, c_lflag); \
 	copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \
-} while(0)
+})
 
 /*
  * Translate a "termios" structure into a "termio". Ugh.
  */
 #define kernel_termios_to_user_termio(termio, termios) \
-do { \
+({ \
 	put_user((termios)->c_iflag, &(termio)->c_iflag); \
 	put_user((termios)->c_oflag, &(termio)->c_oflag); \
 	put_user((termios)->c_cflag, &(termio)->c_cflag); \
 	put_user((termios)->c_lflag, &(termio)->c_lflag); \
 	put_user((termios)->c_line,  &(termio)->c_line); \
 	copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); \
-} while(0)
+})
 
 #define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios))
 #define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios))
diff -u --recursive --new-file v2.1.29/linux/include/asm-m68k/ide.h linux/include/asm-m68k/ide.h
--- v2.1.29/linux/include/asm-m68k/ide.h	Wed Nov  6 04:49:33 1996
+++ linux/include/asm-m68k/ide.h	Tue Mar 11 14:19:12 1997
@@ -362,7 +362,7 @@
 
 #endif /* CONFIG_ATARI */
 
-static __inline__ int ide_ack_intr (ide_ioreg_t base_port, ide_ioreg_t irq_port)
+static __inline__ int ide_ack_intr (ide_ioreg_t status_port, ide_ioreg_t irq_port)
 {
 #ifdef CONFIG_AMIGA
 	if (MACH_IS_AMIGA) {
@@ -371,7 +371,7 @@
 		if (!(ch & 0x80))
 			return(0);
 		if (AMIGAHW_PRESENT(A1200_IDE)) {
-			(void) inb(base_port);
+			(void) inb(status_port);
 			outb(0x7c | (ch & 0x03), irq_port);
 		}
 	}
diff -u --recursive --new-file v2.1.29/linux/include/asm-m68k/socket.h linux/include/asm-m68k/socket.h
--- v2.1.29/linux/include/asm-m68k/socket.h	Fri Dec 20 01:20:03 1996
+++ linux/include/asm-m68k/socket.h	Thu Mar 20 17:11:53 1997
@@ -28,4 +28,9 @@
 #define SO_RCVTIMEO	20
 #define SO_SNDTIMEO	21
 
+/* Security levels - as per NRL IPv6 - don't actually do anything */
+#define SO_SECURITY_AUTHENTICATION		22
+#define SO_SECURITY_ENCRYPTION_TRANSPORT	23
+#define SO_SECURITY_ENCRYPTION_NETWORK		24
+
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v2.1.29/linux/include/asm-m68k/termios.h linux/include/asm-m68k/termios.h
--- v2.1.29/linux/include/asm-m68k/termios.h	Mon Dec 30 02:55:48 1996
+++ linux/include/asm-m68k/termios.h	Tue Mar 11 14:13:58 1997
@@ -60,7 +60,7 @@
  * Translate a "termio" structure into a "termios". Ugh.
  */
 #define user_termio_to_kernel_termios(termios, termio) \
-do { \
+({ \
 	unsigned short tmp; \
 	get_user(tmp, &(termio)->c_iflag); \
 	(termios)->c_iflag = (0xffff0000 & ((termios)->c_iflag)) | tmp; \
@@ -72,20 +72,20 @@
 	(termios)->c_lflag = (0xffff0000 & ((termios)->c_lflag)) | tmp; \
 	get_user((termios)->c_line, &(termio)->c_line); \
 	copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \
-} while(0)
+})
 
 /*
  * Translate a "termios" structure into a "termio". Ugh.
  */
 #define kernel_termios_to_user_termio(termio, termios) \
-do { \
+({ \
 	put_user((termios)->c_iflag, &(termio)->c_iflag); \
 	put_user((termios)->c_oflag, &(termio)->c_oflag); \
 	put_user((termios)->c_cflag, &(termio)->c_cflag); \
 	put_user((termios)->c_lflag, &(termio)->c_lflag); \
 	put_user((termios)->c_line,  &(termio)->c_line); \
 	copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); \
-} while(0)
+})
 
 #define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios))
 #define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios))
diff -u --recursive --new-file v2.1.29/linux/include/asm-mips/socket.h linux/include/asm-mips/socket.h
--- v2.1.29/linux/include/asm-mips/socket.h	Thu Apr 11 23:49:44 1996
+++ linux/include/asm-mips/socket.h	Thu Mar 20 17:11:53 1997
@@ -55,4 +55,9 @@
 					/* other similar things on the	*/
 					/* user level.			*/
 
+/* Security levels - as per NRL IPv6 - don't actually do anything */
+#define SO_SECURITY_AUTHENTICATION		22
+#define SO_SECURITY_ENCRYPTION_TRANSPORT	23
+#define SO_SECURITY_ENCRYPTION_NETWORK		24
+
 #endif /* __ASM_MIPS_SOCKET_H */
diff -u --recursive --new-file v2.1.29/linux/include/asm-ppc/socket.h linux/include/asm-ppc/socket.h
--- v2.1.29/linux/include/asm-ppc/socket.h	Wed Dec 18 00:54:10 1996
+++ linux/include/asm-ppc/socket.h	Thu Mar 20 17:11:53 1997
@@ -32,4 +32,9 @@
 #define SO_RCVTIMEO	18
 #define SO_SNDTIMEO	19
 
+/* Security levels - as per NRL IPv6 - don't actually do anything */
+#define SO_SECURITY_AUTHENTICATION		20
+#define SO_SECURITY_ENCRYPTION_TRANSPORT	21
+#define SO_SECURITY_ENCRYPTION_NETWORK		22
+
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc/asm_offsets.h linux/include/asm-sparc/asm_offsets.h
--- v2.1.29/linux/include/asm-sparc/asm_offsets.h	Mon Mar 17 14:54:31 1997
+++ linux/include/asm-sparc/asm_offsets.h	Thu Mar 20 16:44:24 1997
@@ -149,20 +149,20 @@
 #define AOFF_task_ldt	0x000001d8
 #define ASIZ_task_ldt	0x00000004
 #define AOFF_task_tss	0x000001e0
-#define ASIZ_task_tss	0x000003a0
-#define AOFF_task_fs	0x00000580
+#define ASIZ_task_tss	0x00000390
+#define AOFF_task_fs	0x00000570
 #define ASIZ_task_fs	0x00000004
-#define AOFF_task_files	0x00000584
+#define AOFF_task_files	0x00000574
 #define ASIZ_task_files	0x00000004
-#define AOFF_task_mm	0x00000588
+#define AOFF_task_mm	0x00000578
 #define ASIZ_task_mm	0x00000004
-#define AOFF_task_sig	0x0000058c
+#define AOFF_task_sig	0x0000057c
 #define ASIZ_task_sig	0x00000004
-#define AOFF_task_processor	0x00000590
+#define AOFF_task_processor	0x00000580
 #define ASIZ_task_processor	0x00000004
-#define AOFF_task_last_processor	0x00000594
+#define AOFF_task_last_processor	0x00000584
 #define ASIZ_task_last_processor	0x00000004
-#define AOFF_task_lock_depth	0x00000598
+#define AOFF_task_lock_depth	0x00000588
 #define ASIZ_task_lock_depth	0x00000004
 #define AOFF_mm_count	0x00000000
 #define ASIZ_mm_count	0x00000004
@@ -246,13 +246,11 @@
 #define ASIZ_thread_sstk_info	0x00000008
 #define AOFF_thread_flags	0x00000360
 #define ASIZ_thread_flags	0x00000004
-#define AOFF_thread_ex	0x00000368
-#define ASIZ_thread_ex	0x00000010
-#define AOFF_thread_current_ds	0x00000378
+#define AOFF_thread_current_ds	0x00000364
 #define ASIZ_thread_current_ds	0x00000004
-#define AOFF_thread_core_exec	0x0000037c
+#define AOFF_thread_core_exec	0x00000368
 #define ASIZ_thread_core_exec	0x00000020
-#define AOFF_thread_new_signal	0x0000039c
+#define AOFF_thread_new_signal	0x00000388
 #define ASIZ_thread_new_signal	0x00000004
 
 #endif /* __ASM_OFFSETS_H__ */
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc/checksum.h linux/include/asm-sparc/checksum.h
--- v2.1.29/linux/include/asm-sparc/checksum.h	Mon Mar 17 14:54:31 1997
+++ linux/include/asm-sparc/checksum.h	Thu Mar 20 16:44:24 1997
@@ -1,4 +1,4 @@
-/* $Id: checksum.h,v 1.25 1997/02/19 15:51:19 jj Exp $ */
+/* $Id: checksum.h,v 1.26 1997/03/14 07:54:47 davem Exp $ */
 #ifndef __SPARC_CHECKSUM_H
 #define __SPARC_CHECKSUM_H
 
@@ -45,6 +45,8 @@
 #define csum_partial_copy_fromuser(s, d, l, w)  \
                          csum_partial_copy((char *) (s), (d), (l), (w))
   
+extern unsigned int __csum_partial_copy_sparc_generic (const char *, char *);
+
 extern __inline__ unsigned int 
 csum_partial_copy_nocheck (const char *src, char *dst, int len, 
 			   unsigned int sum)
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc/dma.h linux/include/asm-sparc/dma.h
--- v2.1.29/linux/include/asm-sparc/dma.h	Sat Nov  9 11:40:36 1996
+++ linux/include/asm-sparc/dma.h	Thu Mar 20 16:44:24 1997
@@ -1,4 +1,4 @@
-/* $Id: dma.h,v 1.22 1996/10/17 05:29:01 davem Exp $
+/* $Id: dma.h,v 1.23 1997/03/14 21:05:20 jj Exp $
  * include/asm-sparc/dma.h
  *
  * Copyright 1995 (C) David S. Miller (davem@caip.rutgers.edu)
@@ -74,7 +74,6 @@
 #define DMA_ISESC1(dma)      ((dma)->revision == dvmaesc1)
 
 /* Main routines in dma.c */
-extern void dump_dma_regs(struct sparc_dma_registers *);
 extern unsigned long dvma_init(struct linux_sbus *, unsigned long);
 
 /* Fields in the cond_reg register */
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc/init.h linux/include/asm-sparc/init.h
--- v2.1.29/linux/include/asm-sparc/init.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/init.h	Thu Mar 20 16:44:24 1997
@@ -0,0 +1,26 @@
+#ifndef _SPARC_INIT_H
+#define _SPARC_INIT_H
+
+#ifndef __init
+#if (defined (__svr4__) || defined (__ELF__)) && !defined (MODULE)
+#define __init __attribute__ ((__section__ (".text.init")))
+#define __initdata __attribute__ ((__section__ (".data.init")))
+#define __initfunc(__arginit) \
+	__arginit __init; \
+	__arginit
+/* For assembly routines */
+#define __INIT		.section	".text.init",#alloc,#execinstr
+#define __FINIT	.previous
+#define __INITDATA	.section	".data.init",#alloc,#write
+#else
+#define	__init
+#define __initdata
+#define __initfunc(__arginit) __arginit
+/* For assembly routines */
+#define __INIT
+#define __FINIT
+#define __INITDATA
+#endif
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc/kbio.h linux/include/asm-sparc/kbio.h
--- v2.1.29/linux/include/asm-sparc/kbio.h	Sat Nov  9 00:29:32 1996
+++ linux/include/asm-sparc/kbio.h	Thu Mar 20 16:44:24 1997
@@ -32,6 +32,18 @@
 /* Get keyboard leds */
 #define KIOCGLED    _IOR('k', 15, unsigned char)
 
+/* Used by KIOC[GS]RATE */
+struct kbd_rate {
+	unsigned char delay;	/* Delay in Hz before first repeat.	*/
+	unsigned char rate;	/* In characters per second (0..50).	*/
+};
+
+/* Set keyboard rate */
+#define KIOCSRATE   _IOW('k', 40, struct kbd_rate)
+
+/* Get keyboard rate */
+#define KIOCGRATE   _IOW('k', 41, struct kbd_rate)
+
 /* Top bit records if the key is up or down */
 #define KBD_UP      0x80
 
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc/oplib.h linux/include/asm-sparc/oplib.h
--- v2.1.29/linux/include/asm-sparc/oplib.h	Mon Mar 17 14:54:31 1997
+++ linux/include/asm-sparc/oplib.h	Thu Mar 20 16:44:24 1997
@@ -1,4 +1,4 @@
-/* $Id: oplib.h,v 1.13 1997/01/31 00:16:52 tdyas Exp $
+/* $Id: oplib.h,v 1.15 1997/03/18 18:00:18 jj Exp $
  * oplib.h:  Describes the interface and available routines in the
  *           Linux Prom library.
  *
@@ -31,6 +31,9 @@
  */
 extern int prom_root_node;
 
+/* PROM stdin and stdout */
+extern int prom_stdin, prom_stdout;
+
 /* Pointer to prom structure containing the device tree traversal
  * and usage utility functions.  Only prom-lib should use these,
  * users use the interface defined by the library only!
@@ -108,7 +111,7 @@
 /* Enter the prom, with no chance of continuation for the stand-alone
  * which calls this.
  */
-extern void prom_halt(void);
+extern void prom_halt(void) __attribute__ ((noreturn));
 
 /* Set the PROM 'sync' callback function to the passed function pointer.
  * When the user gives the 'sync' command at the prom prompt while the
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc/pgtsrmmu.h linux/include/asm-sparc/pgtsrmmu.h
--- v2.1.29/linux/include/asm-sparc/pgtsrmmu.h	Thu Dec 19 01:03:37 1996
+++ linux/include/asm-sparc/pgtsrmmu.h	Thu Mar 20 16:44:24 1997
@@ -1,4 +1,4 @@
-/* $Id: pgtsrmmu.h,v 1.25 1996/12/18 06:56:07 tridge Exp $
+/* $Id: pgtsrmmu.h,v 1.28 1997/03/15 07:47:52 davem Exp $
  * pgtsrmmu.h:  SRMMU page table defines and code.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -87,6 +87,8 @@
 #define SRMMU_FAULT_STATUS       0x00000300
 #define SRMMU_FAULT_ADDR         0x00000400
 
+#ifndef __ASSEMBLY__
+
 /* Accessing the MMU control register. */
 extern __inline__ unsigned int srmmu_get_mmureg(void)
 {
@@ -219,7 +221,20 @@
 	return retval;
 }
 
+extern __inline__ int
+srmmu_get_pte (unsigned long addr)
+{
+	register unsigned long entry;
+        
+	__asm__ __volatile__("\n\tlda [%1] %2,%0\n\t" :
+				"=r" (entry):
+				"r" ((addr & 0xfffff000) | 0x400), "i" (ASI_M_FLUSH_PROBE));
+	return entry;
+}
+
 extern unsigned long (*srmmu_read_physical)(unsigned long paddr);
 extern void (*srmmu_write_physical)(unsigned long paddr, unsigned long word);
+
+#endif /* !(__ASSEMBLY__) */
 
 #endif /* !(_SPARC_PGTSRMMU_H) */
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc/ross.h linux/include/asm-sparc/ross.h
--- v2.1.29/linux/include/asm-sparc/ross.h	Sat Nov  9 00:30:01 1996
+++ linux/include/asm-sparc/ross.h	Thu Mar 20 16:44:24 1997
@@ -1,4 +1,4 @@
-/* $Id: ross.h,v 1.11 1996/08/29 09:48:40 davem Exp $
+/* $Id: ross.h,v 1.12 1997/03/10 09:16:57 davem Exp $
  * ross.h: Ross module specific definitions and defines.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -93,6 +93,8 @@
 #define HYPERSPARC_ICCR_FTD     0x00000002
 #define HYPERSPARC_ICCR_ICE     0x00000001
 
+#ifndef __ASSEMBLY__
+
 extern __inline__ unsigned int get_ross_icr(void)
 {
 	unsigned int icreg;
@@ -170,5 +172,7 @@
 		page += vac_line_size;
 	}
 }
+
+#endif /* !(__ASSEMBLY__) */
 
 #endif /* !(_SPARC_ROSS_H) */
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc/semaphore.h linux/include/asm-sparc/semaphore.h
--- v2.1.29/linux/include/asm-sparc/semaphore.h	Sun Jan 26 02:07:47 1997
+++ linux/include/asm-sparc/semaphore.h	Thu Mar 20 16:44:24 1997
@@ -3,6 +3,8 @@
 
 /* Dinky, good for nothing, just barely irq safe, Sparc semaphores. */
 
+#ifdef __KERNEL__
+
 #include <asm/atomic.h>
 
 struct semaphore {
@@ -40,5 +42,7 @@
 	if (atomic_inc_return(&sem->count) <= 0)
 		__up(sem);
 }	
+
+#endif /* __KERNEL__ */
 
 #endif /* !(_SPARC_SEMAPHORE_H) */
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc/socket.h linux/include/asm-sparc/socket.h
--- v2.1.29/linux/include/asm-sparc/socket.h	Fri Dec 13 01:37:40 1996
+++ linux/include/asm-sparc/socket.h	Thu Mar 20 16:44:24 1997
@@ -1,4 +1,4 @@
-/* $Id: socket.h,v 1.8 1996/12/12 19:21:43 davem Exp $ */
+/* $Id: socket.h,v 1.9 1997/03/17 04:50:50 davem Exp $ */
 #ifndef _ASM_SOCKET_H
 #define _ASM_SOCKET_H
 
@@ -34,5 +34,10 @@
 /* Linux specific, keep the same. */
 #define SO_NO_CHECK	0x000b
 #define SO_PRIORITY	0x000c
+
+/* Security levels - as per NRL IPv6 - don't actually do anything */
+#define SO_SECURITY_AUTHENTICATION		0x000d
+#define SO_SECURITY_ENCRYPTION_TRANSPORT	0x000e
+#define SO_SECURITY_ENCRYPTION_NETWORK		0x000f
 
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc/system.h linux/include/asm-sparc/system.h
--- v2.1.29/linux/include/asm-sparc/system.h	Sun Jan 26 02:07:47 1997
+++ linux/include/asm-sparc/system.h	Thu Mar 20 16:44:24 1997
@@ -1,4 +1,4 @@
-/* $Id: system.h,v 1.51 1997/01/25 01:33:05 davem Exp $ */
+/* $Id: system.h,v 1.53 1997/03/19 14:53:43 davem Exp $ */
 #ifndef __SPARC_SYSTEM_H
 #define __SPARC_SYSTEM_H
 
@@ -10,6 +10,7 @@
 #include <asm/page.h>
 #include <asm/oplib.h>
 #include <asm/psr.h>
+#include <asm/ptrace.h>
 #endif
 
 #define EMPTY_PGT       (&empty_bad_page)
@@ -264,6 +265,8 @@
 	__xchg_called_with_bad_pointer();
 	return x;
 }
+
+extern void die_if_kernel(char *str, struct pt_regs *regs) __attribute__ ((noreturn));
 
 #endif /* __ASSEMBLY__ */
 
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/auxio.h linux/include/asm-sparc64/auxio.h
--- v2.1.29/linux/include/asm-sparc64/auxio.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/auxio.h	Thu Mar 20 16:45:06 1997
@@ -0,0 +1,72 @@
+/* $Id: auxio.h,v 1.1 1997/03/14 21:05:27 jj Exp $
+ * auxio.h:  Definitions and code for the Auxiliary I/O register.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _SPARC64_AUXIO_H
+#define _SPARC64_AUXIO_H
+
+#include <asm/system.h>
+
+/* FIXME: All of this should be checked for sun4u. It has /sbus/auxio, but
+   I don't know whether it is the same and don't have a floppy */
+
+extern unsigned char *auxio_register;
+
+/* This register is an unsigned char in IO space.  It does two things.
+ * First, it is used to control the front panel LED light on machines
+ * that have it (good for testing entry points to trap handlers and irq's)
+ * Secondly, it controls various floppy drive parameters.
+ */
+#define AUXIO_ORMEIN      0xf0    /* All writes must set these bits. */
+#define AUXIO_ORMEIN4M    0xc0    /* sun4m - All writes must set these bits. */
+#define AUXIO_FLPY_DENS   0x20    /* Floppy density, high if set. Read only. */
+#define AUXIO_FLPY_DCHG   0x10    /* A disk change occurred.  Read only. */
+#define AUXIO_EDGE_ON     0x10    /* sun4m - On means Jumper block is in. */
+#define AUXIO_FLPY_DSEL   0x08    /* Drive select/start-motor. Write only. */
+#define AUXIO_LINK_TEST   0x08    /* sun4m - On means TPE Carrier detect. */
+
+/* Set the following to one, then zero, after doing a pseudo DMA transfer. */
+#define AUXIO_FLPY_TCNT   0x04    /* Floppy terminal count. Write only. */
+
+/* Set the following to zero to eject the floppy. */
+#define AUXIO_FLPY_EJCT   0x02    /* Eject floppy disk.  Write only. */
+#define AUXIO_LED         0x01    /* On if set, off if unset. Read/Write */
+
+#define AUXREG   ((volatile unsigned char *)(auxio_register))
+
+/* These are available on sun4c */
+#define TURN_ON_LED   if (AUXREG) *AUXREG = (*AUXREG | AUXIO_ORMEIN | AUXIO_LED)
+#define TURN_OFF_LED  if (AUXREG) *AUXREG = ((*AUXREG | AUXIO_ORMEIN) & (~AUXIO_LED))
+#define FLIP_LED      if (AUXREG) *AUXREG = ((*AUXREG | AUXIO_ORMEIN) ^ AUXIO_LED)
+#define FLPY_MOTORON  if (AUXREG) *AUXREG = ((*AUXREG | AUXIO_ORMEIN) | AUXIO_FLPY_DSEL)
+#define FLPY_MOTOROFF if (AUXREG) *AUXREG = ((*AUXREG | AUXIO_ORMEIN) & (~AUXIO_FLPY_DSEL))
+#define FLPY_TCNTON   if (AUXREG) *AUXREG = ((*AUXREG | AUXIO_ORMEIN) | AUXIO_FLPY_TCNT)
+#define FLPY_TCNTOFF  if (AUXREG) *AUXREG = ((*AUXREG | AUXIO_ORMEIN) & (~AUXIO_FLPY_TCNT))
+
+#ifndef __ASSEMBLY__
+extern __inline__ void set_auxio(unsigned char bits_on, unsigned char bits_off)
+{
+	unsigned char regval;
+	unsigned long flags;
+
+	save_flags(flags); cli();
+
+	if(AUXREG) {
+		regval = *AUXREG;
+		*AUXREG = ((regval | bits_on) & ~bits_off) | AUXIO_ORMEIN4M;
+	}
+	restore_flags(flags);
+}
+#endif /* !(__ASSEMBLY__) */
+
+
+/* AUXIO2 (Power Off Control) */
+extern __volatile__ unsigned char * auxio_power_register;
+
+#define	AUXIO_POWER_DETECT_FAILURE	32
+#define	AUXIO_POWER_CLEAR_FAILURE	2
+#define	AUXIO_POWER_OFF			1
+
+
+#endif /* !(_SPARC_AUXIO_H) */
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/bitops.h linux/include/asm-sparc64/bitops.h
--- v2.1.29/linux/include/asm-sparc64/bitops.h	Mon Dec 30 02:00:02 1996
+++ linux/include/asm-sparc64/bitops.h	Thu Mar 20 16:45:06 1997
@@ -1,4 +1,4 @@
-/* $Id: bitops.h,v 1.6 1996/12/26 15:36:49 davem Exp $
+/* $Id: bitops.h,v 1.7 1997/03/14 21:05:38 jj Exp $
  * bitops.h: Bit string operations on the V9.
  *
  * Copyright 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -154,18 +154,18 @@
 	unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
 
 	__asm__ __volatile__("
-	lduwa		[%2] %5, %0
+	lduwa		[%2] %6, %0
 1:
 	andcc		%0, %4, %3
 	bne,pn		%%icc, 2f
 	 xor		%0, %4, %1
-	casa 		[%2] %5, %0, %1
+	casa 		[%2] %6, %0, %1
 	cmp		%0, %1
 	bne,a,pn	%%icc, 1b
-	 lduwa		[%2] %5, %0
+	 lduwa		[%2] %6, %0
 2:
 "	: "=&r" (temp0), "=&r" (temp1), "=r" (m), "=&r" (oldbit)
-	: "ir" (1UL << (nr & 31)), "r" (m), "i" (ASI_PL));
+	: "ir" (1UL << (nr & 31)), "2" (m), "i" (ASI_PL));
 	return oldbit != 0;
 }
 
@@ -176,18 +176,18 @@
 	unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
 
 	__asm__ __volatile__("
-	lduwa		[%2] %5, %0
+	lduwa		[%2] %6, %0
 1:
 	andcc		%0, %4, %3
 	be,pn		%%icc, 2f
 	 xor		%0, %4, %1
-	casa 		[%2] %5, %0, %1
+	casa 		[%2] %6, %0, %1
 	cmp		%0, %1
 	bne,a,pn	%%icc, 1b
-	 lduwa		[%2] %5, %0
+	 lduwa		[%2] %6, %0
 2:
 "	: "=&r" (temp0), "=&r" (temp1), "=r" (m), "=&r" (oldbit)
-	: "ir" (1UL << (nr & 31)), "r" (m), "i" (ASI_PL));
+	: "ir" (1UL << (nr & 31)), "2" (m), "i" (ASI_PL));
 	return oldbit != 0;
 }
 
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/bpp.h linux/include/asm-sparc64/bpp.h
--- v2.1.29/linux/include/asm-sparc64/bpp.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/bpp.h	Thu Mar 20 16:45:06 1997
@@ -0,0 +1,82 @@
+#ifndef _SPARC64_BPP_H
+#define _SPARC64_BPP_H
+
+/*
+ * Copyright (c) 1995 Picture Elements
+ *	Stephen Williams
+ *	Gus Baldauf
+ *
+ * Linux/SPARC port by Peter Zaitcev.
+ * Integration into SPARC tree by Tom Dyas.
+ */
+
+#include  <linux/ioctl.h>
+
+/*
+ * This is a driver that supports IEEE Std 1284-1994 communications
+ * with compliant or compatible devices. It will use whatever features
+ * the device supports, prefering those that are typically faster.
+ *
+ * When the device is opened, it is left in COMPATABILITY mode, and
+ * writes work like any printer device. The driver only attempt to
+ * negotiate 1284 modes when needed so that plugs can be pulled,
+ * switch boxes switched, etc., without disrupting things. It will
+ * also leave the device in compatibility mode when closed.
+ */
+
+
+
+/*
+ * This driver also supplies ioctls to manually manipulate the
+ * pins. This is great for testing devices, or writing code to deal
+ * with bizzarro-mode of the ACME Special TurboThingy Plus.
+ *
+ * NOTE: These ioctl currently do not interact well with
+ * read/write. Caveat emptor.
+ *
+ * PUT_PINS allows us to assign the sense of all the pins, including
+ * the data pins if being driven by the host. The GET_PINS returns the
+ * pins that the peripheral drives, including data if appropriate.
+ */
+
+# define BPP_PUT_PINS _IOW('B', 1, int)
+# define BPP_GET_PINS _IOR('B', 2, void)
+# define BPP_PUT_DATA _IOW('B', 3, int)
+# define BPP_GET_DATA _IOR('B', 4, void)
+
+/*
+ * Set the data bus to input mode. Disengage the data bin driver and
+ * be prepared to read values from the peripheral. If the arg is 0,
+ * then revert the bus to output mode.
+ */
+# define BPP_SET_INPUT _IOW('B', 5, int)
+
+/*
+ * These bits apply to the PUT operation...
+ */
+# define BPP_PP_nStrobe   0x0001
+# define BPP_PP_nAutoFd   0x0002
+# define BPP_PP_nInit     0x0004
+# define BPP_PP_nSelectIn 0x0008
+
+/*
+ * These apply to the GET operation, which also reads the current value
+ * of the previously put values. A bit mask of these will be returned
+ * as a bit mask in the return code of the ioctl().
+ */
+# define BPP_GP_nAck   0x0100
+# define BPP_GP_Busy   0x0200
+# define BPP_GP_PError 0x0400
+# define BPP_GP_Select 0x0800
+# define BPP_GP_nFault 0x1000
+
+
+/*
+ * Prototype for the initialization routine.
+ */
+
+#ifdef __KERNEL__
+extern int bpp_init(void);
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/byteorder.h linux/include/asm-sparc64/byteorder.h
--- v2.1.29/linux/include/asm-sparc64/byteorder.h	Mon Dec 30 02:00:02 1996
+++ linux/include/asm-sparc64/byteorder.h	Thu Mar 20 16:45:06 1997
@@ -1,17 +1,17 @@
-/* $Id: byteorder.h,v 1.2 1996/12/26 13:25:23 davem Exp $ */
+/* $Id: byteorder.h,v 1.3 1997/03/14 21:05:31 jj Exp $ */
 #ifndef _SPARC64_BYTEORDER_H
 #define _SPARC64_BYTEORDER_H
 
-extern __inline__ unsigned long int ntohl(unsigned long int x) { return x; }
-extern __inline__ unsigned short int ntohs(unsigned short int x) { return x; }
-extern __inline__ unsigned long int htonl(unsigned long int x) { return x; }
-extern __inline__ unsigned short int htons(unsigned short int x) { return x; }
+#define ntohl(x) (x)
+#define ntohs(x) (x)
+#define htonl(x) (x)
+#define htons(x) (x)
 
 /* Some programs depend upon these being around. */
-extern __inline__ unsigned long int __constant_ntohl(unsigned long int x) { return x; }
-extern __inline__ unsigned short int __constant_ntohs(unsigned short int x) { return x; }
-extern __inline__ unsigned long int __constant_htonl(unsigned long int x) { return x; }
-extern __inline__ unsigned short int __constant_htons(unsigned short int x) { return x; }
+#define __constant_ntohl(x) (x)
+#define __constant_ntohs(x) (x)
+#define __constant_htonl(x) (x)
+#define __constant_htons(x) (x)
 
 #ifndef __BIG_ENDIAN
 #define __BIG_ENDIAN 4321
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/cache.h linux/include/asm-sparc64/cache.h
--- v2.1.29/linux/include/asm-sparc64/cache.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/cache.h	Thu Mar 20 16:45:06 1997
@@ -0,0 +1,13 @@
+/*
+ * include/asm-sparc64/cache.h
+ */
+#ifndef __ARCH_SPARC64_CACHE_H
+#define __ARCH_SPARC64_CACHE_H
+
+/* FIXME: Should look at this soon */
+/* bytes per L1 cache line */
+#define        L1_CACHE_BYTES  32      /* a guess */
+
+#define        L1_CACHE_ALIGN(x)       (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1))
+
+#endif
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/checksum.h linux/include/asm-sparc64/checksum.h
--- v2.1.29/linux/include/asm-sparc64/checksum.h	Fri Dec 13 01:37:41 1996
+++ linux/include/asm-sparc64/checksum.h	Thu Mar 20 16:45:06 1997
@@ -1,4 +1,4 @@
-/* $Id: checksum.h,v 1.3 1996/12/12 15:39:13 davem Exp $ */
+/* $Id: checksum.h,v 1.5 1997/03/18 18:00:28 jj Exp $ */
 #ifndef __SPARC64_CHECKSUM_H
 #define __SPARC64_CHECKSUM_H
 
@@ -8,6 +8,7 @@
  *  Copyright(C) 1995 Miguel de Icaza
  *  Copyright(C) 1996 David S. Miller
  *  Copyright(C) 1996 Eddie C. Dost
+ *  Copyright(C) 1997 Jakub Jelinek
  *
  * derived from:
  *	Alpha checksum c-code
@@ -15,6 +16,8 @@
  *      RFC1071 Computing the Internet Checksum
  */
 
+#include <asm/uaccess.h> 
+
 /* computes the checksum of a memory block at buff, length len,
  * and adds in "sum" (32-bit)
  *
@@ -34,11 +37,83 @@
  * here even more important to align src and dst on a 32-bit (or even
  * better 64-bit) boundary
  */
-extern unsigned int csum_partial_copy(char *src, char *dst, int len, int sum);
-
+/* FIXME: Remove these macros ASAP */
+#define csum_partial_copy(src, dst, len, sum) \
+			csum_partial_copy_nocheck(src,dst,len,sum)
 #define csum_partial_copy_fromuser(s, d, l, w)  \
-                       csum_partial_copy((char *) (s), (d), (l), (w))
+			csum_partial_copy((char *) (s), (d), (l), (w))
+			
+extern __inline__ unsigned int 
+csum_partial_copy_nocheck (const char *src, char *dst, int len, 
+			   unsigned int sum)
+{
+	register unsigned long ret asm("o0") = (unsigned long)src;
+	register char *d asm("o1") = dst;
+	register unsigned long l asm("g1") = len;
+	
+	__asm__ __volatile__ ("
+		call __csum_partial_copy_sparc_generic
+		 mov %4, %%g7
+	" : "=r" (ret) : "0" (ret), "r" (d), "r" (l), "r" (sum) :
+	"o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g5", "g7");
+	return (unsigned int)ret;
+}
 
+extern __inline__ unsigned int 
+csum_partial_copy_from_user(const char *src, char *dst, int len, 
+			    unsigned int sum, int *err)
+{
+	if (!access_ok (VERIFY_READ, src, len)) {
+		*err = -EFAULT;
+		memset (dst, 0, len);
+		return sum;
+	} else {
+		register unsigned long ret asm("o0") = (unsigned long)src;
+		register char *d asm("o1") = dst;
+		register unsigned long l asm("g1") = len;
+		register unsigned long s asm("g7") = sum;
+
+		__asm__ __volatile__ ("
+		.section __ex_table,#alloc
+		.align 4
+		.word 1f,2
+		.previous
+1:
+		call __csum_partial_copy_sparc_generic
+		 stx %5, [%%sp + 0x7ff + 128]
+		" : "=r" (ret) : "0" (ret), "r" (d), "r" (l), "r" (s), "r" (err) :
+		"o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g5", "g7");
+		return (unsigned int)ret;
+	}
+}
+  
+extern __inline__ unsigned int 
+csum_partial_copy_to_user(const char *src, char *dst, int len, 
+			  unsigned int sum, int *err)
+{
+	if (!access_ok (VERIFY_WRITE, dst, len)) {
+		*err = -EFAULT;
+		return sum;
+	} else {
+		register unsigned long ret asm("o0") = (unsigned long)src;
+		register char *d asm("o1") = dst;
+		register unsigned long l asm("g1") = len;
+		register unsigned long s asm("g7") = sum;
+
+		__asm__ __volatile__ ("
+		.section __ex_table,#alloc
+		.align 4
+		.word 1f,1
+		.previous
+1:
+		call __csum_partial_copy_sparc_generic
+		 stx %5, [%%sp + 0x7ff + 128]
+		" : "=r" (ret) : "0" (ret), "r" (d), "r" (l), "r" (s), "r" (err) :
+		"o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g5", "g7");
+		return (unsigned int)ret;
+	}
+}
+  
 /* ihl is always 5 or greater, almost always is 5, and iph is word aligned
  * the majority of the time.
  */
@@ -52,7 +127,7 @@
 	 *       both operands.
 	 */
 	__asm__ __volatile__("
-	sub		%2, 4, %%g4
+	sub		%2, 4, %%g7
 	lduw		[%1 + 0x00], %0
 	lduw		[%1 + 0x04], %%g2
 	lduw		[%1 + 0x08], %%g3
@@ -66,10 +141,10 @@
 	addcc		%%g3, %0, %0
 	add		%1, 4, %1
 	addccc		%0, %%g0, %0
-	subcc		%%g4, 1, %%g4
+	subcc		%%g7, 1, %%g7
 	be,a,pt		%%icc, 2f
 	 sll		%0, 16, %%g2
-	ba,pt		1b
+	ba,pt		%%xcc, 1b
 	 lduw		[%1 + 0x10], %%g3
 2:
 	addcc		%0, %%g2, %%g2
@@ -78,7 +153,7 @@
 	xnor		%%g0, %0, %0
 "	: "=r" (sum), "=&r" (iph)
 	: "r" (ihl), "1" (iph)
-	: "g2", "g3", "g4");
+	: "g2", "g3", "g7");
 	return sum;
 }
 
@@ -130,29 +205,29 @@
 						     unsigned int sum) 
 {
 	__asm__ __volatile__ ("
-	addcc		%3, %4, %%g4
-	addccc		%5, %%g4, %%g4
+	addcc		%3, %4, %%g7
+	addccc		%5, %%g7, %%g7
 	lduw		[%2 + 0x0c], %%g2
 	lduw		[%2 + 0x08], %%g3
-	addccc		%%g2, %%g4, %%g4
+	addccc		%%g2, %%g7, %%g7
 	lduw		[%2 + 0x04], %%g2
-	addccc		%%g3, %%g4, %%g4
+	addccc		%%g3, %%g7, %%g7
 	lduw		[%2 + 0x00], %%g3
-	addccc		%%g2, %%g4, %%g4
+	addccc		%%g2, %%g7, %%g7
 	lduw		[%1 + 0x0c], %%g2
-	addccc		%%g3, %%g4, %%g4
+	addccc		%%g3, %%g7, %%g7
 	lduw		[%1 + 0x08], %%g3
-	addccc		%%g2, %%g4, %%g4
+	addccc		%%g2, %%g7, %%g7
 	lduw		[%1 + 0x04], %%g2
-	addccc		%%g3, %%g4, %%g4
+	addccc		%%g3, %%g7, %%g7
 	lduw		[%1 + 0x00], %%g3
-	addccc		%%g2, %%g4, %%g4
-	addccc		%%g3, %%g4, %0
+	addccc		%%g2, %%g7, %%g7
+	addccc		%%g3, %%g7, %0
 	addc		0, %0, %0
 "	: "=&r" (sum)
 	: "r" (saddr), "r" (daddr), "r"(htonl((__u32) (len))),
 	  "r"(htonl(proto)), "r"(sum)
-	: "g2", "g3", "g4");
+	: "g2", "g3", "g7");
 
 	return csum_fold(sum);
 }
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/dma.h linux/include/asm-sparc64/dma.h
--- v2.1.29/linux/include/asm-sparc64/dma.h	Mon Dec 30 02:00:02 1996
+++ linux/include/asm-sparc64/dma.h	Thu Mar 20 16:45:06 1997
@@ -1,4 +1,4 @@
-/* $Id: dma.h,v 1.2 1996/12/26 13:25:24 davem Exp $
+/* $Id: dma.h,v 1.3 1997/03/14 21:05:36 jj Exp $
  * include/asm-sparc64/dma.h
  *
  * Copyright 1996 (C) David S. Miller (davem@caip.rutgers.edu)
@@ -73,7 +73,6 @@
 #define DMA_ISESC1(dma)      ((dma)->revision == dvmaesc1)
 
 /* Main routines in dma.c */
-extern void dump_dma_regs(struct sparc_dma_registers *);
 extern unsigned long dvma_init(struct linux_sbus *, unsigned long);
 
 /* Fields in the cond_reg register */
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/floppy.h linux/include/asm-sparc64/floppy.h
--- v2.1.29/linux/include/asm-sparc64/floppy.h	Fri Dec 13 01:37:47 1996
+++ linux/include/asm-sparc64/floppy.h	Thu Mar 20 16:45:06 1997
@@ -1,7 +1,8 @@
-/* $Id: floppy.h,v 1.1 1996/11/20 15:31:07 davem Exp $
+/* $Id: floppy.h,v 1.2 1997/03/14 21:05:25 jj Exp $
  * asm-sparc64/floppy.h: Sparc specific parts of the Floppy driver.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #ifndef __ASM_SPARC64_FLOPPY_H
@@ -11,9 +12,9 @@
 #include <asm/pgtable.h>
 #include <asm/system.h>
 #include <asm/idprom.h>
-#include <asm/machines.h>
 #include <asm/oplib.h>
 #include <asm/auxio.h>
+#include <asm/sbus.h>
 #include <asm/irq.h>
 
 /* References:
@@ -278,43 +279,24 @@
 static int sun_floppy_init(void)
 {
 	char state[128];
-	int tnode, fd_node, num_regs;
+	int fd_node, num_regs;
+	struct linux_sbus *bus;
+	struct linux_sbus_device *sdev;
 
 	use_virtual_dma = 1;
 	
 	FLOPPY_IRQ = 11;
-	/* Forget it if we aren't on a machine that could possibly
-	 * ever have a floppy drive.
-	 */
-	if((sparc_cpu_model != sun4c && sparc_cpu_model != sun4m) ||
-	   ((idprom->id_machtype == (SM_SUN4C | SM_4C_SLC)) ||
-	    (idprom->id_machtype == (SM_SUN4C | SM_4C_ELC)))) {
-		/* We certainly don't have a floppy controller. */
-		goto no_sun_fdc;
-	}
-	/* Well, try to find one. */
-	tnode = prom_getchild(prom_root_node);
-	fd_node = prom_searchsiblings(tnode, "obio");
-	if(fd_node != 0) {
-		tnode = prom_getchild(fd_node);
-		fd_node = prom_searchsiblings(tnode, "SUNW,fdtwo");
-	} else {
-		fd_node = prom_searchsiblings(tnode, "fd");
-	}
-	if(fd_node == 0) {
-		goto no_sun_fdc;
-	}
-
-	/* The sun4m lets us know if the controller is actually usable. */
-	if(sparc_cpu_model == sun4m) {
-		prom_getproperty(fd_node, "status", state, sizeof(state));
-		if(!strcmp(state, "disabled")) {
-			goto no_sun_fdc;
-		}
+	for_all_sbusdev (sdev, bus) {
+		if (!strcmp(sdev->prom_name, "SUNW,fdtwo")) 
+			break;
 	}
+	if (!bus) return -1;
+	fd_node = sdev->prom_node;
+	prom_getproperty(fd_node, "status", state, sizeof(state));
+	if(!strcmp(state, "disabled")) return -1;
 	num_regs = prom_getproperty(fd_node, "reg", (char *) fd_regs, sizeof(fd_regs));
 	num_regs = (num_regs / sizeof(fd_regs[0]));
-	prom_apply_obio_ranges(fd_regs, num_regs);
+	prom_apply_sbus_ranges(sdev->my_bus, fd_regs, num_regs, sdev);
 	sun_fdc = (struct sun_flpy_controller *) sparc_alloc_io(fd_regs[0].phys_addr,
 								0x0,
 								fd_regs[0].reg_size,
@@ -324,26 +306,16 @@
 	/* Last minute sanity check... */
 	if(sun_fdc->status_82072 == 0xff) {
 		sun_fdc = NULL;
-		goto no_sun_fdc;
+		return -1;
 	}
 
-        if(sparc_cpu_model == sun4c) {
-                sun_fdops.fd_inb = sun_82072_fd_inb;
-                sun_fdops.fd_outb = sun_82072_fd_outb;
-                fdc_status = &sun_fdc->status_82072;
-                /* printk("AUXIO @0x%p\n", auxio_register); */ /* P3 */
-        } else {
-                sun_fdops.fd_inb = sun_82077_fd_inb;
-                sun_fdops.fd_outb = sun_82077_fd_outb;
-                fdc_status = &sun_fdc->status_82077;
-                /* printk("DOR @0x%p\n", &sun_fdc->dor_82077); */ /* P3 */
-	}
+        sun_fdops.fd_inb = sun_82077_fd_inb;
+        sun_fdops.fd_outb = sun_82077_fd_outb;
+        fdc_status = &sun_fdc->status_82077;
+        /* printk("DOR @0x%p\n", &sun_fdc->dor_82077); */ /* P3 */
 
 	/* Success... */
 	return (int) sun_fdc;
-
-no_sun_fdc:
-	return -1;
 }
 
 static int sparc_eject(void)
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/head.h linux/include/asm-sparc64/head.h
--- v2.1.29/linux/include/asm-sparc64/head.h	Mon Mar 17 14:54:33 1997
+++ linux/include/asm-sparc64/head.h	Thu Mar 20 16:45:07 1997
@@ -1,7 +1,8 @@
-/* $Id: head.h,v 1.4 1997/02/25 20:00:32 jj Exp $ */
+/* $Id: head.h,v 1.7 1997/03/18 18:00:36 jj Exp $ */
 #ifndef _SPARC64_HEAD_H
 #define _SPARC64_HEAD_H
 
+#define KERNBASE    0xfffff80000000000
 #define BOOT_KERNEL b sparc64_boot; nop; nop; nop; nop; nop; nop; nop;
 
 #define CLEAN_WINDOW							\
@@ -14,29 +15,38 @@
 	nop;		nop;		nop;		nop;
 
 #define TRAP(routine)					\
-	b	etrap;					\
+	ba,pt	%xcc, etrap;				\
 	 rd	%pc, %g7;				\
 	call	routine;				\
 	 add	%sp, STACK_BIAS + REGWIN_SZ, %o0;	\
-	b	rtrap;					\
-	 subcc	%g0, %o0, %g0;				\
+	ba,pt	%xcc, rtrap;				\
+	 nop;						\
 	nop;						\
 	nop;
 
 #define TRAP_ARG(routine, arg)				\
-	b	etrap;					\
+	ba,pt	%xcc, etrap;				\
 	 rd	%pc, %g7;				\
 	add	%sp, STACK_BIAS + REGWIN_SZ, %o0;	\
 	call	routine;				\
 	 mov	arg, %o1;				\
-	b	rtrap;					\
-	 subcc	%g0, %o0, %g0;				\
+	ba,pt	%xcc, rtrap;				\
+	 nop;						\
 	nop;
+	
+#define SYSCALL_TRAP(routine, systbl)			\
+	ba,pt	%xcc, etrap;				\
+	 rd	%pc, %g7;				\
+	sethi	%hi(systbl), %l7;			\
+	call	routine;				\
+	 or	%l7, %lo(systbl), %l7;			\
+	nop; nop; nop;
+	
 
+#define SUNOS_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sunos_sys_table)
+#define	LINUX_32BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sys_call_table32)
+#define LINUX_64BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sys_call_table64)
 /* FIXME: Write these actually */	
-#define SUNOS_SYSCALL_TRAP TRAP(sunos_syscall)
-#define	LINUX_32BIT_SYSCALL_TRAP TRAP(linux32_syscall)
-#define LINUX_64BIT_SYSCALL_TRAP TRAP(linux64_syscall)
 #define NETBSD_SYSCALL_TRAP TRAP(netbsd_syscall)
 #define SOLARIS_SYSCALL_TRAP TRAP(solaris_syscall)
 #define BREAKPOINT_TRAP TRAP(breakpoint_trap)
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/init.h linux/include/asm-sparc64/init.h
--- v2.1.29/linux/include/asm-sparc64/init.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/init.h	Thu Mar 20 16:45:07 1997
@@ -0,0 +1,27 @@
+#ifndef _SPARC64_INIT_H
+#define _SPARC64_INIT_H
+
+#ifndef __init
+/* 'cause of buggy linker, we don't use this for now... */
+#if 0 && !defined (MODULE)
+#define __init __attribute__ ((__section__ (".text.init")))
+#define __initdata __attribute__ ((__section__ (".data.init")))
+#define __initfunc(__arginit) \
+	__arginit __init; \
+	__arginit
+/* For assembly routines */
+#define __INIT		.section	".text.init",#alloc,#execinstr
+#define __FINIT	.previous
+#define __INITDATA	.section	".data.init",#alloc,#write
+#else
+#define	__init
+#define __initdata
+#define __initfunc(__arginit) __arginit
+/* For assembly routines */
+#define __INIT
+#define __FINIT
+#define __INITDATA
+#endif
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/io.h linux/include/asm-sparc64/io.h
--- v2.1.29/linux/include/asm-sparc64/io.h	Mon Mar 17 14:54:33 1997
+++ linux/include/asm-sparc64/io.h	Thu Mar 20 16:45:07 1997
@@ -1,4 +1,4 @@
-/* $Id: io.h,v 1.3 1997/03/03 16:51:53 jj Exp $ */
+/* $Id: io.h,v 1.5 1997/03/18 18:04:00 jj Exp $ */
 #ifndef __SPARC64_IO_H
 #define __SPARC64_IO_H
 
@@ -7,12 +7,88 @@
 #include <asm/page.h>      /* IO address mapping routines need this */
 #include <asm/system.h>
 
-extern void sparc_ultra_mapioaddr   (unsigned long physaddr, unsigned long virt_addr, int bus, int rdonly);
+extern __inline__ unsigned long inb_local(unsigned long addr)
+{
+	return 0;
+}
+
+extern __inline__ void outb_local(unsigned char b, unsigned long addr)
+{
+	return;
+}
+
+extern __inline__ unsigned long inb(unsigned long addr)
+{
+	return 0;
+}
+
+extern __inline__ unsigned long inw(unsigned long addr)
+{
+	return 0;
+}
+
+extern __inline__ unsigned long inl(unsigned long addr)
+{
+	return 0;
+}
+
+extern __inline__ void outb(unsigned char b, unsigned long addr)
+{
+	return;
+}
+
+extern __inline__ void outw(unsigned short b, unsigned long addr)
+{
+	return;
+}
+
+extern __inline__ void outl(unsigned int b, unsigned long addr)
+{
+	return;
+}
+
+/*
+ * Memory functions
+ */
+extern __inline__ unsigned long readb(unsigned long addr)
+{
+	return 0;
+}
+
+extern __inline__ unsigned long readw(unsigned long addr)
+{
+	return 0;
+}
+
+extern __inline__ unsigned long readl(unsigned long addr)
+{
+	return 0;
+}
+
+extern __inline__ void writeb(unsigned short b, unsigned long addr)
+{
+	return;
+}
+
+extern __inline__ void writew(unsigned short b, unsigned long addr)
+{
+	return;
+}
+
+extern __inline__ void writel(unsigned int b, unsigned long addr)
+{
+	return;
+}
+
+#define inb_p inb
+#define outb_p outb
+
+extern void sparc_ultra_mapioaddr   (unsigned long physaddr, unsigned long virt_addr, int rdonly);
 extern void sparc_ultra_unmapioaddr (unsigned long virt_addr);
 
-extern __inline__ void mapioaddr (unsigned long physaddr, unsigned long virt_addr, int bus, int rdonly)
+extern __inline__ void mapioaddr (unsigned long physaddr, unsigned long virt_addr, int rdonly)
 {
-	sparc_ultra_mapioaddr (physaddr, virt_addr, bus, rdonly);
+	sparc_ultra_mapioaddr (physaddr, virt_addr, rdonly);
 	return;
 }
 
@@ -22,7 +98,7 @@
 	return;
 }
 
-extern void *sparc_alloc_io (void *, void *, int, char *, int, int);
+extern void *sparc_alloc_io (void *, void *, int, char *, unsigned, int);
 extern void sparc_free_io (void *, int);
 extern void *sparc_dvma_malloc (int, char *);
 
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/kbio.h linux/include/asm-sparc64/kbio.h
--- v2.1.29/linux/include/asm-sparc64/kbio.h	Fri Dec 13 01:37:47 1996
+++ linux/include/asm-sparc64/kbio.h	Thu Mar 20 16:45:07 1997
@@ -1,4 +1,3 @@
-/* $Id: kbio.h,v 1.1 1996/12/02 00:06:41 davem Exp $ */
 #ifndef __LINUX_KBIO_H
 #define __LINUX_KBIO_H
 
@@ -32,6 +31,18 @@
 
 /* Get keyboard leds */
 #define KIOCGLED    _IOR('k', 15, unsigned char)
+
+/* Used by KIOC[GS]RATE */
+struct kbd_rate {
+	unsigned char delay;	/* Delay in Hz before first repeat.	*/
+	unsigned char rate;	/* In characters per second (0..50).	*/
+};
+
+/* Set keyboard rate */
+#define KIOCSRATE   _IOW('k', 40, struct kbd_rate)
+
+/* Get keyboard rate */
+#define KIOCGRATE   _IOW('k', 41, struct kbd_rate)
 
 /* Top bit records if the key is up or down */
 #define KBD_UP      0x80
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/kdebug.h linux/include/asm-sparc64/kdebug.h
--- v2.1.29/linux/include/asm-sparc64/kdebug.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/kdebug.h	Thu Mar 20 16:45:07 1997
@@ -0,0 +1,79 @@
+/* $Id: kdebug.h,v 1.1 1997/03/14 21:05:34 jj Exp $
+ * kdebug.h:  Defines and definitions for debugging the Linux kernel
+ *            under various kernel debuggers.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _SPARC64_KDEBUG_H
+#define _SPARC64_KDEBUG_H
+
+#include <asm/openprom.h>
+
+/* The debugger lives in 1MB of virtual address space right underneath
+ * the boot prom.
+ */
+
+#define DEBUG_FIRSTVADDR       0xffc00000
+#define DEBUG_LASTVADDR        LINUX_OPPROM_BEGVM
+
+/* Breakpoints are enter through trap table entry 126.  So in sparc assembly
+ * if you want to drop into the debugger you do:
+ *
+ * t DEBUG_BP_TRAP
+ */
+
+#define DEBUG_BP_TRAP     126
+
+#ifndef __ASSEMBLY__
+/* The debug vector is passed in %o1 at boot time.  It is a pointer to
+ * a structure in the debuggers address space.  Here is its format.
+ */
+
+typedef unsigned int (*debugger_funct)(void);
+
+struct kernel_debug {
+	/* First the entry point into the debugger.  You jump here
+	 * to give control over to the debugger.
+	 */
+	unsigned long kdebug_entry;
+	unsigned long kdebug_trapme;   /* Figure out later... */
+	/* The following is the number of pages that the debugger has
+	 * taken from to total pool.
+	 */
+	unsigned long *kdebug_stolen_pages;
+	/* Ok, after you remap yourself and/or change the trap table
+	 * from what you were left with at boot time you have to call
+	 * this synchronization function so the debugger can check out
+	 * what you have done.
+	 */
+	debugger_funct teach_debugger;
+}; /* I think that is it... */
+
+extern struct kernel_debug *linux_dbvec;
+
+/* Use this macro in C-code to enter the debugger. */
+extern __inline__ void sp_enter_debugger(void)
+{
+	__asm__ __volatile__("jmpl %0, %%o7\n\t"
+			     "nop\n\t" : :
+			     "r" (linux_dbvec) : "o7", "memory");
+}
+
+#define SP_ENTER_DEBUGGER do { \
+	     if((linux_dbvec!=0) && ((*(short *)linux_dbvec)!=-1)) \
+	       sp_enter_debugger(); \
+		       } while(0)
+
+#endif /* !(__ASSEMBLY__) */
+
+/* Some nice offset defines for assembler code. */
+#define KDEBUG_ENTRY_OFF    0x0
+#define KDEBUG_DUNNO_OFF    0x4
+#define KDEBUG_DUNNO2_OFF   0x8
+#define KDEBUG_TEACH_OFF    0xc
+
+/* ugh... */
+#define TRAP_TRACE(reg1, reg2) \
+     
+
+#endif /* !(_SPARC64_KDEBUG_H) */
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/machines.h linux/include/asm-sparc64/machines.h
--- v2.1.29/linux/include/asm-sparc64/machines.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/machines.h	Thu Mar 20 16:45:07 1997
@@ -0,0 +1,69 @@
+/* $Id: machines.h,v 1.1 1997/03/14 21:05:37 jj Exp $
+ * machines.h:  Defines for taking apart the machine type value in the
+ *              idprom and determining the kind of machine we are on.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _SPARC64_MACHINES_H
+#define _SPARC64_MACHINES_H
+
+struct Sun_Machine_Models {
+	char *name;
+	unsigned char id_machtype;
+};
+
+/* Current number of machines we know about that has an IDPROM
+ * machtype entry including one entry for the 0x80 OBP machines.
+ */
+#define NUM_SUN_MACHINES   15
+
+extern struct Sun_Machine_Models Sun_Machines[NUM_SUN_MACHINES];
+
+/* The machine type in the idprom area looks like this:
+ *
+ * ---------------
+ * | ARCH | MACH |
+ * ---------------
+ *  7    4 3    0
+ *
+ * The ARCH field determines the architecture line (sun4, sun4c, etc).
+ * The MACH field determines the machine make within that architecture.
+ */
+
+#define SM_ARCH_MASK  0xf0
+#define SM_SUN4       0x20
+#define SM_SUN4C      0x50
+#define SM_SUN4M      0x70
+#define SM_SUN4M_OBP  0x80
+
+#define SM_TYP_MASK   0x0f
+/* Sun4 machines */
+#define SM_4_260      0x01    /* Sun 4/200 series */
+#define SM_4_110      0x02    /* Sun 4/100 series */
+#define SM_4_330      0x03    /* Sun 4/300 series */
+#define SM_4_470      0x04    /* Sun 4/400 series */
+
+/* Sun4c machines                Full Name              - PROM NAME */
+#define SM_4C_SS1     0x01    /* Sun4c SparcStation 1   - Sun 4/60  */
+#define SM_4C_IPC     0x02    /* Sun4c SparcStation IPC - Sun 4/40  */
+#define SM_4C_SS1PLUS 0x03    /* Sun4c SparcStation 1+  - Sun 4/65  */
+#define SM_4C_SLC     0x04    /* Sun4c SparcStation SLC - Sun 4/20  */
+#define SM_4C_SS2     0x05    /* Sun4c SparcStation 2   - Sun 4/75  */
+#define SM_4C_ELC     0x06    /* Sun4c SparcStation ELC - Sun 4/25  */
+#define SM_4C_IPX     0x07    /* Sun4c SparcStation IPX - Sun 4/50  */
+
+/* Sun4m machines, these predate the OpenBoot.  These values only mean
+ * something if the value in the ARCH field is SM_SUN4M, if it is
+ * SM_SUN4M_OBP then you have the following situation:
+ * 1) You either have a sun4d, a sun4e, or a recently made sun4m.
+ * 2) You have to consult OpenBoot to determine which machine this is.
+ */
+#define SM_4M_SS60    0x01    /* Sun4m SparcSystem 600                  */
+#define SM_4M_SS50    0x02    /* Sun4m SparcStation 10                  */
+#define SM_4M_SS40    0x03    /* Sun4m SparcStation 5                   */
+
+/* Sun4d machines -- N/A */
+/* Sun4e machines -- N/A */
+/* Sun4u machines -- N/A */
+
+#endif /* !(_SPARC64_MACHINES_H) */
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/openprom.h linux/include/asm-sparc64/openprom.h
--- v2.1.29/linux/include/asm-sparc64/openprom.h	Mon Mar 17 14:54:33 1997
+++ linux/include/asm-sparc64/openprom.h	Thu Mar 20 16:45:07 1997
@@ -1,4 +1,4 @@
-/* $Id: openprom.h,v 1.2 1997/02/25 12:40:41 jj Exp $ */
+/* $Id: openprom.h,v 1.3 1997/03/18 18:03:20 jj Exp $ */
 #ifndef __SPARC64_OPENPROM_H
 #define __SPARC64_OPENPROM_H
 
@@ -186,7 +186,7 @@
 #define PROMINTR_MAX    15
 
 struct linux_prom_registers {
-	int which_io;         /* is this in OBIO space? */
+	unsigned which_io;         /* hi part of physical address */
 	unsigned phys_addr;   /* The physical address of this register */
 	int reg_size;         /* How many bytes does this register take up? */
 };
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/oplib.h linux/include/asm-sparc64/oplib.h
--- v2.1.29/linux/include/asm-sparc64/oplib.h	Mon Mar 17 14:54:33 1997
+++ linux/include/asm-sparc64/oplib.h	Thu Mar 20 16:45:07 1997
@@ -1,4 +1,4 @@
-/* $Id: oplib.h,v 1.4 1997/02/25 20:00:34 jj Exp $
+/* $Id: oplib.h,v 1.6 1997/03/18 18:01:30 jj Exp $
  * oplib.h:  Describes the interface and available routines in the
  *           Linux Prom library.
  *
@@ -29,6 +29,9 @@
  */
 extern int prom_root_node;
 
+/* PROM stdin and stdout */
+extern int prom_stdin, prom_stdout;
+
 /* /chosen node of the prom device tree, this stays constant after
  * initialization is complete.
  */
@@ -107,7 +110,7 @@
 /* Enter the prom, with no chance of continuation for the stand-alone
  * which calls this.
  */
-extern void prom_halt(void);
+extern void prom_halt(void) __attribute__ ((noreturn));
 
 /* Set the PROM 'sync' callback function to the passed function pointer.
  * When the user gives the 'sync' command at the prom prompt while the
@@ -189,6 +192,16 @@
 extern int prom_restartcpu(int cpunode);
 
 /* PROM device tree traversal functions... */
+
+#ifdef PROMLIB_INTERNAL
+
+/* Internal version of prom_getchild. */
+extern int __prom_getchild(int parent_node);
+
+/* Internal version of prom_getsibling. */
+extern int __prom_getsibling(int node);
+
+#endif
 
 /* Get the child node of the given node, or zero if no child exists. */
 extern int prom_getchild(int parent_node);
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/pgtable.h linux/include/asm-sparc64/pgtable.h
--- v2.1.29/linux/include/asm-sparc64/pgtable.h	Mon Mar 17 14:54:33 1997
+++ linux/include/asm-sparc64/pgtable.h	Thu Mar 20 16:45:07 1997
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.10 1997/03/03 16:51:54 jj Exp $
+/* $Id: pgtable.h,v 1.13 1997/03/13 16:25:05 jj Exp $
  * pgtable.h: SpitFire page table operations.
  *
  * Copyright 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -83,17 +83,20 @@
 #define __DIRTY_BITS	(_PAGE_MODIFIED | _PAGE_WRITE | _PAGE_W)
 #define __ACCESS_BITS	(_PAGE_ACCESSED | _PAGE_READ | _PAGE_R)
 
+/* David: Please FIXME these. I just define them somehow to get it compile. jj */
+#define PAGE_NONE	__pgprot (_PAGE_VALID | _PAGE_CACHE | _PAGE_P | _PAGE_G | __ACCESS_BITS)
+#define PAGE_SHARED	__pgprot (_PAGE_VALID | _PAGE_CACHE | _PAGE_G | __ACCESS_BITS | _PAGE_W | _PAGE_WRITE)
+#define PAGE_COPY	__pgprot (_PAGE_VALID | _PAGE_CACHE | __ACCESS_BITS)
+#define PAGE_READONLY	__pgprot (_PAGE_VALID | _PAGE_CACHE | __ACCESS_BITS)
+#define PAGE_KERNEL	__pgprot (_PAGE_VALID | _PAGE_CACHE | _PAGE_P | __ACCESS_BITS | __DIRTY_BITS)
+#define PAGE_INVALID	__pgprot (0)
+
 #define _PFN_MASK	_PAGE_PADDR
 
 #define _PAGE_CHG_MASK	(_PFN_MASK | _PAGE_MODIFIED | _PAGE_ACCESSED)
 
-#define PAGE_NONE	__pgprot(_PAGE_PRESENT | _PAGE_CACHE)
-#define PAGE_SHARED	__pgprot(_PAGE_PRESENT | __ACCESS_BITS | \
-				 _PAGE_WRITE | _PAGE_CACHE)
-#define PAGE_COPY	__pgprot(_PAGE_PRESENT | __ACCESS_BITS | _PAGE_CACHE)
-#define PAGE_READONLY	__pgprot(_PAGE_PRESENT | __ACCESS_BITS | _PAGE_CACHE)
-#define PAGE_KERNEL	__pgprot(_PAGE_PRESENT | _PAGE_VALID | _PAGE_W| \
-				 _PAGE_CACHE | _PAGE_P | _PAGE_G)
+/* FIXME: Define this correctly to io page protection. Has to include side effects. */
+#define pg_iobits (_PAGE_VALID | _PAGE_P | __ACCESS_BITS | _PAGE_E)
 
 #define __P000	PAGE_NONE
 #define __P001	PAGE_READONLY
@@ -373,13 +376,13 @@
 	paddr = ((unsigned long) pgdir) - PAGE_OFFSET;
 
 	if(tsk->mm == current->mm) {
-		__asm__ __volatile__("
-			rdpr		%%pstate, %%g1
-			wrpr		%%g1, %2, %%pstate
+		__asm__ __volatile__ ("
+			rdpr		%%pstate, %%o4
+			wrpr		%%o4, %1, %%pstate
 			mov		%0, %%g7
-			wrpr		%%g1, 0x0, %%pstate
+			wrpr		%%o4, 0x0, %%pstate
 		" : : "r" (paddr), "i" (PSTATE_MG|PSTATE_IE)
-		  : "g1", "g2");
+		  : "o4");
 	}
 }
 
@@ -575,9 +578,42 @@
 extern inline pte_t mk_swap_pte(unsigned long type, unsigned long offset)
 { pte_t pte; pte_val(pte) = (type) | (offset << 8); return pte; }
 
+extern inline pte_t mk_pte_io(unsigned long page, pgprot_t prot, int space)
+/* FIXME. How is space added to the address??? */
+{ pte_t pte; pte_val(pte) = (page) | pgprot_val(prot); return pte; }
+
+
 #define SWP_TYPE(entry)		(((entry) & 0xff))
 #define SWP_OFFSET(entry)	((entry) >> 8)
 #define SWP_ENTRY(type,offset)	pte_val(mk_swap_pte((type),(offset)))
+
+struct ctx_list {
+	struct ctx_list *next;
+	struct ctx_list *prev;
+	unsigned int ctx_number;
+	struct mm_struct *ctx_mm;
+};
+
+extern struct ctx_list *ctx_list_pool;  /* Dynamically allocated */
+extern struct ctx_list ctx_free;        /* Head of free list */
+extern struct ctx_list ctx_used;        /* Head of used contexts list */
+
+#define NO_CONTEXT     -1
+
+extern __inline__ void remove_from_ctx_list(struct ctx_list *entry)
+{
+	entry->next->prev = entry->prev;
+	entry->prev->next = entry->next;
+}
+
+extern __inline__ void add_to_ctx_list(struct ctx_list *head, struct ctx_list *entry)
+{
+	entry->next = head;
+	(entry->prev = head->prev)->next = entry;
+	head->prev = entry;
+}
+#define add_to_free_ctxlist(entry) add_to_ctx_list(&ctx_free, entry)
+#define add_to_used_ctxlist(entry) add_to_ctx_list(&ctx_used, entry)
 
 #endif /* !(__ASSEMBLY__) */
 
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/processor.h linux/include/asm-sparc64/processor.h
--- v2.1.29/linux/include/asm-sparc64/processor.h	Mon Mar 17 14:54:33 1997
+++ linux/include/asm-sparc64/processor.h	Thu Mar 20 16:45:07 1997
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.8 1997/03/04 16:27:33 jj Exp $
+/* $Id: processor.h,v 1.10 1997/03/14 21:05:39 jj Exp $
  * include/asm-sparc64/processor.h
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -59,7 +59,7 @@
 	struct reg_window reg_window[NSWINS] __attribute__ ((aligned (16)));
 	unsigned long rwbuf_stkptrs[NSWINS] __attribute__ ((aligned (8)));
 	unsigned long w_saved;
-
+	
 	/* Arch-specific task state flags, see below. */
 	unsigned long flags;
 
@@ -69,6 +69,9 @@
 
 	struct sigstack sstk_info;
 	int current_ds, new_signal;
+	
+	struct pt_regs *kregs;
+	
 	struct exec core_exec;     /* just what it says. */
 };
 
@@ -90,7 +93,7 @@
 /* FPU status, FPU qdepth, FPU queue */ 				\
    0,          0,          { { 0, 0, }, }, 				\
 /* user_globals */ 							\
-   { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 					\
+   { 0, 0, 0, 0, 0, 0, 0, 0 }, 						\
 /* ksp, kpc */ 								\
    0,   0, 								\
 /* reg_window */							\
@@ -105,8 +108,8 @@
    0,           0,							\
 /* ex,     sstk_info, current_ds, */					\
    { 0, 0, }, USER_DS,							\
-/* new_signal */							\
-  0,									\
+/* new_signal, kregs */							\
+  0,           0,							\
 /* core_exec */								\
 { 0, },									\
 }
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/smp_lock.h linux/include/asm-sparc64/smp_lock.h
--- v2.1.29/linux/include/asm-sparc64/smp_lock.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/smp_lock.h	Thu Mar 20 16:45:07 1997
@@ -10,7 +10,10 @@
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
 
-#ifdef __SMP__
+#ifndef __SMP__
+#define lock_kernel()           do { } while(0)
+#define unlock_kernel()         do { } while(0)
+#else
 
 extern __inline__ __volatile__ unsigned char ldstub(volatile unsigned char *lock)
 {
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/socket.h linux/include/asm-sparc64/socket.h
--- v2.1.29/linux/include/asm-sparc64/socket.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/socket.h	Thu Mar 20 16:45:07 1997
@@ -1,4 +1,4 @@
-/* $Id: socket.h,v 1.1 1996/12/26 14:22:39 davem Exp $ */
+/* $Id: socket.h,v 1.2 1997/03/17 04:50:55 davem Exp $ */
 #ifndef _ASM_SOCKET_H
 #define _ASM_SOCKET_H
 
@@ -34,5 +34,10 @@
 /* Linux specific, keep the same. */
 #define SO_NO_CHECK	0x000b
 #define SO_PRIORITY	0x000c
+
+/* Security levels - as per NRL IPv6 - don't actually do anything */
+#define SO_SECURITY_AUTHENTICATION		0x000d
+#define SO_SECURITY_ENCRYPTION_TRANSPORT	0x000e
+#define SO_SECURITY_ENCRYPTION_NETWORK		0x000f
 
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/system.h linux/include/asm-sparc64/system.h
--- v2.1.29/linux/include/asm-sparc64/system.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/system.h	Thu Mar 20 16:45:07 1997
@@ -1,10 +1,38 @@
-/* $Id: system.h,v 1.4 1996/12/28 18:39:56 davem Exp $ */
+/* $Id: system.h,v 1.7 1997/03/18 18:02:41 jj Exp $ */
 #ifndef __SPARC64_SYSTEM_H
 #define __SPARC64_SYSTEM_H
 
 #include <asm/ptrace.h>
 #include <asm/processor.h>
 
+#define NCPUS	4	/* No SMP yet */
+
+#define EMPTY_PGT       (&empty_bad_page)
+#define EMPTY_PGE       (&empty_bad_page_table)
+
+#ifndef __ASSEMBLY__
+/*
+ * Sparc (general) CPU types
+ */
+enum sparc_cpu {
+  sun4        = 0x00,
+  sun4c       = 0x01,
+  sun4m       = 0x02,
+  sun4d       = 0x03,
+  sun4e       = 0x04,
+  sun4u       = 0x05, /* V8 ploos ploos */
+  sun_unknown = 0x06,
+  ap1000      = 0x07, /* almost a sun4m */
+};
+                  
+#define sparc_cpu_model sun4u
+                  
+
+extern unsigned long empty_bad_page;
+extern unsigned long empty_bad_page_table;
+extern unsigned long empty_zero_page;
+#endif
+
 #define setipl(__new_ipl) \
 	__asm__ __volatile__("wrpr	%0, %%pil"  : : "r" (__new_ipl) : "memory")
 
@@ -111,6 +139,8 @@
 	__xchg_called_with_bad_pointer();
 	return x;
 }
+
+extern void die_if_kernel(char *str, struct pt_regs *regs) __attribute__ ((noreturn));
 
 #endif /* !(__ASSEMBLY__) */
 
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/termios.h linux/include/asm-sparc64/termios.h
--- v2.1.29/linux/include/asm-sparc64/termios.h	Sun Jan 26 02:07:47 1997
+++ linux/include/asm-sparc64/termios.h	Thu Mar 20 16:45:07 1997
@@ -1,4 +1,4 @@
-/* $Id: termios.h,v 1.3 1996/12/31 22:35:49 davem Exp $ */
+/* $Id: termios.h,v 1.4 1997/03/14 21:05:26 jj Exp $ */
 #ifndef _SPARC64_TERMIOS_H
 #define _SPARC64_TERMIOS_H
 
@@ -87,7 +87,7 @@
  * Translate a "termio" structure into a "termios". Ugh.
  */
 #define user_termio_to_kernel_termios(termios, termio) \
-do { \
+({ \
 	unsigned short tmp; \
 	get_user(tmp, &(termio)->c_iflag); \
 	(termios)->c_iflag = (0xffff0000 & ((termios)->c_iflag)) | tmp; \
@@ -98,7 +98,8 @@
 	get_user(tmp, &(termio)->c_lflag); \
 	(termios)->c_lflag = (0xffff0000 & ((termios)->c_lflag)) | tmp; \
 	copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \
-} while(0)
+	0; \
+})
 
 /*
  * Translate a "termios" structure into a "termio". Ugh.
@@ -106,7 +107,7 @@
  * Note the "fun" _VMIN overloading.
  */
 #define kernel_termios_to_user_termio(termio, termios) \
-do { \
+({ \
 	put_user((termios)->c_iflag, &(termio)->c_iflag); \
 	put_user((termios)->c_oflag, &(termio)->c_oflag); \
 	put_user((termios)->c_cflag, &(termio)->c_cflag); \
@@ -117,10 +118,11 @@
 		put_user((termios)->c_cc[VMIN], &(termio)->c_cc[_VMIN]); \
 		put_user((termios)->c_cc[VTIME], &(termio)->c_cc[_VTIME]); \
 	} \
-} while(0)
+	0; \
+})
 
 #define user_termios_to_kernel_termios(k, u) \
-do { \
+({ \
 	get_user((k)->c_iflag, &(u)->c_iflag); \
 	get_user((k)->c_oflag, &(u)->c_oflag); \
 	get_user((k)->c_cflag, &(u)->c_cflag); \
@@ -134,10 +136,11 @@
 		get_user((k)->c_cc[VMIN],  &(u)->c_cc[_VMIN]); \
 		get_user((k)->c_cc[VTIME], &(u)->c_cc[_VTIME]); \
 	} \
-} while(0)
+	0; \
+})
 
 #define kernel_termios_to_user_termios(u, k) \
-do { \
+({ \
 	put_user((k)->c_iflag, &(u)->c_iflag); \
 	put_user((k)->c_oflag, &(u)->c_oflag); \
 	put_user((k)->c_cflag, &(u)->c_cflag); \
@@ -151,7 +154,8 @@
 		put_user((k)->c_cc[VEOF], &(u)->c_cc[VEOF]); \
 		put_user((k)->c_cc[VEOL], &(u)->c_cc[VEOL]); \
 	} \
-} while(0)
+	0; \
+})
 
 #endif	/* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/uaccess.h linux/include/asm-sparc64/uaccess.h
--- v2.1.29/linux/include/asm-sparc64/uaccess.h	Mon Mar 17 14:54:34 1997
+++ linux/include/asm-sparc64/uaccess.h	Thu Mar 20 16:45:07 1997
@@ -1,4 +1,4 @@
-/* $Id: uaccess.h,v 1.6 1997/03/03 16:51:54 jj Exp $ */
+/* $Id: uaccess.h,v 1.8 1997/03/14 21:05:33 jj Exp $ */
 #ifndef _ASM_UACCESS_H
 #define _ASM_UACCESS_H
 
@@ -36,7 +36,7 @@
  */
 #define __user_ok(addr,size) ((addr) < PAGE_OFFSET)
 #define __kernel_ok (get_fs() == KERNEL_DS)
-#define __access_ok(addr,size) (__user_ok((addr) & get_fs(),(size)))
+#define __access_ok(addr,size) (__user_ok(((unsigned long)(addr)) & get_fs(),(size)))
 #define access_ok(type,addr,size) __access_ok((unsigned long)(addr),(size))
 
 extern inline int verify_area(int type, const void * addr, unsigned long size)
@@ -312,8 +312,8 @@
 })
 
 #define __copy_to_user(to,from,n)		\
-	__copy_user((unsigned long)(to),	\
-		    (unsigned long)(from), n)
+	__copy_user((void *)(to),	\
+		    (void *)(from), n)
 
 #define __copy_to_user_ret(to,from,n,retval) ({ \
 if (__copy_to_user(to,from,n)) \
@@ -335,20 +335,36 @@
 })
 
 #define __copy_from_user(to,from,n)		\
-	__copy_user((unsigned long)(to),	\
-		    (unsigned long)(from), n)
+	__copy_user((void *)(to),	\
+		    (void *)(from), n)
 
 #define __copy_from_user_ret(to,from,n,retval) ({ \
 if (__copy_from_user(to,from,n)) \
 	return retval; \
 })
 
-extern int __clear_user(unsigned long addr, int size);
+extern __inline__ __kernel_size_t __clear_user(void *addr, __kernel_size_t size)
+{
+  __kernel_size_t ret;
+  __asm__ __volatile__ ("
+	.section __ex_table,#alloc
+	.align 4
+	.word 1f,3
+	.previous
+1:
+	mov %2, %%o1
+	call __bzero
+	 mov %1, %%o0
+	mov %%o0, %0
+	" : "=r" (ret) : "r" (addr), "r" (size) :
+	"o0", "o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g5", "g7");
+  return ret;
+}
 
 #define clear_user(addr,n) ({ \
-unsigned long __clear_addr = (unsigned long) (addr); \
-int __clear_size = (int) (n); \
-int __clear_res; \
+void *__clear_addr = (void *) (addr); \
+__kernel_size_t __clear_size = (__kernel_size_t) (n); \
+__kernel_size_t __clear_res; \
 if(__clear_size && __access_ok(__clear_addr, __clear_size)) { \
 __clear_res = __clear_user(__clear_addr, __clear_size); \
 } else __clear_res = __clear_size; \
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/unistd.h linux/include/asm-sparc64/unistd.h
--- v2.1.29/linux/include/asm-sparc64/unistd.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/unistd.h	Thu Mar 20 16:45:07 1997
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.1 1996/12/26 14:22:43 davem Exp $ */
+/* $Id: unistd.h,v 1.2 1997/03/18 18:00:26 jj Exp $ */
 #ifndef _SPARC64_UNISTD_H
 #define _SPARC64_UNISTD_H
 
@@ -277,12 +277,10 @@
 type name(void) \
 { \
 long __res; \
-__asm__ __volatile__ ("or %%g0, %0, %%g1\n\t" \
-		      "t 0x10\n\t" \
-		      "bcc 1f\n\t" \
-		      "or %%g0, %%o0, %0\n\t" \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+		      "t 0x11\n\t" \
 		      "sub %%g0, %%o0, %0\n\t" \
-		      "1:\n\t" \
+		      "movcc %%xcc, %%o0, %0\n\t" \
 		      : "=r" (__res)\
 		      : "0" (__NR_##name) \
 		      : "g1", "o0"); \
@@ -296,13 +294,11 @@
 type name(type1 arg1) \
 { \
 long __res; \
-__asm__ __volatile__ ("or %%g0, %0, %%g1\n\t" \
-		      "or %%g0, %1, %%o0\n\t" \
-		      "t 0x10\n\t" \
-		      "bcc 1f\n\t" \
-		      "or %%g0, %%o0, %0\n\t" \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+		      "mov %1, %%o0\n\t" \
+		      "t 0x11\n\t" \
 		      "sub %%g0, %%o0, %0\n\t" \
-		      "1:\n\t" \
+		      "movcc %%xcc, %%o0, %0\n\t" \
 		      : "=r" (__res), "=r" ((long)(arg1)) \
 		      : "0" (__NR_##name),"1" ((long)(arg1)) \
 		      : "g1", "o0"); \
@@ -316,14 +312,12 @@
 type name(type1 arg1,type2 arg2) \
 { \
 long __res; \
-__asm__ __volatile__ ("or %%g0, %0, %%g1\n\t" \
-		      "or %%g0, %1, %%o0\n\t" \
-		      "or %%g0, %2, %%o1\n\t" \
-		      "t 0x10\n\t" \
-		      "bcc 1f\n\t" \
-		      "or %%g0, %%o0, %0\n\t" \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+		      "mov %1, %%o0\n\t" \
+		      "mov %2, %%o1\n\t" \
+		      "t 0x11\n\t" \
 		      "sub %%g0, %%o0, %0\n\t" \
-		      "1:\n\t" \
+		      "movcc %%xcc, %%o0, %0\n\t" \
 		      : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)) \
 		      : "0" (__NR_##name),"1" ((long)(arg1)),"2" ((long)(arg2)) \
 		      : "g1", "o0", "o1"); \
@@ -337,15 +331,13 @@
 type name(type1 arg1,type2 arg2,type3 arg3) \
 { \
 long __res; \
-__asm__ __volatile__ ("or %%g0, %0, %%g1\n\t" \
-		      "or %%g0, %1, %%o0\n\t" \
-		      "or %%g0, %2, %%o1\n\t" \
-		      "or %%g0, %3, %%o2\n\t" \
-		      "t 0x10\n\t" \
-		      "bcc 1f\n\t" \
-		      "or %%g0, %%o0, %0\n\t" \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+		      "mov %1, %%o0\n\t" \
+		      "mov %2, %%o1\n\t" \
+		      "mov %3, %%o2\n\t" \
+		      "t 0x11\n\t" \
 		      "sub %%g0, %%o0, %0\n\t" \
-		      "1:\n\t" \
+		      "movcc %%xcc, %%o0, %0\n\t" \
 		      : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
 		        "=r" ((long)(arg3)) \
 		      : "0" (__NR_##name), "1" ((long)(arg1)), "2" ((long)(arg2)), \
@@ -361,16 +353,14 @@
 type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
 { \
 long __res; \
-__asm__ __volatile__ ("or %%g0, %0, %%g1\n\t" \
-		      "or %%g0, %1, %%o0\n\t" \
-		      "or %%g0, %2, %%o1\n\t" \
-		      "or %%g0, %3, %%o2\n\t" \
-		      "or %%g0, %4, %%o3\n\t" \
-		      "t 0x10\n\t" \
-		      "bcc 1f\n\t" \
-		      "or %%g0, %%o0, %0\n\t" \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+		      "mov %1, %%o0\n\t" \
+		      "mov %2, %%o1\n\t" \
+		      "mov %3, %%o2\n\t" \
+		      "mov %4, %%o3\n\t" \
+		      "t 0x11\n\t" \
 		      "sub %%g0,%%o0, %0\n\t" \
-		      "1:\n\t" \
+		      "movcc %%xcc, %%o0, %0\n\t" \
 		      : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
 		        "=r" ((long)(arg3)), "=r" ((long)(arg4)) \
 		      : "0" (__NR_##name),"1" ((long)(arg1)),"2" ((long)(arg2)), \
@@ -388,17 +378,15 @@
 { \
       long __res; \
 \
-__asm__ __volatile__ ("or %%g0, %1, %%o0\n\t" \
-		      "or %%g0, %2, %%o1\n\t" \
-		      "or %%g0, %3, %%o2\n\t" \
-		      "or %%g0, %4, %%o3\n\t" \
-		      "or %%g0, %5, %%o4\n\t" \
-		      "or %%g0, %6, %%g1\n\t" \
-		      "t 0x10\n\t" \
-		      "bcc 1f\n\t" \
-		      "or %%g0, %%o0, %0\n\t" \
+__asm__ __volatile__ ("mov %1, %%o0\n\t" \
+		      "mov %2, %%o1\n\t" \
+		      "mov %3, %%o2\n\t" \
+		      "mov %4, %%o3\n\t" \
+		      "mov %5, %%o4\n\t" \
+		      "mov %6, %%g1\n\t" \
+		      "t 0x11\n\t" \
 		      "sub %%g0, %%o0, %0\n\t" \
-		      "1:\n\t" \
+		      "movcc %%xcc, %%o0, %0\n\t" \
 		      : "=r" (__res) \
 		      : "r" ((long)(arg1)),"r" ((long)(arg2)), \
 		        "r" ((long)(arg3)),"r" ((long)(arg4)),"r" ((long)(arg5)), \
@@ -461,16 +449,15 @@
 			   "mov %1, %%g1\n\t"
 			   "mov %2, %%o0\n\t"    /* Clone flags. */
 			   "mov 0, %%o1\n\t"     /* usp arg == 0 */
-			   "t 0x10\n\t"          /* Linux/Sparc clone(). */
-			   "cmp %%o1, 0\n\t"
-			   "be 1f\n\t"           /* The parent, just return. */
-			   " nop\n\t"            /* Delay slot. */
+			   "t 0x11\n\t"          /* Linux/Sparc clone(). */
+			   "brz,a,pn %%o1, 1f\n\t"           /* The parent, just return. */
+			   " mov %%o0, %0\n\t"
 			   "jmpl %%g2, %%o7\n\t" /* Call the function. */
 			   " mov %%g3, %%o0\n\t" /* Get back the arg in delay. */
 			   "mov %3, %%g1\n\t"
-			   "t 0x10\n\t"          /* Linux/Sparc exit(). */
+			   "t 0x11\n\t"          /* Linux/Sparc exit(). */
 			   /* Notreached by child. */
-			   "1: mov %%o0, %0\n\t" :
+			   "1:" :
 			   "=r" (retval) :
 			   "i" (__NR_clone), "r" (flags | CLONE_VM),
 			   "i" (__NR_exit),  "r" (fn), "r" (arg) :
diff -u --recursive --new-file v2.1.29/linux/include/asm-sparc64/vaddrs.h linux/include/asm-sparc64/vaddrs.h
--- v2.1.29/linux/include/asm-sparc64/vaddrs.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/vaddrs.h	Thu Mar 20 16:45:07 1997
@@ -0,0 +1,27 @@
+/* $Id: vaddrs.h,v 1.1 1997/03/18 18:03:43 jj Exp $ */
+#ifndef _SPARC64_VADDRS_H
+#define _SPARC64_VADDRS_H
+
+#include <asm/head.h>
+
+/* asm-sparc64/vaddrs.h:  Here will be define the virtual addresses at
+ *                      which important I/O addresses will be mapped.
+ *                      For instance the timer register virtual address
+ *                      is defined here.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+/* I can see only one reason why we should have statically defined
+ * mappings for devices and is the speedup improvements of not loading
+ * a pointer and then the value in the assembly code
+ */
+#define  IOBASE_VADDR   0xfffffd0000000000ULL  /* Base for mapping pages */
+#define  IOBASE_LEN     0x0000008000000000ULL  /* Length of the IO area */
+#define  IOBASE_END     0xfffffd8000000000ULL
+#define  DVMA_VADDR     0xfffffd8000000000ULL  /* Base area of the DVMA on suns */
+#define  DVMA_LEN       0x0000004000000000ULL  /* Size of the DVMA address space */
+#define  DVMA_END       0xfffffdc000000000ULL
+
+#endif /* !(_SPARC_VADDRS_H) */
+
diff -u --recursive --new-file v2.1.29/linux/include/linux/blk.h linux/include/linux/blk.h
--- v2.1.29/linux/include/linux/blk.h	Thu Feb  6 02:53:33 1997
+++ linux/include/linux/blk.h	Wed Mar 26 10:58:25 1997
@@ -440,15 +440,19 @@
 	struct request *req = CURRENT;
 #endif /* IDE_DRIVER */
 	struct buffer_head * bh;
+	int nsect;
 
 	req->errors = 0;
 	if (!uptodate) {
 		printk("end_request: I/O error, dev %s, sector %lu\n",
 			kdevname(req->rq_dev), req->sector);
-		req->nr_sectors--;
-		req->nr_sectors &= ~SECTOR_MASK;
-		req->sector += (BLOCK_SIZE / 512);
-		req->sector &= ~SECTOR_MASK;		
+		if ((bh = req->bh) != NULL) {
+			nsect = bh->b_size >> 9;
+			req->nr_sectors--;
+			req->nr_sectors &= ~(nsect - 1);
+			req->sector += nsect;
+			req->sector &= ~(nsect - 1);
+		}
 	}
 
 	if ((bh = req->bh) != NULL) {
diff -u --recursive --new-file v2.1.29/linux/include/linux/elf.h linux/include/linux/elf.h
--- v2.1.29/linux/include/linux/elf.h	Fri Dec 13 05:57:36 1996
+++ linux/include/linux/elf.h	Thu Mar 20 17:11:53 1997
@@ -179,7 +179,19 @@
 #define	R_SPARC_JMP_SLOT	21
 #define	R_SPARC_RELATIVE	22
 #define	R_SPARC_UA32		23
-#define	R_SPARC_NUM		24
+#define R_SPARC_PLT32		24
+#define R_SPARC_HIPLT22		25
+#define R_SPARC_LOPLT10		26
+#define R_SPARC_PCPLT32		27
+#define R_SPARC_PCPLT22		28
+#define R_SPARC_PCPLT10		29
+#define R_SPARC_10		30
+#define R_SPARC_11		31
+#define R_SPARC_WDISP16		40
+#define R_SPARC_WDISP19		41
+#define R_SPARC_7		43
+#define R_SPARC_5		44
+#define R_SPARC_6		45
 
 /*
  * 68k ELF relocation types
diff -u --recursive --new-file v2.1.29/linux/include/linux/etherdevice.h linux/include/linux/etherdevice.h
--- v2.1.29/linux/include/linux/etherdevice.h	Thu Feb  6 02:55:46 1997
+++ linux/include/linux/etherdevice.h	Wed Mar 26 10:59:18 1997
@@ -35,7 +35,8 @@
 extern unsigned short	eth_type_trans(struct sk_buff *skb, struct device *dev);
 extern void		eth_header_cache_update(struct hh_cache *hh, struct device *dev,
 						unsigned char * haddr);
-extern int		eth_header_cache(struct dst_entry *dst, struct dst_entry *neigh,
+extern int		eth_header_cache(struct dst_entry *dst,
+					 struct neighbour *neigh,
 					 struct hh_cache *hh);
 extern struct device	* init_etherdev(struct device *, int);
 
diff -u --recursive --new-file v2.1.29/linux/include/linux/firewall.h linux/include/linux/firewall.h
--- v2.1.29/linux/include/linux/firewall.h	Sun May 19 13:40:49 1996
+++ linux/include/linux/firewall.h	Wed Mar 26 11:01:28 1997
@@ -1,26 +1,29 @@
 #ifndef __LINUX_FIREWALL_H
 #define __LINUX_FIREWALL_H
 
+#include <linux/config.h>
+
 /*
  *	Definitions for loadable firewall modules
  */
 
-#define FW_BLOCK	0
-#define FW_ACCEPT	1
+#define FW_QUEUE	0
+#define FW_BLOCK	1
+#define FW_ACCEPT	2
 #define FW_REJECT	(-1)
-#define FW_REDIRECT	2
-#define FW_MASQUERADE	3
-#define FW_SKIP		4
+#define FW_REDIRECT	3
+#define FW_MASQUERADE	4
+#define FW_SKIP		5
 
 struct firewall_ops
 {
 	struct firewall_ops *next;
 	int (*fw_forward)(struct firewall_ops *this, int pf, 
-			struct device *dev, void *phdr, void *arg);
+			struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);
 	int (*fw_input)(struct firewall_ops *this, int pf, 
-			struct device *dev, void *phdr, void *arg);
+			struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);
 	int (*fw_output)(struct firewall_ops *this, int pf, 
-			struct device *dev, void *phdr, void *arg);
+			struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);
 	/* Data falling in the second 486 cache line isn't used directly
 	   during a firewall call and scan, only by insert/delete and other
 	   unusual cases
@@ -32,10 +35,27 @@
 #ifdef __KERNEL__
 extern int register_firewall(int pf, struct firewall_ops *fw);
 extern int unregister_firewall(int pf, struct firewall_ops *fw);
-extern int call_fw_firewall(int pf, struct device *dev, void *phdr, void *arg);
-extern int call_in_firewall(int pf, struct device *dev, void *phdr, void *arg);
-extern int call_out_firewall(int pf, struct device *dev, void *phdr, void *arg);
 extern void fwchain_init(void);
-#endif
+#ifdef CONFIG_FIREWALL
+extern int call_fw_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);
+extern int call_in_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);
+extern int call_out_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);
+#else
+extern __inline__ int call_fw_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
+{
+	return FW_ACCEPT;
+}
+
+extern __inline__ int call_in_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
+{
+	return FW_ACCEPT;
+}
 
+extern __inline__ int call_out_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
+{
+	return FW_ACCEPT;
+}
+
+#endif
+#endif
 #endif
diff -u --recursive --new-file v2.1.29/linux/include/linux/hdlcdrv.h linux/include/linux/hdlcdrv.h
--- v2.1.29/linux/include/linux/hdlcdrv.h	Thu Feb 27 10:57:31 1997
+++ linux/include/linux/hdlcdrv.h	Thu Mar 20 18:17:11 1997
@@ -7,6 +7,7 @@
 #ifndef _HDLCDRV_H
 #define _HDLCDRV_H
 
+#include <linux/version.h>
 #include <linux/sockios.h>
 #include <linux/version.h>
 #if LINUX_VERSION_CODE < 0x20119
@@ -38,23 +39,32 @@
 	               /* this just makes them send even if DCD is on */
 };	
 
-struct hdlcdrv_channel_state {
-	int ptt;
-	int dcd;
-	int ptt_keyed;
-#if LINUX_VERSION_CODE < 0x20119
-	struct enet_statistics stats;
-#else
-	struct net_device_stats stats;
+struct hdlcdrv_old_channel_state {
+  	int ptt;
+  	int dcd;
+  	int ptt_keyed;
+#if LINUX_VERSION_CODE < 0x20100
+  	struct enet_statistics stats;
 #endif
 };
 
+struct hdlcdrv_channel_state {
+ 	int ptt;
+ 	int dcd;
+ 	int ptt_keyed;
+ 	unsigned long tx_packets;
+ 	unsigned long tx_errors;
+ 	unsigned long rx_packets;
+ 	unsigned long rx_errors;
+};
+
 struct hdlcdrv_ioctl {
 	int cmd;
 	union {
 		struct hdlcdrv_params mp;
 		struct hdlcdrv_channel_params cp;
 		struct hdlcdrv_channel_state cs;
+		struct hdlcdrv_old_channel_state ocs;
 		unsigned int calibrate;
 		unsigned char bits;
 		char modename[128];
@@ -72,8 +82,9 @@
 #define HDLCDRVCTL_MODEMPARMASK      2  /* not handled by hdlcdrv */
 #define HDLCDRVCTL_GETCHANNELPAR    10
 #define HDLCDRVCTL_SETCHANNELPAR    11
-#define HDLCDRVCTL_GETSTAT          20
+#define HDLCDRVCTL_OLDGETSTAT       20
 #define HDLCDRVCTL_CALIBRATE        21
+#define HDLCDRVCTL_GETSTAT          22
 
 /*
  * these are mainly for debugging purposes
diff -u --recursive --new-file v2.1.29/linux/include/linux/icmpv6.h linux/include/linux/icmpv6.h
--- v2.1.29/linux/include/linux/icmpv6.h	Thu Feb  6 02:55:47 1997
+++ linux/include/linux/icmpv6.h	Wed Mar 26 10:59:18 1997
@@ -3,21 +3,22 @@
 
 #include <asm/byteorder.h>
 
-struct icmpv6hdr {
+struct icmp6hdr {
 
-	__u8		type;
-	__u8		code;
-	__u16		checksum;
+	__u8		icmp6_type;
+	__u8		icmp6_code;
+	__u16		icmp6_cksum;
 
 
 	union {
+		__u32			un_data32[1];
+		__u16			un_data16[2];
+		__u8			un_data8[4];
+
 		struct icmpv6_echo {
 			__u16		identifier;
 			__u16		sequence;
 		} u_echo;
-		__u32			pointer;
-		__u32			mtu;
-		__u32			unused;
 
                 struct icmpv6_nd_advt {
 #if defined(__LITTLE_ENDIAN_BITFIELD)
@@ -53,34 +54,37 @@
 			__u16		rt_lifetime;
                 } u_nd_ra;
 
-	} u;
+	} icmp6_dataun;
 
-#define icmp6_identifier	u.u_echo.identifier
-#define icmp6_sequence		u.u_echo.sequence
-#define icmp6_pointer		u.pointer
-#define icmp6_mtu		u.mtu
-#define icmp6_unused		u.unused
-#define icmp6_router		u.u_nd_advt.router
-#define icmp6_solicited		u.u_nd_advt.solicited
-#define icmp6_override		u.u_nd_advt.override
-#define icmp6_ndiscreserved	u.u_nd_advt.reserved
-#define icmp6_hop_limit		u.u_nd_ra.hop_limit
-#define icmp6_addrconf_managed	u.u_nd_ra.managed
-#define icmp6_addrconf_other	u.u_nd_ra.other
-#define icmp6_rt_lifetime	u.u_nd_ra.rt_lifetime
+#define icmp6_identifier	icmp6_dataun.u_echo.identifier
+#define icmp6_sequence		icmp6_dataun.u_echo.sequence
+#define icmp6_pointer		icmp6_dataun.un_data32[0]
+#define icmp6_mtu		icmp6_dataun.un_data32[0]
+#define icmp6_unused		icmp6_dataun.un_data32[0]
+#define icmp6_maxdelay		icmp6_dataun.un_data16[0]
+#define icmp6_router		icmp6_dataun.u_nd_advt.router
+#define icmp6_solicited		icmp6_dataun.u_nd_advt.solicited
+#define icmp6_override		icmp6_dataun.u_nd_advt.override
+#define icmp6_ndiscreserved	icmp6_dataun.u_nd_advt.reserved
+#define icmp6_hop_limit		icmp6_dataun.u_nd_ra.hop_limit
+#define icmp6_addrconf_managed	icmp6_dataun.u_nd_ra.managed
+#define icmp6_addrconf_other	icmp6_dataun.u_nd_ra.other
+#define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
 };
 
 
 #define ICMPV6_DEST_UNREACH		1
 #define ICMPV6_PKT_TOOBIG		2
-#define ICMPV6_TIME_EXCEEDED		3
-#define ICMPV6_PARAMETER_PROB		4
+#define ICMPV6_TIME_EXCEED		3
+#define ICMPV6_PARAMPROB		4
+
+#define ICMPV6_INFOMSG_MASK		0x80
 
 #define ICMPV6_ECHO_REQUEST		128
 #define ICMPV6_ECHO_REPLY		129
-#define ICMPV6_MEMBERSHIP_QUERY		130
-#define ICMPV6_MEMBERSHIP_REPORT       	131
-#define ICMPV6_MEMBERSHIP_REDUCTION    	132
+#define ICMPV6_MGM_QUERY		130
+#define ICMPV6_MGM_REPORT       	131
+#define ICMPV6_MGM_REDUCTION    	132
 
 /*
  *	Codes for Destination Unreachable
diff -u --recursive --new-file v2.1.29/linux/include/linux/in.h linux/include/linux/in.h
--- v2.1.29/linux/include/linux/in.h	Thu Feb  6 02:53:33 1997
+++ linux/include/linux/in.h	Wed Mar 26 10:58:25 1997
@@ -33,7 +33,6 @@
   IPPROTO_IDP = 22,		/* XNS IDP protocol			*/
 
   IPPROTO_IPV6	 = 41,		/* IPv6-in-IPv4 tunnelling		*/
-  IPPROTO_ICMPV6 = 58,		/* ICMPv6				*/
 
   IPPROTO_RAW	 = 255,		/* Raw IP packets			*/
   IPPROTO_MAX
diff -u --recursive --new-file v2.1.29/linux/include/linux/in6.h linux/include/linux/in6.h
--- v2.1.29/linux/include/linux/in6.h	Sun Jan 19 05:47:26 1997
+++ linux/include/linux/in6.h	Thu Mar 20 18:17:11 1997
@@ -88,6 +88,18 @@
 #define IPV6_PRIORITY_15		0x0f00
 
 /*
+ *	IPV6 extension headers
+ */
+#define IPPROTO_HOPOPTS		0	/* IPv6 hop-by-hop options	*/
+#define IPPROTO_ROUTING		43	/* IPv6 routing header		*/
+#define IPPROTO_FRAGMENT	44	/* IPv6 fragmentation header	*/
+#define IPPROTO_ESP		50	/* encapsulating security payload */
+#define IPPROTO_AH		51	/* authentication header	*/
+#define IPPROTO_ICMPV6		58	/* ICMPv6			*/
+#define IPPROTO_NONE		59	/* IPv6 no next header		*/
+#define IPPROTO_DSTOPTS		60	/* IPv6 destination options	*/
+
+/*
  *	IPV6 socket options
  */
 
diff -u --recursive --new-file v2.1.29/linux/include/linux/init.h linux/include/linux/init.h
--- v2.1.29/linux/include/linux/init.h	Sun Jan 26 02:07:48 1997
+++ linux/include/linux/init.h	Thu Mar 20 18:17:11 1997
@@ -30,26 +30,6 @@
  * static char linux_logo[] __initdata = { 0x32, 0x36, ... };
  */
 
-#ifndef __init
-#if (defined (__svr4__) || defined (__ELF__)) && !defined (MODULE)
-#define __init __attribute__ ((__section__ (".text.init")))
-#define __initdata __attribute__ ((__section__ (".data.init")))
-#define __initfunc(__arginit) \
-	__arginit __init; \
-	__arginit
-/* For assembly routines */
-#define __INIT		.section	".text.init",#alloc,#execinstr
-#define __FINIT	.previous
-#define __INITDATA	.section	".data.init",#alloc,#write
-#else
-#define	__init
-#define __initdata
-#define __initfunc(__arginit) __arginit
-/* For assembly routines */
-#define __INIT
-#define __FINIT
-#define __INITDATA
-#endif
-#endif
+#include <asm/init.h>
 
 #endif
diff -u --recursive --new-file v2.1.29/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v2.1.29/linux/include/linux/interrupt.h	Thu Feb 27 10:57:31 1997
+++ linux/include/linux/interrupt.h	Mon Mar 24 16:43:05 1997
@@ -16,6 +16,7 @@
 };
 
 extern atomic_t intr_count;
+extern volatile unsigned char bh_running;
 
 extern int bh_mask_count[32];
 extern unsigned long bh_active;
@@ -76,8 +77,9 @@
  */
 extern inline void start_bh_atomic(void)
 {
+	cli();
 	atomic_inc(&intr_count);
-	barrier();
+	sti();
 }
 
 extern inline void end_bh_atomic(void)
diff -u --recursive --new-file v2.1.29/linux/include/linux/ioport.h linux/include/linux/ioport.h
--- v2.1.29/linux/include/linux/ioport.h	Sun Jan 26 02:07:48 1997
+++ linux/include/linux/ioport.h	Thu Mar 20 18:17:11 1997
@@ -17,14 +17,14 @@
  * If you unload the driver, use release_region to free ports.
  */
 extern void reserve_setup(char *str, int *ints);
-extern int check_region(unsigned int from, unsigned int extent);
-extern void request_region(unsigned int from, unsigned int extent,const char *name);
-extern void release_region(unsigned int from, unsigned int extent);
+extern int check_region(unsigned long from, unsigned long extent);
+extern void request_region(unsigned long from, unsigned long extent,const char *name);
+extern void release_region(unsigned long from, unsigned long extent);
 extern int get_ioport_list(char *);
 
 #ifdef __sparc__
-extern unsigned int occupy_region(unsigned int base, unsigned int end,
-				  unsigned int num, unsigned int align,
+extern unsigned int occupy_region(unsigned long base, unsigned long end,
+				  unsigned long num, unsigned int align,
 				  const char *name);
 #endif
 
diff -u --recursive --new-file v2.1.29/linux/include/linux/ipsec.h linux/include/linux/ipsec.h
--- v2.1.29/linux/include/linux/ipsec.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/ipsec.h	Wed Mar 26 11:01:08 1997
@@ -0,0 +1,69 @@
+/*
+ *	Definitions for the SECurity layer
+ *
+ *	Author:
+ *		Robert Muchsel <muchsel@acm.org>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+ 
+#ifndef _LINUX_IPSEC_H
+#define _LINUX_IPSEC_H
+
+#include <linux/config.h>
+#include <linux/socket.h>
+#include <net/sock.h>
+#include <linux/skbuff.h>
+
+/* Values for the set/getsockopt calls */
+
+/* These defines are compatible with NRL IPv6, however their semantics
+   is different */
+
+#define IPSEC_LEVEL_NONE	-1	/* send plaintext, accept any */
+#define IPSEC_LEVEL_DEFAULT	0	/* encrypt/authenticate if possible */
+					/* the default MUST be 0, because a */
+					/* socket is initialized with 0's */
+#define IPSEC_LEVEL_USE		1	/* use outbound, don't require inbound */
+#define IPSEC_LEVEL_REQUIRE	2	/* require both directions */
+#define IPSEC_LEVEL_UNIQUE	2	/* for compatibility only */
+
+#ifdef __KERNEL__
+
+/* skb bit flags set on packet input processing */
+
+#define RCV_SEC			0x0f	/* options on receive */
+#define RCV_AUTH		0x01	/* was authenticated */
+#define RCV_CRYPT		0x02	/* was encrypted */
+#define RCV_TUNNEL		0x04	/* was tunneled */
+#define SND_SEC			0xf0	/* options on send, these are */
+#define SND_AUTH		0x10	/* currently unused */
+#define SND_CRYPT		0x20
+#define SND_TUNNEL		0x40
+
+/*
+ *	FIXME: ignores network encryption for now..
+ */
+ 
+#ifdef CONFIG_NET_SECURITY
+extern __inline__ int ipsec_sk_policy(struct sock *sk, struct sk_buff *skb)
+{
+	return ((sk->authentication < IPSEC_LEVEL_REQUIRE) ||
+		(skb->security & RCV_AUTH)) &&
+		((sk->encryption < IPSEC_LEVEL_REQUIRE) ||
+		(skb->security & RCV_CRYPT));
+}
+
+#else
+
+extern __inline__ int ipsec_sk_policy(struct sock *sk, struct sk_buff *skb)
+{
+	return 1;
+}
+#endif /* CONFIG */
+
+#endif	/* __KERNEL__ */
+#endif	/* _LINUX_IPSEC_H */
diff -u --recursive --new-file v2.1.29/linux/include/linux/ipv6_route.h linux/include/linux/ipv6_route.h
--- v2.1.29/linux/include/linux/ipv6_route.h	Thu Feb 27 10:57:31 1997
+++ linux/include/linux/ipv6_route.h	Thu Mar 20 18:17:11 1997
@@ -13,24 +13,26 @@
 #ifndef _LINUX_IPV6_ROUTE_H
 #define _LINUX_IPV6_ROUTE_H
 
-#define RTI_DEVRT	0x00010000	/* route lookup, dev must match	*/
-#define RTI_ALLONLINK	0x00020000	/* all destinations on link	*/
-#define RTI_DCACHE	RTF_DCACHE	/* rt6_info is a dcache entry	*/
-#define RTI_INVALID	RTF_INVALID	/* invalid route/dcache entry	*/
 
-#define RTI_DYNAMIC	RTF_DYNAMIC	/* rt6_info created dynamicly	*/
-#define RTI_GATEWAY	RTF_GATEWAY
-#define RTI_DYNMOD	RTF_MODIFIED	/* more specific route may exist*/
+#define RTF_DEFAULT	0x00010000	/* default - learned via ND	*/
+#define RTF_ALLONLINK	0x00020000	/* fallback, no routers on link	*/
+#define RTF_ADDRCONF	0x00040000	/* addrconf route - RA		*/
 
-#define DCF_PMTU	RTF_MSS		/* dest cache has valid PMTU	*/
-#define DCF_INVALID	RTF_INVALID
+#define RTF_LINKRT	0x00100000	/* link specific - device match	*/
+#define RTF_NONEXTHOP	0x00200000	/* route with no nexthop	*/
+
+#define RTF_CACHE	0x01000000	/* cache entry			*/
+#define RTF_FLOW	0x02000000	/* flow significant route	*/
+#define RTF_POLICY	0x04000000	/* policy route			*/
 
 struct in6_rtmsg {
 	struct in6_addr		rtmsg_dst;
+	struct in6_addr		rtmsg_src;
 	struct in6_addr		rtmsg_gateway;
 	__u32			rtmsg_type;
-	__u16			rtmsg_prefixlen;
-	__u16			rtmsg_metric;
+	__u16			rtmsg_dst_len;
+	__u16			rtmsg_src_len;
+	__u32			rtmsg_metric;
 	unsigned long		rtmsg_info;
         __u32			rtmsg_flags;
 	int			rtmsg_ifindex;
diff -u --recursive --new-file v2.1.29/linux/include/linux/mroute.h linux/include/linux/mroute.h
--- v2.1.29/linux/include/linux/mroute.h	Thu Feb  6 02:59:50 1997
+++ linux/include/linux/mroute.h	Wed Mar 26 11:01:28 1997
@@ -21,9 +21,11 @@
 #define MRT_DEL_MFC	(MRT_BASE+5)	/* Delete a multicast forwarding entry	*/
 #define MRT_VERSION	(MRT_BASE+6)	/* Get the kernel multicast version	*/
 #define MRT_ASSERT	(MRT_BASE+7)	/* Activate PIM assert mode		*/
+#define MRT_PIM		(MRT_BASE+8)	/* enable PIM code	*/
 
 #define SIOCGETVIFCNT	SIOCPROTOPRIVATE	/* IP protocol privates */
 #define SIOCGETSGCNT	(SIOCPROTOPRIVATE+1)
+#define SIOCGETRPF	(SIOCPROTOPRIVATE+2)
 
 #define MAXVIFS		32	
 typedef unsigned long vifbitmap_t;	/* User mode code depends on this lot */
@@ -56,10 +58,18 @@
 };
 
 #define VIFF_TUNNEL	0x1		/* IPIP tunnel */
-#define VIFF_SRCRT	0x02		/* NI */
+#define VIFF_SRCRT	0x2		/* NI */
+
+
+/* PIM Vif Flags */
+#define VIFF_DR                 0x0010          /* designated router    */
+#define VIFF_NOMRT              0x0020          /* no neighbor on vif   */
+#define VIFF_DOWN               0x0040          /* interface is down    */
+#define VIFF_DISABLED           0x0080          /* disabled interafce   */
+#define VIFF_REGISTER           0x00A0          /* MIssing cap@di.fc.ul.pt */
 
 /*
- *	Cache manipulation structures for mrouted
+ *	Cache manipulation structures for mrouted and PIMd
  */
  
 struct mfcctl
@@ -68,6 +78,10 @@
 	struct in_addr mfcc_mcastgrp;		/* Group in question	*/
 	vifi_t	mfcc_parent;			/* Where it arrived	*/
 	unsigned char mfcc_ttls[MAXVIFS];	/* Where it is going	*/
+	unsigned int mfcc_pkt_cnt;		/* pkt count for src-grp */
+	unsigned int mfcc_byte_cnt;
+	unsigned int mfcc_wrong_if;
+	int	     mfcc_expire;
 };
 
 /* 
@@ -97,6 +111,16 @@
 };
 
 /*
+ *	To get RPF from unicast routing table (PIM: cap@di.fc.ul.pt)
+ */
+struct sioc_rpf_req
+{
+	unsigned long	source;	       /* Source address */
+	unsigned long   rpfneighbor;   /* RPF */
+        vifi_t	 iif;		       /* Incoming Interface */
+};
+
+/*
  *	This is the format the mroute daemon expects to see IGMP control
  *	data. Magically happens to be like an IP packet as per the original
  */
@@ -138,6 +162,7 @@
 	unsigned char	threshold;		/* TTL threshold 		*/
 	unsigned short	flags;			/* Control flags 		*/
 	unsigned long	local,remote;		/* Addresses(remote for tunnels)*/
+	unsigned long	uptime;
 };
 
 struct mfc_cache 
@@ -153,6 +178,8 @@
 	unsigned mfc_last_assert;
 	int mfc_minvif;
 	int mfc_maxvif;
+	unsigned long uptime;
+	unsigned long expire;
 	unsigned long mfc_bytes;
 	unsigned long mfc_pkt;
 	unsigned long mfc_wrong_if;
@@ -180,7 +207,8 @@
  *	Pseudo messages used by mrouted
  */
 
-#define IGMPMSG_NOCACHE		1		/* Kernel cache fill request to mrouted */
+#define IGMPMSG_NOCACHE		1		/* Kern cache fill request to mrouted */
 #define IGMPMSG_WRONGVIF	2		/* For PIM assert processing (unused) */
+#define IGMPMSG_WHOLEPKT	3		/* For PIM Register processing */
 
 #endif
diff -u --recursive --new-file v2.1.29/linux/include/linux/net.h linux/include/linux/net.h
--- v2.1.29/linux/include/linux/net.h	Thu Feb  6 02:53:33 1997
+++ linux/include/linux/net.h	Mon Mar 24 16:43:04 1997
@@ -108,16 +108,24 @@
   int   (*recvmsg)	(struct socket *sock, struct msghdr *m, int total_len, int flags, struct scm_cookie *scm);
 };
 
-struct net_proto_family {
+struct net_proto_family 
+{
 	int	family;
 	int	(*create)(struct socket *sock, int protocol);
+	/* These are counters for the number of different methods of
+	   each we support */
+	short	authentication;
+	short	encryption;
+	short	encrypt_net;
 };
 
-struct net_proto {
+struct net_proto 
+{
 	const char *name;		/* Protocol name */
 	void (*init_func)(struct net_proto *);	/* Bootstrap */
 };
 
+extern struct net_proto_family *net_families[];
 extern int	sock_wake_async(struct socket *sk, int how);
 extern int	sock_register(struct net_proto_family *fam);
 extern int	sock_unregister(int family);
diff -u --recursive --new-file v2.1.29/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.1.29/linux/include/linux/netdevice.h	Mon Mar 17 14:54:34 1997
+++ linux/include/linux/netdevice.h	Wed Mar 26 10:59:18 1997
@@ -69,6 +69,8 @@
 #define IFF_IP_MASK_OK	2
 #define IFF_IP_BRD_OK	4
 
+struct neighbour;
+
 /*
  *	We tag multicasts with these structures.
  */
@@ -264,7 +266,7 @@
 					      struct ifmap *map);
 #define HAVE_HEADER_CACHE
 	int			(*hard_header_cache)(struct dst_entry *dst,
-						     struct dst_entry *neigh,
+						     struct neighbour *neigh,
 						     struct hh_cache *hh);
 	void			(*header_cache_update)(struct hh_cache *hh,
 						       struct device *dev,
diff -u --recursive --new-file v2.1.29/linux/include/linux/notifier.h linux/include/linux/notifier.h
--- v2.1.29/linux/include/linux/notifier.h	Sun Jan 19 05:47:26 1997
+++ linux/include/linux/notifier.h	Thu Mar 20 17:28:31 1997
@@ -99,7 +99,9 @@
 #define NETDEV_CHANGEADDR	0x0008
 
 #define SYS_DOWN	0x0001	/* Notify of system down */
+#define SYS_RESTART	SYS_DOWN
 #define SYS_HALT	0x0002	/* Notify of system halt */
+#define SYS_POWER_OFF	0x0003	/* Notify of system power off */
 
 /*
  *	Publically visible notifier objects
diff -u --recursive --new-file v2.1.29/linux/include/linux/pi2.h linux/include/linux/pi2.h
--- v2.1.29/linux/include/linux/pi2.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/pi2.h	Thu Mar 20 18:17:11 1997
@@ -0,0 +1,130 @@
+
+#define DMA_BUFF_SIZE 2200
+
+#define ON 1
+#define OFF 0
+
+
+/* Register offset info, specific to the PI
+ * E.g., to read the data port on channel A, use
+ *  inportb(pichan[dev].base + CHANA + DATA)
+ */
+#define CHANB   0   /* Base of channel B regs */
+#define CHANA   2   /* Base of channel A regs */
+
+/* 8530 ports on each channel */
+#define CTL 0
+#define DATA    1
+
+#define DMAEN   0x4 /* Offset off DMA Enable register */
+
+/* Timer chip offsets */
+#define TMR0    0x8 /* Offset of timer 0 register */
+#define TMR1    0x9 /* Offset of timer 1 register */
+#define TMR2    0xA /* Offset of timer 2 register */
+#define TMRCMD  0xB /* Offset of timer command register */
+
+/* Timer chip equates */
+#define SC0 0x00 /* Select counter 0 */
+#define SC1 0x40 /* Select counter 1 */
+#define SC2 0x80 /* Select counter 2 */
+#define CLATCH  0x00 /* Counter latching operation */
+#define MSB 0x20 /* Read/load MSB only */
+#define LSB 0x10 /* Read/load LSB only */
+#define LSB_MSB 0x30 /* Read/load LSB, then MSB */
+#define MODE0   0x00 /* Interrupt on terminal count */
+#define MODE1   0x02 /* Programmable one shot */
+#define MODE2   0x04 /* Rate generator */
+#define MODE3   0x06 /* Square wave rate generator */
+#define MODE4   0x08 /* Software triggered strobe */
+#define MODE5   0x0a /* Hardware triggered strobe */
+#define BCD 0x01 /* BCD counter */
+
+/* DMA controller registers */
+#define DMA_STAT    8   /* DMA controller status register */
+#define DMA_CMD     8   /* DMA controller command register */
+#define DMA_MASK        10  /* DMA controller mask register */
+#define DMA_MODE        11  /* DMA controller mode register */
+#define DMA_RESETFF 12  /* DMA controller first/last flip flop  */
+/* DMA data */
+#define DMA_DISABLE (0x04)  /* Disable channel n */
+#define DMA_ENABLE  (0x00)  /* Enable channel n */
+/* Single transfers, incr. address, auto init, writes, ch. n */
+#define DMA_RX_MODE (0x54)
+/* Single transfers, incr. address, no auto init, reads, ch. n */
+#define DMA_TX_MODE (0x48)
+
+#define SINGLE 3686400
+#define DOUBLE 7372800
+
+#define SIOCGPIPARAM		0x5000	/* get PI parameters */
+#define SIOCSPIPARAM		0x5001	/* set */
+#define SIOCGPIBAUD		0x5002	/* get only baud rate */
+#define SIOCSPIBAUD		0x5003	
+#define SIOCGPIDMA		0x5004	/* get only DMA */
+#define SIOCSPIDMA		0x5005	
+#define SIOCGPIIRQ		0x5006	/* get only IRQ */
+#define SIOCSPIIRQ		0x5007	
+
+struct pi_req  {
+    int cmd;
+    int speed;
+    int clockmode;
+    int txdelay;
+    unsigned char persist;
+    int slotime; 
+    int squeldelay;
+    int dmachan;    
+    int irq;    
+};
+
+#ifdef __KERNEL__
+
+/* Information that needs to be kept for each channel. */
+struct pi_local {
+    struct net_device_stats stats; 
+    long open_time;             /* Useless example local info. */
+    unsigned long xtal; 
+
+    struct mbuf *rcvbuf;/* Buffer for current rx packet */
+    struct mbuf *rxdmabuf1; /* DMA rx buffer */
+    struct mbuf *rxdmabuf2; /* DMA rx buffer */
+
+    int bufsiz;         /* Size of rcvbuf */
+    char *rcp;          /* Pointer into rcvbuf */
+
+    struct sk_buff_head sndq;  /* Packets awaiting transmission */
+    int sndcnt;         /* Number of packets on sndq */
+    struct sk_buff *sndbuf;	/* Current buffer being transmitted */
+    char *txdmabuf;     /* Transmit DMA buffer */
+	char *txptr;		/* Used by B port tx */
+	int txcnt;			
+    char tstate;        /* Transmitter state */
+#define IDLE    0       /* Transmitter off, no data pending */
+#define ACTIVE  1       /* Transmitter on, sending data */
+#define UNDERRUN 2      /* Transmitter on, flushing CRC */
+#define FLAGOUT 3       /* CRC sent - attempt to start next frame */
+#define DEFER 4         /* Receive Active - DEFER Transmit */
+#define ST_TXDELAY 5    /* Sending leading flags */
+#define CRCOUT 6
+    char rstate;        /* Set when !DCD goes to 0 (TRUE) */
+/* Normal state is ACTIVE if Receive enabled */
+#define RXERROR 2       /* Error -- Aborting current Frame */
+#define RXABORT 3       /* ABORT sequence detected */
+#define TOOBIG 4        /* too large a frame to store */
+    int dev;            /* Device number */
+    int base;       /* Base of I/O registers */
+    int cardbase;     /* Base address of card */
+    int stata;        /* address of Channel A status regs */
+    int statb;        /* address of Channel B status regs */
+    int speed;        /* Line speed, bps */
+    int clockmode;    /* tapr 9600 modem clocking option */
+    int txdelay;      /* Transmit Delay 10 ms/cnt */
+    unsigned char persist;       /* Persistence (0-255) as a % */
+    int slotime;      /* Delay to wait on persistence hit */
+    int squeldelay;   /* Delay after XMTR OFF for squelch tail */
+    struct iface *iface;    /* Associated interface */
+    int dmachan;           /* DMA channel for this port */
+};
+
+#endif
diff -u --recursive --new-file v2.1.29/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.29/linux/include/linux/proc_fs.h	Mon Mar 17 14:54:35 1997
+++ linux/include/linux/proc_fs.h	Wed Mar 26 10:58:25 1997
@@ -82,8 +82,11 @@
 	PROC_NET_RTRULES,
 	PROC_NET_DEV,
 	PROC_NET_RAW,
+	PROC_NET_RAW6,
 	PROC_NET_TCP,
+	PROC_NET_TCP6,
 	PROC_NET_UDP,
+	PROC_NET_UDP6,
 	PROC_NET_SNMP,
 	PROC_NET_RARP,
 	PROC_NET_IGMP,
@@ -108,12 +111,14 @@
 	PROC_NET_NR_NEIGH,
 	PROC_NET_NR,
 	PROC_NET_SOCKSTAT,
+	PROC_NET_SOCKSTAT6,
 	PROC_NET_RTCACHE,
 	PROC_NET_AX25_BPQETHER,
 	PROC_NET_ALIAS_TYPES,
 	PROC_NET_ALIASES,
 	PROC_NET_IP_MASQ_APP,
 	PROC_NET_RT6,
+	PROC_NET_RT6_TREE,
 	PROC_NET_RT6_STATS,
 	PROC_NET_NDISC,
 	PROC_NET_STRIP_STATUS,
diff -u --recursive --new-file v2.1.29/linux/include/linux/pt.h linux/include/linux/pt.h
--- v2.1.29/linux/include/linux/pt.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/pt.h	Thu Mar 20 18:17:11 1997
@@ -0,0 +1,174 @@
+/*
+ * pt.h: Linux device driver for the Gracilis PackeTwin
+ * Copyright (C) 1995 Craig Small VK2XLZ (vk2xlz@vk2xlz.ampr.org.)
+ *
+ * Please read the notice appearing at the top of the file pt.c
+ */
+#define DMA_BUFF_SIZE 2200
+
+#define ON 1
+#define OFF 0
+
+
+/* Register offset info, specific to the PT
+ * E.g., to read the data port on channel A, use
+ *  inportb(pichan[dev].base + CHANA + DATA)
+ */
+#define CHANB   0   /* Base of channel B regs */
+#define CHANA   2   /* Base of channel A regs */
+
+/* 8530 ports on each channel */
+#define CTL 0
+#define DATA    1
+
+#define DMAEN   0x8 /* Offset off DMA Enable register */
+
+/* Timer chip offsets */
+#define TMR0    0x4 /* Offset of timer 0 register */
+#define TMR1    0x5 /* Offset of timer 1 register */
+#define TMR2    0x6 /* Offset of timer 2 register */
+#define TMRCMD  0x7 /* Offset of timer command register */
+#define INT_REG	0x8
+#define TMR1CLR 0x9
+#define TMR2CLR 0xa
+
+/* Interrupt register equates */
+#define PT_SCC_MSK	0x1
+#define PT_TMR1_MSK	0x2
+#define PT_TMR2_MSK	0x4
+
+/* Serial/interrupt register equates */
+#define PT_DTRA_ON	0x1
+#define PT_DTRB_ON	0x2
+#define PT_EXTCLKA	0x4
+#define PT_EXTCLKB	0x8
+#define PT_LOOPA_ON	0x10
+#define PT_LOOPB_ON	0x20
+#define PT_EI		0x80
+
+/* Timer chip equates */
+#define SC0 0x00 /* Select counter 0 */
+#define SC1 0x40 /* Select counter 1 */
+#define SC2 0x80 /* Select counter 2 */
+#define CLATCH  0x00 /* Counter latching operation */
+#define MSB 0x20 /* Read/load MSB only */
+#define LSB 0x10 /* Read/load LSB only */
+#define LSB_MSB 0x30 /* Read/load LSB, then MSB */
+#define MODE0   0x00 /* Interrupt on terminal count */
+#define MODE1   0x02 /* Programmable one shot */
+#define MODE2   0x04 /* Rate generator */
+#define MODE3   0x06 /* Square wave rate generator */
+#define MODE4   0x08 /* Software triggered strobe */
+#define MODE5   0x0a /* Hardware triggered strobe */
+#define BCD 0x01 /* BCD counter */
+
+/* DMA controller registers */
+#define DMA_STAT    8   /* DMA controller status register */
+#define DMA_CMD     8   /* DMA controller command register */
+#define DMA_MASK        10  /* DMA controller mask register */
+#define DMA_MODE        11  /* DMA controller mode register */
+#define DMA_RESETFF 12  /* DMA controller first/last flip flop  */
+/* DMA data */
+#define DMA_DISABLE (0x04)  /* Disable channel n */
+#define DMA_ENABLE  (0x00)  /* Enable channel n */
+/* Single transfers, incr. address, auto init, writes, ch. n */
+#define DMA_RX_MODE (0x54)
+/* Single transfers, incr. address, no auto init, reads, ch. n */
+#define DMA_TX_MODE (0x48)
+
+/* Write registers */
+#define DMA_CFG		0x08
+#define SERIAL_CFG	0x09
+#define INT_CFG		0x09	/* shares with serial config */
+#define DMA_CLR_FF	0x0a
+
+#define SINGLE 3686400
+#define DOUBLE 7372800
+#define XTAL   ((long) 6144000L)
+
+#define SIOCGPIPARAM		0x5000	/* get PI parameters */
+#define SIOCSPIPARAM		0x5001	/* set */
+#define SIOCGPIBAUD		0x5002	/* get only baud rate */
+#define SIOCSPIBAUD		0x5003	
+#define SIOCGPIDMA		0x5004	/* get only DMA */
+#define SIOCSPIDMA		0x5005	
+#define SIOCGPIIRQ		0x5006	/* get only IRQ */
+#define SIOCSPIIRQ		0x5007	
+
+struct pt_req
+{
+	int cmd;
+	int speed;
+	int clockmode;
+	int txdelay;
+	unsigned char persist;
+	int slotime; 
+	int squeldelay;
+	int dmachan;    
+	int irq;    
+};
+
+/* SCC Interrupt vectors, if we have set 'status low' */
+#define CHBTxIV		0x00
+#define CHBEXTIV	0x02
+#define CHBRxIV		0x04
+#define CHBSRCIV	0x06
+#define CHATxIV		0x08
+#define CHAEXTIV	0x0a
+#define CHARxIV		0x0c
+#define CHASRCIV	0x0e
+
+
+#ifdef __KERNEL__
+
+/* Information that needs to be kept for each channel. */
+struct pt_local
+{
+	struct net_device_stats stats; /* %%%dp*/
+	long open_time;             /* Useless example local info. */
+	unsigned long xtal; 
+
+	struct mbuf *rcvbuf;/* Buffer for current rx packet */
+	struct mbuf *rxdmabuf1; /* DMA rx buffer */
+	struct mbuf *rxdmabuf2; /* DMA rx buffer */
+
+	int bufsiz;			/* Size of rcvbuf 			*/
+	char *rcp;			/* Pointer into rcvbuf 			*/
+	struct sk_buff_head sndq;	/* Packets awaiting transmission 	*/
+	int sndcnt;			/* Number of packets on sndq 		*/
+	struct sk_buff *sndbuf;		/* Current buffer being transmitted	*/
+	char *txdmabuf; 		/* Transmit DMA buffer 			*/
+	char *txptr;			/* Used by B port tx 			*/
+	int txcnt;			
+	char tstate;			/* Transmitter state 			*/
+#define IDLE    0       /* Transmitter off, no data pending 		*/
+#define ACTIVE  1       /* Transmitter on, sending data 		*/
+#define UNDERRUN 2      /* Transmitter on, flushing CRC			*/
+#define FLAGOUT 3       /* CRC sent - attempt to start next frame	*/
+#define DEFER 4         /* Receive Active - DEFER Transmit		*/
+#define ST_TXDELAY 5    /* Sending leading flags			*/
+#define CRCOUT 6
+	char rstate;        /* Set when !DCD goes to 0 (TRUE)		*/
+/* Normal state is ACTIVE if Receive enabled */
+#define RXERROR 2       /* Error -- Aborting current Frame		*/
+#define RXABORT 3       /* ABORT sequence detected			*/
+#define TOOBIG 4        /* too large a frame to store			*/
+	
+	int dev;		/* Device number */
+	int base;		/* Base of I/O registers */
+	int cardbase;		/* Base address of card */
+	int stata;		/* address of Channel A status regs */
+	int statb;		/* address of Channel B status regs */
+	int speed;        	/* Line speed, bps */
+	int clockmode;    	/* tapr 9600 modem clocking option */
+	int txdelay;      	/* Transmit Delay 10 ms/cnt */
+	unsigned char persist;  /* Persistence (0-255) as a % */
+	int slotime;		/* Delay to wait on persistence hit */
+	int squeldelay; 	/* Delay after XMTR OFF for squelch tail */
+	struct iface *iface;    /* Associated interface */
+	int dmachan;		/* DMA channel for this port */
+	char saved_RR0;		/* The saved version of RR) that we compare with */
+	int nrzi;		/* Do we use NRZI (or NRZ) */
+};
+
+#endif
diff -u --recursive --new-file v2.1.29/linux/include/linux/reboot.h linux/include/linux/reboot.h
--- v2.1.29/linux/include/linux/reboot.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/reboot.h	Thu Mar 20 17:32:44 1997
@@ -0,0 +1,51 @@
+#ifndef _LINUX_REBOOT_H
+#define _LINUX_REBOOT_H
+
+/*
+ * Magic values required to use _reboot() system call.
+ */
+
+#define	LINUX_REBOOT_MAGIC1	0xfee1dead
+#define	LINUX_REBOOT_MAGIC2	672274793
+#define	LINUX_REBOOT_MAGIC2A	85072278
+
+
+/*
+ * Commands accepted by the _reboot() system call.
+ *
+ * RESTART     Restart system using default command and mode.
+ * HALT        Stop OS and give system control to ROM monitor, if any.
+ * CAD_ON      Ctrl-Alt-Del sequence causes RESTART command.
+ * CAD_OFF     Ctrl-Alt-Del sequence sends SIGINT to init task.
+ * POWER_OFF   Stop OS and remove all power from system, if possible.
+ * RESTART2    Restart system using given command string.
+ */
+
+#define	LINUX_REBOOT_CMD_RESTART	0x01234567
+#define	LINUX_REBOOT_CMD_HALT		0xCDEF0123
+#define	LINUX_REBOOT_CMD_CAD_ON		0x89ABCDEF
+#define	LINUX_REBOOT_CMD_CAD_OFF	0x00000000
+#define	LINUX_REBOOT_CMD_POWER_OFF	0x4321FEDC
+#define	LINUX_REBOOT_CMD_RESTART2	0xA1B2C3D4
+
+
+#ifdef __KERNEL__
+
+#include <linux/notifier.h>
+
+extern struct notifier_block *reboot_notifier_list;
+extern int register_reboot_notifier(struct notifier_block *);
+extern int unregister_reboot_notifier(struct notifier_block *);
+
+
+/*
+ * Architecture-specific implementations of sys_reboot commands.
+ */
+
+extern void machine_restart(char *cmd);
+extern void machine_halt(void);
+extern void machine_power_off(void);
+
+#endif
+
+#endif /* _LINUX_REBOOT_H */
diff -u --recursive --new-file v2.1.29/linux/include/linux/route.h linux/include/linux/route.h
--- v2.1.29/linux/include/linux/route.h	Thu Feb  6 02:59:02 1997
+++ linux/include/linux/route.h	Mon Mar 24 16:45:57 1997
@@ -66,18 +66,13 @@
 #define RTF_THROW	0x2000		/* Go to next class		*/
 #define RTF_NOPMTUDISC  0x4000		/* Do not send packets with DF	*/
 
-/* Bad idea. IPv6 should not use broken IPv4 interface */
-
-#define RTF_ADDRCONF	0x0800		/* announced on link prefix	  */
-#define RTF_INVALID	0x1000
-#define RTF_DCACHE	0x2000
-#define RTF_DEFAULT	0x4000		/* Route is a default route	  */
-#define RTF_NEXTHOP	0x8000		/* Non gateway route with nexthop */
-
-
-#define RTF_MAGIC	0x10000		/* Route added/deleted authomatically,
+#define RTF_MAGIC	0x8000		/* Route added/deleted authomatically,
 					 * when interface changes its state.
 					 */
+
+/*
+ *	<linux/ipv6_route.h> uses RTF values >= 64k
+ */
 
 #define RTCF_VALVE	0x00200000
 #define RTCF_MASQ	0x00400000
diff -u --recursive --new-file v2.1.29/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.1.29/linux/include/linux/sched.h	Thu Feb  6 02:53:33 1997
+++ linux/include/linux/sched.h	Wed Mar 26 10:58:25 1997
@@ -410,14 +410,15 @@
 	wait->next = next;
 }
 
+extern spinlock_t waitqueue_lock;
+
 extern inline void add_wait_queue(struct wait_queue ** p, struct wait_queue * wait)
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&waitqueue_lock, flags);
 	__add_wait_queue(p, wait);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&waitqueue_lock, flags);
 }
 
 extern inline void __remove_wait_queue(struct wait_queue ** p, struct wait_queue * wait)
@@ -438,10 +439,9 @@
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&waitqueue_lock, flags);
 	__remove_wait_queue(p, wait);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&waitqueue_lock, flags); 
 }
 
 extern inline void poll_wait(struct wait_queue ** wait_address, poll_table * p)
diff -u --recursive --new-file v2.1.29/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v2.1.29/linux/include/linux/skbuff.h	Thu Feb  6 02:53:43 1997
+++ linux/include/linux/skbuff.h	Wed Mar 26 10:58:26 1997
@@ -86,12 +86,6 @@
 	  	unsigned char 	*raw;
 	} mac;
 
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-	/*
-	 *	Generic "neighbour" information
-	 */
-	struct neighbour *nexthop;
-#endif		
 	struct  dst_entry *dst;
 	char    	cb[32];
 
@@ -117,6 +111,7 @@
 #define PACKET_NDISC		17		/* Outgoing NDISC packet			*/
 	atomic_t	users;			/* User count - see datagram.c,tcp.c 		*/
 	unsigned short	protocol;		/* Packet protocol from driver. 		*/
+	unsigned short	security;		/* Security level of packet			*/
 	unsigned int	truesize;		/* Buffer size 					*/
 
 	atomic_t	count;			/* reference count				*/
diff -u --recursive --new-file v2.1.29/linux/include/linux/socket.h linux/include/linux/socket.h
--- v2.1.29/linux/include/linux/socket.h	Thu Feb  6 02:53:33 1997
+++ linux/include/linux/socket.h	Thu Mar 20 18:17:12 1997
@@ -147,6 +147,7 @@
 #define AF_ROSE		11	/* Amateur Radio X.25 PLP	*/
 #define AF_DECNET	12	/* Reserved for DECnet project	*/
 #define AF_NETBEUI	13	/* Reserved for 802.2LLC project*/
+#define AF_SECURITY	14	/* Security callback pseudo AF */
 #define AF_MAX		32	/* For now.. */
 
 /* Protocol families, same as address families. */
@@ -165,6 +166,7 @@
 #define PF_ROSE		AF_ROSE
 #define PF_DECNET	AF_DECNET
 #define PF_NETBEUI	AF_NETBEUI
+#define PF_SECURITY	AF_SECURITY
 
 #define PF_MAX		AF_MAX
 
diff -u --recursive --new-file v2.1.29/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.1.29/linux/include/linux/sysctl.h	Thu Feb 27 10:57:31 1997
+++ linux/include/linux/sysctl.h	Thu Mar 20 18:17:12 1997
@@ -123,8 +123,26 @@
 
 
 /* /proc/sys/net/ipv6 */
-#define NET_IPV6_FORWARDING		1
-#define NET_IPV6_HOPLIMIT		2
+enum {
+	NET_IPV6_FORWARDING = 1,
+	NET_IPV6_HOPLIMIT,
+
+	NET_IPV6_ACCEPT_RA,
+	NET_IPV6_ACCEPT_REDIRECTS,
+
+	NET_IPV6_ND_MAX_MCAST_SOLICIT,
+	NET_IPV6_ND_MAX_UCAST_SOLICIT,
+	NET_IPV6_ND_RETRANS_TIME,
+	NET_IPV6_ND_REACHABLE_TIME,
+	NET_IPV6_ND_DELAY_PROBE_TIME,
+
+	NET_IPV6_AUTOCONF,
+	NET_IPV6_DAD_TRANSMITS,
+	NET_IPV6_RTR_SOLICITS,
+	NET_IPV6_RTR_SOLICIT_INTERVAL,
+	NET_IPV6_RTR_SOLICIT_DELAY,
+};
+
 /* /proc/sys/net/ipx */
 
 /* /proc/sys/net/appletalk */
@@ -158,7 +176,8 @@
 	NET_AX25_IDLE_TIMEOUT,
 	NET_AX25_N2,
 	NET_AX25_PACLEN,
-	NET_AX25_PROTOCOL
+	NET_AX25_PROTOCOL,
+	NET_AX25_DAMA_SLAVE_TIMEOUT
 };
 
 /* /proc/sys/net/rose */
diff -u --recursive --new-file v2.1.29/linux/include/linux/tqueue.h linux/include/linux/tqueue.h
--- v2.1.29/linux/include/linux/tqueue.h	Sun Jan 26 02:21:08 1997
+++ linux/include/linux/tqueue.h	Mon Mar 24 16:43:04 1997
@@ -15,6 +15,7 @@
 
 #include <asm/bitops.h>
 #include <asm/system.h>
+#include <asm/spinlock.h>
 
 /*
  * New proposed "bottom half" handlers:
@@ -74,48 +75,20 @@
  * interrupt.
  */
 
-/*
- * queue_task_irq: put the bottom half handler "bh_pointer" on the list
- * "bh_list".  You may call this function only from an interrupt
- * handler or a bottom half handler.
- */
-extern __inline__ void queue_task_irq(struct tq_struct *bh_pointer,
-			       task_queue *bh_list)
-{
-	if (!set_bit(0,&bh_pointer->sync)) {
-		bh_pointer->next = *bh_list;
-		*bh_list = bh_pointer;
-	}
-}
-
-/*
- * queue_task_irq_off: put the bottom half handler "bh_pointer" on the list
- * "bh_list".  You may call this function only when interrupts are off.
- */
-extern __inline__ void queue_task_irq_off(struct tq_struct *bh_pointer,
-				 task_queue *bh_list)
-{
-	if (!(bh_pointer->sync & 1)) {
-		bh_pointer->sync = 1;
-		bh_pointer->next = *bh_list;
-		*bh_list = bh_pointer;
-	}
-}
-
+extern spinlock_t tqueue_lock;
 
 /*
- * queue_task: as queue_task_irq, but can be called from anywhere.
+ * queue_task
  */
 extern __inline__ void queue_task(struct tq_struct *bh_pointer,
 			   task_queue *bh_list)
 {
 	if (!set_bit(0,&bh_pointer->sync)) {
 		unsigned long flags;
-		save_flags(flags);
-		cli();
+		spin_lock_irqsave(&tqueue_lock, flags);
 		bh_pointer->next = *bh_list;
 		*bh_list = bh_pointer;
-		restore_flags(flags);
+		spin_unlock_irqrestore(&tqueue_lock, flags);
 	}
 }
 
diff -u --recursive --new-file v2.1.29/linux/include/net/addrconf.h linux/include/net/addrconf.h
--- v2.1.29/linux/include/net/addrconf.h	Tue Nov 26 00:46:41 1996
+++ linux/include/net/addrconf.h	Thu Mar 20 18:17:12 1997
@@ -38,9 +38,11 @@
 #include <linux/netdevice.h>
 #include <net/if_inet6.h>
 
-extern struct inet6_ifaddr	*inet6_addr_lst[16];
-extern struct ipv6_mc_list	*inet6_mcast_lst[16];
-extern struct inet6_dev		*inet6_dev_lst;
+#define IN6_ADDR_HSIZE		16
+
+extern struct inet6_ifaddr	*inet6_addr_lst[IN6_ADDR_HSIZE];
+extern struct ifmcaddr6		*inet6_mcast_lst[IN6_ADDR_HSIZE];
+extern struct inet6_dev		*inet6_dev_lst[IN6_ADDR_HSIZE];
 
 extern void			addrconf_init(void);
 extern void			addrconf_cleanup(void);
@@ -51,10 +53,9 @@
 
 extern int			addrconf_add_ifaddr(void *arg);
 extern int			addrconf_set_dstaddr(void *arg);
-extern int			addrconf_get_ifindex(void *arg);
 
 extern struct inet6_ifaddr *	ipv6_chk_addr(struct in6_addr *addr);
-extern struct inet6_ifaddr *	ipv6_get_saddr(struct rt6_info *rt, 
+extern struct inet6_ifaddr *	ipv6_get_saddr(struct dst_entry *dst, 
 					       struct in6_addr *daddr);
 extern struct inet6_ifaddr *	ipv6_get_lladdr(struct device *dev);
 
@@ -80,7 +81,6 @@
 extern void			addrconf_prefix_rcv(struct device *dev,
 						    u8 *opt, int len);
 
-extern struct inet6_dev *	ipv6_dev_by_index(int index);
 extern struct inet6_dev *	ipv6_get_idev(struct device *dev);
 
 extern void			addrconf_forwarding_on(void);
@@ -103,6 +103,11 @@
 	tmp ^= (tmp >> 8);
 
 	return ((tmp ^ (tmp >> 4)) & 0x0f);
+}
+
+static __inline__ int ipv6_devindex_hash(int ifindex)
+{
+	return ifindex & (IN6_ADDR_HSIZE - 1);
 }
 
 /*
diff -u --recursive --new-file v2.1.29/linux/include/net/arp.h linux/include/net/arp.h
--- v2.1.29/linux/include/net/arp.h	Thu Feb  6 02:59:02 1997
+++ linux/include/net/arp.h	Wed Mar 26 11:01:08 1997
@@ -3,12 +3,39 @@
 #define _ARP_H
 
 #include <linux/if_arp.h>
+#include <net/neighbour.h>
+/*
+ *	This structure defines the ARP mapping cache.
+ */
+
+struct arp_table
+{
+	union {
+		struct neighbour	neigh;
+		struct arp_table	*next;
+	} u;
+
+	u32				ip;
+
+	unsigned long			last_updated;		/* For expiry 			*/
+	unsigned int			flags;			/* Control status 		*/
+
+	u32				mask;			/* netmask - used for generalised proxy arps (tridge) 		*/
+	int				hatype;
+
+	/*
+	 *	The following entries are only used for unresolved hw addresses.
+	 */
+	struct timer_list		timer;			/* expire timer 		*/
+	int				retries;		/* remaining retries	 	*/
+};
 
 extern void	arp_init(void);
 extern int	arp_rcv(struct sk_buff *skb, struct device *dev,
 			struct packet_type *pt);
 extern int	arp_find(unsigned char *haddr, struct sk_buff *skb);
-extern int	arp_find_1(unsigned char *haddr, struct dst_entry* dst, struct dst_entry *neigh);
+extern int	arp_find_1(unsigned char *haddr, struct dst_entry* dst,
+			   struct neighbour *neigh);
 extern int	arp_ioctl(unsigned int cmd, void *arg);
 extern void     arp_send(int type, int ptype, u32 dest_ip, 
 			 struct device *dev, u32 src_ip, 
@@ -17,5 +44,5 @@
 extern int	arp_req_delete(struct arpreq *r, struct device *dev);
 extern int	arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short type, __u32 daddr);
 extern int	arp_update_cache(struct hh_cache * hh);
-extern struct dst_entry *arp_find_neighbour(struct dst_entry *dst, int);
+extern struct neighbour *arp_find_neighbour(struct dst_entry *dst, int);
 #endif	/* _ARP_H */
diff -u --recursive --new-file v2.1.29/linux/include/net/ax25.h linux/include/net/ax25.h
--- v2.1.29/linux/include/net/ax25.h	Thu Feb 27 10:57:31 1997
+++ linux/include/net/ax25.h	Wed Mar 26 10:59:18 1997
@@ -6,6 +6,7 @@
  
 #ifndef _AX25_H
 #define _AX25_H 
+#include <linux/config.h>
 #include <linux/ax25.h>
 
 #define AX25_SLOWHZ			10	/* Run timing at 1/10 second - gives us better resolution for 56kbit links */
@@ -113,9 +114,12 @@
 #define AX25_MODULUS 		8	/*  Standard AX.25 modulus */
 #define	AX25_EMODULUS		128	/*  Extended AX.25 modulus */
 
-#define	AX25_PROTO_STD		0
-#define	AX25_PROTO_DAMA_SLAVE	1
-#define	AX25_PROTO_DAMA_MASTER	2
+enum {
+	AX25_PROTO_STD_SIMPLEX,
+	AX25_PROTO_STD_DUPLEX,
+	AX25_PROTO_DAMA_SLAVE,
+	AX25_PROTO_DAMA_MASTER
+};
 
 enum {
 	AX25_VALUES_IPDEFMODE,	/* 0=DG 1=VC */
@@ -131,6 +135,7 @@
 	AX25_VALUES_N2,		/* Default N2 value */
 	AX25_VALUES_PACLEN,	/* AX.25 MTU */
 	AX25_VALUES_PROTOCOL,	/* Std AX.25, DAMA Slave, DAMA Master */
+	AX25_VALUES_DS_TIMEOUT,	/* DAMA Slave timeout */
 	AX25_MAX_VALUES		/* THIS MUST REMAIN THE LAST ENTRY OF THIS LIST */
 };
 
@@ -146,7 +151,8 @@
 #define	AX25_DEF_N2		10			/* N2=10 */
 #define AX25_DEF_IDLE		(20 * 60 * AX25_SLOWHZ)	/* Idle=20 mins */		
 #define AX25_DEF_PACLEN		256			/* Paclen=256 */
-#define	AX25_DEF_PROTOCOL	AX25_PROTO_STD		/* Standard AX.25 */
+#define	AX25_DEF_PROTOCOL	AX25_PROTO_STD_SIMPLEX	/* Standard AX.25 */
+#define AX25_DEF_DS_TIMEOUT	(3 * 60 * AX25_SLOWHZ)  /* DAMA timeout 3 minutes */
 
 typedef struct ax25_uid_assoc {
 	struct ax25_uid_assoc	*next;
@@ -169,6 +175,12 @@
 	char			ip_mode;
 } ax25_route;
 
+typedef struct {
+	char			slave;			/* slave_mode?   */
+	struct timer_list	slave_timer;		/* timeout timer */
+	unsigned short		slave_timeout;		/* when? */
+} ax25_dama_info;
+
 #ifndef _LINUX_SYSCTL_H
 #include <linux/sysctl.h>
 #endif
@@ -179,6 +191,9 @@
 	struct device		*forward;
 	struct ctl_table	systable[AX25_MAX_VALUES+1];
 	int			values[AX25_MAX_VALUES];
+#if defined(CONFIG_AX25_DAMA_SLAVE) || defined(CONFIG_AX25_DAMA_MASTER)
+	ax25_dama_info		dama;
+#endif
 } ax25_dev;
 
 typedef struct ax25_cb {
@@ -191,6 +206,9 @@
 	unsigned short		vs, vr, va;
 	unsigned char		condition, backoff;
 	unsigned char		n2, n2count;
+#ifdef CONFIG_AX25_DAMA_SLAVE
+	unsigned char		dama_slave;
+#endif
 	unsigned short		t1, t2, t3, idle, rtt;
 	unsigned short		t1timer, t2timer, t3timer, idletimer;
 	unsigned short		paclen;
@@ -247,10 +265,14 @@
 extern void ax25_ds_nr_error_recovery(ax25_cb *);
 extern void ax25_ds_enquiry_response(ax25_cb *);
 extern void ax25_ds_establish_data_link(ax25_cb *);
+extern void ax25_dev_dama_on(ax25_dev *);
+extern void ax25_dev_dama_off(ax25_dev *);
 extern void ax25_dama_on(ax25_cb *);
 extern void ax25_dama_off(ax25_cb *);
 
 /* ax25_ds_timer.c */
+extern void ax25_ds_set_timer(ax25_dev *);
+extern void ax25_ds_del_timer(ax25_dev *);
 extern void ax25_ds_timer(ax25_cb *);
 extern void ax25_ds_t1_timeout(ax25_cb *);
 
diff -u --recursive --new-file v2.1.29/linux/include/net/br.h linux/include/net/br.h
--- v2.1.29/linux/include/net/br.h	Mon Nov 18 01:31:32 1996
+++ linux/include/net/br.h	Mon Mar 10 15:51:10 1997
@@ -208,84 +208,19 @@
 #define BRCMD_DISABLE_PROT_STATS 14
 #define BRCMD_ZERO_PROT_STATS	15
 
-/* prototypes of all bridging functions... */
+/* prototypes of exported bridging functions... */
 
-void transmit_config(int port_no);
-int root_bridge(void);
-int supersedes_port_info(int port_no, Config_bpdu *config);
-void record_config_information(int port_no, Config_bpdu *config);
-void record_config_timeout_values(Config_bpdu *config);
-void config_bpdu_generation(void);
-int designated_port(int port_no);
-void reply(int port_no);
-void transmit_tcn(void);
-void configuration_update(void);
-void root_selection(void);
-void designated_port_selection(void);
-void become_designated_port(int port_no);
-void port_state_selection(void);
-void make_forwarding(int port_no);
-void topology_change_detection(void);
-void topology_change_acknowledged(void);
-void acknowledge_topology_change(int port_no);
-void make_blocking(int port_no);
-void set_port_state(int port_no, int state);
-void received_config_bpdu(int port_no, Config_bpdu *config);
-void received_tcn_bpdu(int port_no, Tcn_bpdu *tcn);
-void hello_timer_expiry(void);
-void message_age_timer_expiry(int port_no);
-void forward_delay_timer_expiry(int port_no);
-int designated_for_some_port(void);
-void tcn_timer_expiry(void);
-void topology_change_timer_expiry(void);
-void hold_timer_expiry(int port_no);
 void br_init(void);
-void br_init_port(int port_no);
-void enable_port(int port_no);
-void disable_port(int port_no);
-void set_bridge_priority(bridge_id_t *new_bridge_id);
-void set_port_priority(int port_no, unsigned short new_port_id);
-void set_path_cost(int port_no, unsigned short path_cost);
-void start_hello_timer(void);
-void stop_hello_timer(void);
-int hello_timer_expired(void);
-void start_tcn_timer(void);
-void stop_tcn_timer(void);
-int tcn_timer_expired(void);
-void start_topology_change_timer(void);
-void stop_topology_change_timer(void);
-int topology_change_timer_expired(void);
-void start_message_age_timer(int port_no, unsigned short message_age);
-void stop_message_age_timer(int port_no);
-int message_age_timer_expired(int port_no);
-void start_forward_delay_timer(int port_no);
-void stop_forward_delay_timer(int port_no);
-int forward_delay_timer_expired(int port_no);
-void start_hold_timer(int port_no);
-void stop_hold_timer(int port_no);
-int hold_timer_expired(int port_no);
-
-struct fdb *br_avl_find_addr(unsigned char addr[6]);
-int br_avl_insert (struct fdb * new_node);
-int br_avl_remove (struct fdb * node_to_delete);
-
-int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu);
-int send_config_bpdu(int port_no, Config_bpdu *config_bpdu);
-int find_port(struct device *dev);
-int br_flood(struct sk_buff *skb, int port);
-int br_drop(struct sk_buff *skb);
-int br_learn(struct sk_buff *skb, int port);	/* 3.8 */
-
 int br_receive_frame(struct sk_buff *skb);	/* 3.5 */
 int br_tx_frame(struct sk_buff *skb);
 int br_ioctl(unsigned int cmd, void *arg);
-
 int br_protocol_ok(unsigned short protocol);
 
-void free_fdb(struct fdb *);
-struct fdb *get_fdb(void);
+struct fdb *br_avl_find_addr(unsigned char addr[6]);
+int br_avl_insert (struct fdb * new_node);
 
 /* externs */
 
 extern struct br_stat br_stats;
+
 
diff -u --recursive --new-file v2.1.29/linux/include/net/dst.h linux/include/net/dst.h
--- v2.1.29/linux/include/net/dst.h	Thu Dec 12 06:54:21 1996
+++ linux/include/net/dst.h	Wed Mar 26 11:01:08 1997
@@ -8,6 +8,8 @@
 #ifndef _NET_DST_H
 #define _NET_DST_H
 
+#include <net/neighbour.h>
+
 /*
  * 0 - no debugging messages
  * 1 - rare events and bugs (default)
@@ -28,8 +30,8 @@
 struct dst_entry
 {
 	struct dst_entry        *next;
-	atomic_t		refcnt;
-	atomic_t		use;
+	atomic_t		refcnt;		/* tree/hash references	*/
+	atomic_t		use;		/* client references	*/
 	struct device	        *dev;
 	char			obsolete;
 	char			priority;
@@ -40,7 +42,7 @@
 	unsigned		rtt;
 	int			error;
 
-	struct dst_entry	*neighbour;
+	struct neighbour	*neighbour;
 	struct hh_cache		*hh;
 
 	int			(*input)(struct sk_buff*);
@@ -55,8 +57,9 @@
 struct dst_ops
 {
 	unsigned short		family;
-	struct dst_entry *	(*check)(struct dst_entry *);
-	struct dst_entry *	(*reroute)(struct dst_entry *);
+	struct dst_entry *	(*check)(struct dst_entry *, u32 cookie);
+	struct dst_entry *	(*reroute)(struct dst_entry *,
+					   struct sk_buff *);
 	void			(*destroy)(struct dst_entry *);
 };
 
@@ -67,7 +70,7 @@
 struct dst_entry * dst_clone(struct dst_entry * dst)
 {
 	if (dst)
-		atomic_inc(&dst->refcnt);
+		atomic_inc(&dst->use);
 	return dst;
 }
 
@@ -75,24 +78,24 @@
 void dst_release(struct dst_entry * dst)
 {
 	if (dst)
-		atomic_dec(&dst->refcnt);
+		atomic_dec(&dst->use);
 }
 
 static __inline__
-struct dst_entry * dst_check(struct dst_entry ** dst_p)
+struct dst_entry * dst_check(struct dst_entry ** dst_p, u32 cookie)
 {
 	struct dst_entry * dst = *dst_p;
 	if (dst && dst->obsolete)
-		dst = dst->ops->check(dst);
+		dst = dst->ops->check(dst, cookie);
 	return (*dst_p = dst);
 }
 
 static __inline__
-struct dst_entry * dst_reroute(struct dst_entry ** dst_p)
+struct dst_entry * dst_reroute(struct dst_entry ** dst_p, struct sk_buff *skb)
 {
 	struct dst_entry * dst = *dst_p;
 	if (dst && dst->obsolete)
-		dst = dst->ops->reroute(dst);
+		dst = dst->ops->reroute(dst, skb);
 	return (*dst_p = dst);
 }
 
@@ -100,7 +103,7 @@
 void dst_destroy(struct dst_entry * dst)
 {
 	if (dst->neighbour)
-		dst_release(dst->neighbour);
+		neigh_release(dst->neighbour);
 	if (dst->ops->destroy)
 		dst->ops->destroy(dst);
 	kfree(dst);
@@ -113,7 +116,7 @@
 static __inline__
 void dst_free(struct dst_entry * dst)
 {
-	if (!dst->refcnt) {
+	if (!dst->use) {
 		dst_destroy(dst);
 		return;
 	}
diff -u --recursive --new-file v2.1.29/linux/include/net/flow.h linux/include/net/flow.h
--- v2.1.29/linux/include/net/flow.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/flow.h	Thu Mar 20 18:17:12 1997
@@ -0,0 +1,95 @@
+/*
+ *
+ *	Flow based forwarding rules (usage: firewalling, etc)
+ *
+ */
+
+#ifndef _NET_FLOW_H
+#define _NET_FLOW_H
+
+struct flowi {
+	int	proto;		/*	{TCP, UDP, ICMP}	*/
+
+	union {
+		struct {
+			__u32			daddr;
+			__u32			saddr;
+		} ip4_u;
+		
+		struct {
+			struct in6_addr *	daddr;
+			struct in6_addr *	saddr;
+		} ip6_u;
+	} nl_u;
+
+	struct device	*dev;
+
+	union {
+		struct {
+			__u16	sport;
+			__u16	dport;
+		} ports;
+
+		struct {
+			__u8	type;
+			__u8	code;
+		} icmpt;
+
+		unsigned long	data;
+	} uli_u;
+};
+
+#define FLOWR_NODECISION	0	/* rule not appliable to flow	*/
+#define FLOWR_SELECT		1	/* flow must follow this rule	*/
+#define FLOWR_CLEAR		2	/* priority level clears flow	*/
+#define FLOWR_ERROR		3
+
+struct fl_acc_args {
+	int	type;
+
+
+#define FL_ARG_FORWARD	1
+#define FL_ARG_ORIGIN	2
+
+	union {
+		struct sk_buff		*skb;
+		struct {
+			struct sock	*sk;
+			struct flowi	*flow;
+		} fl_o;
+	} fl_u;
+};
+
+
+struct pkt_filter {
+	atomic_t		refcnt;
+	unsigned int		offset;
+	__u32			value;
+	__u32			mask;
+	struct pkt_filter	*next;
+};
+
+#define FLR_INPUT		1
+#define FLR_OUTPUT		2
+
+struct flow_filter {
+	int				type;
+	union {
+		struct pkt_filter	*filter;
+		struct sock		*sk;
+	} u;
+};
+
+struct flow_rule {
+	struct flow_rule_ops		*ops;
+	unsigned char			private[0];
+};
+
+struct flow_rule_ops {
+	int			(*accept)(struct rt6_info *rt,
+					  struct rt6_info *rule,
+					  struct fl_acc_args *args,
+					  struct rt6_info **nrt);
+};
+
+#endif
diff -u --recursive --new-file v2.1.29/linux/include/net/if_inet6.h linux/include/net/if_inet6.h
--- v2.1.29/linux/include/net/if_inet6.h	Sun Nov  3 01:04:42 1996
+++ linux/include/net/if_inet6.h	Thu Mar 20 18:17:12 1997
@@ -59,14 +59,17 @@
 	struct ipv6_mc_socklist *next;
 };
 
-struct ipv6_mc_list {
-	struct in6_addr		addr;
+#define MAF_TIMER_RUNNING	0x01
+#define MAF_LAST_REPORTER	0x02
+
+struct ifmcaddr6 {
+	struct in6_addr		mca_addr;
 	struct device		*dev;
-	struct ipv6_mc_list	*next;
-	struct ipv6_mc_list	*if_next;
-	struct timer_list	timer;
-        int			tm_running;
-        atomic_t		users;	
+	struct ifmcaddr6	*next;
+	struct ifmcaddr6	*if_next;
+	struct timer_list	mca_timer;
+	unsigned long		mca_flags;
+	atomic_t		mca_users;	
 };
 
 #define	IFA_HOST	IPV6_ADDR_LOOPBACK
@@ -81,9 +84,8 @@
 	struct device		*dev;
 
 	struct inet6_ifaddr	*addr_list;
-	struct ipv6_mc_list	*mc_list;
+	struct ifmcaddr6	*mc_list;
 
-	__u32			if_index;
 	__u32			if_flags;
 	__u32			router:1,
 				unused:31;
diff -u --recursive --new-file v2.1.29/linux/include/net/ip6_fib.h linux/include/net/ip6_fib.h
--- v2.1.29/linux/include/net/ip6_fib.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/ip6_fib.h	Thu Mar 20 18:17:12 1997
@@ -0,0 +1,153 @@
+/*
+ *	Linux INET6 implementation 
+ *
+ *	Authors:
+ *	Pedro Roque		<roque@di.fc.ul.pt>	
+ *
+ *	This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _IP6_FIB_H
+#define _IP6_FIB_H
+
+#ifdef __KERNEL__
+
+#include <linux/ipv6_route.h>
+
+#include <net/dst.h>
+#include <net/flow.h>
+
+struct rt6_info;
+
+struct fib6_node {
+	struct fib6_node	*parent;
+	struct fib6_node	*left;
+	struct fib6_node	*right;
+
+	struct fib6_node	*subtree;
+
+	struct rt6_info		*leaf;
+
+	__u16			fn_bit;		/* bit key */
+	__u16			fn_flags;
+	__u32			fn_sernum;
+};
+
+
+/*
+ *	routing information
+ *
+ */
+
+struct rt6key {
+	struct in6_addr	addr;
+	int		plen;
+};
+
+struct rt6_info {
+	union {
+		struct dst_entry	dst;
+		struct rt6_info		*next;
+	} u;
+
+#define rt6i_dev			u.dst.dev
+#define rt6i_nexthop			u.dst.neighbour
+#define rt6i_use			u.dst.use
+#define rt6i_ref			u.dst.refcnt
+
+#define rt6i_tstamp			u.dst.lastuse
+
+	struct fib6_node		*rt6i_node;
+
+	struct in6_addr			rt6i_gateway;
+	
+	int				rt6i_keylen;
+
+	unsigned long			rt6i_flags;
+	unsigned long			rt6i_metric;
+	unsigned long			rt6i_expires;
+
+	union {
+		struct flow_rule	*rt6iu_flowr;
+		struct flow_filter	*rt6iu_filter;
+	} flow_u;
+
+#define rt6i_flowr			flow_u.rt6iu_flowr
+#define rt6i_filter			flow_u.rt6iu_filter
+
+	struct rt6key			rt6i_dst;
+	struct rt6key			rt6i_src;
+};
+
+
+struct rt6_statistics {
+	__u32		fib_nodes;
+	__u32		fib_route_nodes;
+	__u32		fib_rt_alloc;		/* permanet routes	*/
+	__u32		fib_rt_entries;		/* rt entries in table	*/
+	__u32		fib_rt_cache;		/* cache routes		*/
+};
+
+#define RTN_TL_ROOT	0x0001
+#define RTN_ROOT	0x0002		/* tree root node		*/
+#define RTN_RTINFO	0x0004		/* node with valid routing info	*/
+
+#define RTN_TAG		0x0100
+
+/*
+ *	priority levels (or metrics)
+ *
+ */
+
+#define RTPRI_FIREWALL	8		/* Firewall control information	*/
+#define RTPRI_FLOW	16		/* Flow based forwarding rules	*/
+#define RTPRI_KERN_CTL	32		/* Kernel control routes	*/
+
+#define RTPRI_USER_MIN	256		/* Mimimum user priority	*/
+#define RTPRI_USER_MAX	1024		/* Maximum user priority	*/
+
+#define RTPRI_KERN_DFLT	4096		/* Kernel default routes	*/
+
+#define	MAX_FLOW_BACKTRACE	32
+
+
+typedef void			(*f_pnode)(struct fib6_node *fn, void *);
+
+extern struct fib6_node		ip6_routing_table;
+
+/*
+ *	exported functions
+ */
+
+extern struct fib6_node		*fib6_lookup(struct fib6_node *root,
+					     struct in6_addr *daddr,
+					     struct in6_addr *saddr);
+
+#define RT6_FILTER_RTNODES	1
+
+extern void			fib6_walk_tree(struct fib6_node *root,
+					       f_pnode func, void *arg,
+					       int filter);
+
+extern int			fib6_add(struct fib6_node *root,
+					 struct rt6_info *rt);
+
+extern int			fib6_del(struct rt6_info *rt);
+
+/*
+ *	auxiliary functions
+ */
+extern __inline__ void rt6_release(struct rt6_info *rt)
+{
+	struct dst_entry *dst = (struct dst_entry *) rt;
+	if (atomic_dec_and_test(&dst->refcnt))
+		dst_destroy(dst);
+}
+
+extern void			rt6_ins(struct rt6_info *rt);
+
+#endif
+#endif
diff -u --recursive --new-file v2.1.29/linux/include/net/ip6_fw.h linux/include/net/ip6_fw.h
--- v2.1.29/linux/include/net/ip6_fw.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/ip6_fw.h	Thu Mar 20 18:17:12 1997
@@ -0,0 +1,54 @@
+#ifndef __NET_IP6_FW_H
+#define __NET_IP6_FW_H
+
+#define IP6_FW_LISTHEAD		0x1000
+#define IP6_FW_ACCEPT		0x0001
+#define IP6_FW_REJECT		0x0002
+
+#define IP6_FW_DEBUG	2
+
+#define IP6_FW_MSG_ADD		1
+#define IP6_FW_MSG_DEL		2
+#define IP6_FW_MSG_REPORT	3
+
+
+/*
+ *	Fast "hack" user interface
+ */
+struct ip6_fw_msg {
+	struct in6_addr		dst;
+	struct in6_addr		src;
+	int			dst_len;
+	int			src_len;
+	int			action;
+	int			policy;
+	int			proto;
+	union {
+		struct {
+			__u16	sport;
+			__u16	dport;
+		} transp;
+
+		unsigned long	data;
+
+		int		icmp_type;
+	} u;
+
+	int			msg_len;
+};
+
+#ifdef __KERNEL__
+
+#include <net/flow.h>
+
+struct ip6_fw_rule {
+	struct flow_rule	flowr;
+	struct ip6_fw_rule	*next;
+	struct ip6_fw_rule	*prev;
+	struct flowi		info;
+	unsigned long		policy;
+};
+
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.29/linux/include/net/ip6_route.h linux/include/net/ip6_route.h
--- v2.1.29/linux/include/net/ip6_route.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/ip6_route.h	Thu Mar 20 18:17:12 1997
@@ -0,0 +1,111 @@
+#ifndef _NET_IP6_ROUTE_H
+#define _NET_IP6_ROUTE_H
+
+#define IP6_RT_PRIO_FW		16
+#define IP6_RT_PRIO_USER	1024
+#define IP6_RT_PRIO_ADDRCONF	256
+#define IP6_RT_PRIO_KERN	512
+#define IP6_RT_FLOW_MASK	0x00ff
+
+#ifdef __KERNEL__
+
+#include <net/flow.h>
+#include <net/ip6_fib.h>
+
+/*
+ *	Structure for assync processing of operations on the routing
+ *	table
+ */
+
+struct rt6_req {
+	int			operation;
+	struct rt6_info		*ptr;
+
+	struct rt6_req		*next;
+	struct rt6_req		*prev;
+
+#define RT_OPER_ADD		1
+#define RT_OPER_DEL		2
+};
+
+
+struct pol_chain {
+	int			type;
+	int			priority;
+	struct fib6_node	*rules;
+	struct pol_chain	*next;
+};
+
+extern struct rt6_info	ip6_null_entry;
+
+extern void			ip6_route_input(struct sk_buff *skb);
+
+extern struct dst_entry *	ip6_route_output(struct sock *sk,
+						 struct flowi *fl);
+
+extern void			ip6_route_init(void);
+extern void			ip6_route_cleanup(void);
+
+extern int			ipv6_route_ioctl(unsigned int cmd, void *arg);
+
+extern struct rt6_info *	ip6_route_add(struct in6_rtmsg *rtmsg,
+					      int *err);
+extern int			ip6_del_rt(struct rt6_info *);
+
+extern int			ip6_rt_addr_add(struct in6_addr *addr,
+						struct device *dev);
+
+extern void			rt6_sndmsg(int type, struct in6_addr *dst,
+					   struct in6_addr *src,
+					   struct in6_addr *gw,
+					   struct device *dev, 
+					   int dstlen, int srclen,
+					   int metric, __u32 flags);
+
+extern struct rt6_info		*rt6_lookup(struct in6_addr *daddr,
+					    struct in6_addr *saddr,
+					    struct device *dev, int flags);
+
+/*
+ *	support functions for ND
+ *
+ */
+extern struct rt6_info *	rt6_get_dflt_router(struct in6_addr *addr,
+						    struct device *dev);
+extern struct rt6_info *	rt6_add_dflt_router(struct in6_addr *gwaddr,
+						    struct device *dev);
+
+extern void			rt6_purge_dflt_routers(int lst_resort);
+
+extern struct rt6_info *	rt6_redirect(struct in6_addr *dest,
+					     struct in6_addr *saddr,
+					     struct in6_addr *target,
+					     struct device *dev,
+					     int on_link);
+
+extern void			rt6_pmtu_discovery(struct in6_addr *addr,
+						   struct device *dev,
+						   int pmtu);
+
+extern struct rt6_info *	ip6_rt_copy(struct rt6_info *rt);
+
+/*
+ *	Store a destination cache entry in a socket
+ *	For UDP/RAW sockets this is done on udp_connect.
+ */
+
+extern __inline__ void ip6_dst_store(struct sock *sk, struct dst_entry *dst)
+{
+	struct ipv6_pinfo *np;
+	struct rt6_info *rt;
+		
+	np = &sk->net_pinfo.af_inet6;
+	np->dst = dst;
+	
+	rt = (struct rt6_info *) dst;
+	
+	np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0;
+}
+
+#endif
+#endif
diff -u --recursive --new-file v2.1.29/linux/include/net/ipv6.h linux/include/net/ipv6.h
--- v2.1.29/linux/include/net/ipv6.h	Thu Feb  6 02:59:05 1997
+++ linux/include/net/ipv6.h	Wed Mar 26 11:01:16 1997
@@ -4,7 +4,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: ipv6.h,v 1.19 1996/09/24 17:04:20 roque Exp $
+ *	$Id: ipv6.h,v 1.5 1997/03/18 18:24:10 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -17,6 +17,7 @@
 
 #include <linux/ipv6.h>
 #include <net/ndisc.h>
+#include <net/flow.h>
 
 /*
  *	NextHeader field of IPv6 header
@@ -86,8 +87,29 @@
 
 extern struct ipv6_mib	ipv6_statistics;
 
-extern int		ipv6_forwarding;	/* host/router switch */
-extern int		ipv6_hop_limit;		/* default hop limit */
+struct ipv6_config {
+	int		forwarding;
+	int		hop_limit;
+	int		accept_ra;
+	int		accept_redirects;
+	
+	int		nd_max_mcast_solicit;
+	int		nd_max_ucast_solicit;
+	int		nd_retrans_time;
+	int		nd_base_reachable_time;
+	int		nd_delay_probe_time;
+
+	int		autoconf;
+	int		dad_transmits;
+	int		rtr_solicits;
+	int		rtr_solicit_interval;
+	int		rtr_solicit_delay;
+
+	int		rt_cache_timeout;
+	int		rt_gc_period;
+};
+
+extern struct ipv6_config ipv6_config;
 
 struct ipv6_frag {
 	__u16			offset;
@@ -141,6 +163,11 @@
 
 extern int		ipv6_addr_type(struct in6_addr *addr);
 
+extern __inline__ int ipv6_addr_scope(struct in6_addr *addr)
+{
+	return ipv6_addr_type(addr) & IPV6_ADDR_SCOPE_MASK;
+}
+
 extern __inline__ int ipv6_addr_cmp(struct in6_addr *a1, struct in6_addr *a2)
 {
 	return memcmp((void *) a1, (void *) a2, sizeof(struct in6_addr));
@@ -169,54 +196,18 @@
 		 a->s6_addr32[2] | a->s6_addr32[3] ) == 0); 
 }
 
+extern __inline__ int gfp_any(void)
+{
+	int pri = GFP_KERNEL;
+	if (intr_count)
+		pri = GFP_ATOMIC;
+	return pri;
+}
+
 /*
  *	Prototypes exported by ipv6
  */
 
-#if 0
-extern int			ipv6_build_header(struct sk_buff *skb,
-						  struct device *dev,
-						  struct in6_addr *saddr_in, 
-						  struct in6_addr *daddr_in,
-						  int proto, int len, 
-						  struct ipv6_pinfo *np);
-#endif
-
-extern void			ipv6_redo_mac_hdr(struct sk_buff *skb,
-						  struct neighbour *neigh,
-						  int len);
-
-extern int			ipv6_bld_hdr_2(struct sock *sk,
-					       struct sk_buff *skb,
-					       struct device *dev,
-					       struct neighbour *neigh,
-					       struct in6_addr *saddr,
-					       struct in6_addr *daddr,
-					       int proto, int len);
-
-extern int			ipv6_xmit(struct sock *sk,
-					  struct sk_buff *skb,
-					  struct in6_addr *saddr,
-					  struct in6_addr *daddr,
-					  struct ipv6_options *opt,
-					  int proto);
-
-extern void			ipv6_queue_xmit(struct sock *sk,
-						struct device *dev,
-						struct sk_buff *skb,
-						int free);
-
-extern int			ipv6_build_xmit(struct sock *sk,
-						inet_getfrag_t getfrag,
-						const void * data,
-						struct in6_addr * daddr,
-						unsigned short int length,
-						struct in6_addr * saddr,
-						struct device *dev,
-						struct ipv6_options *opt,
-						int proto, int hlimit,
-						int noblock);
-
 /*
  *	rcv function (called from netdevice level)
  */
@@ -225,16 +216,41 @@
 					 struct device *dev, 
 					 struct packet_type *pt);
 
-extern void			ipv6_forward(struct sk_buff *skb,
-					     struct device *dev,
-					     int flags);
+/*
+ *	upper-layer output functions
+ */
+extern int			ip6_xmit(struct sock *sk,
+					 struct sk_buff *skb,
+					 struct flowi *fl,
+					 struct ipv6_options *opt);
+
+extern int			ip6_nd_hdr(struct sock *sk,
+					   struct sk_buff *skb,
+					   struct device *dev,
+					   struct in6_addr *saddr,
+					   struct in6_addr *daddr,
+					   int proto, int len);
+
+extern int			ip6_build_xmit(struct sock *sk,
+					       inet_getfrag_t getfrag,
+					       const void *data,
+					       struct flowi *fl,
+					       unsigned short length,
+					       struct ipv6_options *opt,
+					       int hlimit, int flags);
+
+/*
+ *	skb processing functions
+ */
 
-#define IP6_FW_SRCRT	0x1
-#define	IP6_FW_STRICT	0x2
+extern int			ip6_forward(struct sk_buff *skb);
+extern int			ip6_input(struct sk_buff *skb);
+extern int			ip6_mc_input(struct sk_buff *skb);
 
 /*
  *	Extension header (options) processing
  */
+
 extern int			ipv6opt_bld_rthdr(struct sk_buff *skb,
 						  struct ipv6_options *opt,
 						  struct in6_addr *addr,
@@ -254,27 +270,6 @@
 
 extern void			ipv6opt_free(struct ipv6_options *opt);
 
-
-/*
- *	socket lookup (af_inet6.c)
- */
-
-extern struct sock *		inet6_get_sock(struct proto *prot, 
-					       struct in6_addr *loc_addr, 
-					       struct in6_addr *rmt_addr,
-					       unsigned short loc_port,
-					       unsigned short rmt_port);
-
-extern struct sock *		inet6_get_sock_raw(struct sock *sk, 
-						   unsigned short num,
-						   struct in6_addr *loc_addr, 
-						   struct in6_addr *rmt_addr);
-
-extern struct sock *		inet6_get_sock_mcast(struct sock *sk, 
-						     unsigned short num,
-						     unsigned short rmt_port,
-						     struct in6_addr *loc_addr, 
-						     struct in6_addr *rmt_addr);
 
 /*
  *	socket options (ipv6_sockglue.c)
diff -u --recursive --new-file v2.1.29/linux/include/net/ipv6_route.h linux/include/net/ipv6_route.h
--- v2.1.29/linux/include/net/ipv6_route.h	Thu Dec 12 06:54:21 1996
+++ linux/include/net/ipv6_route.h	Wed Dec 31 16:00:00 1969
@@ -1,200 +0,0 @@
-/*
- *	Linux INET6 implementation 
- *
- *	Authors:
- *	Pedro Roque		<roque@di.fc.ul.pt>	
- *
- *	This program is free software; you can redistribute it and/or
- *      modify it under the terms of the GNU General Public License
- *      as published by the Free Software Foundation; either version
- *      2 of the License, or (at your option) any later version.
- */
-
-#ifndef _NET_IPV6_ROUTE_H
-#define _NET_IPV6_ROUTE_H
-
-#include <linux/ipv6_route.h>
-
-
-#ifdef __KERNEL__
-
-
-struct fib6_node {
-	struct fib6_node	*parent;
-	struct fib6_node	*left;
-	struct fib6_node	*right;
-
-	struct rt6_info		*leaf;
-
-	__u16			fn_bit;		/* bit key */
-	__u16			fn_flags;
-	__u32			fn_sernum;
-};
-
-
-struct rt6_info;
-
-typedef void (*rt6_output_method_t) (struct sk_buff *skb, struct rt6_info *rt);
-
-struct rt6_info {
-	struct fib6_node	*fib_node;
-	struct rt6_info		*next;
-
-	struct in6_addr		rt_dst;
-	
-	atomic_t		rt_use;		/* dcache references	*/
-	atomic_t		rt_ref;		/* fib references	*/
-
-	struct neighbour        *rt_nexthop;
-	struct device		*rt_dev;
-	
-	rt6_output_method_t	rt_output_method;
-
-	__u16			rt_metric;
-	__u16			rt_prefixlen;
-	__u32			rt_flags;
-	unsigned long		rt_expires;
-};
-
-extern struct rt6_info		*default_rt_list;
-extern struct rt6_info		*last_resort_rt;
-
-struct dest_entry {
-	struct rt6_info		rt;
-
-	__u32			dc_irtt;
-	__u32			dc_window;
-	__u16			dc_pmtu;
-
-	unsigned long		dc_tstamp;	/* for garbage collection */
-
-#define dc_addr			rt.rt_dst
-#define dc_usecnt		rt.rt_use
-#define dc_nexthop		rt.rt_nexthop
-#define dc_flags		rt.rt_flags
-};
-
-/*
- *	Structure for assync processing of operations on the routing
- *	table
- */
-
-struct rt6_req {
-	int			operation;
-	struct rt6_info		*ptr;
-
-	struct rt6_req		*next;
-	struct rt6_req		*prev;
-
-#define RT_OPER_ADD		1
-#define RT_OPER_DEL		2
-};
-
-struct rt6_statistics {
-	__u32		fib_nodes;
-	__u32		fib_route_nodes;
-	__u32		fib_rt_alloc;
-	__u32		fib_rt_entries;
-	__u32		fib_dc_alloc;
-};
-
-#define RTN_ROOT	0x0001		/* root node			*/
-#define RTN_BACKTRACK	0x0002		/* backtrack point		*/
-#define RTN_TAG		0x0010
-
-/*
- *	Values for destination cache garbage colection
- *	These are wild guesses for now...
- */
-
-#define	DC_WATER_MARK		512
-#define DC_SHORT_TIMEOUT	(5*HZ)
-#define DC_LONG_TIMEOUT	        (15*HZ)
-
-#define DC_TIME_RUN		(5*HZ)
-#define DC_TIME_RETRY		HZ
-
-#define RT6_FILTER_NONE		0
-#define RT6_FILTER_RTNODES	1
-/*
- *	Prototypes
- */
-
-/*
- *	check/obtain destination cache from routing table
- */
-
-extern struct dest_entry *	ipv6_dst_check(struct dest_entry *dc, 
-					       struct in6_addr * daddr,
-					       __u32 sernum, int flags);
-
-extern struct dest_entry *	ipv6_dst_route(struct in6_addr * daddr,
-					       struct device *src_dev,
-					       int flags);
-
-extern void			ipv6_dst_unlock(struct dest_entry *dest);
-
-extern struct rt6_info *	fibv6_lookup(struct in6_addr *addr,
-					     struct device *dev,
-					     int flags);
-
-/*
- *	user space set/del route
- */
-
-extern int			ipv6_route_ioctl(unsigned int cmd, void *arg);
-
-
-extern void			ipv6_route_init(void);
-extern void			ipv6_route_cleanup(void);
-
-extern void			rt6_ifdown(struct device *dev);
-
-extern int			ipv6_route_add(struct in6_rtmsg *rt);
-
-extern int			fib6_del_rt(struct rt6_info *rt);
-
-extern void			rt6_sndmsg(__u32 type, struct in6_addr *dst,
-					   struct in6_addr *gw, __u16 plen,
-					   struct device *dev,
-					   __u16 metric, __u16 flags);
-/*
- *	ICMP interface
- */
-
-extern struct rt6_info *	ipv6_rt_redirect(struct device *dev,
-						 struct in6_addr *dest,
-						 struct in6_addr *target,
-						 int on_link);
-
-extern void			rt6_handle_pmtu(struct in6_addr *addr,
-						int pmtu);
-/*
- *
- */
-
-extern struct fib6_node		routing_table;
-extern struct rt6_statistics	rt6_stats;
-
-static __inline__ void rt_release(struct rt6_info *rt)
-{
-	atomic_dec(&rt->rt_ref);
-	if ((rt->rt_use | rt->rt_ref) == 0)
-	{
-		if (rt->rt_nexthop)
-		{
-			neighbour_unlock(rt->rt_nexthop);
-		}
-
-		if (rt->rt_flags & RTI_DCACHE)
-		{
-			rt6_stats.fib_dc_alloc--;
-		}
-		rt6_stats.fib_rt_alloc--;
-		kfree(rt);
-	}
-}
-
-#endif
-
-#endif
diff -u --recursive --new-file v2.1.29/linux/include/net/ndisc.h linux/include/net/ndisc.h
--- v2.1.29/linux/include/net/ndisc.h	Thu Feb  6 02:59:05 1997
+++ linux/include/net/ndisc.h	Wed Mar 26 11:01:16 1997
@@ -91,8 +91,8 @@
 };
 
 struct nd_msg {
-        struct icmpv6hdr icmph;
-        struct in6_addr target;
+        struct icmp6hdr	icmph;
+        struct in6_addr	target;
         struct {
                 __u8	opt_type;
                 __u8	opt_len;
@@ -101,7 +101,7 @@
 };
 
 struct ra_msg {
-        struct icmpv6hdr	icmph;
+        struct icmp6hdr		icmph;
 	__u32			reachable_time;
 	__u32			retrans_timer;
 };
@@ -147,12 +147,9 @@
 					      struct in6_addr *saddr,
 					      struct in6_addr *daddr);
 
-extern int			(*ndisc_eth_hook) (unsigned char *,
-						   struct device *,
-						   struct sk_buff *);
 extern int			ndisc_eth_resolv(unsigned char *,
-						struct device *,
 						struct sk_buff *);
+
 extern void			ndisc_forwarding_on(void);
 extern void			ndisc_forwarding_off(void);
 
@@ -163,7 +160,20 @@
 struct rt6_info *		dflt_rt_lookup(void);
 
 extern unsigned long	nd_rand_seed;
-extern int		ipv6_random(void);
+extern unsigned long	ipv6_random(void);
+
+/*
+ *	IGMP
+ */
+extern void			igmp6_init(struct net_proto_family *ops);
+
+extern int			igmp6_event_query(struct sk_buff *skb,
+						  struct icmp6hdr *hdr,
+						  int len);
+
+extern int			igmp6_event_report(struct sk_buff *skb,
+						   struct icmp6hdr *hdr,
+						   int len);
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.29/linux/include/net/neighbour.h linux/include/net/neighbour.h
--- v2.1.29/linux/include/net/neighbour.h	Thu Feb  6 02:59:05 1997
+++ linux/include/net/neighbour.h	Wed Mar 26 11:01:08 1997
@@ -12,8 +12,6 @@
 
 #include <asm/atomic.h>
 #include <linux/skbuff.h>
-#include <linux/interrupt.h>
-#include <linux/netdevice.h>
 
 /*
  *	flags
@@ -30,6 +28,7 @@
 	unsigned long		lastused;
 	unsigned long		flags;
 	unsigned char		ha[MAX_ADDR_LEN];
+	struct hh_cache		*hh;
 	atomic_t		refcnt;
 	struct neigh_ops	*ops;
 	struct sk_buff_head	arp_queue;
@@ -40,12 +39,11 @@
 	int			family;
 	unsigned int		(*hash)(void *primary_key);
 	int			(*resolve)(unsigned char *h_dest,
-					   struct device *dev,
 					   struct sk_buff *skb);
 	void			(*destructor)(struct neighbour *);
 };
 
-extern struct neighbour		*neigh_alloc(int size, int priority);
+extern struct neighbour		*neigh_alloc(int size, struct neigh_ops *);
 
 /*
  *	Neighbour references
@@ -63,7 +61,7 @@
  */
 
 
-static __inline__ void neighbour_unlock(struct neighbour *neigh)
+static __inline__ void neigh_release(struct neighbour *neigh)
 {
 	if (atomic_dec_and_test(&neigh->refcnt))
 		neigh->lastused = jiffies;
@@ -111,6 +109,8 @@
 extern struct neighbour *	neigh_lookup(struct neigh_table *tbl,
 					     void *pkey, int key_len,
 					     struct device *dev);
+
+extern void			neigh_destroy(struct neighbour *neigh);
 
 static __inline__ void neigh_insert(struct neigh_table *tbl,
 				    struct neighbour *neigh)
diff -u --recursive --new-file v2.1.29/linux/include/net/netbeui.h linux/include/net/netbeui.h
--- v2.1.29/linux/include/net/netbeui.h	Thu Jan  2 05:13:27 1997
+++ linux/include/net/netbeui.h	Thu Mar 20 18:17:12 1997
@@ -5,6 +5,9 @@
 #ifndef __NET_NETBEUI_H
 #define __NET_NETBEUI_H
 
+
+#define NB_NAME_LEN	16
+
 /*
  *	Used to keep lists of netbeui sessions
  */
@@ -51,12 +54,15 @@
 #define NB_NAME_COLLIDE		2	/* Name collided - we failed */
 #define NB_OURS			3	/* We own the name	*/
 #define NB_NAME_OTHER		4	/* Name found - owned by other */
+#define NB_NAME_GET		5	/* Trying to allocate a name */
+#define NB_STATE		7	/* State bits */
+#define NB_NAME_GROUP		8	/* Group name bit */
 	int ours;			/* We own this name */
 	int users;			/* Number of nb_ses's to this name */
 	struct timer_list	timer;	/* Our timer */
 	int timer_mode;			/* Timer mode */
 #define NB_TIMER_ACQUIRE	1	/* Expiry means we got our name */
-#define NB_TIMER_COLLIDE	2	/* Expire a collded record */
+#define NB_TIMER_COLLIDE	2	/* Expire a collided record */
 #define NB_TIMER_DROP		3	/* Drop a learned record */	
 };
 
@@ -78,5 +84,67 @@
 extern struct nb_name *netbeui_lookup_name(char *name);
 extern int nb_delete_name(struct nb_name *name);
 
+/*
+ *	NetBEUI Protocol items
+ */
+
+#define ADD_GROUP_NAME_QUERY	0x00
+#define ADD_NAME_QUERY		0x01
+#define NAME_IN_CONFLICT	0x02
+#define STATUS_QUERY		0x03
+#define TERMINATE_TRACE		0x07
+#define DATAGRAM		0x08
+#define DATAGRAM_BROADCAST	0x09
+#define NAME_QUERY		0x0A
+#define ADD_NAME_RESPONSE	0x0D
+#define NAME_RECOGNIZED		0x0E
+#define STATUS_RESPONSE		0x0F
+#define TERMINATE_TRACE2	0x13
+#define DATA_ACK		0x14
+#define DATA_FIRST_MIDDLE	0x15
+#define DATA_ONLY_LAST		0x16
+#define SESSION_CONFIRM		0x17
+#define SESSION_END		0x18
+#define SESSION_INITIALIZE	0x19
+#define NO_RECEIVE		0x1A
+#define RECEIVE_OUTSTANDING	0x1B
+#define RECEIVE_CONTINUE	0x1C
+#define SESSION_ALIVE		0x1F
+
+#define NB_TRANSMIT_COUNT	6
+#define NB_TRANSMIT_TIMEOUT	(HZ/2)
+
+#define NB_DESCRIM_1		0xEF
+#define NB_DESCRIM_2		0xFF
+
+struct nb_dgram_pkt
+{
+	__u16	length;
+	__u8	descrim1;
+	__u8	descrim2;
+	__u8	command;
+	__u8	option1;
+	__u16	option2;
+	__u16	tx_seq;
+	__u16	rx_seq;
+	__u8	dest[NB_NAME_LEN];
+	__u8	src[NB_NAME_LEN];
+};
+
+struct nb_sess_pkt
+{
+	__u16	length;
+	__u8	descrim1;
+	__u8	descrim2;
+	__u8	command;
+	__u8	option1;
+	__u16	option2;
+	__u16	tx_seq;
+	__u16	rx_seq;
+	__u8	dnum;
+	__u8	snum;
+};
+
+#define NO_SEQ	0
 
 #endif
diff -u --recursive --new-file v2.1.29/linux/include/net/netlink.h linux/include/net/netlink.h
--- v2.1.29/linux/include/net/netlink.h	Thu Dec 12 06:54:21 1996
+++ linux/include/net/netlink.h	Thu Mar 20 18:17:12 1997
@@ -66,6 +66,7 @@
 #define NETLINK_ARPD		8
 #define NETLINK_IPSEC		10	/* IPSEC */
 #define NETLINK_ROUTE6		11	/* af_inet6 route comm channel */
+#define NETLINK_IP6_FW		13
 /* Wouldn't this suffice instead of the confusion at the top of
    this file?  i.e. 3 is firewall or ppp... */
 /* #define MAX_LINKS		16 */
diff -u --recursive --new-file v2.1.29/linux/include/net/protocol.h linux/include/net/protocol.h
--- v2.1.29/linux/include/net/protocol.h	Thu Feb  6 02:55:48 1997
+++ linux/include/net/protocol.h	Wed Mar 26 10:59:18 1997
@@ -34,34 +34,36 @@
 
 
 /* This is used to register protocols. */
-struct inet_protocol {
-  int			(*handler)(struct sk_buff *skb, unsigned short len);
-  void			(*err_handler)(struct sk_buff *skb, unsigned char *dp);
-  struct inet_protocol *next;
-  unsigned char		protocol;
-  unsigned char		copy:1;
-  void			*data;
-  const char		*name;
+struct inet_protocol 
+{
+	int			(*handler)(struct sk_buff *skb, unsigned short len);
+	void			(*err_handler)(struct sk_buff *skb, unsigned char *dp);
+	struct inet_protocol	*next;
+	unsigned char		protocol;
+	unsigned char		copy:1;
+	void			*data;
+	const char		*name;
 };
 
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-struct inet6_protocol {
-  int			(*handler)(struct sk_buff *skb, struct device *dev,
-				   struct in6_addr *saddr,
-				   struct in6_addr *daddr,
-				   struct ipv6_options *opt, 
-				   unsigned short len,
-				   int redo, struct inet6_protocol *protocol);
-
-  void			(*err_handler)(int type, int code, unsigned char *buff,
-				       __u32 info, struct in6_addr *saddr,
-				       struct in6_addr *daddr,
-				       struct inet6_protocol *protocol);
-  struct inet6_protocol *next;
-  unsigned char		protocol;
-  unsigned char		copy:1;
-  void			*data;
-  const char		*name;
+struct inet6_protocol 
+{
+	int	(*handler)(struct sk_buff *skb, struct device *dev,
+			struct in6_addr *saddr,
+			struct in6_addr *daddr,
+			struct ipv6_options *opt, 
+			unsigned short len,
+			int redo, struct inet6_protocol *protocol);
+
+	void	(*err_handler)(int type, int code, unsigned char *buff,
+			__u32 info, struct in6_addr *saddr,
+			struct in6_addr *daddr,
+			struct inet6_protocol *protocol);
+	struct inet6_protocol *next;
+	unsigned char	protocol;
+	unsigned char	copy:1;
+	void		*data;
+	const char	*name;
 };
 #endif
 
@@ -73,12 +75,12 @@
 extern struct inet6_protocol *inet6_protos[MAX_INET_PROTOS];
 #endif
 
-extern void		inet_add_protocol(struct inet_protocol *prot);
-extern int		inet_del_protocol(struct inet_protocol *prot);
+extern void	inet_add_protocol(struct inet_protocol *prot);
+extern int	inet_del_protocol(struct inet_protocol *prot);
 
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-extern void		inet6_add_protocol(struct inet6_protocol *prot);
-extern int		inet6_del_protocol(struct inet6_protocol *prot);
+extern void	inet6_add_protocol(struct inet6_protocol *prot);
+extern int	inet6_del_protocol(struct inet6_protocol *prot);
 #endif
 
 #endif	/* _PROTOCOL_H */
diff -u --recursive --new-file v2.1.29/linux/include/net/route.h linux/include/net/route.h
--- v2.1.29/linux/include/net/route.h	Thu Feb  6 02:59:02 1997
+++ linux/include/net/route.h	Wed Mar 26 11:01:08 1997
@@ -100,7 +100,7 @@
 /*
  *	Flags not visible at user level.
  */
-#define RTF_INTERNAL	0xFFFF0000
+#define RTF_INTERNAL	0xFFFF8000	/* to get RTF_MAGIC as well... */
 
 /*
  *	Flags saved in FIB.
diff -u --recursive --new-file v2.1.29/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.29/linux/include/net/sock.h	Tue Mar  4 10:25:26 1997
+++ linux/include/net/sock.h	Wed Mar 26 10:59:20 1997
@@ -152,9 +152,9 @@
 				mc_loop:1,
                                 unused:2;
 
-	/* device for outgoing mcast packets */
+	/* device for outgoing packets */
 
-	struct device		*mc_if;
+	struct device		*oif;
 
 	struct ipv6_mc_socklist	*ipv6_mc_list;
 	/* 
@@ -164,8 +164,8 @@
 	 * (ex. PMTU)
 	 */
 	
-	struct dest_entry	*dest;
-	__u32			dc_sernum;
+	struct dst_entry	*dst;
+	__u32			dst_cookie;
 
 	struct ipv6_options	*opt;
 };
@@ -317,7 +317,7 @@
 	__u32			syn_seq;
 	__u32			urg_seq;
 	__u32			urg_data;
-	int			users;			/* user count */
+	int			sock_readers;		/* user count */
 
 	unsigned char		delayed_acks,
 				dup_acks;
@@ -328,7 +328,6 @@
 	volatile char		dead,
 				urginline,
 				intr,
-				blog,
 				done,
 				reuse,
 				keepopen,
@@ -343,14 +342,15 @@
 	unsigned long	        lingertime;
 	int			proc;
 
-	struct sock             **hashtable;
-	int			hashent;
 	struct sock		*next;
+	struct sock		**pprev;
+	struct sock		*bind_next;
+	struct sock		**bind_pprev;
 	struct sock		*prev;
+	int			hashent;
 	struct sock		*pair;
 
 	struct sk_buff		* send_head;
-	struct sk_buff		* send_tail;
 
 	struct sk_buff_head	back_log;
 	struct sk_buff		*partial;
@@ -426,7 +426,10 @@
 	unsigned short		type;
 	unsigned char		localroute;	/* Route locally only */
 	struct ucred		peercred;
-  
+	/* What the user has tried to set with the security API */
+	short			authentication;
+	short			encryption;  
+	short			encrypt_net;
 /*
  *	This is where all the private (optional) areas that don't
  *	overlap will eventually live. 
@@ -511,19 +514,6 @@
 						struct sk_buff *skb);  
 };
 
-#if 0
-/*
- *	Inet protocol options
- */
-struct inet_options {
-	__u8				version;
-	union {
-		struct options		opt_v4;
-		struct ipv6_options	opt_v6;
-	} u;
-};
-#endif
-
 /*
  *	IP protocol blocks we attach to sockets.
  *	socket layer -> transport layer interface
@@ -665,14 +655,27 @@
 #if 0
 /* debugging code: the test isn't even 100% correct, but it can catch bugs */
 /* Note that a double lock is ok in theory - it's just _usually_ a bug */
-	if (sk->users) {
+	if (sk->sock_readers) {
 		__label__ here;
 		printk("double lock on socket at %p\n", &&here);
 here:
 	}
 #endif
-	sk->users++;
+#ifdef __SMP__
+	/*
+	 * This is a very broken bottom half synchronization mechanism.
+	 * You don't want to know..
+	 */
+	{ unsigned long flags;
+	save_flags(flags);
+	cli();
+	sk->sock_readers++;
+	restore_flags(flags);
+	}
+#else
+	sk->sock_readers++;
 	barrier();
+#endif
 }
 
 static inline void release_sock(struct sock *sk)
@@ -680,14 +683,14 @@
 	barrier();
 #if 0
 /* debugging code: remove me when ok */
-	if (sk->users == 0) {
+	if (sk->sock_readers == 0) {
 		__label__ here;
-		sk->users = 1;
+		sk->sock_readers = 1;
 		printk("trying to unlock unlocked socket at %p\n", &&here);
 here:
 	}
 #endif
-	if ((sk->users = sk->users-1) == 0)
+	if ((sk->sock_readers = sk->sock_readers-1) == 0)
 		__release_sock(sk);
 }
 
@@ -738,6 +741,7 @@
 						     unsigned long fallback,
 						     int noblock,
 						     int *errcode);
+
 extern int 			sock_no_fcntl(struct socket *, unsigned int, unsigned long);
 extern int			sock_no_getsockopt(struct socket *, int , int,
 						   char *, int *);
diff -u --recursive --new-file v2.1.29/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.1.29/linux/include/net/tcp.h	Tue Mar  4 10:25:26 1997
+++ linux/include/net/tcp.h	Wed Mar 26 11:01:09 1997
@@ -23,10 +23,10 @@
 #include <net/checksum.h>
 
 /* This is for all connections with a full identity, no wildcards. */
-#define TCP_HTABLE_SIZE		128
+#define TCP_HTABLE_SIZE		256
 
 /* This is for listening sockets, thus all sockets which possess wildcards. */
-#define TCP_LHTABLE_SIZE	16	/* Yes, really, this is all you need. */
+#define TCP_LHTABLE_SIZE	32	/* Yes, really, this is all you need. */
 
 /* This is for all sockets, to keep track of the local port allocations. */
 #define TCP_BHTABLE_SIZE	64
@@ -71,23 +71,19 @@
 static __inline__ void tcp_sk_bindify(struct sock *sk)
 {
 	int hashent = tcp_sk_bhashfn(sk);
+	struct sock **htable = &tcp_bound_hash[hashent];
 
-	sk->prev = tcp_bound_hash[hashent];
-	tcp_bound_hash[hashent] = sk;
+	if((sk->bind_next = *htable) != NULL)
+		(*htable)->bind_pprev = &sk->bind_next;
+	*htable = sk;
+	sk->bind_pprev = htable;
 }
 
 static __inline__ void tcp_sk_unbindify(struct sock *sk)
 {
-	int hashent = tcp_sk_bhashfn(sk);
-	struct sock **htable = &tcp_bound_hash[hashent];
-
-	while(*htable) {
-		if(*htable == sk) {
-			*htable = sk->prev;
-			break;
-		}
-		htable = &((*htable)->prev);
-	}
+	if(sk->bind_next)
+		sk->bind_next->bind_pprev = sk->bind_pprev;
+	*(sk->bind_pprev) = sk->bind_next;
 }
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -315,7 +311,7 @@
 
 extern int			tcp_do_sendmsg(struct sock *sk, 
 					       int iovlen, struct iovec *iov,
-					       int len, int flags);
+					       int flags);
 
 extern int			tcp_ioctl(struct sock *sk, 
 					  int cmd, 
@@ -371,8 +367,9 @@
 						     struct sk_buff *skb,
 						     struct open_request *req);
 
-extern int			tcp_v4_backlog_rcv(struct sock *sk,
-						   struct sk_buff *skb);
+extern int			tcp_v4_do_rcv(struct sock *sk,
+					      struct sk_buff *skb);
+
 extern int			tcp_v4_connect(struct sock *sk,
 					       struct sockaddr *uaddr,
 					       int addr_len);
diff -u --recursive --new-file v2.1.29/linux/init/main.c linux/init/main.c
--- v2.1.29/linux/init/main.c	Mon Mar 17 14:54:35 1997
+++ linux/init/main.c	Thu Mar 20 17:25:10 1997
@@ -22,6 +22,7 @@
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/fs.h>
+#include <linux/proc_fs.h>
 #include <linux/ctype.h>
 #include <linux/delay.h>
 #include <linux/utsname.h>
@@ -887,7 +888,9 @@
 	}
 #endif
 	mem_init(memory_start,memory_end);
+#ifdef CONFIG_PROC_FS
 	proc_root_init();
+#endif
 	kmem_cache_sizes_init();
 	vma_init();
 	buffer_init();
diff -u --recursive --new-file v2.1.29/linux/kernel/exit.c linux/kernel/exit.c
--- v2.1.29/linux/kernel/exit.c	Sun Jan 26 02:07:48 1997
+++ linux/kernel/exit.c	Fri Mar 14 17:40:55 1997
@@ -577,7 +577,7 @@
 
 NORET_TYPE void do_exit(long code)
 {
-	if (intr_count) {
+	if (0 && intr_count) {
 		printk("Aiee, killing interrupt handler\n");
 		intr_count = 0;
 	}
diff -u --recursive --new-file v2.1.29/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.1.29/linux/kernel/ksyms.c	Fri Feb  7 05:54:55 1997
+++ linux/kernel/ksyms.c	Thu Mar 20 17:25:10 1997
@@ -40,6 +40,7 @@
 #include <linux/minix_fs.h>
 #include <linux/ext2_fs.h>
 #include <linux/random.h>
+#include <linux/reboot.h>
 #include <linux/mount.h>
 #include <linux/pagemap.h>
 #include <linux/sysctl.h>
@@ -81,8 +82,6 @@
 extern int request_dma(unsigned int dmanr, char * deviceID);
 extern void free_dma(unsigned int dmanr);
 
-extern void hard_reset_now(void);
-
 #ifdef MODVERSIONS
 const struct module_symbol __export_Using_Versions
 __attribute__((section("__ksymtab"))) = {
@@ -302,7 +301,11 @@
 EXPORT_SYMBOL(simple_strtoul);
 EXPORT_SYMBOL(system_utsname);
 EXPORT_SYMBOL(sys_call_table);
-EXPORT_SYMBOL(hard_reset_now);
+EXPORT_SYMBOL(machine_restart);
+EXPORT_SYMBOL(machine_halt);
+EXPORT_SYMBOL(machine_power_off);
+EXPORT_SYMBOL(register_reboot_notifier);
+EXPORT_SYMBOL(unregister_reboot_notifier);
 EXPORT_SYMBOL(_ctype);
 EXPORT_SYMBOL(secure_tcp_sequence_number);
 
diff -u --recursive --new-file v2.1.29/linux/kernel/panic.c linux/kernel/panic.c
--- v2.1.29/linux/kernel/panic.c	Thu Dec 19 01:03:37 1996
+++ linux/kernel/panic.c	Thu Mar 20 17:25:10 1997
@@ -14,9 +14,9 @@
 #include <linux/sched.h>
 #include <linux/delay.h>
 #include <linux/smp.h>
+#include <linux/reboot.h>
 
 asmlinkage void sys_sync(void);	/* it's really int */
-extern void hard_reset_now(void);
 extern void do_unblank_screen(void);
 extern int C_A_D;
 
@@ -62,7 +62,7 @@
 		 *	choosing not too. It might crash, be corrupt or do
 		 *	more harm than good for other reasons.
 		 */
-		hard_reset_now();
+		machine_restart(NULL);
 	}
 #ifdef __sparc__
 	printk("Press L1-A to return to the boot prom\n");
diff -u --recursive --new-file v2.1.29/linux/kernel/printk.c linux/kernel/printk.c
--- v2.1.29/linux/kernel/printk.c	Sun Jan 26 02:07:49 1997
+++ linux/kernel/printk.c	Wed Mar 19 08:31:01 1997
@@ -174,8 +174,8 @@
 	static signed char msg_level = -1;
 	long flags;
 
-	save_flags(flags);
-	cli();
+	__save_flags(flags);
+	__cli();
 	va_start(args, fmt);
 	i = vsprintf(buf + 3, fmt, args); /* hopefully i < sizeof(buf)-4 */
 	buf_end = buf + 3 + i;
@@ -218,7 +218,7 @@
 		if (*p == '\n')
 			msg_level = -1;
 	}
-	restore_flags(flags);
+	__restore_flags(flags);
 	wake_up_interruptible(&log_wait);
 	return i;
 }
diff -u --recursive --new-file v2.1.29/linux/kernel/resource.c linux/kernel/resource.c
--- v2.1.29/linux/kernel/resource.c	Sun Jan 26 02:07:49 1997
+++ linux/kernel/resource.c	Thu Mar 20 18:17:12 1997
@@ -69,7 +69,7 @@
 /*
  * Call this from the device driver to register the ioport region.
  */
-void request_region(unsigned int from, unsigned int num, const char *name)
+void request_region(unsigned long from, unsigned long num, const char *name)
 {
 	resource_entry_t *p;
 	int i;
@@ -95,7 +95,7 @@
 /* 
  * Call this when the device driver is unloaded
  */
-void release_region(unsigned int from, unsigned int num)
+void release_region(unsigned long from, unsigned long num)
 {
 	resource_entry_t *p, *q;
 
@@ -114,7 +114,7 @@
 /*
  * Call this to check the ioport region before probing
  */
-int check_region(unsigned int from, unsigned int num)
+int check_region(unsigned long from, unsigned long num)
 {
 	return (find_gap(&iolist, from, num) == NULL) ? -EBUSY : 0;
 }
@@ -123,10 +123,10 @@
 /*
  * This is for architectures with MMU-managed ports (sparc).
  */
-unsigned int occupy_region(unsigned int base, unsigned int end,
-			unsigned int num, unsigned int align, const char *name)
+unsigned int occupy_region(unsigned long base, unsigned long end,
+			unsigned long num, unsigned int align, const char *name)
 {
-	unsigned int from = 0, till;
+	unsigned long from = 0, till;
 	unsigned long flags;
 	int i;
 	resource_entry_t *p;		/* Scanning ptr */
diff -u --recursive --new-file v2.1.29/linux/kernel/sched.c linux/kernel/sched.c
--- v2.1.29/linux/kernel/sched.c	Tue Mar  4 10:25:26 1997
+++ linux/kernel/sched.c	Mon Mar 24 20:11:50 1997
@@ -38,6 +38,7 @@
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/mmu_context.h>
+#include <asm/spinlock.h>
 
 #include <linux/timex.h>
 
@@ -125,7 +126,7 @@
 	(p->prev_run = init_task.prev_run)->next_run = p;
 	p->next_run = &init_task;
 	init_task.prev_run = p;
-#ifdef __SMP__
+#if 0 /* def __SMP__ */
 	/* this is safe only if called with cli()*/
 	inc_smp_counter(&smp_process_available);
 	if ((0!=p->pid) && smp_threads_ready)
@@ -186,6 +187,18 @@
 }
 
 /*
+ * The scheduler lock is protecting against multiple entry
+ * into the scheduling code, and doesn't need to worry
+ * about interrupts (because interrupts cannot call the
+ * scheduler).
+ *
+ * The run-queue lock locks the parts that actually access
+ * and change the run-queues, and have to be interrupt-safe.
+ */
+spinlock_t scheduler_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t runqueue_lock = SPIN_LOCK_UNLOCKED;
+
+/*
  * Wake up a process. Put it on the run-queue if it's not
  * already there.  The "current" process is always on the
  * run-queue (except when the actual re-schedule is in
@@ -197,12 +210,11 @@
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&runqueue_lock, flags);
 	p->state = TASK_RUNNING;
 	if (!p->next_run)
 		add_to_runqueue(p);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&runqueue_lock, flags);
 }
 
 static void process_timeout(unsigned long __data)
@@ -230,17 +242,6 @@
 {
 	int weight;
 
-#ifdef __SMP__	
-	/* We are not permitted to run a task someone else is running */
-	if (p->processor != NO_PROC_ID)
-		return -1000;
-#ifdef PAST_2_0		
-	/* This process is locked to a processor group */
-	if (p->processor_mask && !(p->processor_mask & (1<<this_cpu))
-		return -1000;
-#endif		
-#endif
-
 	/*
 	 * Realtime process, select the first one on the
 	 * runqueue (taking priorities within processes
@@ -274,24 +275,17 @@
 	return weight;
 }
 
-void allow_interrupts(void)
-{
-#if defined(__SMP__) && defined(__i386__)
-/* UGH UGH UGH. Damn broken IRQ handling test-fix for x86.. */
-	int this_cpu=smp_processor_id();
-	int lock_depth = current_set[this_cpu]->lock_depth;
-	if (lock_depth) {
-		cli();
-		current_set[this_cpu]->lock_depth = 0;
-		active_kernel_processor = NO_PROC_ID;
-		__asm__ __volatile__("lock ; btrl    $0, kernel_flag");
-		sti();
-		/* interrupts should work here */
-		lock_kernel();
-		current_set[this_cpu]->lock_depth += lock_depth-1;
-	}
+#ifdef __SMP__
+
+#define idle_task (task[cpu_number_map[this_cpu]])
+#define can_schedule(p)	((p)->processor == NO_PROC_ID)
+
+#else
+
+#define idle_task (&init_task)
+#define can_schedule(p) (1)
+
 #endif
-}		
 
 /*
  *  'schedule()' is the scheduler function. It's a very simple and nice
@@ -305,31 +299,28 @@
  */
 asmlinkage void schedule(void)
 {
-	int c;
-	struct task_struct * p;
+	static int need_recalculate = 0;
+	int lock_depth;
 	struct task_struct * prev, * next;
-	unsigned long timeout = 0;
-	int this_cpu=smp_processor_id();
-
-/* check alarm, wake up any interruptible tasks that have got a signal */
-	allow_interrupts();
-	lock_kernel();
-	if (intr_count)
-		goto scheduling_in_interrupt;
+	unsigned long timeout;
+	int this_cpu;
 
+	need_resched = 0;
+	this_cpu = smp_processor_id();
+	prev = current;
+	release_kernel_lock(prev, this_cpu, lock_depth);
 	if (bh_active & bh_mask)
 		do_bottom_half();
 
-	run_task_queue(&tq_scheduler);
+	spin_lock(&scheduler_lock);
+	spin_lock_irq(&runqueue_lock);
 
-	need_resched = 0;
-	prev = current;
-	cli();
 	/* move an exhausted RR process to be last.. */
 	if (!prev->counter && prev->policy == SCHED_RR) {
 		prev->counter = prev->priority;
 		move_last_runqueue(prev);
 	}
+	timeout = 0;
 	switch (prev->state) {
 		case TASK_INTERRUPTIBLE:
 			if (prev->signal & ~prev->blocked)
@@ -346,54 +337,44 @@
 			del_from_runqueue(prev);
 		case TASK_RUNNING:
 	}
-	p = init_task.next_run;
-	sti();
+	{
+		struct task_struct * p = init_task.next_run;
+		/*
+		 * This is subtle.
+		 * Note how we can enable interrupts here, even
+		 * though interrupts can add processes to the run-
+		 * queue. This is because any new processes will
+		 * be added to the front of the queue, so "p" above
+		 * is a safe starting point.
+		 * run-queue deletion and re-ordering is protected by
+		 * the scheduler lock
+		 */
+		spin_unlock_irq(&runqueue_lock);
 	
-#ifdef __SMP__
-	/*
-	 *	This is safe as we do not permit re-entry of schedule()
-	 */
-	prev->processor = NO_PROC_ID;
-#define idle_task (task[cpu_number_map[this_cpu]])
-#else
-#define idle_task (&init_task)
-#endif	
-
 /*
  * Note! there may appear new tasks on the run-queue during this, as
  * interrupts are enabled. However, they will be put on front of the
  * list, so our list starting at "p" is essentially fixed.
  */
 /* this is the scheduler proper: */
-	c = -1000;
-	next = idle_task;
-	while (p != &init_task) {
-		int weight = goodness(p, prev, this_cpu);
-		if (weight > c)
-			c = weight, next = p;
-		p = p->next_run;
+		{
+			int c = -1000;
+			next = idle_task;
+			while (p != &init_task) {
+				if (can_schedule(p)) {
+					int weight = goodness(p, prev, this_cpu);
+					if (weight > c)
+						c = weight, next = p;
+				}
+				p = p->next_run;
+			}
+			need_recalculate = !c;
+		}
 	}
 
-	/* if all runnable processes have "counter == 0", re-calculate counters */
-	if (!c) {
-		for_each_task(p)
-			p->counter = (p->counter >> 1) + p->priority;
-	}
-#ifdef __SMP__
-	/*
-	 *	Allocate process to CPU
-	 */
-	 
-	 next->processor = this_cpu;
-	 next->last_processor = this_cpu;
-#endif	 
-#ifdef __SMP_PROF__ 
-	/* mark processor running an idle thread */
-	if (0==next->pid)
-		set_bit(this_cpu,&smp_idle_map);
-	else
-		clear_bit(this_cpu,&smp_idle_map);
-#endif
+	next->processor = this_cpu;
+	next->last_processor = this_cpu;
+
 	if (prev != next) {
 		struct timer_list timer;
 
@@ -407,16 +388,22 @@
 		}
 		get_mmu_context(next);
 		switch_to(prev,next);
+
 		if (timeout)
 			del_timer(&timer);
 	}
-	goto out;
+	spin_unlock(&scheduler_lock);
 
-scheduling_in_interrupt:
-	printk("Aiee: scheduling in interrupt %p\n",
-		__builtin_return_address(0));
-out:
-	unlock_kernel();
+	if (lock_depth) {
+		reaquire_kernel_lock(prev, smp_processor_id(), lock_depth);
+
+		/* Do we need to re-calculate counters? */
+		if (need_recalculate) {
+			struct task_struct *p;
+			for_each_task(p)
+				p->counter = (p->counter >> 1) + p->priority;
+		}
+	}
 }
 
 #ifndef __alpha__
@@ -436,68 +423,69 @@
 
 #endif
 
+spinlock_t waitqueue_lock;
+
 /*
  * wake_up doesn't wake up stopped processes - they have to be awakened
  * with signals or similar.
- *
- * Note that this doesn't need cli-sti pairs: interrupts may not change
- * the wait-queue structures directly, but only call wake_up() to wake
- * a process. The process itself must remove the queue once it has woken.
  */
 void wake_up(struct wait_queue **q)
 {
+	unsigned long flags;
 	struct wait_queue *next;
 	struct wait_queue *head;
 
-	if (!q || !(next = *q))
-		return;
-	head = WAIT_QUEUE_HEAD(q);
-	while (next != head) {
-		struct task_struct *p = next->task;
-		next = next->next;
-		if (p != NULL) {
-			if ((p->state == TASK_UNINTERRUPTIBLE) ||
-			    (p->state == TASK_INTERRUPTIBLE))
-				wake_up_process(p);
+	spin_lock_irqsave(&waitqueue_lock, flags);
+	if (q && (next = *q)) {
+		head = WAIT_QUEUE_HEAD(q);
+		while (next != head) {
+			struct task_struct *p = next->task;
+			next = next->next;
+			if (p != NULL) {
+				if ((p->state == TASK_UNINTERRUPTIBLE) ||
+				    (p->state == TASK_INTERRUPTIBLE))
+					wake_up_process(p);
+			}
+			if (next)
+				continue;
+			printk("wait_queue is bad (eip = %p)\n",
+				__builtin_return_address(0));
+			printk("        q = %p\n",q);
+			printk("       *q = %p\n",*q);
+			break;
 		}
-		if (!next)
-			goto bad;
 	}
-	return;
-bad:
-	printk("wait_queue is bad (eip = %p)\n",
-		__builtin_return_address(0));
-	printk("        q = %p\n",q);
-	printk("       *q = %p\n",*q);
+	spin_unlock_irqrestore(&waitqueue_lock, flags);
 }
 
 void wake_up_interruptible(struct wait_queue **q)
 {
+	unsigned long flags;
 	struct wait_queue *next;
 	struct wait_queue *head;
 
-	if (!q || !(next = *q))
-		return;
-	head = WAIT_QUEUE_HEAD(q);
-	while (next != head) {
-		struct task_struct *p = next->task;
-		next = next->next;
-		if (p != NULL) {
-			if (p->state == TASK_INTERRUPTIBLE)
-				wake_up_process(p);
+	spin_lock_irqsave(&waitqueue_lock, flags);
+	if (q && (next = *q)) {
+		head = WAIT_QUEUE_HEAD(q);
+		while (next != head) {
+			struct task_struct *p = next->task;
+			next = next->next;
+			if (p != NULL) {
+				if (p->state == TASK_INTERRUPTIBLE)
+					wake_up_process(p);
+			}
+			if (next)
+				continue;
+			printk("wait_queue is bad (eip = %p)\n",
+				__builtin_return_address(0));
+			printk("        q = %p\n",q);
+			printk("       *q = %p\n",*q);
+			break;
 		}
-		if (!next)
-			goto bad;
 	}
-	return;
-bad:
-	printk("wait_queue is bad (eip = %p)\n",
-		__builtin_return_address(0));
-	printk("        q = %p\n",q);
-	printk("       *q = %p\n",*q);
+	spin_unlock_irqrestore(&waitqueue_lock, flags);
 }
 
-
 /*
  * Semaphores are implemented using a two-way counter:
  * The "count" variable is decremented for each process
@@ -635,14 +623,14 @@
 	if (current == task[0])
 		panic("task[0] trying to sleep");
 	current->state = state;
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&waitqueue_lock, flags);
 	__add_wait_queue(p, &wait);
+	spin_unlock(&waitqueue_lock);
 	sti();
 	schedule();
-	cli();
+	spin_lock_irq(&waitqueue_lock);
 	__remove_wait_queue(p, &wait);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&waitqueue_lock, flags);
 }
 
 void interruptible_sleep_on(struct wait_queue **p)
@@ -718,8 +706,10 @@
 	} else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
 		int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
 		insert_timer(timer, tv4.vec, i);
-	} else if ((idx + 1UL) == 0UL) {
-		/* can happen if you add a timer with expires == jiffies */
+	} else if (expires < timer_jiffies) {
+		/* can happen if you add a timer with expires == jiffies,
+		 * or you set a timer to go off in the past
+		 */
 		insert_timer(timer, tv1.vec, tv1.index);
 	} else if (idx < 0xffffffffUL) {
 		int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
@@ -730,11 +720,13 @@
 	}
 }
 
+static spinlock_t timerlist_lock = SPIN_LOCK_UNLOCKED;
+
 void add_timer(struct timer_list *timer)
 {
 	unsigned long flags;
-	save_flags(flags);
-	cli();
+
+	spin_lock_irqsave(&timerlist_lock, flags);
 #if SLOW_BUT_DEBUGGING_TIMERS
         if (timer->next || timer->prev) {
                 printk("add_timer() called with non-zero list from %p\n",
@@ -746,7 +738,7 @@
 #if SLOW_BUT_DEBUGGING_TIMERS
 out:
 #endif
-	restore_flags(flags);
+	spin_unlock_irqrestore(&timerlist_lock, flags);
 }
 
 static inline int detach_timer(struct timer_list *timer)
@@ -770,11 +762,11 @@
 {
 	int ret;
 	unsigned long flags;
-	save_flags(flags);
-	cli();
+
+	spin_lock_irqsave(&timerlist_lock, flags);
 	ret = detach_timer(timer);
 	timer->next = timer->prev = 0;
-	restore_flags(flags);
+	spin_unlock_irqrestore(&timerlist_lock, flags);
 	return ret;
 }
 
@@ -798,7 +790,7 @@
 
 static inline void run_timer_list(void)
 {
-	cli();
+	spin_lock_irq(&timerlist_lock);
 	while ((long)(jiffies - timer_jiffies) >= 0) {
 		struct timer_list *timer;
 		if (!tv1.index) {
@@ -812,14 +804,14 @@
 			unsigned long data = timer->data;
 			detach_timer(timer);
 			timer->next = timer->prev = NULL;
-			sti();
+			spin_unlock_irq(&timerlist_lock);
 			fn(data);
-			cli();
+			spin_lock_irq(&timerlist_lock);
 		}
 		++timer_jiffies; 
 		tv1.index = (tv1.index + 1) & TVR_MASK;
 	}
-	sti();
+	spin_unlock_irq(&timerlist_lock);
 }
 
 
@@ -841,6 +833,8 @@
 	}
 }
 
+spinlock_t tqueue_lock;
+
 void tqueue_bh(void)
 {
 	run_task_queue(&tq_timer);
@@ -1484,12 +1478,13 @@
 
 	p->policy = policy;
 	p->rt_priority = lp.sched_priority;
-	cli();
+	spin_lock(&scheduler_lock);
+	spin_lock_irq(&runqueue_lock);
 	if (p->next_run)
 		move_last_runqueue(p);
-	sti();
-	schedule();
-
+	spin_unlock_irq(&runqueue_lock);
+	spin_unlock(&scheduler_lock);
+	need_resched = 1;
 	return 0;
 }
 
@@ -1558,11 +1553,12 @@
 
 asmlinkage int sys_sched_yield(void)
 {
-	lock_kernel();
-	cli();
+	spin_unlock(&scheduler_lock);
+	spin_lock_irq(&runqueue_lock);
 	move_last_runqueue(current);
-	sti();
-	unlock_kernel();
+	spin_unlock_irq(&runqueue_lock);
+	spin_lock(&scheduler_lock);
+	need_resched = 1;
 	return 0;
 }
 
@@ -1570,7 +1566,6 @@
 {
 	int ret = -EINVAL;
 
-	lock_kernel();
 	switch (policy) {
 	case SCHED_FIFO:
 	case SCHED_RR:
@@ -1580,7 +1575,6 @@
 		ret = 0;
 		break;
 	}
-	unlock_kernel();
 	return ret;
 }
 
@@ -1588,7 +1582,6 @@
 {
 	int ret = -EINVAL;
 
-	lock_kernel();
 	switch (policy) {
 	case SCHED_FIFO:
 	case SCHED_RR:
@@ -1597,23 +1590,18 @@
 	case SCHED_OTHER:
 		ret = 0;
 	}
-	unlock_kernel();
 	return ret;
 }
 
 asmlinkage int sys_sched_rr_get_interval(pid_t pid, struct timespec *interval)
 {
 	struct timespec t;
-	int ret;
 
-	lock_kernel();
 	t.tv_sec = 0;
-	t.tv_nsec = 0;            /* <-- Linus, please fill correct value in here */
-	ret = -ENOSYS; goto out;  /* and then delete this line. Thanks!           */
-	ret = copy_to_user(interval, &t, sizeof(struct timespec)) ? -EFAULT : 0;
-out:
-	unlock_kernel();
-	return ret;
+	t.tv_nsec = 150000;
+	if (copy_to_user(interval, &t, sizeof(struct timespec)))
+		return -EFAULT;
+	return 0;
 }
 
 /*
diff -u --recursive --new-file v2.1.29/linux/kernel/signal.c linux/kernel/signal.c
--- v2.1.29/linux/kernel/signal.c	Thu Feb 27 10:57:31 1997
+++ linux/kernel/signal.c	Sun Mar 16 17:10:07 1997
@@ -170,40 +170,24 @@
 	struct sigaction * oldaction)
 {
 	struct sigaction new_sa, *p;
-	int ret = -EINVAL;
 
-	lock_kernel();
 	if (signum<1 || signum>32)
-		goto out;
+		return -EINVAL;
 	p = signum - 1 + current->sig->action;
 	if (action) {
-		ret = verify_area(VERIFY_READ, action, sizeof(*action));
-		if (ret)
-			goto out;
-		ret = -EINVAL;
-		if (signum==SIGKILL || signum==SIGSTOP)
-			goto out;
-		ret = -EFAULT;
 		if (copy_from_user(&new_sa, action, sizeof(struct sigaction)))
-			goto out;
-		if (new_sa.sa_handler != SIG_DFL && new_sa.sa_handler != SIG_IGN) {
-			ret = verify_area(VERIFY_READ, new_sa.sa_handler, 1);
-			if (ret)
-				goto out;
-		}
+			return -EFAULT;
+		if (signum==SIGKILL || signum==SIGSTOP)
+			return -EINVAL;
 	}
-	ret = -EFAULT;
 	if (oldaction) {
 		if (copy_to_user(oldaction, p, sizeof(struct sigaction)))
-			goto out;	
+			return -EFAULT;
 	}
 	if (action) {
 		*p = new_sa;
 		check_pending(signum);
 	}
-	ret = 0;
-out:
-	unlock_kernel();
-	return ret;
+	return 0;
 }
 #endif
diff -u --recursive --new-file v2.1.29/linux/kernel/softirq.c linux/kernel/softirq.c
--- v2.1.29/linux/kernel/softirq.c	Thu Feb 27 10:57:31 1997
+++ linux/kernel/softirq.c	Wed Mar 26 09:30:49 1997
@@ -23,6 +23,8 @@
 #include <asm/irq.h>
 #include <asm/bitops.h>
 #include <asm/atomic.h>
+#include <asm/hardirq.h>
+#include <asm/softirq.h>
 
 atomic_t intr_count = 0;
 
@@ -48,36 +50,28 @@
 static inline void run_bottom_halves(void)
 {
 	unsigned long active;
-	unsigned long mask, left;
 	void (**bh)(void);
 
-	cli();
-	active = bh_active & bh_mask;
-	bh_active &= ~active;
-	sti();
+	active = get_active_bhs();
+	clear_active_bhs(active);
 	bh = bh_base;
-	for (mask = 1, left = ~0 ; left & active ; bh++,mask += mask,left += left) {
-		if (mask & active) {
+	do {
+		if (active & 1)
 			(*bh)();
-		}
-	}
+		bh++;
+		active >>= 1;
+	} while (active);
 }
 
-/*
- * We really shouldn't need to get the kernel lock here,
- * but we do it the easy way for now (the scheduler gets
- * upset if somebody messes with intr_count without having
- * the kernel lock).
- *
- * Get rid of the kernel lock here at the same time we
- * make interrupt handling sane. 
- */
 asmlinkage void do_bottom_half(void)
 {
-	lock_kernel();
-	atomic_inc(&intr_count);
-	if (intr_count == 1)
-		run_bottom_halves();
-	atomic_dec(&intr_count);
-	unlock_kernel();
+	int cpu = smp_processor_id();
+
+	if (hardirq_trylock(cpu)) {
+		if (softirq_trylock()) {
+			run_bottom_halves();
+			softirq_endlock();
+		}
+		hardirq_endlock(cpu);
+	}
 }
diff -u --recursive --new-file v2.1.29/linux/kernel/sys.c linux/kernel/sys.c
--- v2.1.29/linux/kernel/sys.c	Tue Feb  4 06:44:25 1997
+++ linux/kernel/sys.c	Thu Mar 20 17:27:37 1997
@@ -4,7 +4,6 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -23,10 +22,8 @@
 #include <linux/tty.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
-#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
-#include <linux/apm_bios.h>
-#endif
 #include <linux/notifier.h>
+#include <linux/reboot.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -37,12 +34,26 @@
 
 int C_A_D = 1;
 
+
 /*
- *	List of functions to call at shutdown. This is used to stop any
- *	idling DMA operations and the like. 
+ *	Notifier list for kernel code which wants to be called
+ *	at shutdown. This is used to stop any idling DMA operations
+ *	and the like. 
  */
- 
-struct notifier_block *boot_notifier_list=NULL;
+
+struct notifier_block *reboot_notifier_list = NULL;
+
+int register_reboot_notifier(struct notifier_block * nb)
+{
+	return notifier_chain_register(&reboot_notifier_list, nb);
+}
+
+int unregister_reboot_notifier(struct notifier_block * nb)
+{
+	return notifier_chain_unregister(&reboot_notifier_list, nb);
+}
+
+
 
 extern void adjust_clock(void);
 
@@ -189,10 +200,6 @@
 
 #endif
 
-extern void hard_reset_now(void);
-#ifdef __sparc__
-extern void halt_now(void);
-#endif
 extern asmlinkage int sys_kill(int, int);
 
 /*
@@ -202,42 +209,69 @@
  * You can also set the meaning of the ctrl-alt-del-key here.
  *
  * reboot doesn't sync: do that yourself before calling this.
+ *
  */
-asmlinkage int sys_reboot(int magic, int magic_too, int flag)
+asmlinkage int sys_reboot(int magic1, int magic2, int cmd, void * arg)
 {
+	char buffer[256];
+
+	/* We only trust the superuser with rebooting the system. */
 	if (!suser())
 		return -EPERM;
-	if (magic != 0xfee1dead || 
-	    (magic_too != 672274793 && magic_too != 85072278))
+
+	/* For safety, we require "magic" arguments. */
+	if (magic1 != LINUX_REBOOT_MAGIC1 ||
+	    (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A))
 		return -EINVAL;
 
+	lock_kernel();
+	switch (cmd) {
+	case LINUX_REBOOT_CMD_RESTART:
+		notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL);
+		printk(KERN_EMERG "Restarting system.\n");
+		machine_restart(NULL);
+		break;
 
-	if (flag == 0x01234567)
-	{
-		/* SMP: We need to lock during the shutdown still */
-		lock_kernel();
-		notifier_call_chain(&boot_notifier_list, SYS_DOWN, NULL);
-		hard_reset_now();
-	}
-	else if (flag == 0x89ABCDEF)
+	case LINUX_REBOOT_CMD_CAD_ON:
 		C_A_D = 1;
-	else if (!flag)
+		break;
+
+	case LINUX_REBOOT_CMD_CAD_OFF:
 		C_A_D = 0;
-	else if (flag == 0xCDEF0123) {
-		/* SMP: We need to lock during the shutdown still */
-		lock_kernel();
-		printk(KERN_EMERG "System halted\n");
-#ifdef __sparc__
-		halt_now();
-#endif	
-		sys_kill(-1, SIGKILL);
-#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
-		apm_set_power_state(APM_STATE_OFF);
-#endif
-		notifier_call_chain(&boot_notifier_list, SYS_HALT, NULL);
+		break;
+
+	case LINUX_REBOOT_CMD_HALT:
+		notifier_call_chain(&reboot_notifier_list, SYS_HALT, NULL);
+		printk(KERN_EMERG "System halted.\n");
+		machine_halt();
 		do_exit(0);
-	} else
+		break;
+
+	case LINUX_REBOOT_CMD_POWER_OFF:
+		notifier_call_chain(&reboot_notifier_list, SYS_POWER_OFF, NULL);
+		printk(KERN_EMERG "Power down.\n");
+		machine_power_off();
+		do_exit(0);
+		break;
+
+	case LINUX_REBOOT_CMD_RESTART2:
+		if (strncpy_from_user(&buffer[0], (char *)arg, sizeof(buffer) - 1) < 0) {
+			unlock_kernel();
+			return -EFAULT;
+		}
+		buffer[sizeof(buffer) - 1] = '\0';
+
+		notifier_call_chain(&reboot_notifier_list, SYS_RESTART, buffer);
+		printk(KERN_EMERG "Restarting system with command '%s'.\n", buffer);
+		machine_restart(buffer);
+		break;
+
+	default:
+		unlock_kernel();
 		return -EINVAL;
+		break;
+	};
+	unlock_kernel();
 	return 0;
 }
 
@@ -248,12 +282,10 @@
  */
 void ctrl_alt_del(void)
 {
-	if (C_A_D)
-	{
-		notifier_call_chain(&boot_notifier_list, SYS_DOWN, NULL);
-		hard_reset_now();
-	}
-	else
+	if (C_A_D) {
+		notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL);
+		machine_restart(NULL);
+	} else
 		kill_proc(1, SIGINT, 1);
 }
 	
diff -u --recursive --new-file v2.1.29/linux/kernel/time.c linux/kernel/time.c
--- v2.1.29/linux/kernel/time.c	Fri Feb  7 05:54:55 1997
+++ linux/kernel/time.c	Wed Mar 19 16:57:45 1997
@@ -91,11 +91,6 @@
 		return -EPERM;
 	if (get_user(value, tptr))
 		return -EFAULT;
-	/*
-	 *	SMP: We need to lock out everything for the time update
-	 *	the new cli/sti semantics will let us drop this lock soon.
-	 */
-	lock_kernel();
 	cli();
 	xtime.tv_sec = value;
 	xtime.tv_usec = 0;
@@ -103,7 +98,6 @@
 	time_maxerror = MAXPHASE;
 	time_esterror = MAXPHASE;
 	sti();
-	unlock_kernel();
 	return 0;
 }
 
@@ -111,23 +105,17 @@
 
 asmlinkage int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
 {
-	int err = -EFAULT;
-
-	lock_kernel();
 	if (tv) {
 		struct timeval ktv;
 		do_gettimeofday(&ktv);
 		if (copy_to_user(tv, &ktv, sizeof(ktv)))
-			goto out;
+			return -EFAULT;
 	}
 	if (tz) {
 		if (copy_to_user(tz, &sys_tz, sizeof(sys_tz)))
-			goto out;
+			return -EFAULT;
 	}
-	err = 0;
-out:
-	unlock_kernel();
-	return err;
+	return 0;
 }
 
 /*
diff -u --recursive --new-file v2.1.29/linux/mm/filemap.c linux/mm/filemap.c
--- v2.1.29/linux/mm/filemap.c	Sun Jan 26 02:07:49 1997
+++ linux/mm/filemap.c	Thu Mar 20 17:27:37 1997
@@ -753,7 +753,7 @@
 	filp->f_reada = 1;
 	if (page_cache)
 		free_page(page_cache);
-	if (!IS_RDONLY(inode)) {
+	if (DO_UPDATE_ATIME(inode)) {
 		inode->i_atime = CURRENT_TIME;
 		inode->i_dirt = 1;
 	}
@@ -1188,7 +1188,7 @@
 		return -EACCES;
 	if (!inode->i_op || !inode->i_op->readpage)
 		return -ENOEXEC;
-	if (!IS_RDONLY(inode)) {
+	if (DO_UPDATE_ATIME(inode)) {
 		inode->i_atime = CURRENT_TIME;
 		inode->i_dirt = 1;
 	}
diff -u --recursive --new-file v2.1.29/linux/mm/kmalloc.c linux/mm/kmalloc.c
--- v2.1.29/linux/mm/kmalloc.c	Fri Sep 27 01:45:16 1996
+++ linux/mm/kmalloc.c	Thu Mar 13 16:01:37 1997
@@ -266,6 +266,7 @@
 	priority &= GFP_LEVEL_MASK;
 
 /* Sanity check... */
+#if 0	/* no longer valid */
 	if (intr_count && priority != GFP_ATOMIC) {
 		static int count = 0;
 		if (++count < 5) {
@@ -274,7 +275,7 @@
 			priority = GFP_ATOMIC;
 		}
 	}
-
+#endif
 	save_flags(flags);
 	cli();
 	page = *pg;
diff -u --recursive --new-file v2.1.29/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.1.29/linux/mm/page_alloc.c	Sun Jan 26 02:07:49 1997
+++ linux/mm/page_alloc.c	Thu Mar 13 16:01:52 1997
@@ -201,6 +201,7 @@
 
 	if (order >= NR_MEM_LISTS)
 		return 0;
+#if 0
 	if (intr_count && priority != GFP_ATOMIC) {
 		static int count = 0;
 		if (++count < 5) {
@@ -209,6 +210,7 @@
 			priority = GFP_ATOMIC;
 		}
 	}
+#endif
 	reserved_pages = 5;
 	if (priority != GFP_NFS)
 		reserved_pages = min_free_pages;
diff -u --recursive --new-file v2.1.29/linux/mm/slab.c linux/mm/slab.c
--- v2.1.29/linux/mm/slab.c	Sun Jan 26 02:07:49 1997
+++ linux/mm/slab.c	Thu Mar 13 16:02:46 1997
@@ -478,7 +478,7 @@
 		printk(KERN_ERR "%sNULL ptr\n", func_nm);
 		return NULL;
 	}
-	if (intr_count) {
+	if (0 && intr_count) {
 		printk(KERN_ERR "%sCalled during int - %s\n", func_nm, name);
 		return NULL;
 	}
@@ -743,7 +743,7 @@
 		goto err_end;
 	}
 
-	if (intr_count) {
+	if (0 && intr_count) {
 		printk(KERN_ERR "kmem_dest: Called during int - %s\n", cachep->c_name);
 err_end:
 		return 1;
@@ -809,7 +809,7 @@
 		goto end;
 	}
 
-	if (intr_count) {
+	if (0 && intr_count) {
 		printk(KERN_ERR "kmem_shrink: Called during int - %s\n", cachep->c_name);
 		goto end;
 	}
@@ -951,7 +951,7 @@
 	 * in kmem_cache_alloc().  If a caller is slightly mis-behaving,
 	 * will eventually be caught here (where it matters)
 	 */
-	if (intr_count && (flags & SLAB_LEVEL_MASK) != SLAB_ATOMIC) {
+	if (0 && intr_count && (flags & SLAB_LEVEL_MASK) != SLAB_ATOMIC) {
 		static int count = 0;
 		if (count < 8) {
 			printk(KERN_ERR "kmem_grow: Called nonatomically from "
diff -u --recursive --new-file v2.1.29/linux/net/802/transit/timertr.h linux/net/802/transit/timertr.h
--- v2.1.29/linux/net/802/transit/timertr.h	Thu Dec 19 00:32:56 1996
+++ linux/net/802/transit/timertr.h	Thu Mar 20 17:27:37 1997
@@ -1,5 +1,5 @@
 
-/* this file was generated on Wed Dec 18 23:01:37 GMT 1996  */
+/* this file was generated on Mon Mar 10 22:45:36 GMT 1997  */
 
 
 /* size of transition table is 898 bytes */
diff -u --recursive --new-file v2.1.29/linux/net/Config.in linux/net/Config.in
--- v2.1.29/linux/net/Config.in	Thu Feb 27 10:57:32 1997
+++ linux/net/Config.in	Thu Mar 20 18:17:12 1997
@@ -8,12 +8,17 @@
   bool 'Routing messages' CONFIG_RTNETLINK
 fi
 bool 'Network firewalls' CONFIG_FIREWALL
+if [ "$CONFIG_FIREWALL" = "y" ]; then
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    bool 'Socket Security API Support (EXPERIMENTAL)' CONFIG_NET_SECURITY
+  fi
+fi
 bool 'Network aliasing'  CONFIG_NET_ALIAS
 bool 'TCP/IP networking' CONFIG_INET
 if [ "$CONFIG_INET" = "y" ]; then
   source net/ipv4/Config.in
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    tristate 'The IPv6 protocol' CONFIG_IPV6
+    tristate 'The IPv6 protocol (EXPERIMENTAL)' CONFIG_IPV6
   fi
 fi
 
@@ -31,7 +36,7 @@
 fi
 tristate 'Amateur Radio AX.25 Level 2' CONFIG_AX25
 if [ "$CONFIG_AX25" != "n" ]; then
-# bool 'AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE
+  bool 'AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE
 # bool 'AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER
   dep_tristate 'Amateur Radio NET/ROM' CONFIG_NETROM $CONFIG_AX25
   dep_tristate 'Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25
@@ -40,7 +45,10 @@
   tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
   tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
   bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE
-  bool '802.2 LLC (VERY EXPERIMENTAL)' CONFIG_LLC
+  bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
+#  if [ "$CONFIG_LLC" = "y" ]; then
+#   bool 'Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
+#  fi
   tristate 'WAN router' CONFIG_WAN_ROUTER
 fi
 endmenu
diff -u --recursive --new-file v2.1.29/linux/net/README linux/net/README
--- v2.1.29/linux/net/README	Sun Feb  2 05:18:48 1997
+++ linux/net/README	Thu Mar 20 18:17:12 1997
@@ -9,7 +9,8 @@
 ax25			jsn@cs.nott.ac.uk
 core			alan@lxorguk.ukuu.org.uk
 ethernet		alan@lxorguk.ukuu.org.uk
-ipv4			alan@lxorguk.ukuu.org.uk
+ipv4			davem@caip.rutgers.edu,Eric.Schenk@dna.lth.se
+ipv6			davem@caip.rutgers.edu,Eric.Schenk@dna.lth.se
 ipx			alan@lxorguk.ukuu.org.uk,greg@caldera.com
 lapb			jsn@cs.nott.ac.uk
 netrom			jsn@cs.nott.ac.uk
diff -u --recursive --new-file v2.1.29/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.1.29/linux/net/appletalk/ddp.c	Thu Feb 27 10:57:32 1997
+++ linux/net/appletalk/ddp.c	Thu Mar 20 18:17:12 1997
@@ -410,14 +410,11 @@
 
 static struct atalk_iface *atalk_find_anynet(int node, struct device *dev)
 {
-	struct atalk_iface *iface;
-	for(iface=atalk_iface_list;iface!=NULL;iface=iface->next)
-	{
-		if ( iface->dev != dev || ( iface->status & ATIF_PROBE ))
-			continue;
-		if ( node == ATADDR_BCAST || iface->address.s_node == node )
-			return iface;
-	}
+	struct atalk_iface *iface=dev->atalk_ptr;
+	if (iface==NULL || ( iface->status & ATIF_PROBE ))
+		return NULL;
+	if ( node == ATADDR_BCAST || iface->address.s_node == node || node == ATADDR_ANYNODE)
+		return iface;
 	return NULL;
 }
 
@@ -1184,7 +1181,8 @@
 #define SIOCDELIPDDPRT SIOCDEVPRIVATE+1
 #define SIOCFINDIPDDPRT SIOCDEVPRIVATE+2
 
-struct ipddp_route {
+struct ipddp_route 
+{
 	struct device *dev;		/* Carrier device */
 	__u32 ip;			/* IP address */
 	struct at_addr at;		/* Gateway appletalk address */
@@ -1215,6 +1213,7 @@
 	skb_pull(skb,4);
 	
 	((struct net_device_stats *) dev->priv)->tx_packets++;
+	((struct net_device_stats *) dev->priv)->tx_bytes+=skb->len;
 
 	/* printk("ipddp_xmit called with headroom %d\n",skb_headroom(skb)); */
 
@@ -1226,7 +1225,7 @@
 
 struct net_device_stats *ipddp_get_stats(struct device *dev)
 {
-	return (struct enet_statistics *) dev->priv;
+	return (struct net_device_stats *) dev->priv;
 }
 
 int ipddp_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
@@ -1443,16 +1442,12 @@
 		return(0);
 	}
 
-#ifdef CONFIG_FIREWALL
-
-	if(call_in_firewall(AF_APPLETALK, skb->dev, ddp, NULL)!=FW_ACCEPT)
+	if(call_in_firewall(AF_APPLETALK, skb->dev, ddp, NULL,&skb)!=FW_ACCEPT)
 	{
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
 
-#endif
-
 	/* Check the packet is aimed at us */
 
 	if(ddp->deh_dnet == 0)	/* Net 0 is 'this network' */
@@ -1474,17 +1469,16 @@
 			return(0);
 		}
 
-#ifdef CONFIG_FIREWALL
 		/*
 		 *	Check firewall allows this routing
 		 */
 
-		if(call_fw_firewall(AF_APPLETALK, skb->dev, ddp, NULL)!=FW_ACCEPT)
+		if(call_fw_firewall(AF_APPLETALK, skb->dev, ddp, NULL, &skb)!=FW_ACCEPT)
 		{
 			kfree_skb(skb, FREE_READ);
 			return(0);
 		}
-#endif
+
 		ta.s_net=ddp->deh_dnet;
 		ta.s_node=ddp->deh_dnode;
 
@@ -1546,9 +1540,10 @@
 	 *	Check if IP-over-DDP
 	 */
 
-	if(skb->data[12]==22) {
-		struct enet_statistics *estats = 
-			(struct enet_statistics *) dev_ipddp.priv;
+	if(skb->data[12]==22) 
+	{
+		struct net_device_stats *estats = 
+			(struct net_device_stats *) dev_ipddp.priv;
 		skb->protocol=htons(ETH_P_IP);
 		skb_pull(skb,13);
 		skb->dev=&dev_ipddp;
@@ -1558,6 +1553,7 @@
 	 *		ntohs(skb->h.iph->tot_len),skb->len);
 	 */
 		estats->rx_packets++;
+		estats->rx_bytes+=skb->len+13;
 		netif_rx(skb);
 		return 0;
 	}
@@ -1762,15 +1758,11 @@
 	else
 		ddp->deh_sum=atalk_checksum(ddp, len+sizeof(*ddp));
 
-#ifdef CONFIG_FIREWALL
-
-	if(call_out_firewall(AF_APPLETALK, skb->dev, ddp, NULL)!=FW_ACCEPT)
+	if(call_out_firewall(AF_APPLETALK, skb->dev, ddp, NULL, &skb)!=FW_ACCEPT)
 	{
 		kfree_skb(skb, FREE_WRITE);
 		return -EPERM;
 	}
-
-#endif
 
 	/*
 	 *	Loopback broadcast packets to non gateway targets (ie routes
diff -u --recursive --new-file v2.1.29/linux/net/ax25/Makefile linux/net/ax25/Makefile
--- v2.1.29/linux/net/ax25/Makefile	Thu Feb 27 10:57:32 1997
+++ linux/net/ax25/Makefile	Thu Mar 20 18:17:13 1997
@@ -16,6 +16,10 @@
 
 OX_OBJS  += af_ax25.o
 
+ifeq ($(CONFIG_AX25_DAMA_SLAVE),y)
+O_OBJS += ax25_ds_in.o ax25_ds_subr.o ax25_ds_timer.o
+endif
+
 include $(TOPDIR)/Rules.make
 
 tar:
diff -u --recursive --new-file v2.1.29/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.1.29/linux/net/ax25/af_ax25.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ax25/af_ax25.c	Thu Mar 20 18:17:13 1997
@@ -93,6 +93,7 @@
  *			Frederic(F1OAT)		Support for pseudo-digipeating.
  *			Jonathan(G4KLX)		Support for packet forwarding.
  *	AX.25 036	Jonathan(G4KLX)		Major restructuring.
+ *			Joerg(DL1BKE)		Fixed DAMA Slave.
  */
 
 #include <linux/config.h>
@@ -461,8 +462,12 @@
 		case AX25_KILL:
 			ax25_clear_queues(ax25);
 			ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-
 			ax25->state = AX25_STATE_0;
+#ifdef CONFIG_AX25_DAMA_SLAVE
+			if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
+				ax25_dama_off(ax25);
+#endif
+
 			if (ax25->sk != NULL) {
 				ax25->sk->state     = TCP_CLOSE;
 				ax25->sk->err       = ENETRESET;
@@ -472,10 +477,6 @@
 				ax25->sk->dead  = 1;
 			}
 
-#ifdef CONFIG_AX25_DAMA_SLAVE
-			if (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
-				ax25_dama_off(ax25);
-#endif
 			ax25_set_timer(ax25);
 	  		break;
 
@@ -634,14 +635,13 @@
 	if (level != SOL_AX25)
 		return -ENOPROTOOPT;
 
-	if(optlen<sizeof(int))
+	if (optlen < sizeof(int))
 		return -EINVAL;
-		
-	if(get_user(opt, (int *)optval))
+
+	if (get_user(opt, (int *)optval))
 		return -EFAULT;
 
-	switch (optname) 
-	{
+	switch (optname) {
 		case AX25_WINDOW:
 			if (sk->protinfo.ax25->modulus == AX25_MODULUS) {
 				if (opt < 1 || opt > 7)
@@ -716,13 +716,12 @@
 {
 	struct sock *sk = sock->sk;
 	int val = 0;
-	int err;
 	int len;
 
 	if (level != SOL_AX25)
 		return -ENOPROTOOPT;
 
-	if(get_user(len,optlen))
+	if (get_user(len, optlen))
 		return -EFAULT;
 
 	switch (optname) {
@@ -774,11 +773,14 @@
 			return -ENOPROTOOPT;
 	}
 
-	len=min(len,sizeof(int));
-	if(put_user(len, optlen))
+	len = min(len, sizeof(int));
+
+	if (put_user(len, optlen))
 		return -EFAULT;
-	if(copy_to_user(optval, &val, len))
+
+	if (copy_to_user(optval, &val, len))
 		return -EFAULT;
+
 	return 0;
 }
 
@@ -976,13 +978,14 @@
 				ax25_clear_queues(sk->protinfo.ax25);
 				sk->protinfo.ax25->n2count = 0;
 				switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
-					case AX25_PROTO_STD:
+					case AX25_PROTO_STD_SIMPLEX:
+					case AX25_PROTO_STD_DUPLEX:
 						ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
 						sk->protinfo.ax25->t3timer = 0;
 						break;
 #ifdef AX25_CONFIG_DAMA_SLAVE
 					case AX25_PROTO_DAMA_SLAVE:
-						sk->protinfo.ax25->t3timer = sk->protinfo.ax25->t3;	/* DAMA slave timeout */
+						sk->protinfo.ax25->t3timer = 0;
 						break;
 #endif
 				}
@@ -1168,12 +1171,17 @@
 	sk->state          = TCP_SYN_SENT;
 
 	switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
-		case AX25_PROTO_STD:
+		case AX25_PROTO_STD_SIMPLEX:
+		case AX25_PROTO_STD_DUPLEX:
 			ax25_std_establish_data_link(sk->protinfo.ax25);
 			break;
+
 #ifdef CONFIG_AX25_DAMA_SLAVE
 		case AX25_PROTO_DAMA_SLAVE:
-			ax25_ds_establish_data_link(sk->protinfo.ax25);
+			if (sk->protinfo.ax25->ax25_dev->dama.slave)
+				ax25_ds_establish_data_link(sk->protinfo.ax25);
+			else
+				ax25_std_establish_data_link(sk->protinfo.ax25);
 			break;
 #endif
 	}
diff -u --recursive --new-file v2.1.29/linux/net/ax25/ax25_dev.c linux/net/ax25/ax25_dev.c
--- v2.1.29/linux/net/ax25/ax25_dev.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ax25/ax25_dev.c	Thu Mar 20 18:17:13 1997
@@ -83,6 +83,8 @@
 
 	ax25_unregister_sysctl();
 
+	memset(ax25_dev, 0x00, sizeof(*ax25_dev));
+
 	ax25_dev->dev     = dev;
 	ax25_dev->forward = NULL;
 
@@ -98,7 +100,12 @@
 	ax25_dev->values[AX25_VALUES_IDLE]	= AX25_DEF_IDLE;
 	ax25_dev->values[AX25_VALUES_N2]        = AX25_DEF_N2;
 	ax25_dev->values[AX25_VALUES_PACLEN]	= AX25_DEF_PACLEN;
+#ifdef CONFIG_AX25_DAMA_SLAVE
+	ax25_dev->values[AX25_VALUES_PROTOCOL]	= AX25_PROTO_DAMA_SLAVE;
+#else
 	ax25_dev->values[AX25_VALUES_PROTOCOL]  = AX25_DEF_PROTOCOL;
+#endif
+	ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT;
 
 	save_flags(flags); cli();
 	ax25_dev->next = ax25_dev_list;
@@ -119,6 +126,10 @@
 	ax25_unregister_sysctl();
 
 	save_flags(flags); cli();
+
+#ifdef CONFIG_AX25_DAMA_SLAVE
+	ax25_ds_del_timer(ax25_dev);
+#endif
 
 	/*
 	 *	Remove any packet forwarding that points to this device.
diff -u --recursive --new-file v2.1.29/linux/net/ax25/ax25_ds_in.c linux/net/ax25/ax25_ds_in.c
--- v2.1.29/linux/net/ax25/ax25_ds_in.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ax25/ax25_ds_in.c	Thu Mar 20 18:17:13 1997
@@ -0,0 +1,562 @@
+/*
+ *	AX.25 release 036
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	Most of this code is based on the SDL diagrams published in the 7th
+ *	ARRL Computer Networking Conference papers. The diagrams have mistakes
+ *	in them, but are mostly correct. Before you modify the code could you
+ *	read the SDL diagrams as the code is not obvious and probably very
+ *	easy to break;
+ *
+ *	History
+ *	AX.25 036	Jonathan(G4KLX)	Cloned from ax25_in.c
+ *			Joerg(DL1BKE)	Fixed it.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25_DAMA_SLAVE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/ip.h>			/* For ip_rcv */
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+/*
+ *	State machine for state 1, Awaiting Connection State.
+ *	The handling of the timer(s) is in file ax25_timer.c.
+ *	Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
+{
+	switch (frametype) {
+		case AX25_SABM:
+			ax25->modulus = AX25_MODULUS;
+			ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			break;
+
+		case AX25_SABME:
+			ax25->modulus = AX25_EMODULUS;
+			ax25->window  = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			break;
+
+		case AX25_DISC:
+			ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
+			break;
+
+		case AX25_UA:
+			ax25_calculate_rtt(ax25);
+			ax25->t1timer = 0;
+			ax25->t3timer = ax25->t3;
+			ax25->idletimer = ax25->idle;
+			ax25->vs      = 0;
+			ax25->va      = 0;
+			ax25->vr      = 0;
+			ax25->state   = AX25_STATE_3;
+			ax25->n2count = 0;
+			if (ax25->sk != NULL) {
+				ax25->sk->state = TCP_ESTABLISHED;
+				/* For WAIT_SABM connections we will produce an accept ready socket here */
+				if (!ax25->sk->dead)
+					ax25->sk->state_change(ax25->sk);
+			}
+			ax25_dama_on(ax25);
+
+			/* according to DK4EG´s spec we are required to
+			 * send a RR RESPONSE FINAL NR=0. Please mail
+			 * <jr@lykos.oche.de> if this causes problems
+			 * with the TheNetNode DAMA Master implementation.
+			 */ 
+
+			ax25_std_enquiry_response(ax25);
+			break;
+
+		case AX25_DM:
+			if (pf) {
+				if (ax25->modulus == AX25_MODULUS) {
+					ax25_clear_queues(ax25);
+					ax25->state = AX25_STATE_0;
+					if (ax25->sk != NULL) {
+						ax25->sk->state     = TCP_CLOSE;
+						ax25->sk->err       = ECONNREFUSED;
+						ax25->sk->shutdown |= SEND_SHUTDOWN;
+						if (!ax25->sk->dead)
+							ax25->sk->state_change(ax25->sk);
+						ax25->sk->dead      = 1;
+					}
+				} else {
+					ax25->modulus = AX25_MODULUS;
+					ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+				}
+			}
+			break;
+
+		default:
+			if (pf)
+				ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
+			break;
+	}
+
+	return 0;
+}
+
+/*
+ *	State machine for state 2, Awaiting Release State.
+ *	The handling of the timer(s) is in file ax25_timer.c
+ *	Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_ds_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
+{
+	switch (frametype) {
+		case AX25_SABM:
+		case AX25_SABME:
+			ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+			ax25->dama_slave = 0;
+			ax25_dama_off(ax25);
+			break;
+
+		case AX25_DISC:
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			ax25->state = AX25_STATE_0;
+			ax25_dama_off(ax25);
+
+			if (ax25->sk != NULL) {
+				ax25->sk->state     = TCP_CLOSE;
+				ax25->sk->err       = 0;
+				ax25->sk->shutdown |= SEND_SHUTDOWN;
+				if (!ax25->sk->dead)
+					ax25->sk->state_change(ax25->sk);
+				ax25->sk->dead      = 1;
+			}
+			break;
+
+		case AX25_DM:
+		case AX25_UA:
+			if (pf) {
+				ax25->state = AX25_STATE_0;
+				ax25_dama_off(ax25);
+
+				if (ax25->sk != NULL) {
+					ax25->sk->state     = TCP_CLOSE;
+					ax25->sk->err       = 0;
+					ax25->sk->shutdown |= SEND_SHUTDOWN;
+					if (!ax25->sk->dead)
+						ax25->sk->state_change(ax25->sk);
+					ax25->sk->dead      = 1;
+				}
+			}
+			break;
+
+		case AX25_I:
+		case AX25_REJ:
+		case AX25_RNR:
+		case AX25_RR:
+			if (pf) {
+				ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+				ax25->dama_slave = 0;
+				ax25_dama_off(ax25);
+			}
+			break;
+
+		default:
+			break;
+	}
+
+	return 0;
+}
+
+/*
+ *	State machine for state 3, Connected State.
+ *	The handling of the timer(s) is in file ax25_timer.c
+ *	Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
+{
+	int queued = 0;
+
+	switch (frametype) {
+		case AX25_SABM:
+		case AX25_SABME:
+			if (frametype == AX25_SABM) {
+				ax25->modulus = AX25_MODULUS;
+				ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+			} else {
+				ax25->modulus = AX25_EMODULUS;
+				ax25->window  = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+			}
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			ax25->condition = 0x00;
+			ax25->t1timer   = 0;
+			ax25->t3timer   = ax25->t3;
+			ax25->idletimer = ax25->idle;
+			ax25->vs        = 0;
+			ax25->va        = 0;
+			ax25->vr        = 0;
+			ax25_requeue_frames(ax25);
+			ax25_dama_on(ax25);
+			break;
+
+		case AX25_DISC:
+			ax25_clear_queues(ax25);
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			ax25->t3timer = 0;
+			ax25->state   = AX25_STATE_0;
+			ax25_dama_off(ax25);
+
+			if (ax25->sk != NULL) {
+				ax25->sk->state     = TCP_CLOSE;
+				ax25->sk->err       = 0;
+				ax25->sk->shutdown |= SEND_SHUTDOWN;
+				if (!ax25->sk->dead)
+					ax25->sk->state_change(ax25->sk);
+				ax25->sk->dead      = 1;
+			}
+			break;
+
+		case AX25_DM:
+			ax25_clear_queues(ax25);
+			ax25->t3timer = 0;
+			ax25->state   = AX25_STATE_0;
+			ax25_dama_off(ax25);
+
+			if (ax25->sk != NULL) {
+				ax25->sk->state     = TCP_CLOSE;
+				ax25->sk->err       = ECONNRESET;
+				ax25->sk->shutdown |= SEND_SHUTDOWN;
+				if (!ax25->sk->dead)
+					ax25->sk->state_change(ax25->sk);
+				ax25->sk->dead      = 1;
+			}
+			break;
+
+		case AX25_RR:
+		case AX25_RNR:
+			if (frametype == AX25_RR)
+				ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+			else
+				ax25->condition |= AX25_COND_PEER_RX_BUSY;
+			if (ax25_validate_nr(ax25, nr)) {
+				ax25_check_iframes_acked(ax25, nr);
+				if (type == AX25_COMMAND && pf)
+					ax25_ds_enquiry_response(ax25);
+			} else {
+				ax25_ds_nr_error_recovery(ax25);
+				ax25->state = AX25_STATE_1;
+			}
+			break;
+
+		case AX25_REJ:
+			ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+			if (ax25_validate_nr(ax25, nr)) {
+				ax25_frames_acked(ax25, nr);
+				ax25_calculate_rtt(ax25);
+				ax25->n2count = 0;
+				ax25->t1timer = 0;
+				ax25->t3timer = ax25->t3;
+				ax25_requeue_frames(ax25);
+				if (type == AX25_COMMAND && pf)
+					ax25_ds_enquiry_response(ax25);
+			} else {
+				ax25_ds_nr_error_recovery(ax25);
+				ax25->state = AX25_STATE_1;
+			}
+			break;
+
+		case AX25_I:
+			if (!ax25_validate_nr(ax25, nr)) {
+				ax25_ds_nr_error_recovery(ax25);
+				ax25->state = AX25_STATE_1;
+				break;
+			}
+			if (ax25->condition & AX25_COND_PEER_RX_BUSY) {
+				ax25_frames_acked(ax25, nr);
+				ax25->n2count = 0;
+			} else {
+				ax25_check_iframes_acked(ax25, nr);
+			}
+			if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+				if (pf) ax25_ds_enquiry_response(ax25);
+				break;
+			}
+			if (ns == ax25->vr) {
+				ax25->vr = (ax25->vr + 1) % ax25->modulus;
+				queued = ax25_rx_iframe(ax25, skb);
+				if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+					ax25->vr = ns;	/* ax25->vr - 1 */
+					if (pf) ax25_ds_enquiry_response(ax25);
+					break;
+				}
+				ax25->condition &= ~AX25_COND_REJECT;
+				if (pf) {
+					ax25_ds_enquiry_response(ax25);
+				} else {
+					if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
+						ax25->t2timer = ax25->t2;
+						ax25->condition |= AX25_COND_ACK_PENDING;
+					}
+				}
+			} else {
+				if (ax25->condition & AX25_COND_REJECT) {
+					if (pf) ax25_ds_enquiry_response(ax25);
+				} else {
+					ax25->condition |= AX25_COND_REJECT;
+					ax25_ds_enquiry_response(ax25);
+					ax25->condition &= ~AX25_COND_ACK_PENDING;
+				}
+			}
+			break;
+
+		case AX25_FRMR:
+		case AX25_ILLEGAL:
+			ax25_ds_establish_data_link(ax25);
+			ax25->state = AX25_STATE_1;
+			break;
+
+		default:
+			break;
+	}
+
+	return queued;
+}
+
+/*
+ *	State machine for state 4, Timer Recovery State.
+ *	The handling of the timer(s) is in file ax25_timer.c
+ *	Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_ds_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
+{
+	int queued = 0;
+
+	switch (frametype) {
+		case AX25_SABM:
+		case AX25_SABME:
+			if (frametype == AX25_SABM) {
+				ax25->modulus = AX25_MODULUS;
+				ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+			} else {
+				ax25->modulus = AX25_EMODULUS;
+				ax25->window  = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+			}
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			ax25->condition = 0x00;
+			ax25->t1timer   = 0;
+			ax25->t3timer   = ax25->t3;
+			ax25->idletimer = ax25->idle;
+			ax25->vs        = 0;
+			ax25->va        = 0;
+			ax25->vr        = 0;
+			ax25->state     = AX25_STATE_3;
+			ax25->n2count   = 0;
+			ax25_requeue_frames(ax25);
+			ax25_dama_on(ax25);
+			break;
+
+		case AX25_DISC:
+			ax25_clear_queues(ax25);
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			ax25->t3timer = 0;
+			ax25->state   = AX25_STATE_0;
+			ax25_dama_off(ax25);
+
+			if (ax25->sk != NULL) {
+				ax25->sk->state     = TCP_CLOSE;
+				ax25->sk->err       = 0;
+				ax25->sk->shutdown |= SEND_SHUTDOWN;
+				if (!ax25->sk->dead)
+					ax25->sk->state_change(ax25->sk);
+				ax25->sk->dead      = 1;
+			}
+			break;
+
+		case AX25_DM:
+			ax25_clear_queues(ax25);
+			ax25->t3timer = 0;
+			ax25->state   = AX25_STATE_0;
+			ax25_dama_off(ax25);
+
+			if (ax25->sk != NULL) {
+				ax25->sk->state     = TCP_CLOSE;
+				ax25->sk->err       = ECONNRESET;
+				ax25->sk->shutdown |= SEND_SHUTDOWN;
+				if (!ax25->sk->dead)
+					ax25->sk->state_change(ax25->sk);
+				ax25->sk->dead      = 1;
+			}
+			break;
+
+		case AX25_RR:
+		case AX25_RNR:
+			if (frametype == AX25_RR)
+				ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+			else
+				ax25->condition |= AX25_COND_PEER_RX_BUSY;
+			if (type == AX25_RESPONSE && pf) {
+				ax25->t1timer = 0;
+				if (ax25_validate_nr(ax25, nr)) {
+					ax25_frames_acked(ax25, nr);
+					ax25->n2count = 0;
+					if (ax25->vs == ax25->va) {
+						ax25->t3timer = ax25->t3;
+						ax25->state   = AX25_STATE_3;
+					} else {
+						ax25_requeue_frames(ax25);
+					}
+				} else {
+					ax25_ds_nr_error_recovery(ax25);
+					ax25->state = AX25_STATE_1;
+				}
+				break;
+			}
+			if (ax25_validate_nr(ax25, nr)) {
+				ax25_frames_acked(ax25, nr);
+				ax25->n2count = 0;
+				if (type == AX25_COMMAND && pf)
+					ax25_ds_enquiry_response(ax25);
+			} else {
+				ax25_ds_nr_error_recovery(ax25);
+				ax25->state = AX25_STATE_1;
+			}
+			break;
+
+		case AX25_REJ:
+			ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+			if (pf) {
+				ax25->t1timer = 0;
+				if (ax25_validate_nr(ax25, nr)) {
+					ax25_frames_acked(ax25, nr);
+					ax25->n2count = 0;
+					if (ax25->vs == ax25->va) {
+						ax25->t3timer = ax25->t3;
+						ax25->state   = AX25_STATE_3;
+					} else {
+						ax25_requeue_frames(ax25);
+					}
+					if (type == AX25_COMMAND && pf)
+						ax25_ds_enquiry_response(ax25);
+				} else {
+					ax25_ds_nr_error_recovery(ax25);
+					ax25->state = AX25_STATE_1;
+				}
+				break;
+			}
+			if (ax25_validate_nr(ax25, nr)) {
+				ax25_frames_acked(ax25, nr);
+				ax25->n2count = 0;
+				ax25_requeue_frames(ax25);
+				if (type == AX25_COMMAND && pf)
+					ax25_ds_enquiry_response(ax25);
+			} else {
+				ax25_ds_nr_error_recovery(ax25);
+				ax25->state = AX25_STATE_1;
+			}
+			break;
+
+		case AX25_I:
+			if (!ax25_validate_nr(ax25, nr)) {
+				ax25_ds_nr_error_recovery(ax25);
+				ax25->state = AX25_STATE_1;
+				break;
+			}
+			ax25_frames_acked(ax25, nr);
+			ax25->n2count = 0;
+			if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+				if (pf) ax25_ds_enquiry_response(ax25);
+				break;
+			}
+			if (ns == ax25->vr) {
+				ax25->vr = (ax25->vr + 1) % ax25->modulus;
+				queued = ax25_rx_iframe(ax25, skb);
+				if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+					ax25->vr = ns;	/* ax25->vr - 1 */
+					if (pf) ax25_ds_enquiry_response(ax25);
+					break;
+				}
+				ax25->condition &= ~AX25_COND_REJECT;
+				if (pf) {
+					ax25_ds_enquiry_response(ax25);
+				} else {
+					if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
+						ax25->t2timer = ax25->t2;
+						ax25->condition |= AX25_COND_ACK_PENDING;
+					}
+				}
+			} else {
+				if (ax25->condition & AX25_COND_REJECT) {
+					if (pf) ax25_ds_enquiry_response(ax25);
+				} else {
+					ax25->condition |= AX25_COND_REJECT;
+					ax25_ds_enquiry_response(ax25);
+					ax25->condition &= ~AX25_COND_ACK_PENDING;
+				}
+			}
+			break;
+
+		case AX25_FRMR:
+		case AX25_ILLEGAL:
+			ax25_ds_establish_data_link(ax25);
+			ax25->state = AX25_STATE_1;
+			break;
+
+		default:
+			break;
+	}
+
+	return queued;
+}
+
+/*
+ *	Higher level upcall for a LAPB frame
+ */
+int ax25_ds_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type)
+{
+	int queued = 0, frametype, ns, nr, pf;
+
+	frametype = ax25_decode(ax25, skb, &ns, &nr, &pf);
+
+	switch (ax25->state) {
+		case AX25_STATE_1:
+			queued = ax25_ds_state1_machine(ax25, skb, frametype, pf, type);
+			break;
+		case AX25_STATE_2:
+			queued = ax25_ds_state2_machine(ax25, skb, frametype, pf, type);
+			break;
+		case AX25_STATE_3:
+			queued = ax25_ds_state3_machine(ax25, skb, frametype, ns, nr, pf, type);
+			break;
+		case AX25_STATE_4:
+			queued = ax25_ds_state4_machine(ax25, skb, frametype, ns, nr, pf, type);
+			break;
+	}
+
+	return queued;
+}
+
+#endif
diff -u --recursive --new-file v2.1.29/linux/net/ax25/ax25_ds_subr.c linux/net/ax25/ax25_ds_subr.c
--- v2.1.29/linux/net/ax25/ax25_ds_subr.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ax25/ax25_ds_subr.c	Thu Mar 20 18:17:13 1997
@@ -0,0 +1,225 @@
+/*
+ *	AX.25 release 036
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	Most of this code is based on the SDL diagrams published in the 7th
+ *	ARRL Computer Networking Conference papers. The diagrams have mistakes
+ *	in them, but are mostly correct. Before you modify the code could you
+ *	read the SDL diagrams as the code is not obvious and probably very
+ *	easy to break;
+ *
+ *	History
+ *	AX.25 036	Jonathan(G4KLX)	Cloned from ax25_out.c and ax25_subr.c.
+ *			Joerg(DL1BKE)	Changed ax25_ds_enquiry_response(),
+ *					fixed ax25_dama_on() and ax25_dama_off().
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25_DAMA_SLAVE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+void ax25_ds_nr_error_recovery(ax25_cb *ax25)
+{
+	ax25_ds_establish_data_link(ax25);
+}
+
+/*
+ *	dl1bke 960114: transmit I frames on DAMA poll
+ */
+void ax25_ds_enquiry_response(ax25_cb *ax25)
+{
+	ax25_cb *ax25o;
+
+	/* Please note that neither DK4EG´s nor DG2FEF´s
+	 * DAMA spec mention the following behaviour as seen
+	 * with TheFirmware:
+	 *
+	 * 	DB0ACH->DL1BKE <RR C P R0> [DAMA]
+	 *	DL1BKE->DB0ACH <I NR=0 NS=0>
+	 *	DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5>
+	 *	DL1BKE->DB0ACH <RR R F R0>
+	 *
+	 * The Flexnet DAMA Master implementation apparently
+	 * insists on the "proper" AX.25 behaviour:
+	 *
+	 * 	DB0ACH->DL1BKE <RR C P R0> [DAMA]
+	 *	DL1BKE->DB0ACH <RR R F R0>
+	 *	DL1BKE->DB0ACH <I NR=0 NS=0>
+	 *	DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5>
+	 *
+	 * Flexnet refuses to send us *any* I frame if we send
+	 * a REJ in case AX25_COND_REJECT is set. It is superfluous in 
+	 * this mode anyway (a RR or RNR invokes the retransmission).
+	 * Is this a Flexnet bug?
+	 */
+
+	ax25_std_enquiry_response(ax25);
+
+	if (!(ax25->condition & AX25_COND_PEER_RX_BUSY)) {
+		ax25_requeue_frames(ax25);
+		ax25_kick(ax25);
+	}
+
+	if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || skb_peek(&ax25->ack_queue) != NULL)
+		ax25_ds_t1_timeout(ax25);
+	else
+		ax25->n2count = 0;
+
+	ax25->t3timer = ax25->t3;
+	ax25_ds_set_timer(ax25->ax25_dev);
+
+	for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) {
+		if (ax25o == ax25)
+			continue;
+
+		if (ax25o->ax25_dev != ax25->ax25_dev)
+			continue;
+
+		if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) {
+			ax25_ds_t1_timeout(ax25o);
+			continue;
+		}
+
+		if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && 
+		    (ax25o->state == AX25_STATE_3 || 
+		    (ax25o->state == AX25_STATE_4 && ax25o->t1timer == 0))) {
+			ax25_requeue_frames(ax25o);
+			ax25_kick(ax25o);
+		}
+
+		if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL)
+			ax25_ds_t1_timeout(ax25o);
+
+		ax25o->t3timer = ax25o->t3;
+	}
+}
+
+void ax25_ds_establish_data_link(ax25_cb *ax25)
+{
+	ax25->condition = 0x00;
+	ax25->n2count   = 0;
+
+	ax25->t3timer = ax25->t3;
+	ax25->t2timer = 0;
+	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+}
+
+/*
+ *	:::FIXME:::
+ *	This is a kludge. Not all drivers recognize kiss commands. 
+ *	We need a driver level  request to switch duplex mode, that does 
+ *	either SCC changing, PI config or KISS as required. Currently
+ *	this request isn't reliable.
+ *
+ */
+static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char param)
+{
+	struct sk_buff *skb;
+	unsigned char *p;
+
+	if (ax25_dev->dev == NULL)
+		return;
+
+	if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL)
+		return;
+
+	p = skb_put(skb, 2);
+
+	*p++ = cmd;
+	*p++ = param;
+
+	skb->arp      = 1;
+	skb->dev      = ax25_dev->dev;
+	skb->priority = SOPRI_NORMAL;
+	skb->protocol = htons(ETH_P_AX25);
+
+	dev_queue_xmit(skb);
+}
+
+/*
+ *	A nasty problem arises if we count the number of DAMA connections
+ *	wrong, especially when connections on the device already existed
+ *	and our network node (or the sysop) decides to turn on DAMA Master
+ *	mode. We thus flag the 'real' slave connections with 
+ *	ax25->dama_slave=1 and look on every disconnect if still slave
+ *	connections exist.
+ */
+
+static int ax25_check_dama_slave(ax25_dev *ax25_dev)
+{
+	ax25_cb *ax25;
+
+	for (ax25 = ax25_list; ax25 != NULL ; ax25 = ax25->next)
+		if (ax25->ax25_dev == ax25_dev && ax25->dama_slave && ax25->state > AX25_STATE_1)
+			return 1;
+
+	return 0;
+}
+
+void ax25_dev_dama_on(ax25_dev *ax25_dev)
+{
+	if (ax25_dev == NULL)
+		return;
+
+	if (ax25_dev->dama.slave == 0)
+		ax25_kiss_cmd(ax25_dev, 5, 1);
+
+	ax25_dev->dama.slave = 1;
+	ax25_ds_set_timer(ax25_dev);
+}
+
+void ax25_dev_dama_off(ax25_dev *ax25_dev)
+{
+	if (ax25_dev == NULL)
+		return;
+
+	if (ax25_dev->dama.slave && !ax25_check_dama_slave(ax25_dev))
+	{
+		ax25_kiss_cmd(ax25_dev, 5, 0);
+		ax25_dev->dama.slave = 0;
+		ax25_ds_del_timer(ax25_dev);
+	}
+}
+
+void ax25_dama_on(ax25_cb *ax25)
+{
+	ax25_dev_dama_on(ax25->ax25_dev);
+	ax25->dama_slave = 1;
+}
+
+void ax25_dama_off(ax25_cb *ax25)
+{
+	ax25_dev_dama_off(ax25->ax25_dev);
+	ax25->dama_slave = 0;
+}
+
+#endif
diff -u --recursive --new-file v2.1.29/linux/net/ax25/ax25_ds_timer.c linux/net/ax25/ax25_ds_timer.c
--- v2.1.29/linux/net/ax25/ax25_ds_timer.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ax25/ax25_ds_timer.c	Thu Mar 20 18:17:13 1997
@@ -0,0 +1,313 @@
+/*
+ *	AX.25 release 036
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	History
+ *	AX.25 036	Jonathan(G4KLX)	Cloned from ax25_timer.c.
+ *			Joerg(DL1BKE)	Added DAMA Slave Timeout timer
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25_DAMA_SLAVE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+static void ax25_ds_timeout(unsigned long);
+
+/*
+ *	Add DAMA slave timeout timer to timer list.
+ *	Unlike the connection based timers the timeout function gets 
+ *	triggered every second. Please note that NET_AX25_DAMA_SLAVE_TIMEOUT
+ *	(aka /proc/sys/net/ax25/{dev}/dama_slave_timeout) is still in
+ *	1/10th of a second.
+ */
+
+static void ax25_ds_add_timer(ax25_dev *ax25_dev)
+{
+	struct timer_list *t = &ax25_dev->dama.slave_timer;
+	t->data		= (unsigned long) ax25_dev;
+	t->function	= &ax25_ds_timeout;
+	t->expires	= jiffies + HZ;
+	add_timer(t);
+}
+
+void ax25_ds_del_timer(ax25_dev *ax25_dev)
+{
+	if (ax25_dev) del_timer(&ax25_dev->dama.slave_timer);
+}
+
+void ax25_ds_set_timer(ax25_dev *ax25_dev)
+{
+	if (ax25_dev == NULL)		/* paranoia */
+		return;
+
+	del_timer(&ax25_dev->dama.slave_timer);
+	ax25_dev->dama.slave_timeout = ax25_dev->values[AX25_VALUES_DS_TIMEOUT] / 10;
+	ax25_ds_add_timer(ax25_dev);
+}
+
+/*
+ *	DAMA Slave Timeout
+ *	Silently discard all (slave) connections in case our master forgot us...
+ */
+
+static void ax25_ds_timeout(unsigned long arg)
+{
+	ax25_dev *ax25_dev = (struct ax25_dev *) arg;
+	ax25_cb *ax25;
+	
+	if (ax25_dev == NULL || !ax25_dev->dama.slave)
+		return;			/* Yikes! */
+	
+	if (!ax25_dev->dama.slave_timeout || --ax25_dev->dama.slave_timeout)
+	{
+		ax25_ds_set_timer(ax25_dev);
+		return;
+	}
+	
+	for (ax25=ax25_list; ax25 != NULL; ax25 = ax25->next)
+	{
+		if (ax25->ax25_dev != ax25_dev || !ax25->dama_slave)
+			continue;
+
+		ax25_link_failed(&ax25->dest_addr, ax25_dev->dev);
+		ax25_clear_queues(ax25);
+		ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+		ax25->state = AX25_STATE_0;
+
+		if (ax25->sk != NULL)
+		{
+			SOCK_DEBUG(ax25->sk, "AX.25 DAMA Slave Timeout\n");
+			ax25->sk->state     = TCP_CLOSE;
+			ax25->sk->err       = ETIMEDOUT;
+			ax25->sk->shutdown |= SEND_SHUTDOWN;
+			if (!ax25->sk->dead)
+				ax25->sk->state_change(ax25->sk);
+			ax25->sk->dead      = 1;
+		}
+		
+		ax25_set_timer(ax25);	/* notify socket... */
+	}
+	
+	ax25_dev_dama_off(ax25_dev);
+}
+
+
+/*
+ *	AX.25 TIMER 
+ *
+ *	This routine is called every 100ms. Decrement timer by this
+ *	amount - if expired then process the event.
+ */
+void ax25_ds_timer(ax25_cb *ax25)
+{
+	switch (ax25->state) {
+		case AX25_STATE_0:
+			/* Magic here: If we listen() and a new link dies before it
+			   is accepted() it isn't 'dead' so doesn't get removed. */
+			if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) {
+				del_timer(&ax25->timer);
+				ax25_destroy_socket(ax25);
+				return;
+			}
+			break;
+
+		case AX25_STATE_3:
+		case AX25_STATE_4:
+			/*
+			 * Check the state of the receive buffer.
+			 */
+			if (ax25->sk != NULL) {
+				if (ax25->sk->rmem_alloc < (ax25->sk->rcvbuf / 2) && (ax25->condition & AX25_COND_OWN_RX_BUSY)) {
+					ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
+					break;
+				}
+			}
+			break;
+
+		default:
+			break;
+	}
+	
+	/* dl1bke 960114: T3 works much like the IDLE timeout, but
+	 *                gets reloaded with every frame for this
+	 *		  connection.
+	 */
+
+	if (ax25->t3timer > 0 && --ax25->t3timer == 0) {
+		ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+		ax25_clear_queues(ax25);
+		ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+
+		ax25->state = AX25_STATE_0;
+		ax25_dama_off(ax25);
+
+		if (ax25->sk != NULL) {
+			SOCK_DEBUG(ax25->sk, "AX.25 T3 Timeout\n");
+			ax25->sk->state     = TCP_CLOSE;
+			ax25->sk->err       = ETIMEDOUT;
+			ax25->sk->shutdown |= SEND_SHUTDOWN;
+			if (!ax25->sk->dead)
+				ax25->sk->state_change(ax25->sk);
+			ax25->sk->dead      = 1;
+		}
+
+		ax25_set_timer(ax25);
+
+		return;
+	}
+
+	/* dl1bke 960228: close the connection when IDLE expires.
+	 *		  unlike T3 this timer gets reloaded only on
+	 *		  I frames.
+	 */
+
+	if (ax25->idletimer > 0 && --ax25->idletimer == 0) {
+		ax25_clear_queues(ax25);
+
+		ax25->n2count = 0;
+		ax25->t3timer = ax25->t3;
+
+		/* state 1 or 2 should not happen, but... */
+
+		if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2)
+			ax25->state = AX25_STATE_0;
+		else
+			ax25->state = AX25_STATE_2;
+
+		ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+
+		if (ax25->sk != NULL) {
+			ax25->sk->state     = TCP_CLOSE;
+			ax25->sk->err       = 0;
+			ax25->sk->shutdown |= SEND_SHUTDOWN;
+			if (!ax25->sk->dead)
+				ax25->sk->state_change(ax25->sk);
+			ax25->sk->dead      = 1;
+			ax25->sk->destroy   = 1;
+		}
+	}
+
+	ax25_set_timer(ax25);
+}
+
+/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC
+ *                within the poll of any connected channel. Remember 
+ *                that we are not allowed to send anything unless we
+ *                get polled by the Master.
+ *
+ *                Thus we'll have to do parts of our T1 handling in
+ *                ax25_enquiry_response().
+ */
+void ax25_ds_t1_timeout(ax25_cb *ax25)
+{	
+	switch (ax25->state) {
+		case AX25_STATE_1: 
+			if (ax25->n2count == ax25->n2) {
+				if (ax25->modulus == AX25_MODULUS) {
+					ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+					ax25_clear_queues(ax25);
+					ax25->state = AX25_STATE_0;
+					if (ax25->sk != NULL) {
+						ax25->sk->state     = TCP_CLOSE;
+						ax25->sk->err       = ETIMEDOUT;
+						ax25->sk->shutdown |= SEND_SHUTDOWN;
+						if (!ax25->sk->dead)
+							ax25->sk->state_change(ax25->sk);
+						ax25->sk->dead      = 1;
+					}
+				} else {
+					ax25->modulus = AX25_MODULUS;
+					ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+					ax25->n2count = 0;
+					ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND);
+				}
+			} else {
+				ax25->n2count++;
+				if (ax25->modulus == AX25_MODULUS)
+					ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND);
+				else
+					ax25_send_control(ax25, AX25_SABME, AX25_POLLOFF, AX25_COMMAND);
+			}
+			break;
+
+		case AX25_STATE_2:
+			if (ax25->n2count == ax25->n2) {
+				ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+				ax25_clear_queues(ax25);
+				ax25->state = AX25_STATE_0;
+				ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+
+				if (ax25->sk != NULL) {
+					ax25->sk->state     = TCP_CLOSE;
+					ax25->sk->err       = ETIMEDOUT;
+					ax25->sk->shutdown |= SEND_SHUTDOWN;
+					if (!ax25->sk->dead)
+						ax25->sk->state_change(ax25->sk);
+					ax25->sk->dead      = 1;
+				}
+			} else {
+				ax25->n2count++;
+			}
+			break;
+
+		case AX25_STATE_3: 
+			ax25->n2count = 1;
+			ax25->state   = AX25_STATE_4;
+			break;
+
+		case AX25_STATE_4:
+			if (ax25->n2count == ax25->n2) {
+				ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+				ax25_clear_queues(ax25);
+				ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
+				ax25->state = AX25_STATE_0;
+				if (ax25->sk != NULL) {
+					SOCK_DEBUG(ax25->sk, "AX.25 link Failure\n");
+					ax25->sk->state     = TCP_CLOSE;
+					ax25->sk->err       = ETIMEDOUT;
+					ax25->sk->shutdown |= SEND_SHUTDOWN;
+					if (!ax25->sk->dead)
+						ax25->sk->state_change(ax25->sk);
+					ax25->sk->dead      = 1;
+				}
+			} else {
+				ax25->n2count++;
+			}
+			break;
+	}
+
+	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+
+	ax25_set_timer(ax25);
+}
+
+#endif
diff -u --recursive --new-file v2.1.29/linux/net/ax25/ax25_in.c linux/net/ax25/ax25_in.c
--- v2.1.29/linux/net/ax25/ax25_in.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ax25/ax25_in.c	Thu Mar 20 18:17:13 1997
@@ -37,6 +37,7 @@
  *					Modularisation changes.
  *	AX.25 035	Hans(PE1AYX)	Fixed interface to IP layer.
  *	AX.25 036	Jonathan(G4KLX)	Move DAMA code into own file.
+ *			Joerg(DL1BKE)	Fixed DAMA Slave.
  */
 
 #include <linux/config.h>
@@ -158,6 +159,16 @@
 
 #ifdef CONFIG_INET
 	if (pid == AX25_P_IP) {
+		/* working around a TCP bug to keep additional listeners 
+		 * happy. TCP re-uses the buffer and destroys the original
+		 * content.
+		 */
+		struct sk_buff *skbn = skb_copy(skb, GFP_ATOMIC);
+		if (skbn != NULL) {
+			kfree_skb(skb, FREE_READ);
+			skb = skbn;
+		}
+
 		skb_pull(skb, 1);	/* Remove PID */
 		skb->h.raw    = skb->data;
 		skb->nh.raw   = skb->data;
@@ -190,7 +201,7 @@
 /*
  *	Higher level upcall for a LAPB frame
  */
-static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type)
+static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama)
 {
 	int queued = 0;
 
@@ -200,12 +211,17 @@
 	del_timer(&ax25->timer);
 
 	switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
-		case AX25_PROTO_STD:
+		case AX25_PROTO_STD_SIMPLEX:
+		case AX25_PROTO_STD_DUPLEX:
 			queued = ax25_std_frame_in(ax25, skb, type);
 			break;
+
 #ifdef CONFIG_AX25_DAMA_SLAVE
 		case AX25_PROTO_DAMA_SLAVE:
-			queued = ax25_ds_frame_in(ax25, skb, type);
+			if (dama || ax25->ax25_dev->dama.slave)
+				queued = ax25_ds_frame_in(ax25, skb, type);
+			else
+				queued = ax25_std_frame_in(ax25, skb, type);
 			break;
 #endif
 	}
@@ -240,12 +256,10 @@
 		return 0;
 	}
 
-#ifdef CONFIG_FIREWALL
-	if (call_in_firewall(PF_AX25, skb->dev, skb->h.raw, NULL) != FW_ACCEPT) {
+	if (call_in_firewall(PF_AX25, skb->dev, skb->h.raw, NULL,&skb) != FW_ACCEPT) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
-#endif
 
 	/*
 	 *	Parse the address header.
@@ -356,7 +370,7 @@
 		 *	free it immediately. This routine itself wakes the user context layers so we
 		 *	do no further work
 		 */
-		if (ax25_process_rx_frame(ax25, skb, type) == 0)
+		if (ax25_process_rx_frame(ax25, skb, type, dama) == 0)
 			kfree_skb(skb, FREE_READ);
 
 		return 0;
@@ -450,7 +464,7 @@
 	ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE);
 
 #ifdef CONFIG_AX25_DAMA_SLAVE
-	if (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
+	if (dama && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
 		ax25_dama_on(ax25);
 #endif
 
diff -u --recursive --new-file v2.1.29/linux/net/ax25/ax25_ip.c linux/net/ax25/ax25_ip.c
--- v2.1.29/linux/net/ax25/ax25_ip.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ax25/ax25_ip.c	Thu Mar 20 18:17:13 1997
@@ -123,7 +123,7 @@
 		mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev);
 		if (mode == 'V' || (mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) {
 			/*
-			 *	We clone the buffer and release the original thereby
+			 *	We copy the buffer and release the original thereby
 			 *	keeping it straight
 			 *
 			 *	Note: we report 1 back so the caller will
@@ -131,8 +131,13 @@
 			 *	We don't want that to happen. (It won't be upset
 			 *	as we have pulled the frame from the queue by
 			 *	freeing it).
+			 *
+			 *	NB: TCP modifies buffers that are still
+			 *	on a device queue, thus we use skb_copy()
+			 *      instead of using skb_clone() unless this
+			 *	gets fixed.
 			 */
-			if ((ourskb = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+			if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) {
 				dev_kfree_skb(skb, FREE_WRITE);
 				return 1;
 			}
diff -u --recursive --new-file v2.1.29/linux/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- v2.1.29/linux/net/ax25/ax25_out.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ax25/ax25_out.c	Thu Mar 20 18:17:13 1997
@@ -94,12 +94,17 @@
 	}
 
 	switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
-		case AX25_PROTO_STD:
+		case AX25_PROTO_STD_SIMPLEX:
+		case AX25_PROTO_STD_DUPLEX:
 			ax25_std_establish_data_link(ax25);
 			break;
-#ifdef AX25_CONFIG_DAMA_SLAVE
+
+#ifdef CONFIG_AX25_DAMA_SLAVE
 		case AX25_PROTO_DAMA_SLAVE:
-			ax25_ds_establish_data_link(ax25);
+			if (ax25_dev->dama.slave)
+				ax25_ds_establish_data_link(ax25);
+			else
+				ax25_std_establish_data_link(ax25);
 			break;
 #endif
 	}
@@ -192,7 +197,8 @@
 		skb_queue_tail(&ax25->write_queue, skb);	  /* Throw it on the queue */
 	}
 
-	if (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD) {
+	if (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_SIMPLEX ||
+	    ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_DUPLEX) {
 		if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4)
 			ax25_kick(ax25);
 	}
@@ -274,9 +280,11 @@
 			 * in DAMA mode.
 			 */
 			switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
-				case AX25_PROTO_STD:
+				case AX25_PROTO_STD_SIMPLEX:
+				case AX25_PROTO_STD_DUPLEX:
 					ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF);
 					break;
+
 #ifdef CONFIG_AX25_DAMA_SLAVE
 				case AX25_PROTO_DAMA_SLAVE:
 					ax25_send_iframe(ax25, skbn, AX25_POLLOFF);
@@ -343,12 +351,10 @@
 {
 	unsigned char *ptr;
 
-#ifdef CONFIG_FIREWALL
-	if (call_out_firewall(PF_AX25, skb->dev, skb->data, NULL) != FW_ACCEPT) {
+	if (call_out_firewall(PF_AX25, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) {
 		dev_kfree_skb(skb, FREE_WRITE);
 		return;
 	}
-#endif
 
 	skb->protocol = htons(ETH_P_AX25);
 	skb->dev      = ax25_fwd_dev(skb->dev);
diff -u --recursive --new-file v2.1.29/linux/net/ax25/ax25_timer.c linux/net/ax25/ax25_timer.c
--- v2.1.29/linux/net/ax25/ax25_timer.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ax25/ax25_timer.c	Thu Mar 20 18:17:13 1997
@@ -22,6 +22,8 @@
  *	AX.25 033	Jonathan(G4KLX)	Modularisation functions.
  *	AX.25 035	Frederic(F1OAT)	Support for pseudo-digipeating.
  *	AX.25 036	Jonathan(G4KLX)	Split Standard and DAMA code into seperate files.
+ *			Joerg(DL1BKE)	Fixed DAMA Slave. We are *required* to start with
+ *					standard AX.25 mode.
  */
 
 #include <linux/config.h>
@@ -62,7 +64,7 @@
 
 	ax25->timer.data     = (unsigned long)ax25;
 	ax25->timer.function = &ax25_timer;
-	ax25->timer.expires  = jiffies + 10;
+	ax25->timer.expires  = jiffies + (HZ / 10);
 
 	add_timer(&ax25->timer);
 }
@@ -78,12 +80,17 @@
 	ax25_cb *ax25 = (ax25_cb *)param;
 
 	switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
-		case AX25_PROTO_STD:
+		case AX25_PROTO_STD_SIMPLEX:
+		case AX25_PROTO_STD_DUPLEX:
 			ax25_std_timer(ax25);
 			break;
+
 #ifdef CONFIG_AX25_DAMA_SLAVE
 		case AX25_PROTO_DAMA_SLAVE:
-			ax25_ds_timer(ax25);
+			if (ax25->ax25_dev->dama.slave)
+				ax25_ds_timer(ax25);
+			else
+				ax25_std_timer(ax25);
 			break;
 #endif
 	}
diff -u --recursive --new-file v2.1.29/linux/net/ax25/sysctl_net_ax25.c linux/net/ax25/sysctl_net_ax25.c
--- v2.1.29/linux/net/ax25/sysctl_net_ax25.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ax25/sysctl_net_ax25.c	Thu Mar 20 18:17:13 1997
@@ -21,7 +21,8 @@
 static int min_idle[] = {0},		max_idle[] = {65535 * AX25_SLOWHZ};
 static int min_n2[] = {1},		max_n2[] = {31};
 static int min_paclen[] = {1},		max_paclen[] = {512};
-static int min_proto[] = {0},		max_proto[] = {2};
+static int min_proto[] = {0},		max_proto[] = {3};
+static int min_ds_timeout[] = {0},	max_ds_timeout[] = {65535 * AX25_SLOWHZ};
 
 static struct ctl_table_header *ax25_table_header;
 
@@ -91,6 +92,10 @@
 	 NULL, sizeof(int), 0644, NULL,
 	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
 	 &min_proto, &max_proto},
+	{NET_AX25_DAMA_SLAVE_TIMEOUT, "dama_slave_timeout",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_ds_timeout, &max_ds_timeout},
 	{0}	/* that's all, folks! */
 };
 
@@ -117,6 +122,16 @@
 		ax25_table[n].proc_handler = NULL;
 
 		memcpy(ax25_dev->systable, ax25_param_table, sizeof(ax25_dev->systable));
+		
+#ifndef CONFIG_AX25_DAMA_SLAVE
+		/* 
+		 * We do not wish to have a representation of this parameter
+		 * in /proc/sys/ when configured *not* to include the
+		 * AX.25 DAMA slave code, do we?
+		 */
+
+		ax25_dev->systable[AX25_VALUES_DS_TIMEOUT].procname = NULL;
+#endif
 
 		ax25_dev->systable[AX25_MAX_VALUES].ctl_name = 0;	/* just in case... */
 
diff -u --recursive --new-file v2.1.29/linux/net/bridge/br.c linux/net/bridge/br.c
--- v2.1.29/linux/net/bridge/br.c	Thu Dec 19 00:32:56 1996
+++ linux/net/bridge/br.c	Mon Mar 10 15:51:10 1997
@@ -31,6 +31,7 @@
 #include <linux/types.h>
 #include <linux/socket.h>
 #include <linux/in.h>
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
@@ -45,26 +46,83 @@
 #include <asm/system.h>
 #include <net/br.h>
 
+static void transmit_config(int port_no);
+static int root_bridge(void);
+static int supersedes_port_info(int port_no, Config_bpdu *config);
+static void record_config_information(int port_no, Config_bpdu *config);
+static void record_config_timeout_values(Config_bpdu *config);
+static void config_bpdu_generation(void);
+static int designated_port(int port_no);
+static void reply(int port_no);
+static void transmit_tcn(void);
+static void configuration_update(void);
+static void root_selection(void);
+static void designated_port_selection(void);
+static void become_designated_port(int port_no);
+static void port_state_selection(void);
+static void make_forwarding(int port_no);
+static void topology_change_detection(void);
+static void topology_change_acknowledged(void);
+static void acknowledge_topology_change(int port_no);
+static void make_blocking(int port_no);
+static void set_port_state(int port_no, int state);
+static void received_config_bpdu(int port_no, Config_bpdu *config);
+static void received_tcn_bpdu(int port_no, Tcn_bpdu *tcn);
+static void hello_timer_expiry(void);
+static void message_age_timer_expiry(int port_no);
+static void forward_delay_timer_expiry(int port_no);
+static int designated_for_some_port(void);
+static void tcn_timer_expiry(void);
+static void topology_change_timer_expiry(void);
+static void hold_timer_expiry(int port_no);
+static void br_init_port(int port_no);
+static void enable_port(int port_no);
+static void disable_port(int port_no);
+static void set_bridge_priority(bridge_id_t *new_bridge_id);
+static void set_port_priority(int port_no, unsigned short new_port_id);
+static void set_path_cost(int port_no, unsigned short path_cost);
+static void start_hello_timer(void);
+static void stop_hello_timer(void);
+static int hello_timer_expired(void);
+static void start_tcn_timer(void);
+static void stop_tcn_timer(void);
+static int tcn_timer_expired(void);
+static void start_topology_change_timer(void);
+static void stop_topology_change_timer(void);
+static int topology_change_timer_expired(void);
+static void start_message_age_timer(int port_no, unsigned short message_age);
+static void stop_message_age_timer(int port_no);
+static int message_age_timer_expired(int port_no);
+static void start_forward_delay_timer(int port_no);
+static void stop_forward_delay_timer(int port_no);
+static int forward_delay_timer_expired(int port_no);
+static void start_hold_timer(int port_no);
+static void stop_hold_timer(int port_no);
+static int hold_timer_expired(int port_no);
 static int br_device_event(struct notifier_block *dnot, unsigned long event, void *ptr);
 static void br_tick(unsigned long arg);
-int br_forward(struct sk_buff *skb, int port);	/* 3.7 */
-int br_port_cost(struct device *dev);	/* 4.10.2 */
-void br_bpdu(struct sk_buff *skb); /* consumes skb */
-int br_tx_frame(struct sk_buff *skb);
-int br_cmp(unsigned int *a, unsigned int *b);
-
-unsigned char bridge_ula[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+static int br_forward(struct sk_buff *skb, int port);	/* 3.7 */
+static int br_port_cost(struct device *dev);	/* 4.10.2 */
+static void br_bpdu(struct sk_buff *skb); /* consumes skb */
+static int br_cmp(unsigned int *a, unsigned int *b);
+static int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu);
+static int send_config_bpdu(int port_no, Config_bpdu *config_bpdu);
+static int find_port(struct device *dev);
+static int br_flood(struct sk_buff *skb, int port);
+static int br_drop(struct sk_buff *skb);
+static int br_learn(struct sk_buff *skb, int port);	/* 3.8 */
 
-Bridge_data     bridge_info;			  /* (4.5.3)	 */
+static unsigned char bridge_ula[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+static Bridge_data     bridge_info;			  /* (4.5.3)	 */
 Port_data       port_info[All_ports];		  /* (4.5.5)	 */
 Config_bpdu     config_bpdu[All_ports];
 Tcn_bpdu        tcn_bpdu[All_ports];
-Timer           hello_timer;			  /* (4.5.4.1)	 */
-Timer           tcn_timer;			  /* (4.5.4.2)	 */
-Timer           topology_change_timer;		  /* (4.5.4.3)	 */
-Timer           message_age_timer[All_ports];	  /* (4.5.6.1)	 */
-Timer           forward_delay_timer[All_ports];	  /* (4.5.6.2)	 */
-Timer           hold_timer[All_ports];		  /* (4.5.6.3)	 */
+static Timer    hello_timer;			  /* (4.5.4.1)	 */
+static Timer    tcn_timer;			  /* (4.5.4.2)	 */
+static Timer    topology_change_timer;		  /* (4.5.4.3)	 */
+static Timer    message_age_timer[All_ports];	  /* (4.5.6.1)	 */
+static Timer    forward_delay_timer[All_ports];	  /* (4.5.6.2)	 */
+static Timer    hold_timer[All_ports];		  /* (4.5.6.3)	 */
 
 /* entries timeout after this many seconds */
 unsigned int fdb_aging_time = FDB_TIMEOUT; 
@@ -118,7 +176,7 @@
 
 /* Add a protocol to be handled opposite to the standard policy of the bridge */
 
-int br_add_exempt_protocol(unsigned short p)
+static int br_add_exempt_protocol(unsigned short p)
 {
 	unsigned x;
 	if (p == 0) return -EINVAL;
@@ -134,7 +192,7 @@
 }
 
 /* Valid Policies are 0=No Protocols bridged 1=Bridge all protocols */
-int br_set_policy(int policy)
+static int br_set_policy(int policy)
 {
 	if (policy>1) return -EINVAL;
 	br_stats.policy=policy;
@@ -155,7 +213,7 @@
  * 802.1d bridging protocol.
  */
 
-void transmit_config(int port_no)			  /* (4.6.1)	 */
+static void transmit_config(int port_no)	  /* (4.6.1)	 */
 {
 	if (hold_timer[port_no].active) {	  /* (4.6.1.3.1)	 */
 		port_info[port_no].config_pending = TRUE;	/* (4.6.1.3.1)	 */
@@ -198,13 +256,13 @@
 	}
 }
 
-int root_bridge(void)
+static int root_bridge(void)
 {
 	return (br_cmp(bridge_info.designated_root.BRIDGE_ID,
 		 bridge_info.bridge_id.BRIDGE_ID)?FALSE:TRUE);
 }
 
-int supersedes_port_info(int port_no, Config_bpdu *config)	  /* (4.6.2.2)	 */
+static int supersedes_port_info(int port_no, Config_bpdu *config)	  /* (4.6.2.2)	 */
 {
 	return (
 		(br_cmp(config->root_id.BRIDGE_ID,
@@ -241,7 +299,7 @@
 		);
 }
 
-void record_config_information(int port_no, Config_bpdu *config)	  /* (4.6.2)	 */
+static void record_config_information(int port_no, Config_bpdu *config)	  /* (4.6.2)	 */
 {
 	port_info[port_no].designated_root = config->root_id;	/* (4.6.2.3.1)   */
 	port_info[port_no].designated_cost = config->root_path_cost;
@@ -250,7 +308,7 @@
 	start_message_age_timer(port_no, config->message_age);	/* (4.6.2.3.2)   */
 }
 
-void record_config_timeout_values(Config_bpdu *config)		  /* (4.6.3)	 */
+static void record_config_timeout_values(Config_bpdu *config)		  /* (4.6.3)	 */
 {
 	bridge_info.max_age = config->max_age;	  /* (4.6.3.3)	 */
 	bridge_info.hello_time = config->hello_time;
@@ -259,7 +317,7 @@
 		bridge_info.top_change = 1;
 }
 
-void config_bpdu_generation(void)
+static void config_bpdu_generation(void)
 {						  /* (4.6.4)	 */
 	int             port_no;
 	for (port_no = One; port_no <= No_of_ports; port_no++) {	/* (4.6.4.3) */
@@ -272,7 +330,7 @@
 	}
 }
 
-int designated_port(int port_no)
+static int designated_port(int port_no)
 {
 	return ((br_cmp(port_info[port_no].designated_bridge.BRIDGE_ID,
 		 bridge_info.bridge_id.BRIDGE_ID) == 0
@@ -284,12 +342,12 @@
 		);
 }
 
-void reply(int port_no)					  /* (4.6.5)	 */
+static void reply(int port_no)					  /* (4.6.5)	 */
 {
 	transmit_config(port_no);		  /* (4.6.5.3)	 */
 }
 
-void transmit_tcn(void)
+static void transmit_tcn(void)
 {						  /* (4.6.6)	 */
 	int             port_no;
 
@@ -298,7 +356,7 @@
 	send_tcn_bpdu(port_no, &tcn_bpdu[bridge_info.root_port]);	/* (4.6.6.3)     */
 }
 
-void configuration_update(void)	/* (4.6.7) */
+static void configuration_update(void)	/* (4.6.7) */
 {
 	root_selection();			  /* (4.6.7.3.1)	 */
 	/* (4.6.8.2)	 */
@@ -306,7 +364,7 @@
 	/* (4.6.9.2)	 */
 }
 
-void root_selection(void)
+static void root_selection(void)
 {						  /* (4.6.8) */
 	int             root_port;
 	int             port_no;
@@ -386,7 +444,7 @@
 	}
 }
 
-void designated_port_selection(void)
+static void designated_port_selection(void)
 {						  /* (4.6.9)	 */
 	int             port_no;
 
@@ -423,7 +481,7 @@
 	}
 }
 
-void become_designated_port(int port_no)
+static void become_designated_port(int port_no)
 {						  /* (4.6.10)	 */
 
 	/* (4.6.10.3.1) */
@@ -436,7 +494,7 @@
 	port_info[port_no].designated_port = port_info[port_no].port_id;
 }
 
-void port_state_selection(void)
+static void port_state_selection(void)
 {						  /* (4.6.11) */
 	int             port_no;
 	for (port_no = One; port_no <= No_of_ports; port_no++) {
@@ -456,7 +514,7 @@
 
 }
 
-void make_forwarding(int port_no)
+static void make_forwarding(int port_no)
 {						  /* (4.6.12) */
 	if (port_info[port_no].state == Blocking) {	/* (4.6.12.3) */
 		set_port_state(port_no, Listening);	/* (4.6.12.3.1) */
@@ -464,7 +522,7 @@
 	}
 }
 
-void topology_change_detection(void)
+static void topology_change_detection(void)
 {						  /* (4.6.14)       */
 	if (root_bridge()) {			  /* (4.6.14.3.1)   */
 		bridge_info.top_change = 1;
@@ -476,19 +534,19 @@
 	bridge_info.top_change = 1;
 }
 
-void topology_change_acknowledged(void)
+static void topology_change_acknowledged(void)
 {						  /* (4.6.15) */
 	bridge_info.top_change_detected = 0;
 	stop_tcn_timer();			  /* (4.6.15.3.2) */
 }
 
-void acknowledge_topology_change(int port_no)
+static void acknowledge_topology_change(int port_no)
 {						  /* (4.6.16) */
 	port_info[port_no].top_change_ack = 1;
 	transmit_config(port_no);		  /* (4.6.16.3.2) */
 }
 
-void make_blocking(int port_no)				  /* (4.6.13)	 */
+static void make_blocking(int port_no)				  /* (4.6.13)	 */
 {
 
 	if ((port_info[port_no].state != Disabled)
@@ -508,12 +566,12 @@
 	}
 }
 
-void set_port_state(int port_no, int state)
+static void set_port_state(int port_no, int state)
 {
 	port_info[port_no].state = state;
 }
 
-void received_config_bpdu(int port_no, Config_bpdu *config)		  /* (4.7.1)	 */
+static void received_config_bpdu(int port_no, Config_bpdu *config)		  /* (4.7.1)	 */
 {
 	int         root;
 
@@ -550,7 +608,7 @@
 	}
 }
 
-void received_tcn_bpdu(int port_no, Tcn_bpdu *tcn)			  /* (4.7.2)	 */
+static void received_tcn_bpdu(int port_no, Tcn_bpdu *tcn)			  /* (4.7.2)	 */
 {
 	if (port_info[port_no].state != Disabled) {
 		if (designated_port(port_no)) {
@@ -561,13 +619,13 @@
 	}
 }
 
-void hello_timer_expiry(void)
+static void hello_timer_expiry(void)
 {						  /* (4.7.3)	 */
 	config_bpdu_generation();		  /* (4.6.4.2.2)	 */
 	start_hello_timer();
 }
 
-void message_age_timer_expiry(int port_no)		  /* (4.7.4)	 */
+static void message_age_timer_expiry(int port_no) /* (4.7.4)	 */
 {
 	int         root;
 	root = root_bridge();
@@ -592,7 +650,7 @@
 	}
 }
 
-void forward_delay_timer_expiry(int port_no)		  /* (4.7.5)	 */
+static void forward_delay_timer_expiry(int port_no)	/* (4.7.5)	 */
 {
 	if (port_info[port_no].state == Listening) {	/* (4.7.5.1)	 */
 		set_port_state(port_no, Learning);	/* (4.7.5.1.1)	 */
@@ -606,7 +664,7 @@
 	}
 }
 
-int designated_for_some_port(void)
+static int designated_for_some_port(void)
 {
 	int             port_no;
 
@@ -621,20 +679,20 @@
 	return (FALSE);
 }
 
-void tcn_timer_expiry(void)
+static void tcn_timer_expiry(void)
 {						  /* (4.7.6)	 */
 	transmit_tcn();				  /* (4.7.6.1)	 */
 	start_tcn_timer();			  /* (4.7.6.2)	 */
 }
 
-void topology_change_timer_expiry(void)
+static void topology_change_timer_expiry(void)
 {						  /* (4.7.7)	 */
 	bridge_info.top_change_detected = 0;
 	bridge_info.top_change = 0;
 	  /* (4.7.7.2)	 */
 }
 
-void hold_timer_expiry(int port_no)			  /* (4.7.8)	 */
+static void hold_timer_expiry(int port_no)	  /* (4.7.8)	 */
 {
 	if (port_info[port_no].config_pending) {
 		transmit_config(port_no);	  /* (4.7.8.1)	 */
@@ -682,7 +740,7 @@
 	/*start_hello_timer();*/
 }
 
-void br_init_port(int port_no)
+static void br_init_port(int port_no)
 {
 	become_designated_port(port_no);	  /* (4.8.1.4.1) */
 	set_port_state(port_no, Blocking);	  /* (4.8.1.4.2)    */
@@ -693,13 +751,13 @@
 	stop_hold_timer(port_no);		  /* (4.8.1.4.7)	 */
 }
 
-void enable_port(int port_no)				  /* (4.8.2)	 */
+static void enable_port(int port_no)				  /* (4.8.2)	 */
 {
 	br_init_port(port_no);
 	port_state_selection();			  /* (4.8.2.7)	 */
 }						  /* */
 
-void disable_port(int port_no)				  /* (4.8.3)	 */
+static void disable_port(int port_no)				  /* (4.8.3)	 */
 {
 	int         root;
 
@@ -724,7 +782,8 @@
 }
 
 
-void set_bridge_priority(bridge_id_t *new_bridge_id)		  /* (4.8.4)	 */
+static void set_bridge_priority(bridge_id_t *new_bridge_id)
+                                 		  /* (4.8.4)	 */
 {
 
 	int         root;
@@ -750,7 +809,8 @@
 	}
 }
 
-void set_port_priority(int port_no, unsigned short new_port_id)		  /* (4.8.5)	 */
+static void set_port_priority(int port_no, unsigned short new_port_id)
+                                  		  /* (4.8.5)	 */
 {
 	if (designated_port(port_no)) {		  /* (4.8.5.2)	 */
 		port_info[port_no].designated_port = new_port_id;
@@ -770,7 +830,8 @@
 	}
 }
 
-void set_path_cost(int port_no, unsigned short path_cost)		  /* (4.8.6)	 */
+static void set_path_cost(int port_no, unsigned short path_cost)
+                                                  /* (4.8.6)	 */
 {
 	port_info[port_no].path_cost = path_cost; /* (4.8.6.1)	 */
 	configuration_update();			  /* (4.8.6.2)	 */
@@ -807,18 +868,18 @@
 	add_timer(&tl);
 }
 
-void start_hello_timer(void)
+static void start_hello_timer(void)
 {
 	hello_timer.value = 0;
 	hello_timer.active = TRUE;
 }
 
-void stop_hello_timer(void)
+static void stop_hello_timer(void)
 {
 	hello_timer.active = FALSE;
 }
 
-int hello_timer_expired(void)
+static int hello_timer_expired(void)
 {
 	if (hello_timer.active && (++hello_timer.value >= bridge_info.hello_time)) {
 		hello_timer.active = FALSE;
@@ -827,18 +888,18 @@
 	return (FALSE);
 }
 
-void start_tcn_timer(void)
+static void start_tcn_timer(void)
 {
 	tcn_timer.value = 0;
 	tcn_timer.active = TRUE;
 }
 
-void stop_tcn_timer(void)
+static void stop_tcn_timer(void)
 {
 	tcn_timer.active = FALSE;
 }
 
-int tcn_timer_expired(void)
+static int tcn_timer_expired(void)
 {
 	if (tcn_timer.active && (++tcn_timer.value >=
 				 bridge_info.bridge_hello_time)) {
@@ -849,18 +910,18 @@
 
 }
 
-void start_topology_change_timer(void)
+static void start_topology_change_timer(void)
 {
 	topology_change_timer.value = 0;
 	topology_change_timer.active = TRUE;
 }
 
-void stop_topology_change_timer(void)
+static void stop_topology_change_timer(void)
 {
 	topology_change_timer.active = FALSE;
 }
 
-int topology_change_timer_expired(void)
+static int topology_change_timer_expired(void)
 {
 	if (topology_change_timer.active
 			&& (++topology_change_timer.value
@@ -872,18 +933,18 @@
 	return (FALSE);
 }
 
-void start_message_age_timer(int port_no, unsigned short message_age)
+static void start_message_age_timer(int port_no, unsigned short message_age)
 {
 	message_age_timer[port_no].value = message_age;
 	message_age_timer[port_no].active = TRUE;
 }
 
-void stop_message_age_timer(int port_no)
+static void stop_message_age_timer(int port_no)
 {
 	message_age_timer[port_no].active = FALSE;
 }
 
-int message_age_timer_expired(int port_no)
+static int message_age_timer_expired(int port_no)
 {
 	if (message_age_timer[port_no].active &&
 	      (++message_age_timer[port_no].value >= bridge_info.max_age)) {
@@ -893,18 +954,18 @@
 	return (FALSE);
 }
 
-void start_forward_delay_timer(int port_no)
+static void start_forward_delay_timer(int port_no)
 {
 	forward_delay_timer[port_no].value = 0;
 	forward_delay_timer[port_no].active = TRUE;
 }
 
-void stop_forward_delay_timer(int port_no)
+static void stop_forward_delay_timer(int port_no)
 {
 	forward_delay_timer[port_no].active = FALSE;
 }
 
-int forward_delay_timer_expired(int port_no)
+static int forward_delay_timer_expired(int port_no)
 {
 		if (forward_delay_timer[port_no].active &&
 				(++forward_delay_timer[port_no].value >= bridge_info.forward_delay)) {
@@ -914,19 +975,18 @@
 		return (FALSE);
 }
 
-void start_hold_timer(int port_no)
+static void start_hold_timer(int port_no)
 {
 	hold_timer[port_no].value = 0;
 	hold_timer[port_no].active = TRUE;
 }
 
-void stop_hold_timer(int port_no)
+static void stop_hold_timer(int port_no)
 {
 	hold_timer[port_no].active = FALSE;
 }
 
-
-int hold_timer_expired(int port_no)
+static int hold_timer_expired(int port_no)
 {
 	if (hold_timer[port_no].active &&
 		   (++hold_timer[port_no].value >= bridge_info.hold_time)) {
@@ -937,7 +997,7 @@
 
 }
 
-int send_config_bpdu(int port_no, Config_bpdu *config_bpdu)
+static int send_config_bpdu(int port_no, Config_bpdu *config_bpdu)
 {
 struct sk_buff *skb;
 struct device *dev = port_info[port_no].dev;
@@ -996,7 +1056,7 @@
 	return(0);
 }
 
-int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu)
+static int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu)
 {
 struct sk_buff *skb;
 struct device *dev = port_info[port_no].dev;
@@ -1281,7 +1341,7 @@
  * state or lack of resources...
  */
 
-int br_learn(struct sk_buff *skb, int port)	/* 3.8 */
+static int br_learn(struct sk_buff *skb, int port)	/* 3.8 */
 {
 	struct fdb *f;
 
@@ -1330,7 +1390,7 @@
  * this routine always consumes the frame
  */
 
-int br_drop(struct sk_buff *skb)
+static int br_drop(struct sk_buff *skb)
 {
 	kfree_skb(skb, 0);
 	return(1);
@@ -1340,7 +1400,7 @@
  * this routine always consumes the frame
  */
 
-int br_dev_drop(struct sk_buff *skb)
+static int br_dev_drop(struct sk_buff *skb)
 {
 	dev_kfree_skb(skb, 0);
 	return(1);
@@ -1355,7 +1415,7 @@
  * if not...
  */
 
-int br_forward(struct sk_buff *skb, int port)	/* 3.7 */
+static int br_forward(struct sk_buff *skb, int port)	/* 3.7 */
 {
 	struct fdb *f;
 	
@@ -1438,7 +1498,7 @@
  * consumes the original frame.
  */
 	
-int br_flood(struct sk_buff *skb, int port)
+static int br_flood(struct sk_buff *skb, int port)
 {
 	int i;
 	struct sk_buff *nskb;
@@ -1469,7 +1529,7 @@
 	return(0);
 }
 
-int find_port(struct device *dev)
+static int find_port(struct device *dev)
 {
 	int i;
 
@@ -1480,7 +1540,7 @@
 	return(0);
 }
 
-int br_port_cost(struct device *dev)	/* 4.10.2 */
+static int br_port_cost(struct device *dev)	/* 4.10.2 */
 {
 	if (strncmp(dev->name, "eth", 3) == 0)	/* ethernet */
 		return(100);
@@ -1495,7 +1555,7 @@
  * this routine always consumes the skb 
  */
 
-void br_bpdu(struct sk_buff *skb) /* consumes skb */
+static void br_bpdu(struct sk_buff *skb) /* consumes skb */
 {
 	Tcn_bpdu *bpdu;
 	int port;
@@ -1624,7 +1684,7 @@
 	return 0;
 }
 
-int br_cmp(unsigned int *a, unsigned int *b)
+static int br_cmp(unsigned int *a, unsigned int *b)
 {
 	int i;	
 	for (i=0; i<2; i++) 
@@ -1638,4 +1698,3 @@
 	}
 	return(0);
 }
-
diff -u --recursive --new-file v2.1.29/linux/net/bridge/br_tree.c linux/net/bridge/br_tree.c
--- v2.1.29/linux/net/bridge/br_tree.c	Mon May 13 02:15:24 1996
+++ linux/net/bridge/br_tree.c	Mon Mar 10 15:51:10 1997
@@ -18,12 +18,12 @@
  * <hayes@netplumbing.com>
  */
 
-struct fdb fdb_head;
-struct fdb *fhp = &fdb_head;
-struct fdb **fhpp = &fhp;
+static struct fdb fdb_head;
+static struct fdb *fhp = &fdb_head;
+static struct fdb **fhpp = &fhp;
 static int fdb_inited = 0;
 
-int addr_cmp(unsigned char *a1, unsigned char *a2);
+static int addr_cmp(unsigned char *a1, unsigned char *a2);
 
 /*
  * fdb_head is the AVL tree corresponding to fdb
@@ -50,7 +50,7 @@
  *    foreach node in tree->fdb_avl_right: node->fdb_avl_key >= tree->fdb_avl_key.
  */
 
-int
+static int
 fdb_init(void)
 {
 	fdb_head.fdb_avl_height = 0;
@@ -60,8 +60,7 @@
 	return(0);
 }
 
-struct fdb *
-br_avl_find_addr(unsigned char addr[6])
+struct fdb *br_avl_find_addr(unsigned char addr[6])
 {
 	struct fdb * result = NULL;
 	struct fdb * tree;
@@ -109,14 +108,15 @@
 	}
 }
 
+
+#if (0)
 /*
  * Rebalance a tree.
  * After inserting or deleting a node of a tree we have a sequence of subtrees
  * nodes[0]..nodes[k-1] such that
  * nodes[0] is the root and nodes[i+1] = nodes[i]->{fdb_avl_left|fdb_avl_right}.
  */
-static void 
-br_avl_rebalance (struct fdb *** nodeplaces_ptr, int count)
+static void br_avl_rebalance (struct fdb *** nodeplaces_ptr, int count)
 {
 	if (!fdb_inited)
 		fdb_init();
@@ -196,10 +196,10 @@
 	printk_avl(&fdb_head);
 #endif /* DEBUG_AVL */
 }
+#endif /* (0) */
 
 /* Insert a node into a tree. */
-int 
-br_avl_insert (struct fdb * new_node)
+int br_avl_insert (struct fdb * new_node)
 {
 	struct fdb ** nodeplace = fhpp;
 	struct fdb ** stack[avl_maxheight];
@@ -248,9 +248,10 @@
 	return(1);
 }
 
+
+#if (0)
 /* Removes a node out of a tree. */
-int
-br_avl_remove (struct fdb * node_to_delete)
+static int br_avl_remove (struct fdb * node_to_delete)
 {
 	struct fdb ** nodeplace = fhpp;
 	struct fdb ** stack[avl_maxheight];
@@ -301,6 +302,7 @@
 	br_avl_rebalance(stack_ptr,stack_count);
 	return(0);
 }
+#endif /* (0) */
 
 #ifdef DEBUG_AVL
 
@@ -388,8 +390,7 @@
 #endif /* (0) */
 #endif /* DEBUG_AVL */
 
-int
-addr_cmp(unsigned char a1[], unsigned char a2[])
+static int addr_cmp(unsigned char a1[], unsigned char a2[])
 {
 	int i;
 
diff -u --recursive --new-file v2.1.29/linux/net/core/dst.c linux/net/core/dst.c
--- v2.1.29/linux/net/core/dst.c	Thu Dec 12 06:54:23 1996
+++ linux/net/core/dst.c	Thu Mar 20 18:17:13 1997
@@ -41,7 +41,7 @@
 	del_timer(&dst_gc_timer);
 	dstp = &dst_garbage_list;
 	while ((dst = *dstp) != NULL) {
-		if (dst->refcnt) {
+		if (dst->use) {
 			dstp = &dst->next;
 			delayed++;
 			continue;
diff -u --recursive --new-file v2.1.29/linux/net/core/firewall.c linux/net/core/firewall.c
--- v2.1.29/linux/net/core/firewall.c	Sun Feb  2 05:18:49 1997
+++ linux/net/core/firewall.c	Thu Mar 20 18:17:13 1997
@@ -97,13 +97,13 @@
 	return -ENOENT;
 }
 
-int call_fw_firewall(int pf, struct device *dev, void *phdr, void *arg)
+int call_fw_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
 {
 	struct firewall_ops *fw=firewall_chain[pf];
 
 	while(fw!=NULL)
 	{
-		int rc=fw->fw_forward(fw,pf,dev,phdr,arg);
+		int rc=fw->fw_forward(fw,pf,dev,phdr,arg,skb);
 		if(rc!=FW_SKIP)
 			return rc;
 		fw=fw->next;
@@ -115,13 +115,13 @@
  *	Actual invocation of the chains
  */
 
-int call_in_firewall(int pf, struct device *dev, void *phdr, void *arg)
+int call_in_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
 {
 	struct firewall_ops *fw=firewall_chain[pf];
 
 	while(fw!=NULL)
 	{
-		int rc=fw->fw_input(fw,pf,dev,phdr,arg);
+		int rc=fw->fw_input(fw,pf,dev,phdr,arg,skb);
 		if(rc!=FW_SKIP)
 			return rc;
 		fw=fw->next;
@@ -129,13 +129,13 @@
 	return firewall_policy[pf];
 }
 
-int call_out_firewall(int pf, struct device *dev, void *phdr, void *arg)
+int call_out_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
 {
 	struct firewall_ops *fw=firewall_chain[pf];
 
 	while(fw!=NULL)
 	{
-		int rc=fw->fw_output(fw,pf,dev,phdr,arg);
+		int rc=fw->fw_output(fw,pf,dev,phdr,arg,skb);
 		if(rc!=FW_SKIP)
 			return rc;
 		fw=fw->next;
diff -u --recursive --new-file v2.1.29/linux/net/core/neighbour.c linux/net/core/neighbour.c
--- v2.1.29/linux/net/core/neighbour.c	Thu Jan  2 04:07:39 1997
+++ linux/net/core/neighbour.c	Thu Mar 20 18:17:13 1997
@@ -14,6 +14,7 @@
 #include <linux/kernel.h>
 #include <linux/socket.h>
 #include <linux/sched.h>
+#include <linux/netdevice.h>
 #include <net/neighbour.h>
 
 
@@ -44,11 +45,11 @@
 	memset(tbl->hash_buckets, 0, bmemlen);
 }
 
-struct neighbour *neigh_alloc(int size, int priority)
+struct neighbour *neigh_alloc(int size, struct neigh_ops *ops)
 {
 	struct neighbour *neigh;
 	
-	neigh = kmalloc(size, priority);
+	neigh = kmalloc(size, GFP_ATOMIC);
 	if (neigh == NULL)
 	{
 		return NULL;
@@ -57,7 +58,7 @@
 	memset(neigh, 0, size);
 	
 	skb_queue_head_init(&neigh->arp_queue);
-
+	neigh->ops = ops;
 	return neigh;
 }
 
@@ -96,7 +97,6 @@
 	hash_val = tbl->neigh_ops->hash(neigh->primary_key) % tbl->tbl_size;
 	
 	neigh->tbl = tbl;
-	neigh->ops = tbl->neigh_ops;
 	
 	head = &tbl->hash_buckets[hash_val];
 	
@@ -143,7 +143,7 @@
 					return neigh;
 			}
 			neigh = neigh->next;
-			
+
 		} while (neigh != head);
 	}
 
@@ -156,8 +156,6 @@
  */
 void neigh_destroy(struct neighbour *neigh)
 {	
-	unsigned long flags;
-
 	if (neigh->tbl)
 	{
 		printk(KERN_DEBUG "neigh_destroy: neighbour still in table. "
@@ -171,10 +169,6 @@
 
 	neigh_purge_send_q(neigh);
 
-	save_flags(flags);
-	cli();
-	restore_flags(flags);
-	
 	kfree(neigh);
 }
 
@@ -185,14 +179,14 @@
 	unsigned int hash_val;
 	struct neighbour *next, *prev;
 	
-	tbl = neigh->tbl;	
+	tbl = neigh->tbl;
 	neigh->tbl = NULL;
-	
+
 	hash_val = neigh->ops->hash(neigh->primary_key) % tbl->tbl_size;
 
 	head = &tbl->hash_buckets[hash_val];
 	tbl->tbl_entries--;
-	
+
 	next = neigh->next;
 	if (neigh == (*head))
 	{
@@ -220,21 +214,21 @@
 		     unsigned long filter, int max, void *args)
 {
 	int i;
-	
+
 	if (max == 0)
 		max = tbl->tbl_size;
-	
+
 	for (i=0; i < max; i++)
 	{
 		struct neighbour **head;
 		struct neighbour *entry;
-		
+
 		head = &tbl->hash_buckets[i];
 		entry = *head;
 
 		if (!entry)
 			continue;
-		
+
 		do {
 			if (entry->flags & (~filter))
 			{
@@ -247,10 +241,10 @@
 
 					curp = entry;
 					entry = curp->next;
-					
+
 					neigh_unlink(curp);
 					neigh_destroy(curp);
-					
+
 					if ((*head) == NULL)
 						break;
 					continue;
diff -u --recursive --new-file v2.1.29/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v2.1.29/linux/net/core/skbuff.c	Thu Jan  2 05:13:28 1997
+++ linux/net/core/skbuff.c	Thu Mar 20 18:17:13 1997
@@ -47,7 +47,6 @@
 #include <linux/skbuff.h>
 
 #include <net/ip.h>
-#include <net/ipv6.h>
 #include <net/protocol.h>
 #include <net/dst.h>
 #include <net/tcp.h>
@@ -607,7 +606,7 @@
 	int len;
 	unsigned char *bptr;
 
-	if (intr_count && priority!=GFP_ATOMIC) 
+	if (0 && intr_count && priority!=GFP_ATOMIC) 
 	{
 		static int count = 0;
 		if (++count < 5) {
@@ -663,6 +662,7 @@
 	skb->truesize=size;
 	skb->stamp.tv_sec=0;	/* No idea about time */
 	skb->ip_summed = 0;
+	skb->security = 0;	/* By default packets are insecure */
 	skb->dst = NULL;
 	skb->destructor = NULL;
 	memset(skb->cb, 0, sizeof(skb->cb));
@@ -799,9 +799,6 @@
 	n->h.raw=skb->h.raw+offset;
 	n->nh.raw=skb->nh.raw+offset;
 	n->mac.raw=skb->mac.raw+offset;
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-	n->nexthop = skb->nexthop;
-#endif
 	n->seq=skb->seq;
 	n->end_seq=skb->end_seq;
 	n->ack_seq=skb->ack_seq;
@@ -813,9 +810,8 @@
 	n->users=1;
 	n->pkt_type=skb->pkt_type;
 	n->stamp=skb->stamp;
-	n->arp=skb->arp;
 	n->destructor = NULL;
-	
+	n->security=skb->security;
 	IS_SKB(n);
 	return n;
 }
@@ -859,9 +855,6 @@
 	n->nh.raw=skb->nh.raw+offset;
 	n->mac.raw=skb->mac.raw+offset;
 	memcpy(n->cb, skb->cb, sizeof(skb->cb));
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- 	n->nexthop = skb->nexthop;
-#endif
 	n->seq=skb->seq;
  	n->end_seq=skb->end_seq;
 	n->ack_seq=skb->ack_seq;
@@ -873,7 +866,8 @@
 	n->pkt_type=skb->pkt_type;
 	n->stamp=skb->stamp;
 	n->destructor = NULL;
- 	
+	n->security=skb->security;
+
 	IS_SKB(n);
 	return n;
 }
diff -u --recursive --new-file v2.1.29/linux/net/core/sock.c linux/net/core/sock.c
--- v2.1.29/linux/net/core/sock.c	Tue Mar  4 10:25:26 1997
+++ linux/net/core/sock.c	Thu Mar 20 18:17:13 1997
@@ -114,6 +114,7 @@
 #include <net/sock.h>
 #include <net/raw.h>
 #include <net/icmp.h>
+#include <linux/ipsec.h>
 
 #define min(a,b)	((a)<(b)?(a):(b))
 
@@ -131,7 +132,7 @@
 	int err;
 	struct linger ling;
 	int ret = 0;
-
+	
 	/*
 	 *	Options without arguments
 	 */
@@ -145,8 +146,13 @@
 	}
 #endif	
 		
-  	if(optlen<sizeof(int))
+  	if(optlen<sizeof(int)) {
+#if 1 /* DaveM Debugging */
+		printk("sock_setsockopt: optlen is %d, going on anyways.\n", optlen);
+#else
   		return(-EINVAL);
+#endif
+	}
   	
 	err = get_user(val, (int *)optval);
 	if (err)
@@ -178,12 +184,19 @@
 			sk->broadcast=valbool;
 			break;
 		case SO_SNDBUF:
-			if(val > SK_WMEM_MAX*2)
-				val = SK_WMEM_MAX*2;
-			if(val < 256)
-				val = 256;
+			/*
+			 *	The spec isnt clear if ENOBUFS or EINVAL
+			 *	is best
+			 */
+			 
+			if(val > SK_WMEM_MAX*2 || val < 2048)
+				return -EINVAL;
+			/*
+			 *	Once this is all 32bit values we can
+			 *	drop this check.
+			 */
 			if(val > 65535)
-				val = 65535;
+				return -EINVAL;
 			sk->sndbuf = val;
 			/*
 			 *	Wake up sending tasks if we
@@ -193,12 +206,11 @@
 			break;
 
 		case SO_RCVBUF:
-			if(val > SK_RMEM_MAX*2)
-			 	val = SK_RMEM_MAX*2;
-			if(val < 256)
-				val = 256;
+			if(val > SK_RMEM_MAX*2 || val < 256)
+				return -EINVAL;
+			/* Can go soon: FIXME */
 			if(val > 65535)
-				val = 65535;
+				return -EINVAL;
 			sk->rcvbuf = val;
 			break;
 
@@ -253,7 +265,50 @@
 		case SO_PASSCRED:
 			sock->passcred = valbool;
 			break;
-
+			
+			
+#ifdef CONFIG_NET_SECURITY			
+		/*
+		 *	FIXME: make these error things that are not
+		 *	available!
+		 */
+		 
+		case SO_SECURITY_AUTHENTICATION:
+			if(val<=IPSEC_LEVEL_DEFAULT)
+			{
+				sk->authentication=val;
+				return 0;
+			}
+			if(net_families[sock->ops->family]->authentication)
+				sk->authentication=val;
+			else
+				return -EINVAL;
+			break;
+			
+		case SO_SECURITY_ENCRYPTION_TRANSPORT:
+			if(val<=IPSEC_LEVEL_DEFAULT)
+			{
+				sk->encryption=val;
+				return 0;
+			}
+			if(net_families[sock->ops->family]->encryption)
+				sk->encryption = val;
+			else
+				return -EINVAL;
+			break;
+			
+		case SO_SECURITY_ENCRYPTION_NETWORK:
+			if(val<=IPSEC_LEVEL_DEFAULT)
+			{
+				sk->encrypt_net=val;
+				return 0;
+			}
+			if(net_families[sock->ops->family]->encrypt_net)
+				sk->encrypt_net = val;
+			else
+				return -EINVAL;
+			break;
+#endif
 		/* We implement the SO_SNDLOWAT etc to
 		   not be settable (1003.1g 5.3) */
 		default:
@@ -368,6 +423,19 @@
 			if(copy_to_user((void*)optval, &sk->peercred, len))
 				return -EFAULT;
 			return 0;
+			
+		case SO_SECURITY_AUTHENTICATION:
+			val = sk->authentication;
+			break;
+			
+		case SO_SECURITY_ENCRYPTION_TRANSPORT:
+			val = sk->encryption;
+			break;
+			
+		case SO_SECURITY_ENCRYPTION_NETWORK:
+			val = sk->encrypt_net;
+			break;
+			
 		default:
 			return(-ENOPROTOOPT);
 	}
diff -u --recursive --new-file v2.1.29/linux/net/ethernet/eth.c linux/net/ethernet/eth.c
--- v2.1.29/linux/net/ethernet/eth.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ethernet/eth.c	Thu Mar 20 18:17:13 1997
@@ -57,19 +57,10 @@
 #include <net/sock.h>
 #include <net/ipv6.h>
 
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-#include <linux/in6.h>
-#include <net/ndisc.h>
-#endif
 
 #include <asm/checksum.h>
 
 
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-int (*ndisc_eth_hook) (unsigned char *, struct device *, 
-		       struct sk_buff *) = NULL;
-#endif
-
 void eth_setup(char *str, int *ints)
 {
 	struct device *d = dev_base;
@@ -156,36 +147,26 @@
 {
 	struct ethhdr *eth = (struct ethhdr *)skb->data;
 	struct device *dev = skb->dev;
+ 	struct neighbour *neigh = NULL;
 
 	/*
 	 *	Only ARP/IP and NDISC/IPv6 are currently supported
 	 */
 	
+ 	if (skb->dst)
+ 		neigh = skb->dst->neighbour;
+ 
+ 	if (neigh)
+ 		return neigh->ops->resolve(eth->h_dest, skb);
+ 
 	switch (eth->h_proto)
 	{
 #ifdef CONFIG_INET
 	case __constant_htons(ETH_P_IP):
-
-		/*
-		 *	Try to get ARP to resolve the header.
-		 */
-
-		return arp_find(eth->h_dest, skb) ? 1 : 0;
-		break;
-#endif
-
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-	case __constant_htons(ETH_P_IPV6):
-#ifdef CONFIG_IPV6
-		return (ndisc_eth_resolv(eth->h_dest, dev, skb));
-#else
-		if (ndisc_eth_hook)
-			return (ndisc_eth_hook(eth->h_dest, dev, skb));
-#endif
-		break;
+ 		return arp_find(eth->h_dest, skb);
 #endif	
 	default:
-		printk(KERN_DEBUG 
+		printk(KERN_DEBUG
 		       "%s: unable to resolve type %X addresses.\n", 
 		       dev->name, (int)eth->h_proto);
 		
@@ -252,7 +233,8 @@
 	return htons(ETH_P_802_2);
 }
 
-int eth_header_cache(struct dst_entry *dst, struct dst_entry *neigh, struct hh_cache *hh)
+int eth_header_cache(struct dst_entry *dst, struct neighbour *neigh,
+		     struct hh_cache *hh)
 {
 	unsigned short type = hh->hh_type;
 	struct ethhdr *eth = (struct ethhdr*)hh->hh_data;
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/Makefile linux/net/ipv4/Makefile
--- v2.1.29/linux/net/ipv4/Makefile	Fri Jan  3 01:33:27 1997
+++ linux/net/ipv4/Makefile	Thu Mar 20 18:17:13 1997
@@ -41,7 +41,7 @@
 
 ifeq ($(CONFIG_IP_MASQUERADE),y)
 IPV4X_OBJS += ip_masq.o ip_masq_app.o
-M_OBJS += ip_masq_ftp.o ip_masq_irc.o ip_masq_raudio.o
+M_OBJS += ip_masq_ftp.o ip_masq_irc.o ip_masq_raudio.o ip_masq_quake.o
 endif
 
 ifeq ($(CONFIG_IP_ALIAS),y)
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.1.29/linux/net/ipv4/arp.c	Tue Feb  4 06:44:25 1997
+++ linux/net/ipv4/arp.c	Thu Mar 20 18:17:13 1997
@@ -230,30 +230,6 @@
 
 int sysctl_arp_dead_res_time = ARP_DEAD_RES_TIME;
 
-/*
- *	This structure defines the ARP mapping cache.
- */
-
-struct arp_table
-{
-	union {
-		struct dst_entry	dst;
-		struct arp_table	*next;
-	} u;
-	unsigned long			last_updated;		/* For expiry 			*/
-	unsigned int			flags;			/* Control status 		*/
-	u32				ip;
-	u32				mask;			/* netmask - used for generalised proxy arps (tridge) 		*/
-	int				hatype;
-	unsigned char			ha[MAX_ADDR_LEN];	/* Hardware address		*/
-
-	/*
-	 *	The following entries are only used for unresolved hw addresses.
-	 */
-	struct timer_list		timer;			/* expire timer 		*/
-	int				retries;		/* remaining retries	 	*/
-	struct sk_buff_head		skb;			/* list of queued packets 	*/
-};
 
 #if RT_CACHE_DEBUG >= 1
 #define ASSERT_BH() if (!intr_count) printk(KERN_CRIT __FUNCTION__ " called from SPL=0\n");
@@ -261,28 +237,17 @@
 #define ASSERT_BH()
 #endif
 
+static void arp_neigh_destroy(struct neighbour *neigh);
+
 /*
- *	Interface to generic destionation cache.
+ *	Interface to generic neighbour cache.
  */
 
-static void arp_dst_destroy(struct dst_entry * dst);
-static struct dst_entry * arp_dst_check(struct dst_entry * dst)
-{
-	return dst;
-}
-
-static struct dst_entry * arp_dst_reroute(struct dst_entry * dst)
-{
-	return dst;
-}
-
-
-struct dst_ops arp_dst_ops =
-{
-	AF_UNSPEC,
-	arp_dst_check,
-	arp_dst_reroute,
-	arp_dst_destroy
+struct neigh_ops arp_neigh_ops = {
+	AF_INET,
+	NULL,
+	arp_find,
+	arp_neigh_destroy
 };
 
 
@@ -342,17 +307,17 @@
 {
 	struct hh_cache *hh;
 	void (*update)(struct hh_cache*, struct device*, unsigned char*) =
-		entry->u.dst.dev->header_cache_update;
+		entry->u.neigh.dev->header_cache_update;
 
 #if RT_CACHE_DEBUG >= 1
-	if (!update && entry->u.dst.hh)
+	if (!update && entry->u.neigh.hh)
 	{
-		printk(KERN_DEBUG "arp_update_hhs: no update callback for %s\n", entry->u.dst.dev->name);
+		printk(KERN_DEBUG "arp_update_hhs: no update callback for %s\n", entry->u.neigh.dev->name);
 		return;
 	}
 #endif
-	for (hh=entry->u.dst.hh; hh; hh=hh->hh_next)
-		update(hh, entry->u.dst.dev, entry->ha);
+	for (hh=entry->u.neigh.hh; hh; hh=hh->hh_next)
+		update(hh, entry->u.neigh.dev, entry->u.neigh.ha);
 }
 
 /*
@@ -363,7 +328,7 @@
 {
 	struct hh_cache *hh;
 
-	for (hh=entry->u.dst.hh; hh; hh=hh->hh_next)
+	for (hh=entry->u.neigh.hh; hh; hh=hh->hh_next)
 		hh->hh_uptodate = 0;
 }
 
@@ -378,7 +343,7 @@
 	ASSERT_BH();
 
 	/* Release the list of `skb' pointers. */
-	while ((skb = skb_dequeue(&entry->skb)) != NULL)
+	while ((skb = skb_dequeue(&entry->u.neigh.arp_queue)) != NULL)
 		kfree_skb(skb, FREE_WRITE);
 	return;
 }
@@ -399,13 +364,13 @@
 	arp_purge_send_q(entry);
 	arp_invalidate_hhs(entry);
 
-	dst_free(&entry->u.dst);
+	neigh_destroy(&entry->u.neigh);
 }
 
 
-static void arp_dst_destroy(struct dst_entry * dst)
+static void arp_neigh_destroy(struct neighbour *neigh)
 {
-	struct arp_table *entry = (struct arp_table*)dst;
+	struct arp_table *entry = (struct arp_table*)neigh;
 	struct hh_cache *hh, *next;
 
 	ASSERT_BH();
@@ -413,8 +378,8 @@
 	del_timer(&entry->timer);
 	arp_purge_send_q(entry);
 
-	hh = entry->u.dst.hh;
-	entry->u.dst.hh = NULL;
+	hh = entry->u.neigh.hh;
+	entry->u.neigh.hh = NULL;
 
 	for ( ; hh; hh = next)
 	{
@@ -459,7 +424,7 @@
 	arpreq->stamp = arpd_stamp;
 	arpreq->updated = updated;
 	if (ha)
-		memcpy(arpreq->ha, ha, sizeof(arpreq->ha));
+		memcpy(arpreq->u.neigh.ha, ha, sizeof(arpreq->u.neigh.ha));
 
 	retval = netlink_post(NETLINK_ARPD, skb);
 	if (retval)
@@ -538,7 +503,7 @@
 	else
 	{
 		start_bh_atomic();
-		arp_update(retreq->ip, retreq->ha, dev, retreq->updated, 0);
+		arp_update(retreq->ip, retreq->u.neigh.ha, dev, retreq->updated, 0);
 		end_bh_atomic();
 	}
 
@@ -576,7 +541,7 @@
 	unsigned long now = jiffies;
 	int result = 0;
 
-	static int last_index;
+	static last_index;
 
 	if (last_index >= ARP_TABLE_SIZE)
 		last_index = 0;
@@ -589,8 +554,8 @@
 		{
 			if (!(entry->flags & ATF_PERM))
 			{
-				if (!entry->u.dst.refcnt &&
-				    now - entry->u.dst.lastuse > sysctl_arp_timeout)
+				if (!entry->u.neigh.refcnt &&
+				    now - entry->u.neigh.lastused > sysctl_arp_timeout)
 				{
 #if RT_CACHE_DEBUG >= 2
 					printk("arp_force_expire: %08x expired\n", entry->ip);
@@ -601,11 +566,11 @@
 						goto done;
 					continue;
 				}
-				if (!entry->u.dst.refcnt &&
-				    entry->u.dst.lastuse < oldest_used)
+				if (!entry->u.neigh.refcnt &&
+				    entry->u.neigh.lastused < oldest_used)
 				{
 					oldest_entry = pentry;
-					oldest_used = entry->u.dst.lastuse;
+					oldest_used = entry->u.neigh.lastused;
 				}
 			}
 			pentry = &entry->u.next;
@@ -637,7 +602,7 @@
 			    (entry->retries < sysctl_arp_max_tries ||
 			     entry->timer.expires - now <
 			     sysctl_arp_res_time - sysctl_arp_res_time/32)) {
-				if (!entry->u.dst.refcnt) {
+				if (!entry->u.neigh.refcnt) {
 #if RT_CACHE_DEBUG >= 2
 					printk("arp_unres_expire: %08x discarded\n", entry->ip);
 #endif
@@ -688,8 +653,8 @@
 				continue;
 			}
 
-			if (!entry->u.dst.refcnt &&
-			    now - entry->u.dst.lastuse > sysctl_arp_timeout)
+			if (!entry->u.neigh.refcnt &&
+			    now - entry->u.neigh.lastused > sysctl_arp_timeout)
 			{
 #if RT_CACHE_DEBUG >= 2
 				printk("arp_expire: %08x expired\n", entry->ip);
@@ -700,13 +665,13 @@
 			if (entry->last_updated &&
 			    now - entry->last_updated > sysctl_arp_confirm_interval)
 			{
-				struct device * dev = entry->u.dst.dev;
+				struct device * dev = entry->u.neigh.dev;
 				entry->retries = sysctl_arp_max_tries+sysctl_arp_max_pings;
 				del_timer(&entry->timer);
 				entry->timer.expires = jiffies + ARP_CONFIRM_TIMEOUT;
 				add_timer(&entry->timer);
 				arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip,
-					 dev, dev->pa_addr, entry->ha,
+					 dev, dev->pa_addr, entry->u.neigh.ha,
 					 dev->dev_addr, NULL);
 #if RT_CACHE_DEBUG >= 2
 				printk("arp_expire: %08x requires confirmation\n", entry->ip);
@@ -750,7 +715,7 @@
 
 	if (entry->last_updated && --entry->retries > 0)
 	{
-		struct device *dev = entry->u.dst.dev;
+		struct device *dev = entry->u.neigh.dev;
 
 #if RT_CACHE_DEBUG >= 2
 		printk("arp_expire_request: %08x timed out\n", entry->ip);
@@ -759,7 +724,7 @@
 		entry->timer.expires = jiffies + sysctl_arp_res_time;
 		add_timer(&entry->timer);
 		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr,
-			 entry->retries > sysctl_arp_max_tries ? entry->ha : NULL,
+			 entry->retries > sysctl_arp_max_tries ? entry->u.neigh.ha : NULL,
 			 dev->dev_addr, NULL);
 		return;
 	}
@@ -770,7 +735,7 @@
 
 	arp_purge_send_q(entry);
 
-	if (entry->u.dst.refcnt)
+	if (entry->u.neigh.refcnt)
 	{
 		/*
 		 *	The host is dead, but someone refers to it.
@@ -780,7 +745,7 @@
 		 *	to ARP_DEAD_RES_TIME.
 		 */
 
-		struct device *dev = entry->u.dst.dev;
+		struct device *dev = entry->u.neigh.dev;
 #if RT_CACHE_DEBUG >= 2
 		printk("arp_expire_request: %08x is dead\n", entry->ip);
 #endif
@@ -842,7 +807,9 @@
 		}
 	}
 
-	entry = (struct arp_table *)dst_alloc(sizeof(struct arp_table), &arp_dst_ops);
+	entry = (struct arp_table *)neigh_alloc(sizeof(struct arp_table),
+						&arp_neigh_ops);
+	entry->u.neigh.refcnt = 1;
 
 	if (entry != NULL)
 	{
@@ -854,7 +821,6 @@
 		entry->timer.function = arp_expire_request;
 		entry->timer.data = (unsigned long)entry;
 		entry->last_updated = jiffies;
-		skb_queue_head_init(&entry->skb);
 	}
 	return entry;
 }
@@ -887,7 +853,7 @@
 
 		while ((entry = *pentry) != NULL)
 		{
-			if (entry->u.dst.dev != dev)
+			if (entry->u.neigh.dev != dev)
 			{
 				pentry = &entry->u.next;
 				continue;
@@ -912,7 +878,7 @@
 
 	ASSERT_BH();
 
-	while((skb = skb_dequeue(&entry->skb)) != NULL)	{
+	while((skb = skb_dequeue(&entry->u.neigh.arp_queue)) != NULL) {
 		dev_queue_xmit(skb);
 	}
 }
@@ -934,7 +900,7 @@
 	hash = HASH(sip);
 
 	for (entry=arp_tables[hash]; entry; entry = entry->u.next)
-		if (entry->ip == sip && entry->u.dst.dev == dev)
+		if (entry->ip == sip && entry->u.neigh.dev == dev)
 			break;
 
 	if (entry)
@@ -946,9 +912,9 @@
 		{
 			del_timer(&entry->timer);
 			entry->last_updated = updated;
-			if (memcmp(entry->ha, sha, dev->addr_len) != 0)
+			if (memcmp(entry->u.neigh.ha, sha, dev->addr_len) != 0)
 			{
-				memcpy(entry->ha, sha, dev->addr_len);
+				memcpy(entry->u.neigh.ha, sha, dev->addr_len);
 				if (entry->flags & ATF_COM)
 					arp_update_hhs(entry);
 			}
@@ -982,14 +948,14 @@
 
 	entry->ip = sip;
 	entry->flags = ATF_COM;
-	memcpy(entry->ha, sha, dev->addr_len);
-	entry->u.dst.dev = dev;
+	memcpy(entry->u.neigh.ha, sha, dev->addr_len);
+	entry->u.neigh.dev = dev;
 	entry->hatype = dev->type;
 	entry->last_updated = updated;
 
 	entry->u.next = arp_tables[hash];
 	arp_tables[hash] = entry;
-	dst_release(&entry->u.dst);
+	neigh_release(&entry->u.neigh);
 	return 0;
 }
 
@@ -1000,7 +966,7 @@
 	struct arp_table *entry;
 
 	for (entry = arp_tables[HASH(paddr)]; entry != NULL; entry = entry->u.next)
-		if (entry->ip == paddr && entry->u.dst.dev == dev)
+		if (entry->ip == paddr && entry->u.neigh.dev == dev)
 			return entry;
 	return NULL;
 }
@@ -1043,7 +1009,7 @@
 
 static void arp_start_resolution(struct arp_table *entry)
 {
-	struct device * dev = entry->u.dst.dev;
+	struct device * dev = entry->u.neigh.dev;
 
 	del_timer(&entry->timer);
 	entry->timer.expires = jiffies + sysctl_arp_res_time;
@@ -1072,17 +1038,17 @@
 	if (entry != NULL)
 	{
 		entry->ip = paddr;
-		entry->u.dst.dev = dev;
+		entry->u.neigh.dev = dev;
 		entry->hatype = dev->type;
 
 		if (skb != NULL)
-			skb_queue_tail(&entry->skb, skb);
+			skb_queue_tail(&entry->u.neigh.arp_queue, skb);
 
 		atomic_inc(&arp_unres_size);
 		entry->u.next = arp_tables[hash];
 		arp_tables[hash] = entry;
 		arp_start_resolution(entry);
-		dst_release(&entry->u.dst);
+		neigh_release(&entry->u.neigh);
 	}
 	return entry;
 }
@@ -1125,8 +1091,8 @@
 	{
 		if (entry->flags & ATF_COM)
 		{
-			entry->u.dst.lastuse = jiffies;
-			memcpy(haddr, entry->ha, dev->addr_len);
+			entry->u.neigh.lastused = jiffies;
+			memcpy(haddr, entry->u.neigh.ha, dev->addr_len);
 			if (skb)
 				skb->arp = 1;
 			end_bh_atomic();
@@ -1142,8 +1108,8 @@
 		{
 			if (entry->last_updated)
 			{
-				if (entry->skb.qlen < ARP_MAX_UNRES_PACKETS)
-					skb_queue_tail(&entry->skb, skb);
+				if (entry->u.neigh.arp_queue.qlen < ARP_MAX_UNRES_PACKETS)
+					skb_queue_tail(&entry->u.neigh.arp_queue, skb);
 				else
 					kfree_skb(skb, FREE_WRITE);
 			}
@@ -1171,7 +1137,7 @@
 }
 
 int arp_find_1(unsigned char *haddr, struct dst_entry *dst,
-	       struct dst_entry *neigh)
+	       struct neighbour *neigh)
 {
 	struct rtable *rt = (struct rtable*)dst;
 	struct device *dev = dst->dev;
@@ -1218,8 +1184,8 @@
 
 	if (entry->flags & ATF_COM)
 	{
-		entry->u.dst.lastuse = jiffies;
-		memcpy(haddr, entry->ha, dev->addr_len);
+		entry->u.neigh.lastused = jiffies;
+		memcpy(haddr, entry->u.neigh.ha, dev->addr_len);
 		end_bh_atomic();
 		return 1;
 	}
@@ -1229,7 +1195,7 @@
 }
 
 
-struct dst_entry* arp_find_neighbour(struct dst_entry *dst, int resolve)
+struct neighbour* arp_find_neighbour(struct dst_entry *dst, int resolve)
 {
 	struct rtable *rt = (struct rtable*)dst;
 	struct device *dev = rt->u.dst.dev;
@@ -1255,10 +1221,10 @@
 
 	if (entry != NULL) 	/* It exists */
 	{
-		atomic_inc(&entry->u.dst.refcnt);
+		atomic_inc(&entry->u.neigh.refcnt);
 		end_bh_atomic();
-		entry->u.dst.lastuse = jiffies;
-		return (struct dst_entry*)entry;
+		entry->u.neigh.lastused = jiffies;
+		return (struct neighbour*)entry;
 	}
 
 	if (!resolve)
@@ -1267,11 +1233,11 @@
 	entry = arp_new_entry(paddr, dev, NULL);
 
 	if (entry)
-		atomic_inc(&entry->u.dst.refcnt);
+		atomic_inc(&entry->u.neigh.refcnt);
 
 	end_bh_atomic();
 
-	return (struct dst_entry*)entry;
+	return (struct neighbour*)entry;
 }
 
 /*
@@ -1526,14 +1492,14 @@
 
 		for (entry = arp_proxy_list; entry; entry = entry->u.next) {
 			if (!((entry->ip^tip)&entry->mask) &&
-			    ((!entry->u.dst.dev &&
+			    ((!entry->u.neigh.dev &&
 			      (!(entry->flags & ATF_COM) || entry->hatype == dev->type))
-			     || entry->u.dst.dev == dev) )
+			     || entry->u.neigh.dev == dev) )
 				break;
 		}
 
 		if (entry && !(entry->flags & ATF_DONTPUB)) {
-			char *ha = (entry->flags & ATF_COM) ? entry->ha : dev->dev_addr;
+			char *ha = (entry->flags & ATF_COM) ? entry->u.neigh.ha : dev->dev_addr;
 
 			if (rt->rt_flags&(RTF_LOCAL|RTF_NAT) ||
 			    (!(rt->rt_flags&RTCF_DOREDIRECT) &&
@@ -1651,7 +1617,7 @@
 	while ((entry = *entryp) != NULL && entry->mask == mask &&
 	       entry->ip == ip)
 	{
-		if (!entry->u.dst.dev || entry->u.dst.dev == dev)
+		if (!entry->u.neigh.dev || entry->u.neigh.dev == dev)
 			break;
 		entryp = &entry->u.next;
 	}
@@ -1659,7 +1625,7 @@
 	while ((entry = *entryp) != NULL)
 	{
 		if (entry->ip != ip || entry->mask != mask ||
-		    entry->u.dst.dev != dev)
+		    entry->u.neigh.dev != dev)
 		{
 			entry = NULL;
 			break;
@@ -1672,7 +1638,7 @@
 	}
 
 	if (entry)
-		atomic_inc(&entry->u.dst.refcnt);
+		atomic_inc(&entry->u.neigh.refcnt);
 	else
 	{
 		entry = arp_alloc(r->arp_flags&ATF_PUBL ? 0 : 1);
@@ -1682,7 +1648,7 @@
 			return -ENOMEM;
 		}
 		entry->ip = ip;
-		entry->u.dst.dev = dev;
+		entry->u.neigh.dev = dev;
 		entry->mask = mask;
 
 		if (dev)
@@ -1709,24 +1675,24 @@
 		ha = r->arp_ha.sa_data;
 
 	if (ha)
-		memcpy(entry->ha, ha, dev ? dev->addr_len : MAX_ADDR_LEN);
+		memcpy(entry->u.neigh.ha, ha, dev ? dev->addr_len : MAX_ADDR_LEN);
 	else
-		memset(entry->ha, 0, MAX_ADDR_LEN);
+		memset(entry->u.neigh.ha, 0, MAX_ADDR_LEN);
 
-	entry->last_updated = entry->u.dst.lastuse = jiffies;
+	entry->last_updated = entry->u.neigh.lastused = jiffies;
 	
 	if (!(entry->flags & ATF_PUBL))
 	{
 		if (entry->flags & ATF_COM)
 		{
-			arpd_update(entry->ip, entry->u.dst.dev, ha);
+			arpd_update(entry->ip, entry->u.neigh.dev, ha);
 			arp_update_hhs(entry);
 		}
 		else
 			arp_start_resolution(entry);
 	}
 
-	dst_release(&entry->u.dst);
+	neigh_release(&entry->u.neigh);
 	end_bh_atomic();
 	return 0;
 }
@@ -1761,14 +1727,14 @@
 		if (entry->ip == si->sin_addr.s_addr &&
 		    (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask) &&
 		    ( (r->arp_flags&ATF_PUBL) ?
-		      (entry->u.dst.dev == dev && entry->hatype == r->arp_ha.sa_family)
-		     : (entry->u.dst.dev == dev || !dev)))
+		      (entry->u.neigh.dev == dev && entry->hatype == r->arp_ha.sa_family)
+		     : (entry->u.neigh.dev == dev || !dev)))
 		{
-			if (entry->u.dst.dev)
+			if (entry->u.neigh.dev)
 			{
-				memcpy(r->arp_ha.sa_data, entry->ha, entry->u.dst.dev->addr_len);
-				r->arp_ha.sa_family = entry->u.dst.dev->type;
-				strncpy(r->arp_dev, entry->u.dst.dev->name, sizeof(r->arp_dev));
+				memcpy(r->arp_ha.sa_data, entry->u.neigh.ha, entry->u.neigh.dev->addr_len);
+				r->arp_ha.sa_family = entry->u.neigh.dev->type;
+				strncpy(r->arp_dev, entry->u.neigh.dev->name, sizeof(r->arp_dev));
 			}
 			else
 			{
@@ -1811,10 +1777,10 @@
 	{
 		if (entry->ip == si->sin_addr.s_addr
 		    && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask)
-		    && (entry->u.dst.dev == dev || (!(r->arp_flags&ATF_PUBL) && !dev))
+		    && (entry->u.neigh.dev == dev || (!(r->arp_flags&ATF_PUBL) && !dev))
 		    && (!(r->arp_flags&ATF_MAGIC) || r->arp_flags == entry->flags))
 		{
-			if (!entry->u.dst.refcnt)
+			if (!entry->u.neigh.refcnt)
 			{
 				arp_free(entryp);
 				retval = 0;
@@ -1971,21 +1937,21 @@
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 			if (entry->hatype == ARPHRD_AX25 || entry->hatype == ARPHRD_NETROM)
-			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
+			     strcpy(hbuffer,ax2asc((ax25_address *)entry->u.neigh.ha));
 			else {
 #else
 			if(entry->hatype==ARPHRD_AX25)
-			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
+			     strcpy(hbuffer,ax2asc((ax25_address *)entry->u.neigh.ha));
 			else {
 #endif
 #endif
 				
-			if (entry->u.dst.dev)
+			if (entry->u.neigh.dev)
 			{
-				for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->u.dst.dev->addr_len;j++)
+				for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->u.neigh.dev->addr_len;j++)
 				{
-					hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ];
-					hbuffer[k++]=hexbuf[  entry->ha[j]&15     ];
+					hbuffer[k++]=hexbuf[ (entry->u.neigh.ha[j]>>4)&15 ];
+					hbuffer[k++]=hexbuf[  entry->u.neigh.ha[j]&15     ];
 					hbuffer[k++]=':';
 				}
 				hbuffer[--k]=0;
@@ -2008,16 +1974,16 @@
 				 "     %-17s %s\n",
 				 entry->mask==DEF_ARP_NETMASK ?
 				 "*" : in_ntoa(entry->mask),
-					entry->u.dst.dev ? entry->u.dst.dev->name : "*");
+					entry->u.neigh.dev ? entry->u.neigh.dev->name : "*");
 #else
 			size += sprintf(buffer+len+size,
 				 "     %-17s %s\t%d\t%d\t%1d\n",
 				 entry->mask==DEF_ARP_NETMASK ?
 				 "*" : in_ntoa(entry->mask),
-				 entry->u.dst.dev ? entry->u.dst.dev->name : "*", 
-				 entry->u.dst.refcnt,
-				 entry->u.dst.hh ? entry->u.dst.hh->hh_refcnt : -1,
-				 entry->u.dst.hh ? entry->u.dst.hh->hh_uptodate : 0);
+				 entry->u.neigh.dev ? entry->u.neigh.dev->name : "*", 
+				 entry->u.neigh.refcnt,
+				 entry->u.neigh.hh ? entry->u.neigh.hh->hh_refcnt : -1,
+				 entry->u.neigh.hh ? entry->u.neigh.hh->hh_uptodate : 0);
 #endif
 	
 			len += size;
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v2.1.29/linux/net/ipv4/icmp.c	Tue Mar  4 10:25:26 1997
+++ linux/net/ipv4/icmp.c	Thu Mar 20 18:17:14 1997
@@ -687,6 +687,16 @@
 	unsigned char *dp;
 	struct sock *raw_sk;
 	
+	/*
+	 *	Incomplete header ?
+	 */
+	 
+	if(skb->len<sizeof(struct iphdr)+8)
+	{
+		kfree_skb(skb, FREE_READ);
+		return;
+	}
+	
 	iph = (struct iphdr *) (icmph + 1);
 	dp = (unsigned char*)iph;
 	
@@ -733,15 +743,32 @@
 	 *	RFC 1122: 3.2.2.1 MUST pass ICMP unreach messages to the transport layer.
 	 *	RFC 1122: 3.2.2.2 MUST pass ICMP time expired messages to transport layer.
 	 */
+	 
+	/*
+	 *	Check the other end isnt violating RFC 1122. Some routers send
+	 *	bogus responses to broadcast frames. If you see this message
+	 *	first check your netmask matches at both ends, if it does then
+	 *	get the other vendor to fix their kit.
+	 */
+	 
+	if(__ip_chk_addr(iph->daddr)==IS_BROADCAST)
+	{
+		printk("%s sent an invalid ICMP error to a broadcast.\n",
+			in_ntoa(iph->daddr));
+		kfree_skb(skb, FREE_READ);
+	}
 
-	/* Deliver ICMP message to raw sockets. Pretty useless feature?
+	/*
+	 *	Deliver ICMP message to raw sockets. Pretty useless feature?
 	 */
 
 	/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
 	hash = iph->protocol & (MAX_INET_PROTOS - 1);
-	if ((raw_sk = raw_v4_htable[hash]) != NULL) {
+	if ((raw_sk = raw_v4_htable[hash]) != NULL) 
+	{
 		raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr, iph->daddr);
-		while (raw_sk) {
+		while (raw_sk) 
+		{
 			raw_err(raw_sk, skb);
 			raw_sk = raw_v4_lookup(raw_sk->next, iph->protocol,
 					       iph->saddr, iph->daddr);
@@ -750,8 +777,6 @@
 
 	/*
 	 *	This can't change while we are doing it. 
-	 *
-	 *	FIXME: Deliver to appropriate raw sockets too.
 	 */
 
 	ipprot = (struct inet_protocol *) inet_protos[hash];
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v2.1.29/linux/net/ipv4/ip_forward.c	Sun Feb  2 05:18:49 1997
+++ linux/net/ipv4/ip_forward.c	Thu Mar 20 18:17:14 1997
@@ -71,6 +71,7 @@
 	struct iphdr *iph;	/* Our header */
 	struct rtable *rt;	/* Route we use */
 	struct ip_options * opt	= &(IPCB(skb)->opt);
+	unsigned short mtu;
 #if defined(CONFIG_FIREWALL) || defined(CONFIG_IP_MASQUERADE)
 	int fw_res = 0;
 #endif
@@ -118,7 +119,12 @@
 
 	skb->priority = rt->u.dst.priority;
 	dev2 = rt->u.dst.dev;
+	mtu = dev2->mtu;
 
+#ifdef CONFIG_NET_SECURITY
+	call_fw_firewall(PF_SECURITY, dev2, NULL, &mtu, NULL);
+#endif	
+	
 	/*
 	 *	In IP you never have to forward a frame on the interface that it 
 	 *	arrived upon. We now generate an ICMP HOST REDIRECT giving the route
@@ -133,9 +139,9 @@
 	 */
 
 	if (dev2->flags & IFF_UP) {
-		if (skb->len > dev2->mtu && (ntohs(iph->frag_off) & IP_DF)) {
+		if (skb->len > mtu && (ntohs(iph->frag_off) & IP_DF)) {
 			ip_statistics.IpFragFails++;
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dev2->mtu));
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
 			kfree_skb(skb, FREE_WRITE);
 			return -1;
 		}
@@ -175,7 +181,7 @@
 				goto skip_call_fw_firewall;
 #endif
 #ifdef CONFIG_FIREWALL
-		fw_res=call_fw_firewall(PF_INET, dev2, iph, NULL);
+		fw_res=call_fw_firewall(PF_INET, dev2, iph, NULL, &skb);
 		switch (fw_res) {
 		case FW_ACCEPT:
 		case FW_MASQUERADE:
@@ -220,7 +226,7 @@
 		}
 
 #ifdef CONFIG_FIREWALL
-		if ((fw_res = call_out_firewall(PF_INET, dev2, iph, NULL)) < FW_ACCEPT) {
+		if ((fw_res = call_out_firewall(PF_INET, dev2, iph, NULL,&skb)) < FW_ACCEPT) {
 			/* FW_ACCEPT and FW_MASQUERADE are treated equal:
 			   masquerading is only supported via forward rules */
 			if (fw_res == FW_REJECT)
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.1.29/linux/net/ipv4/ip_fragment.c	Thu Dec 12 06:54:24 1996
+++ linux/net/ipv4/ip_fragment.c	Thu Mar 20 18:17:14 1997
@@ -562,7 +562,7 @@
 			else
 				qp->fragments = tmp->next;
 
-			if (tfp->next != NULL)
+			if (tmp->next != NULL)
 				tmp->next->prev = tmp->prev;
 			
 			next=tfp;	/* We have killed the original next frame */
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v2.1.29/linux/net/ipv4/ip_fw.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ipv4/ip_fw.c	Thu Mar 20 18:17:14 1997
@@ -1196,17 +1196,17 @@
  *	Interface to the generic firewall chains.
  */
  
-int ipfw_input_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr, void *arg)
+int ipfw_input_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **pskb)
 {
 	return ip_fw_chk(phdr, dev, arg, ip_fw_in_chain, ip_fw_in_policy, IP_FW_MODE_FW);
 }
 
-int ipfw_output_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr, void *arg)
+int ipfw_output_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **pskb)
 {
 	return ip_fw_chk(phdr, dev, arg, ip_fw_out_chain, ip_fw_out_policy, IP_FW_MODE_FW);
 }
 
-int ipfw_forward_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr, void *arg)
+int ipfw_forward_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **pskb)
 {
 	return ip_fw_chk(phdr, dev, arg, ip_fw_fwd_chain, ip_fw_fwd_policy, IP_FW_MODE_FW);
 }
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.1.29/linux/net/ipv4/ip_input.c	Tue Mar  4 10:25:26 1997
+++ linux/net/ipv4/ip_input.c	Thu Mar 20 18:17:14 1997
@@ -155,6 +155,7 @@
 #include <linux/mroute.h>
 #include <net/netlink.h>
 #include <linux/net_alias.h>
+#include <linux/ipsec.h>
 
 /*
  *	SNMP management statistics
@@ -266,7 +267,12 @@
 				else
 					break;	/* One pending raw socket left */
 				if(skb1)
-					raw_rcv(raw_sk, skb1);
+				{
+					if(ipsec_sk_policy(raw_sk,skb1))	
+						raw_rcv(raw_sk, skb1);
+					else
+						kfree_skb(skb1, FREE_WRITE);
+				}
 				raw_sk = sknext;
 			} while(raw_sk!=NULL);
 				
@@ -323,7 +329,12 @@
 	 */
 
 	if(raw_sk!=NULL)	/* Shift to last raw user */
-		raw_rcv(raw_sk, skb);
+	{
+		if(ipsec_sk_policy(raw_sk, skb))
+			raw_rcv(raw_sk, skb);
+		else
+			kfree_skb(skb, FREE_WRITE);
+	}
 	else if (!flag)		/* Free and report errors */
 	{
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);	
@@ -434,7 +445,7 @@
 		int fwres;
 		u16 rport;
 
-		if ((fwres=call_in_firewall(PF_INET, skb->dev, iph, &rport))<FW_ACCEPT) {
+		if ((fwres=call_in_firewall(PF_INET, skb->dev, iph, &rport, &skb))<FW_ACCEPT) {
 			if (fwres==FW_REJECT)
 				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 			goto drop;
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/ip_masq_quake.c linux/net/ipv4/ip_masq_quake.c
--- v2.1.29/linux/net/ipv4/ip_masq_quake.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ipv4/ip_masq_quake.c	Thu Mar 20 18:17:14 1997
@@ -0,0 +1,316 @@
+/*
+ *		IP_MASQ_QUAKE quake masquerading module
+ *
+ *
+ * Version:	@(#)ip_masq_quake.c 0.02   22/02/97
+ *
+ * Author:	Harald Hoyer mailto:HarryH@Royal.Net
+ *		
+ *
+ * Fixes: 
+ *      Harald Hoyer            :       Unofficial Quake Specs found at 
+ *                                 http://www.gamers.org/dEngine/quake/spec/ 
+ *      Harald Hoyer            :       Check for QUAKE-STRING
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *  
+ *  
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <net/protocol.h>
+#include <net/udp.h>
+#include <net/ip_masq.h>
+
+#define DEBUG_CONFIG_IP_MASQ_QUAKE 0
+
+typedef struct
+{ 
+        __u16 type;     // (Little Endian) Type of message.
+	__u16 length;   // (Little Endian) Length of message, header included. 
+	char  message[0];  // The contents of the message.
+} QUAKEHEADER;
+
+struct quake_priv_data {
+	/* Have we seen a client connect message */
+	char	cl_connect;
+};
+
+static int
+masq_quake_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+        MOD_INC_USE_COUNT;
+	if ((ms->app_data = kmalloc(sizeof(struct quake_priv_data),
+				    GFP_ATOMIC)) == NULL) 
+		printk(KERN_INFO "Quake: No memory for application data\n");
+	else 
+	{
+		struct quake_priv_data *priv = 
+			(struct quake_priv_data *)ms->app_data;
+		priv->cl_connect = 0;
+	}
+        return 0;
+}
+
+static int
+masq_quake_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+	MOD_DEC_USE_COUNT;
+	if (ms->app_data)
+		kfree_s(ms->app_data, sizeof(struct quake_priv_data));
+	return 0;
+}
+
+int
+masq_quake_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, struct device *dev)
+{
+	struct sk_buff *skb;
+	struct iphdr *iph;
+	struct udphdr *uh;
+	QUAKEHEADER *qh;
+	__u16 udp_port;
+	char *data;
+	unsigned char code;
+	struct quake_priv_data *priv = (struct quake_priv_data *)ms->app_data;
+        
+	if(priv->cl_connect == -1)
+	  return 0;
+
+	skb = *skb_p;
+
+	iph = skb->nh.iph;
+	uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
+
+	/* Check for lenght */
+	if(ntohs(uh->len) < 5)
+	  return 0;
+	
+	qh = (QUAKEHEADER *)&uh[1];
+
+	if(qh->type != 0x0080)
+	  return 0;
+
+	
+	code = qh->message[0];
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+	  printk("Quake_in: code = %d \n", (int)code);
+#endif
+
+	switch(code) {
+	case 0x01:
+	  /* Connection Request */
+
+	  if(ntohs(qh->length) < 0x0c) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+	    printk("Quake_in: length < 0xc \n");
+#endif
+	    return 0;
+	  }
+
+	  data = &qh->message[1];
+
+	  /* Check for stomping string */
+	  if(memcmp(data,"QUAKE\0\3",7)) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+	    printk("Quake_out: memcmp failed \n");
+#endif
+	    return 0;
+	  }
+	  else {
+	    priv->cl_connect = 1;
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+	    printk("Quake_out: memcmp ok \n");
+#endif
+	  }
+	  break;
+
+	case 0x81:
+	  /* Accept Connection */
+	  if((ntohs(qh->length) < 0x09) || (priv->cl_connect == 0))
+	    return 0;
+	  data = &qh->message[1];
+
+	  memcpy(&udp_port, data, 2);
+
+	  ms->dport = htons(udp_port);
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+	  printk("Quake_in: in_rewrote UDP port %d \n", udp_port);
+#endif
+	  priv->cl_connect = -1;
+
+	  break;
+	}
+	 
+	return 0;
+}
+
+int
+masq_quake_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, struct device *dev)
+{
+	struct sk_buff *skb;
+	struct iphdr *iph;
+	struct udphdr *uh;
+	QUAKEHEADER *qh;
+	__u16 udp_port;
+	char *data;
+	unsigned char code;
+	struct ip_masq *n_ms;
+	struct quake_priv_data *priv = (struct quake_priv_data *)ms->app_data;
+
+	if(priv->cl_connect == -1)
+	  return 0;
+        
+	skb = *skb_p;
+
+	iph = skb->nh.iph;
+	uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
+
+	/* Check for lenght */
+	if(ntohs(uh->len) < 5)
+	  return 0;
+	
+	qh = (QUAKEHEADER *)&uh[1];
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+	  printk("Quake_out: qh->type = %d \n", (int)qh->type);
+#endif
+
+	if(qh->type != 0x0080)
+	  return 0;
+	
+	code = qh->message[0];
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+	  printk("Quake_out: code = %d \n", (int)code);
+#endif
+
+	switch(code) {
+	case 0x01:
+	  /* Connection Request */
+
+	  if(ntohs(qh->length) < 0x0c) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+	    printk("Quake_out: length < 0xc \n");
+#endif
+	    return 0;
+	  }
+
+	  data = &qh->message[1];
+
+	  /* Check for stomping string */
+	  if(memcmp(data,"QUAKE\0\3",7)) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+	    printk("Quake_out: memcmp failed \n");
+#endif
+	    return 0;
+	  }
+	  else {
+	    priv->cl_connect = 1;
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+	    printk("Quake_out: memcmp ok \n");
+#endif
+	  }
+	  break;
+
+	case 0x81:
+	  /* Accept Connection */
+	  if((ntohs(qh->length) < 0x09) || (priv->cl_connect == 0))
+	    return 0;
+
+	  data = &qh->message[1];
+
+	  memcpy(&udp_port, data, 2);
+	  
+	  n_ms = ip_masq_new(dev, IPPROTO_UDP,
+			     ms->saddr, htons(udp_port),
+			     ms->daddr, ms->dport,
+			     0);
+
+	  if (n_ms==NULL)
+	    return 0;
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+	  printk("Quake_out: out_rewrote UDP port %d -> %d\n",
+		 udp_port, ntohs(n_ms->mport));
+#endif
+	  udp_port = ntohs(n_ms->mport);
+	  memcpy(data, &udp_port, 2);
+
+	  break;
+	}
+	 
+	return 0;
+}
+
+struct ip_masq_app ip_masq_quake = {
+        NULL,			/* next */
+	"Quake_26",	       	/* name */
+        0,                      /* type */
+        0,                      /* n_attach */
+        masq_quake_init_1,      /* ip_masq_init_1 */
+        masq_quake_done_1,      /* ip_masq_done_1 */
+        masq_quake_out,         /* pkt_out */
+        masq_quake_in           /* pkt_in */
+};
+struct ip_masq_app ip_masq_quakenew = {
+        NULL,			/* next */
+	"Quake_27",	       	/* name */
+        0,                      /* type */
+        0,                      /* n_attach */
+        masq_quake_init_1,      /* ip_masq_init_1 */
+        masq_quake_done_1,      /* ip_masq_done_1 */
+        masq_quake_out,         /* pkt_out */
+        masq_quake_in           /* pkt_in */
+};
+
+/*
+ * 	ip_masq_quake initialization
+ */
+
+int ip_masq_quake_init(void)
+{
+        return (register_ip_masq_app(&ip_masq_quake, IPPROTO_UDP, 26000) +
+		register_ip_masq_app(&ip_masq_quakenew, IPPROTO_UDP, 27000));
+}
+
+/*
+ * 	ip_masq_quake fin.
+ */
+
+int ip_masq_quake_done(void)
+{
+        return (unregister_ip_masq_app(&ip_masq_quake) +
+                unregister_ip_masq_app(&ip_masq_quakenew));
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+        if (ip_masq_quake_init() != 0)
+                return -EIO;
+        return 0;
+}
+
+void cleanup_module(void)
+{
+        if (ip_masq_quake_done() != 0)
+                printk("ip_masq_quake: can't remove module");
+}
+
+#endif /* MODULE */
+
+
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.1.29/linux/net/ipv4/ip_output.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ipv4/ip_output.c	Thu Mar 20 18:17:14 1997
@@ -65,6 +65,7 @@
 #include <linux/firewall.h>
 #include <linux/mroute.h>
 #include <net/netlink.h>
+#include <linux/ipsec.h>
 
 static void __inline__ ip_ll_header_reserve(struct sk_buff *skb)
 {
@@ -356,11 +357,27 @@
 	iph->tot_len = htons(tot_len);
 	iph->id = htons(ip_id_count++);
 
-#ifdef CONFIG_FIREWALL
-	if (call_out_firewall(PF_INET, dev, iph, NULL) < FW_ACCEPT) {
+	if (call_out_firewall(PF_INET, dev, iph, NULL,&skb) < FW_ACCEPT) {
 		kfree_skb(skb, FREE_WRITE);
 		return;
 	}
+	
+#ifdef CONFIG_NET_SECURITY	
+	/*
+	 *	Add an IP checksum (must do this before SECurity because
+	 *	of possible tunneling)
+	 */
+
+	ip_send_check(iph);
+
+	if (call_out_firewall(PF_SECURITY, NULL, NULL, (void *) 4, &skb)<FW_ACCEPT)
+	{
+		kfree_skb(skb, FREE_WRITE);
+		return;
+	}
+	
+	iph = skb->nh.iph;
+	/* don't update tot_len, as the dev->mtu is already decreased */	
 #endif
 
 	if (skb_headroom(skb) < dev->hard_header_len && dev->hard_header) {
@@ -458,7 +475,9 @@
 	struct ip_options *opt = ipc->opt;
 	struct device *dev = rt->u.dst.dev;
 	int df = htons(IP_DF);
-	
+#ifdef CONFIG_NET_SECURITY
+	int fw_res;
+#endif	
 
 	if (sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
 	    (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
@@ -517,9 +536,17 @@
 		if (err)
 			err = -EFAULT;
 
-#ifdef CONFIG_FIREWALL
-		if(!err && call_out_firewall(PF_INET, skb->dev, iph, NULL) < FW_ACCEPT)
+		if(!err && call_out_firewall(PF_INET, skb->dev, iph, NULL, &skb) < FW_ACCEPT)
 			err = -EPERM;
+#ifdef CONFIG_NET_SECURITY
+		if ((fw_res=call_out_firewall(PF_SECURITY, NULL, NULL, (void *) 5, &skb))<FW_ACCEPT)
+		{
+			kfree_skb(skb, FREE_WRITE);
+			if (fw_res != FW_QUEUE)
+				return -EPERM;
+			else
+				return 0;
+		}
 #endif
 
 		if (err)
@@ -679,9 +706,14 @@
 		 *	Account for the fragment.
 		 */
 		 
-#ifdef CONFIG_FIREWALL
-		if(!err && !offset && call_out_firewall(PF_INET, skb->dev, iph, NULL) < FW_ACCEPT)
+		if(!err && !offset && call_out_firewall(PF_INET, skb->dev, iph, NULL, &skb) < FW_ACCEPT)
 			err = -EPERM;
+#ifdef CONFIG_NET_SECURITY
+		if ((fw_res=call_out_firewall(PF_SECURITY, NULL, NULL, (void *) 6, &skb))<FW_ACCEPT)
+		{
+			if (fw_res != FW_QUEUE)
+				err= -EPERM;
+		}
 #endif		
 		if (err)
  		{
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v2.1.29/linux/net/ipv4/ipip.c	Thu Dec 12 06:54:24 1996
+++ linux/net/ipv4/ipip.c	Thu Mar 20 18:17:14 1997
@@ -29,7 +29,6 @@
 #include <linux/in.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
-#include <linux/firewall.h>
 #include <linux/if_arp.h>
 #include <linux/mroute.h>
 
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/proc.c linux/net/ipv4/proc.c
--- v2.1.29/linux/net/ipv4/proc.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv4/proc.c	Thu Mar 20 18:17:14 1997
@@ -79,7 +79,7 @@
  *	a memory timer destroy. Instead of playing with timers we just
  *	concede defeat and cli().
  */
-	start_bh_atomic();
+	SOCKHASH_LOCK();
 	sp = pro->sklist_next;
 	while(sp != (struct sock *)pro) {
 		pos += 128;
@@ -128,7 +128,7 @@
 		sp = sp->sklist_next;
 		i++;
 	}
-	end_bh_atomic();
+	SOCKHASH_UNLOCK();
 
 	begin = len - (pos - offset);
 	*start = buffer + begin;
@@ -138,24 +138,20 @@
 	return len;
 } 
 
-
 int tcp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
 	return get__netinfo(&tcp_prot, buffer,0, start, offset, length);
 }
 
-
 int udp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
 	return get__netinfo(&udp_prot, buffer,1, start, offset, length);
 }
 
-
 int raw_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
 	return get__netinfo(&raw_prot, buffer,1, start, offset, length);
 }
-
 
 /*
  *	Report socket allocation statistics [mea@utu.fi]
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v2.1.29/linux/net/ipv4/raw.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv4/raw.c	Thu Mar 20 18:17:14 1997
@@ -151,7 +151,7 @@
 	int type = skb->h.icmph->type;
 	int code = skb->h.icmph->code;
 
-	if (sk->ip_recverr && !sk->users) {
+	if (sk->ip_recverr && !sk->sock_readers) {
 		struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
 		if (skb2 && sock_queue_err_skb(sk, skb2))
 			kfree_skb(skb, FREE_READ);
@@ -193,7 +193,7 @@
 	
 	skb->h.raw = skb->nh.raw;
 
-	if (sk->users) {
+	if (sk->sock_readers) {
 		__skb_queue_tail(&sk->back_log, skb);
 		return 0;
 	}
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.1.29/linux/net/ipv4/route.c	Sun Jan 19 05:47:28 1997
+++ linux/net/ipv4/route.c	Thu Mar 20 18:17:14 1997
@@ -93,8 +93,9 @@
  */
 
 static void ipv4_dst_destroy(struct dst_entry * dst);
-static struct dst_entry * ipv4_dst_check(struct dst_entry * dst);
-static struct dst_entry * ipv4_dst_reroute(struct dst_entry * dst);
+static struct dst_entry * ipv4_dst_check(struct dst_entry * dst, u32);
+static struct dst_entry * ipv4_dst_reroute(struct dst_entry * dst,
+					   struct sk_buff *);
 
 
 struct dst_ops ipv4_dst_ops =
@@ -212,7 +213,7 @@
 			 * Cleanup aged off entries.
 			 */
 
-			if (!rth->u.dst.refcnt && now - rth->u.dst.lastuse > RT_CACHE_TIMEOUT) {
+			if (!rth->u.dst.use && now - rth->u.dst.lastuse > RT_CACHE_TIMEOUT) {
 				*rthp = rth_next;
 				atomic_dec(&rt_cache_size);
 #if RT_CACHE_DEBUG >= 2
@@ -337,7 +338,7 @@
 		if (!rt_hash_table[i])
 			continue;
 		for (rthp=&rt_hash_table[i]; (rth=*rthp); rthp=&rth->u.rt_next)	{
-			if (rth->u.dst.refcnt || now - rth->u.dst.lastuse > expire)
+			if (rth->u.dst.use || now - rth->u.dst.lastuse > expire)
 				continue;
 			atomic_dec(&rt_cache_size);
 			*rthp = rth->u.rt_next;
@@ -357,7 +358,7 @@
 
 static int rt_ll_bind(struct rtable *rt)
 {
-	struct dst_entry *neigh;
+	struct neighbour *neigh;
 	struct hh_cache	*hh = NULL;
 
 	if (rt->u.dst.dev && rt->u.dst.dev->hard_header_cache) {
@@ -754,12 +755,13 @@
 	}
 }
 
-static struct dst_entry * ipv4_dst_check(struct dst_entry * dst)
+static struct dst_entry * ipv4_dst_check(struct dst_entry * dst, u32 cookie)
 {
 	return NULL;
 }
 
-static struct dst_entry * ipv4_dst_reroute(struct dst_entry * dst)
+static struct dst_entry * ipv4_dst_reroute(struct dst_entry * dst,
+					   struct sk_buff *skb)
 {
 	return NULL;
 }
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.29/linux/net/ipv4/tcp.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv4/tcp.c	Thu Mar 20 18:17:14 1997
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	@(#)tcp.c	1.0.16	05/25/93
+ * Version:	$Id: tcp.c,v 1.50 1997/03/16 03:25:59 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -196,6 +196,8 @@
  *					improvement.
  *	Stefan Magdalinski	:	adjusted tcp_readable() to fix FIONREAD
  *	Willy Konynenberg	:	Transparent proxying support.
+ *		Keith Owens	:	Do proper meging with partial SKB's in
+ *					tcp_do_sendmsg to avoid burstiness.
  *					
  * To Fix:
  *		Fast path the code. Two things here - fix the window calculation
@@ -525,6 +527,7 @@
 	unsigned long flags;
 
 	SOCK_DEBUG(sk, "tcp_readable: %p - ",sk);
+
 	save_flags(flags);
 	cli();
 	if (sk == NULL || (skb = skb_peek(&sk->receive_queue)) == NULL)
@@ -772,11 +775,11 @@
 
 	copy = min(sk->mss - tcp_size, skb_tailroom(skb));
 	copy = min(copy, seglen);
-	
+
 	tcp_size += copy;
-	
+
 	fault = copy_from_user(skb->tail, from, copy);
-	
+
 	if (fault)
 	{
 		return -1;
@@ -796,8 +799,7 @@
  *	and starts the transmit system.
  */
 
-int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov,
-		   int len, int flags)
+int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags)
 {
 	int err = 0;
 	int copied  = 0;
@@ -808,16 +810,14 @@
 	 */
 	while (sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT)
 	{
-		
 		if (copied)
 			return copied;
-		
-		if (sk->err) 
+
+		if (sk->err)
 			return sock_error(sk);
-		
+
 		if (sk->state != TCP_SYN_SENT && sk->state != TCP_SYN_RECV)
 		{
-			printk("tcp_do_sendmsg1: EPIPE dude...\n");
 			if (sk->keepopen)
 				send_sig(SIGPIPE, current, 0);
 			return -EPIPE;
@@ -831,22 +831,21 @@
 		
 		wait_for_tcp_connect(sk);
 	}
-	
-	
+
 	/*
 	 *	Ok commence sending
 	 */
-	
+
 	while(--iovlen >= 0)
 	{
 		int seglen=iov->iov_len;
 		unsigned char * from=iov->iov_base;
-		u32 actual_win;
 
 		iov++;
 
-		while(seglen > 0) 
+		while(seglen > 0)
 		{
+			unsigned int actual_win;
 			int copy;
 			int tmp;
 			struct sk_buff *skb;
@@ -870,7 +869,6 @@
 			{
 				if (copied)
 					return copied;
-				printk("tcp_do_sendmsg2: SEND_SHUTDOWN, EPIPE...\n");
 				send_sig(SIGPIPE,current,0);
 				return -EPIPE;
 			}
@@ -885,11 +883,11 @@
 				int tcp_size;
 
 				/* Tail */
-				
+
 				skb = sk->write_queue.prev;
-				tcp_size = skb->tail - 
+				tcp_size = skb->tail -
 					(unsigned char *)(skb->h.th + 1);
-					
+
 				/*
 				 * This window_seq test is somewhat dangerous
 				 * If the remote does SWS avoidance we should
@@ -901,7 +899,7 @@
 
 				if (skb->end > skb->tail &&
 				    sk->mss - tcp_size > 0 &&
-				    skb->end_seq < tp->snd_una + tp->snd_wnd) 
+				    tp->snd_nxt < skb->end_seq) 
 				{
 					int tcopy;
 					
@@ -912,12 +910,11 @@
 					{
 						return -EFAULT;
 					}
-					
+
 					from += tcopy;
 					copied += tcopy;
-					len -= tcopy;
 					seglen -= tcopy;
-					
+
 					/*
 					 *	FIXME: if we're nagling we
 					 *	should send here.
@@ -944,7 +941,7 @@
 			actual_win = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
 
 			if (copy > actual_win &&
-			    (((long) actual_win) >= (sk->max_window >> 1))
+			    (((int) actual_win) >= (sk->max_window >> 1))
 			    && actual_win)
 			{
 				copy = actual_win;
@@ -974,12 +971,12 @@
 			}
 
 			skb = sock_wmalloc(sk, tmp, 0, GFP_KERNEL);
-	
+
 			/*
-			 *	If we didn't get any memory, we need to sleep. 
+			 *	If we didn't get any memory, we need to sleep.
 			 */
-	
-			if (skb == NULL) 
+
+			if (skb == NULL)
 			{
 				sk->socket->flags |= SO_NOSPACE;
 				if (flags&MSG_DONTWAIT)
@@ -1037,13 +1034,16 @@
 
 			skb->csum = csum_partial_copy_from_user(from,
 					skb_put(skb, copy), copy, 0, &err);
-		
+
 			from += copy;
 			copied += copy;
-			len -= copy;
+
 			sk->write_seq += copy;
 		
 			tcp_send_skb(sk, skb);
+
+			release_sock(sk);
+			lock_sock(sk);
 		}
 	}
 
@@ -1181,14 +1181,14 @@
 			break;
 		tcp_eat_skb(sk, skb);
 	}
-       
+
 	SOCK_DEBUG(sk, "sk->rspace = %lu\n", sock_rspace(sk));
-	
+
   	/*
-  	 *  We send a ACK if the sender is blocked
-  	 *  else let tcp_data deal with the acking policy.
+  	 *	We send a ACK if the sender is blocked
+  	 *	else let tcp_data deal with the acking policy.
   	 */
-  
+
 	if (sk->delayed_acks)
 	{
 		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
@@ -1464,7 +1464,7 @@
 					       msg->msg_name);       
 	}
 	if(addr_len)
-		*addr_len= tp->af_specific->sockaddr_len;
+		*addr_len = tp->af_specific->sockaddr_len;
 
 	remove_wait_queue(sk->sleep, &wait);
 	current->state = TASK_RUNNING;
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.1.29/linux/net/ipv4/tcp_input.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv4/tcp_input.c	Thu Mar 20 18:17:14 1997
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	@(#)tcp_input.c	1.0.16	05/25/93
+ * Version:	$Id: tcp_input.c,v 1.39 1997/03/17 04:49:35 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -36,14 +36,14 @@
  *		Eric Schenk	:	Yet another double ACK bug.
  *		Eric Schenk	:	Delayed ACK bug fixes.
  *		Eric Schenk	:	Floyd style fast retrans war avoidance.
+ *		David S. Miller	:	Don't allow zero congestion window.
  */
 
 #include <linux/config.h>
 #include <linux/mm.h>
 #include <linux/sysctl.h>
 #include <net/tcp.h>
-
-
+#include <linux/ipsec.h>
 
 typedef void			(*tcp_sys_cong_ctl_t)(struct sock *sk,
 						      u32 seq, u32 ack,
@@ -159,34 +159,37 @@
 
 	tp->backoff = 0;
 }
- 
+
+static int __tcp_sequence(struct tcp_opt *tp, u32 seq, u32 end_seq)
+{
+	u32 end_window;
+
+	end_window = tp->rcv_wup + tp->rcv_wnd;
+
+	if (tp->rcv_wnd)
+	{
+		if (!before(seq, tp->rcv_nxt) && before(seq, end_window))
+			return 1;
+
+		if ((end_seq - seq) && after(end_seq, tp->rcv_nxt) &&
+		    !after(end_seq, end_window))
+			return 1;
+	}
+
+	return 0;
+}
 
 /*
  *	This functions checks to see if the tcp header is actually acceptable. 
  */
  
-extern __inline__ int tcp_sequence(struct tcp_opt *tp, u32 seq, u32 seg_nxt)
+extern __inline__ int tcp_sequence(struct tcp_opt *tp, u32 seq, u32 end_seq)
 {
-	u32 end_window = tp->rcv_wup + tp->rcv_wnd;
-	u32 end_seq = seg_nxt;
-
-	/*
-	 *	When the window is open (most common case)
-	 *	we want to accept segments if they have yet unseen data
-	 *	or in the case of a dataless segment if seg.seq == rcv.nxt
-	 *	this means:
-	 *
-	 *	if (seq == end_seq)
-	 *		end_seq >= rcv.nxt
-	 *	else
-	 *		end_seq >  rcv.nxt
-	 */
-
-	if (seq == end_seq)
-		end_seq++;
-
-	return ((before(seq, end_window) && after(end_seq, tp->rcv_nxt)) ||
-		(seq == end_window && seq == end_seq));
+	if (seq == tp->rcv_nxt)
+	{
+		return (tp->rcv_wnd || (end_seq == seq));
+	}
+	return __tcp_sequence(tp, seq, end_seq);
 }
 
 /*
@@ -253,9 +256,9 @@
 	unsigned char *ptr;
 	int length=(th->doff*4)-sizeof(struct tcphdr);
 	int mss = 0;
-    
+
 	ptr = (unsigned char *)(th + 1);
-  
+
 	while(length>0)
 	{
 	  	int opcode=*ptr++;
@@ -295,7 +298,7 @@
  *  See draft-stevens-tcpca-spec-01 for documentation.
  */
 
-static void tcp_fast_retrans(struct sock *sk, u32 seq, u32 ack, int not_dup)
+static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup)
 {
 	struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
 
@@ -318,7 +321,7 @@
 		 * than two segments. Retransmit the missing segment.
 		 */
 
-		if (tp->high_seq == 0 || after(seq, tp->high_seq))
+		if (tp->high_seq == 0 || after(ack, tp->high_seq))
 		{
 			sk->dup_acks++;
 
@@ -354,7 +357,7 @@
 		if (sk->dup_acks >= 3)
 		{
 			tp->retrans_head = NULL;
-			sk->cong_window = sk->ssthresh;
+			sk->cong_window = max(sk->ssthresh, 1);
 			sk->retransmits = 0;
 		}
 		sk->dup_acks = 0;
@@ -374,28 +377,27 @@
 static void tcp_cong_avoid_vegas(struct sock *sk, u32 seq, u32 ack,
 				 u32 seq_rtt)
 {
+	struct tcp_opt * tp;
+	unsigned int actual, expected;
+	unsigned int inv_rtt, inv_basertt, inv_basebd;
+	u32 snt_bytes;
+
 	/*
 	 *	From:
 	 *      TCP Vegas: New Techniques for Congestion 
 	 *	Detection and Avoidance.
-	 *              
+	 *
 	 *
 	 *	Warning: This code is a scratch implementation taken
 	 *	from the paper only. The code they distribute seams
 	 *	to have improved several things over the initial spec.
 	 */
 
-	struct tcp_opt * tp;
-	unsigned int Actual, Expected;
-	unsigned int inv_rtt, inv_basertt; 
-	u32 snt_bytes;
-	
-
 	tp = &(sk->tp_pinfo.af_tcp);
 
 	if (!seq_rtt)
 		seq_rtt = 1;
-	
+
 	if (tp->basertt)
 		tp->basertt = min(seq_rtt, tp->basertt);
 	else
@@ -403,18 +405,21 @@
 
 	/*
 	 * 
-	 *	Actual	 = throughput for this segment.
-	 *	Expected = number_of_bytes in transit / BaseRTT
+	 *	actual	 = throughput for this segment.
+	 *	expected = number_of_bytes in transit / BaseRTT
 	 * 
 	 */
 
-	snt_bytes = (ack - seq) << SHIFT_FACTOR;
-	inv_rtt = (1 << SHIFT_FACTOR) / seq_rtt;
-       
-	Actual =  snt_bytes * inv_rtt;
+	snt_bytes = ack - seq;
 
+	inv_rtt = (1 << SHIFT_FACTOR) / seq_rtt;
 	inv_basertt = (1 << SHIFT_FACTOR) / tp->basertt;
-	Expected = ((tp->snd_nxt - tp->snd_una) << SHIFT_FACTOR) * inv_basertt;
+
+	actual =  snt_bytes * inv_rtt;
+
+	expected = (tp->snd_nxt - tp->snd_una) * inv_basertt;
+
+	inv_basebd = sk->mss * inv_basertt;
 
 	/*
 	 *      Slow Start
@@ -422,17 +427,14 @@
 	
 	if (sk->cong_window < sk->ssthresh &&
 	    (seq == tp->snd_nxt ||
-	      (((Expected - Actual) <=
-		((TCP_VEGAS_GAMMA << SHIFT_FACTOR) * sk->mss * inv_basertt))
-	       )
-	     ))
+	     (expected - actual <= TCP_VEGAS_GAMMA * inv_basebd)))
 	{
 		/*
 		 * "Vegas allows exponential growth only every other
 		 *  RTT"
 		 */
 			
-		if (!(sk->cong_count++))
+		if (sk->cong_count++)
 		{
 			sk->cong_window++;
 			sk->cong_count = 0;
@@ -443,9 +445,8 @@
 		/*
 		 *      Congestion Avoidance
 		 */
-			
-		if (Expected - Actual <=
-		    ((TCP_VEGAS_ALPHA << SHIFT_FACTOR) * sk->mss * inv_basertt))
+
+		if (expected - actual <= TCP_VEGAS_ALPHA * inv_basebd)
 		{
 			/* Increase Linearly */
 				
@@ -455,18 +456,17 @@
 				sk->cong_count = 0;
 			}
 		}
-			
-		if (Expected - Actual >=
-		    ((TCP_VEGAS_BETA << SHIFT_FACTOR) * sk->mss * inv_basertt))
+
+		if (expected - actual >= TCP_VEGAS_BETA * inv_basebd)
 		{
 			/* Decrease Linearly */
-				
+
 			if (sk->cong_count++ >= sk->cong_window)
 			{
 				sk->cong_window--;
 				sk->cong_count = 0;
 			}
-			
+
 			/* Never less than 2 segments */
 			if (sk->cong_window < 2)
 				sk->cong_window = 2;
@@ -523,7 +523,7 @@
 	struct sk_buff *skb;
 	unsigned long now = jiffies;
 	int acked = 0;
-	
+
 	while((skb=skb_peek(&sk->write_queue)) && (skb != tp->send_head))
 	{
 
@@ -545,16 +545,18 @@
 		 
 		if (after(skb->end_seq, ack))
 			break;
-		
-		SOCK_DEBUG(sk, "removing seg %x-%x from retransmit queue\n", skb->seq, skb->end_seq);
+
+		SOCK_DEBUG(sk, "removing seg %x-%x from retransmit queue\n",
+			   skb->seq, skb->end_seq);
+
 		acked = FLAG_DATA_ACKED;
 		
 		atomic_dec(&sk->packets_out);
 
 		*seq = skb->seq;
 		*seq_rtt = now - skb->when;
-				
-		skb_unlink(skb);		
+
+		skb_unlink(skb);
 		
 		kfree_skb(skb, FREE_WRITE);
 	}
@@ -565,7 +567,7 @@
 		if (!sk->dead)
 			sk->write_space(sk);
 	}
-	
+
 	return acked;
 }
 
@@ -616,7 +618,7 @@
 	if(sk->zapped)
 		return(1);	/* Dead, can't ack any more so why bother */
 
-	if (tp->pending == TIME_KEEPOPEN) 
+	if (tp->pending == TIME_KEEPOPEN)
 	{
 	  	tp->probes_out = 0;
 	}
@@ -754,19 +756,22 @@
 	}
 	else
 		tcp_clear_xmit_timer(sk, TIME_RETRANS);
-	
-	tcp_fast_retrans(sk, ack_seq, ack, (flag & (FLAG_DATA|FLAG_WIN_UPDATE)));
+
+
+	tcp_fast_retrans(sk, ack, (flag & (FLAG_DATA|FLAG_WIN_UPDATE)));
 
 	/*
 	 *	Remember the highest ack received.
 	 */
-	 
+
 	tp->snd_una = ack;
+
 	return 1;
 
 uninteresting_ack:
 
-	SOCK_DEBUG(sk, "Ack ignored %u %u\n",ack,tp->snd_nxt);
+	SOCK_DEBUG(sk, "Ack ignored %u %u\n", ack, tp->snd_nxt);
+
 	return 0;
 }
 
@@ -888,10 +893,11 @@
 			SOCK_DEBUG(sk, "ofo packet was allready received \n");
 			skb_unlink(skb);
 			kfree_skb(skb, FREE_READ);
-			
+
 			continue;
 		}
-		SOCK_DEBUG(sk, "ofo requeuing : rcv_next %X seq %X - %X\n", tp->rcv_nxt, skb->seq, skb->end_seq);
+		SOCK_DEBUG(sk, "ofo requeuing : rcv_next %X seq %X - %X\n",
+			   tp->rcv_nxt, skb->seq, skb->end_seq);
 
 		skb_unlink(skb);
 		 
@@ -946,6 +952,7 @@
 		 * force an imediate ack
 		 */
 		SOCK_DEBUG(sk, "retransmit received: seq %X\n", skb->seq);
+
 		sk->delayed_acks = MAX_DELAY_ACK;
 		kfree_skb(skb, FREE_READ);
 
@@ -959,7 +966,9 @@
 		 * Partial packet
 		 * seq < rcv_next < end_seq
 		 */
-		SOCK_DEBUG(sk, "partial packet: rcv_next %X seq %X - %X\n", tp->rcv_nxt, skb->seq, skb->end_seq);
+		SOCK_DEBUG(sk, "partial packet: rcv_next %X seq %X - %X\n",
+			   tp->rcv_nxt, skb->seq, skb->end_seq);
+
 		skb_queue_tail(&sk->receive_queue, skb);
 
 		tp->rcv_nxt = skb->end_seq;
@@ -986,7 +995,8 @@
 
 	tp->pred_flags = 0;
 
-	SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n", tp->rcv_nxt, skb->seq, skb->end_seq);
+	SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n",
+		   tp->rcv_nxt, skb->seq, skb->end_seq);
 
 	if (skb_peek(&sk->out_of_order_queue) == NULL) {
 		skb_queue_head(&sk->out_of_order_queue,skb);
@@ -1064,16 +1074,16 @@
 	}
 
 	sk->delayed_acks++;
-	
+
 	/*
 	 *	Now tell the user we may have some data. 
 	 */
-	 
-	if (!sk->dead) 
+
+	if (!sk->dead)
 	{
 		SOCK_DEBUG(sk, "Data wakeup.\n");
 		sk->data_ready(sk,0);
-	} 
+	}
 	return(1);
 }
 
@@ -1314,10 +1324,9 @@
 		{
 			if (after(skb->seq, tp->rcv_nxt))
 			{
-				printk(KERN_DEBUG "->seq:%d end:%d "
-				       "wup:%d wnd:%d\n",
-				       skb->seq, skb->end_seq, 
-				       tp->rcv_wup, tp->rcv_wnd);
+				SOCK_DEBUG(sk, "seq:%d end:%d wup:%d wnd:%d\n",
+					   skb->seq, skb->end_seq,
+					   tp->rcv_wup, tp->rcv_wnd);
 			}
 			tcp_send_ack(sk);
 			kfree_skb(skb, FREE_READ);
@@ -1327,6 +1336,7 @@
 
 	if(th->syn && skb->seq != sk->syn_seq)
 	{
+		printk(KERN_DEBUG "syn in established state\n");
 		tcp_reset(sk, skb);
 		kfree_skb(skb, FREE_READ);
 		return 1;
@@ -1586,7 +1596,7 @@
 
 			sk = tp->af_specific->get_sock(skb, th);
 
-			if (sk == NULL)
+			if (sk == NULL || !ipsec_sk_policy(sk,skb))
 				goto discard;
 
 			skb_set_owner_r(skb, sk);
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.29/linux/net/ipv4/tcp_ipv4.c	Mon Mar 17 14:54:35 1997
+++ linux/net/ipv4/tcp_ipv4.c	Thu Mar 20 18:17:14 1997
@@ -5,6 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
+ * Version:	$Id: tcp_ipv4.c,v 1.23 1997/03/17 04:49:38 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -32,6 +33,7 @@
 #include <linux/types.h>
 #include <linux/fcntl.h>
 #include <linux/random.h>
+#include <linux/ipsec.h>
 
 #include <net/icmp.h>
 #include <net/tcp.h>
@@ -84,7 +86,7 @@
 
 	SOCKHASH_LOCK();
 	sk2 = tcp_bound_hash[tcp_bhashfn(snum)];
-	for(; sk2 != NULL; sk2 = sk2->prev) {
+	for(; sk2 != NULL; sk2 = sk2->bind_next) {
 		if((sk2->num == snum) && (sk2 != sk)) {
 			unsigned char state = sk2->state;
 			int sk2_reuse = sk2->reuse;
@@ -92,7 +94,7 @@
 			if(!sk2->rcv_saddr || !sk->rcv_saddr) {
 				if((!sk2_reuse)			||
 				   (!sk_reuse)			||
-				   (state != TCP_LISTEN)) {
+				   (state == TCP_LISTEN)) {
 					retval = 1;
 					break;
 				}
@@ -115,7 +117,7 @@
 {
 	struct sock *sk = tcp_bound_hash[tcp_bhashfn(num)];
 
-	for(; sk != NULL; sk = sk->prev) {
+	for(; sk != NULL; sk = sk->bind_next) {
 		if(sk->num == num)
 			return 1;
 	}
@@ -156,7 +158,7 @@
 			goto done;
 		} else {
 			int j = 0;
-			do { sk = sk->prev; } while(++j < size && sk);
+			do { sk = sk->bind_next; } while(++j < size && sk);
 			if(j < size) {
 				best = (start + i);
 				size = j;
@@ -196,18 +198,17 @@
 	SOCKHASH_LOCK();
 	state = sk->state;
 	if(state != TCP_CLOSE || !sk->dead) {
-		struct sock **htable;
+		struct sock **skp;
 
-		if(state == TCP_LISTEN) {
-			sk->hashent = tcp_sk_listen_hashfn(sk);
-			htable = &tcp_listening_hash[0];
-		} else {
-			sk->hashent = tcp_sk_hashfn(sk);
-			htable = &tcp_established_hash[0];
-		}
-		sk->next = htable[sk->hashent];
-		htable[sk->hashent] = sk;
-		sk->hashtable = htable;
+		if(state == TCP_LISTEN)
+			skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+		else
+			skp = &tcp_established_hash[tcp_sk_hashfn(sk)];
+
+		if((sk->next = *skp) != NULL)
+			(*skp)->pprev = &sk->next;
+		*skp = sk;
+		sk->pprev = skp;
 		tcp_sk_bindify(sk);
 	}
 	SOCKHASH_UNLOCK();
@@ -215,57 +216,44 @@
 
 static void tcp_v4_unhash(struct sock *sk)
 {
-	struct sock **htable;
-
 	SOCKHASH_LOCK();
-	htable = sk->hashtable;
-	if(htable) {
-		struct sock **skp = &(htable[sk->hashent]);
-		while(*skp != NULL) {
-			if(*skp == sk) {
-				*skp = sk->next;
-				break;
-			}
-			skp = &((*skp)->next);
-		}
-		sk->hashtable = NULL;
+	if(sk->pprev) {
+		if(sk->next)
+			sk->next->pprev = sk->pprev;
+		*sk->pprev = sk->next;
+		sk->pprev = NULL;
+		tcp_sk_unbindify(sk);
 	}
-	tcp_sk_unbindify(sk);
 	SOCKHASH_UNLOCK();
 }
 
 static void tcp_v4_rehash(struct sock *sk)
 {
-	struct sock **htable;
 	unsigned char state;
 
 	SOCKHASH_LOCK();
-	htable = &(sk->hashtable[sk->hashent]);
 	state = sk->state;
-	if(htable) {
-		while(*htable != NULL) {
-			if(*htable == sk) {
-				*htable = sk->next;
-				break;
-			}
-			htable = &((*htable)->next);
-		}
+	if(sk->pprev) {
+		if(sk->next)
+			sk->next->pprev = sk->pprev;
+		*sk->pprev = sk->next;
+		sk->pprev = NULL;
 		tcp_sk_unbindify(sk);
 	}
-	htable = NULL;
 	if(state != TCP_CLOSE || !sk->dead) {
-		if(state == TCP_LISTEN) {
-			sk->hashent = tcp_sk_listen_hashfn(sk);
-			htable = &tcp_listening_hash[0];
-		} else {
-			sk->hashent = tcp_sk_hashfn(sk);
-			htable = &tcp_established_hash[0];
-		}
-		sk->next = htable[sk->hashent];
-		htable[sk->hashent] = sk;
+		struct sock **skp;
+
+		if(state == TCP_LISTEN)
+			skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+		else
+			skp = &tcp_established_hash[tcp_sk_hashfn(sk)];
+
+		if((sk->next = *skp) != NULL)
+			(*skp)->pprev = &sk->next;
+		*skp = sk;
+		sk->pprev = skp;
 		tcp_sk_bindify(sk);
 	}
-	sk->hashtable = htable;
 	SOCKHASH_UNLOCK();
 }
 
@@ -343,7 +331,7 @@
 	secondlist((hpnum), tcp_bound_hash[tcp_bhashfn(hnum)],(fpass))
 
 #define tcp_v4_proxy_loop_next(hnum, hpnum, sk, fpass) \
-	secondlist((hpnum),(sk)->next,(fpass))
+	secondlist((hpnum),(sk)->bind_next,(fpass))
 
 struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
 				 unsigned short rnum, unsigned long laddr,
@@ -457,11 +445,11 @@
 	/*
 	 *	Don't allow a double connect.
 	 */
-	 	
+
 	if (sk->daddr)
 		return -EINVAL;
-	
-	if (addr_len < sizeof(struct sockaddr_in)) 
+
+	if (addr_len < sizeof(struct sockaddr_in))
 		return(-EINVAL);
 
 	if (usin->sin_family != AF_INET) {
@@ -476,7 +464,7 @@
 		dst_release(sk->dst_cache);
 		sk->dst_cache = NULL;
 	}
-		  
+
 	tmp = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr,
 			       RT_TOS(sk->ip_tos)|(sk->localroute || 0));
 	if (tmp < 0)
@@ -517,7 +505,7 @@
 	sk->err = 0;
 	
 	buff = sock_wmalloc(sk, MAX_SYN_SIZE, 0, GFP_KERNEL);
-	if (buff == NULL) 
+	if (buff == NULL)
 	{
 		release_sock(sk);
 		return(-ENOBUFS);
@@ -526,10 +514,10 @@
 	/*
 	 *	Put in the IP header and routing stuff.
 	 */
-	
+
 	tmp = ip_build_header(buff, sk);
 
-	if (tmp < 0) 
+	if (tmp < 0)
 	{
 		kfree_skb(buff, FREE_WRITE);
 		release_sock(sk);
@@ -566,7 +554,7 @@
 	if (sk->user_mss)
 		sk->mss = sk->user_mss;
 	else
-		sk->mss = (sk->mtu - sizeof(struct iphdr) - 
+		sk->mss = (sk->mtu - sizeof(struct iphdr) -
 			   sizeof(struct tcphdr));
 
 	/*
@@ -591,7 +579,7 @@
 	tp->rto = rt->u.dst.rtt;
 
 	tcp_init_xmit_timers(sk);
-	
+
 	/* Now works the right way instead of a hacked initial setting */
 	sk->retransmits = 0;
 
@@ -601,7 +589,7 @@
 	buff->when = jiffies;
 
 	skb1 = skb_clone(buff, GFP_KERNEL);
-	ip_queue_xmit(skb1);  
+	ip_queue_xmit(skb1);
 
 	/* Timer for repeating the SYN until an answer  */
 	tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
@@ -641,7 +629,7 @@
 
 	lock_sock(sk);
 	retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov,
-				len, msg->msg_flags);
+				msg->msg_flags);
 
 	release_sock(sk);
 
@@ -741,7 +729,7 @@
  *		arrived with segment.
  *	Exception: precedence violation. We do not implement it in any case.
  */
- 
+
 static void tcp_v4_send_reset(struct sk_buff *skb)
 {
 	struct tcphdr  *th = skb->h.th;
@@ -750,7 +738,7 @@
 
 	if (th->rst)
 		return;
-  
+
 	skb1 = ip_reply(skb, sizeof(struct tcphdr));
 	if (skb1 == NULL)
 		return;
@@ -759,14 +747,14 @@
 	memset(th1, 0, sizeof(*th1));
 
 	/*
-	 *	Swap the send and the receive. 
+	 *	Swap the send and the receive.
 	 */
- 
+
 	th1->dest = th->source;
 	th1->source = th->dest;
 	th1->doff = sizeof(*th1)/4;
 	th1->rst = 1;
-   
+
 	if (th->ack)
 	  	th1->seq = th->ack_seq;
 	else {
@@ -776,7 +764,7 @@
 		else
 			th1->ack_seq = htonl(ntohl(th->seq)+1);
 	}
- 
+
 	skb1->csum = csum_partial((u8 *) th1, sizeof(*th1), 0);
 	th1->check = tcp_v4_check(th1, sizeof(*th1), skb1->nh.iph->saddr,
 				  skb1->nh.iph->daddr, skb1->csum);
@@ -821,7 +809,7 @@
 	int tmp;
 
 	skb = sock_wmalloc(sk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
-	
+
 	if (skb == NULL)
 	{
 		return;
@@ -839,27 +827,27 @@
 	mss = skb->dst->pmtu;
 
 	mss -= sizeof(struct iphdr) + sizeof(struct tcphdr);
-	
+
 	if (sk->user_mss)
 		mss = min(mss, sk->user_mss);
-	
+
 	th =(struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
 	skb->h.th = th;
 	memset(th, 0, sizeof(struct tcphdr));
-	
+
 	th->syn = 1;
 	th->ack = 1;
 
 	th->source = sk->dummy_th.source;
 	th->dest = req->rmt_port;
-	       
+
 	skb->seq = req->snt_isn;
 	skb->end_seq = skb->seq + 1;
 
 	th->seq = ntohl(skb->seq);
 	th->ack_seq = htonl(req->rcv_isn + 1);
 	th->doff = sizeof(*th)/4 + 1;
-	
+
 	th->window = ntohs(tp->rcv_wnd);
 
 	ptr = skb_put(skb, TCPOLEN_MSS);
@@ -870,14 +858,12 @@
 	skb->csum = csum_partial(ptr, TCPOLEN_MSS, 0);
 
 	th->check = tcp_v4_check(th, sizeof(*th) + TCPOLEN_MSS,
-				 af_req->loc_addr, 
+				 af_req->loc_addr,
 				 af_req->rmt_addr,
 				 csum_partial((char *)th, sizeof(*th), skb->csum));
 
 	ip_queue_xmit(skb);
 	tcp_statistics.TcpOutSegs++;
-					      
-
 }
 
 static void tcp_v4_or_free(struct open_request *req)
@@ -886,7 +872,7 @@
 
 	if (af_req->req.sk)
 		return;
-	
+
 	if (af_req->opt)
 		kfree_s(af_req->opt, sizeof(struct options) + af_req->opt->optlen);
 }
@@ -915,14 +901,14 @@
 	{
 		SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n",sk);
 		tcp_statistics.TcpAttemptFails++;
-		return -ENOTCONN;		
+		return -ENOTCONN;
 	}
 
-	if (sk->ack_backlog >= sk->max_ack_backlog || 
+	if (sk->ack_backlog >= sk->max_ack_backlog ||
 	    tcp_v4_syn_filter(sk, skb, saddr))
 	{
-		printk(KERN_DEBUG "dropping syn ack:%d max:%d\n",
-		       sk->ack_backlog, sk->max_ack_backlog);
+		SOCK_DEBUG(sk, "dropping syn ack:%d max:%d\n", sk->ack_backlog,
+			   sk->max_ack_backlog);
 #ifdef CONFIG_IP_TCPSF
 		tcp_v4_random_drop(sk);
 #endif
@@ -930,7 +916,6 @@
 		goto exit;
 	}
 
-
 	af_req = kmalloc(sizeof(struct tcp_v4_open_req), GFP_ATOMIC);
 
 	if (af_req == NULL)
@@ -959,7 +944,7 @@
 
 	af_req->loc_addr = daddr;
 	af_req->rmt_addr = saddr;
-	
+
 	/*
 	 *	options
 	 */
@@ -968,11 +953,11 @@
 	{
 		af_req->opt = (struct ip_options*) kmalloc(sizeof(struct ip_options) +
 							   opt->optlen, GFP_ATOMIC);
-		if (af_req->opt) 
+		if (af_req->opt)
 		{
 			if (ip_options_echo(af_req->opt, skb))
 			{
-				kfree_s(af_req->opt, sizeof(struct options) + 
+				kfree_s(af_req->opt, sizeof(struct options) +
 					opt->optlen);
 				af_req->opt = NULL;
 			}
@@ -982,7 +967,7 @@
 	req->class = &or_ipv4;
 
 	tcp_v4_send_synack(sk, req);
-	
+
 	req->expires = jiffies + TCP_TIMEOUT_INIT;
 	tcp_inc_slow_timer(TCP_SLT_SYNACK);
 	tcp_synq_queue(&sk->tp_pinfo.af_tcp, req);
@@ -1019,13 +1004,12 @@
 	skb_queue_head_init(&newsk->receive_queue);
 	skb_queue_head_init(&newsk->out_of_order_queue);
 	skb_queue_head_init(&newsk->error_queue);
-	
+
 	/*
 	 *	Unused
 	 */
 
 	newsk->send_head = NULL;
-	newsk->send_tail = NULL;
 
 	newtp = &(newsk->tp_pinfo.af_tcp);
 	newtp->send_head = NULL;
@@ -1040,7 +1024,6 @@
 	newsk->cong_count = 0;
 	newsk->ssthresh = 0;
 	newtp->backoff = 0;
-	newsk->blog = 0;
 	newsk->intr = 0;
 	newsk->proc = 0;
 	newsk->done = 0;
@@ -1084,8 +1067,8 @@
 
 	newsk->dummy_th.source = sk->dummy_th.source;
 	newsk->dummy_th.dest = req->rmt_port;
-	newsk->users=0;
-	
+	newsk->sock_readers=0;
+
 	newtp->rcv_nxt = req->rcv_isn + 1;
 	newtp->rcv_wup = req->rcv_isn + 1;
 	newsk->copied_seq = req->rcv_isn + 1;
@@ -1095,7 +1078,7 @@
 	newsk->daddr = af_req->rmt_addr;
 	newsk->saddr = af_req->loc_addr;
 	newsk->rcv_saddr = af_req->loc_addr;
-	
+
 	/*
 	 *	options / mss / route_cache
 	 */
@@ -1109,10 +1092,10 @@
 	}
 
 	newsk->dst_cache = &rt->u.dst;
-	
+
 	newsk->window_clamp = rt->u.dst.window;
 	snd_mss = rt->u.dst.pmtu;
-	
+
 	newsk->mtu = snd_mss;
 	/* sanity check */
 	if (newsk->mtu < 64)
@@ -1126,9 +1109,9 @@
 	{
 		snd_mss = min(snd_mss, sk->user_mss);
 	}
-	
+
 	newsk->mss = min(req->mss, snd_mss);
-	
+
 	tcp_v4_hash(newsk);
 	add_to_prot_sklist(newsk);
 	return newsk;
@@ -1139,21 +1122,19 @@
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct open_request *req;
 
-
 	/*
 	 *	assumption: the socket is not in use.
 	 *	as we checked the user count on tcp_rcv and we're
 	 *	running from a soft interrupt.
 	 */
-		
+
 	req = tp->syn_wait_queue;
-	
 
 	if (!req)
 	{
 		return sk;
 	}
-	
+
 	do {
 		struct tcp_v4_open_req *af_req;
 
@@ -1164,11 +1145,15 @@
 		    req->rmt_port == skb->h.th->source)
 		{
 			u32 flg;
-				
+
 			if (req->sk)
 			{
-				printk(KERN_DEBUG "BUG: syn_recv:"
-				       "socket exists\n");
+				/*
+				 *	socket already created but not
+				 *	yet accepted()...
+				 */
+
+				sk = req->sk;
 				break;
 			}
 
@@ -1179,7 +1164,7 @@
 			 */
 			flg = *(((u32 *)skb->h.th) + 3);
 			flg &= __constant_htonl(0x002f0000);
-				
+
 			if ((flg == __constant_htonl(0x00020000)) &&
 			    (!after(skb->seq, req->rcv_isn)))
 			{
@@ -1190,7 +1175,6 @@
 				return NULL;
 			}
 
-			skb_orphan(skb);
 			sk = tp->af_specific->syn_recv_sock(sk, skb, req);
 
 			tcp_dec_slow_timer(TCP_SLT_SYNACK);
@@ -1200,7 +1184,6 @@
 				return NULL;
 			}
 
-			skb_set_owner_r(skb, sk);
 			req->expires = 0UL;
 			req->sk = sk;
 			break;
@@ -1208,40 +1191,53 @@
 
 		req = req->dl_next;
 	} while (req != tp->syn_wait_queue);
-	
 
+	skb_orphan(skb);
+	skb_set_owner_r(skb, sk);
 	return sk;
 }
 
-static int __inline__ tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
+int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
 {
 	skb_set_owner_r(skb, sk);
 
+	/*
+	 *	socket locking is here for SMP purposes as backlog rcv
+	 *	is currently called with bh processing disabled.
+	 */
+	lock_sock(sk);
+
 	if (sk->state == TCP_ESTABLISHED)
 	{
 		if (tcp_rcv_established(sk, skb, skb->h.th, skb->len))
 			goto reset;
-		return 0;
+		goto ok;
 	}
 
 	if (sk->state == TCP_LISTEN)
 	{
+		struct sock *nsk;
+
 		/*
 		 *	find possible connection requests
 		 */
-		sk = tcp_v4_check_req(sk, skb);
 
-		if (sk == NULL)
+		nsk = tcp_v4_check_req(sk, skb);
+
+		if (nsk == NULL)
 		{
 			goto discard_it;
 		}
+
+		release_sock(sk);
+		lock_sock(nsk);
+		sk = nsk;
 	}
-	
+
 	if (tcp_rcv_state_process(sk, skb, skb->h.th, NULL, skb->len) == 0)
-		return 0;
+		goto ok;
 
 reset:
-	
 	tcp_v4_send_reset(skb);
 
 discard_it:
@@ -1249,12 +1245,10 @@
 	 *	Discard frame
 	 */
 	kfree_skb(skb, FREE_READ);
-	return 0;
-}
 
-int __inline__ tcp_v4_backlog_rcv(struct sock *sk, struct sk_buff *skb)
-{
-	return tcp_v4_do_rcv(sk, skb);
+ok:
+	release_sock(sk);
+	return 0;
 }
 
 /*
@@ -1263,7 +1257,7 @@
 
 int tcp_v4_rcv(struct sk_buff *skb, unsigned short len)
 {
-	struct tcphdr *th;	
+	struct tcphdr *th;
 	struct sock *sk;
 	u32	saddr = skb->nh.iph->saddr;
 	u32	daddr = skb->nh.iph->daddr;
@@ -1276,14 +1270,14 @@
 	/*
 	 *	Pull up the IP header.
 	 */
-	
+
 	skb_pull(skb, skb->h.raw-skb->data);
 
 	/*
 	 *	Try to use the device checksum if provided.
 	 */
-		
-	switch (skb->ip_summed) 
+
+	switch (skb->ip_summed)
 	{
 	case CHECKSUM_NONE:
 		skb->csum = csum_partial((char *)th, len, 0);
@@ -1297,8 +1291,8 @@
 		}
 	default:
 		/* CHECKSUM_UNNECESSARY */
-	}
-	
+	};
+
 	tcp_statistics.TcpInSegs++;
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
@@ -1310,15 +1304,17 @@
 	sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest);
 	if (!sk)
 		goto no_tcp_socket;
+	if(!ipsec_sk_policy(sk,skb))
+		goto discard_it;
 
 	skb->seq = ntohl(th->seq);
 	skb->end_seq = skb->seq + th->syn + th->fin + len - th->doff*4;
 	skb->ack_seq = ntohl(th->ack_seq);
-		
+
 	skb->acked = 0;
 	skb->used = 0;
 
-	if (!sk->users)
+	if (!sk->sock_readers)
 		return tcp_v4_do_rcv(sk, skb);
 
 	__skb_queue_tail(&sk->back_log, skb);
@@ -1334,7 +1330,6 @@
 	kfree_skb(skb, FREE_READ);
   	return 0;
 }
-  
 
 int tcp_v4_build_header(struct sock *sk, struct sk_buff *skb)
 {
@@ -1342,7 +1337,7 @@
 }
 
 int tcp_v4_rebuild_header(struct sock *sk, struct sk_buff *skb)
-{	
+{
 	struct rtable *rt;
 	struct iphdr *iph;
 	struct tcphdr *th;
@@ -1362,18 +1357,18 @@
 		dst_release(skb->dst);
 		skb->dst = &rt->u.dst;
 	}
-  
+
 	/*
 	 *	Discard the surplus MAC header
 	 */
-	
+
 	skb_pull(skb, skb->nh.raw-skb->data);
 
 	iph = skb->nh.iph;
 	th = skb->h.th;
 	size = skb->tail - skb->h.raw;
 
-	return 0;	
+	return 0;
 }
 
 static struct sock * tcp_v4_get_sock(struct sk_buff *skb, struct tcphdr *th)
@@ -1385,11 +1380,10 @@
 static void v4_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
 {
 	struct sockaddr_in *sin = (struct sockaddr_in *) uaddr;
-	
+
 	sin->sin_family		= AF_INET;
 	sin->sin_addr.s_addr	= sk->daddr;
 	sin->sin_port		= sk->dummy_th.dest;
-
 }
 
 struct tcp_func ipv4_specific = {
@@ -1430,26 +1424,23 @@
 	 */
 	sk->cong_window = 1;
 	sk->ssthresh = 0x7fffffff;
-	
+
 	sk->priority = 1;
 	sk->state = TCP_CLOSE;
 
 	/* this is how many unacked bytes we will accept for this socket.  */
 	sk->max_unacked = 2048; /* needs to be at most 2 full packets. */
 	sk->max_ack_backlog = SOMAXCONN;
-	
+
 	sk->mtu = 576;
 	sk->mss = 536;
 
-	sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
-	
-
 	/*
-	 *	Speed up by setting some standard state for the dummy_th
-	 *	if TCP uses it (maybe move to tcp_init later)
+	 *	Speed up by setting some standard state for the dummy_th.
 	 */
-  	
-  	sk->dummy_th.ack=1;	
+
+	sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
+  	sk->dummy_th.ack=1;
   	sk->dummy_th.doff=sizeof(struct tcphdr)>>2;
 
 	sk->tp_pinfo.af_tcp.af_specific = &ipv4_specific;
@@ -1467,11 +1458,11 @@
 	{
 		tcp_dec_slow_timer(TCP_SLT_KEEPALIVE);
 	}
-	
+
 	/*
-	 *	Cleanup up the write buffer. 
+	 *	Cleanup up the write buffer.
 	 */
-	 
+
   	while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
 		IS_SKB(skb);
 		kfree_skb(skb, FREE_WRITE);
@@ -1508,7 +1499,7 @@
 	tcp_v4_sendmsg,			/* sendmsg */
 	tcp_recvmsg,			/* recvmsg */
 	NULL,				/* bind */
-	tcp_v4_backlog_rcv,		/* backlog_rcv */
+	tcp_v4_do_rcv,			/* backlog_rcv */
 	tcp_v4_hash,			/* hash */
 	tcp_v4_unhash,			/* unhash */
 	tcp_v4_rehash,			/* rehash */
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.1.29/linux/net/ipv4/tcp_output.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv4/tcp_output.c	Thu Mar 20 18:17:14 1997
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	@(#)tcp_input.c	1.0.16	05/25/93
+ * Version:	$Id: tcp_output.c,v 1.31 1997/03/16 07:03:07 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -103,13 +103,13 @@
 	/*
 	 *	length of packet (not counting length of pre-tcp headers) 
 	 */
-	 
+
 	size = skb->len - ((unsigned char *) th - skb->data);
 
 	/*
-	 *	Sanity check it.. 
+	 *	Sanity check it..
 	 */
-	 
+
 	if (size < sizeof(struct tcphdr) || size > skb->len) 
 	{
 		printk("tcp_send_skb: bad skb (skb = %p, data = %p, th = %p, len = %u)\n",
@@ -122,8 +122,8 @@
 	 *	If we have queued a header size packet.. (these crash a few
 	 *	tcp stacks if ack is not set)
 	 */
-	 
-	if (size == sizeof(struct tcphdr)) 
+
+	if (size == sizeof(struct tcphdr))
 	{
 		/* 
                  * If it's got a syn or fin discard
@@ -136,35 +136,15 @@
 		}
 	}
 
-
 	/*
 	 *	Actual processing.
 	 */
-	 
-	tcp_statistics.TcpOutSegs++;  
 	skb->seq = ntohl(th->seq);
 	skb->end_seq = skb->seq + size - 4*th->doff;
 
-	
-	if (tp->send_head || !tcp_snd_test(sk, skb))
-	{
-		/* 
-		 * Remember where we must start sending
-		 */
-
-		if (tp->send_head == NULL)
-			tp->send_head = skb;
-
-		skb_queue_tail(&sk->write_queue, skb);
-
-		if (sk->packets_out == 0 && !tp->pending)
-		{
-			tp->pending = TIME_PROBE0;
-			tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto);
-		}
+	skb_queue_tail(&sk->write_queue, skb);
 
-	}
-	else
+	if (tp->send_head == NULL && tcp_snd_test(sk, skb))
 	{
 		struct sk_buff * buff;
 
@@ -172,28 +152,47 @@
 		 *	This is going straight out
 		 */
 
-		skb_queue_tail(&sk->write_queue, skb);
-
-		clear_delayed_acks(sk);
-		 
 		th->ack_seq = htonl(tp->rcv_nxt);
 		th->window = htons(tcp_select_window(sk));
 
 		tp->af_specific->send_check(sk, th, size, skb);
 
-		tp->snd_nxt = skb->end_seq;
+		buff = skb_clone(skb, GFP_KERNEL);
+
+		if (buff == NULL)
+		{
+			goto queue;
+		}
 		
+		clear_delayed_acks(sk);
+		skb_set_owner_w(buff, sk);
+
+		tp->snd_nxt = skb->end_seq;
 		atomic_inc(&sk->packets_out);
 
 		skb->when = jiffies;
-		
-		buff = skb_clone(skb, GFP_ATOMIC);
-		skb_set_owner_w(buff, sk);
 
+		tcp_statistics.TcpOutSegs++;
 		tp->af_specific->queue_xmit(buff);
 
 		if (!tcp_timer_is_set(sk, TIME_RETRANS))
 			tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+
+		return 0;
+	}
+
+queue:
+	/* 
+	 *	Remember where we must start sending
+	 */
+
+	if (tp->send_head == NULL)
+		tp->send_head = skb;
+
+	if (sk->packets_out == 0 && !tp->pending)
+	{
+		tp->pending = TIME_PROBE0;
+		tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto);
 	}
 
 	return 0;
@@ -327,9 +326,9 @@
 {
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 	
-	printk(KERN_DEBUG "tcp_write_xmit: frag needed size=%d mss=%d\n", 
-	       size, sk->mss);
-				
+	SOCK_DEBUG(sk, "tcp_write_xmit: frag needed size=%d mss=%d\n",
+		   size, sk->mss);
+
 	if (tcp_fragment(sk, skb, sk->mss))
 	{
 		/* !tcp_frament Failed! */
@@ -358,13 +357,13 @@
 void tcp_write_xmit(struct sock *sk)
 {
 	struct sk_buff *skb;
-	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;	
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 	u16 rcv_wnd;
 	int sent_pkts = 0;
 
 	/*
 	 *	The bytes will have to remain here. In time closedown will
-	 *	empty the write queue and all will be happy 
+	 *	empty the write queue and all will be happy
 	 */
 
 	if(sk->zapped)
@@ -379,10 +378,8 @@
 	 *	c) not retransmiting [Nagle]
 	 */
 
-	lock_sock(sk);
-
 	rcv_wnd = htons(tcp_select_window(sk));
-	
+
 	while((skb = tp->send_head) && tcp_snd_test(sk, skb))
 	{
 		struct tcphdr *th;
@@ -392,34 +389,26 @@
 		IS_SKB(skb);
 
 		/*
-		 *	See if we really need to send the packet. 
+		 *	See if we really need to send the packet.
+		 *	(debugging code)
 		 */
-		 
-		if (!after(skb->end_seq, tp->snd_una)) 
+
+		if (!after(skb->end_seq, tp->snd_una))
 		{
 			tcp_wrxmit_prob(sk, skb);
 			continue;
 		} 
 
 
-		/* 
-		 * Advance the send_head
-		 * This one is going out.
+		/*
+		 *	Put in the ack seq and window at this point rather
+		 *	than earlier, in order to keep them monotonic.
+		 *	We really want to avoid taking back window allocations.
+		 *	That's legal, but RFC1122 says it's frowned on.
+		 *	Ack and window will in general have changed since
+		 *	this packet was put on the write queue.
 		 */
 
-		update_send_head(sk);
-
-		atomic_inc(&sk->packets_out);
-
-
-/*
- * put in the ack seq and window at this point rather than earlier,
- * in order to keep them monotonic.  We really want to avoid taking
- * back window allocations.  That's legal, but RFC1122 says it's frowned on.
- * Ack and window will in general have changed since this packet was put
- * on the write queue.
- */
-
 		th = skb->h.th;
 		size = skb->len - (((unsigned char *) th) - skb->data);
 
@@ -438,27 +427,36 @@
 		if (before(skb->end_seq, tp->snd_nxt))
 			printk(KERN_DEBUG "tcp_write_xmit:"
 			       " sending already sent seq\n");
-#endif		
+#endif
 
-		tp->snd_nxt = skb->end_seq;
+		buff = skb_clone(skb, GFP_ATOMIC);
 
-		skb->when = jiffies;
+		if (buff == NULL)
+			break;
+
+		/*
+		 *	Advance the send_head. This one is going out.
+		 */
+
+		update_send_head(sk);
 		clear_delayed_acks(sk);
 
-		buff = skb_clone(skb, GFP_ATOMIC);
+		atomic_inc(&sk->packets_out);
 		skb_set_owner_w(buff, sk);
 
+		tp->snd_nxt = skb->end_seq;
+
+		skb->when = jiffies;
+
 		sent_pkts = 1;
 		tp->af_specific->queue_xmit(buff);
 
 	}
-	
+
 	if (sent_pkts && !tcp_timer_is_set(sk, TIME_RETRANS))
 	{
 		tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 	}
-
-	release_sock(sk);
 }
 
 
@@ -639,8 +637,6 @@
 	int ct=0;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
-	lock_sock(sk);
-
 	if (tp->retrans_head == NULL)
 		tp->retrans_head = skb_peek(&sk->write_queue);
 
@@ -651,7 +647,7 @@
 	{
 		struct sk_buff *buff;
 		struct tcphdr *th;
-		int tcp_size;			
+		int tcp_size;
 		int size;
 
 		IS_SKB(skb);
@@ -696,6 +692,7 @@
 		}
 
 		SOCK_DEBUG(sk, "retransmit sending\n");
+
 		/*
 		 *	update ack and window
 		 */
@@ -707,7 +704,11 @@
 		tp->af_specific->send_check(sk, th, size, skb);
 
 		skb->when = jiffies;
+
 		buff = skb_clone(skb, GFP_ATOMIC);
+
+		if (buff == NULL)
+			break;
 		skb_set_owner_w(buff, sk);
 
 		clear_delayed_acks(sk);
@@ -749,8 +750,6 @@
 			tp->retrans_head = NULL;
 		}
 	}
-
-	release_sock(sk);
 }
 
 /*
@@ -825,7 +824,7 @@
 	tp->af_specific->send_check(sk, t1, sizeof(*t1), buff);
 
 	/*
-	 * The fin can only be transmited after the data.
+	 *	The fin can only be transmited after the data.
  	 */
  	
 	skb_queue_tail(&sk->write_queue, buff);
@@ -839,9 +838,12 @@
 		buff->when = jiffies;
 
 		skb1 = skb_clone(buff, GFP_KERNEL);
-		skb_set_owner_w(skb1, sk);
 
-		tp->af_specific->queue_xmit(skb1);
+		if (skb1)
+		{
+			skb_set_owner_w(skb1, sk);
+			tp->af_specific->queue_xmit(skb1);
+		}
 
                 if (!tcp_timer_is_set(sk, TIME_RETRANS))
 			tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
@@ -901,20 +903,21 @@
 	tp->af_specific->send_check(sk, th, sizeof(*th)+4, skb);
 
 	skb_queue_tail(&sk->write_queue, skb);
-
-	atomic_inc(&sk->packets_out);
 	
-	skb->when = jiffies;
 	buff = skb_clone(skb, GFP_ATOMIC);
 
-	skb_set_owner_w(buff, sk);
-
-	tp->af_specific->queue_xmit(buff);
+	if (buff)
+	{
+		skb_set_owner_w(buff, sk);
 
-	tcp_reset_xmit_timer(sk, TIME_RETRANS, TCP_TIMEOUT_INIT);
+		atomic_inc(&sk->packets_out);
+		skb->when = jiffies;
 
-	tcp_statistics.TcpOutSegs++;
+		tp->af_specific->queue_xmit(buff);
+		tcp_statistics.TcpOutSegs++;
 
+		tcp_reset_xmit_timer(sk, TIME_RETRANS, TCP_TIMEOUT_INIT);
+	}
 	return 0;
 }
 
@@ -1029,8 +1032,11 @@
 
 	tp->af_specific->send_check(sk, th, sizeof(struct tcphdr), buff);
 
-	SOCK_DEBUG(sk, "\rtcp_send_ack: seq %x ack %x\n", tp->snd_nxt, tp->rcv_nxt);
+	SOCK_DEBUG(sk, "\rtcp_send_ack: seq %x ack %x\n",
+		   tp->snd_nxt, tp->rcv_nxt);
+
 	tp->af_specific->queue_xmit(buff);
+
   	tcp_statistics.TcpOutSegs++;
 }
 
@@ -1096,7 +1102,10 @@
 					    skb);
 
 		buff = skb_clone(skb, GFP_ATOMIC);
-
+		if (buff == NULL)
+		{
+			return;
+		}
 		skb_set_owner_w(buff, sk);
 		atomic_inc(&sk->packets_out);
 
@@ -1122,10 +1131,10 @@
 		/*
 		 *	Put in the IP header and routing stuff. 
 		 */
-		 
+
 		tmp = tp->af_specific->build_net_header(sk, buff);
 
-		if (tmp < 0) 
+		if (tmp < 0)
 		{
 			kfree_skb(buff, FREE_WRITE);
 			return;
@@ -1143,9 +1152,9 @@
 /*		t1->fin = 0;	-- We are sending a 'previous' sequence, and 0 bytes of data - thus no FIN bit */
 		t1->ack_seq = htonl(tp->rcv_nxt);
 		t1->window = htons(tcp_select_window(sk));
-		
+
 		tp->af_specific->send_check(sk, t1, sizeof(*t1), buff);
-	}		
+	}
 
 	/*
 	 *	Send it.
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.1.29/linux/net/ipv4/tcp_timer.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv4/tcp_timer.c	Thu Mar 20 18:17:14 1997
@@ -255,7 +255,7 @@
 		return;
 	}
 	
-	if (sk->users) 
+	if (sk->sock_readers) 
 	{
 		/* 
 		 * Try again in second 
@@ -401,6 +401,8 @@
 		return;
 	}
 
+	lock_sock(sk);
+
 	/*
 	 * Clear delay ack timer
 	 */
@@ -412,16 +414,15 @@
 	 */
 
 	tp->retrans_head = NULL;
-	
 
 	if (sk->retransmits == 0)
 	{
-		/* 
-		 * remember window where we lost 
+		/*
+		 * remember window where we lost
 		 * "one half of the current window but at least 2 segments"
 		 */
-		
-		sk->ssthresh = max(sk->cong_window >> 1, 2); 
+
+		sk->ssthresh = max(sk->cong_window >> 1, 2);
 		sk->cong_count = 0;
 		sk->cong_window = 1;
 	}
@@ -452,6 +453,8 @@
 	tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 
 	tcp_write_timeout(sk);
+
+	release_sock(sk);
 }
 
 /*
@@ -472,7 +475,7 @@
 			struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 			
 			/* TCP_LISTEN is implied. */
-			if (!sk->users && tp->syn_wait_queue) {
+			if (!sk->sock_readers && tp->syn_wait_queue) {
 				struct open_request *req;
 
 				req = tp->syn_wait_queue;
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/timer.c linux/net/ipv4/timer.c
--- v2.1.29/linux/net/ipv4/timer.c	Sun Nov  3 01:04:45 1996
+++ linux/net/ipv4/timer.c	Thu Mar 20 18:17:14 1997
@@ -92,7 +92,7 @@
 	 * only process if socket is not in use
 	 */
 
-	if (sk->users)
+	if (sk->sock_readers)
 	{
 		sk->timer.expires = jiffies+HZ;
 		add_timer(&sk->timer);
diff -u --recursive --new-file v2.1.29/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.1.29/linux/net/ipv4/udp.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv4/udp.c	Thu Mar 20 18:17:14 1997
@@ -111,6 +111,7 @@
 #include <net/icmp.h>
 #include <net/route.h>
 #include <net/checksum.h>
+#include <linux/ipsec.h>
 
 /*
  *	Snmp MIB for the UDP layer
@@ -123,21 +124,28 @@
 static int udp_v4_verify_bind(struct sock *sk, unsigned short snum)
 {
 	struct sock *sk2;
-	int retval = 0;
+	int retval = 0, sk_reuse = sk->reuse;
 
 	SOCKHASH_LOCK();
 	for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
 		if((sk2->num == snum) && (sk2 != sk)) {
+			unsigned char state = sk2->state;
+			int sk2_reuse = sk2->reuse;
+
 			if(!sk2->rcv_saddr || !sk->rcv_saddr) {
-				if(sk->reuse && sk->reuse && (sk2->state != TCP_LISTEN))
-					continue;
-				retval = 1;
-				break;
-			}
-			if((sk2->rcv_saddr == sk->rcv_saddr) &&
-			   (!sk->reuse || !sk2->reuse || (sk2->state == TCP_LISTEN))) {
-				retval = 1;
-				break;
+				if((!sk2_reuse)			||
+				   (!sk_reuse)			||
+				   (state == TCP_LISTEN)) {
+					retval = 1;
+					break;
+				}
+			} else if(sk2->rcv_saddr == sk->rcv_saddr) {
+				if((!sk_reuse)			||
+				   (!sk2_reuse)			||
+				   (state == TCP_LISTEN)) {
+					retval = 1;
+					break;
+				}
 			}
 		}
 	}
@@ -431,7 +439,7 @@
 	if (sk == NULL)
 	  	return;	/* No socket for error */
 
-	if (sk->ip_recverr && !sk->users) {
+	if (sk->ip_recverr && !sk->sock_readers) {
 		struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
 		if (skb2 && sock_queue_err_skb(sk, skb2))
 			kfree_skb(skb2, FREE_READ);
@@ -929,6 +937,16 @@
 static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
 {
 	/*
+	 *	Check the security clearance
+	 */
+	 
+	if(!ipsec_sk_policy(sk,skb))
+	{	
+		kfree_skb(skb, FREE_WRITE);
+		return(0);
+	}
+	 
+	/*
 	 *	Charge it to the socket, dropping if the queue is full.
 	 */
 
@@ -946,7 +964,7 @@
 
 static inline void udp_deliver(struct sock *sk, struct sk_buff *skb)
 {
-	if (sk->users) {
+	if (sk->sock_readers) {
 		__skb_queue_tail(&sk->back_log, skb);
 		return;
 	}
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/Makefile linux/net/ipv6/Makefile
--- v2.1.29/linux/net/ipv6/Makefile	Wed Nov 13 01:07:41 1996
+++ linux/net/ipv6/Makefile	Thu Mar 20 18:17:14 1997
@@ -8,12 +8,18 @@
 
 
 O_TARGET := ipv6.o
-O_OBJS   := af_inet6.o ipv6_output.o ipv6_input.o addrconf.o sit.o \
-	    ipv6_route.o ipv6_sockglue.o ndisc.o udp.o raw.o \
-	    protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
-	    exthdrs.o sysctl_net_ipv6.o datagram.o
+IPV6_OBJS :=	af_inet6.o ip6_output.o ip6_input.o addrconf.o sit.o \
+		route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \
+		protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
+		exthdrs.o sysctl_net_ipv6.o datagram.o proc.o
 
 MOD_LIST_NAME := IPV6_MODULES
 M_OBJS   := $(O_TARGET)
+
+#ifeq ($(CONFIG_IPV6_FIREWALL),y)
+#  IPV6_OBJS += ip6_fw.o
+#endif
+
+O_OBJS := $(IPV6_OBJS)
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.1.29/linux/net/ipv6/addrconf.c	Mon Mar 17 14:54:36 1997
+++ linux/net/ipv6/addrconf.c	Thu Mar 20 18:17:14 1997
@@ -5,12 +5,14 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
+ *	$Id: addrconf.c,v 1.15 1997/03/18 18:24:23 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
  *      as published by the Free Software Foundation; either version
  *      2 of the License, or (at your option) any later version.
  */
+
 /*
  *	Changes:
  *
@@ -36,27 +38,35 @@
 #include <net/ipv6.h>
 #include <net/protocol.h>
 #include <net/ndisc.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
 #include <net/addrconf.h>
 #include <net/sit.h>
 
 #include <asm/uaccess.h>
 
-#define HASH_SIZE		16
+/* Set to 3 to get tracing... */
+#define ACONF_DEBUG 2
+
+#if ACONF_DEBUG >= 3
+#define ADBG(x) printk x
+#else
+#define ADBG(x)
+#endif
+
 /*
  *	Configured unicast address list
  */
-struct inet6_ifaddr		*inet6_addr_lst[HASH_SIZE];
+struct inet6_ifaddr		*inet6_addr_lst[IN6_ADDR_HSIZE];
 
 /*
  *	Hash list of configured multicast addresses
  */
-struct ipv6_mc_list		*inet6_mcast_lst[HASH_SIZE];
+struct ifmcaddr6		*inet6_mcast_lst[IN6_ADDR_HSIZE];
 
 /*
  *	AF_INET6 device list
  */
-struct inet6_dev		*inet6_dev_lst;
+struct inet6_dev		*inet6_dev_lst[IN6_ADDR_HSIZE];
 
 atomic_t			addr_list_lock = 0;
 
@@ -67,15 +77,11 @@
 	0, 0, addrconf_verify
 };
 
-
-int DupAddrDetectTransmits = 1;
-
-/*
- *	/proc/sys switch for autoconf (enabled by default)
- */
-int addrconf_sys_autoconf  = 1;
+static int addrconf_ifdown(struct device *dev);
 
 static void addrconf_dad_start(struct inet6_ifaddr *ifp);
+static void addrconf_dad_timer(unsigned long data);
+static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
 static void addrconf_rs_timer(unsigned long data);
 
 int ipv6_addr_type(struct in6_addr *addr)
@@ -89,58 +95,41 @@
 	 * 0x4/3
 	 */
 
-	if ((st & __constant_htonl(0xE0000000)) == 
-	    __constant_htonl(0x40000000))
-	{
+	if ((st & __constant_htonl(0xE0000000)) == __constant_htonl(0x40000000))
 		return IPV6_ADDR_UNICAST;
-	}
 
-	if ((st & __constant_htonl(0xFF000000)) == 
-	    __constant_htonl(0xFF000000))
-	{
+	if ((st & __constant_htonl(0xFF000000)) == __constant_htonl(0xFF000000)) {
 		int type = IPV6_ADDR_MULTICAST;
 
-		switch((st >> 16) & 0x0f)
-		{
-			case 0x01:
+		switch((st & __constant_htonl(0x00FF0000))) {
+			case __constant_htonl(0x00010000):
 				type |= IPV6_ADDR_LOOPBACK;
 				break;
-			case 0x02:
+
+			case __constant_htonl(0x00020000):
 				type |= IPV6_ADDR_LINKLOCAL;
 				break;
-			case 0x05:
+
+			case __constant_htonl(0x00050000):
 				type |= IPV6_ADDR_SITELOCAL;
 				break;
-		}
+		};
 		return type;
 	}
 	
-	if ((st & __constant_htonl(0xFFC00000)) == 
-	    __constant_htonl(0xFE800000))
-	{
+	if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFE800000))
 		return (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST);
-	}
 
-	if ((st & __constant_htonl(0xFFC00000)) == 
-	    __constant_htonl(0xFEC00000))
-	{
+	if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFEC00000))
 		return (IPV6_ADDR_SITELOCAL | IPV6_ADDR_UNICAST);
-	}
 
-	if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0)
-	{
-		if (addr->s6_addr32[2] == 0)
-		{
+	if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0) {
+		if (addr->s6_addr32[2] == 0) {
 			if (addr->in6_u.u6_addr32[3] == 0)
-			{
 				return IPV6_ADDR_ANY;
-			}
 
 			if (addr->s6_addr32[3] == __constant_htonl(0x00000001))
-			{
-				return (IPV6_ADDR_LOOPBACK | 
-					IPV6_ADDR_UNICAST);
-			}
+				return (IPV6_ADDR_LOOPBACK | IPV6_ADDR_UNICAST);
 
 			return (IPV6_ADDR_COMPATv4 | IPV6_ADDR_UNICAST);
 		}
@@ -152,81 +141,64 @@
 	return IPV6_ADDR_RESERVED;
 }
 
-struct inet6_dev * ipv6_add_dev(struct device *dev)
+static struct inet6_dev * ipv6_add_dev(struct device *dev)
 {
-	struct inet6_dev *dev6;
-
-	/*
-	 *	called by netdev notifier from a syscall
-	 */
-	dev6 = (struct inet6_dev *) kmalloc(sizeof(struct inet6_dev), 
-					    GFP_ATOMIC);
-
-	if (dev6 == NULL)
-		return NULL;
-
-	memset(dev6, 0, sizeof(struct inet6_dev));
-	dev6->dev = dev;
-	dev6->if_index = dev->ifindex;
+	struct inet6_dev *ndev, **bptr, *iter;
+	int hash;
 
-	/*
-	 *	insert at head.
-	 */
+	ndev = kmalloc(sizeof(struct inet6_dev), gfp_any());
 
-	dev6->next = inet6_dev_lst;
-	inet6_dev_lst = dev6;
+	if (ndev) {
+		memset(ndev, 0, sizeof(struct inet6_dev));
 
-	return dev6;
-}
+		ndev->dev = dev;
+		hash = ipv6_devindex_hash(dev->ifindex);
+		bptr = &inet6_dev_lst[hash];
+		iter = *bptr;
 
-struct inet6_dev * ipv6_dev_by_index(int index)
-{
-	struct inet6_dev *in6_dev;
+		for (; iter; iter = iter->next)
+			bptr = &iter->next;
 
-	for (in6_dev = inet6_dev_lst; in6_dev; in6_dev = in6_dev->next)
-	{
-		if (in6_dev->if_index == index)
-			return in6_dev;
+		*bptr = ndev;
 	}
-
-	return NULL;
+	return ndev;
 }
 
 void addrconf_forwarding_on(void)
 {
-	struct inet6_dev *in6_dev;
-	struct in6_addr maddr;
+	struct inet6_dev *idev;
+	int i;
 
-	for (in6_dev = inet6_dev_lst; in6_dev; in6_dev = in6_dev->next)
-	{
-		printk(KERN_DEBUG "dev %s\n", in6_dev->dev->name);
-
-		if (in6_dev->dev->type == ARPHRD_ETHER)
-		{
-			printk(KERN_DEBUG "joining all-routers\n");
-			in6_dev->router = 1;
-			ipv6_addr_all_routers(&maddr);
-			ipv6_dev_mc_inc(in6_dev->dev, &maddr);		
-		}
-	}
+	for (i = 0; i < IN6_ADDR_HSIZE; i++) {
+		for (idev = inet6_dev_lst[i]; idev; idev = idev->next) {
+#if ACONF_DEBUG >= 2
+			printk(KERN_DEBUG "dev %s\n", idev->dev->name);
+#endif
+
+			if (idev->dev->type == ARPHRD_ETHER) {
+				struct in6_addr maddr;
 
-	if (last_resort_rt && (last_resort_rt->rt_flags & RTI_ALLONLINK))
-	{
-		rt_release(last_resort_rt);
-		last_resort_rt = NULL;
+#if ACONF_DEBUG >= 2
+				printk(KERN_DEBUG "joining all-routers\n");
+#endif
+				idev->router = 1;
+				ipv6_addr_all_routers(&maddr);
+				ipv6_dev_mc_inc(idev->dev, &maddr);
+			}
+		}
 	}
 }
 
 struct inet6_dev * ipv6_get_idev(struct device *dev)
 {
-	struct inet6_dev *in6_dev;
+	struct inet6_dev *idev;
+	int hash;
 
-	for (in6_dev = inet6_dev_lst; in6_dev; in6_dev = in6_dev->next)
-	{
-		if (in6_dev->dev == dev)
-		{
-			return in6_dev;
-		}
+	hash = ipv6_devindex_hash(dev->ifindex);
+
+	for (idev = inet6_dev_lst[hash]; idev; idev = idev->next) {
+		if (idev->dev == dev)
+			return idev;
 	}
 	return NULL;
 }
@@ -234,45 +206,34 @@
 struct inet6_ifaddr * ipv6_add_addr(struct inet6_dev *idev, 
 				    struct in6_addr *addr, int scope)
 {
-	struct inet6_ifaddr * ifaddr;
+	struct inet6_ifaddr *ifa;
 	int hash;
-	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	ifa = kmalloc(sizeof(struct inet6_ifaddr), gfp_any());
 
-	ifaddr = (struct inet6_ifaddr *) kmalloc(sizeof(struct inet6_ifaddr), 
-						 GFP_ATOMIC);
-
-	if (ifaddr == NULL)
-	{
-		printk(KERN_DEBUG "ipv6_add_addr: malloc failed\n");
-		restore_flags(flags);
+	if (ifa == NULL) {
+		ADBG(("ipv6_add_addr: malloc failed\n"));
 		return NULL;
 	}
 
-	memset(ifaddr, 0, sizeof(struct inet6_ifaddr));
-	memcpy(&ifaddr->addr, addr, sizeof(struct in6_addr));
-
-	ifaddr->scope = scope;
-	ifaddr->idev = idev;
-	
+	memset(ifa, 0, sizeof(struct inet6_ifaddr));
+	memcpy(&ifa->addr, addr, sizeof(struct in6_addr));
 
-	/* add to list */
+	init_timer(&ifa->timer);
+	ifa->scope = scope;
+	ifa->idev = idev;
 
+	/* Add to list. */
 	hash = ipv6_addr_hash(addr);
 
-	ifaddr->lst_next = inet6_addr_lst[hash];
-	inet6_addr_lst[hash] = ifaddr;
-
+	ifa->lst_next = inet6_addr_lst[hash];
+	inet6_addr_lst[hash] = ifa;
 
-	/* add to inet6_dev unicast addr list */
-	ifaddr->if_next = idev->addr_list;
-	idev->addr_list = ifaddr;
+	/* Add to inet6_dev unicast addr list. */
+	ifa->if_next = idev->addr_list;
+	idev->addr_list = ifa;
 
-	restore_flags(flags);
-	return ifaddr;
-	
+	return ifa;
 }
 
 void ipv6_del_addr(struct inet6_ifaddr *ifp)
@@ -280,8 +241,7 @@
 	struct inet6_ifaddr *iter, **back;
 	int hash;
 
-	if (addr_list_lock)
-	{
+	if (addr_list_lock) {
 		ifp->flags |= ADDR_INVALID;
 		return;
 	}
@@ -291,10 +251,8 @@
 	iter = inet6_addr_lst[hash];
 	back = &inet6_addr_lst[hash];
 
-	for (; iter; iter = iter->lst_next)
-	{
-		if (iter == ifp)
-		{
+	for (; iter; iter = iter->lst_next) {
+		if (iter == ifp) {
 			*back = ifp->lst_next;
 			ifp->lst_next = NULL;
 			break;
@@ -305,10 +263,8 @@
 	iter = ifp->idev->addr_list;
 	back = &ifp->idev->addr_list;
 
-	for (; iter; iter = iter->if_next)
-	{
-		if (iter == ifp)
-		{
+	for (; iter; iter = iter->if_next) {
+		if (iter == ifp) {
 			*back = ifp->if_next;
 			ifp->if_next = NULL;
 			break;
@@ -327,30 +283,26 @@
  *		an address of the attached interface 
  *	iii)	don't use deprecated addresses
  *
- *	at the moment i believe only iii) is missing.
+ *	at the moment I believe only iii) is missing.
  */
-struct inet6_ifaddr * ipv6_get_saddr(struct rt6_info *rt, struct in6_addr *daddr)
+struct inet6_ifaddr * ipv6_get_saddr(struct dst_entry *dst,
+				     struct in6_addr *daddr)
 {
 	int scope;
-	struct inet6_ifaddr * ifp = NULL;
-	struct inet6_dev    * i6dev;
-	struct inet6_ifaddr * match = NULL;
+	struct inet6_ifaddr *ifp = NULL;
+	struct inet6_ifaddr *match = NULL;
 	struct device *dev = NULL;
+	struct rt6_info *rt;
 	int i;
 
+	rt = (struct rt6_info *) dst;
 	if (rt)
-	{
-		dev = rt->rt_dev;
-	}
+		dev = rt->rt6i_dev;
 	
 	atomic_inc(&addr_list_lock);
 
-	scope = ipv6_addr_type(daddr);
-
-	scope &= IPV6_ADDR_SCOPE_MASK;
-
-	if (rt && (rt->rt_flags & RTI_ALLONLINK))
-	{
+	scope = ipv6_addr_scope(daddr);
+	if (rt && (rt->rt6i_flags & RTF_ALLONLINK)) {
 		/*
 		 *	route for the "all destinations on link" rule
 		 *	when no routers are present
@@ -363,30 +315,23 @@
 	 *	search dev and walk through dev addresses
 	 */
 
-	if (dev)
-	{
+	if (dev) {
+		struct inet6_dev *idev;
+		int hash;
+
 		if (dev->flags & IFF_LOOPBACK)
-		{
 			scope = IFA_HOST;
-		}
 
-		for (i6dev = inet6_dev_lst; i6dev; i6dev=i6dev->next)
-		{
-			if (i6dev->dev == dev)
-			{
-				for (ifp=i6dev->addr_list; ifp; 
-				     ifp=ifp->if_next)
-				{
-					if (ifp->scope == scope)
-					{
+		hash = ipv6_devindex_hash(dev->ifindex);
+		for (idev = inet6_dev_lst[hash]; idev; idev=idev->next) {
+			if (idev->dev == dev) {
+				for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
+					if (ifp->scope == scope) {
 						if (!(ifp->flags & ADDR_STATUS))
-						{
 							goto out;
-						}
+
 						if (!(ifp->flags & ADDR_INVALID))
-						{
 							match = ifp;
-						}
 					}
 				}
 				break;
@@ -395,37 +340,27 @@
 	}
 
 	if (scope == IFA_LINK)
-	{
 		goto out;
-	}
 
 	/*
 	 *	dev == NULL or search failed for specified dev
 	 */
 
-	for (i=0; i < HASH_SIZE; i++)
-	{
-		for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next)
-		{
-			if (ifp->scope == scope)
-			{
+	for (i=0; i < IN6_ADDR_HSIZE; i++) {
+		for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {
+			if (ifp->scope == scope) {
 				if (!(ifp->flags & ADDR_STATUS))
-				{
 					goto out;
-				}
+
 				if (!(ifp->flags & ADDR_INVALID))
-				{
 					match = ifp;
-				}
 			}
 		}
 	}
 
-  out:
+out:
 	if (ifp == NULL && match)
-	{
 		ifp = match;
-	}
 	atomic_dec(&addr_list_lock);
 	return ifp;
 }
@@ -433,14 +368,14 @@
 struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev)
 {
 	struct inet6_ifaddr *ifp;
-	struct inet6_dev *i6dev;
+	struct inet6_dev *idev;
+	int hash;
 
-	for (i6dev = inet6_dev_lst; i6dev; i6dev=i6dev->next)
-	{
-		if (i6dev->dev == dev)
-		{
-			for (ifp=i6dev->addr_list; ifp; ifp=ifp->if_next)
-			{
+	hash = ipv6_devindex_hash(dev->ifindex);
+
+	for (idev = inet6_dev_lst[hash]; idev; idev=idev->next) {
+		if (idev->dev == dev) {
+			for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
 				if (ifp->scope == IFA_LINK)
 					return ifp;
 			}
@@ -464,154 +399,15 @@
 	atomic_inc(&addr_list_lock);
 
 	hash = ipv6_addr_hash(addr);
-
-	for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next)
-	{
+	for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
 		if (ipv6_addr_cmp(&ifp->addr, addr) == 0)
-		{
 			break;
-		}
 	}
 
 	atomic_dec(&addr_list_lock);
 	return ifp;	
 }
 
-static void sit_route_add(struct inet6_dev *idev)
-{
-	struct in6_rtmsg rtmsg;
-	struct device *dev = idev->dev;
-	int err;
-
-	rtmsg.rtmsg_type = RTMSG_NEWROUTE;
-
-	memset(&rtmsg.rtmsg_dst, 0, sizeof(struct in6_addr));
-	memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
-
-	if (dev->pa_dstaddr == 0)
-	{
-		/* prefix length - 96 bytes "::d.d.d.d" */
-		rtmsg.rtmsg_prefixlen = 96;
-		rtmsg.rtmsg_metric = 1;
-		rtmsg.rtmsg_flags = RTF_NEXTHOP|RTF_UP;
-	}
-	else
-	{
-		rtmsg.rtmsg_prefixlen = 10;
-		rtmsg.rtmsg_dst.s6_addr32[0] = __constant_htonl(0xfe800000);
-		rtmsg.rtmsg_dst.s6_addr32[3] = dev->pa_dstaddr;
-		rtmsg.rtmsg_metric = 1;
-		rtmsg.rtmsg_flags = RTF_NEXTHOP|RTF_UP;
-	}
-
-	rtmsg.rtmsg_ifindex = idev->if_index; 
-
-	err = ipv6_route_add(&rtmsg);
-
-	if (err)
-	{
-		printk(KERN_DEBUG "sit_route_add: error in route_add\n");
-	}
-}
-
-static void init_loopback(struct device *dev)
-{
-	struct in6_addr addr;
-	struct inet6_dev  *idev;
-	struct inet6_ifaddr * ifp;
-	struct in6_rtmsg rtmsg;
-	int err;
-
-	/* ::1 */
-
-	memset(&addr, 0, sizeof(struct in6_addr));
-	addr.s6_addr[15] = 1;
-
-	idev = ipv6_add_dev(dev);
-
-	if (idev == NULL)
-	{
-		printk(KERN_DEBUG "init loopback: add_dev failed\n");
-		return;
-	}
-
-	ifp = ipv6_add_addr(idev, &addr, IFA_HOST);
-
-	if (ifp == NULL)
-	{
-		printk(KERN_DEBUG "init_loopback: add_addr failed\n");
-		return;
-	}
-
-	ifp->flags |= ADDR_PERMANENT;
-
-	memcpy(&rtmsg.rtmsg_dst, &addr, sizeof(struct in6_addr));
-	memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
-
-	rtmsg.rtmsg_prefixlen = 128;
-	rtmsg.rtmsg_metric = 1;
-	rtmsg.rtmsg_ifindex = idev->if_index;
-
-	rtmsg.rtmsg_flags = RTF_NEXTHOP|RTF_HOST|RTF_UP;
-
-	err = ipv6_route_add(&rtmsg);
-
-	if (err)
-	{
-		printk(KERN_DEBUG "init_loopback: error in route_add\n");
-	}
-
-	/* add route for ::127.0.0.1 */
-}
-
-static void addrconf_eth_config(struct device *dev)
-{
-	struct in6_addr addr;
-	struct in6_addr maddr;
-	struct inet6_ifaddr * ifp;
-	struct inet6_dev    * idev;
-
-	memset(&addr, 0, sizeof(struct in6_addr));
-
-	/* generate link local address*/
-	addr.s6_addr[0] = 0xFE;
-	addr.s6_addr[1] = 0x80;
-
-	memcpy(addr.s6_addr + (sizeof(struct in6_addr) - dev->addr_len), 
-	       dev->dev_addr, dev->addr_len);
-
-	idev = ipv6_add_dev(dev);
-			
-	if (idev == NULL)
-		return;
-	
-	ifp = ipv6_add_addr(idev, &addr, IFA_LINK);
-			
-	if (ifp == NULL)
-		return;
-
-	ifp->flags |= (DAD_INCOMPLETE | ADDR_PERMANENT);
-	ifp->prefix_len = 10;
-
-	/* join to all nodes multicast group */
-	ipv6_addr_all_nodes(&maddr);
-	ipv6_dev_mc_inc(dev, &maddr);
-	
-	if (ipv6_forwarding)
-	{
-		idev->router = 1;
-		ipv6_addr_all_routers(&maddr);
-		ipv6_dev_mc_inc(dev, &maddr);		
-	}
-
-	/* join to solicited addr multicast group */
-	addrconf_addr_solict_mult(&addr, &maddr);
-	ipv6_dev_mc_inc(dev, &maddr);
-			
-	/* start dad */
-	addrconf_dad_start(ifp);
-}
-
 void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
 {
 	struct prefix_info *pinfo;
@@ -623,9 +419,8 @@
 
 	pinfo = (struct prefix_info *) opt;
 	
-	if (len < sizeof(struct prefix_info))
-	{
-		printk(KERN_DEBUG "addrconf: prefix option too short\n");
+	if (len < sizeof(struct prefix_info)) {
+		ADBG(("addrconf: prefix option too short\n"));
 		return;
 	}
 	
@@ -636,17 +431,13 @@
 	addr_type = ipv6_addr_type(&pinfo->prefix);
 
 	if (addr_type & IPV6_ADDR_LINKLOCAL)
-	{
 		return;
-	}
 
 	valid_lft = ntohl(pinfo->valid);
 	prefered_lft = ntohl(pinfo->prefered);
 
-	if (prefered_lft > valid_lft)
-	{
-		printk(KERN_WARNING
-		       "addrconf: prefix option has invalid lifetime\n");
+	if (prefered_lft > valid_lft) {
+		printk(KERN_WARNING "addrconf: prefix option has invalid lifetime\n");
 		return;
 	}
 
@@ -655,11 +446,7 @@
 	 *	delete it
 	 */
 
-	if (last_resort_rt && (last_resort_rt->rt_flags & RTI_ALLONLINK))
-	{
-		rt_release(last_resort_rt);
-		last_resort_rt = NULL;
-	}
+	rt6_purge_dflt_routers(RTF_ALLONLINK);
 
 	/*
 	 *	Two things going on here:
@@ -669,178 +456,86 @@
 
 	rt_expires = jiffies + valid_lft * HZ;
 	if (rt_expires < jiffies)
-	{
 		rt_expires = ~0;
-	}
 
-	rt = fibv6_lookup(&pinfo->prefix, dev, RTI_DYNAMIC|RTI_GATEWAY);
-		
-	if (rt)
-	{
-		if (pinfo->onlink == 0 || valid_lft == 0)
-		{
-			/*
-			 *	delete route
-			 */
-			fib6_del_rt(rt);
+	rt = rt6_lookup(&pinfo->prefix, NULL, dev, RTF_LINKRT);
+
+	if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
+		if (pinfo->onlink == 0 || valid_lft == 0) {
+			ip6_del_rt(rt);
 			rt = NULL;
+		} else {
+			rt->rt6i_expires = rt_expires;
 		}
-		else
-		{
-			rt->rt_expires = rt_expires;
-		}
-	}
-	else if (pinfo->onlink && valid_lft)
-	{
+	} else if (pinfo->onlink && valid_lft) {
 		struct in6_rtmsg rtmsg;
-		struct inet6_dev *idev;
+		int err;
+
+		memset(&rtmsg, 0, sizeof(rtmsg));
 		
 		printk(KERN_DEBUG "adding on link route\n");
-		ipv6_addr_copy(&rtmsg.rtmsg_dst, &pinfo->prefix);
-		memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
 
-		rtmsg.rtmsg_prefixlen = pinfo->prefix_len;
-		rtmsg.rtmsg_metric = 1;
-		
-		if ((idev = ipv6_get_idev(dev)))
-		{
-			rtmsg.rtmsg_ifindex = idev->if_index;
-		}
-		rtmsg.rtmsg_flags = RTF_UP | RTF_ADDRCONF;
-		rtmsg.rtmsg_info = rt_expires;
+		ipv6_addr_copy(&rtmsg.rtmsg_dst, &pinfo->prefix);
+		rtmsg.rtmsg_dst_len	= pinfo->prefix_len;
+		rtmsg.rtmsg_metric	= IP6_RT_PRIO_ADDRCONF;
+		rtmsg.rtmsg_ifindex	= dev->ifindex;
+		rtmsg.rtmsg_flags	= RTF_UP | RTF_ADDRCONF;
+		rtmsg.rtmsg_info	= rt_expires;
 
-		ipv6_route_add(&rtmsg);
+		ip6_route_add(&rtmsg, &err);
 	}
 
-	if (pinfo->autoconf && addrconf_sys_autoconf)
-	{
+	if (pinfo->autoconf && ipv6_config.autoconf) {
 		struct inet6_ifaddr * ifp;
 		struct in6_addr addr;
 		int plen;
 
 		plen = pinfo->prefix_len >> 3;
 
-		if (plen + dev->addr_len == sizeof(struct in6_addr))
-		{
+		if (plen + dev->addr_len == sizeof(struct in6_addr)) {
 			memcpy(&addr, &pinfo->prefix, plen);
 			memcpy(addr.s6_addr + plen, dev->dev_addr,
 			       dev->addr_len);
-		}
-		else
-		{
-			printk(KERN_DEBUG
-			       "addrconf: prefix_len invalid\n");
+		} else {
+			ADBG(("addrconf: prefix_len invalid\n"));
 			return;
 		}
 
 		ifp = ipv6_chk_addr(&addr);
 
-		if (ifp == NULL && valid_lft)
-		{
-			/* create */
-
-			struct inet6_dev *in6_dev;
-
-			in6_dev = ipv6_get_idev(dev);
+		if (ifp == NULL && valid_lft) {
+			struct inet6_dev *in6_dev = ipv6_get_idev(dev);
 
 			if (in6_dev == NULL)
-			{
-				printk(KERN_DEBUG
-				       "addrconf: device not configured\n");
-			}
+				ADBG(("addrconf: device not configured\n"));
 			
 			ifp = ipv6_add_addr(in6_dev, &addr,
 					    addr_type & IPV6_ADDR_SCOPE_MASK);
 
-			if (dev->flags & IFF_MULTICAST)
-			{
+			if (dev->flags & IFF_MULTICAST) {
 				struct in6_addr maddr;
 
-				/* join to solicited addr multicast group */
+				/* Join to solicited addr multicast group. */
 				addrconf_addr_solict_mult(&addr, &maddr);
 				ipv6_dev_mc_inc(dev, &maddr);
 			}
 
-			ifp->flags |= DAD_INCOMPLETE;
 			ifp->prefix_len = pinfo->prefix_len;
 
 			addrconf_dad_start(ifp);
-			
 		}
 
-		if (ifp && valid_lft == 0)
-		{
+		if (ifp && valid_lft == 0) {
 			ipv6_del_addr(ifp);
 			ifp = NULL;
 		}
 
-		if (ifp)
-		{
+		if (ifp) {
 			ifp->valid_lft = valid_lft;
 			ifp->prefered_lft = prefered_lft;
 			ifp->tstamp = jiffies;
 		}
 	}
-
-}
-
-static int addrconf_ifdown(struct device *dev)
-{
-	struct inet6_dev *idev, **bidev;
-	struct inet6_ifaddr *ifa, **bifa;
-	int i;
-
-	start_bh_atomic();
-
-	bidev = &inet6_dev_lst;
-
-	for (idev = inet6_dev_lst; idev; idev = idev->next)
-	{
-		if (idev->dev == dev)
-		{
-			*bidev = idev->next;
-			break;
-		}
-		bidev = &idev;
-	}
-
-	if (idev == NULL)
-	{
-		printk(KERN_DEBUG "addrconf_ifdown: device not found\n");
-		end_bh_atomic();
-		return -ENODEV;
-	}
-	
-	/*
-	 *	FIXME: clear multicast group membership
-	 */
-
-	/*
-	 *	clean addr_list
-	 */
-
-	for (i=0; i<16; i++)
-	{
-		bifa = &inet6_addr_lst[i];
-		
-		for (ifa=inet6_addr_lst[i]; ifa; )
-		{
-			if (ifa->idev == idev)
-			{
-				*bifa = ifa->lst_next;
-				del_timer(&ifa->timer);
-				kfree(ifa);
-				ifa = *bifa;
-				continue;
-			}
-			bifa = &ifa;
-			ifa = ifa->lst_next;
-		}
-	}
-
-	kfree(idev);
-	end_bh_atomic();
-	return 0;
 }
 
 /*
@@ -851,41 +546,31 @@
 int addrconf_set_dstaddr(void *arg)
 {
 	struct in6_ifreq ireq;
-	struct inet6_dev *idev;
 	struct device *dev;
 	int err = -EINVAL;
 
-	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
-	{
+	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) {
 		err = -EFAULT;
 		goto err_exit;
 	}
 
-	idev = ipv6_dev_by_index(ireq.ifr6_ifindex);
+	dev = dev_get_by_index(ireq.ifr6_ifindex);
 
-	if (idev == NULL)
-	{
+	if (dev == NULL) {
 		err = -ENODEV;
 		goto err_exit;
 	}
 
-	dev = idev->dev;
-
-	if (dev->type == ARPHRD_SIT)
-	{
+	if (dev->type == ARPHRD_SIT) {
 		struct device *dev;
 		
 		if (!(ipv6_addr_type(&ireq.ifr6_addr) & IPV6_ADDR_COMPATv4))
-		{
 			return -EADDRNOTAVAIL;
-		}
 		
 		dev = sit_add_tunnel(ireq.ifr6_addr.s6_addr32[3]);
 		
 		if (dev == NULL)
-		{
 			err = -ENODEV;
-		}
 		else
 			err = 0;
 	}
@@ -899,87 +584,141 @@
  */
 int addrconf_add_ifaddr(void *arg)
 {
-	struct inet6_dev *in6_dev;
+	struct inet6_dev *idev;
 	struct in6_ifreq ireq;
 	struct inet6_ifaddr *ifp;
 	struct device *dev;
-	int addr_type;
-	int err;
+	int scope;
 	
 	if (!suser())
 		return -EPERM;
 	
-	err = copy_from_user(&ireq, arg, sizeof(struct in6_ifreq));
-	if (err)
+	if(copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
 		return -EFAULT;
 
-	in6_dev = ipv6_dev_by_index(ireq.ifr6_ifindex);
+	if((dev = dev_get_by_index(ireq.ifr6_ifindex)) == NULL)
+		return -EINVAL;
 
-	if (in6_dev == NULL)
+	if ((idev = ipv6_get_idev(dev)) == NULL)
 		return -EINVAL;
 
-	dev = in6_dev->dev;
-	
-	addr_type  = ipv6_addr_type(&ireq.ifr6_addr);
-	addr_type &= IPV6_ADDR_SCOPE_MASK;
-	
-	ifp = ipv6_add_addr(in6_dev, &ireq.ifr6_addr, addr_type);
+	scope = ipv6_addr_scope(&ireq.ifr6_addr);
 
-	if (ifp == NULL)
+	if((ifp = ipv6_add_addr(idev, &ireq.ifr6_addr, scope)) == NULL)
 		return -ENOMEM;
 
 	ifp->prefix_len = 128;
 
-	if (dev->flags & IFF_MULTICAST)
-	{
+	if (dev->flags & IFF_MULTICAST) {
 		struct in6_addr maddr;
 
-		/* join to solicited addr multicast group */
+		/* Join to solicited addr multicast group. */
 		addrconf_addr_solict_mult(&ireq.ifr6_addr, &maddr);
 		ipv6_dev_mc_inc(dev, &maddr);
 	}
 
-
 	ifp->prefix_len = ireq.ifr6_prefixlen;
 	ifp->flags |= ADDR_PERMANENT;
 
 	if (!(dev->flags & (IFF_NOARP|IFF_LOOPBACK)))
-	{
-		ifp->flags |= DAD_INCOMPLETE;
 		addrconf_dad_start(ifp);
-	}
+	else
+		ip6_rt_addr_add(&ifp->addr, dev);
+
 	return 0;
 }
 
-static void sit_add_v4_addrs(struct inet6_dev *idev)
+static void sit_route_add(struct device *dev)
 {
-	struct inet6_ifaddr * ifp;
-	struct in6_addr addr;
-	struct device *dev;
-	int scope;
-
+	struct in6_rtmsg rtmsg;
+	struct rt6_info *rt;
+	int err;
+
+	ADBG(("sit_route_add(%s): ", dev->name));
+	memset(&rtmsg, 0, sizeof(rtmsg));
+
+	rtmsg.rtmsg_type			= RTMSG_NEWROUTE;
+	rtmsg.rtmsg_metric			= IP6_RT_PRIO_ADDRCONF;
+
+	if (dev->pa_dstaddr == 0) {
+		ADBG(("pa_dstaddr=0, "));
+		/* prefix length - 96 bytes "::d.d.d.d" */
+		rtmsg.rtmsg_dst_len		= 96;
+		rtmsg.rtmsg_flags		= RTF_NONEXTHOP|RTF_UP;
+	} else {
+		ADBG(("pa_dstaddr=%08x, ", dev->pa_dstaddr));
+		rtmsg.rtmsg_dst_len 		= 10;
+		rtmsg.rtmsg_dst.s6_addr32[0]	= __constant_htonl(0xfe800000);
+		rtmsg.rtmsg_dst.s6_addr32[3]	= dev->pa_dstaddr;
+		rtmsg.rtmsg_gateway.s6_addr32[3]= dev->pa_dstaddr;
+		rtmsg.rtmsg_flags		= RTF_UP;
+	}
+
+	rtmsg.rtmsg_ifindex 			= dev->ifindex; 
+	ADBG(("doing ip6_route_add()\n"));
+	rt = ip6_route_add(&rtmsg, &err);
+	
+	if (err) {
+#if ACONF_DEBUG >= 1
+		printk(KERN_DEBUG "sit_route_add: error %d in route_add\n", err);
+#endif
+	}
+
+	ADBG(("sit_route_add(cont): "));
+	if (dev->pa_dstaddr) {
+		struct rt6_info *mrt;
+
+		ADBG(("pa_dstaddr != 0, "));
+		rt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_gateway);
+		if (rt->rt6i_nexthop == NULL) {
+			ADBG(("can't get neighbour\n"));
+			printk(KERN_DEBUG "sit_route: get_neigh failed\n");
+		}
+
+		/*
+		 *	Add multicast route.
+		 */
+		ADBG(("add MULT, "));
+		ipv6_addr_set(&rtmsg.rtmsg_dst, __constant_htonl(0xFF000000), 0, 0, 0);
+
+		rtmsg.rtmsg_dst_len = 8;
+		rtmsg.rtmsg_flags = RTF_UP;
+		rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+
+		memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
+		ADBG(("doing ip6_route_add()\n"));
+		mrt = ip6_route_add(&rtmsg, &err);
+
+		if (mrt)
+			mrt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
+	} else {
+		ADBG(("pa_dstaddr==0\n"));
+	}
+}
+
+static void sit_add_v4_addrs(struct inet6_dev *idev)
+{
+	struct inet6_ifaddr * ifp;
+	struct in6_addr addr;
+	struct device *dev;
+	int scope;
+
 	memset(&addr, 0, sizeof(struct in6_addr));
 
-	if (idev->dev->pa_dstaddr)
-	{
+	if (idev->dev->pa_dstaddr) {
 		addr.s6_addr32[0] = __constant_htonl(0xfe800000);
 		scope = IFA_LINK;
-	}
-	else
-	{
+	} else {
 		scope = IPV6_ADDR_COMPATv4;
 	}
 
-        for (dev = dev_base; dev != NULL; dev = dev->next) 
-        {
-		if (dev->family == AF_INET && (dev->flags & IFF_UP))
-		{
+        for (dev = dev_base; dev != NULL; dev = dev->next) {
+		if (dev->family == AF_INET && (dev->flags & IFF_UP)) {
 			int flag = scope;
 			
 			addr.s6_addr32[3] = dev->pa_addr;
 
-			if (dev->flags & IFF_LOOPBACK)
-			{
+			if (dev->flags & IFF_LOOPBACK) {
 				if (idev->dev->pa_dstaddr)
 					continue;
 				
@@ -987,15 +726,94 @@
 			}
 
 			ifp = ipv6_add_addr(idev, &addr, flag);
-			
+
 			if (ifp == NULL)
 				continue;
 
 			ifp->flags |= ADDR_PERMANENT;
+			ip6_rt_addr_add(&ifp->addr, dev);
 		}
         }
 }
 
+static void init_loopback(struct device *dev)
+{
+	struct in6_addr addr;
+	struct inet6_dev  *idev;
+	struct inet6_ifaddr * ifp;
+	int err;
+
+	/* ::1 */
+
+	memset(&addr, 0, sizeof(struct in6_addr));
+	addr.s6_addr[15] = 1;
+
+	idev = ipv6_add_dev(dev);
+
+	if (idev == NULL) {
+		printk(KERN_DEBUG "init loopback: add_dev failed\n");
+		return;
+	}
+
+	ifp = ipv6_add_addr(idev, &addr, IFA_HOST);
+
+	if (ifp == NULL) {
+		printk(KERN_DEBUG "init_loopback: add_addr failed\n");
+		return;
+	}
+
+	ifp->flags |= ADDR_PERMANENT;
+
+	err = ip6_rt_addr_add(&addr, dev);
+	if (err)
+		printk(KERN_DEBUG "init_loopback: error in route_add\n");
+}
+
+static void addrconf_eth_config(struct device *dev)
+{
+	struct in6_addr addr;
+	struct in6_addr maddr;
+	struct inet6_ifaddr * ifp;
+	struct inet6_dev    * idev;
+
+	memset(&addr, 0, sizeof(struct in6_addr));
+
+	/* Generate link local address. */
+	addr.s6_addr[0] = 0xFE;
+	addr.s6_addr[1] = 0x80;
+
+	memcpy(addr.s6_addr + (sizeof(struct in6_addr) - dev->addr_len), 
+	       dev->dev_addr, dev->addr_len);
+
+	idev = ipv6_add_dev(dev);
+	if (idev == NULL)
+		return;
+	
+	ifp = ipv6_add_addr(idev, &addr, IFA_LINK);
+	if (ifp == NULL)
+		return;
+
+	ifp->flags = ADDR_PERMANENT;
+	ifp->prefix_len = 10;
+
+	/* Join to all nodes multicast group. */
+	ipv6_addr_all_nodes(&maddr);
+	ipv6_dev_mc_inc(dev, &maddr);
+
+	if (ipv6_config.forwarding) {
+		idev->router = 1;
+		ipv6_addr_all_routers(&maddr);
+		ipv6_dev_mc_inc(dev, &maddr);
+	}
+
+	/* Join to solicited addr multicast group. */
+	addrconf_addr_solict_mult(&addr, &maddr);
+	ipv6_dev_mc_inc(dev, &maddr);
+
+	/* Start duplicate address detection. */
+	addrconf_dad_start(ifp);
+}
+
 int addrconf_notify(struct notifier_block *this, unsigned long event, 
 		    void * data)
 {
@@ -1026,7 +844,7 @@
 			 *  route.
 			 */
 
-			sit_route_add(idev);
+			sit_route_add(dev);
 			break;
 
 		case ARPHRD_LOOPBACK:
@@ -1034,12 +852,12 @@
 			break;
 
 		case ARPHRD_ETHER:
-
 			printk(KERN_DEBUG "Configuring eth interface\n");
 			addrconf_eth_config(dev);
 			break;
-		}
-		rt6_sndmsg(RTMSG_NEWDEVICE, NULL, NULL, 0, dev, 0, 0);
+		};
+
+		rt6_sndmsg(RTMSG_NEWDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
 		break;
 
 	case NETDEV_DOWN:
@@ -1047,104 +865,72 @@
 		 *	Remove all addresses from this interface
 		 *	and take the interface out of the list.
 		 */
-		if (addrconf_ifdown(dev) == 0)
-		{
+		if (addrconf_ifdown(dev) == 0) {
+#if 0
 			rt6_ifdown(dev);
-			rt6_sndmsg(RTMSG_DELDEVICE, NULL, NULL, 0, dev, 0, 0);
+#endif
+			rt6_sndmsg(RTMSG_DELDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
 		}
 
 		break;
-	}
+	};
 	
 	return NOTIFY_OK;
 }
 
-static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
-{
-	struct in6_rtmsg rtmsg;
-	struct device *dev;
-	int err;
-
-
-	if (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)
-	{
-		struct in6_addr all_routers;
 
-		/*
-		 *	1) configure a link route for this interface
-		 *	2) send a (delayed) router solicitation
-		 */
-
-		memcpy(&rtmsg.rtmsg_dst, &ifp->addr, sizeof(struct in6_addr));
-		memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
-
-		dev = ifp->idev->dev;
-
-		rtmsg.rtmsg_prefixlen = ifp->prefix_len;
-		rtmsg.rtmsg_metric = 1;
-		rtmsg.rtmsg_ifindex = ifp->idev->if_index;
-
-		rtmsg.rtmsg_flags = RTF_UP;
-
-		err = ipv6_route_add(&rtmsg);
-		
-		if (err)
-		{
-			printk(KERN_DEBUG "dad_complete: error in route_add\n");
-		}
+static int addrconf_ifdown(struct device *dev)
+{
+	struct inet6_dev *idev, **bidev;
+	struct inet6_ifaddr *ifa, **bifa;
+	int i, hash;
 
-		if (ipv6_forwarding == 0)
-		{
-			ipv6_addr_set(&all_routers,
-				      __constant_htonl(0xff020000U), 0, 0,
-				      __constant_htonl(0x2U));
+	start_bh_atomic();
 
-			/*
-			 *	If a host as already performed a random delay
-			 *	[...] as part of DAD [...] there is no need
-			 *	to delay again before sending the first RS
-			 */
-			ndisc_send_rs(ifp->idev->dev, &ifp->addr,
-				      &all_routers);
+	hash = ipv6_devindex_hash(dev->ifindex);
+	bidev = &inet6_dev_lst[hash];
 
-			ifp->probes = 1;
-			ifp->timer.function = addrconf_rs_timer;
-			ifp->timer.expires = (jiffies + 
-					      RTR_SOLICITATION_INTERVAL);
-			ifp->idev->if_flags |= IF_RS_SENT;
-			add_timer(&ifp->timer);
+	for (idev = inet6_dev_lst[hash]; idev; idev = idev->next) {
+		if (idev->dev == dev) {
+			*bidev = idev->next;
+			break;
 		}
+		bidev = &idev->next;
 	}
 
-}
+	if (idev == NULL) {
+		printk(KERN_DEBUG "addrconf_ifdown: device not found\n");
+		end_bh_atomic();
+		return -ENODEV;
+	}
 
-static void addrconf_dad_timer(unsigned long data)
-{
-	struct inet6_ifaddr *ifp;
-	struct in6_addr unspec;
-	struct in6_addr mcaddr;
+	/*
+	 *	FIXME: clear multicast group membership
+	 */
 
-	ifp = (struct inet6_ifaddr *) data;
+	/*
+	 *	clean addr_list
+	 */
 
-	if (--ifp->probes == 0)
-	{
-		/*
-		 * DAD was successful
-		 */
+	for (i=0; i<16; i++) {
+		bifa = &inet6_addr_lst[i];
 
-		ifp->flags &= ~DAD_INCOMPLETE;
-		addrconf_dad_completed(ifp);
-		return;
+		for (ifa=inet6_addr_lst[i]; ifa; ) {
+			if (ifa->idev == idev) {
+				*bifa = ifa->lst_next;
+				del_timer(&ifa->timer);
+				kfree(ifa);
+				ifa = *bifa;
+				continue;
+			}
+			ifa = ifa->lst_next;
+			bifa = &ifa->lst_next;
+		}
 	}
 
-	/* send a neighbour solicitation for our addr */
-	memset(&unspec, 0, sizeof(unspec));
-	addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
-
-	ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
-
-	ifp->timer.expires = jiffies + RETRANS_TIMER;
-	add_timer(&ifp->timer);
+	kfree(idev);
+	end_bh_atomic();
+	return 0;
 }
 
 static void addrconf_rs_timer(unsigned long data)
@@ -1153,11 +939,10 @@
 
 	ifp = (struct inet6_ifaddr *) data;
 
-	if (ipv6_forwarding)
+	if (ipv6_config.forwarding)
 		return;
 
-	if (ifp->idev->if_flags & IF_RA_RCVD)
-	{
+	if (ifp->idev->if_flags & IF_RA_RCVD) {
 		/*
 		 *	Announcement received after solicitation
 		 *	was sent
@@ -1165,8 +950,7 @@
 		return;
 	}
 
-	if (ifp->probes++ <= MAX_RTR_SOLICITATIONS)
-	{
+	if (ifp->probes++ <= ipv6_config.rtr_solicits) {
 		struct in6_addr all_routers;
 
 		ipv6_addr_set(&all_routers,
@@ -1175,54 +959,77 @@
 
 		ndisc_send_rs(ifp->idev->dev, &ifp->addr,
 			      &all_routers);
-	
 		
 		ifp->timer.function = addrconf_rs_timer;
-		ifp->timer.expires = jiffies + RTR_SOLICITATION_INTERVAL;
+		ifp->timer.expires = (jiffies + 
+				      ipv6_config.rtr_solicit_interval);
 		add_timer(&ifp->timer);
-	}
-	else
-	{
+	} else {
+		struct in6_rtmsg rtmsg;
+		int err;
+
+#if ACONF_DEBUG >= 2
 		printk(KERN_DEBUG "%s: no IPv6 routers present\n",
 		       ifp->idev->dev->name);
+#endif
 
-		if (!default_rt_list && !last_resort_rt)
-		{
-			struct rt6_info *rt;
+		memset(&rtmsg, 0, sizeof(struct in6_rtmsg));
+		rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+		rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+		rtmsg.rtmsg_flags = (RTF_ALLONLINK | RTF_ADDRCONF | 
+				     RTF_DEFAULT | RTF_UP);
 
-			/*
-			 *	create a last resort route with all
-			 *	destinations on link
-			 */
-			rt = kmalloc(sizeof(struct rt6_info), GFP_ATOMIC);
+		rtmsg.rtmsg_ifindex = ifp->idev->dev->ifindex;
 
-			if (rt)
-			{
-				memset(rt, 0, sizeof(struct rt6_info));
-				rt->rt_dev = ifp->idev->dev;
-				rt->rt_ref = 1;
-				rt->rt_flags = (RTI_ALLONLINK | RTF_UP);
-				last_resort_rt = rt;
-			}
-		}
+		ip6_route_add(&rtmsg, &err);
 	}
 }
 
+/*
+ *	Duplicate Address Detection
+ */
 static void addrconf_dad_start(struct inet6_ifaddr *ifp)
 {
 	static int rand_seed = 1;
-	int rand_num;
+	struct device *dev;
+	unsigned long rand_num;
+
+	dev = ifp->idev->dev;
+
+	if (dev->flags & IFF_MULTICAST) {
+		struct in6_rtmsg rtmsg;
+		struct rt6_info *mrt;
+		int err;
+
+		memset(&rtmsg, 0, sizeof(rtmsg));
+		ipv6_addr_set(&rtmsg.rtmsg_dst,
+			      __constant_htonl(0xFF000000), 0, 0, 0);
+
+		rtmsg.rtmsg_dst_len = 8;
+		rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+		rtmsg.rtmsg_ifindex = dev->ifindex;
+
+		rtmsg.rtmsg_flags = RTF_UP;
+
+		mrt = ip6_route_add(&rtmsg, &err);
+
+		if (err)
+			printk(KERN_DEBUG "dad_start: mcast route add failed\n");
+		else
+			mrt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
+	}
 
-	if (rand_seed)
-	{
+	if (rand_seed) {
 		rand_seed = 0;
 		nd_rand_seed = ifp->addr.s6_addr32[3];
 	}
 
 	init_timer(&ifp->timer);
-	ifp->probes = DupAddrDetectTransmits;
 
-	rand_num = ipv6_random() % MAX_RTR_SOLICITATION_DELAY;
+	ifp->probes = ipv6_config.dad_transmits;
+	ifp->flags |= DAD_INCOMPLETE;
+
+	rand_num = ipv6_random() % ipv6_config.rtr_solicit_delay;
 
 	ifp->timer.function = addrconf_dad_timer;
 	ifp->timer.data = (unsigned long) ifp;
@@ -1231,6 +1038,97 @@
 	add_timer(&ifp->timer);
 }
 
+static void addrconf_dad_timer(unsigned long data)
+{
+	struct inet6_ifaddr *ifp;
+	struct in6_addr unspec;
+	struct in6_addr mcaddr;
+
+	ifp = (struct inet6_ifaddr *) data;
+
+	if (ifp->probes == 0) {
+		/*
+		 * DAD was successful
+		 */
+
+		ifp->flags &= ~DAD_INCOMPLETE;
+		addrconf_dad_completed(ifp);
+		return;
+	}
+
+	ifp->probes--;
+
+	/* send a neighbour solicitation for our addr */
+	memset(&unspec, 0, sizeof(unspec));
+	addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
+
+	ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
+
+	ifp->timer.expires = jiffies + ipv6_config.rtr_solicit_interval;
+	add_timer(&ifp->timer);
+}
+
+static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
+{
+	struct device *dev;
+	int err;
+
+	dev = ifp->idev->dev;
+
+	if (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) {
+		struct in6_rtmsg rtmsg;
+		struct in6_addr all_routers;
+
+		/*
+		 *	1) configure a link route for this interface
+		 *	2) send a (delayed) router solicitation
+		 */
+
+		memset(&rtmsg, 0, sizeof(rtmsg));
+		
+		memcpy(&rtmsg.rtmsg_dst, &ifp->addr, sizeof(struct in6_addr));
+
+		rtmsg.rtmsg_dst_len = ifp->prefix_len;
+		rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+		rtmsg.rtmsg_ifindex = dev->ifindex;
+
+		rtmsg.rtmsg_flags = RTF_UP;
+
+		ip6_route_add(&rtmsg, &err);
+		
+		if (err)
+			printk(KERN_DEBUG "dad_complete: error in route_add\n");
+
+		if (ipv6_config.forwarding == 0) {
+			ipv6_addr_set(&all_routers,
+				      __constant_htonl(0xff020000U), 0, 0,
+				      __constant_htonl(0x2U));
+
+			/*
+			 *	If a host as already performed a random delay
+			 *	[...] as part of DAD [...] there is no need
+			 *	to delay again before sending the first RS
+			 */
+			ndisc_send_rs(ifp->idev->dev, &ifp->addr,
+				      &all_routers);
+
+			ifp->probes = 1;
+			ifp->timer.function = addrconf_rs_timer;
+			ifp->timer.expires = (jiffies +
+					      ipv6_config.rtr_solicit_interval);
+			ifp->idev->if_flags |= IF_RS_SENT;
+			add_timer(&ifp->timer);
+		}
+	}
+	
+	/*
+	 *	configure the address for reception
+	 */
+
+	ip6_rt_addr_add(&ifp->addr, dev);
+}
+
+#ifdef CONFIG_PROC_FS
 static int iface_proc_info(char *buffer, char **start, off_t offset,
 			   int length, int dummy)
 {
@@ -1238,13 +1136,11 @@
 	int i;
 	int len = 0;
 
-	for (i=0; i < HASH_SIZE; i++)
-		for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next)
-		{
+	for (i=0; i < IN6_ADDR_HSIZE; i++)
+		for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {
 			int j;
 
-			for (j=0; j<16; j++)
-			{
+			for (j=0; j<16; j++) {
 				sprintf(buffer + len, "%02x",
 					ifp->addr.s6_addr[j]);
 				len += 2;
@@ -1252,7 +1148,7 @@
 
 			len += sprintf(buffer + len,
 				       " %02x %02x %02x %02x %8s\n",
-				       ifp->idev->if_index,
+				       ifp->idev->dev->ifindex,
 				       ifp->prefix_len,
 				       ifp->scope,
 				       ifp->flags,
@@ -1275,7 +1171,7 @@
         0, NULL,
         &iface_proc_info
 };
-
+#endif	/* CONFIG_PROC_FS */
 
 /*
  *	Periodic address status verification
@@ -1287,29 +1183,23 @@
 	unsigned long now = jiffies;
 	int i;
 
-	for (i=0; i < HASH_SIZE; i++)
-	{
-		for (ifp=inet6_addr_lst[i]; ifp;)
-		{
-			if (!(ifp->flags & ADDR_PERMANENT))
-			{
+	for (i=0; i < IN6_ADDR_HSIZE; i++) {
+		for (ifp=inet6_addr_lst[i]; ifp;) {
+			if (!(ifp->flags & ADDR_PERMANENT)) {
 				struct inet6_ifaddr *bp;
 				unsigned long age;
 
 				age = (now - ifp->tstamp) / HZ;
 
 				if (age > ifp->prefered_lft)
-				{
 					ifp->flags |= ADDR_DEPRECATED;
-				}
 
 				bp = ifp;
 				ifp=ifp->lst_next;
 				
 				if (age > bp->valid_lft)
-				{
 					ipv6_del_addr(bp);
-				}
+
 				continue;
 			}
 			ifp=ifp->lst_next;
@@ -1320,18 +1210,25 @@
 	add_timer(&addr_chk_timer);	
 }
 
+/*
+ *	Init / cleanup code
+ */
+
 void addrconf_init()
 {
 	struct device *dev;
 
-	/* init addr hash list */	  
-	memset(inet6_addr_lst, 0, 16 * sizeof(struct inet6_ifaddr *));
+	/*
+	 *	init address and device hash lists
+	 */
 
-	memset(inet6_mcast_lst,   0, 16 * sizeof(struct ipv6_mc_list *));
+	memset(inet6_addr_lst, 0, IN6_ADDR_HSIZE * sizeof(struct inet6_ifaddr *));
 
-	inet6_dev_lst = NULL;
+	memset(inet6_mcast_lst, 0, IN6_ADDR_HSIZE * sizeof(struct ifmcaddr6 *));
 
-	/* 
+	memset(inet6_dev_lst, 0, IN6_ADDR_HSIZE * sizeof(struct inet6_dev *));
+
+	/*
 	 *	Init loopback device
 	 */
 
@@ -1350,16 +1247,19 @@
 	if (dev && (dev->flags & IFF_UP))
 		addrconf_eth_config(dev);
 	
-	proc_register(&proc_net, &iface_proc_entry);
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&iface_proc_entry);
+#endif
 	
 	addr_chk_timer.expires = jiffies + ADDR_CHECK_FREQUENCY;
 	add_timer(&addr_chk_timer);
 }
 
+#ifdef MODULE
 void addrconf_cleanup(void)
 {
-	struct inet6_dev *idev, *bidev;
-	struct inet6_ifaddr *ifa, *bifa;
+ 	struct inet6_dev *idev;
+ 	struct inet6_ifaddr *ifa;
 	int i;
 
 	del_timer(&addr_chk_timer);
@@ -1368,26 +1268,32 @@
 	 *	clean dev list.
 	 */
 
-	for (idev = inet6_dev_lst; idev; )
-	{
-		bidev = idev;
-		idev = idev->next;
-		kfree(bidev);
+	for (i=0; i < IN6_ADDR_HSIZE; i++) {
+		for (idev = inet6_dev_lst[i]; idev; ) {
+			struct inet6_dev *back;
+
+			back = idev;
+			idev = idev->next;
+			kfree(back);
+		}
 	}
 
 	/*
 	 *	clean addr_list
 	 */
 
-	for (i=0; i<16; i++)
-	{
-		for (ifa=inet6_addr_lst[i]; ifa; )
-		{
+	for (i=0; i < IN6_ADDR_HSIZE; i++) {
+		for (ifa=inet6_addr_lst[i]; ifa; ) {
+			struct inet6_ifaddr *bifa;
+
 			bifa = ifa;
 			ifa = ifa->lst_next;
 			kfree(bifa);
 		}
 	}
 
-	proc_unregister(&proc_net, iface_proc_entry.low_ino);
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(iface_proc_entry.low_ino);
+#endif
 }
+#endif	/* MODULE */
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.1.29/linux/net/ipv6/af_inet6.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv6/af_inet6.c	Thu Mar 20 18:17:14 1997
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/af_inet.c
  *
- *	$Id: af_inet6.c,v 1.12 1997/03/02 06:14:44 davem Exp $
+ *	$Id: af_inet6.c,v 1.16 1997/03/18 18:24:26 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -35,35 +35,36 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 
-#include <asm/uaccess.h>
-#include <asm/system.h>
-
 #include <linux/inet.h>
 #include <linux/netdevice.h>
+#include <linux/icmpv6.h>
+
 #include <net/ip.h>
 #include <net/ipv6.h>
-#include <net/protocol.h>
-#include <net/arp.h>
-#include <net/rarp.h>
-#include <net/route.h>
-#include <net/tcp.h>
 #include <net/udp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <net/raw.h>
-#include <net/icmp.h>
-#include <linux/icmpv6.h>
+#include <net/tcp.h>
+#include <net/sit.h>
+#include <net/protocol.h>
 #include <net/inet_common.h>
 #include <net/transp_v6.h>
-#include <net/ndisc.h>
-#include <net/ipv6_route.h>
-#include <net/sit.h>
-#include <linux/ip_fw.h>
+#include <net/ip6_route.h>
 #include <net/addrconf.h>
 
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
 extern struct proto_ops inet6_stream_ops;
 extern struct proto_ops inet6_dgram_ops;
 
+/* IPv6 procfs goodies... */
+
+#ifdef CONFIG_PROC_FS
+extern int raw6_get_info(char *, char **, off_t, int, int);
+extern int tcp6_get_info(char *, char **, off_t, int, int);
+extern int udp6_get_info(char *, char **, off_t, int, int);
+extern int afinet6_get_info(char *, char **, off_t, int, int);
+#endif
+
 static int inet6_create(struct socket *sock, int protocol)
 {
 	struct sock *sk;
@@ -101,32 +102,31 @@
 		goto free_and_badtype;
 	}
 	
-	sock_init_data(sock,sk);
-	sk->zapped=0;
+	sock_init_data(sock, sk);
 
-	sk->family = AF_INET6;
-	sk->protocol = protocol;
+	sk->zapped		= 0;
+	sk->family		= AF_INET6;
+	sk->protocol		= protocol;
 
-	sk->prot = prot;
-	sk->backlog_rcv = prot->backlog_rcv;
+	sk->prot		= prot;
+	sk->backlog_rcv		= prot->backlog_rcv;
 
-	sk->timer.data = (unsigned long)sk;
-	sk->timer.function = &net_timer;
-	init_timer(&sk->timer);
+	sk->timer.data		= (unsigned long)sk;
+	sk->timer.function	= &net_timer;
 
-	sk->net_pinfo.af_inet6.hop_limit  = ipv6_hop_limit;
+	sk->net_pinfo.af_inet6.hop_limit  = ipv6_config.hop_limit;
 	sk->net_pinfo.af_inet6.mcast_hops = IPV6_DEFAULT_MCASTHOPS;
 	sk->net_pinfo.af_inet6.mc_loop	  = 1;
 
 	/* Init the ipv4 part of the socket since we can have sockets
 	 * using v6 API for ipv4.
 	 */
-	sk->ip_ttl=64;
+	sk->ip_ttl	= 64;
 
-	sk->ip_mc_loop=1;
-	sk->ip_mc_ttl=1;
-	sk->ip_mc_index=0;
-	sk->ip_mc_list=NULL;
+	sk->ip_mc_loop	= 1;
+	sk->ip_mc_ttl	= 1;
+	sk->ip_mc_index	= 0;
+	sk->ip_mc_list	= NULL;
 
 	if (sk->type==SOCK_RAW && protocol==IPPROTO_RAW)
 		sk->ip_hdrincl=1;
@@ -429,7 +429,32 @@
 	inet6_create
 };
 
-
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_raw6 = {
+	PROC_NET_RAW6, 4, "raw6",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	raw6_get_info
+};
+static struct proc_dir_entry proc_net_tcp6 = {
+	PROC_NET_TCP6, 4, "tcp6",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	tcp6_get_info
+};
+static struct proc_dir_entry proc_net_udp6 = {
+	PROC_NET_RAW6, 4, "udp6",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	udp6_get_info
+};
+static struct proc_dir_entry proc_net_sockstat6 = {
+	PROC_NET_SOCKSTAT6, 9, "sockstat6",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	afinet6_get_info
+};
+#endif	/* CONFIG_PROC_FS */
 
 #ifdef MODULE
 int init_module(void)
@@ -439,12 +464,16 @@
 {
 	struct sk_buff *dummy_skb;
 
-	printk(KERN_INFO "IPv6 v0.1 for NET3.037\n");
+	printk(KERN_INFO "IPv6 v0.2 for NET3.037\n");
 
 	if (sizeof(struct ipv6_options) > sizeof(dummy_skb->cb))
 	{
 		printk(KERN_CRIT "inet6_proto_init: size fault\n");
+#ifdef MODULE
 		return -EINVAL;
+#else
+		return;
+#endif
 	}
 
   	(void) sock_register(&inet6_family_ops);
@@ -459,7 +488,6 @@
 	ipv6_init();
 
 	icmpv6_init(&inet6_family_ops);
-	ndisc_init(&inet6_family_ops);
 
         addrconf_init();
  
@@ -472,6 +500,14 @@
 
 	tcpv6_init();
 
+	/* Create /proc/foo6 entries. */
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&proc_net_raw6);
+	proc_net_register(&proc_net_tcp6);
+	proc_net_register(&proc_net_udp6);
+	proc_net_register(&proc_net_sockstat6);
+#endif
+
 #ifdef MODULE
 	return 0;
 #endif
@@ -483,6 +519,11 @@
 	sit_cleanup();
 	ipv6_cleanup();
 	sock_unregister(AF_INET6);
-}
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(proc_net_raw6.low_ino);
+	proc_net_unregister(proc_net_tcp6.low_ino);
+	proc_net_unregister(proc_net_udp6.low_ino);
+	proc_net_unregister(proc_net_sockstat6.low_ino);
 #endif
-
+}
+#endif	/* MODULE */
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/datagram.c linux/net/ipv6/datagram.c
--- v2.1.29/linux/net/ipv6/datagram.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ipv6/datagram.c	Thu Mar 20 18:17:14 1997
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: datagram.c,v 1.3 1996/10/11 16:03:05 roque Exp $
+ *	$Id: datagram.c,v 1.8 1997/03/18 18:24:28 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -23,18 +23,15 @@
 
 #include <net/ipv6.h>
 #include <net/ndisc.h>
-#include <net/ipv6_route.h>
 #include <net/addrconf.h>
 #include <net/transp_v6.h>
 
-
 int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct ipv6_options *opt = (struct ipv6_options *) skb->cb;
 	
-	if (np->rxinfo)
-	{
+	if (np->rxinfo) {
 		struct in6_pktinfo src_info;
 
 		src_info.ipi6_ifindex = skb->dev->ifindex;
@@ -42,14 +39,12 @@
 		put_cmsg(msg, SOL_IPV6, IPV6_RXINFO, sizeof(src_info), &src_info);
 	}
 
-	if (np->rxhlim)
-	{
+	if (np->rxhlim) {
 		int hlim = skb->nh.ipv6h->hop_limit;
 		put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
 	}
 
-	if (opt->srcrt)
-	{
+	if (opt->srcrt) {
 		int hdrlen = sizeof(struct rt0_hdr) + (opt->srcrt->hdrlen << 3);
 
 		put_cmsg(msg, SOL_IPV6, IPV6_RXSRCRT, hdrlen, opt->srcrt);
@@ -61,17 +56,14 @@
 		      struct in6_addr **src_addr, struct ipv6_options *opt, 
 		      int *hlimit)
 {
-	struct inet6_dev *in6_dev = NULL;
 	struct in6_pktinfo *src_info;
 	struct cmsghdr *cmsg;
 	struct ipv6_rt_hdr *rthdr;
 	int len;
 	int err = 0;
 
-	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
-	{
-		if (cmsg->cmsg_level != SOL_IPV6)
-		{
+	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+		if (cmsg->cmsg_level != SOL_IPV6) {
 			printk(KERN_DEBUG "cmsg_level %d\n", cmsg->cmsg_level);
 			continue;
 		}
@@ -80,34 +72,25 @@
 
 		case IPV6_TXINFO:
 			if (cmsg->cmsg_len < (sizeof(struct cmsghdr) +
-					      sizeof(struct in6_pktinfo)))
-			{
+					      sizeof(struct in6_pktinfo))) {
 				err = -EINVAL;
 				goto exit_f;
 			}
 
 			src_info = (struct in6_pktinfo *) cmsg->cmsg_data;
 			
-			if (src_info->ipi6_ifindex)
-			{
-				in6_dev = ipv6_dev_by_index(src_info->ipi6_ifindex);
-				if (in6_dev == NULL)
-				{
-					err = -ENODEV;
-					goto exit_f;
-				}
+			if (src_info->ipi6_ifindex) {
+				int index = src_info->ipi6_ifindex;
 
-				*src_dev = in6_dev->dev;
+				*src_dev = dev_get_by_index(index);
 			}
 			
-			if (!ipv6_addr_any(&src_info->ipi6_addr))
-			{
+			if (!ipv6_addr_any(&src_info->ipi6_addr)) {
 				struct inet6_ifaddr *ifp;
 
 				ifp = ipv6_chk_addr(&src_info->ipi6_addr);
 
-				if ( ifp == NULL)
-				{
+				if (ifp == NULL) {
 					err = -EINVAL;
 					goto exit_f;
 				}
@@ -124,8 +107,7 @@
 			len -= sizeof(struct cmsghdr);
 
 			/* validate option length */
-			if (len < sizeof(struct ipv6_rt_hdr))
-			{
+			if (len < sizeof(struct ipv6_rt_hdr)) {
 				err = -EINVAL;
 				goto exit_f;
 			}
@@ -135,21 +117,18 @@
 			/*
 			 *	TYPE 0
 			 */
-			if (rthdr->type)
-			{
+			if (rthdr->type) {
 				err = -EINVAL;
 				goto exit_f;
 			}
 
-			if (((rthdr->hdrlen + 1) << 3) < len)
-			{	
+			if (((rthdr->hdrlen + 1) << 3) < len) {
 				err = -EINVAL;
 				goto exit_f;
 			}
 
 			/* segments left must also match */
-			if ((rthdr->hdrlen >> 1) != rthdr->segments_left)
-			{
+			if ((rthdr->hdrlen >> 1) != rthdr->segments_left) {
 				err = -EINVAL;
 				goto exit_f;
 			}
@@ -160,27 +139,26 @@
 			break;
 			
 		case IPV6_HOPLIMIT:
-			
+
 			len = cmsg->cmsg_len;
 			len -= sizeof(struct cmsghdr);
 			
-			if (len < sizeof(int))
-			{
+			if (len < sizeof(int)) {
 				err = -EINVAL;
 				goto exit_f;
 			}
 
 			*hlimit = *((int *) cmsg->cmsg_data);
 			break;
-			
+
 		default:
 			printk(KERN_DEBUG "invalid cmsg type: %d\n",
 			       cmsg->cmsg_type);
 			err = -EINVAL;
 			break;
-		}
+		};
 	}
 
-  exit_f:
+exit_f:
 	return err;
 }
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/exthdrs.c linux/net/ipv6/exthdrs.c
--- v2.1.29/linux/net/ipv6/exthdrs.c	Thu Dec 12 06:54:25 1996
+++ linux/net/ipv6/exthdrs.c	Thu Mar 20 18:17:14 1997
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: exthdrs.c,v 1.7 1996/09/12 18:44:18 roque Exp $
+ *	$Id: exthdrs.c,v 1.4 1997/03/18 18:24:29 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -31,13 +31,13 @@
 #include <net/transp_v6.h>
 #include <net/rawv6.h>
 #include <net/ndisc.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
 #include <net/addrconf.h>
 
 /*
  *	inbound
  */
-
+#if 0
 int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
 			__u8 *nhptr, struct ipv6_options *opt)
 {
@@ -53,8 +53,7 @@
 	struct ipv6_rt_hdr *hdr = (struct ipv6_rt_hdr *) skb->h.raw;
 	struct rt0_hdr *rthdr;
 
-	if (hdr->segments_left == 0)
-	{
+	if (hdr->segments_left == 0) {
 		struct ipv6_options *opt;
 
 		opt = (struct ipv6_options *) skb->cb;
@@ -65,8 +64,7 @@
 	}
 
 	if (hdr->type != IPV6_SRCRT_TYPE_0 || hdr->hdrlen & 0x01 ||
-	    hdr->hdrlen > 46)
-	{
+	    hdr->hdrlen > 46) {
                 /* 
 		 *	Discard 
 		 */
@@ -90,8 +88,7 @@
 
 	n = hdr->hdrlen >> 1;
 
-	if (hdr->segments_left > n)
-	{
+	if (hdr->segments_left > n) {
 		pos = (__u8 *) hdr - (__u8 *) skb->nh.ipv6h + 2;
 
 		pos += 3;
@@ -109,8 +106,7 @@
 
 	addr_type = ipv6_addr_type(addr);
 
-	if (addr_type == IPV6_ADDR_MULTICAST)
-	{
+	if (addr_type == IPV6_ADDR_MULTICAST) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
@@ -126,9 +122,7 @@
 	bit_map = ntohl(rthdr->bitmap);
 
 	if ((bit_map & (1 << i)) == IPV6_SRCRT_STRICT)
-	{
 		strict = 1;
-	}
 
 	ipv6_forward(skb, dev, (strict ? IP6_FW_STRICT : 0) | IP6_FW_SRCRT);
 
@@ -154,10 +148,8 @@
 	hops = ihdr->rt_hdr.hdrlen >> 1;
 	
 	if (hops > 1)
-	{
 		memcpy(phdr->addr, ihdr->addr + 1,
 		       (hops - 1) * sizeof(struct in6_addr));
-	}
 
 	ipv6_addr_copy(phdr->addr + (hops - 1), addr);
 	
@@ -165,9 +157,4 @@
 
 	return NEXTHDR_ROUTING;
 }
-
-/*
- * Local variables:
- * c-file-style: "Linux"
- * End:
- */
+#endif
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/icmp.c linux/net/ipv6/icmp.c
--- v2.1.29/linux/net/ipv6/icmp.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv6/icmp.c	Thu Mar 20 18:17:14 1997
@@ -5,6 +5,8 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
+ *	$Id: icmp.c,v 1.8 1997/03/18 18:24:30 davem Exp $
+ *
  *	Based on net/ipv4/icmp.c
  *
  *	RFC 1885
@@ -28,17 +30,9 @@
 #include <linux/socket.h>
 #include <linux/in.h>
 #include <linux/kernel.h>
-#include <linux/major.h>
 #include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
 #include <linux/sockios.h>
 #include <linux/net.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
 #include <linux/skbuff.h>
 
 #include <linux/inet.h>
@@ -49,15 +43,13 @@
 #include <net/sock.h>
 
 #include <net/ipv6.h>
+#include <net/checksum.h>
 #include <net/protocol.h>
-#include <net/route.h>
-#include <net/ndisc.h>
 #include <net/raw.h>
-#include <net/inet_common.h>
+#include <net/rawv6.h>
 #include <net/transp_v6.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
 #include <net/addrconf.h>
-#include <net/rawv6.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -88,7 +80,7 @@
 
 
 struct icmpv6_msg {
-	struct icmpv6hdr	icmph;
+	struct icmp6hdr		icmph;
 	__u8 			*data;
 	struct in6_addr		*daddr;
 	int			len;
@@ -106,7 +98,7 @@
 			   char *buff, unsigned int offset, unsigned int len)
 {
 	struct icmpv6_msg *msg = (struct icmpv6_msg *) data;
-	struct icmpv6hdr *icmph;
+	struct icmp6hdr *icmph;
 	__u32 csum;
 
 	/* 
@@ -115,26 +107,25 @@
 	 *	on an echo reply. (those are the rules on RFC 1883)
 	 */
 
-	if (offset)
-	{
+	if (offset) {
 		csum = csum_partial_copy((void *) msg->data +
-					 offset - sizeof(struct icmpv6hdr), 
+					 offset - sizeof(struct icmp6hdr), 
 					 buff, len, msg->csum);
 		msg->csum = csum;
 		return 0;
 	}
 
 	csum = csum_partial_copy((void *) &msg->icmph, buff,
-				 sizeof(struct icmpv6hdr), msg->csum);
+				 sizeof(struct icmp6hdr), msg->csum);
 
 	csum = csum_partial_copy((void *) msg->data, 
-				 buff + sizeof(struct icmpv6hdr),
-				 len - sizeof(struct icmpv6hdr), csum);
+				 buff + sizeof(struct icmp6hdr),
+				 len - sizeof(struct icmp6hdr), csum);
 
-	icmph = (struct icmpv6hdr *) buff;
+	icmph = (struct icmp6hdr *) buff;
 
-	icmph->checksum = csum_ipv6_magic(saddr, msg->daddr, msg->len,
-					  IPPROTO_ICMPV6, csum);
+	icmph->icmp6_cksum = csum_ipv6_magic(saddr, msg->daddr, msg->len,
+					     IPPROTO_ICMPV6, csum);
 	return 0; 
 }
 
@@ -163,6 +154,7 @@
 	struct in6_addr *saddr = NULL;
 	struct device *src_dev = NULL;
 	struct icmpv6_msg msg;
+	struct flowi fl;
 	int addr_type = 0;
 	int optlen;
 	int len;
@@ -171,9 +163,8 @@
 	 *	sanity check pointer in case of parameter problem
 	 */
 
-	if (type == ICMPV6_PARAMETER_PROB && 
-	    (info > (skb->tail - ((unsigned char *) hdr))))
-	{
+	if (type == ICMPV6_PARAMPROB && 
+	    (info > (skb->tail - ((unsigned char *) hdr)))) {
 		printk(KERN_DEBUG "icmpv6_send: bug! pointer > skb\n");
 		return;
 	}
@@ -188,23 +179,18 @@
 	addr_type = ipv6_addr_type(&hdr->daddr);
 
 	if (ipv6_chk_addr(&hdr->daddr))
-	{
 		saddr = &hdr->daddr;
-	}
 
 	/*
 	 *	Dest addr check
 	 */
 
-	if ((addr_type & IPV6_ADDR_MULTICAST || skb->pkt_type != PACKET_HOST))
-	{
+	if ((addr_type & IPV6_ADDR_MULTICAST || skb->pkt_type != PACKET_HOST)) {
 		if (type != ICMPV6_PKT_TOOBIG &&
-		    !(type == ICMPV6_PARAMETER_PROB && 
+		    !(type == ICMPV6_PARAMPROB && 
 		      code == ICMPV6_UNK_OPTION && 
 		      (opt_unrec(skb, info))))
-		{
 			return;
-		}
 
 		saddr = NULL;
 	}
@@ -216,16 +202,13 @@
 	 */
 
 	if (addr_type & IPV6_ADDR_LINKLOCAL)
-	{
 		src_dev = skb->dev;
-	}
 
 	/*
 	 *	Must not send if we know that source is Anycast also.
 	 *	for now we don't know that.
 	 */
-	if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST))
-	{
+	if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
 		printk(KERN_DEBUG "icmpv6_send: addr_any/mcast source\n");
 		return;
 	}
@@ -235,9 +218,9 @@
 	 *	getfrag_t callback.
 	 */
 
-	msg.icmph.type = type;
-	msg.icmph.code = code;
-	msg.icmph.checksum = 0;
+	msg.icmph.icmp6_type = type;
+	msg.icmph.icmp6_code = code;
+	msg.icmph.icmp6_cksum = 0;
 	msg.icmph.icmp6_pointer = htonl(info);
 
 	msg.data = skb->nh.raw;
@@ -252,31 +235,37 @@
 	optlen = 0;
 
 	len = min(skb->tail - ((unsigned char *) hdr), 
-		  576 - sizeof(struct ipv6hdr) - sizeof(struct icmpv6hdr)
+		  576 - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr)
 		  - optlen);
 
-	if (len < 0)
-	{
+	if (len < 0) {
 		printk(KERN_DEBUG "icmp: len problem\n");
 		return;
 	}
 
-	len += sizeof(struct icmpv6hdr);
+	len += sizeof(struct icmp6hdr);
 
 	msg.len = len;
 
+	fl.proto = IPPROTO_ICMPV6;
+	fl.nl_u.ip6_u.daddr = &hdr->saddr;
+	fl.nl_u.ip6_u.saddr = saddr;
+	fl.dev = src_dev;
+	fl.uli_u.icmpt.type = type;
+	fl.uli_u.icmpt.code = code;
 
-	ipv6_build_xmit(sk, icmpv6_getfrag, &msg, &hdr->saddr, len,
-			saddr, src_dev, NULL, IPPROTO_ICMPV6, 0, 1);
+	ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
+		       MSG_DONTWAIT);
 }
 
 static void icmpv6_echo_reply(struct sk_buff *skb)
 {
 	struct sock *sk = icmpv6_socket->sk;
 	struct ipv6hdr *hdr = skb->nh.ipv6h;
-	struct icmpv6hdr *icmph = (struct icmpv6hdr *) skb->h.raw;
+	struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
 	struct in6_addr *saddr;
 	struct icmpv6_msg msg;
+	struct flowi fl;
 	unsigned char *data;
 	int len;
 
@@ -288,11 +277,11 @@
 		saddr = NULL;
 
 	len = skb->tail - data;
-	len += sizeof(struct icmpv6hdr);
+	len += sizeof(struct icmp6hdr);
 
-	msg.icmph.type = ICMPV6_ECHO_REPLY;
-	msg.icmph.code = 0;
-	msg.icmph.checksum = 0;
+	msg.icmph.icmp6_type = ICMPV6_ECHO_REPLY;
+	msg.icmph.icmp6_code = 0;
+	msg.icmph.icmp6_cksum = 0;
 	msg.icmph.icmp6_identifier = icmph->icmp6_identifier;
 	msg.icmph.icmp6_sequence = icmph->icmp6_sequence;
 
@@ -300,9 +289,16 @@
 	msg.csum = 0;
 	msg.len = len;
 	msg.daddr = &hdr->saddr;
-       
-	ipv6_build_xmit(sk, icmpv6_getfrag, &msg, &hdr->saddr, len, saddr,
-			skb->dev, NULL, IPPROTO_ICMPV6, 0, 1);
+
+	fl.proto = IPPROTO_ICMPV6;
+	fl.nl_u.ip6_u.daddr = &hdr->saddr;
+	fl.nl_u.ip6_u.saddr = saddr;
+	fl.dev = skb->dev;
+	fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY;
+	fl.uli_u.icmpt.code = 0;
+
+	ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
+		       MSG_DONTWAIT);
 }
 
 static __inline__ int ipv6_ext_hdr(u8 nexthdr)
@@ -339,19 +335,24 @@
 	pbuff = (char *) (hdr + 1);
 	len -= sizeof(struct ipv6hdr);
 
-	while (ipv6_ext_hdr(nexthdr)) 
-	{
+	while (ipv6_ext_hdr(nexthdr)) {
 		int hdrlen;
 
 		if (nexthdr == NEXTHDR_NONE)
 			return;
 
 		nexthdr = *pbuff;
+
+		/* Header length is size in 8-octet units, not
+		 * including the first 8 octets.
+		 */
 		hdrlen = *(pbuff+1);
+		hdrlen = (hdrlen + 1) << 3;
 
-		if (((hdrlen + 1) << 3) > len)
+		if (hdrlen > len)
 			return;
 		
+		/* Now this is right. */
 		pbuff += hdrlen;
 		len -= hdrlen;
 	}
@@ -360,16 +361,13 @@
 
 	for (ipprot = (struct inet6_protocol *) inet6_protos[hash]; 
 	     ipprot != NULL; 
-	     ipprot=(struct inet6_protocol *)ipprot->next)
-	{
+	     ipprot=(struct inet6_protocol *)ipprot->next) {
 		if (ipprot->protocol != nexthdr)
 			continue;
 
 		if (ipprot->err_handler) 
-		{
 			ipprot->err_handler(type, code, pbuff, info,
 					    saddr, daddr, ipprot);
-		}
 		return;
 	}
 
@@ -378,16 +376,12 @@
 	sk = raw_v6_htable[hash];
 
 	if (sk == NULL)
-	{
 		return;
-	}
 
 	while((sk = raw_v6_lookup(sk, nexthdr, daddr, saddr))) {
 		rawv6_err(sk, type, code, pbuff, saddr, daddr);
 		sk = sk->next;
 	}
-
-	return;
 }
   
 /*
@@ -400,32 +394,29 @@
 	       int redo, struct inet6_protocol *protocol)
 {
 	struct ipv6hdr *orig_hdr;
-	struct icmpv6hdr *hdr = (struct icmpv6hdr *) skb->h.raw;
+	struct icmp6hdr *hdr = (struct icmp6hdr *) skb->h.raw;
 	int ulen;
 
-	/* perform checksum */
-
-
+	/* Perform checksum. */
 	switch (skb->ip_summed) {	
 	case CHECKSUM_NONE:
 		skb->csum = csum_partial((char *)hdr, len, 0);
 	case CHECKSUM_HW:
 		if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6, 
-				    skb->csum))
-		{
+				    skb->csum)) {
 			printk(KERN_DEBUG "icmpv6 checksum failed\n");
 			goto discard_it;
 		}
 	default:
 		/* CHECKSUM_UNNECESSARY */
-	}
+	};
 
 	/*
 	 *	length of original packet carried in skb
 	 */
 	ulen = skb->tail - (unsigned char *) (hdr + 1);
 	
-	switch (hdr->type) {
+	switch (hdr->icmp6_type) {
 
 	case ICMPV6_ECHO_REQUEST:
 		icmpv6_echo_reply(skb);
@@ -438,20 +429,19 @@
 	case ICMPV6_PKT_TOOBIG:
 		orig_hdr = (struct ipv6hdr *) (hdr + 1);
 		if (ulen >= sizeof(struct ipv6hdr))
-		{
-			rt6_handle_pmtu(&orig_hdr->daddr,
-					ntohl(hdr->icmp6_mtu));
-		}
+			rt6_pmtu_discovery(&orig_hdr->daddr, dev,
+					   ntohl(hdr->icmp6_mtu));
 
 		/*
-		 * Drop through to notify
+		 *	Drop through to notify
 		 */
 
 	case ICMPV6_DEST_UNREACH:
-	case ICMPV6_TIME_EXCEEDED:
-	case ICMPV6_PARAMETER_PROB:
+	case ICMPV6_TIME_EXCEED:
+	case ICMPV6_PARAMPROB:
 
-		icmpv6_notify(hdr->type, hdr->code, (char *) (hdr + 1), ulen,
+		icmpv6_notify(hdr->icmp6_type, hdr->icmp6_code,
+			      (char *) (hdr + 1), ulen,
 			      saddr, daddr, protocol);
 		break;
 
@@ -463,32 +453,35 @@
 		ndisc_rcv(skb, dev, saddr, daddr, opt, len);		
 		break;
 
-	case ICMPV6_MEMBERSHIP_QUERY:
-	case ICMPV6_MEMBERSHIP_REPORT:
-	case ICMPV6_MEMBERSHIP_REDUCTION:
-		/* forward the packet to the igmp module */
+	case ICMPV6_MGM_QUERY:
+		igmp6_event_query(skb, hdr, len);
+		break;
+
+	case ICMPV6_MGM_REPORT:
+		igmp6_event_report(skb, hdr, len);
+		break;
+
+	case ICMPV6_MGM_REDUCTION:
 		break;
 
 	default:
 		printk(KERN_DEBUG "icmpv6: msg of unkown type\n");
 		
 		/* informational */
-		if (hdr->type & 0x80)
-		{
+		if (hdr->icmp6_type & 0x80)
 			goto discard_it;
-		}
 
 		/* 
 		 * error of unkown type. 
 		 * must pass to upper level 
 		 */
 
-		icmpv6_notify(hdr->type, hdr->code, (char *) (hdr + 1), ulen,
+		icmpv6_notify(hdr->icmp6_type, hdr->icmp6_code,
+			      (char *) (hdr + 1), ulen,
 			      saddr, daddr, protocol);	
-	}
-
-  discard_it:
+	};
 
+discard_it:
 	kfree_skb(skb, FREE_READ);
 	return 0;
 }
@@ -509,7 +502,7 @@
 
 	if((err=ops->create(icmpv6_socket, IPPROTO_ICMPV6))<0)
 		printk(KERN_DEBUG 
-		       "Failed to create the ICMP control socket.\n");
+		       "Failed to create the ICMP6 control socket.\n");
 
 	MOD_DEC_USE_COUNT;
 
@@ -518,6 +511,9 @@
 	sk->num = 256;			/* Don't receive any data */
 
 	inet6_add_protocol(&icmpv6_protocol);
+
+	ndisc_init(ops);
+	igmp6_init(ops);
 }
 
 static struct icmp6_err {
@@ -539,8 +535,7 @@
 
 	switch (type) {
 	case ICMPV6_DEST_UNREACH:
-		if (code <= ICMPV6_PORT_UNREACH)
-		{
+		if (code <= ICMPV6_PORT_UNREACH) {
 			*err  = tab_unreach[code].err;
 			fatal = tab_unreach[code].fatal;
 		}
@@ -550,7 +545,7 @@
 		*err = EMSGSIZE;
 		break;
 		
-	case ICMPV6_PARAMETER_PROB:
+	case ICMPV6_PARAMPROB:
 		*err = EPROTO;
 		fatal = 1;
 		break;
@@ -558,9 +553,3 @@
 
 	return fatal;
 }
-
-/*
- * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h  -c -o icmp.o icmp.c"
- * End:
- */
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ip6_fib.c linux/net/ipv6/ip6_fib.c
--- v2.1.29/linux/net/ipv6/ip6_fib.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ipv6/ip6_fib.c	Thu Mar 20 18:17:15 1997
@@ -0,0 +1,927 @@
+/*
+ *	Linux INET6 implementation 
+ *	Forwarding Information Database
+ *
+ *	Authors:
+ *	Pedro Roque		<roque@di.fc.ul.pt>	
+ *
+ *	$Id: ip6_fib.c,v 1.6 1997/03/18 18:24:33 davem Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <linux/route.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+
+#ifdef 	CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/addrconf.h>
+#include <net/netlink.h>
+
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+
+#define RT_DEBUG 2
+
+struct rt6_statistics	rt6_stats;
+
+/*
+ *	A routing update causes an increase of the serial number on the
+ *	afected subtree. This allows for cached routes to be asynchronously
+ *	tested when modifications are made to the destination cache as a
+ *	result of redirects, path MTU changes, etc.
+ */
+
+static __u32	rt_sernum	= 0;
+
+static void fib6_run_gc(unsigned long);
+
+static struct timer_list ip6_fib_timer = {
+	NULL, NULL,
+	0,
+	0,
+	fib6_run_gc
+};
+
+/*
+ *	Auxiliary address test functions for the radix tree.
+ *
+ *	These assume a 32bit processor (although it will work on 
+ *	64bit processors)
+ */
+
+/*
+ *	compare "prefix length" bits of an address
+ */
+
+static __inline__ int addr_match(void *token1, void *token2, int prefixlen)
+{
+	__u32 *a1 = token1;
+	__u32 *a2 = token2;
+	int pdw;
+	int pbi;
+
+	pdw = prefixlen >> 0x05;  /* num of whole __u32 in prefix */
+	pbi = prefixlen &  0x1f;  /* num of bits in incomplete u32 in prefix */
+
+	if (pdw)
+		if (memcmp(a1, a2, pdw << 2))
+			return 0;
+
+	if (pbi) {
+		__u32 w1, w2;
+		__u32 mask;
+
+		w1 = a1[pdw];
+		w2 = a2[pdw];
+
+		mask = htonl((0xffffffff) << (0x20 - pbi));
+
+		if ((w1 ^ w2) & mask)
+			return 0;
+	}
+
+	return 1;
+}
+
+/*
+ *	test bit
+ */
+
+static __inline__ int addr_bit_set(void *token, int fn_bit)
+{
+	int dw;
+	__u32 b1;
+	__u32 mask;
+	int bit = fn_bit;
+	__u32 *addr = token;
+
+	dw = bit >> 0x05;
+
+	b1 = addr[dw];
+	
+	bit = ~bit;
+	bit &= 0x1f;
+	mask = htonl(1 << bit);
+	return (b1 & mask);
+}
+
+
+
+/*
+ *	find the first different bit between two addresses
+ *	length of address must be a multiple of 32bits
+ */
+
+static __inline__ int addr_diff(void *token1, void *token2, int addrlen)
+{
+	__u32 *a1 = token1;
+	__u32 *a2 = token2;
+	int i;
+
+	addrlen >>= 2;
+
+	for (i = 0; i < addrlen; i++) {
+		__u32 b1, b2;
+		__u32 xb;
+
+		b1 = a1[i];
+		b2 = a2[i];
+
+		xb = b1 ^ b2;
+
+		if (xb) {
+			int res = 0;
+			int j=31;
+
+			xb = ntohl(xb);
+
+			while (test_bit(j, &xb) == 0) {
+				res++;
+				j--;
+			}
+
+			return (i * 32 + res);
+		}
+	}
+
+	/*
+	 *	we should *never* get to this point since that 
+	 *	would mean the addrs are equal
+	 */
+
+	return -1;
+}
+
+static __inline__ struct fib6_node * node_alloc(void)
+{
+	struct fib6_node *fn;
+
+	if ((fn = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC))) {
+		memset(fn, 0, sizeof(struct fib6_node));
+		rt6_stats.fib_nodes++;
+	}
+
+	return fn;
+}
+
+static __inline__ void node_free(struct fib6_node * fn)
+{
+	rt6_stats.fib_nodes--;
+	kfree(fn);
+}
+
+/*
+ *	Routing Table
+ *
+ *	return the apropriate node for a routing tree "add" operation
+ *	by either creating and inserting or by returning an existing
+ *	node.
+ */
+
+static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr,
+				     int addrlen, int plen,
+				     unsigned long offset,
+				     struct rt6_info *rt)
+				     
+{
+	struct fib6_node *fn;
+	struct fib6_node *pn = NULL;
+	struct fib6_node *in;
+	struct fib6_node *ln;
+	struct rt6key *key;
+	__u32	bit;
+	__u32	dir = 0;
+	__u32	sernum = ++rt_sernum;
+
+	/* insert node in tree */
+
+	fn = root;
+
+	if (plen == 0)
+		return fn;
+
+	for (;;) {
+		if (fn == NULL) {
+			ln = node_alloc();
+
+			if (ln == NULL)
+				return NULL;
+			ln->fn_bit = plen;
+			
+			ln->parent = pn;
+			ln->fn_sernum = sernum;
+			rt->rt6i_node = ln;
+
+			if (dir)
+				pn->right = ln;
+			else
+				pn->left  = ln;
+
+			return ln;
+		}
+
+		key = (struct rt6key *)((u8 *)fn->leaf + offset);
+
+		if (addr_match(&key->addr, addr, fn->fn_bit)) {
+			if (plen == fn->fn_bit) {
+				/* clean up an intermediate node */
+				if ((fn->fn_flags & RTN_RTINFO) == 0) {
+					rt6_release(fn->leaf);
+					fn->leaf = NULL;
+				}
+			
+				fn->fn_sernum = sernum;
+				
+				return fn;
+			}
+
+			if (plen > fn->fn_bit) {
+				/* Walk down on tree. */
+				fn->fn_sernum = sernum;
+				dir = addr_bit_set(addr, fn->fn_bit);
+				pn = fn;
+				fn = dir ? fn->right: fn->left;
+
+				continue;
+			}
+		}
+
+		/*
+		 * split since we don't have a common prefix anymore or 
+		 * we have a less significant route.
+		 * we've to insert an intermediate node on the list
+		 * this new node will point to the one we need to create
+		 * and the current
+		 */
+
+		pn = fn->parent;
+
+		/* find 1st bit in difference between the 2 addrs */
+		bit = addr_diff(addr, &key->addr, addrlen);
+
+
+		/* 
+		 *		(intermediate)	
+		 *	          /	   \
+		 *	(new leaf node)    (old node)
+		 */
+		if (plen > bit) {
+			in = node_alloc();
+		
+			if (in == NULL)
+				return NULL;
+
+			/* 
+			 * new intermediate node. 
+			 * RTN_RTINFO will
+			 * be off since that an address that chooses one of
+			 * the branches would not match less specific routes
+			 * int the other branch
+			 */
+
+			in->fn_bit = bit;
+
+			in->parent = pn;
+			in->leaf = rt;
+
+			in->fn_sernum = sernum;
+			atomic_inc(&rt->rt6i_ref);
+
+			/* leaf node */
+			ln = node_alloc();
+
+			if (ln == NULL) {
+				node_free(in);
+				return NULL;
+			}
+
+			/* update parent pointer */
+			if (dir)
+				pn->right = in;
+			else
+				pn->left  = in;
+
+			ln->fn_bit = plen;
+
+			ln->parent = in;
+			fn->parent = in;
+
+			ln->fn_sernum = sernum;
+
+			if (addr_bit_set(addr, bit)) {
+				in->right = ln;
+				in->left  = fn;
+			} else {
+				in->left  = ln;
+				in->right = fn;
+			}
+
+			return ln;
+		}
+
+		/* 
+		 *		(new leaf node)
+		 *	          /	   \
+		 *	     (old node)    NULL
+		 */
+
+		ln = node_alloc();
+
+		if (ln == NULL)
+			return NULL;
+
+		ln->fn_bit = plen;
+
+		ln->parent = pn;
+
+		ln->fn_sernum = sernum;
+		
+		if (dir)
+			pn->right = ln;
+		else
+			pn->left  = ln;
+		
+
+		if (addr_bit_set(&key->addr, plen))
+			ln->right = fn;
+		else
+			ln->left  = fn;
+
+		fn->parent = ln;
+
+		return ln;
+	}
+
+	return NULL;
+}
+
+/*
+ *	Insert routing information in a node.
+ */
+
+static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt)
+{
+	struct rt6_info *iter = NULL;
+	struct rt6_info **ins;
+
+	rt->rt6i_node = fn;
+	ins = &fn->leaf;
+
+	for (iter = fn->leaf; iter; iter=iter->u.next) {
+		/*
+		 *	Search for duplicates
+		 */
+
+		if (iter->rt6i_metric == rt->rt6i_metric) {
+			/*
+			 *	Same priority level
+			 */
+
+			if ((iter->rt6i_dev == rt->rt6i_dev) &&
+			    (iter->rt6i_flowr == rt->rt6i_flowr) &&
+			    (ipv6_addr_cmp(&iter->rt6i_gateway,
+					   &rt->rt6i_gateway) == 0))
+				return -EEXIST;
+		}
+
+		if (iter->rt6i_metric > rt->rt6i_metric)
+			break;
+
+		ins = &iter->u.next;
+	}
+
+	/*
+	 *	insert node
+	 */
+
+	*ins = rt;
+	rt->u.next = iter;
+	atomic_inc(&rt->rt6i_ref);
+	rt6_stats.fib_rt_entries++;
+
+	if ((fn->fn_flags & RTN_RTINFO) == 0) {
+		rt6_stats.fib_route_nodes++;
+		fn->fn_flags |= RTN_RTINFO;
+	}
+
+	return 0;
+}
+
+static __inline__ void fib6_start_gc(struct rt6_info *rt)
+{
+	if ((ip6_fib_timer.expires == 0) &&
+	    (rt->rt6i_flags & (RTF_ADDRCONF | RTF_CACHE))) {
+		ip6_fib_timer.expires = jiffies + ipv6_config.rt_gc_period;
+		add_timer(&ip6_fib_timer);
+	}
+}
+
+/*
+ *	Add routing information to the routing tree.
+ *	<destination addr>/<source addr>
+ *	with source addr info in sub-trees
+ */
+
+int fib6_add(struct fib6_node *root, struct rt6_info *rt)
+{
+	struct fib6_node *fn;
+	int err = -ENOMEM;
+	unsigned long offset;
+	
+	offset = (u8*) &rt->rt6i_dst - (u8*) rt;
+	fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
+			rt->rt6i_dst.plen, offset, rt);
+
+	if (fn == NULL) {
+#if RT_DEBUG >= 2
+		printk(KERN_DEBUG "fib6_add: fn == NULL\n");
+#endif
+		goto out;
+	}
+
+	if (rt->rt6i_src.plen) {
+		struct fib6_node *sn;
+
+#if RT_DEBUG >= 2
+		printk(KERN_DEBUG "fib6_add: src.len > 0\n");
+#endif
+
+		if (fn->subtree == NULL) {
+			struct fib6_node *sfn;
+
+			if (fn->leaf == NULL) {
+				fn->leaf = rt;
+				atomic_inc(&rt->rt6i_ref);
+			}
+
+			sfn = node_alloc();
+
+			if (sfn == NULL)
+				goto out;
+
+			sfn->parent = fn;
+			sfn->leaf = &ip6_null_entry;
+			sfn->fn_flags = RTN_ROOT;
+			sfn->fn_sernum = ++rt_sernum;
+
+			fn->subtree = sfn;
+		}
+
+		offset = (u8*) &rt->rt6i_src - (u8*) rt;
+
+		sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
+				sizeof(struct in6_addr), rt->rt6i_src.plen,
+				offset, rt);
+
+		if (sn == NULL)
+			goto out;
+
+		fn = sn;
+	}
+
+	err = fib6_add_rt2node(fn, rt);
+
+	if (err == 0)
+		fib6_start_gc(rt);
+out:
+	return err;
+}
+
+/*
+ *	Routing tree lookup
+ *
+ */
+
+struct lookup_args {
+	unsigned long	offset;		/* key offset on rt6_info	*/
+	struct in6_addr	*addr;		/* search key			*/
+};
+
+static struct fib6_node * fib6_lookup_1(struct fib6_node *root,
+					struct lookup_args *args)
+{
+	struct fib6_node *fn;
+	int dir;
+
+	/*
+	 *	Descend on a tree
+	 */
+
+	fn = root;
+
+	for (;;) {
+		struct fib6_node *next;
+
+		dir = addr_bit_set(args->addr, fn->fn_bit);
+
+		next = dir ? fn->right : fn->left;
+
+		if (next) {
+			fn = next;
+			continue;
+		}
+
+		break;
+	}
+
+	while ((fn->fn_flags & RTN_ROOT) == 0) {
+		if (fn->subtree) {
+			struct fib6_node *st;
+			struct lookup_args *narg;
+
+			narg = args + 1;
+
+			if (narg->addr) {
+				st = fib6_lookup_1(fn->subtree, narg);
+
+				if (!(st->fn_flags & RTN_ROOT))
+				{
+					return st;
+				}
+			}
+		}
+
+		if (fn->fn_flags & RTN_RTINFO) {
+			struct rt6key *key;
+
+			key = (struct rt6key *) ((u8 *) fn->leaf +
+						 args->offset);
+
+			if (addr_match(&key->addr, args->addr, key->plen))
+				return fn;
+		}
+
+		fn = fn->parent;
+	}
+
+	return NULL;
+}
+
+struct fib6_node * fib6_lookup(struct fib6_node *root, struct in6_addr *daddr,
+			       struct in6_addr *saddr)
+{
+	struct lookup_args args[2];
+	struct rt6_info *rt = NULL;
+	struct fib6_node *fn;
+
+	args[0].offset = (u8*) &rt->rt6i_dst - (u8*) rt;
+	args[0].addr = daddr;
+
+	args[1].offset = (u8*) &rt->rt6i_src - (u8*) rt;
+	args[1].addr = saddr;
+
+	fn = fib6_lookup_1(root, args);
+
+	if (fn == NULL)
+		fn = root;
+
+	return fn;
+}
+
+/*
+ *	Deletion
+ *
+ */
+
+static struct rt6_info * fib6_find_prefix(struct fib6_node *fn)
+{
+	while(fn) {
+		if(fn->left)
+			return fn->left->leaf;
+
+		if(fn->right)
+			return fn->right->leaf;
+
+		fn = fn->subtree;
+	}
+	return NULL;
+}
+
+/*
+ *	called to trim the tree of intermediate nodes when possible
+ */
+
+static void fib6_del_2(struct fib6_node *fn)
+{
+	struct rt6_info *rt;
+
+	fn->fn_flags &= ~RTN_RTINFO;
+	rt6_stats.fib_route_nodes--;
+
+	if (fn->fn_flags & RTN_TL_ROOT)
+		return;
+
+	do {
+		struct fib6_node *pn, *child;
+		int children = 0;
+
+		child = NULL;
+
+		if (fn->left) {
+			children++;
+			child = fn->left;
+		}
+
+		if (fn->right) {
+			children++;
+			child = fn->right;
+		}
+
+		if (children > 1 || (fn->fn_flags & RTN_RTINFO))
+			break;
+
+		if (fn->subtree)
+			goto stree_node;
+
+		pn = fn->parent;
+
+		if ((fn->fn_flags & RTN_ROOT) == 0) {
+			if (pn->left == fn)
+				pn->left = child;
+			else
+				pn->right = child;
+
+			if (child)
+				child->parent = pn;
+
+			if (fn->leaf)
+				rt6_release(fn->leaf);
+		} else {
+			if (children)
+				break;
+
+			pn->subtree = NULL;
+		}
+
+		node_free(fn);
+		fn = pn;
+
+	} while (!(fn->fn_flags & RTN_TL_ROOT));
+
+	return;
+
+stree_node:
+
+	rt6_release(fn->leaf);
+	rt = fib6_find_prefix(fn);
+
+	if (rt == NULL)
+		panic("fib6_del_2: inconsistent tree\n");
+
+	atomic_inc(&rt->rt6i_ref);
+	fn->leaf = rt;
+}
+
+static struct fib6_node * fib6_del_1(struct rt6_info *rt)
+{
+	struct fib6_node *fn;
+	
+	fn = rt->rt6i_node;
+
+	if (fn) {
+		struct rt6_info **back;
+		struct rt6_info *lf;
+
+		back = &fn->leaf;
+		
+		for(lf = fn->leaf; lf; lf=lf->u.next) {
+			if (rt == lf) {
+				/*
+				 *	Delete this entry.
+				 */
+
+				*back = lf->u.next;
+				rt6_release(lf);
+				return fn;
+			}
+			back = &lf->u.next;
+		}
+	}
+
+	return NULL;
+}
+
+int fib6_del(struct rt6_info *rt)
+{
+	struct fib6_node *fn;
+
+	fn = fib6_del_1(rt);
+
+	if (fn == NULL)
+		return -ENOENT;
+
+	if (fn->leaf == NULL)
+		fib6_del_2(fn);
+
+	return 0;
+}
+
+/*
+ *	Tree transversal function
+ *
+ */
+
+void fib6_walk_tree(struct fib6_node *root, f_pnode func, void *arg,
+		    int filter)
+{
+	struct fib6_node *fn;
+
+	fn = root;
+	
+	do {
+		if (!(fn->fn_flags & RTN_TAG)) {
+			fn->fn_flags |= RTN_TAG;
+			
+			if (fn->left) {
+				fn = fn->left;
+				continue;
+			}
+		}
+
+		fn->fn_flags &= ~RTN_TAG;
+
+		if (fn->right) {
+			fn = fn->right;
+			continue;
+		}
+		
+		do {
+			struct fib6_node *node;
+			
+			if (fn->fn_flags & RTN_ROOT)
+				break;
+			node = fn;
+			fn = fn->parent;
+			
+			if (!(node->fn_flags & RTN_TAG)) {
+				if (node->subtree) {
+					fib6_walk_tree(node->subtree, func,
+						       arg, filter);
+				}
+
+				if (!filter ||
+				    (node->fn_flags & RTN_RTINFO))
+					(*func)(node, arg);
+			}
+			
+		} while (!(fn->fn_flags & RTN_TAG));
+
+	} while (!(fn->fn_flags & RTN_ROOT) || (fn->fn_flags & RTN_TAG));
+}
+
+/*
+ *	Garbage collection
+ */
+
+static int fib6_gc_node(struct fib6_node *fn, int timeout)
+{
+	struct rt6_info *rt, **back;
+	int more = 0;
+	unsigned long now = jiffies;
+
+	back = &fn->leaf;
+
+	for (rt = fn->leaf; rt;) {
+		if ((rt->rt6i_flags & RTF_CACHE) && rt->rt6i_use == 0) {
+			if (now - rt->rt6i_tstamp > timeout) {
+				struct rt6_info *old;
+
+				old = rt;
+
+				rt = rt->u.next;
+
+				*back = rt;
+
+				old->rt6i_node = NULL;
+				rt6_release(old);
+				rt6_stats.fib_rt_entries--;
+				continue;
+			}
+			more++;
+		}
+
+		/*
+		 *	check addrconf expiration here.
+		 */
+		back = &rt->u.next;
+		rt = rt->u.next;
+	}
+
+	return more;
+}
+
+struct fib6_gc_args {
+	unsigned long	timeout;
+	int		more;
+};
+
+static void fib6_garbage_collect(struct fib6_node *fn, void *p_arg)
+{
+	struct fib6_gc_args * args = (struct fib6_gc_args *) p_arg;
+
+	if (fn->fn_flags & RTN_RTINFO) {
+		int more;
+
+		more = fib6_gc_node(fn, args->timeout);
+
+		if (fn->leaf) {
+			args->more += more;
+			return;
+		}
+
+		rt6_stats.fib_route_nodes--;
+		fn->fn_flags &= ~RTN_RTINFO;
+	}
+
+	/*
+	 *	tree nodes (with no routing information)
+	 */
+
+	if (!fn->subtree && !(fn->fn_flags & RTN_TL_ROOT)) {
+		int children = 0;
+		struct fib6_node *chld = NULL;
+
+		if (fn->left) {
+			children++;
+			chld = fn->left;
+		}
+			
+		if (fn->right) {
+			children++;
+			chld = fn->right;
+		}
+		
+		if ((fn->fn_flags & RTN_ROOT)) {
+			if (children == 0) {
+				struct fib6_node *pn;
+
+				pn = fn->parent;
+				pn->subtree = NULL;
+
+				node_free(fn);
+			}
+			return;
+		}
+
+		if (children <= 1) {
+			struct fib6_node *pn = fn->parent;
+			
+			if (pn->left == fn)
+				pn->left = chld;
+			else
+				pn->right = chld;
+			
+			if (chld)
+				chld->parent = pn;
+			
+			if (fn->leaf)
+				rt6_release(fn->leaf);
+
+			node_free(fn);
+
+			return;
+		}
+	}
+
+	if (fn->leaf == NULL) {
+		struct rt6_info *nrt;
+		
+		nrt = fib6_find_prefix(fn);
+
+		if (nrt == NULL)
+			panic("fib6: inconsistent tree\n");
+
+		atomic_inc(&nrt->rt6i_ref);
+		fn->leaf = nrt;
+	}
+}
+
+static void fib6_run_gc(unsigned long dummy)
+{
+	struct fib6_gc_args arg = {
+		ipv6_config.rt_cache_timeout,
+		0
+	};
+
+	fib6_walk_tree(&ip6_routing_table, fib6_garbage_collect, &arg, 0);
+
+	if (arg.more) {
+		ip6_fib_timer.expires = jiffies + ipv6_config.rt_gc_period;
+		add_timer(&ip6_fib_timer);
+	} else {
+		ip6_fib_timer.expires = 0;
+	}
+}
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ip6_fw.c linux/net/ipv6/ip6_fw.c
--- v2.1.29/linux/net/ipv6/ip6_fw.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ipv6/ip6_fw.c	Thu Mar 20 18:17:15 1997
@@ -0,0 +1,378 @@
+/*
+ *	IPv6 Firewall
+ *	Linux INET6 implementation
+ *
+ *	Authors:
+ *	Pedro Roque		<roque@di.fc.ul.pt>	
+ *
+ *	$Id: ip6_fw.c,v 1.4 1997/03/18 18:24:34 davem Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/route.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+#include <linux/udp.h>
+
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/ip6_fw.h>
+#include <net/netlink.h>
+
+static unsigned long ip6_fw_rule_cnt;
+static struct ip6_fw_rule ip6_fw_rule_list = {
+	{0},
+	NULL, NULL,
+	{0},
+	IP6_FW_REJECT
+};
+
+static int ip6_fw_accept(struct dst_entry *dst, struct fl_acc_args *args);
+
+struct flow_rule_ops ip6_fw_ops = {
+	ip6_fw_accept
+};
+
+
+static struct rt6_info ip6_fw_null_entry = {
+	{{NULL, 0, 0, NULL,
+	  0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL,
+	  ip6_pkt_discard, ip6_pkt_discard, NULL}},
+	NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0UL,
+	0, &ip6_fw_rule_list, {{{{0}}}, 128}, {{{{0}}}, 128}
+};
+
+static struct fib6_node ip6_fw_fib = {
+	NULL, NULL, NULL, NULL,
+	&ip6_fw_null_entry,
+	0, RTN_ROOT|RTN_TL_ROOT, 0
+};
+
+static void ip6_rule_add(struct ip6_fw_rule *rl)
+{
+	struct ip6_fw_rule *next;
+
+	start_bh_atomic();
+	ip6_fw_rule_cnt++;
+	next = &ip6_fw_rule_list;
+	rl->next = next;
+	rl->prev = next->prev;
+	rl->prev->next = rl;
+	next->prev = rl;
+	end_bh_atomic();
+}
+
+static void ip6_rule_del(struct ip6_fw_rule *rl)
+{
+	struct ip6_fw_rule *next, *prev;
+
+	start_bh_atomic();
+	ip6_fw_rule_cnt--;
+	next = rl->next;
+	prev = rl->prev;
+	next->prev = prev;
+	prev->next = next;
+	end_bh_atomic();
+}
+
+static __inline__ struct ip6_fw_rule * ip6_fwrule_alloc(void)
+{
+	struct ip6_fw_rule *rl;
+
+	rl = kmalloc(sizeof(struct ip6_fw_rule), GFP_ATOMIC);
+
+	memset(rl, 0, sizeof(struct ip6_fw_rule));
+
+	if (rl)
+		rl->flowr.ops = &ip6_fw_ops;
+
+	return rl;
+}
+
+static __inline__ void ip6_fwrule_free(struct ip6_fw_rule * rl)
+{
+	kfree(rl);
+}
+
+static __inline__ int port_match(int rl_port, int fl_port)
+{
+	int res = 0;
+	if (rl_port == 0 || (rl_port == fl_port))
+		res = 1;
+	return res;
+}
+
+static int ip6_fw_accept_trans(struct ip6_fw_rule *rl,
+			       struct fl_acc_args *args)
+{
+	int res = FLOWR_NODECISION;
+	int proto = 0;
+	int sport = 0;
+	int dport = 0;
+
+	switch (args->type) {
+	case FL_ARG_FORWARD:
+	{
+		struct sk_buff *skb = args->fl_u.skb;
+		struct ipv6hdr *hdr = skb->nh.ipv6h;
+		int len;
+
+		len = skb->len - sizeof(struct ipv6hdr);
+
+		proto = hdr->nexthdr;
+
+		switch (proto) {
+		case IPPROTO_TCP:
+		{
+			struct tcphdr *th;
+
+			if (len < sizeof(struct tcphdr)) {
+				res = FLOWR_ERROR;
+				goto out;
+			}
+			th = (struct tcphdr *)(hdr + 1);
+			sport = th->source;
+			dport = th->dest;
+			break;
+		}
+		case IPPROTO_UDP:
+		{
+			struct udphdr *uh;
+
+			if (len < sizeof(struct udphdr)) {
+				res = FLOWR_ERROR;
+				goto out;
+			}
+			uh = (struct udphdr *)(hdr + 1);
+			sport = uh->source;
+			dport = uh->dest;
+			break;
+		}
+		default:
+			goto out;
+		};
+		break;
+	}
+
+	case FL_ARG_ORIGIN:
+	{
+		proto = args->fl_u.fl_o.flow->proto;
+
+		if (proto == IPPROTO_ICMPV6) {
+			goto out;
+		} else {
+			sport = args->fl_u.fl_o.flow->uli_u.ports.sport;
+			dport = args->fl_u.fl_o.flow->uli_u.ports.dport;
+		}
+		break;
+	}
+
+	if (proto == rl->info.proto &&
+	    port_match(args->fl_u.fl_o.flow->uli_u.ports.sport, sport) &&
+	    port_match(args->fl_u.fl_o.flow->uli_u.ports.dport, dport)) {
+		if (rl->policy & IP6_FW_REJECT)
+			res = FLOWR_SELECT;
+		else
+			res = FLOWR_CLEAR;
+	}
+
+	default:
+#if IP6_FW_DEBUG >= 1
+		printk(KERN_DEBUG "ip6_fw_accept: unknown arg type\n");
+#endif
+		goto out;
+	};
+
+out:
+	return res;
+}
+
+static int ip6_fw_accept(struct dst_entry *dst, struct fl_acc_args *args)
+{
+	struct rt6_info *rt;
+	struct ip6_fw_rule *rl;
+	int proto;
+	int res = FLOWR_NODECISION;
+
+	rt = (struct rt6_info *) dst;
+	rl = (struct ip6_fw_rule *) rt->rt6i_flowr;
+
+	proto = rl->info.proto;
+
+	switch (proto) {
+	case 0:
+		if (rl->policy & IP6_FW_REJECT)
+			res = FLOWR_SELECT;
+		else
+			res = FLOWR_CLEAR;
+		break;
+	case IPPROTO_TCP:
+	case IPPROTO_UDP:
+		res = ip6_fw_accept_trans(rl, args);
+		break;
+	case IPPROTO_ICMPV6:
+	};
+
+	return res;
+}
+
+static struct dst_entry * ip6_fw_dup(struct dst_entry *frule,
+				     struct dst_entry *rt,
+				     struct fl_acc_args *args)
+{
+	struct ip6_fw_rule *rl;
+	struct rt6_info *nrt;
+	struct rt6_info *frt;
+
+	frt = (struct rt6_info *) frule;
+
+	rl = (struct ip6_fw_rule *) frt->rt6i_flowr;
+
+	nrt = ip6_rt_copy((struct rt6_info *) rt);
+
+	if (nrt) {
+		nrt->u.dst.input = frule->input;
+		nrt->u.dst.output = frule->output;
+
+		nrt->rt6i_flowr = flow_clone(frt->rt6i_flowr);
+
+		nrt->rt6i_flags |= RTF_CACHE;
+		nrt->rt6i_tstamp = jiffies;
+	}
+
+	return (struct dst_entry *) nrt;
+}
+
+int ip6_fw_reject(struct sk_buff *skb)
+{
+#if IP6_FW_DEBUG >= 1
+	printk(KERN_DEBUG "packet rejected: \n");
+#endif
+
+	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADM_PROHIBITED, 0,
+		    skb->dev);
+	/*
+	 *	send it via netlink, as (rule, skb)
+	 */
+
+	kfree_skb(skb, FREE_READ);
+	return 0;
+}
+
+int ip6_fw_discard(struct sk_buff *skb)
+{
+	printk(KERN_DEBUG "ip6_fw: BUG fw_reject called\n");
+	kfree_skb(skb, FREE_READ);
+	return 0;
+}
+
+int ip6_fw_msg_add(struct ip6_fw_msg *msg)
+{
+	struct in6_rtmsg rtmsg;
+	struct ip6_fw_rule *rl;
+	struct rt6_info *rt;
+	int err;
+
+	ipv6_addr_copy(&rtmsg.rtmsg_dst, &msg->dst);
+	ipv6_addr_copy(&rtmsg.rtmsg_src, &msg->src);
+	rtmsg.rtmsg_dst_len = msg->dst_len;
+	rtmsg.rtmsg_src_len = msg->src_len;
+	rtmsg.rtmsg_metric = IP6_RT_PRIO_FW;
+
+	rl = ip6_fwrule_alloc();
+
+	if (rl == NULL)
+		return -ENOMEM;
+
+	rl->policy = msg->policy;
+	rl->info.proto = msg->proto;
+	rl->info.uli_u.data = msg->u.data;
+
+	rtmsg.rtmsg_flags = RTF_NONEXTHOP|RTF_POLICY;
+	rt = ip6_route_add(&rtmsg, &err);
+
+	if (rt == NULL) {
+		ip6_fwrule_free(rl);
+		return -ENOMEM;
+	}
+
+	rt->u.dst.error = -EPERM;
+
+	if (msg->policy == IP6_FW_ACCEPT) {
+		/*
+		 *	Accept rules are never selected
+		 *	(i.e. packets use normal forwarding)
+		 */
+		rt->u.dst.input = ip6_fw_discard;
+		rt->u.dst.output = ip6_fw_discard;
+	} else {
+		rt->u.dst.input = ip6_fw_reject;
+		rt->u.dst.output = ip6_fw_reject;
+	}
+
+	ip6_rule_add(rl);
+
+	rt->rt6i_flowr = flow_clone((struct flow_rule *)rl);
+
+	return 0;
+}
+
+static int ip6_fw_msgrcv(int unit, struct sk_buff *skb)
+{
+	int count = 0;
+
+	while (skb->len) {
+		struct ip6_fw_msg *msg;
+
+		if (skb->len < sizeof(struct ip6_fw_msg)) {
+			count = -EINVAL;
+			break;
+		}
+
+		msg = (struct ip6_fw_msg *) skb->data;
+		skb_pull(skb, sizeof(struct ip6_fw_msg));
+		count += sizeof(struct ip6_fw_msg);
+
+		switch (msg->action) {
+		case IP6_FW_MSG_ADD:
+			ip6_fw_msg_add(msg);
+			break;
+		case IP6_FW_MSG_DEL:
+			break;
+		default:
+			return -EINVAL;
+		};
+	}
+
+	return count;
+}
+
+static void ip6_fw_destroy(struct flow_rule *rl)
+{
+	ip6_fwrule_free((struct ip6_fw_rule *)rl);
+}
+
+#ifdef MODULE
+#define ip6_fw_init module_init
+#endif
+
+void ip6_fw_init(void)
+{
+	netlink_attach(NETLINK_IP6_FW, ip6_fw_msgrcv);
+}
+
+#ifdef MODULE
+void module_cleanup(void)
+{
+	netlink_detach(NETLINK_IP6_FW);
+}
+#endif
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ip6_input.c linux/net/ipv6/ip6_input.c
--- v2.1.29/linux/net/ipv6/ip6_input.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ipv6/ip6_input.c	Thu Mar 20 18:17:15 1997
@@ -0,0 +1,408 @@
+/*
+ *	IPv6 input
+ *	Linux INET6 implementation 
+ *
+ *	Authors:
+ *	Pedro Roque		<roque@di.fc.ul.pt>
+ *	Ian P. Morris		<I.P.Morris@soton.ac.uk>
+ *
+ *	$Id: ip6_input.c,v 1.4 1997/03/18 18:24:35 davem Exp $
+ *
+ *	Based in linux/net/ipv4/ip_input.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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+#include <linux/icmpv6.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/transp_v6.h>
+#include <net/rawv6.h>
+#include <net/ndisc.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev,
+			 __u8 *nhptr, struct ipv6_options *opt);
+
+struct hdrtype_proc {
+	u8	type;
+	int	(*func) (struct sk_buff **, struct device *dev, __u8 *ptr,
+			 struct ipv6_options *opt);
+} hdrproc_lst[] = {
+
+  /*
+	TODO
+
+	{NEXTHDR_HOP,		ipv6_hop_by_hop}
+	{NEXTHDR_ROUTING,	ipv6_routing_header},
+   */
+	{NEXTHDR_FRAGMENT,	ipv6_reassembly},
+  
+	{NEXTHDR_DEST,		ipv6_dest_opt},
+   /*	
+	{NEXTHDR_AUTH,		ipv6_auth_hdr},
+	{NEXTHDR_ESP,		ipv6_esp_hdr},
+    */
+	{NEXTHDR_MAX,		NULL}
+};
+
+/* New header structures */
+
+
+struct ipv6_tlvtype {
+	u8 type;
+	u8 len;
+};
+
+struct ipv6_destopt_hdr {
+	u8 nexthdr;
+	u8 hdrlen;
+};
+
+
+struct tlvtype_proc {
+	u8	type;
+	int	(*func) (struct sk_buff *, struct device *dev, __u8 *ptr,
+			 struct ipv6_options *opt);
+	/*
+	 *	these functions do NOT update skb->h.raw
+	 */
+
+} tlvprocdestopt_lst[] = {
+	{255,			NULL}
+};
+
+static int ip6_dstopt_unknown(struct sk_buff *skb, struct ipv6_tlvtype *hdr)
+{
+	struct in6_addr *daddr;
+	int pos;
+
+	/*
+	 *	unkown destination option type
+	 */
+	
+	pos = (__u8 *) skb->h.raw - (__u8 *) skb->nh.raw;
+	
+	/* I think this is correct please check - IPM */
+
+	switch ((hdr->type & 0xC0) >> 6) {
+	case 0: /* ignore */
+		skb->h.raw += hdr->len+2;
+		return 1;
+		
+	case 1: /* drop packet */
+		break;
+
+	case 2: /* send ICMP PARM PROB regardless and drop packet */
+		icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_UNK_OPTION,
+			    pos, skb->dev);
+		break;
+		
+	case 3: /* Send ICMP if not a multicast address and drop packet */
+		daddr = &skb->nh.ipv6h->daddr;
+		if (!(ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST))
+			icmpv6_send(skb, ICMPV6_PARAMPROB,
+				    ICMPV6_UNK_OPTION, pos, skb->dev);
+	};
+	
+	kfree_skb(skb, FREE_READ);
+	return 0;
+}
+
+static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb,
+			 struct device *dev, __u8 *nhptr,
+			 struct ipv6_options *opt, void *lastopt)
+{
+	struct ipv6_tlvtype *hdr;
+	struct tlvtype_proc *curr;
+
+	while ((hdr=(struct ipv6_tlvtype *)skb->h.raw) != lastopt) {
+		switch (hdr->type & 0x3F) {		
+		case 0: /* TLV encoded Pad1 */
+			skb->h.raw++;
+			break;
+
+		case 1: /* TLV encoded PadN */
+			skb->h.raw += hdr->len+2;
+			break;
+
+		default: /* Other TLV code so scan list */
+			for (curr=procs; curr->type != 255; curr++) {
+				if (curr->type == (hdr->type & 0x3F)) {
+					curr->func(skb, dev, nhptr, opt);
+					skb->h.raw += hdr->len+2;
+					break;
+				}
+			}
+			if (curr->type==255) {
+				if (ip6_dstopt_unknown(skb, hdr) == 0)
+					return 0;
+			}
+			break;
+		}
+	}
+	return 1;
+}
+
+static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev,
+			 __u8 *nhptr, struct ipv6_options *opt)
+{
+	struct sk_buff *skb=*skb_ptr;
+	struct ipv6_destopt_hdr *hdr = (struct ipv6_destopt_hdr *) skb->h.raw;
+	int res = 0;
+
+	if (ip6_parse_tlv(tlvprocdestopt_lst, skb, dev, nhptr, opt,
+			  skb->h.raw+hdr->hdrlen))
+		res = hdr->nexthdr;
+
+	return res;
+}
+
+
+int ipv6_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+	struct ipv6hdr *hdr;
+	int pkt_len;
+
+	if (skb->pkt_type == PACKET_OTHERHOST) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+	hdr = skb->nh.ipv6h;
+
+	if (skb->len < sizeof(struct ipv6hdr) || hdr->version != 6)
+		goto err;
+
+	pkt_len = ntohs(hdr->payload_len);
+
+	if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
+		goto err;
+
+	skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+
+	ip6_route_input(skb);
+	
+	return 0;
+err:
+	ipv6_statistics.Ip6InHdrErrors++;
+	kfree_skb(skb, FREE_READ);
+	return 0;
+}
+
+/*
+ *	0 - deliver
+ *	1 - block
+ */
+static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
+{
+	struct icmp6hdr *icmph;
+	struct raw6_opt *opt;
+
+	opt = &sk->tp_pinfo.tp_raw;
+	icmph = (struct icmp6hdr *) (skb->nh.ipv6h + 1);
+	return test_bit(icmph->icmp6_type, &opt->filter);
+}
+
+/*
+ *	demultiplex raw sockets.
+ *	(should consider queueing the skb in the sock receive_queue
+ *	without calling rawv6.c)
+ */
+static struct sock * ipv6_raw_deliver(struct sk_buff *skb,
+				      struct ipv6_options *opt,
+				      int nexthdr, int len)
+{
+	struct in6_addr *saddr;
+	struct in6_addr *daddr;
+	struct sock *sk, *sk2;
+	__u8 hash;
+
+	saddr = &skb->nh.ipv6h->saddr;
+	daddr = saddr + 1;
+
+	hash = nexthdr & (MAX_INET_PROTOS - 1);
+
+	sk = raw_v6_htable[hash];
+
+	/*
+	 *	The first socket found will be delivered after
+	 *	delivery to transport protocols.
+	 */
+
+	if (sk == NULL)
+		return NULL;
+
+	sk = raw_v6_lookup(sk, nexthdr, daddr, saddr);
+
+	if (sk) {
+		sk2 = sk;
+
+		while ((sk2 = raw_v6_lookup(sk2->next, nexthdr, daddr, saddr))) {
+			struct sk_buff *buff;
+
+			if (nexthdr == IPPROTO_ICMPV6 &&
+			    icmpv6_filter(sk2, skb))
+				continue;
+
+			buff = skb_clone(skb, GFP_ATOMIC);
+			buff->sk = sk2;
+			rawv6_rcv(buff, skb->dev, saddr, daddr, opt, len);
+		}
+	}
+
+	if (sk && nexthdr == IPPROTO_ICMPV6 && icmpv6_filter(sk, skb))
+		sk = NULL;
+
+	return sk;
+}
+
+/*
+ *	Deliver the packet to the host
+ */
+
+int ip6_input(struct sk_buff *skb)
+{
+	struct ipv6_options *opt = (struct ipv6_options *) skb->cb;
+	struct ipv6hdr *hdr = skb->nh.ipv6h;
+	struct inet6_protocol *ipprot;
+	struct hdrtype_proc *hdrt;
+	struct sock *raw_sk;
+	__u8 *nhptr;
+	int nexthdr;
+	int found = 0;
+	u8 hash;
+	int len;
+	
+	skb->h.raw += sizeof(struct ipv6hdr);
+
+	/*
+	 *	Parse extension headers
+	 */
+
+	nexthdr = hdr->nexthdr;
+	nhptr = &hdr->nexthdr;
+
+	/*
+	 *	check for extension headers
+	 */
+
+st_loop:
+
+	for (hdrt=hdrproc_lst; hdrt->type != NEXTHDR_MAX; hdrt++) {
+		if (hdrt->type == nexthdr) {
+			if ((nexthdr = hdrt->func(&skb, skb->dev, nhptr, opt))) {
+				nhptr = skb->h.raw;
+				hdr = skb->nh.ipv6h;
+				goto st_loop;
+			}
+			return 0;
+		}
+	}
+
+	len = skb->tail - skb->h.raw;
+
+	raw_sk = ipv6_raw_deliver(skb, opt, nexthdr, len);
+
+	hash = nexthdr & (MAX_INET_PROTOS - 1);
+	for (ipprot = (struct inet6_protocol *) inet6_protos[hash]; 
+	     ipprot != NULL; 
+	     ipprot = (struct inet6_protocol *) ipprot->next) {
+		struct sk_buff *buff = skb;
+		
+		if (ipprot->protocol != nexthdr)
+			continue;
+		
+		if (ipprot->copy || raw_sk)
+			buff = skb_clone(skb, GFP_ATOMIC);
+		
+		
+		ipprot->handler(buff, skb->dev, &hdr->saddr, &hdr->daddr,
+				opt, len, 0, ipprot);
+		found = 1;
+	}
+	
+	if (raw_sk) {
+		skb->sk = raw_sk;
+		rawv6_rcv(skb, skb->dev, &hdr->saddr, &hdr->daddr, opt, len);
+		found = 1;
+	}
+	
+	/*
+	 *	not found: send ICMP parameter problem back
+	 */
+	
+	if (!found) {
+		unsigned long offset;
+#if IP6_DEBUG >= 2
+		printk(KERN_DEBUG "proto not found %d\n", nexthdr);
+#endif
+		offset = nhptr - (u8*) hdr;
+		icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_UNK_NEXTHDR,
+			    offset, skb->dev);
+		kfree_skb(skb, FREE_READ);
+	}
+
+	return 0;
+}
+
+int ip6_mc_input(struct sk_buff *skb)
+{
+	struct ipv6hdr *hdr;	
+	int deliver = 0;
+	int discard = 1;
+
+	hdr = skb->nh.ipv6h;
+	if (ipv6_chk_mcast_addr(skb->dev, &hdr->daddr))
+		deliver = 1;
+
+#if 0
+	if (ipv6_config.multicast_route) {
+		int addr_type;
+
+		addr_type = ipv6_addr_type(&hdr->daddr);
+
+		if (!(addr_type & (IPV6_ADDR_LOOPBACK | IPV6_ADDR_LINKLOCAL))) {
+			struct sk_buff *skb2;
+			struct dst_entry *dst;
+
+			dst = skb->dst;
+			
+			if (deliver) {
+				skb2 = skb_clone(skb, GFP_ATOMIC);
+			} else {
+				discard = 0;
+				skb2 = skb;
+			}
+
+			dst->output(skb2);
+		}
+	}
+#endif
+
+	if (deliver) {
+		discard = 0;
+		ip6_input(skb);
+	}
+
+	if (discard)
+		kfree_skb(skb, FREE_READ);
+
+	return 0;
+}
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
--- v2.1.29/linux/net/ipv6/ip6_output.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ipv6/ip6_output.c	Thu Mar 20 18:17:15 1997
@@ -0,0 +1,629 @@
+/*
+ *	IPv6 output functions
+ *	Linux INET6 implementation 
+ *
+ *	Authors:
+ *	Pedro Roque		<roque@di.fc.ul.pt>	
+ *
+ *	$Id: ip6_output.c,v 1.3 1997/03/18 18:24:37 davem Exp $
+ *
+ *	Based on linux/net/ipv4/ip_output.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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/route.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/protocol.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+static u32	ipv6_fragmentation_id = 1;
+
+static void ipv6_build_mac_hdr(struct sk_buff *skb, struct dst_entry *dst,
+			       int len)
+{
+	struct device *dev;
+	
+	
+	dev = dst->dev;
+
+	skb->arp = 1;
+	
+	if (dev->hard_header) {
+		int mac;
+		
+#if 0
+		if (dst->hh)
+			hh_copy_header(dst->hh, skb);
+#endif
+		mac = dev->hard_header(skb, dev, ETH_P_IPV6, NULL, NULL, len);
+
+		if (mac < 0)
+			skb->arp = 0;
+	}
+	
+	skb->mac.raw = skb->data;
+}
+
+/*
+ *	xmit an sk_buff (used by TCP)
+ *	sk can be NULL (for sending RESETs)
+ */
+
+int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
+	     struct ipv6_options *opt)
+{
+	struct ipv6_pinfo *np = NULL;
+	struct dst_entry *dst = NULL;
+	struct ipv6hdr *hdr;
+	int seg_len;
+
+	hdr = skb->nh.ipv6h;
+
+	if (sk)
+		np = &sk->net_pinfo.af_inet6;
+
+	if (np && np->dst) {
+		/*
+		 *	dst_check returns NULL if route is no longer valid
+		 */
+		dst = dst_check(&dst, np->dst_cookie);
+	}
+
+	if (dst == NULL) {
+		dst = ip6_route_output(sk, fl);
+
+		if (dst->error) {
+			/*
+			 *	NETUNREACH usually
+			 */
+			return dst->error;
+		}
+	}
+
+	skb->dst = dst_clone(dst);
+	skb->dev = dst->dev;
+	seg_len = skb->tail - ((unsigned char *) hdr);
+	
+	/*
+	 *	Link Layer headers
+	 */
+
+	skb->protocol = __constant_htons(ETH_P_IPV6);
+	hdr = skb->nh.ipv6h;
+
+	ipv6_build_mac_hdr(skb, dst, seg_len);
+
+	
+	/*
+	 *	Fill in the IPv6 header
+	 */
+
+	hdr->version = 6;
+	hdr->priority = np ? np->priority : 0;
+
+	if (np)
+		memcpy(hdr->flow_lbl, (void *) &np->flow_lbl, 3);
+	else
+		memset(hdr->flow_lbl, 0, 3);
+
+	hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr));
+	hdr->nexthdr = fl->proto;
+	hdr->hop_limit = np ? np->hop_limit : ipv6_config.hop_limit;
+	
+	ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
+	ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
+
+	ipv6_statistics.Ip6OutRequests++;
+	dst->output(skb);
+
+	if (sk)
+		ip6_dst_store(sk, dst);
+	else
+		dst_release(dst);
+
+	return 0;
+}
+
+/*
+ *	To avoid extra problems ND packets are send through this
+ *	routine. It's code duplication but i really want to avoid
+ *	extra checks since ipv6_build_header is used by TCP (which
+ *	is for us performace critical)
+ */
+
+int ip6_nd_hdr(struct sock *sk, struct sk_buff *skb, struct device *dev,
+	       struct in6_addr *saddr, struct in6_addr *daddr,
+	       int proto, int len)
+{
+	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+	struct ipv6hdr *hdr;
+	int totlen;
+
+	skb->protocol = __constant_htons(ETH_P_IPV6);
+	skb->dev = dev;
+
+	totlen = len + sizeof(struct ipv6hdr);
+
+	skb->mac.raw = skb->data;
+
+	hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
+	skb->nh.ipv6h = hdr;
+
+	hdr->version  = 6;
+	hdr->priority = np->priority & 0x0f;
+	memset(hdr->flow_lbl, 0, 3);
+
+	hdr->payload_len = htons(len);
+	hdr->nexthdr = proto;
+	hdr->hop_limit = np->hop_limit;
+
+	ipv6_addr_copy(&hdr->saddr, saddr);
+	ipv6_addr_copy(&hdr->daddr, daddr);
+
+	return 0;
+}
+
+static void ip6_bld_1(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
+		      int hlimit, unsigned short pktlength)
+{
+	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+	struct ipv6hdr *hdr;
+	
+	skb->nh.raw = skb_put(skb, sizeof(struct ipv6hdr));
+	hdr = skb->nh.ipv6h;
+	
+	hdr->version = 6;
+	hdr->priority = np->priority;
+	
+	memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
+	
+	hdr->payload_len = htons(pktlength - sizeof(struct ipv6hdr));
+
+	/*
+	 *	FIXME: hop limit has default UNI/MCAST and
+	 *	msgctl settings
+	 */
+	hdr->hop_limit = hlimit;
+
+	ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
+	ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);	
+}
+
+static int ip6_frag_xmit(struct sock *sk, inet_getfrag_t getfrag,
+			 const void *data, struct dst_entry *dst,
+			 struct flowi *fl, struct ipv6_options *opt,
+			 int hlimit, int flags, unsigned short length)
+{
+	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+	struct ipv6hdr *hdr;
+	struct sk_buff *last_skb;
+	struct frag_hdr *fhdr;
+	int unfrag_len;
+	int payl_len;
+	int frag_len;
+	int last_len;
+	int nfrags;
+	int fhdr_dist;
+	int err;
+
+	/*
+	 *	Fragmentation
+	 *
+	 *	Extension header order:
+	 *	Hop-by-hop -> Routing -> Fragment -> rest (...)
+	 *	
+	 *	We must build the non-fragmented part that
+	 *	will be in every packet... this also means
+	 *	that other extension headers (Dest, Auth, etc)
+	 *	must be considered in the data to be fragmented
+	 */
+
+	unfrag_len = sizeof(struct ipv6hdr) + sizeof(struct frag_hdr);
+	payl_len = length;
+
+	if (opt) {
+		unfrag_len += opt->opt_nflen;
+		payl_len += opt->opt_flen;
+	}
+
+	nfrags = payl_len / ((dst->pmtu - unfrag_len) & ~0x7);
+
+	/*
+	 *	Length of fragmented part on every packet but 
+	 *	the last must be an:
+	 *	"integer multiple of 8 octects".
+	 */
+
+	frag_len = (dst->pmtu - unfrag_len) & ~0x7;
+
+	/*
+	 *	We must send from end to start because of 
+	 *	UDP/ICMP checksums. We do a funny trick:
+	 *	fill the last skb first with the fixed
+	 *	header (and its data) and then use it
+	 *	to create the following segments and send it
+	 *	in the end. If the peer is checking the M_flag
+	 *	to trigger the reassembly code then this 
+	 *	might be a good idea.
+	 */
+
+	last_len = payl_len - (nfrags * frag_len);
+
+	if (last_len == 0) {
+		last_len = frag_len;
+		nfrags--;
+	}
+		
+	last_skb = sock_alloc_send_skb(sk, unfrag_len + frag_len +
+				       dst->dev->hard_header_len + 15,
+				       0, flags & MSG_DONTWAIT, &err);
+
+	if (last_skb == NULL)
+		return err;
+
+	last_skb->dst = dst_clone(dst);
+	last_skb->dev = dst->dev;
+	last_skb->protocol = htons(ETH_P_IPV6);
+	last_skb->when = jiffies;
+	last_skb->arp = 0;
+
+	/* 
+	 * build the mac header... 
+	 */
+	if (dst->dev->hard_header_len) {
+		skb_reserve(last_skb, (dst->dev->hard_header_len + 15) & ~15);
+		ipv6_build_mac_hdr(last_skb, dst, unfrag_len + frag_len);
+	}
+	
+	hdr = (struct ipv6hdr *) skb_put(last_skb, sizeof(struct ipv6hdr));
+	last_skb->nh.ipv6h = hdr;
+
+	hdr->version = 6;
+	hdr->priority = np->priority;
+	
+	memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
+	hdr->payload_len = htons(unfrag_len + frag_len - sizeof(struct ipv6hdr));
+
+	hdr->hop_limit = hlimit;
+
+	hdr->nexthdr = NEXTHDR_FRAGMENT;
+
+	ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
+	ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
+
+#if 0
+	if (opt && opt->srcrt) {
+		hdr->nexthdr = ipv6opt_bld_rthdr(last_skb, opt, daddr,
+						 NEXTHDR_FRAGMENT);
+	}
+#endif
+
+	fhdr = (struct frag_hdr *) skb_put(last_skb, sizeof(struct frag_hdr));
+	memset(fhdr, 0, sizeof(struct frag_hdr));
+
+	fhdr->nexthdr  = fl->proto;		
+	fhdr->frag_off = ntohs(nfrags * frag_len);
+	fhdr->identification = ipv6_fragmentation_id++;
+
+	fhdr_dist = (unsigned char *) fhdr - last_skb->data;
+
+	err = getfrag(data, &hdr->saddr, last_skb->tail, nfrags * frag_len,
+		      last_len);
+
+	if (!err) {
+		while (nfrags--) {
+			struct sk_buff *skb;
+			
+			struct frag_hdr *fhdr2;
+				
+			printk(KERN_DEBUG "sending frag %d\n", nfrags);
+			skb = skb_copy(last_skb, sk->allocation);
+
+			if (skb == NULL)
+				return -ENOMEM;
+			
+			fhdr2 = (struct frag_hdr *) (skb->data + fhdr_dist);
+
+			/* more flag on */
+			fhdr2->frag_off = ntohs(nfrags * frag_len + 1);
+
+			/*
+			 *	FIXME:
+			 *	if (nfrags == 0)
+			 *	put rest of headers
+			 */
+
+			err = getfrag(data, &hdr->saddr,skb_put(skb, frag_len),
+				      nfrags * frag_len, frag_len);
+
+			if (err) {
+				kfree_skb(skb, FREE_WRITE);
+				break;
+			}
+
+			ipv6_statistics.Ip6OutRequests++;
+			dst->output(skb);
+		}
+	}
+
+	if (err) {
+		kfree_skb(last_skb, FREE_WRITE);
+		return -EFAULT;
+	}
+
+	printk(KERN_DEBUG "sending last frag \n");
+
+	hdr->payload_len = htons(unfrag_len + last_len - 
+				 sizeof(struct ipv6hdr));
+
+	/*
+	 *	update last_skb to reflect the getfrag we did
+	 *	on start.
+	 */
+	
+	last_skb->tail += last_len;
+	last_skb->len += last_len;
+
+	/* 
+	 *	toss the mac header out and rebuild it.
+	 *	needed because of the different frame length.
+	 *	ie: not needed for an ethernet.
+	 */
+
+	if (dst->dev->type != ARPHRD_ETHER && last_len != frag_len) {
+		skb_pull(last_skb, (unsigned char *)last_skb->nh.ipv6h - 
+			 last_skb->data);
+		ipv6_build_mac_hdr(last_skb, dst, unfrag_len + last_len);
+	}
+
+	ipv6_statistics.Ip6OutRequests++;
+	dst->output(last_skb);
+
+	return 0;
+}
+
+int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
+		   struct flowi *fl, unsigned short length,
+		   struct ipv6_options *opt, int hlimit, int flags)
+{
+	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+	struct in6_addr *final_dst = NULL;
+	struct dst_entry *dst;
+	int pktlength;
+	int err = 0;
+	
+	if (opt && opt->srcrt) {
+		struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+		final_dst = fl->nl_u.ip6_u.daddr;
+		fl->nl_u.ip6_u.daddr = rt0->addr;
+	}
+
+	dst = NULL;
+	
+	if (np->dst)
+		dst = dst_check(&np->dst, np->dst_cookie);
+
+	if (dst == NULL)
+		dst = ip6_route_output(sk, fl);
+
+	if (dst->error) {
+		ipv6_statistics.Ip6OutNoRoutes++;
+		err = -ENETUNREACH;
+		goto out;
+	}
+
+	if (fl->nl_u.ip6_u.saddr == NULL) {
+		struct inet6_ifaddr *ifa;
+		
+		ifa = ipv6_get_saddr(dst, fl->nl_u.ip6_u.daddr);
+		
+		if (ifa == NULL) {
+#if IP6_DEBUG >= 2
+			printk(KERN_DEBUG "ip6_build_xmit: "
+			       "no availiable source address\n");
+#endif
+			err = -ENETUNREACH;
+			goto out;
+		}
+		fl->nl_u.ip6_u.saddr = &ifa->addr;
+	}
+	
+	pktlength = length;
+
+	if (hlimit < 0)
+		hlimit = np->hop_limit;
+
+	if (!sk->ip_hdrincl) {
+		pktlength += sizeof(struct ipv6hdr);
+		if (opt)
+			pktlength += opt->opt_flen + opt->opt_nflen;
+	}
+
+	if (pktlength <= dst->pmtu) {
+		struct sk_buff *skb;
+		struct ipv6hdr *hdr;
+		struct device *dev;
+
+		skb = sock_alloc_send_skb(sk, pktlength + 15 +
+					  dst->dev->hard_header_len, 0,
+					  flags & MSG_DONTWAIT, &err);
+
+		if (skb == NULL) {
+			ipv6_statistics.Ip6OutDiscards++;
+			goto out;
+		}
+
+		dev = dst->dev;
+		skb->dst = dst_clone(dst);
+
+		skb->dev = dev;
+		skb->protocol = htons(ETH_P_IPV6);
+		skb->when = jiffies;
+		skb->arp = 0;
+
+		if (dev && dev->hard_header_len) {
+			skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+			ipv6_build_mac_hdr(skb, dst, pktlength);
+		}
+
+		hdr = (struct ipv6hdr *) skb->tail;
+		skb->nh.ipv6h = hdr;
+		
+		if (!sk->ip_hdrincl) {
+			ip6_bld_1(sk, skb, fl, hlimit, pktlength);
+#if 0
+			if (opt && opt->srcrt) {
+				hdr->nexthdr = ipv6opt_bld_rthdr(skb, opt,
+								 final_dst,
+								 fl->proto);
+			}
+			else
+#endif
+				hdr->nexthdr = fl->proto;
+		}
+
+		skb_put(skb, length);
+		err = getfrag(data, &hdr->saddr,
+			      ((char *) hdr) + (pktlength - length),
+			      0, length);
+		
+		if (!err) {
+			ipv6_statistics.Ip6OutRequests++;
+			dst->output(skb);
+		} else {
+			err = -EFAULT;
+			kfree_skb(skb, FREE_WRITE);
+		}
+	} else {
+		if (sk->ip_hdrincl)
+			return -EMSGSIZE;
+		
+		err = ip6_frag_xmit(sk, getfrag, data, dst, fl, opt, hlimit,
+				    flags, pktlength);
+	}
+	
+	/*
+	 *	cleanup
+	 */
+  out:
+	
+	if (np->dst)
+		ip6_dst_store(sk, dst);
+	else
+		dst_release(dst);
+
+	return err;
+}
+
+int ip6_forward(struct sk_buff *skb)
+{
+	struct dst_entry *dst = skb->dst;
+	struct ipv6hdr *hdr = skb->nh.ipv6h;
+	int size;
+	
+	/*
+	 *	check hop-by-hop options present
+	 */
+#if 0
+	if (hdr->nexthdr == NEXTHDR_HOP)
+	{
+	}
+#endif
+	/*
+	 *	check and decrement ttl
+	 */
+	if (hdr->hop_limit <= 1) {
+		icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
+			    0, skb->dev);
+
+		kfree_skb(skb, FREE_READ);
+		return -ETIMEDOUT;
+	}
+
+	hdr->hop_limit--;
+
+	if (skb->dev == dst->dev && dst->neighbour) {
+		struct in6_addr *target = NULL;
+		struct rt6_info *rt;
+		struct nd_neigh *ndn = (struct nd_neigh *) dst->neighbour;
+
+		/*
+		 *	incoming and outgoing devices are the same
+		 *	send a redirect.
+		 */
+
+		rt = (struct rt6_info *) dst;
+		if ((rt->rt6i_flags & RTF_GATEWAY))
+			target = &ndn->ndn_addr;
+		else
+			target = &hdr->daddr;
+
+		ndisc_send_redirect(skb, dst->neighbour, target);
+	}
+	
+	size = sizeof(struct ipv6hdr) + ntohs(hdr->payload_len);
+
+	if (size > dst->pmtu) {
+		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev);
+		kfree_skb(skb, FREE_READ);
+		return -EMSGSIZE;
+	}
+
+	skb->dev = dst->dev;
+
+	/*
+	 *	Rebuild the mac header
+	 */
+	if (skb_headroom(skb) < dst->dev->hard_header_len) {
+		struct sk_buff *buff;
+
+		buff = alloc_skb(dst->dev->hard_header_len + skb->len + 15,
+				 GFP_ATOMIC);
+
+		if (buff == NULL) {
+			kfree_skb(skb, FREE_WRITE);
+			return -ENOMEM;
+		}
+		
+		skb_reserve(buff, (dst->dev->hard_header_len + 15) & ~15);
+
+		buff->protocol = __constant_htons(ETH_P_IPV6);
+		buff->h.raw = skb_put(buff, size);
+		buff->dst = dst_clone(dst);
+		buff->dev = dst->dev;
+
+		memcpy(buff->h.raw, hdr, size);
+		buff->nh.ipv6h = (struct ipv6hdr *) buff->h.raw;
+		kfree_skb(skb, FREE_READ);
+		skb = buff;
+	} else {
+		skb_pull(skb, skb->nh.raw - skb->data);
+	}
+
+	ipv6_build_mac_hdr(skb, dst, size);
+
+	if (dst->neighbour)
+		ndisc_event_send(dst->neighbour, skb);
+
+	ipv6_statistics.Ip6ForwDatagrams++;
+	dst->output(skb);
+
+	return 0;
+}
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ipv6_input.c linux/net/ipv6/ipv6_input.c
--- v2.1.29/linux/net/ipv6/ipv6_input.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv6/ipv6_input.c	Wed Dec 31 16:00:00 1969
@@ -1,445 +0,0 @@
-/*
- *	IPv6 input
- *	Linux INET6 implementation 
- *
- *	Authors:
- *	Pedro Roque		<roque@di.fc.ul.pt>
- *	Ian P. Morris		<I.P.Morris@soton.ac.uk>
- *
- *	Based in linux/net/ipv4/ip_input.c
- *
- *	$Id: ipv6_input.c,v 1.4 1997/02/28 09:56:33 davem Exp $
- *
- *	This program is free software; you can redistribute it and/or
- *      modify it under the terms of the GNU General Public License
- *      as published by the Free Software Foundation; either version
- *      2 of the License, or (at your option) any later version.
- */
-
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/sched.h>
-#include <linux/net.h>
-#include <linux/netdevice.h>
-#include <linux/in6.h>
-#include <linux/icmpv6.h>
-
-#include <net/sock.h>
-#include <net/snmp.h>
-
-#include <net/ipv6.h>
-#include <net/protocol.h>
-#include <net/transp_v6.h>
-#include <net/rawv6.h>
-#include <net/ndisc.h>
-#include <net/ipv6_route.h>
-#include <net/addrconf.h>
-
-/*
- *	Header processing function list
- *	We process headers in order (as per RFC)
- *	If the processing function returns 0 the packet is considered 
- *	delivered else it returns the value of the nexthdr.
- *	The ptr field of the function points to the previous nexthdr field.
- *	This is allows the processing function to change it if it's sematics
- *	is: return a new packet without this header (like fragmentation).
- *	When a next_header value is not within the list 
- *	the inet protocol list is searched (i.e. to deliver to 
- *	TCP for instance)
- */
-
-static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev, __u8 *nhptr,
-			 struct ipv6_options *opt);
-
-
-struct hdrtype_proc {
-	u8	type;
-	int	(*func) (struct sk_buff **, struct device *dev, __u8 *ptr,
-			 struct ipv6_options *opt);
-} hdrproc_lst[] = {
-  /*
-	TODO
-
-	{NEXTHDR_HOP,		ipv6_hop_by_hop}
-   */
-	{NEXTHDR_ROUTING,	ipv6_routing_header},
-	{NEXTHDR_FRAGMENT,	ipv6_reassembly},
-  
-	{NEXTHDR_DEST,		ipv6_dest_opt},
-   /*	
-	{NEXTHDR_AUTH,		ipv6_auth_hdr},
-	{NEXTHDR_ESP,		ipv6_esp_hdr},
-    */
-	{NEXTHDR_MAX,		NULL}
-};
-
-/* New header structures */
-
-
-struct ipv6_tlvtype {
-	u8 type;
-	u8 len;
-};
-
-struct ipv6_destopt_hdr {
-	u8 nexthdr;
-	u8 hdrlen;
-};
-
-
-struct tlvtype_proc {
-	u8	type;
-	int	(*func) (struct sk_buff *, struct device *dev, __u8 *ptr,
-			 struct ipv6_options *opt); 
-	
-	/* these functions do NOT  update skb->h.raw */
-			 
-} tlvprocdestopt_lst[] = {
-	{255,			NULL}
-};
-
-
-static int parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb,
-		     struct device *dev, __u8 *nhptr, struct ipv6_options *opt,
-		     void *lastopt)
-{
-	struct ipv6_tlvtype *hdr;
-	struct tlvtype_proc *curr;
-	int pos;
-
-	while ((hdr=(struct ipv6_tlvtype *)skb->h.raw) != lastopt)
-		switch (hdr->type & 0x3F)
-		{
-		case 0: /* TLV encoded Pad1 */
-			skb->h.raw++;
-			break;
-
-		case 1: /* TLV encoded PadN */
-			skb->h.raw += hdr->len+2;
-			break;
-
-		default: /* Other TLV code so scan list */
-			for (curr=procs; curr->type != 255; curr++)
-				if (curr->type == (hdr->type & 0x3F))
-				{
-					curr->func(skb, dev, nhptr, opt);
-					skb->h.raw += hdr->len+2;
-					break;
-				}
-		
-			if (curr->type==255)
-			{ 
-				/* unkown type */
-				pos= (__u8 *) skb->h.raw - (__u8 *) skb->nh.raw;
-				/* I think this is correct please check - IPM */
-
-				switch ((hdr->type & 0xC0) >> 6) {
-					case 0: /* ignore */
-						skb->h.raw += hdr->len+2;
-						break;
-						
-					case 1: /* drop packet */
-						kfree_skb(skb, FREE_READ);
-						return 0;
-
-					case 2: /* send ICMP PARM PROB regardless and 
-						   drop packet */
-						icmpv6_send(skb, ICMPV6_PARAMETER_PROB, 
-							    2, pos, dev);
-						kfree_skb(skb, FREE_READ);
-						return 0;
-
-					case 3: /* Send ICMP if not a multicast address 
-						   and drop packet */
-						if (!(ipv6_addr_type(&(skb->nh.ipv6h->daddr)) & IPV6_ADDR_MULTICAST) )
-							icmpv6_send(skb, ICMPV6_PARAMETER_PROB, 2, pos, dev);
-						kfree_skb(skb, FREE_READ);
-						return 0;
-					}
-			}
-			break;
-		}
-	
-	return 1;
-}
- 
-
-
-static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev, __u8 *nhptr,
-			 struct ipv6_options *opt)
-{
-	struct sk_buff *skb=*skb_ptr;
-	struct ipv6_destopt_hdr *hdr = (struct ipv6_destopt_hdr *) skb->h.raw;
-	
-	if (parse_tlv(tlvprocdestopt_lst, skb, dev, nhptr, opt,skb->h.raw+hdr->hdrlen))
-		return hdr->nexthdr;
-	else
-		return 0;
-}
-
-
-
-/*
- *	0 - deliver
- *	1 - block
- */
-static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
-{
-	struct icmpv6hdr *icmph;
-	struct raw6_opt *opt;
-
-	opt = &sk->tp_pinfo.tp_raw;
-	icmph = (struct icmpv6hdr *) (skb->nh.ipv6h + 1);
-	return test_bit(icmph->type, &opt->filter);
-}
-
-/*
- *	demultiplex raw sockets.
- *	(should consider queueing the skb in the sock receive_queue
- *	without calling rawv6.c)
- */
-static struct sock * ipv6_raw_deliver(struct sk_buff *skb,
-				      struct device *dev,
-				      struct ipv6_options *opt,
-				      __u16 nexthdr,
-				      __u16 len,
-				      struct in6_addr *saddr,
-				      struct in6_addr *daddr)
-{
-	struct sock *sk, *sk2;
-	__u8 hash;
-
-	hash = nexthdr & (MAX_INET_PROTOS - 1);
-
-	sk = raw_v6_htable[hash];
-
-	/*
-	 *	The first socket found will be delivered after
-	 *	delivery to transport protocols.
-	 */
-
-	if (sk == NULL)
-		return NULL;
-	
-	sk = raw_v6_lookup(sk, nexthdr, daddr, saddr);
-
-	if (sk)
-	{
-		sk2 = sk;
-
-		while ((sk2 = raw_v6_lookup(sk2->next, nexthdr, daddr, saddr)))
-		{
-			struct sk_buff *buff;
-
-			if (nexthdr == IPPROTO_ICMPV6 &&
-			    icmpv6_filter(sk2, skb))
-			{
-				continue;
-			}
-			buff = skb_clone(skb, GFP_ATOMIC);
-			buff->sk = sk2;
-			rawv6_rcv(buff, dev, saddr, daddr, opt, len);
-		}
-	}
-
-	if (sk && nexthdr == IPPROTO_ICMPV6 && icmpv6_filter(sk, skb))
-	{
-		sk = NULL;
-	}   
-	
-	return sk;
-}
-
-int ipv6_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
-{
-	struct inet6_ifaddr	*ifp;
-	struct ipv6_options	*opt = (struct ipv6_options *) skb->cb;
-	struct ipv6hdr		*hdr;
-	u8			hash;
-	u8			addr_type;
-	struct inet6_protocol	*ipprot;
-	struct sock		*raw_sk;
-	int			found = 0;
-	int			nexthdr = 0;
-	__u8			*nhptr;
-	int			pkt_len;
-
-	if (skb->pkt_type == PACKET_OTHERHOST) {
-		kfree_skb(skb, FREE_READ);
-		return 0;
-	}
-
-	hdr = skb->nh.ipv6h;
-	skb->h.raw = (__u8*)hdr;
-
-	if (skb->len < sizeof(struct ipv6hdr) || hdr->version != 6)
-	{
-		ipv6_statistics.Ip6InHdrErrors++;
-		printk(KERN_DEBUG "ipv6_rcv: broken header\n");
-		kfree_skb(skb, FREE_READ);
-		return 0;
-	}
-
-	pkt_len = ntohs(hdr->payload_len);
-
-	if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
-	{
-		printk(KERN_DEBUG "ipv6_rcv: invalid payload length\n");
-		kfree_skb(skb, FREE_READ);
-		return 0;
-	}
-
-	skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
-
-	/* check daddr */
-
-	/* Accounting & Firewall check */
-
-	addr_type = ipv6_addr_type(&hdr->daddr);
-
-	if (addr_type & IPV6_ADDR_MULTICAST)
-	{
-		/* 
-		 * if mcast address is not for one of our groups
-		 * either pass it to mcast router or discard it 
-		 */
-
-		if (ipv6_chk_mcast_addr(dev, &hdr->daddr) == 0)
-		{
-			/* something like:
-			   if (acting_as_router)
-				ipv6_mcast_route(skb, ...)
-			   else	
-			   */
-			kfree_skb(skb, FREE_READ);
-			return 0;
-		}
-	}
-
-	if (addr_type & IPV6_ADDR_MULTICAST ||
-	    (ifp = ipv6_chk_addr(&hdr->daddr)))	    
-	{
-
-		/* loop in a cicle parsing nexthdrs */
-
-		skb->h.raw   += sizeof(struct ipv6hdr);
- 
-		/* extension header processing must update skb->h.raw */
-
-		nexthdr = hdr->nexthdr;
-		nhptr = &hdr->nexthdr;
-
-
-		while(1)
-		{
-			struct hdrtype_proc *hdrt;
-
-			/* check for extension header */
-
-			for (hdrt=hdrproc_lst; hdrt->type != NEXTHDR_MAX; hdrt++)
-			{
-				if (hdrt->type == nexthdr)
-				{
-					if ((nexthdr = hdrt->func(&skb, dev, nhptr, opt)))
-					{
-						nhptr = skb->h.raw;
-						hdr = skb->nh.ipv6h;
-						continue;
-					}
-					return 0;
-				}
-			}
-			break;
-
-		}
-
-		/* 
-		 * deliver to raw sockets
-		 * should we deliver raw after or before parsing 
-		 * extension headers ?
-		 * delivering after means we do reassembly of datagrams
-		 * in ip.
-		 */
-
-		pkt_len = skb->tail - skb->h.raw;
-
-		raw_sk = ipv6_raw_deliver(skb, dev, opt, nexthdr, pkt_len,
-					  &hdr->saddr, &hdr->daddr);
-
-		/* check inet6_protocol list */
-
-		hash = nexthdr & (MAX_INET_PROTOS -1);
-		for (ipprot = (struct inet6_protocol *) inet6_protos[hash]; 
-		     ipprot != NULL; 
-		     ipprot = (struct inet6_protocol *) ipprot->next)
-		{
-			struct sk_buff *buff = skb;
-
-			if (ipprot->protocol != nexthdr)
-				continue;
-
-			if (ipprot->copy || raw_sk)
-				buff = skb_clone(skb, GFP_ATOMIC);
-
-
-			ipprot->handler(buff, dev, 
-					&hdr->saddr, &hdr->daddr,
-					opt, pkt_len,
-					0, ipprot);
-			found = 1;
-		}
-
-		if (raw_sk)
-		{
-			skb->sk = raw_sk;
-			rawv6_rcv(skb, dev, &hdr->saddr, &hdr->daddr, opt,
-				  htons(hdr->payload_len));
-			found = 1;
-		}
- 	       
-		/* not found: send ICMP parameter problem back */
-
-		if (!found)
-		{
-			printk(KERN_DEBUG "proto not found %d\n", nexthdr);
-			kfree_skb(skb, FREE_READ);
-		}
-			
-	}
-	else
-	{
-		if (skb->pkt_type != PACKET_HOST) {
-			kfree_skb(skb, FREE_READ);
-			return 0;
-		}
-
-		if (ipv6_forwarding)
-		{
-			if (addr_type & IPV6_ADDR_LINKLOCAL)
-			{
-				printk(KERN_DEBUG
-				       "link local pkt to forward\n");
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-			ipv6_forward(skb, dev, 0);
-		}
-		else
-		{
-			printk(KERN_WARNING "IPV6: packet to forward -"
-			       "host not configured as router\n");
-			kfree_skb(skb, FREE_READ);
-		}
-	}
-
-	return 0;
-}
-
-/*
- * Local variables:
- * c-file-style: "Linux"
- * End:
- */
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ipv6_output.c linux/net/ipv6/ipv6_output.c
--- v2.1.29/linux/net/ipv6/ipv6_output.c	Mon Jan 13 22:16:49 1997
+++ linux/net/ipv6/ipv6_output.c	Wed Dec 31 16:00:00 1969
@@ -1,969 +0,0 @@
-/*
- *	IPv6 output functions
- *	Linux INET6 implementation 
- *
- *	Authors:
- *	Pedro Roque		<roque@di.fc.ul.pt>	
- *
- *	Based on linux/net/ipv4/ip_output.c
- *
- *	$Id: ipv6_output.c,v 1.19 1996/10/16 18:34:16 roque Exp $
- *
- *	This program is free software; you can redistribute it and/or
- *      modify it under the terms of the GNU General Public License
- *      as published by the Free Software Foundation; either version
- *      2 of the License, or (at your option) any later version.
- */
-
-/*
- *	Changes:
- *
- *	Andi Kleen		:	exception handling
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/sched.h>
-#include <linux/net.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/in6.h>
-
-#include <net/sock.h>
-#include <net/snmp.h>
-
-#include <net/ipv6.h>
-#include <net/ndisc.h>
-#include <net/protocol.h>
-#include <net/transp_v6.h>
-#include <net/ipv6_route.h>
-#include <net/addrconf.h>
-
-static u32	ipv6_fragmentation_id = 1;
-int		ipv6_forwarding = 0;		/* default: host */
-
-static int __inline__ ipv6_build_mac_header(struct sk_buff *skb,
-					    struct device *dev,
-					    struct neighbour *neigh, 
-					    int len)
-{
-	int mac;
-	int hdrlen = 0;
-
-	skb->arp = 1;
-	skb->nexthop = neigh;
-
-	skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
-
-	if (dev->hard_header_len)
-	{
-
-		/*
-		 *	FIXME: use cached hardware header if availiable
-		 */
-		if (dev->hard_header)
-		{
-			mac = dev->hard_header(skb, dev, ETH_P_IPV6, 
-					       NULL, NULL, len);
-		
-			if (mac < 0)
-			{				
-				hdrlen = -mac;
-				skb->arp = 0;
-			}
-			else
-			{				
-				hdrlen = mac;
-			}
-		}
-		else
-			hdrlen = dev->hard_header_len;
-	}
-
-	skb->mac.raw = skb->data;
-	return hdrlen;
-}
-
-void ipv6_redo_mac_hdr(struct sk_buff *skb, struct neighbour *neigh, int len)
-{
-	struct device *dev = neigh->dev;
-	int mac;
-	
-	skb->dev = dev;
-	skb->nexthop = neigh;
-	skb->arp = 1;
-
-	skb_pull(skb, (unsigned char *) skb->nh.ipv6h - skb->data);
-
-	/*
-	 *	neighbour cache should have the ether address
-	 *	cached... use it
-	 */ 
-
-	if (dev->hard_header)
-	{
-		/*
-		 *	FIXME: use cached hardware header if availiable
-		 */
-
-		mac = dev->hard_header(skb, dev, ETH_P_IPV6, 
-				       NULL, NULL, len);
-		
-		if (mac < 0)
-		{				
-			skb->arp = 0;
-		}
-
-	}
-	skb->mac.raw = skb->data;
-}
-
-void default_output_method(struct sk_buff *skb, struct rt6_info *rt)
-{
-	struct sock *sk = skb->sk;
-	struct device *dev = skb->dev;
-
-	if (dev->flags & IFF_UP)
-	{
-		/*
-		 *	If we have an owner use its priority setting,
-		 *	otherwise use NORMAL
-		 */
-
-		dev_queue_xmit(skb);
-	}
-	else
-	{
-		if(sk)
-			sk->err = ENETDOWN;
-
-		ipv6_statistics.Ip6OutDiscards++;
-		
-		kfree_skb(skb, FREE_WRITE);
-	}
-}
-
-/*
- *	xmit an sk_buff (used by TCP)
- *	sk can be NULL (for sending RESETs)
- */
-int ipv6_xmit(struct sock *sk, struct sk_buff *skb, struct in6_addr *saddr,
-	      struct in6_addr *daddr, struct ipv6_options *opt, int proto)
-{
-	struct ipv6hdr *hdr;
-	struct dest_entry *dc;
-	struct ipv6_pinfo *np = NULL;
-	struct device *dev = skb->dev;
-	int seg_len;
-	int addr_type;
-	int rt_flags = 0;
-
-
-	addr_type = ipv6_addr_type(daddr);
-
-	if (addr_type & (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_SITELOCAL))
-	{
-		/*
-		 *	force device match on route lookup
-		 */
-		
-		rt_flags |= RTI_DEVRT;
-	}
-
-	if (sk && sk->localroute)
-		rt_flags |= RTI_GATEWAY;
-
-	hdr = skb->nh.ipv6h;
-	
-
-	if (sk)
-	{
-		np = &sk->net_pinfo.af_inet6;
-	}
-	
-	if (np && np->dest)
-	{
-		dc = ipv6_dst_check(np->dest, daddr, np->dc_sernum, rt_flags);
-	}
-	else
-	{
-		dc = ipv6_dst_route(daddr, dev, rt_flags);
-	}
-
-	if (dc == NULL)
-	{
-		ipv6_statistics.Ip6OutNoRoutes++;
-		return(-ENETUNREACH);
-	}
-
-	dev = dc->rt.rt_dev;
-	
-	if (saddr == NULL)
-	{
-		struct inet6_ifaddr *ifa;
-		
-		ifa = ipv6_get_saddr((struct rt6_info *) dc, daddr);
-		
-		if (ifa == NULL)
-		{
-			printk(KERN_DEBUG 
-			       "ipv6_xmit: get_saddr failed\n");
-			return -ENETUNREACH;
-		}
-		
-		saddr = &ifa->addr;
-		
-		if (np)
-		{
-			ipv6_addr_copy(&np->saddr, saddr);
-		}
-	}
-
-	seg_len = skb->tail - ((unsigned char *) hdr);
-
-	/*
-	 *	Link Layer headers
-	 */
-
-	skb->protocol = __constant_htons(ETH_P_IPV6);
-	skb->dev = dev;
-	
-	ipv6_redo_mac_hdr(skb, dc->dc_nexthop, seg_len);
-	
-	/*
-	 *	Fill in the IPv6 header
-	 */
-
-	hdr->version = 6;
-	hdr->priority = np ? np->priority : 0;
-
-	if (np)
-		memcpy(hdr->flow_lbl, (void *) &np->flow_lbl, 3);
-	else
-		memset(hdr->flow_lbl, 0, 3);
-
-	hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr));
-	hdr->nexthdr = proto;
-	hdr->hop_limit = np ? np->hop_limit : ipv6_hop_limit;
-	
-	memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
-	memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-
-	
-	/*
-	 *	Options
-	 */
-
-
-	/*
-	 *	Output the packet
-	 */
-
-	ipv6_statistics.Ip6OutRequests++;
-
-	if (dc->rt.rt_output_method)
-	{
-		(*dc->rt.rt_output_method)(skb, (struct rt6_info *) dc);
-	}
-	else
-		default_output_method(skb, (struct rt6_info *) dc);
-
-	/*
-	 *	Update serial number of cached dest_entry or
-	 *	release destination cache entry
-	 */
-	
-	if (np)
-	{
-		np->dest = dc;
-		if (dc->rt.fib_node)
-		{
-			np->dc_sernum = dc->rt.fib_node->fn_sernum;
-		}
-	}
-	else
-	{
-		ipv6_dst_unlock(dc);
-	}
-
-	return 0;
-}
-
-/*
- *	To avoid extra problems ND packets are send through this
- *	routine. It's code duplication but i really want to avoid
- *	extra checks since ipv6_build_header is used by TCP (which
- *	is for us performace critical)
- */
-
-int ipv6_bld_hdr_2(struct sock *sk, struct sk_buff *skb, struct device *dev,
-		   struct neighbour *neigh,
-		   struct in6_addr *saddr, struct in6_addr *daddr,
-		   int proto, int len)
-{
-	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-	struct ipv6hdr *hdr;
-	int hdrlen = 0;
-
-	skb->dev = dev;
-
-	/* build MAC header */
-	hdrlen += ipv6_build_mac_header(skb, dev, neigh, len);
-
-	/* build fixed IPv6 header */
-
-	if (proto == IPPROTO_RAW)
-		return hdrlen;
-
-	
-	hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
-	skb->nh.ipv6h = hdr;
-	
-	hdr->version  = 6;
-	hdr->priority = np->priority & 0x0f;
-
-	memset(hdr->flow_lbl, 0, 3);
-
-	hdr->hop_limit =  np->hop_limit;
-
-	if (saddr == NULL)
-	{
-		printk(KERN_DEBUG "bug: bld_hdr called with no saddr\n");
-		return -ENETUNREACH;
-	}
-
-	memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
-	memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-	
-	hdrlen += sizeof(struct ipv6hdr);
-
-	hdr->nexthdr = proto;
-
-	return hdrlen;
-}
-
-void ipv6_queue_xmit(struct sock *sk, struct device *dev, struct sk_buff *skb,
-		     int free)
-{
-	struct ipv6hdr *hdr;
-	u32 seg_len;
-
-	hdr = skb->nh.ipv6h;
-	skb->protocol = __constant_htons(ETH_P_IPV6);
-
-	seg_len = skb->tail - ((unsigned char *) hdr);
-
-	hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr));
-
-	if (dev == NULL)
-	{
-		printk(KERN_DEBUG "ipv6_queue_xmit: unknown device\n");
-		return;
-	}
-
-	skb->dev = dev;
-	
-	ipv6_statistics.Ip6OutRequests++;
-
-
-	/*
-	 *	Multicast loopback
-	 */
-
-	if (dev->flags & IFF_UP)
-	{
-		/*
-		 *	If we have an owner use its priority setting,
-		 *	otherwise use NORMAL
-		 */
-
-		dev_queue_xmit(skb);
-	}
-	else
-	{
-		if(sk)
-			sk->err = ENETDOWN;
-
-		ipv6_statistics.Ip6OutDiscards++;
-
-		kfree_skb(skb, FREE_WRITE);
-	}
-
-}
-
-
-int ipv6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
-		    struct in6_addr *dest, unsigned short int length,
-		    struct in6_addr *saddr, struct device *dev,
-		    struct ipv6_options *opt, int proto, int hlimit,
-		    int noblock)
-{
-	rt6_output_method_t output_method = default_output_method;
-	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-	struct dest_entry *dc = NULL;
-	struct in6_addr *daddr = dest;
-	struct ipv6hdr *hdr;
-	struct neighbour *neigh;	
-	int	addr_type;
-	int	pktlength;
-	int	pmtu = 0;
-	int	rt_flags = 0;
-	int	error; 
-	
-	if (opt && opt->srcrt)
-	{
-		struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
-		daddr = rt0->addr;
-	}
-
-	addr_type = ipv6_addr_type(daddr);
-	
-	if (hlimit < 1)
-	{
-		if (addr_type & IPV6_ADDR_MULTICAST)
-		{
-			hlimit = np->mcast_hops;
-			if (dev == NULL)
-			{
-				dev = np->mc_if;
-			}
-		}
-		else
-			hlimit = np->hop_limit;
-	}
-		
-	if (addr_type & (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_SITELOCAL |
-			 IPV6_ADDR_MULTICAST))
-	{
-		/*
-		 *	force device match on route lookup
-		 */
-		
-		rt_flags |= RTI_DEVRT;
-	}
-
-	if (sk->localroute)
-	{
-		rt_flags |= RTI_GATEWAY;
-	}
-
-	if (np->dest)
-	{
-		np->dest = ipv6_dst_check(np->dest, daddr, np->dc_sernum,
-					  rt_flags);
-		
-		dc = np->dest;
-
-		if (dc && dc->rt.fib_node)
-		{
-			np->dc_sernum = dc->rt.fib_node->fn_sernum;
-		}
-		else
-		{
-			printk(KERN_WARNING "dc entry not in table\n");
-		}
-	}
-	else
-	{
-		dc = ipv6_dst_route(daddr, dev, rt_flags);
-	}
-
-	if (dc == NULL)
-	{
-		if ((addr_type & IPV6_ADDR_MULTICAST) && dev)
-		{
-			neigh = NULL;
-			pmtu = dev->mtu;
-		}
-		else
-		{
-			ipv6_statistics.Ip6OutNoRoutes++;
-			return(-ENETUNREACH);
-		}
-	}
-	else
-	{
-		neigh = dc->dc_nexthop;
-		dev = neigh->dev;
-
-		if (dc->rt.rt_output_method)
-		{
-			output_method = dc->rt.rt_output_method;
-		}
-
-		if (dc->dc_flags & DCF_PMTU)
-			pmtu = dc->dc_pmtu;
-		else
-			pmtu = dev->mtu;
-	}
-
-
-	if (saddr == NULL)
-	{
-		struct inet6_ifaddr *ifa;
-
-		ifa = ipv6_get_saddr((struct rt6_info *) dc, daddr);
-
-		if (ifa == NULL)
-		{
-			printk(KERN_DEBUG 
-			       "ipv6_build_xmit: get_saddr failed\n");
-			return -ENETUNREACH;
-		}
-
-		saddr = &ifa->addr;
-	}
-
-	if (dc && np->dest == NULL)
-	{
-		ipv6_dst_unlock(dc);
-	}
-
-	pktlength = length;
-
-	if (!sk->ip_hdrincl)
-	{ 
-		pktlength += sizeof(struct ipv6hdr);
-		if (opt)
-		{
-			pktlength += opt->opt_flen + opt->opt_nflen;
-		}
-	}
-		
-
-	dev_lock_list();
-
-	/*
-	 *	reminder: don't allow fragmentation for IPPROTO_RAW
-	 */
-
-
-	if (pktlength <= pmtu) 
-	{
-		struct sk_buff *skb =
-			sock_alloc_send_skb(sk, pktlength+15+
-					    dev->hard_header_len,
-					    0, noblock, &error);
-		
-		if (skb == NULL)
-		{
-			ipv6_statistics.Ip6OutDiscards++;
-			dev_unlock_list();
-			return error;
-
-		}
-
-		skb->dev=dev;
-		skb->protocol = htons(ETH_P_IPV6);
-		skb->when=jiffies;
-		skb->arp=0;
-
-		/* build the mac header... */
-		ipv6_build_mac_header(skb, dev, neigh, pktlength);
-
-		hdr = (struct ipv6hdr *) skb->tail;
-		skb->nh.ipv6h = hdr;
-
-		if (!sk->ip_hdrincl)
-		{
-			skb_put(skb, sizeof(struct ipv6hdr));
-
-			hdr->version = 6;
-			hdr->priority = np->priority;
-
-			memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
-
-			hdr->payload_len = htons(pktlength - 
-						 sizeof(struct ipv6hdr));
-			
-			hdr->hop_limit = hlimit;
-
-			memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
-			memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-
-			if (opt && opt->srcrt)
-			{
-				hdr->nexthdr = ipv6opt_bld_rthdr(skb, opt,
-								 dest, proto);
-								 
-			}
-			else
-				hdr->nexthdr = proto;
-		}
-			
-		skb_put(skb, length);
-		error = getfrag(data, &hdr->saddr,
-				((char *) hdr) + (pktlength - length),
-				0, length);
-			
-		if (!error) 
-		{
-			ipv6_statistics.Ip6OutRequests++;
-			(*output_method)(skb, (struct rt6_info *) dc);
-		} else
-		{
-			error = -EFAULT;
-			kfree_skb(skb, FREE_WRITE);			
-		}
-
-		dev_unlock_list();
-		return error;
-	}
-	else
-	{
-		/*
-		 *	Fragmentation
-		 */
-
-		/*
-		 *	Extension header order:
-		 *	Hop-by-hop -> Routing -> Fragment -> rest (...)
-		 *	
-		 *	We must build the non-fragmented part that
-		 *	will be in every packet... this also means
-		 *	that other extension headers (Dest, Auth, etc)
-		 *	must be considered in the data to be fragmented
-		 */
-
-		struct sk_buff  *last_skb;
-		struct frag_hdr *fhdr;
-		int unfrag_len;
-		int payl_len;
-		int frag_len;
-		int last_len;
-		int nfrags;
-		int err;
-		int fhdr_dist;
-		__u32 id;
-
-		if (sk->ip_hdrincl)
-		{
-			return -EMSGSIZE;
-		}
-
-		id = ipv6_fragmentation_id++;
-
-		unfrag_len = sizeof(struct ipv6hdr) + sizeof(struct frag_hdr);
-		payl_len = length;
-
-		if (opt)
-		{
-			unfrag_len += opt->opt_nflen;
-			payl_len += opt->opt_flen;
-		}
-
-		nfrags = payl_len / ((pmtu - unfrag_len) & ~0x7);
-
-		/* 
-		 * Length of fragmented part on every packet but 
-		 * the last must be an:
-		 * "integer multiple of 8 octects".
-		 */
-
-		frag_len = (pmtu - unfrag_len) & ~0x7;
-
-		/*
-		 *	We must send from end to start because of 
-		 *	UDP/ICMP checksums. We do a funny trick:
-		 *	fill the last skb first with the fixed
-		 *	header (and its data) and then use it
-		 *	to create the following segments and send it
-		 *	in the end. If the peer is checking the M_flag
-		 *	to trigger the reassembly code then this 
-		 *	might be a good idea.
-		 */
-
-		last_len = payl_len - (nfrags * frag_len);
-
-		if (last_len == 0)
-		{
-			last_len = frag_len;
-			nfrags--;
-		}
-		
-		last_skb = sock_alloc_send_skb(sk, unfrag_len + frag_len +
-					       dev->hard_header_len + 15,
-					       0, noblock, &err);
-
-		if (last_skb == NULL)
-		{
-			dev_unlock_list();
-			return err;
-		}
-
-		last_skb->dev=dev;
-		last_skb->protocol = htons(ETH_P_IPV6);
-		last_skb->when=jiffies;
-		last_skb->arp=0;
-
-		/* 
-		 * build the mac header... 
-		 */
-		ipv6_build_mac_header(last_skb, dev, neigh,
-				      unfrag_len + frag_len);
-
-		hdr = (struct ipv6hdr *) skb_put(last_skb, 
-						 sizeof(struct ipv6hdr));
-		last_skb->nh.ipv6h = hdr;
-
-		hdr->version = 6;
-		hdr->priority = np->priority;
-
-		memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
-		hdr->payload_len = htons(unfrag_len + frag_len - 
-					 sizeof(struct ipv6hdr));
-		
-		hdr->hop_limit = hlimit;
-
-		hdr->nexthdr = NEXTHDR_FRAGMENT;
-
-		memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
-		memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-		
-		if (opt && opt->srcrt)
-		{
-			hdr->nexthdr = ipv6opt_bld_rthdr(last_skb, opt, dest,
-							 NEXTHDR_FRAGMENT);
-		}
-	
-		fhdr = (struct frag_hdr *)
-			skb_put(last_skb, sizeof(struct frag_hdr));
-
-		memset(fhdr, 0, sizeof(struct frag_hdr));
-
-		fhdr->nexthdr  = proto;		
-		fhdr->frag_off = ntohs(nfrags * frag_len);
-		fhdr->identification = id;
-
-		fhdr_dist = (unsigned char *) fhdr - last_skb->data;
-
-		error = getfrag(data, &hdr->saddr, last_skb->tail,
-				nfrags * frag_len, last_len);
-		
-		if (!error)
-		{	
-			while (nfrags--)
-			{
-				struct sk_buff *skb;
-			
-				struct frag_hdr *fhdr2;
-				
-				printk(KERN_DEBUG "sending frag %d\n", nfrags);
-				skb = skb_copy(last_skb, sk->allocation);
-				
-				fhdr2 = (struct frag_hdr *)
-					(skb->data + fhdr_dist);
-				
-				/* more flag on */
-				fhdr2->frag_off = ntohs(nfrags * frag_len + 1);
-				
-				/*
-				 *	FIXME:
-				 *	if (nfrags == 0)
-				 *	put rest of headers
-				 */
-				
-				error = getfrag(data, &hdr->saddr,
-						skb_put(skb, frag_len), 
-						nfrags * frag_len, frag_len);
-			
-				if (error)
-				{
-					kfree_skb(skb, FREE_WRITE);
-					break;
-				}
-
-				ipv6_statistics.Ip6OutRequests++;
-				(*output_method)(skb, (struct rt6_info *) dc);
-			}
-		}
-
-		if (error)
-		{
-			kfree_skb(last_skb, FREE_WRITE);
-			dev_unlock_list();
-			return -EFAULT;
-		}
-
-		printk(KERN_DEBUG "sending last frag \n");
-
-		hdr->payload_len = htons(unfrag_len + last_len - 
-					 sizeof(struct ipv6hdr));
-
-		/*
-		 *	update last_skb to reflect the getfrag we did
-		 *	on start.
-		 */
-		last_skb->tail += last_len;
-		last_skb->len += last_len;
-
-		/* 
-		 * toss the mac header out and rebuild it.
-		 * needed because of the different frame length.
-		 * ie: not needed for an ethernet.
-		 */
-
-		if (dev->type != ARPHRD_ETHER && last_len != frag_len)
-		{
-			ipv6_redo_mac_hdr(last_skb, neigh,
-					  unfrag_len + last_len);
-		}
-
-		ipv6_statistics.Ip6OutRequests++;
-		(*output_method)(last_skb, (struct rt6_info *) dc);
-
-		dev_unlock_list();
-		return 0;
-	}
-	return -1;
-}
-
-static int pri_values[4] =
-{
-	SOPRI_BACKGROUND,
-	SOPRI_NORMAL,
-	SOPRI_NORMAL,
-	SOPRI_INTERACTIVE
-};
-
-void ipv6_forward(struct sk_buff *skb, struct device *dev, int flags)
-{
-	struct neighbour *neigh;
-	struct dest_entry *dest;
-	int priority;
-	int rt_flags;
-	int size;
-	int pmtu;
-
-	if (skb->nh.ipv6h->hop_limit <= 1)
-	{
-		icmpv6_send(skb, ICMPV6_TIME_EXCEEDED, ICMPV6_EXC_HOPLIMIT,
-			    0, dev);
-
-		kfree_skb(skb, FREE_READ);
-		return;
-	}
-
-	skb->nh.ipv6h->hop_limit--;
-
-	if (ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)
-	{
-		printk(KERN_DEBUG "ipv6_forward: link local source addr\n");
-		icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_NOT_NEIGHBOUR,
-			    0, dev);		
-		kfree_skb(skb, FREE_READ);
-		return;
-	}
-
-	rt_flags = RTF_MODIFIED;
-
-	if ((flags & IP6_FW_STRICT))
-	{
-		rt_flags |= RTF_GATEWAY;
-	}
-
-	dest = ipv6_dst_route(&skb->nh.ipv6h->daddr, NULL, rt_flags);
-
-	if (dest == NULL)
-	{
-		int code;
-
-		if (flags & IP6_FW_STRICT)
-			code = ICMPV6_NOT_NEIGHBOUR;
-		else
-			code = ICMPV6_NOROUTE;
-			
-		icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0, dev);
-			    
-		kfree_skb(skb, FREE_READ);
-		return;
-	}
-
-	neigh = dest->dc_nexthop;
-
-	if (neigh->dev == dev && (dev->flags & IFF_MULTICAST) &&
-	    !(flags & IP6_FW_SRCRT))
-	{
-		struct in6_addr *target = NULL;
-		struct nd_neigh *ndn = (struct nd_neigh *) neigh;
-		
-		/* 
-		 *	outgoing device equal to incoming device
-		 *	send a redirect
-		 */
-		
-		if ((dest->dc_flags & RTF_GATEWAY))
-		{
-			target = &ndn->ndn_addr;
-		}
-		else
-		{
-			target = &skb->nh.ipv6h->daddr;
-		}
-
-		ndisc_send_redirect(skb, neigh, target);
-	}
-
-	pmtu = neigh->dev->mtu;
-
-	size = sizeof(struct ipv6hdr) + ntohs(skb->nh.ipv6h->payload_len);
-	
-	if (size > pmtu)
-	{
-		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, pmtu, dev);
-		kfree_skb(skb, FREE_READ);
-		return;
-	}
-
-	ipv6_dst_unlock(dest);
-
-	if (skb_headroom(skb) < neigh->dev->hard_header_len)
-	{
-		struct sk_buff *buff;
-
-		buff = alloc_skb(neigh->dev->hard_header_len + skb->len + 15,
-				 GFP_ATOMIC);
-
-		if (buff == NULL)
-		{
-			return;
-		}
-		
-		skb_reserve(buff, (neigh->dev->hard_header_len + 15) & ~15);
-
-		buff->protocol = __constant_htons(ETH_P_IPV6);
-		buff->h.raw = skb_put(buff, size);
-
-		memcpy(buff->h.raw, skb->nh.ipv6h, size);
-		buff->nh.ipv6h = (struct ipv6hdr *) buff->h.raw;
-		kfree_skb(skb, FREE_READ);
-		skb = buff;
-	}
-
-	ipv6_redo_mac_hdr(skb, neigh, size);
-
-	priority = skb->nh.ipv6h->priority;
-
-	priority = (priority & 0x7) >> 1;
-	priority = pri_values[priority];
-
-	if (dev->flags & IFF_UP)
-	{
-		skb->dev = neigh->dev;
-		dev_queue_xmit(skb);
-	}
-	else
-	{
-		ipv6_statistics.Ip6OutDiscards++;
-		kfree_skb(skb, FREE_READ);
-	}
-}
-
-
-/*
- * Local variables:
- * c-file-style: "Linux"
- * End:
- */
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ipv6_route.c linux/net/ipv6/ipv6_route.c
--- v2.1.29/linux/net/ipv6/ipv6_route.c	Thu Dec 12 06:54:25 1996
+++ linux/net/ipv6/ipv6_route.c	Wed Dec 31 16:00:00 1969
@@ -1,2058 +0,0 @@
-/*
- *	IPv6 routing table
- *	Linux INET6 implementation 
- *
- *	Authors:
- *	Pedro Roque		<roque@di.fc.ul.pt>	
- *
- *	This program is free software; you can redistribute it and/or
- *      modify it under the terms of the GNU General Public License
- *      as published by the Free Software Foundation; either version
- *      2 of the License, or (at your option) any later version.
- */
-
-/*
- *	Changes:
- *
- *	Masaki Hirabaru			:	Fix for /proc info > pagesize
- *	<masaki@merit.edu>
- */
-
-#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/sched.h>
-#include <linux/net.h>
-#include <linux/route.h>
-#include <linux/netdevice.h>
-#include <linux/in6.h>
-
-#ifdef 	CONFIG_PROC_FS
-#include <linux/proc_fs.h>
-#endif
-
-#include <net/tcp.h>
-#include <net/sock.h>
-#include <net/snmp.h>
-
-#include <net/ipv6.h>
-#include <net/ndisc.h>
-#include <net/protocol.h>
-#include <net/ipv6_route.h>
-#include <net/addrconf.h>
-
-#include <net/netlink.h>
-
-#include <asm/uaccess.h>
-
-/*
- *	Routing Table
- *
- *	simplified version of a radix tree
- *
- *	- every node shares it's acestors prefix
- *	- the tree is ordered from less to most specific mask
- *	- default routes are handled apart
- *
- *	this facilitates recursion a lot
- */
-
-static struct rt6_info null_entry = {
-	NULL, NULL, 
-	{{{0}}},
-	0, 1,
-	NULL, NULL,
-	0, 0, RTF_REJECT
-};
-
-struct fib6_node routing_table = {
-	NULL, NULL, NULL, &null_entry, 
-	0, RTN_ROOT, 0
-};
-
-struct rt6_info		*default_rt_list = NULL;
-struct rt6_info		*loopback_rt = NULL;
-
-/*
- *	last_resort_rt - no routers present.
- *	Assume all destinations on link.
- */
-struct rt6_info		*last_resort_rt = NULL;
-
-static struct rt6_req request_queue = {
-	0, NULL, &request_queue, &request_queue
-};
-
-
-/*
- *	A routing update causes an increase of the serial number on the
- *	afected subtree. This allows for cached routes to be asynchronously
- *	tested when modifications are made to the destination cache as a
- *	result of redirects, path MTU changes, etc.
- */
-
-static __u32	rt_sernum	= 0;
-
-static atomic_t rt6_lock	= 0;
-static int	rt6_bh_mask	= 0;
-
-#define RT_BH_REQUEST	1
-#define RT_BH_GC	2
-
-static void __rt6_run_bh(void);
-
-typedef void (*f_pnode)(struct fib6_node *fn, void *);
-
-static void	rt6_walk_tree(f_pnode func, void * arg, int filter);
-static void	rt6_rt_timeout(struct fib6_node *fn, void *arg);
-static int	rt6_msgrcv(int unit, struct sk_buff *skb);
-
-struct rt6_statistics rt6_stats = {
-	1, 0, 1, 1, 0
-};
-
-static atomic_t	rt_clients = 0;
-
-void rt6_timer_handler(unsigned long data);
-
-struct timer_list rt6_gc_timer = {
-	NULL,
-	NULL,
-	0,
-	0,
-	rt6_timer_handler
-};
-
-static __inline__ void rt6_run_bh(void)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-
-	if (rt6_lock == 0 && rt6_bh_mask)
-	{
-		__rt6_run_bh();
-	}
-	restore_flags(flags);
-}
-
-/*
- *	request queue operations
- *	FIFO queue/dequeue
- */
-static __inline__ void rtreq_queue(struct rt6_req * req)
-{
-	unsigned long flags;
-	struct rt6_req *next = &request_queue;
-
-	save_flags(flags);
-	cli();
-
-	req->prev = next->prev;
-	req->prev->next = req;
-	next->prev = req;
-	req->next = next;
-	restore_flags(flags);
-}
-
-static __inline__ struct rt6_req * rtreq_dequeue(void)
-{
-	struct rt6_req *next = &request_queue;
-	struct rt6_req *head;
-
-	head = next->next;
-
-	if (head == next)
-	{
-		return NULL;
-	}
-
-	head->next->prev = head->prev;
-	next->next = head->next;
-
-	head->next = NULL;
-	head->prev = NULL;
-
-	return head;
-}
-
-/*
- *	compare "prefix length" bits of an address
- */
-static __inline__ int addr_match(struct in6_addr *a1, struct in6_addr *a2,
-				 int prefixlen)
-{
-	int pdw;
-	int pbi;
-
-	pdw = prefixlen >> 0x05;  /* num of whole __u32 in prefix */
-	pbi = prefixlen &  0x1f;  /* num of bits in incomplete u32 in prefix */
-
-	if (pdw) 
-	{
-		if (memcmp(a1, a2, pdw << 2))
-			return 0;
-	}
-
-	if (pbi) 
-	{
-		__u32 w1, w2;
-		__u32 mask;
-
-		w1 = a1->s6_addr32[pdw];
-		w2 = a2->s6_addr32[pdw];
-
-		mask = htonl((0xffffffff) << (0x20 - pbi));
-
-		if ((w1 ^ w2) & mask)
-			return 0;
-	}
-
-	return 1;
-}
-
-/*
- *	test bit. range [0-127]
- */
-
-static __inline__ int addr_bit_set(struct in6_addr *addr, int fn_bit)
-{
-	int dw;
-	__u32 b1;
-	__u32 mask;
-	int bit = fn_bit;
-
-	dw = bit >> 0x05;
-
-	b1 = addr->s6_addr32[dw];
-	
-	bit = ~bit;
-	bit &= 0x1f;
-	mask = htonl(1 << bit);
-	return (b1 & mask);
-}
-
-static __inline__ int addr_bit_equal(struct in6_addr *a1, struct in6_addr *a2,
-				     int fn_bit)
-{
-	int dw;
-	__u32 b1, b2;
-	__u32 mask;
-	int bit = fn_bit;
-
-	dw = bit >> 0x05;
-
-	b1 = a1->s6_addr32[dw];
-	b2 = a2->s6_addr32[dw];
-	
-	bit = ~bit;
-	bit &= 0x1f;
-	mask = htonl(1 << bit);
-	return !((b1 ^ b2) & mask);
-}
-
-/*
- *	find the first different bit between two addresses
- */
-static __inline__ int addr_diff(struct in6_addr *a1, struct in6_addr *a2)
-{
-	int i;
-
-	for (i = 0; i<4; i++)
-	{
-		__u32 b1, b2;
-		__u32 xb;
-		
-		b1 = a1->s6_addr32[i];
-		b2 = a2->s6_addr32[i];
-		
-		xb = b1 ^ b2;
-
-		if (xb)
-		{
-			int res = 0;
-			int j=31;
-
-			xb = ntohl(xb);
-
-			while (test_bit(j, &xb) == 0)
-			{
-				res++;
-				j--;
-			}
-			
-			return (i * 32 + res);
-		}
-	}
-
-	/*
-	 *	bit values are in range [0-127]
-	 *	128 is an ilegal value as we should *never* get to
-	 *	this point since that would mean the addrs are equal
-	 */
-	return 128;
-}
-
-/*
- *	add a rt to a node that may already contain routes
- *	sort routes in ascending metric order so that fib lookup
- *	returns the smallest metric by default
- */
-
-static __inline__ void fib6_add_rt2node(struct fib6_node *fn,
-					struct rt6_info *rt)
-{
-	struct rt6_info *iter, **back;
-
-	rt->fib_node = fn;
-	back = &fn->leaf;
-	
-	for (iter = fn->leaf; iter; iter=iter->next)
-	{
-		if (iter->rt_metric > rt->rt_metric)
-		{
-			break;
-		}
-
-		back = &iter->next;
-	}
-
-	rt->next = iter;
-	*back = rt;
-}
-
-/*
- *	Routing Table
- */
-
-static int fib6_add_1(struct rt6_info *rt)
-{
-	struct fib6_node *fn;
-	struct fib6_node *pn = NULL;
-	struct fib6_node *in;
-	struct fib6_node *ln;
-	struct in6_addr *addr;
-	__u32	bit;
-	__u32	dir = 0;
-	__u32	sernum = ++rt_sernum;
-	int pbit = rt->rt_prefixlen - 1;
-
-	addr = &rt->rt_dst;
-
-	/* insert node in tree */
-
-	fn = &routing_table;
-
-	for (;;)
-	{
-		if (fn == NULL)
-		{			
-			ln = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
-			if (ln == NULL)
-				return (-ENOMEM);
-
-			memset(ln, 0, sizeof(struct fib6_node));
-			ln->fn_bit   = pbit;
-			ln->fn_flags = RTN_BACKTRACK;
-			
-			ln->parent = pn;
-			ln->leaf = rt;
-			ln->fn_sernum = sernum;
-			rt->fib_node = ln;
-
-			atomic_inc(&rt->rt_ref);
-
-			if (dir)
-				pn->right = ln;
-			else
-				pn->left  = ln;
-
-			rt6_stats.fib_nodes++;
-			rt6_stats.fib_route_nodes++;
-			rt6_stats.fib_rt_entries++;
-
-			return(0);
-		}
-
-		if (addr_match(&fn->leaf->rt_dst, addr, fn->fn_bit))
-		{
-			if (pbit == fn->fn_bit && pbit &&
-			    addr_bit_equal(addr, &fn->leaf->rt_dst,
-					   rt->rt_prefixlen))
-			{
-				/* clean up an intermediate node */
-				if ((fn->fn_flags & RTN_BACKTRACK) == 0) 
-				{
-					rt_release(fn->leaf);
-					fn->leaf = NULL;
-					fn->fn_flags |= RTN_BACKTRACK;
-				}
-			
-				fib6_add_rt2node(fn, rt);
-				fn->fn_sernum = sernum;
-				atomic_inc(&rt->rt_ref);
-				
-				rt6_stats.fib_route_nodes++;
-				rt6_stats.fib_rt_entries++;
-				
-				return 0;
-			}
-
-			if (pbit > fn->fn_bit || pbit == 0)
-			{
-				/* walk down on tree */
-
-				fn->fn_sernum = sernum;
-
-				dir = addr_bit_set(addr, fn->fn_bit); 
-				pn = fn;
-				fn = dir ? fn->right: fn->left;
-
-				continue;
-			}
-		}
-
-		/*
-		 * split since we don't have a common prefix anymore or 
-		 * we have a less significant route.
-		 * we've to insert an intermediate node on the list
-		 * this new node will point to the one we need to create
-		 * and the current
-		 */
-
-		pn = fn->parent;
-
-		/* find 1st bit in difference between the 2 addrs */
-		bit = addr_diff(addr, &fn->leaf->rt_dst);
-
-
-		/* 
-		 *		(intermediate)	
-		 *	          /	   \
-		 *	(new leaf node)    (old node)
-		 */
-		if (rt->rt_prefixlen > bit)
-		{
-			in = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-		
-			if (in == NULL)
-				return (-ENOMEM);
-
-			memset(in, 0, sizeof(struct fib6_node));
-
-			/* 
-			 * new intermediate node. 
-			 * RTN_BACKTRACK will
-			 * be off since that an address that chooses one of
-			 * the branches would not match less specific routes
-			 * int the other branch
-			 */
-
-			in->fn_bit = bit;
-			in->parent = pn;
-			in->leaf = rt;
-			in->fn_sernum = sernum;
-			atomic_inc(&rt->rt_ref);
-
-			/* leaf node */
-			ln = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
-			if (ln == NULL)
-			{
-				kfree(in);
-				return (-ENOMEM);
-			}
-
-			/* update parent pointer */
-			if (dir)
-				pn->right = in;
-			else
-				pn->left  = in;
-
-			memset(ln, 0, sizeof(struct fib6_node));
-			ln->fn_bit   = pbit;
-			ln->fn_flags = RTN_BACKTRACK;
-			
-			ln->parent = in;
-			fn->parent = in;
-
-			ln->leaf = rt;
-			ln->fn_sernum = sernum;
-			atomic_inc(&rt->rt_ref);
-
-			rt->fib_node = ln;
-
-			if (addr_bit_set(addr, bit))
-			{
-				in->right = ln;
-				in->left  = fn;
-			}
-			else
-			{
-				in->left  = ln;
-				in->right = fn;
-			}
-
-			rt6_stats.fib_nodes += 2;
-			rt6_stats.fib_route_nodes++;
-			rt6_stats.fib_rt_entries++;
-
-			return 0;
-		}
-
-		/* 
-		 *		(new leaf node)
-		 *	          /	   \
-		 *	     (old node)    NULL
-		 */
-
-		ln = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
-		if (ln == NULL)
-			return (-ENOMEM);
-
-		memset(ln, 0, sizeof(struct fib6_node));
-		ln->fn_bit   = pbit;
-		ln->fn_flags = RTN_BACKTRACK;
-			
-
-		ln->parent = pn;
-		ln->leaf = rt;
-		ln->fn_sernum = sernum;
-		atomic_inc(&rt->rt_ref);
-
-		rt->fib_node = ln;
-
-		if (dir)
-			pn->right = ln;
-		else
-			pn->left  = ln;
-		
-
-		if (addr_bit_set(&fn->leaf->rt_dst, pbit))
-			ln->right = fn;
-		else
-			ln->left  = fn; 
-
-		fn->parent = ln;
-
-		rt6_stats.fib_nodes++;
-		rt6_stats.fib_route_nodes++;
-		rt6_stats.fib_rt_entries++;
-
-		return(0);
-	}
-
-	return (-1);
-}
-
-static struct rt6_info * fib6_lookup_1(struct in6_addr *addr, int flags)
-{
-	struct fib6_node *fn, *next;
-	int dir;
-
-	fn = &routing_table;
-
-	for (;;)
-	{
-		dir = addr_bit_set(addr, fn->fn_bit);
-
-		next = dir ? fn->right: fn->left;
-
-		if (next)
-		{
-			fn = next;
-			continue;
-		}
-
-		break;
-	}
-
-
-	while ((fn->fn_flags & RTN_ROOT) == 0)
-	{
-		if (fn->fn_flags & RTN_BACKTRACK)
-		{
-			if (addr_match(&fn->leaf->rt_dst, addr, 
-				       fn->leaf->rt_prefixlen))
-			{
-				struct rt6_info *rt;
-				
-				for (rt = fn->leaf; rt; rt = rt->next)
-				{
-					if ((rt->rt_flags & flags) == 0)
-						return rt;
-				}
-			}
-		}
-		
-		fn = fn->parent;
-	}
-
-	return NULL;
-}
-
-
-
-/*
- *	called to trim the tree of intermediate nodes when possible
- */
-
-static void fib6_del_3(struct fib6_node *fn)
-{
-	int children = 0;
-	int dir = 0;
-	int bit;
-
-	/*
-	 *	0 or one children:
-	 *		delete the node
-	 *
-	 *	2 children:
-	 *		move the bit down
-	 */
-
-	if (fn->left)
-	{
-		children++;
-		dir = 0;
-	}
-
-	if (fn->right)
-	{
-		children++;
-		dir = 1;
-	}
-
-	if (children < 2)
-	{
-		struct fib6_node *child;
-		struct fib6_node *pn;
-
-		child = dir ? fn->right : fn->left;
-
-		if (fn->parent->left == fn)
-		{
-			fn->parent->left = child;
-		}
-		else
-		{
-			fn->parent->right = child;
-		}
-
-		if (child)
-		{
-			child->parent = fn->parent;
-		}
-
-		/*
-		 *	try to collapse on top
-		 */
-		pn = fn->parent;
-		fn->parent = NULL;
-		
-		if ((pn->fn_flags & (RTN_BACKTRACK | RTN_ROOT)) == 0)
-		{
-			if (pn->leaf)
-			{
-				rt_release(pn->leaf);
-				pn->leaf = NULL;
-			}
-			fib6_del_3(pn);
-		}
-
-		if (fn->fn_flags & RTN_BACKTRACK)
-		{
-			rt6_stats.fib_route_nodes--;
-		}
-		rt6_stats.fib_nodes--;
-		kfree(fn);
-		return;
-	}
-
-	bit = addr_diff(&fn->left->leaf->rt_dst, &fn->right->leaf->rt_dst);
-	
-	fn->fn_bit = bit;
-	fn->fn_flags &= ~RTN_BACKTRACK;
-	
-	fn->leaf = fn->left->leaf;
-	atomic_inc(&fn->leaf->rt_ref);
-
-	rt6_stats.fib_route_nodes--;
-}
-
-static struct fib6_node * fib6_del_2(struct in6_addr *addr, __u32 prefixlen, 
-				     struct in6_addr *gw, struct device *dev)
-{
-	struct fib6_node *fn;
-
-	for (fn = &routing_table; fn;) 
-	{
-		int dir;
-
-		if ((fn->fn_flags & RTN_BACKTRACK) &&
-		    prefixlen == fn->leaf->rt_prefixlen &&
-		    addr_match(&fn->leaf->rt_dst, addr, fn->leaf->rt_prefixlen)
-		    )
-		{
-			break;
-		}
-
-		dir = addr_bit_set(addr, fn->fn_bit);
-
-		fn = dir ? fn->right: fn->left;
-	}
-
-	/* 
-	 *	if route tree node found
-	 *	search among it's entries
-	 */
-
-	if (fn)
-	{
-		struct rt6_info *back = NULL;
-		struct rt6_info *lf;
-
-		for(lf = fn->leaf; lf; lf=lf->next)
-		{
-			if ((gw && (ipv6_addr_cmp(addr, &lf->rt_dst) == 0)) ||
-			    (dev && dev == lf->rt_dev))
-			{
-				/* delete this entry */
-				if (back == NULL)
-					fn->leaf = lf->next;
-				else
-					back->next = lf->next;
-
-				lf->fib_node = NULL;
-				rt_release(lf);
-				return fn;		
-			}
-			back = lf;
-		}
-	}
-
-	return NULL;
-}
-
-static struct fib6_node * fib6_del_rt_2(struct rt6_info *rt)
-{
-	struct fib6_node *fn;
-	struct in6_addr *addr = &rt->rt_dst;
-	int prefixlen = rt->rt_prefixlen;
-	
-	for (fn = &routing_table; fn;) 
-	{
-		int dir;
-
-		if ((fn->fn_flags & RTN_BACKTRACK) &&
-		    prefixlen == fn->leaf->rt_prefixlen &&
-		    addr_match(&fn->leaf->rt_dst, addr, fn->leaf->rt_prefixlen)
-		    )
-		{
-			break;
-		}
-
-		dir = addr_bit_set(addr, fn->fn_bit);
-
-		fn = dir ? fn->right: fn->left;
-	}
-
-	/* 
-	 *	if route tree node found
-	 *	search among its entries
-	 */
-
-	if (fn)
-	{
-		struct rt6_info **back;
-		struct rt6_info *lf;
-
-		back = &fn->leaf;
-		
-		for(lf = fn->leaf; lf; lf=lf->next)
-		{
-			if (rt == lf)
-			{
-				/*
-				 *	delete this entry
-				 */
-				
-				*back = lf->next;
-				rt_release(lf);
-				return fn;
-			}
-			back = &lf->next;
-		}
-	}
-
-	return NULL;
-}
-
-int fib6_del_1(struct in6_addr *addr, __u32 prefixlen, struct in6_addr *gw, 
-	       struct device *dev)
-{
-	struct fib6_node *fn;
-
-	fn = fib6_del_2(addr, prefixlen, gw, dev);
-
-	if (fn == NULL)
-		return -ENOENT;
-	
-	if (fn->leaf == NULL)
-	{
-		fib6_del_3(fn);
-	}
-
-	return 0;
-}
-
-int fib6_del_rt(struct rt6_info *rt)
-{
-	struct fib6_node *fn;
-
-	fn = fib6_del_rt_2(rt);
-
-	if (fn == NULL)
-		return -ENOENT;
-	
-	if (fn->leaf == NULL)
-	{
-		fib6_del_3(fn);
-	}
-
-	return 0;
-}
-
-static void fib6_flush_1(struct fib6_node *fn, void *p_arg)
-{
-	struct rt6_info *rt;
-
-	for (rt = fn->leaf; rt;)
-	{
-		struct rt6_info *itr;
-
-		itr = rt;
-		rt = rt->next;
-		itr->fib_node = NULL;
-		rt_release(itr);
-	}
-	
-	if (fn->fn_flags & RTN_BACKTRACK)
-	{
-		rt6_stats.fib_route_nodes--;
-	}
-	rt6_stats.fib_nodes--;
-	kfree(fn);
-}
-
-void fib6_flush(void)
-{
-	rt6_walk_tree(fib6_flush_1, NULL, RT6_FILTER_NONE);
-}
-
-int ipv6_route_add(struct in6_rtmsg *rtmsg)
-{
-	struct rt6_info *rt;
-	struct device * dev = NULL;
-	struct inet6_dev *idev;
-	struct rt6_req *request;
-	int flags = rtmsg->rtmsg_flags;
-
-	idev = ipv6_dev_by_index(rtmsg->rtmsg_ifindex);
-	if (idev)
-	{
-		dev = idev->dev; 
-	}
-	
-	rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
-					 GFP_ATOMIC);
-
-	rt6_stats.fib_rt_alloc++;
-
-	memset(rt, 0, sizeof(struct rt6_info));
-		
-	memcpy(&rt->rt_dst, &rtmsg->rtmsg_dst, sizeof(struct in6_addr));
-	rt->rt_prefixlen = rtmsg->rtmsg_prefixlen;
-
-	if (rt->rt_prefixlen == 0)
-	{
-		printk(KERN_DEBUG "ip6_fib: zero length route not allowed\n");
-		return -EINVAL;
-	}
-	
-	if (flags & (RTF_GATEWAY | RTF_NEXTHOP)) 
-	{
-		/* check to see if its an acceptable gateway */
-		if (flags & RTF_GATEWAY)
-		{
-			struct rt6_info *gw_rt;
-
-			gw_rt = fibv6_lookup(&rtmsg->rtmsg_gateway, dev,
-					     RTI_GATEWAY);
-
-			if (gw_rt == NULL)
-			{
-				return -EHOSTUNREACH;
-			}
-
-			dev = gw_rt->rt_dev;
-		}
-
-		rt->rt_nexthop = ndisc_get_neigh(dev, &rtmsg->rtmsg_gateway);
-
-		if (rt->rt_nexthop == NULL)
-		{
-			printk(KERN_DEBUG "ipv6_route_add: no nexthop\n");
-			kfree(rt);
-			return -EINVAL;
-		}
-
-		rt->rt_dev = dev;
-
-		if (loopback_rt == NULL && (dev->flags & IFF_LOOPBACK))
-		{
-			loopback_rt = rt;
-		}
-
-	}
-	else
-	{
-		if (dev == NULL)
-		{
-			printk(KERN_DEBUG "ipv6_route_add: NULL dev\n");
-			kfree(rt);
-			return -EINVAL;
-		}
-
-		rt->rt_dev = dev;
-		rt->rt_nexthop = NULL;
-	}
-	
-	rt->rt_metric = rtmsg->rtmsg_metric;
-	rt->rt_flags = rtmsg->rtmsg_flags;
-
-	if (rt->rt_flags & RTF_ADDRCONF)
-	{
-		rt->rt_expires = rtmsg->rtmsg_info;
-	}
-
-	request = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
-	if (request == NULL)
-	{
-		printk(KERN_WARNING "ipv6_route_add: kmalloc failed\n");
-		return -ENOMEM;
-	}
-
-	request->operation = RT_OPER_ADD;
-	request->ptr = rt;
-	request->next = request->prev = NULL;
-	rtreq_queue(request);
-	rt6_bh_mask |= RT_BH_REQUEST;
-
-	rt6_run_bh();
-
-	return 0;
-}
-
-int ipv6_route_del(struct in6_rtmsg *rtmsg)
-{
-	struct rt6_info * rt;
-	int res = -ENOENT;
-	
-	atomic_inc(&rt6_lock);
-	
-	rt = fib6_lookup_1(&rtmsg->rtmsg_dst, 0);
-	
-	if (rt && (rt->rt_prefixlen == rtmsg->rtmsg_prefixlen))
-	{
-		int test;
-		
-		start_bh_atomic();
-		
-		test = (rt6_lock == 1);
-		
-		if (test)
-		{
-			res = fib6_del_rt(rt);
-		}
-		end_bh_atomic();
-
-		if (!test)
-		{
-			struct rt6_req *request;
-			
-			request = kmalloc(sizeof(struct rt6_req), GFP_KERNEL);
-			
-			if (!request)
-			{
-				res = -ENOMEM;
-				goto out;
-			}
-			request->operation = RT_OPER_DEL;
-			request->ptr = rt;
-			request->next = request->prev = NULL;
-			rtreq_queue(request);
-			rt6_bh_mask |= RT_BH_REQUEST;
-			res = 0;
-		}
-	}
-  out:
-	atomic_dec(&rt6_lock);
-	rt6_run_bh();
-	return res;
-}
-
-/*
- *	search the routing table
- *	the flags parameter restricts the search to entries where
- *	the flag is *not* set
- */
-struct rt6_info * fibv6_lookup(struct in6_addr *addr, struct device *src_dev,
-			       int flags)
-{
-	struct rt6_info *rt;
-
-	atomic_inc(&rt6_lock);
-	
-	if ((rt = fib6_lookup_1(addr, flags)))
-	{
-		if (src_dev)
-		{
-			struct rt6_info *sprt;
-
-			for (sprt=rt; sprt; sprt=sprt->next)
-			{
-				if (sprt->rt_dev == src_dev)
-				{
-					rt = sprt;
-					goto out;
-				}
-			}
-			
-			if (flags & RTI_DEVRT)
-			{
-				rt = NULL;
-			}
-		}
-
-		goto out;
-	}
-
-	if (!(flags & RTI_GATEWAY))
-	{
-		if ((rt = dflt_rt_lookup()))
-		{
-			goto out;
-		}
-
-		rt = last_resort_rt;
-	}
-  out:
-	atomic_dec(&rt6_lock);
-	return rt;
-}
-
-/*
- *	Destination Cache
- */
-
-struct dest_entry * ipv6_dst_route(struct in6_addr * daddr,
-				   struct device *src_dev,
-				   int flags)
-{
-	struct dest_entry * dc = NULL;
-	struct rt6_info * rt;
-
-	atomic_inc(&rt6_lock);
-	
-	rt = fibv6_lookup(daddr, src_dev, flags);
-
-	if (rt == NULL)
-	{
-		goto exit;
-	}
-	
-	if (rt->rt_nexthop)
-	{
-		/*
-		 *	We can use the generic route
-		 *	(warning: the pmtu value maybe invalid)
-		 */
-
-		dc = (struct dest_entry *) rt;
-		atomic_inc(&rt->rt_use);
-	}
-	else
-	{
-		struct rt6_req *request;
-
-		if (ipv6_chk_addr(daddr) && !(rt->rt_dev->flags & IFF_LOOPBACK))
-		{
-			rt = loopback_rt;
-
-			if (rt == NULL)
-			{
-				goto exit;
-			}
-		}
-
-		/*
-		 *	dynamicly allocate a new route
-		 */
-		
-		dc = (struct dest_entry *) kmalloc(sizeof(struct dest_entry), 
-					   GFP_ATOMIC);
-
-		if (dc == NULL)
-		{
-			printk(KERN_WARNING "dst_route: kmalloc failed\n");
-			goto exit;
-		}
-
-		rt6_stats.fib_rt_alloc++;
-		rt6_stats.fib_dc_alloc++;
-
-		memset(dc, 0, sizeof(struct dest_entry));
-
-		memcpy(&dc->dc_addr, daddr, sizeof(struct in6_addr));
-		dc->rt.rt_prefixlen = 128;
-		dc->dc_usecnt = 1;
-		dc->rt.rt_metric = rt->rt_metric;
-
-		dc->dc_flags = (rt->rt_flags | RTF_HOST | RTI_DYNAMIC |
-				RTI_DCACHE | DCF_PMTU);
-
-		dc->dc_pmtu = rt->rt_dev->mtu;
-		dc->rt.rt_dev = rt->rt_dev;
-		dc->rt.rt_output_method = rt->rt_output_method;
-		dc->dc_tstamp = jiffies;
-		/* add it to the request queue */
-		
-		request = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
-
-		if (request == NULL)
-		{
-			printk(KERN_WARNING "dst_route: kmalloc failed\n");
-			dc = NULL;
-			goto exit;
-		}
-
-		dc->dc_nexthop = ndisc_get_neigh(rt->rt_dev, daddr);
-
-		rt6_bh_mask |= RT_BH_REQUEST;
-		
-		request->operation = RT_OPER_ADD;
-		request->ptr = (struct rt6_info *) dc;
-		request->next = request->prev = NULL;
-		rtreq_queue(request);
-	}
-
-	atomic_inc(&rt_clients);
-
-  exit:
-       
-	atomic_dec(&rt6_lock);
-	rt6_run_bh();
-
-	return dc;
-}
-
-/*
- *	check cache entry for vality...
- *	this needs to be done as a inline func that calls
- *	ipv6_slow_dst_check if entry is invalid
- */
-
-struct dest_entry * ipv6_dst_check(struct dest_entry *dc,
-				   struct in6_addr *daddr,
-				   __u32 sernum, int flags)
-{
-	int uptodate = 0;
-
-	/*
-	 *	destination cache becomes invalid when routing
-	 *	changes or a more specific dynamic entry is
-	 *	created.
-	 *	if route is removed from table fib_node will
-	 *	become NULL
-	 */
-
-	if (dc->rt.fib_node && (dc->rt.fib_node->fn_sernum == sernum))
-		uptodate = 1;
-
-	if (uptodate && ((dc->dc_flags & DCF_INVALID) == 0))
-	{
-		if (dc->dc_nexthop && !(dc->dc_nexthop->flags & NCF_NOARP))
-		{
-			ndisc_event_send(dc->dc_nexthop, NULL);
-		}
-		return dc;
-	}
-
-	/* route for destination may have changed */
-
-	ipv6_dst_unlock(dc);
-
-	return ipv6_dst_route(daddr, NULL, flags);
-}
-
-void ipv6_dst_unlock(struct dest_entry *dc)
-{
-	/*
-	 *	decrement counter and mark entry for deletion
-	 *	if counter reaches 0. we delay deletions in hope
-	 *	we can reuse cache entries.
-	 */
-
-	atomic_dec(&dc->dc_usecnt);
-	
-	if (dc->dc_usecnt == 0)
-	{
-
-		if (dc->dc_flags & RTI_DCACHE)
-		{
-			/*
-			 *	update last usage tstamp
-			 */
-
-			dc->dc_tstamp = jiffies;
-			rt6_bh_mask |= RT_BH_GC;
-		}
-
-		if (dc->rt.rt_ref == 0)
-		{
-			/*
-			 *	entry out of the routing table
-			 *	pending to be released on last deref
-			 */
-
-			if (dc->dc_nexthop)
-			{
-				neighbour_unlock(dc->dc_nexthop);
-			}
-			
-			if (dc->dc_flags & RTI_DCACHE)
-			{
-				rt6_stats.fib_dc_alloc--;
-			}
-
-			rt6_stats.fib_rt_alloc--;
-			kfree(dc);
-		}
-
-	}
-
-	atomic_dec(&rt_clients);
-}
-
-/*
- *	Received a packet too big icmp that lowers the mtu for this
- *	address. If the route for the destination is genric we create
- *	a new route with the apropriate MTU info. The route_add
- *	procedure will update the serial number on the generic routes
- *	belonging to the afected tree forcing clients to request a route
- *	lookup.
- */
-void rt6_handle_pmtu(struct in6_addr *addr, int pmtu)
-{
-	struct rt6_info *rt;
-	struct rt6_req *req;
-	struct dest_entry *dc;
-
-	printk(KERN_DEBUG "rt6_handle_pmtu\n");
-
-	if (pmtu < 0 || pmtu > 65536)
-	{
-		printk(KERN_DEBUG "invalid MTU value\n");
-		return;
-	}
-
-	rt = fibv6_lookup(addr, NULL, 0);
-
-	if (rt == NULL)
-	{
-		printk(KERN_DEBUG "rt6_handle_pmtu: route not found\n");
-		return;
-	}
-
-	if (rt->rt_flags & RTI_DCACHE)
-	{
-		/*
-		 *	we do have a destination cache entry for this
-		 *	address.
-		 */
-		
-		dc = (struct dest_entry *) rt;
-		
-		/*
-		 *	fixme: some sanity checks are likely to be needed
-		 *	 here
-		 */
-
-		dc->dc_pmtu = pmtu;
-		dc->dc_flags |= DCF_PMTU;
-		return;
-	}
-
-	req = (struct rt6_req *) kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
-
-	/* now add the new destination cache entry	*/
-	
-	dc = (struct dest_entry *) kmalloc(sizeof(struct dest_entry),
-					   GFP_ATOMIC);
-	
-	rt6_stats.fib_rt_alloc++;
-	rt6_stats.fib_dc_alloc++;
-
-	memset(dc, 0, sizeof(struct dest_entry));
-	
-	memcpy(&dc->dc_addr, addr, sizeof(struct in6_addr));
-	dc->rt.rt_prefixlen = 128;
-	dc->rt.rt_metric = rt->rt_metric;
-
-	dc->dc_flags = (rt->rt_flags | RTI_DYNAMIC | RTI_DCACHE | DCF_PMTU |
-			RTF_HOST);
-
-	dc->dc_pmtu = pmtu;
-	dc->dc_tstamp = jiffies;
-	
-	dc->dc_nexthop = rt->rt_nexthop;
-	atomic_inc(&dc->dc_nexthop->refcnt);
-
-	dc->rt.rt_dev = rt->rt_dev;
-	dc->rt.rt_output_method = rt->rt_output_method;
-
-	req->operation = RT_OPER_ADD;
-	req->ptr = (struct rt6_info *) dc;
-	req->next = req->prev = NULL;
-
-	rtreq_queue(req);
-
-	rt6_bh_mask |= RT_BH_REQUEST;
-
-	rt6_run_bh();
-}
-
-/*
- *	Redirect received: target is nexthop for dest
- */
-struct rt6_info * ipv6_rt_redirect(struct device *dev, struct in6_addr *dest,
-				   struct in6_addr *target, int on_link)
-				       
-{
-	struct rt6_info *rt;
-	struct rt6_req *req;
-	int metric;
-
-	rt = fibv6_lookup(dest, dev, 0);
-
-	if (rt == NULL)
-	{
-		printk(KERN_WARNING "rt_redirect: unable to locate route\n");
-		return NULL;
-	}
-
-	metric = rt->rt_metric;
-
-	if ((rt->rt_flags & RTF_HOST) == 0)
-	{
-		/* Need to create an host route for this address */
-		
-		rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
-						 GFP_ATOMIC);
-		memset(rt, 0, sizeof(struct rt6_info));
-		ipv6_addr_copy(&rt->rt_dst, dest);
-		rt->rt_prefixlen = 128;
-		rt->rt_flags = RTF_HOST | RTF_UP;
-		rt->rt_dev = dev;
-
-		/*
-		 *	clone rt->rt_output_method ?
-		 */
-
-		rt->rt_metric = metric;
-
-		rt6_stats.fib_rt_alloc++;
-
-		req = (struct rt6_req *) kmalloc(sizeof(struct rt6_req),
-						 GFP_ATOMIC);
-		req->operation = RT_OPER_ADD;
-		req->ptr  = rt;
-		req->next = req->prev = NULL;
-		
-		rtreq_queue(req);
-		rt6_bh_mask |= RT_BH_REQUEST;
-	}
-	else
-	{
-		rt->rt_flags |= RTF_MODIFIED;
-	}
-
-	rt->rt_flags |= RTF_DYNAMIC;
- 
-	if (on_link)
-	{
-		rt->rt_flags &= ~RTF_GATEWAY;
-	}
-	else
-	{
-		rt->rt_flags |= RTF_GATEWAY;
-	}
-
-	if (rt->rt_nexthop)
-	{
-		struct nd_neigh *ndn = (struct nd_neigh *) rt->rt_nexthop;
-		
-		if (ipv6_addr_cmp(&ndn->ndn_addr, target) == 0)
-		{
-			rt->rt_nexthop = neighbour_clone(rt->rt_nexthop);
-			goto exit;
-		}
-		else
-		{
-			neighbour_unlock(rt->rt_nexthop);
-		}
-	}
-	
-	rt->rt_nexthop = ndisc_get_neigh(dev, target);
-
-  exit:
-	rt6_run_bh();
-	return rt;
-}
-
-static int dcache_gc_node(struct fib6_node *fn, int timeout)
-{
-	struct rt6_info *rt, *back;
-	int more = 0;
-	unsigned long now = jiffies;
-
-	back = NULL;
-
-	for (rt = fn->leaf; rt;)
-	{
-		if ((rt->rt_flags & RTI_DCACHE) && rt->rt_use == 0)
-		{
-			struct dest_entry *dc;
-			
-			dc = (struct dest_entry *) rt;
-			
-			if (now - dc->dc_tstamp > timeout)
-			{
-				struct rt6_info *old;
-
-				old = rt;
-
-				rt = rt->next;
-
-				if (back == NULL)
-				{
-					fn->leaf = rt;
-				}
-				else
-				{
-					back->next = rt;
-				}
-
-				old->fib_node = NULL;
-				rt_release(old);
-				rt6_stats.fib_rt_entries--;
-				continue;
-			}
-			else
-			{
-				more++;
-			}
-		}
-
-		back = rt;
-		rt = rt->next;
-	}
-
-	if (fn->leaf == NULL)
-	{
-		return -1;
-	}
-	return more;
-}
-
-struct dc_gc_args {
-	unsigned long	timeout;
-	int		more;
-};
-
-static void dc_garbage_collect(struct fib6_node *fn, void *p_arg)
-{
-	struct dc_gc_args * args = (struct dc_gc_args *) p_arg;
-	
-	if (fn->fn_flags & RTN_BACKTRACK)
-	{
-		if (fn->fn_bit == 127)
-		{
-			int more;
-						
-			more = dcache_gc_node(fn, args->timeout);
-
-			if (more == -1)
-			{
-				if (fn->parent->left == fn)
-					fn->parent->left = NULL;
-				else
-					fn->parent->right = NULL;
-				
-				kfree(fn);
-
-				rt6_stats.fib_nodes--;
-				rt6_stats.fib_route_nodes--;
-				
-				return;
-			}
-			args->more += more;
-		}
-	}
-	else if (!(fn->fn_flags & RTN_ROOT))
-	{
-		int children = 0;
-		struct fib6_node *chld = NULL;
-
-		if (fn->left)
-		{
-			children++;
-			chld = fn->left;			
-		}
-			
-		if (fn->right)
-		{
-			children++;
-			chld = fn->right;
-		}
-		
-		if (children <= 1)
-		{			
-			struct fib6_node *pn = fn->parent;
-			
-			if (pn->left == fn)
-			{
-				pn->left = chld;
-			}
-			else
-			{
-				pn->right = chld;
-			}
-			
-			if (chld)
-			{
-				chld->parent = pn;
-			}
-			
-			rt_release(fn->leaf);
-			
-			rt6_stats.fib_nodes--;
-			kfree(fn);
-		}	       
-	}
-}
-
-/*
- *	called with ints off
- */
-
-static void __rt6_run_bh(void)
-{
-	static last_gc_run = 0;
-
-	if (rt6_bh_mask & RT_BH_REQUEST)
-	{
-		struct rt6_req *request;
-
-		while ((request = rtreq_dequeue()))
-		{
-			struct rt6_info *rt;
-
-			rt = request->ptr;
-
-			switch (request->operation) {
-			case RT_OPER_ADD:
-				fib6_add_1(rt);
-				break;
-
-			case RT_OPER_DEL:
-				fib6_del_rt(rt);
-				break;
-
-			default:
-				printk(KERN_WARNING
-				       "rt6_run_bh: bad request in queue\n");
-			}
-
-			kfree(request);
-		}
-
-		rt6_bh_mask &= ~RT_BH_REQUEST;
-	}
-
-	if (rt6_bh_mask & RT_BH_GC)
-	{
-		if (jiffies - last_gc_run > DC_TIME_RUN)
-		{
-			struct dc_gc_args args;
-
-			if (rt6_stats.fib_dc_alloc >= DC_WATER_MARK)
-				args.timeout = DC_SHORT_TIMEOUT;
-			else
-				args.timeout = DC_LONG_TIMEOUT;
-
-			args.more = 0;
-			rt6_walk_tree(dc_garbage_collect, &args, RT6_FILTER_NONE);
-
-			last_gc_run = jiffies;
-			
-			if (!args.more)
-			{
-				rt6_bh_mask &= ~RT_BH_GC;
-			}
-		}
-	}
-}
-
-/*
- *	Timer for expiring routes learned via addrconf and stale DC 
- *	entries when there is no network actuvity
- */
-
-void rt6_timer_handler(unsigned long data)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-
-	if (rt6_lock == 0)
-	{
-		if (rt_clients == 0 && rt6_bh_mask)
-		{
-			__rt6_run_bh();
-		}
-
-		/*
-		 *	route expiry
-		 */
-		
-		rt6_walk_tree(rt6_rt_timeout, NULL, RT6_FILTER_RTNODES);
-	}
-
-	restore_flags(flags);
-
-	rt6_gc_timer.expires = jiffies + 4 * DC_LONG_TIMEOUT;
-	add_timer(&rt6_gc_timer);
-}
-
-/*
- *	Check if routes should be timed out.
- *	Called from rt6_walk_tree for every node.
- */
-
-static void rt6_rt_timeout(struct fib6_node *fn, void *arg)
-{
-	struct rt6_info *rt;
-	unsigned long now = jiffies;
-
-	for (rt = fn->leaf; rt; rt = rt->next)
-	{
-		if ((rt->rt_flags & RTF_ADDRCONF) && now > rt->rt_expires)
-		{
-			struct rt6_req *req;
-
-			/*
-			 *	request route deletion. routes will only
-			 *	be deleted after walk_tree completes
-			 */
-
-			req = (struct rt6_req *) kmalloc(sizeof(struct rt6_req),
-							 GFP_ATOMIC);
-			req->operation = RT_OPER_DEL;
-			req->ptr  = rt;
-			req->next = req->prev = NULL;
-		}
-	}
-}
-
-static void rt6_sndrtmsg(struct in6_rtmsg *rtmsg)
-{
-	struct sk_buff *skb;
-	
-	skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
-	if (skb == NULL)
-		return;
-
-	memcpy(skb_put(skb, sizeof(struct in6_rtmsg)), &rtmsg,
-	       sizeof(struct in6_rtmsg));
-	
-	if (netlink_post(NETLINK_ROUTE6, skb))
-	{
-		kfree_skb(skb, FREE_WRITE);
-	}
-}
-
-int ipv6_route_ioctl(unsigned int cmd, void *arg)
-{
-	struct in6_rtmsg rtmsg;
-	int err;
-
-	switch(cmd) 
-	{
-		case SIOCADDRT:		/* Add a route */
-		case SIOCDELRT:		/* Delete a route */
-			if (!suser())
-				return -EPERM;
-			err = copy_from_user(&rtmsg, arg,
-					     sizeof(struct in6_rtmsg));
-			if (err)
-				return -EFAULT;
-			
-			err = (cmd == SIOCDELRT) ? ipv6_route_del(&rtmsg) :
-			      ipv6_route_add(&rtmsg);
-
-			if (err == 0)
-			{
-				rt6_sndrtmsg(&rtmsg);
-			}
-			return err;
-	}
-
-	return -EINVAL;
-}
-
-static void rt6_ifdown_scan(struct fib6_node *fn, void *arg)
-{
-	struct rt6_info *rt;
-	struct device *dev = (struct device *) arg;
-
-	for (rt = fn->leaf; rt; rt=rt->next)
-	{
-		if (((rt->rt_flags & RTI_DCACHE) == 0) && rt->rt_dev == dev)
-		{
-			struct rt6_req *req;
-
-			req = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
-			req->operation = RT_OPER_DEL;
-			req->ptr  = rt;
-			req->next = req->prev = NULL;
-			rt6_bh_mask |= RT_BH_REQUEST;
-		}
-	}
-}
-
-void rt6_ifdown(struct device *dev)
-{
-	rt6_walk_tree(rt6_ifdown_scan, (void *) dev, RT6_FILTER_RTNODES);
-}
-
-static void rt6_walk_tree(f_pnode func, void * arg, int filter)
-{
-	struct fib6_node *fn;
-	/*
-	 *	adquire lock
-	 *	this warranties that the operation will be atomic with
-	 *	respect to the garbage collect routine that also does
-	 *	a tree transversal and tags nodes with the RTN_TAG flag
-	 */
-	atomic_inc(&rt6_lock);
-
-	fn = &routing_table;
-
-	do {
-		if (!(fn->fn_flags & RTN_TAG))
-		{
-			fn->fn_flags |= RTN_TAG;
-
-			if (fn->left)
-			{
-				fn = fn->left;
-				continue;
-			}
-		}
-
-		fn->fn_flags &= ~RTN_TAG;
-
-		if (fn->right)
-		{
-			fn = fn->right;
-			continue;
-		}
-	       		
-		do {
-			struct fib6_node *node;
-			
-			if (fn->fn_flags & RTN_ROOT)
-				break;
-			node = fn;
-			fn = fn->parent;
-			
-			if (!(node->fn_flags & RTN_TAG) && 
-			    (!filter || (node->fn_flags & RTN_BACKTRACK)))
-			{
-				(*func)(node, arg);
-			}
-
-		} while (!(fn->fn_flags & RTN_TAG));
-		
-	} while (!(fn->fn_flags & RTN_ROOT) || (fn->fn_flags & RTN_TAG));
-
-	atomic_dec(&rt6_lock);
-}
-
-#ifdef CONFIG_PROC_FS
-#define RT6_INFO_LEN (32 + 2 + 32 + 2 + 2 + 2 + 4 + 8 + 7 + 1)
-
-struct rt6_proc_arg {
-	char *buffer;
-	int offset;
-	int length;
-	int skip;
-	int len;
-};
-
-static void rt6_info_node(struct fib6_node *fn, void *p_arg)
-{
-	struct rt6_info *rt;
-	struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
-
-	for (rt = fn->leaf; rt; rt = rt->next)
-	{
-		int i;
-
-		if (arg->skip < arg->offset / RT6_INFO_LEN)
-		{
-			arg->skip++;
-			continue;
-		}
-
-		if (arg->len >= arg->length)
-			return;
-		
-		for (i=0; i<16; i++)
-		{
-			sprintf(arg->buffer + arg->len, "%02x",
-				rt->rt_dst.s6_addr[i]);
-			arg->len += 2;
-		}
-		arg->len += sprintf(arg->buffer + arg->len, " %02x ",
-				    rt->rt_prefixlen);
-		if (rt->rt_nexthop)
-		{
-			for (i=0; i<16; i++)
-			{
-				struct nd_neigh *ndn;
-
-				ndn = (struct nd_neigh *) rt->rt_nexthop;
-				sprintf(arg->buffer + arg->len, "%02x",
-					ndn->ndn_addr.s6_addr[i]);
-				arg->len += 2;
-			}
-		}
-		else
-		{
-			sprintf(arg->buffer + arg->len,
-				"00000000000000000000000000000000");
-			arg->len += 32;
-		}
-		arg->len += sprintf(arg->buffer + arg->len,
-				    " %02x %02x %02x %04x %8s\n",
-				    rt->rt_metric, rt->rt_use,
-				    rt->rt_ref, rt->rt_flags, 
-				    rt->rt_dev ? rt->rt_dev->name : "");
-	}
-}
-
-static int rt6_proc_info(char *buffer, char **start, off_t offset, int length,
-			 int dummy)
-{
-	struct rt6_proc_arg arg;
-	struct fib6_node sfn;
-	arg.buffer = buffer;
-	arg.offset = offset;
-	arg.length = length;
-	arg.skip = 0;
-	arg.len = 0;
-
-	rt6_walk_tree(rt6_info_node, &arg, RT6_FILTER_RTNODES);
-	
-	sfn.leaf = default_rt_list;
-	rt6_info_node(&sfn, &arg);
-
-	sfn.leaf = last_resort_rt;
-	rt6_info_node(&sfn, &arg);
-			     
-	*start = buffer;
-
-	if (offset)
-		*start += offset % RT6_INFO_LEN;
-
-	arg.len -= offset % RT6_INFO_LEN;
-
-	if (arg.len > length)
-		arg.len = length;
-
-	return arg.len;
-}
-
-
-static int rt6_proc_stats(char *buffer, char **start, off_t offset, int length,
-			  int dummy)
-{
-	int len;
-
-	len = sprintf(buffer, "%04x %04x %04x %04x %04x\n",
-		      rt6_stats.fib_nodes, rt6_stats.fib_route_nodes,
-		      rt6_stats.fib_rt_alloc, rt6_stats.fib_rt_entries,
-		      rt6_stats.fib_dc_alloc);
-
-	len -= offset;
-
-	if (len > length)
-		len = length;
-
-	*start = buffer + offset;
-
-	return len;
-}
-
-#endif			/* CONFIG_PROC_FS */
-
-/*
- *	init/cleanup code
- *
- */
-
-void ipv6_route_init(void)
-{
-#ifdef 	CONFIG_PROC_FS
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_RT6, 10, "ipv6_route",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		rt6_proc_info
-	});
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_RT6_STATS, 9, "rt6_stats",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		rt6_proc_stats
-	});
-
-#endif
-	rt6_gc_timer.expires = jiffies + 4 * DC_LONG_TIMEOUT;
-	add_timer(&rt6_gc_timer);
-	netlink_attach(NETLINK_ROUTE6, rt6_msgrcv);
-}
-
-#ifdef MODULE
-void ipv6_route_cleanup(void)
-{
-	proc_net_unregister(PROC_NET_RT6);
-	proc_net_unregister(PROC_NET_RT6_STATS);
-	netlink_detach(NETLINK_ROUTE6);
-	del_timer(&rt6_gc_timer);
-	fib6_flush();
-}
-#endif
-
-/*
- *	NETLINK interface
- *	routing socket moral equivalent
- */
-
-static int rt6_msgrcv(int unit, struct sk_buff *skb)
-{
-	int count = 0;
-	struct in6_rtmsg *rtmsg;
-	
-	while (skb->len)
-	{
-		if (skb->len < sizeof(struct in6_rtmsg))
-		{
-			count = -EINVAL;
-			goto out;
-		}
-		
-		rtmsg = (struct in6_rtmsg *) skb->data;
-		skb_pull(skb, sizeof(struct in6_rtmsg));
-		count += sizeof(struct in6_rtmsg);
-
-		switch (rtmsg->rtmsg_type) {
-		case RTMSG_NEWROUTE:
-			ipv6_route_add(rtmsg);
-			break;
-		case RTMSG_DELROUTE:
-			ipv6_route_del(rtmsg);
-			break;
-		default:
-			count = -EINVAL;
-			goto out;
-		}
-	}
-
-  out:
-	kfree_skb(skb, FREE_READ);	
-	return count;
-}
-
-void rt6_sndmsg(__u32 type, struct in6_addr *dst, struct in6_addr *gw,
-		__u16 plen, struct device *dev, __u16 metric, __u16 flags)
-{
-	struct sk_buff *skb;
-	struct in6_rtmsg *msg;
-	int ifindex = 0;
-	
-	skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
-	if (skb == NULL)
-		return;
-
-
-	msg = (struct in6_rtmsg *) skb_put(skb, sizeof(struct in6_rtmsg));
-	
-	msg->rtmsg_type = type;
-
-	if (dst)
-	{
-		ipv6_addr_copy(&msg->rtmsg_dst, dst);
-	}
-	else
-		memset(&msg->rtmsg_dst, 0, sizeof(struct in6_addr));
-
-	if (gw)
-	{
-		ipv6_addr_copy(&msg->rtmsg_gateway, gw);
-	}
-	else
-		memset(&msg->rtmsg_gateway, 0, sizeof(struct in6_addr));
-
-	msg->rtmsg_prefixlen = plen;
-	msg->rtmsg_metric = metric;
-
-	if (dev)
-	{
-		struct inet6_dev *idev;
-
-		idev = ipv6_get_idev(dev);
-		if (idev)
-		{
-			ifindex = idev->if_index;
-		}
-	}
-	
-	msg->rtmsg_ifindex = ifindex;
-
-	msg->rtmsg_flags = flags;
-
-	if (netlink_post(NETLINK_ROUTE6, skb))
-	{
-		kfree_skb(skb, FREE_WRITE);
-	}
-}
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c
--- v2.1.29/linux/net/ipv6/ipv6_sockglue.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ipv6/ipv6_sockglue.c	Thu Mar 20 18:17:15 1997
@@ -7,7 +7,7 @@
  *
  *	Based on linux/net/ipv4/ip_sockglue.c
  *
- *	$Id: ipv6_sockglue.c,v 1.12 1996/10/29 22:45:53 roque Exp $
+ *	$Id: ipv6_sockglue.c,v 1.8 1997/03/18 18:24:38 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -40,7 +40,7 @@
 #include <net/ndisc.h>
 #include <net/protocol.h>
 #include <net/transp_v6.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
 #include <net/addrconf.h>
 #include <net/inet_common.h>
 #include <net/sit.h>
@@ -78,12 +78,9 @@
 	if(level!=SOL_IPV6)
 		goto out;
 
-	if (optval == NULL)
-	{
+	if (optval == NULL) {
 		val=0;
-	}
-	else
-	{
+	} else {
 		err = get_user(val, (int *) optval);
 		if(err)
 			return err;
@@ -93,43 +90,33 @@
 	switch (optname) {
 
 	case IPV6_ADDRFORM:
-		if (val == PF_INET)
-		{
+		if (val == PF_INET) {
 			if (sk->protocol != IPPROTO_UDP &&
 			    sk->protocol != IPPROTO_TCP)
-			{				
 				goto out;
-			}
 			
-			if (sk->state != TCP_ESTABLISHED)
-			{
+			if (sk->state != TCP_ESTABLISHED) {
 				retv = ENOTCONN;
 				goto out;
 			}
 			
-			if (!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED))
-			{
+			if (!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED)) {
 				retv = -EADDRNOTAVAIL;
 				goto out;
 			}
 
-			if (sk->protocol == IPPROTO_TCP)
-			{
+			if (sk->protocol == IPPROTO_TCP) {
 				struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 				
 				sk->prot = &tcp_prot;
 				tp->af_specific = &ipv4_specific;
 				sk->socket->ops = &inet_stream_ops;
-			}
-			else
-			{
+			} else {
 				sk->prot = &udp_prot;
 				sk->socket->ops = &inet_dgram_ops;
 			}
 			retv = 0;
-		}
-		else
-		{
+		} else {
 			retv = -EINVAL;
 		}
 		break;
@@ -146,11 +133,8 @@
 
 	case IPV6_UNICAST_HOPS:
 		if (val > 255)
-		{
 			retv = -EINVAL;
-		}
-		else
-		{
+		else {
 			np->hop_limit = val;
 			retv = 0;
 		}
@@ -158,11 +142,8 @@
 
 	case IPV6_MULTICAST_HOPS:
 		if (val > 255)
-		{
 			retv = -EINVAL;
-		}
-		else
-		{
+		else {
 			np->mcast_hops = val;
 			retv = 0;
 		}
@@ -180,23 +161,19 @@
 		if(err)
 			return -EFAULT;
 				
-		if (ipv6_addr_any(&addr))
-		{
-			np->mc_if = NULL;
-		}
-		else
-		{
+		if (ipv6_addr_any(&addr)) {
+			np->oif = NULL;
+		} else {
 			struct inet6_ifaddr *ifp;
 
 			ifp = ipv6_chk_addr(&addr);
 
-			if (ifp == NULL)
-			{
+			if (ifp == NULL) {
 				retv = -EADDRNOTAVAIL;
 				break;
 			}
 
-			np->mc_if = ifp->idev->dev;
+			np->oif = ifp->idev->dev;
 		}
 		retv = 0;
 		break;
@@ -212,8 +189,8 @@
 		if(err)
 			return -EFAULT;
 		
-		if (mreq.ipv6mr_ifindex == 0)
-		{
+		if (mreq.ipv6mr_ifindex == 0) {
+#if 0
 			struct in6_addr mcast;
 			struct dest_entry *dc;
 
@@ -226,34 +203,22 @@
 				dev = dc->rt.rt_dev;
 				ipv6_dst_unlock(dc);
 			}
-		}
-		else
-		{
-			struct inet6_dev *idev;
-			
-			if ((idev = ipv6_dev_by_index(mreq.ipv6mr_ifindex)))
-			{
-				dev = idev->dev;
-			}
+#endif
+		} else {
+			dev = dev_get_by_index(mreq.ipv6mr_ifindex);
 		}
 
 		if (dev == NULL)
-		{
 			return -ENODEV;
-		}
 		
 		if (optname == IPV6_ADD_MEMBERSHIP)
-		{
 			retv = ipv6_sock_mc_join(sk, dev, &mreq.ipv6mr_multiaddr);
-		}
 		else
-		{
 			retv = ipv6_sock_mc_drop(sk, dev, &mreq.ipv6mr_multiaddr);
-		}
-	}
 	}
+	};
 
-  out:
+out:
 	return retv;
 }
 
@@ -285,7 +250,7 @@
 
 	register_netdevice_notifier(&ipv6_dev_notf);
 	
-	ipv6_route_init();
+	ip6_route_init();
 }
 
 #ifdef MODULE
@@ -294,14 +259,8 @@
 	unregister_netdevice_notifier(&ipv6_dev_notf);
 	dev_remove_pack(&ipv6_packet_type);
 	ipv6_sysctl_unregister();	
-	ipv6_route_cleanup();
+	ip6_route_cleanup();
 	ndisc_cleanup();
 	addrconf_cleanup();	
 }
 #endif
-
-/*
- * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O6 -m486 -c ipv6_sockglue.c"
- * End:
- */
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/mcast.c linux/net/ipv6/mcast.c
--- v2.1.29/linux/net/ipv6/mcast.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ipv6/mcast.c	Thu Mar 20 18:17:15 1997
@@ -5,6 +5,8 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
+ *	$Id: mcast.c,v 1.7 1997/03/18 18:24:39 davem Exp $
+ *
  *	Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c 
  *
  *	This program is free software; you can redistribute it and/or
@@ -13,6 +15,8 @@
  *      2 of the License, or (at your option) any later version.
  */
 
+#define __NO_VERSION__
+#include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -31,13 +35,32 @@
 #include <net/protocol.h>
 #include <net/if_inet6.h>
 #include <net/ndisc.h>
-#include <net/ipv6_route.h>
 #include <net/addrconf.h>
 
+#include <net/checksum.h>
+
+/* Set to 3 to get tracing... */
+#define MCAST_DEBUG 2
+
+#if MCAST_DEBUG >= 3
+#define MDBG(x) printk x
+#else
+#define MDBG(x)
+#endif
+
+static struct inode igmp6_inode;
+static struct socket *igmp6_socket=&igmp6_inode.u.socket_i;
+
+static void igmp6_join_group(struct ifmcaddr6 *ma);
+static void igmp6_leave_group(struct ifmcaddr6 *ma);
+void igmp6_timer_handler(unsigned long data);
+
+#define IGMP6_UNSOLICITED_IVAL	(10*HZ)
 
 /*
  *	socket join on multicast group
  */
+
 int ipv6_sock_mc_join(struct sock *sk, struct device *dev, 
 		      struct in6_addr *addr)
 {
@@ -45,21 +68,25 @@
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	int err;
 
+	MDBG(("ipv6_sock_mc_join(%s) addr[", dev ? dev->name : "[NULL]"));
+	MDBG(("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
+	      addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+	      addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+	      addr->s6_addr16[6], addr->s6_addr16[7]));
 	if (!(ipv6_addr_type(addr) & IPV6_ADDR_MULTICAST))
 		return -EINVAL;
 
 	if(!(dev->flags & IFF_MULTICAST))
 		return -EADDRNOTAVAIL;
 
-	mc_lst = (struct ipv6_mc_socklist *) 
-		kmalloc(sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
+	mc_lst = kmalloc(sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
 
 	if (mc_lst == NULL)
 		return -ENOMEM;
 
 	mc_lst->next = NULL;
 	memcpy(&mc_lst->addr, addr, sizeof(struct in6_addr));
-	mc_lst->dev  = dev;
+	mc_lst->dev = dev;
 
 	/*
 	 *	now add/increase the group membership on the device
@@ -67,8 +94,7 @@
 
 	err = ipv6_dev_mc_inc(dev, addr);
 
-	if (err)
-	{
+	if (err) {
 		kfree(mc_lst);
 		return err;
 	}
@@ -85,7 +111,30 @@
 int ipv6_sock_mc_drop(struct sock *sk, struct device *dev, 
 		      struct in6_addr *addr)
 {
-	return 0;
+	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+	struct ipv6_mc_socklist *mc_lst, **lnk;
+
+	lnk = &np->ipv6_mc_list;
+
+	MDBG(("ipv6_sock_mc_drop(%s) addr[", dev ? dev->name : "[NULL]"));
+	MDBG(("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
+	      addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+	      addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+	      addr->s6_addr16[6], addr->s6_addr16[7]));
+
+	for (mc_lst = *lnk ; mc_lst; mc_lst = mc_lst->next) {
+		if (mc_lst->dev == dev &&
+		    ipv6_addr_cmp(&mc_lst->addr, addr) == 0) {
+			*lnk = mc_lst->next;
+			ipv6_dev_mc_dec(mc_lst->dev, &mc_lst->addr);
+			kfree(mc_lst);
+
+			return 0;
+		}
+		lnk = &mc_lst->next;
+	}
+
+	return -ENOENT;
 }
 
 void ipv6_sock_mc_close(struct sock *sk)
@@ -93,14 +142,15 @@
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct ipv6_mc_socklist *mc_lst;
 
-	for (mc_lst = np->ipv6_mc_list; mc_lst; )
-	{
+	for (mc_lst = np->ipv6_mc_list; mc_lst; ) {
 		struct ipv6_mc_socklist *back;
 
 		/*
 		 *	leave group
 		 */
 
+		ipv6_dev_mc_dec(mc_lst->dev, &mc_lst->addr);
+
 		back = mc_lst;
 		mc_lst = mc_lst->next;
 		kfree(back);
@@ -112,53 +162,59 @@
  */
 int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr)
 {
-	struct ipv6_mc_list *mc;
-	struct inet6_dev    *i6dev;
+	struct ifmcaddr6 *mc;
+	struct inet6_dev    *idev;
 	char buf[6];
-	u8 hash;
-	
-	for (i6dev = inet6_dev_lst; i6dev; i6dev=i6dev->next)
-		if (i6dev->dev == dev)
+	int hash;
+
+	MDBG(("ipv6_dev_mc_inc(%s) addr[", dev ? dev->name : "[NULL]"));
+	MDBG(("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
+	      addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+	      addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+	      addr->s6_addr16[6], addr->s6_addr16[7]));
+	hash = ipv6_devindex_hash(dev->ifindex);
+
+	for (idev = inet6_dev_lst[hash]; idev; idev=idev->next)
+		if (idev->dev == dev)
 			break;
-		
-	if (i6dev == NULL)
-	{
+
+	if (idev == NULL) {
 		printk(KERN_DEBUG "ipv6_dev_mc_inc: device not found\n");
 		return -EINVAL;
 	}
 
-	for (mc = i6dev->mc_list; mc; mc = mc->if_next)
-		if (ipv6_addr_cmp(&mc->addr, addr) == 0)
-		{
-			atomic_inc(&mc->users);
+	hash = ipv6_addr_hash(addr);
+
+	for (mc = inet6_mcast_lst[hash]; mc; mc = mc->next) {
+		if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0) {
+			atomic_inc(&mc->mca_users);
 			return 0;
 		}
+	}
 
 	/*
 	 *	not found: create a new one.
 	 */
 
-	mc = (struct ipv6_mc_list *) kmalloc(sizeof(struct ipv6_mc_list),
-					     GFP_ATOMIC);
+	mc = kmalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC);
 
 	if (mc == NULL)
-	{
 		return -ENOMEM;
-	}
 
-	memset(mc, 0, sizeof(struct ipv6_mc_list));
+	MDBG(("create new ipv6 MC entry, "));
+	memset(mc, 0, sizeof(struct ifmcaddr6));
+	mc->mca_timer.function = igmp6_timer_handler;
+	mc->mca_timer.data = (unsigned long) mc;
 
-	memcpy(&mc->addr, addr, sizeof(struct in6_addr));
+	memcpy(&mc->mca_addr, addr, sizeof(struct in6_addr));
 	mc->dev = dev;
-	mc->users = 1;
-
-	hash = ipv6_addr_hash(addr);
+	mc->mca_users = 1;
 
 	mc->next = inet6_mcast_lst[hash];
 	inet6_mcast_lst[hash] = mc;
-	
-	mc->if_next = i6dev->mc_list;
-	i6dev->mc_list = mc;
+
+	mc->if_next = idev->mc_list;
+	idev->mc_list = mc;
 
 	/*
 	 *	multicast mapping is defined in IPv6-over-foo documents
@@ -167,27 +223,67 @@
 	switch (dev->type) {
 	case ARPHRD_ETHER:
 		ipv6_mc_map(addr, buf);
+		MDBG(("ARPHRD_ETHER[%02x:%02x:%02x:%02x:%02x:%02x] dev_mc_add()\n",
+		      buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]));
 		dev_mc_add(dev, buf, ETH_ALEN, 0);
 		break;
-		
+
 	default:
 		printk(KERN_DEBUG "dev_mc_inc: unkown device type\n");
-	}
-	
+	};
 
-	/*
-	 *	FIXME: ICMP report handling
-	 */
+	igmp6_join_group(mc);
 
 	return 0;
 }
 
+static void ipv6_mca_remove(struct device *dev, struct ifmcaddr6 *ma)
+{
+	struct inet6_dev *idev;
+
+	idev = ipv6_get_idev(dev);
+
+	if (idev) {
+		struct ifmcaddr6 *iter, **lnk;
+
+		lnk = &idev->mc_list;
+		
+		for (iter = *lnk; iter; iter = iter->if_next) {
+			if (iter == ma) {
+				*lnk = iter->if_next;
+				break;
+			}
+			lnk = &iter->if_next;
+		}
+	}
+}
+
 /*
  *	device multicast group del
  */
 int ipv6_dev_mc_dec(struct device *dev, struct in6_addr *addr)
 {
-	return 0;
+	struct ifmcaddr6 *ma, **lnk;
+	int hash;
+
+	hash = ipv6_addr_hash(addr);
+
+	lnk = &inet6_mcast_lst[hash];
+
+	for (ma = inet6_mcast_lst[hash]; ma; ma = ma->next) {
+		if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0) {
+			if (atomic_dec_and_test(&ma->mca_users)) {
+				igmp6_leave_group(ma);
+				*lnk = ma->next;
+				ipv6_mca_remove(ma->dev, ma);
+				kfree(ma);
+			}
+			return 0;
+		}
+		lnk = &ma->next;
+	}
+
+	return -ENOENT;
 }
 
 /*
@@ -195,17 +291,15 @@
  */
 int ipv6_chk_mcast_addr(struct device *dev, struct in6_addr *addr)
 {
-	struct ipv6_mc_list *mc;	
-	u8 hash;
+	struct ifmcaddr6 *mc;
+	int hash;
 
 	hash = ipv6_addr_hash(addr);
 
-	for (mc = inet6_mcast_lst[hash]; mc; mc=mc->next)
-		if ((mc->dev == dev) &&
-		    ipv6_addr_cmp(&mc->addr, addr) == 0)
-		{
+	for (mc = inet6_mcast_lst[hash]; mc; mc=mc->next) {
+		if ((mc->dev == dev) && ipv6_addr_cmp(&mc->mca_addr, addr) == 0)
 			return 1;
-		}
+	}
 
 	return 0;
 }
@@ -214,8 +308,216 @@
  *	IGMP handling (alias multicast ICMPv6 messages)
  */
 
-/*
- * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h  -c -o mcast.o mcast.c"
- * End:
- */
+static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
+{
+	unsigned long delay;
+
+	ma->mca_flags |= MAF_TIMER_RUNNING;
+
+	delay = ipv6_random() % resptime;
+	ma->mca_timer.expires = jiffies + delay;
+	add_timer(&ma->mca_timer);
+}
+
+int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
+{
+	struct ifmcaddr6 *ma;
+	struct in6_addr *addrp;
+	unsigned long resptime;
+
+	if (len < sizeof(struct icmp6hdr) + sizeof(struct ipv6hdr))
+		return -EINVAL;
+
+	resptime = hdr->icmp6_maxdelay;
+
+	addrp = (struct in6_addr *) (hdr + 1);
+
+	if (ipv6_addr_any(addrp)) {
+		struct inet6_dev *idev;
+
+		idev = ipv6_get_idev(skb->dev);
+
+		if (idev == NULL)
+			return 0;
+
+		for (ma = idev->mc_list; ma; ma=ma->if_next)
+			igmp6_group_queried(ma, resptime);
+	} else {
+		int hash = ipv6_addr_hash(addrp);
+
+		for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
+			if (ma->dev == skb->dev &&
+			    ipv6_addr_cmp(addrp, &ma->mca_addr) == 0) {
+				igmp6_group_queried(ma, resptime);
+				break;
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+int igmp6_event_report(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
+{
+	struct ifmcaddr6 *ma;
+	struct in6_addr *addrp;
+	struct device *dev;
+	int hash;
+
+	if (len < sizeof(struct icmp6hdr) + sizeof(struct ipv6hdr))
+		return -EINVAL;
+
+	addrp = (struct in6_addr *) (hdr + 1);
+
+	dev = skb->dev;
+
+	/*
+	 *	Cancel the timer for this group
+	 */
+
+	hash = ipv6_addr_hash(addrp);
+
+	for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
+		if ((ma->dev == dev) && ipv6_addr_cmp(&ma->mca_addr, addrp) == 0) {
+			if (ma->mca_flags & MAF_TIMER_RUNNING) {
+				del_timer(&ma->mca_timer);
+				ma->mca_flags &= ~MAF_TIMER_RUNNING;
+			}
+
+			ma->mca_flags &= ~MAF_LAST_REPORTER;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+void igmp6_send(struct in6_addr *addr, struct device *dev, int type)
+{
+	struct sock *sk = igmp6_socket->sk;
+        struct sk_buff *skb;
+        struct icmp6hdr *hdr;
+	struct inet6_ifaddr *ifp;
+	struct in6_addr *addrp; 
+	int err, len, plen;
+
+	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+
+	plen = sizeof(struct ipv6hdr) + len;
+
+	skb = sock_alloc_send_skb(sk, dev->hard_header_len + plen, 0, 0, &err);
+
+	if (skb == NULL)
+		return;
+
+	if (dev->hard_header_len) {
+		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+		if (dev->hard_header) {
+			unsigned char ha[MAX_ADDR_LEN];
+			ipv6_mc_map(addr, ha);
+			dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, plen);
+			skb->arp = 1;
+		}
+	}
+
+	ifp = ipv6_get_lladdr(dev);
+
+	if (ifp == NULL) {
+#if MCAST_DEBUG >= 1
+		printk(KERN_DEBUG "igmp6: %s no linklocal address\n",
+		       dev->name);
+#endif
+		return;
+	}
+
+	ip6_nd_hdr(sk, skb, dev, &ifp->addr, addr, IPPROTO_ICMPV6, len);
+
+	/*
+	 *	need hop-by-hop router alert option.
+	 */
+
+	hdr = (struct icmp6hdr *) skb_put(skb, sizeof(struct icmp6hdr));
+	memset(hdr, 0, sizeof(struct icmp6hdr));
+	hdr->icmp6_type = type;
+
+	addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr));
+	ipv6_addr_copy(addrp, addr);
+
+	hdr->icmp6_cksum = csum_ipv6_magic(&ifp->addr, addr, len,
+					   IPPROTO_ICMPV6,
+					   csum_partial((__u8 *) hdr, len, 0));
+
+	dev_queue_xmit(skb);
+}
+
+static void igmp6_join_group(struct ifmcaddr6 *ma)
+{
+	unsigned long delay;
+	int addr_type;
+
+	addr_type = ipv6_addr_type(&ma->mca_addr);
+
+	if ((addr_type & IPV6_ADDR_LINKLOCAL))
+		return;
+
+	igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
+
+	delay = ipv6_random() % IGMP6_UNSOLICITED_IVAL;
+	ma->mca_timer.expires = jiffies + delay;
+
+	add_timer(&ma->mca_timer);
+	ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER;
+}
+
+static void igmp6_leave_group(struct ifmcaddr6 *ma)
+{
+	int addr_type;
+
+	addr_type = ipv6_addr_type(&ma->mca_addr);
+
+	if ((addr_type & IPV6_ADDR_LINKLOCAL))
+		return;
+
+	if (ma->mca_flags & MAF_LAST_REPORTER)
+		igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REDUCTION);
+
+	if (ma->mca_flags & MAF_TIMER_RUNNING)
+		del_timer(&ma->mca_timer);
+}
+
+void igmp6_timer_handler(unsigned long data)
+{
+	struct ifmcaddr6 *ma = (struct ifmcaddr6 *) data;
+
+	ma->mca_flags |=  MAF_LAST_REPORTER;
+	igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
+	ma->mca_flags &= ~MAF_TIMER_RUNNING;
+}
+
+void igmp6_init(struct net_proto_family *ops)
+{
+	struct sock *sk;
+	int err;
+
+	igmp6_inode.i_mode = S_IFSOCK;
+	igmp6_inode.i_sock = 1;
+	igmp6_inode.i_uid = 0;
+	igmp6_inode.i_gid = 0;
+
+	igmp6_socket->inode = &igmp6_inode;
+	igmp6_socket->state = SS_UNCONNECTED;
+	igmp6_socket->type = SOCK_RAW;
+
+	if((err=ops->create(igmp6_socket, IPPROTO_ICMPV6))<0)
+		printk(KERN_DEBUG 
+		       "Failed to create the IGMP6 control socket.\n");
+
+	MOD_DEC_USE_COUNT;
+
+	sk = igmp6_socket->sk;
+	sk->allocation = GFP_ATOMIC;
+	sk->num = 256;			/* Don't receive any data */
+
+	sk->net_pinfo.af_inet6.hop_limit = 1;
+}
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.1.29/linux/net/ipv6/ndisc.c	Mon Mar 17 14:54:36 1997
+++ linux/net/ipv6/ndisc.c	Thu Mar 20 18:17:15 1997
@@ -6,7 +6,7 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *	Mike Shaver		<shaver@ingenia.com>
  *
- *	$Id: ndisc.c,v 1.28 1996/10/11 16:03:06 roque Exp $
+ *	$Id: ndisc.c,v 1.13 1997/03/18 18:24:41 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -23,8 +23,15 @@
  *	Janos Farkas			:	kmalloc failure checks
  */
 
+/* Set to 3 to get tracing... */
 #define ND_DEBUG 2
 
+#if ND_DEBUG >= 3
+#define NDBG(x) printk x
+#else
+#define NDBG(x)
+#endif
+
 #define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/config.h>
@@ -37,7 +44,6 @@
 #include <linux/in6.h>
 #include <linux/route.h>
 
-#include <net/neighbour.h>
 #include <linux/if_arp.h>
 #include <linux/ipv6.h>
 #include <linux/icmpv6.h>
@@ -48,7 +54,7 @@
 #include <net/ipv6.h>
 #include <net/protocol.h>
 #include <net/ndisc.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
 #include <net/addrconf.h>
 
 
@@ -68,9 +74,7 @@
 static struct neigh_table nd_tbl;
 
 unsigned int	ndisc_hash(void *primary_key);
-int			ndisc_eth_resolv(unsigned char *h_dest,
-					 struct device *dev,
-					 struct sk_buff *skb);
+int		ndisc_eth_resolv(unsigned char *h_dest, struct sk_buff *skb);
 
 static struct neigh_ops nd_neigh_ops = {
 	ETH_P_IPV6,
@@ -86,13 +90,8 @@
  *	Protocol variables
  */
 
-int	nd_max_multicast_solicit	= 3;
-int	nd_max_unicast_solicit		= 3;
-int	nd_retrans_timer		= RETRANS_TIMER;
-int	nd_reachable_time		= RECHABLE_TIME;
-int	nd_base_reachable_time		= RECHABLE_TIME;
-int	nd_delay_first_probe		= 5 * HZ;
-int	nd_gc_interval			= 5 * HZ;
+unsigned long	nd_reachable_time		= RECHABLE_TIME;
+int		nd_gc_interval			= 5 * HZ;
 
 /* 
  *	garbage collection timeout must be greater than reachable time
@@ -106,7 +105,7 @@
 
 static int  ndisc_event_timer(struct nd_neigh *ndn);
 
-int ipv6_random(void)
+unsigned long ipv6_random(void)
 {
 	nd_rand_seed=nd_rand_seed*69069L+1;
         return nd_rand_seed^jiffies;
@@ -116,17 +115,15 @@
 {
 	unsigned long val;
 
-	val = ipv6_random() % (MAX_RANDOM_FACTOR * nd_base_reachable_time);
-	if (val < (MIN_RANDOM_FACTOR * nd_base_reachable_time))
-	{
-		val += (MIN_RANDOM_FACTOR * nd_base_reachable_time);
-	}
+	val = ipv6_random() % (MAX_RANDOM_FACTOR *
+			       ipv6_config.nd_base_reachable_time);
+
+	if (val < (MIN_RANDOM_FACTOR * ipv6_config.nd_base_reachable_time))
+		val+= (MIN_RANDOM_FACTOR * ipv6_config.nd_base_reachable_time);
 
 	return val;
 }
 
-void ndisc_verify_reachability(struct neighbour * neigh);
-
 unsigned int ndisc_hash(void *primary_key)
 {
         struct in6_addr *addr = (struct in6_addr *) primary_key;
@@ -152,8 +149,7 @@
 	 *	periodicly compute ReachableTime from random function
 	 */
 	
-	if ((now - last_rand) > REACH_RANDOM_INTERVAL)
-	{
+	if ((now - last_rand) > REACH_RANDOM_INTERVAL) {
 		last_rand = now;
 		nd_reachable_time = rand_reach_time();
 	}
@@ -161,13 +157,10 @@
 	neigh_table_lock(&nd_tbl);
 
 	start_bh_atomic();
-	if (nd_tbl.tbl_lock == 1)
-	{
+	if (nd_tbl.tbl_lock == 1) {
 		ntbl_walk_table(&nd_tbl, ndisc_gc_func, 0, 0, NULL);
 		ndisc_gc_timer.expires = now + nd_gc_interval;
-	}
-	else
-	{
+	} else {
 #if ND_DEBUG >= 2
 		printk(KERN_DEBUG "ndisc_gc delayed: table locked\n");
 #endif
@@ -185,8 +178,7 @@
 	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
         unsigned long now = jiffies;
 
-	if (ndn->ndn_refcnt == 0)
-	{
+	if (ndn->ndn_refcnt == 0) {
 		switch (ndn->ndn_nud_state) {
 		
 		case NUD_REACHABLE:
@@ -196,7 +188,7 @@
 		case NUD_FAILED:
 			return 1;
 		default:
-		}
+		};
 	}
 	return 0;
 }
@@ -209,9 +201,7 @@
 	ndn->ndn_expires = now + timer;
 	
 	if (del_timer(&ndisc_timer))
-	{
 		tval = ndisc_timer.expires;
-	}
 
 	tval = min(tval, ndn->ndn_expires);
 
@@ -222,17 +212,15 @@
 static void ndisc_del_timer(struct nd_neigh *ndn)
 {
 	unsigned long tval = ~0UL;
-
-	if (!(ndn->ndn_nud_state & NUD_IN_TIMER))
-		return;
+	unsigned long neigh_val;
 
 	if (del_timer(&ndisc_timer))
-	{
 		tval = ndisc_timer.expires;
-	}
-	
-	if (tval == ndn->ndn_expires)
-	{
+
+	neigh_val = ndn->ndn_expires;
+	ndn->ndn_expires = 0;
+
+	if (tval == neigh_val) {
 		int i;
 		
 		tval = ~0UL;
@@ -240,27 +228,24 @@
 		neigh_table_lock(&nd_tbl);
 		
 		/* need to search the entire neighbour cache */
-		for (i=0; i < nd_tbl.tbl_size; i++)
-		{
+		for (i=0; i < nd_tbl.tbl_size; i++) {
 			struct neighbour *neigh, *head;
 			head = nd_tbl.hash_buckets[i];
 				
 			if ((neigh = head) == NULL)
 				continue;
-			
-			do
-			{
+
+			do {
 				struct nd_neigh *n;
 
 				n = (struct nd_neigh *) neigh;
 
-				if (n->ndn_nud_state & NUD_IN_TIMER)
-				{
+				if ((n->ndn_nud_state & NUD_IN_TIMER) &&
+				     n->ndn_expires)
 					tval = min(tval, n->ndn_expires);
-				}
-				
+
 				neigh = neigh->next;
-				
+
 			} while (neigh != head);
 		}
 		neigh_table_unlock(&nd_tbl);
@@ -277,12 +262,9 @@
 {
 	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
 
-	if (ndn->ndn_refcnt == 0)
-	{
+	if (ndn->ndn_refcnt == 0) {
 		if (ndn->ndn_nud_state & NUD_IN_TIMER)
-		{
 			ndisc_del_timer(ndn);
-		}
 		
 		return 1;
 	}
@@ -294,20 +276,26 @@
 {
 	struct nd_neigh *ndn;
 
+	NDBG(("ndisc_new_neigh("));
+	if(dev)
+		NDBG(("%s,", dev->name));
+	else
+		NDBG(("[NULL],"));
+	NDBG(("[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]): ",
+	      addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+	      addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+	      addr->s6_addr16[6], addr->s6_addr16[7]));
+
 	ndn = (struct nd_neigh *) neigh_alloc(sizeof(struct nd_neigh),
-					      GFP_ATOMIC);
-	
-	if (ndn == NULL)
-	{
+					      &nd_neigh_ops);
+	if (ndn == NULL) {
 
 #if ND_DEBUG >= 2
 		printk(KERN_DEBUG "neigh_alloc: out of memory\n");
 #endif
 
 		start_bh_atomic();
-		if (nd_tbl.tbl_lock == 1)
-		{
-
+		if (nd_tbl.tbl_lock == 1) {
 #if ND_DEBUG >= 2
 			printk(KERN_DEBUG "ndisc_alloc: forcing gc\n");
 #endif
@@ -329,12 +317,19 @@
 	ndn->ndn_dev = dev;
 	ndn->ndn_tstamp = jiffies;
 
-	if (dev->type == ARPHRD_LOOPBACK || dev->type == ARPHRD_SIT)
-	{
+	if ((ndn->ndn_type & IPV6_ADDR_MULTICAST)) {
+		NDBG(("MULTICAST(NCF_NOARP) "));
+		ndn->ndn_flags |= NCF_NOARP;
+	}
+
+	if (dev->type == ARPHRD_LOOPBACK || dev->type == ARPHRD_SIT) {
+		NDBG(("%s(NCF_NOARP) ",
+		      (dev->type==ARPHRD_LOOPBACK) ? "LOOPBACK" : "SIT"));
 		ndn->ndn_flags |= NCF_NOARP;
 	}
 
 	neigh_insert(&nd_tbl, (struct neighbour *) ndn);
+	NDBG(("returning ndn(%p)\n", ndn));
 	return ndn;
 }
 
@@ -353,10 +348,9 @@
 	 *	cached information about nexthop and addr resolution
 	 */
 
-	if (dev == NULL)
-	{
+	if (dev == NULL) {
 #if ND_DEBUG >= 1
-		printk(KERN_DEBUG "ncache_get_neigh: NULL device\n");
+		printk(KERN_DEBUG "ndisc_get_neigh: NULL device\n");
 #endif
 		return NULL;
 	}
@@ -365,10 +359,7 @@
 
         neigh = (struct nd_neigh *) neigh_lookup(&nd_tbl, (void *) addr,
 						 sizeof(struct in6_addr), dev);
-
-	
-	if (neigh == NULL)
-	{
+	if (neigh == NULL) {
 		neigh = ndisc_new_neigh(dev, addr);
 
 		if (neigh == NULL)
@@ -377,9 +368,7 @@
 
 	neigh_table_unlock(&nd_tbl);
 
-	atomic_inc(&neigh->ndn_refcnt);
-	
-	return (struct neighbour *) neigh;	
+	return neighbour_clone((struct neighbour *) neigh);
 }
 
 /*
@@ -388,45 +377,35 @@
  *	1 - Address Resolution unfinished / packet queued
  */
 
-int ndisc_eth_resolv(unsigned char *h_dest, struct device *dev,
-		     struct sk_buff *skb)
+int ndisc_eth_resolv(unsigned char *h_dest, struct sk_buff *skb)
 {
-	struct nd_neigh *ndn;
+	struct nd_neigh *ndn = NULL;
 
-	ndn = (struct nd_neigh *) skb->nexthop;
-	
-	if (ndn == NULL)
-	{
-		struct in6_addr *daddr;
-		int addr_type;
-
-		daddr = &skb->nh.ipv6h->daddr;
-		
-		addr_type = ipv6_addr_type(daddr);
-		
-		if (addr_type & IPV6_ADDR_MULTICAST)
-		{
-			ipv6_mc_map(daddr, h_dest);
-			return 0;
-		}
+	if (skb->dst)
+		ndn = (struct nd_neigh *) skb->dst->neighbour;
 
+	if (ndn == NULL) {
 #if ND_DEBUG >= 2
 		printk(KERN_DEBUG "ndisc_eth_resolv: nexthop is NULL\n");
 #endif
 		goto discard;
 	}
 
-	if (skb->pkt_type == PACKET_NDISC)
-		goto ndisc_pkt;
-	
+	if ((ndn->ndn_type & IPV6_ADDR_MULTICAST)) {
+		struct in6_addr *daddr;
+
+		daddr = &skb->nh.ipv6h->daddr;
+		ipv6_mc_map(daddr, h_dest);
+		return 0;
+	}
+
 	switch (ndn->ndn_nud_state) {	
 	case NUD_FAILED:
 	case NUD_NONE:
 		ndisc_event_send((struct neighbour *)ndn, skb);
 
 	case NUD_INCOMPLETE:			
-		if (skb_queue_len(&ndn->neigh.arp_queue) >= NDISC_QUEUE_LEN)
-		{
+		if (skb_queue_len(&ndn->neigh.arp_queue) >= NDISC_QUEUE_LEN) {
 			struct sk_buff *buff;
 			
 			buff = ndn->neigh.arp_queue.prev;
@@ -437,18 +416,15 @@
 		return 1;
 	default:
 		ndisc_event_send((struct neighbour *)ndn, skb);
-	}
+	};
 
-  ndisc_pkt:
-	
-	if ((ndn->ndn_flags & NTF_COMPLETE) == 0)
-	{
+	if ((ndn->ndn_flags & NTF_COMPLETE) == 0) {
 #if ND_DEBUG >=1
 		/* This shouldn't happen */
 		printk(KERN_DEBUG "ND: using incomplete entry\n");
 #endif
 	}
-	memcpy(h_dest, ndn->ndn_ha, dev->addr_len);
+	memcpy(h_dest, ndn->ndn_ha, skb->dev->addr_len);
 	return 0;
 
   discard:
@@ -457,14 +433,12 @@
 	return 1;
 }
 
-
 /*
  *	Send a Neighbour Advertisement
  */
 
 void ndisc_send_na(struct device *dev, struct nd_neigh *ndn,
-		   struct in6_addr *daddr,
-		   struct in6_addr *solicited_addr,
+		   struct in6_addr *daddr, struct in6_addr *solicited_addr,
 		   int router, int solicited, int override, int inc_opt) 
 {
         struct sock *sk = ndisc_socket->sk;
@@ -473,38 +447,64 @@
         struct sk_buff *skb;
 	int err;
 
+	NDBG(("ndisc_send_na("));
+	if(dev)
+		NDBG(("%s,", dev->name));
+	else
+		NDBG(("[NULL]"));
+	NDBG(("%p): ", ndn));
+	if(daddr)
+		NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+		      daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+		      daddr->s6_addr16[6], daddr->s6_addr16[7]));
+	if(solicited_addr)
+		NDBG(("solicit_addr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      solicited_addr->s6_addr16[0], solicited_addr->s6_addr16[1],
+		      solicited_addr->s6_addr16[2], solicited_addr->s6_addr16[3],
+		      solicited_addr->s6_addr16[4], solicited_addr->s6_addr16[5],
+		      solicited_addr->s6_addr16[6], solicited_addr->s6_addr16[7]));
+	NDBG(("rtr(%d)sol(%d)ovr(%d)iopt(%d)\n", router, solicited, override, inc_opt));
+
 	opt_len = ((dev->addr_len + 1) >> 3) + 1;
-	len = sizeof(struct icmpv6hdr) + sizeof(struct in6_addr);
+	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
 
+#if ND_DEBUG >=1
+	if (dev == NULL) {
+		printk(KERN_DEBUG "send_na: null device\n");
+		return;
+	}
+#endif
 	if (inc_opt)
-	{
 		len += opt_len << 3;
-	}
 
-	skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
+	skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+				  0, 0, &err);
 
-	if (skb == NULL)
-	{
+	if (skb == NULL) {
 		printk(KERN_DEBUG "send_na: alloc skb failed\n");
 		return;
 	}
-
-	if (ipv6_bld_hdr_2(sk, skb, dev, (struct neighbour *) ndn, 
-			   solicited_addr, daddr, IPPROTO_ICMPV6, len) < 0)
-        {
-		kfree_skb(skb, FREE_WRITE);
-		printk(KERN_DEBUG 
-		       "ndisc_send_na: ipv6_build_header returned < 0\n");
-		return;
+	/*
+	 *	build the MAC header
+	 */
+	
+	if (dev->hard_header_len) {
+		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+		if (dev->hard_header) {
+			dev->hard_header(skb, dev, ETH_P_IPV6, ndn->ndn_ha,
+					 NULL, len);
+			skb->arp = 1;
+		}
 	}
 
-	skb->pkt_type = PACKET_NDISC;
-	
+	ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len);
+
 	msg = (struct nd_msg *) skb_put(skb, len);
 
-        msg->icmph.type = NDISC_NEIGHBOUR_ADVERTISEMENT;
-        msg->icmph.code = 0;
-        msg->icmph.checksum = 0;
+        msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
+        msg->icmph.icmp6_code = 0;
+        msg->icmph.icmp6_cksum = 0;
 
         msg->icmph.icmp6_unused = 0;
         msg->icmph.icmp6_router    = router;
@@ -514,47 +514,66 @@
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
 
-	if (inc_opt)
-	{
+	if (inc_opt) {
 		/* Set the source link-layer address option. */
 		msg->opt.opt_type = ND_OPT_TARGET_LL_ADDR;
 		msg->opt.opt_len = opt_len;
 		memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
 
-		if ((opt_len << 3) - (2 + dev->addr_len))
-		{
+		if ((opt_len << 3) - (2 + dev->addr_len)) {
 			memset(msg->opt.link_addr + dev->addr_len, 0,
 			       (opt_len << 3) - (2 + dev->addr_len));
 		}
 	}
 
 	/* checksum */
-	msg->icmph.checksum = csum_ipv6_magic(solicited_addr, daddr, len, 
-					      IPPROTO_ICMPV6,
-					      csum_partial((__u8 *) msg, 
-							   len, 0));
+	msg->icmph.icmp6_cksum = csum_ipv6_magic(solicited_addr, daddr, len, 
+						 IPPROTO_ICMPV6,
+						 csum_partial((__u8 *) msg, 
+							      len, 0));
 
-	ipv6_queue_xmit(sk, skb->dev, skb, 1);
+	dev_queue_xmit(skb);
 }        
 
 void ndisc_send_ns(struct device *dev, struct neighbour *neigh,
 		   struct in6_addr *solicit,
 		   struct in6_addr *daddr, struct in6_addr *saddr) 
 {
+	unsigned char ha[MAX_ADDR_LEN];
         struct sock *sk = ndisc_socket->sk;
         struct sk_buff *skb;
         struct nd_msg *msg;
-        int len, opt_len;
+        int len, opt_len;	
+	void *h_dest;
 	int err;
 
+	NDBG(("ndisc_send_ns(%s,%p): ", (dev ? dev->name : "[NULL]"), neigh));
+	if(daddr)
+		NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+		      daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+		      daddr->s6_addr16[6], daddr->s6_addr16[7]));
+	if(saddr)
+		NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+		      saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+		      saddr->s6_addr16[6], saddr->s6_addr16[7]));
+	if(solicit)
+		NDBG(("solicit[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      solicit->s6_addr16[0], solicit->s6_addr16[1],
+		      solicit->s6_addr16[2], solicit->s6_addr16[3],
+		      solicit->s6_addr16[4], solicit->s6_addr16[5],
+		      solicit->s6_addr16[6], solicit->s6_addr16[7]));
+	NDBG(("\n"));
+
 	/* length of addr in 8 octet groups.*/
 	opt_len = ((dev->addr_len + 1) >> 3) + 1;
-	len = sizeof(struct icmpv6hdr) + sizeof(struct in6_addr) +
+	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr) +
                 (opt_len << 3);
 
-	skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
-	if (skb == NULL)
-	{
+	skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+				  0, 0, &err);
+	if (skb == NULL) {
 #if ND_DEBUG >= 1
 		printk(KERN_DEBUG "send_ns: alloc skb failed\n");
 #endif
@@ -563,41 +582,47 @@
 
 	skb->pkt_type = PACKET_NDISC;
 
-	if (saddr == NULL)
-	{
+	if (saddr == NULL) {
 		struct inet6_ifaddr *ifa;
 
 		/* use link local address */
 		ifa = ipv6_get_lladdr(dev);
 
 		if (ifa)
-		{
 			saddr = &ifa->addr;
-		}
 	}
 
-	if(ipv6_addr_type(daddr) == IPV6_ADDR_MULTICAST)
-	{
+	if ((ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST)) {
 		nd_stats.snt_probes_mcast++;
-	}
-        else
-	{
+		ipv6_mc_map(daddr, ha);
+		h_dest = ha;
+	} else {
+		if (neigh == NULL) {
+#if ND_DEBUG >= 1
+			printk(KERN_DEBUG "send_ns: ucast destination "
+			       "with null neighbour\n");
+#endif
+			return;
+		}
+		h_dest = neigh->ha;
 		nd_stats.snt_probes_ucast++;
 	}
 
-	if (ipv6_bld_hdr_2(sk, skb, dev, neigh, saddr, daddr, IPPROTO_ICMPV6,
-			   len) < 0 )
-	{
-		kfree_skb(skb, FREE_WRITE);
-		printk(KERN_DEBUG
-		       "ndisc_send_ns: ipv6_build_header returned < 0\n");
-		return;
+	if (dev->hard_header_len) {
+		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+		if (dev->hard_header) {
+			dev->hard_header(skb, dev, ETH_P_IPV6, h_dest, NULL,
+					 len);
+			skb->arp = 1;
+		}
 	}
+
+	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
 	
 	msg = (struct nd_msg *)skb_put(skb, len);
-	msg->icmph.type = NDISC_NEIGHBOUR_SOLICITATION;
-	msg->icmph.code = 0;
-	msg->icmph.checksum = 0;
+	msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;
+	msg->icmph.icmp6_code = 0;
+	msg->icmph.icmp6_cksum = 0;
 	msg->icmph.icmp6_unused = 0;
 
 	/* Set the target address. */
@@ -609,20 +634,19 @@
 
 	memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
 
-	if ((opt_len << 3) - (2 + dev->addr_len))
-	{
+	if ((opt_len << 3) - (2 + dev->addr_len)) {
 		memset(msg->opt.link_addr + dev->addr_len, 0,
 		       (opt_len << 3) - (2 + dev->addr_len));
 	}
 
 	/* checksum */
-	msg->icmph.checksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr,
-					      daddr, len, 
-					      IPPROTO_ICMPV6,
-					      csum_partial((__u8 *) msg, 
-							   len, 0));
+	msg->icmph.icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr,
+						 daddr, len, 
+						 IPPROTO_ICMPV6,
+						 csum_partial((__u8 *) msg, 
+							      len, 0));
 	/* send it! */
-	ipv6_queue_xmit(sk, skb->dev, skb, 1);
+	dev_queue_xmit(skb);
 }
 
 void ndisc_send_rs(struct device *dev, struct in6_addr *saddr,
@@ -630,35 +654,52 @@
 {
 	struct sock *sk = ndisc_socket->sk;
         struct sk_buff *skb;
-        struct icmpv6hdr *hdr;
+        struct icmp6hdr *hdr;
 	__u8 * opt;
         int len, opt_len;
 	int err;
 
+	NDBG(("ndisc_send_rs(%s): ", (dev ? dev->name : "[NULL]")));
+	if(daddr)
+		NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+		      daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+		      daddr->s6_addr16[6], daddr->s6_addr16[7]));
+	if(saddr)
+		NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+		      saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+		      saddr->s6_addr16[6], saddr->s6_addr16[7]));
+	NDBG(("\n"));
+
 	/* length of addr in 8 octet groups.*/
 	opt_len = ((dev->addr_len + 1) >> 3) + 1;
-	len = sizeof(struct icmpv6hdr) + (opt_len << 3);
+	len = sizeof(struct icmp6hdr) + (opt_len << 3);
 
-        skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
-	if (skb == NULL)
-	{
+        skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+				  0, 0, &err);
+	if (skb == NULL) {
 		printk(KERN_DEBUG "send_ns: alloc skb failed\n");
 		return;
 	}
 
-        if (ipv6_bld_hdr_2(sk, skb, dev, NULL, saddr, daddr, IPPROTO_ICMPV6,
-			   len) < 0 )
-	{
-                kfree_skb(skb, FREE_WRITE);
-                printk(KERN_DEBUG
-                       "ndisc_send_ns: ipv6_build_header returned < 0\n");
-                return;
-        }
+	if (dev->hard_header_len) {
+		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+		if (dev->hard_header) {
+			unsigned char ha[MAX_ADDR_LEN];
+
+			ipv6_mc_map(daddr, ha);
+			dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, len);
+			skb->arp = 1;
+		}
+	}
+
+	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
 	
-        hdr = (struct icmpv6hdr *) skb_put(skb, len);
-        hdr->type = NDISC_ROUTER_SOLICITATION;
-        hdr->code = 0;
-        hdr->checksum = 0;
+        hdr = (struct icmp6hdr *) skb_put(skb, len);
+        hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;
+        hdr->icmp6_code = 0;
+        hdr->icmp6_cksum = 0;
         hdr->icmp6_unused = 0;
 
 	opt = (u8*) (hdr + 1);
@@ -669,27 +710,25 @@
 
         memcpy(opt + 2, dev->dev_addr, dev->addr_len);
 
-	if ((opt_len << 3) - (2 + dev->addr_len))
-	{
+	if ((opt_len << 3) - (2 + dev->addr_len)) {
 		memset(opt + 2 + dev->addr_len, 0,
 		       (opt_len << 3) - (2 + dev->addr_len));
 	}
 
 	/* checksum */
-	hdr->checksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len,
-					IPPROTO_ICMPV6,
-					csum_partial((__u8 *) hdr, len, 0));
+	hdr->icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len,
+					   IPPROTO_ICMPV6,
+					   csum_partial((__u8 *) hdr, len, 0));
 
 	/* send it! */
-	ipv6_queue_xmit(sk, skb->dev, skb, 1);
+	dev_queue_xmit(skb);
 }
 		   
 
 static int ndisc_store_hwaddr(struct nd_neigh *ndn, __u8 *opt, int opt_len,
 			      int option)
 {
-	while (*opt != option && opt_len)
-	{
+	while (*opt != option && opt_len) {
 		int len;
 
 		len = opt[1] << 3;
@@ -704,8 +743,7 @@
 		opt_len -= len;
 	}
 
-	if (*opt == option)
-	{
+	if (*opt == option) {
 		memcpy(ndn->neigh.ha, opt + 2, ndn->ndn_dev->addr_len); 
 		return 0;
 	}
@@ -723,40 +761,32 @@
 
 	neigh_table_lock(&nd_tbl);
 	
-	for (i=0; i < nd_tbl.tbl_size; i++)
-	{
+	for (i=0; i < nd_tbl.tbl_size; i++) {
 		struct nd_neigh *ndn, *head;
 
 		head = (struct nd_neigh *) nd_tbl.hash_buckets[i];
 
 		if ((ndn = head) == NULL)
 			continue;
-		
-		do
-		{
-                        if (ndn->ndn_nud_state & NUD_IN_TIMER)
-			{
+
+		do {
+                        if (ndn->ndn_nud_state & NUD_IN_TIMER) {
 				unsigned long time;
 
 				time = ndn->ndn_expires - now;
 
 				if ((long) time <= 0)
-				{
 					time = ndisc_event_timer(ndn);
-				}
 				
 				if (time)
-				{
 					ntimer = min(ntimer, time);
-				}
 			}
 			ndn = (struct nd_neigh *) ndn->neigh.next;
 
 		} while (ndn != head);
 	}
 
-	if (ntimer != (~0UL))
-	{
+	if (ntimer != (~0UL)) {
 		ndisc_timer.expires = now + ntimer;
 		add_timer(&ndisc_timer);
 	}
@@ -774,23 +804,20 @@
 	int max_probes;
 
 	if (ndn->ndn_nud_state == NUD_DELAY)
-	{
 		ndn->ndn_nud_state = NUD_PROBE;
-	}
 
-	max_probes = (ndn->ndn_nud_state == NUD_PROBE ? nd_max_unicast_solicit:
-		      nd_max_multicast_solicit);
+	max_probes = (ndn->ndn_nud_state == NUD_PROBE ?
+		      ipv6_config.nd_max_ucast_solicit:
+		      ipv6_config.nd_max_mcast_solicit);
 
-	if (ndn->ndn_probes == max_probes)
-	{
+	if (ndn->ndn_probes == max_probes) {
 		struct sk_buff *skb;
 
 		ndn->ndn_nud_state = NUD_FAILED;
 		ndn->ndn_flags &= ~NTF_COMPLETE;
 		nd_stats.res_failed++;
 
-		while((skb=skb_dequeue(&ndn->neigh.arp_queue)))
-		{
+		while((skb=skb_dequeue(&ndn->neigh.arp_queue))) {
 			/*
 			 *	"The sender MUST return an ICMP
 			 *	 destination unreachable"
@@ -808,20 +835,17 @@
 	dev = ndn->ndn_dev;
 	target = &ndn->ndn_addr;
 
-	if (ndn->ndn_nud_state == NUD_INCOMPLETE)
-	{
+	if (ndn->ndn_nud_state == NUD_INCOMPLETE) {
 		addrconf_addr_solict_mult(&ndn->ndn_addr, &mcaddr);
 		daddr = &mcaddr;
 		ndn = NULL;
-	}
-	else
-	{
+	} else {
 		daddr = &ndn->ndn_addr;
 	}
 
 	ndisc_send_ns(dev, (struct neighbour *) ndn, target, daddr, NULL);
 
-	return nd_retrans_timer;
+	return ipv6_config.nd_retrans_time;
 }
 
 void ndisc_event_send(struct neighbour *neigh, struct sk_buff *skb)
@@ -830,14 +854,15 @@
 	struct in6_addr daddr;
 	unsigned long now = jiffies;
 	struct in6_addr *saddr = NULL;
-	
+
+	if ((ndn->ndn_flags & NCF_NOARP))
+		return;
+
 	switch (ndn->ndn_nud_state) {
 	case NUD_FAILED:
 		ndn->ndn_probes = 0;
 	case NUD_NONE:
-
-		if (skb && !skb->stamp.tv_sec)
-		{
+		if (skb && !skb->stamp.tv_sec) {
 			/*
 			 *	skb->stamp allows us to know if we are
 			 *	originating the skb or forwarding it.
@@ -850,7 +875,7 @@
 		addrconf_addr_solict_mult(&ndn->ndn_addr, &daddr);
 		ndisc_send_ns(ndn->ndn_dev, NULL, &ndn->ndn_addr, &daddr,
 			      saddr);
-		ndisc_add_timer(ndn, nd_retrans_timer);
+		ndisc_add_timer(ndn, ipv6_config.nd_retrans_time);
 
 		break;
 
@@ -860,7 +885,7 @@
 
 	case NUD_STALE:
 		ndn->ndn_nud_state = NUD_DELAY;
-		ndisc_add_timer(ndn, nd_delay_first_probe);
+		ndisc_add_timer(ndn, ipv6_config.nd_delay_probe_time);
 	}
 }
 
@@ -872,26 +897,21 @@
 {
 	struct sk_buff *skb;
 
+	NDBG(("ndisc_event_na(%p,%p,%d,%d,%d)\n", ndn, opt, opt_len,
+	      solicited, override));
+
 	if (ndn->ndn_nud_state == NUD_NONE)
-	{
 		ndn->ndn_nud_state = NUD_INCOMPLETE;
-	}
 
-	if (ndn->ndn_nud_state == NUD_INCOMPLETE || override)
-	{
-		if (opt_len == 0)
-		{
+	if (ndn->ndn_nud_state == NUD_INCOMPLETE || override) {
+		if (opt_len == 0) {
 			printk(KERN_DEBUG "no opt on NA\n");
-		}
-		else
-		{
-			/* record hardware address */
-
+		} else {
+			/* Record hardware address. */
 			ndn->ndn_flags |= NTF_COMPLETE;
 
 			if (ndisc_store_hwaddr(ndn, opt, opt_len,
-					       ND_OPT_TARGET_LL_ADDR))
-			{
+					       ND_OPT_TARGET_LL_ADDR)) {
 #if ND_DEBUG >= 2
 				printk(KERN_DEBUG
 				       "event_na: invalid TARGET_LL_ADDR\n");
@@ -903,42 +923,40 @@
 		}
 	}
 
-
-	if (solicited || override || ndn->ndn_nud_state == NUD_INCOMPLETE)
-	{
-
+	if (solicited || override || ndn->ndn_nud_state == NUD_INCOMPLETE) {
 		ndn->ndn_probes = 0;
 		ndn->ndn_tstamp = jiffies;
 
 		if (ndn->ndn_nud_state & NUD_IN_TIMER)
-		{
 			ndisc_del_timer(ndn);
-		}
 
 		if (solicited)
-		{
 			ndn->ndn_nud_state = NUD_REACHABLE;
-		}
 		else
-		{
 			ndn->ndn_nud_state = NUD_STALE;
-		}
 	}
 			
 	while ((skb=skb_dequeue(&ndn->neigh.arp_queue)))
-	{
 		dev_queue_xmit(skb);
-	}
 }
 
-static void ndisc_event_ns(struct in6_addr *saddr, struct sk_buff *skb)
+static struct nd_neigh * ndisc_event_ns(struct in6_addr *saddr,
+					struct sk_buff *skb)
 {
 	struct nd_neigh *ndn;
 	u8 *opt;
 	int len;
 
+	NDBG(("ndisc_event_ns: "));
+	if(saddr)
+		NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+		      saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+		      saddr->s6_addr16[6], saddr->s6_addr16[7]));
+	NDBG(("\n"));
+
 	opt = skb->h.raw;
-	opt += sizeof(struct icmpv6hdr) + sizeof(struct in6_addr);
+	opt += sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
 
 	len = skb->tail - opt;
 
@@ -949,124 +967,48 @@
 					       skb->dev);
 
 	if (ndn == NULL)
-	{
 		ndn = ndisc_new_neigh(skb->dev, saddr);
-	}
 
        	neigh_table_unlock(&nd_tbl);
 
+	if (ndn == NULL)
+		return NULL;
+
 	switch(ndn->ndn_nud_state) {
-		case NUD_REACHABLE:
-		case NUD_STALE:
-		case NUD_DELAY:
-			if (*opt != ND_OPT_SOURCE_LL_ADDR ||
-			    len != ndn->ndn_dev->addr_len ||
-			    memcmp(ndn->neigh.ha, opt + 2, len))
-			{
-				break;
-			}
+	case NUD_REACHABLE:
+	case NUD_STALE:
+	case NUD_DELAY:
+		if (*opt != ND_OPT_SOURCE_LL_ADDR ||
+		    len != ndn->ndn_dev->addr_len ||
+		    memcmp(ndn->neigh.ha, opt + 2, len))
+			break;
 
-			if (ndn->ndn_nud_state & NUD_IN_TIMER)
-			{
-				ndisc_del_timer(ndn);
-			}
-		default:
-			ndn->ndn_flags |= NTF_COMPLETE;
+		if (ndn->ndn_nud_state & NUD_IN_TIMER)
+			ndisc_del_timer(ndn);
+
+		/* FALLTHROUGH */
+	default:
+		ndn->ndn_flags |= NTF_COMPLETE;
 			
-			if (ndisc_store_hwaddr(ndn, opt, len,
-					       ND_OPT_SOURCE_LL_ADDR))
-			{
+		if (ndisc_store_hwaddr(ndn, opt, len, ND_OPT_SOURCE_LL_ADDR)) {
 #if ND_DEBUG >= 1
-				printk(KERN_DEBUG
-				       "event_ns: invalid SOURCE_LL_ADDR\n");
+			printk(KERN_DEBUG
+			       "event_ns: invalid SOURCE_LL_ADDR\n");
 #endif
 
-				ndn->ndn_flags &= ~NTF_COMPLETE;
-				ndn->ndn_nud_state = NUD_NONE;
-				return;
-			}
-
-			ndn->ndn_nud_state = NUD_STALE;
-			ndn->ndn_tstamp = jiffies;
-			ndn->ndn_probes = 0;
-	}
-
-}
-
-static struct rt6_info *ndisc_get_dflt_router(struct device *dev,
-					      struct in6_addr *addr)
-{	
-	struct rt6_info *iter;
-
-	for (iter = default_rt_list; iter; iter=iter->next)
-	{
-		if (dev == iter->rt_dev &&
-		    ipv6_addr_cmp(&iter->rt_dst, addr) == 0)
-		{
-			return iter;
+			ndn->ndn_flags &= ~NTF_COMPLETE;
+			ndn->ndn_nud_state = NUD_NONE;
+			return ndn;
 		}
-	}
-	return NULL;
-}
-
-static void ndisc_add_dflt_router(struct rt6_info *rt)
-{
-	struct rt6_info *iter;
 
-	rt->rt_ref++;
-	rt->fib_node = &routing_table;
-	rt6_stats.fib_rt_alloc++;
-
-	if (default_rt_list == NULL)
-	{
-		default_rt_list = rt;
-		return;
-	}
-
-	for (iter = default_rt_list; iter->next; iter=iter->next)
-		;
-
-	iter->next = rt;
-}
-
-static void ndisc_del_dflt_router(struct rt6_info *rt)
-{
-	struct rt6_info *iter, *back;
-
-	if (rt == default_rt_list)
-	{
-		default_rt_list = rt->next;
-	}
-	else
-	{
-		back = NULL;
-		for (iter = default_rt_list; iter; iter=iter->next)
-		{
-			if (iter == rt)
-			{
-				back->next = rt->next;
-				break;
-			}
-			back = iter;
-		}
-	}
+		ndn->ndn_nud_state = NUD_STALE;
+		ndn->ndn_tstamp = jiffies;
+		ndn->ndn_probes = 0;
+	};
 
-	rt->fib_node = NULL;
-	rt_release(rt);
+	return ndn;
 }
 
-static void ndisc_purge_dflt_routers(void)
-{
-	struct rt6_info *iter, *rt;
-
-	for (iter = default_rt_list; iter; )
-	{
-		rt = iter;
-		iter=iter->next;
-		rt_release(rt);
-	}
-	default_rt_list = NULL;
-}
 
 static void ndisc_ll_addr_update(struct nd_neigh *ndn, u8* opt, int len,
 				 int type)
@@ -1077,19 +1019,14 @@
 	case NUD_DELAY:
 		if (len == ndn->ndn_dev->addr_len &&
 		    memcmp(ndn->neigh.ha, opt + 2, len) == 0)
-		{
 			break;
-		}
 
 		if (ndn->ndn_nud_state & NUD_IN_TIMER)
-		{
 			ndisc_del_timer(ndn);
-		}
 	default:
 		ndn->ndn_flags |= NTF_COMPLETE;
 		
-		if (ndisc_store_hwaddr(ndn, opt, len, type))
-		{
+		if (ndisc_store_hwaddr(ndn, opt, len, type)) {
 #if ND_DEBUG >=1
 			printk(KERN_DEBUG "NDISC: invalid LL_ADDR\n");
 #endif
@@ -1101,45 +1038,7 @@
 		ndn->ndn_nud_state = NUD_STALE;
 		ndn->ndn_tstamp = jiffies;
 		ndn->ndn_probes = 0;
-	}
-	
-}
-
-struct rt6_info * dflt_rt_lookup(void)
-{
-	struct rt6_info *match = NULL;
-	struct rt6_info *rt;
-	int score = -1;
-	unsigned long now = jiffies;
-
-	for (rt = default_rt_list; rt; rt=rt->next)
-	{
-		struct neighbour *neigh = rt->rt_nexthop;
-		struct nd_neigh *ndn = (struct nd_neigh *) neigh;
-		
-		if (score < 0)
-		{
-			score = 0;
-			match = rt;
-		}
-
-		if (ndn->ndn_nud_state == NUD_REACHABLE)
-		{
-			if (score < 1)
-			{
-				score = 1;
-				match = rt;
-			}
-
-			if (now  - ndn->ndn_tstamp < nd_reachable_time)
-			{
-				return rt;
-			}
-		}
-
-	}
-
-	return match;
+	};
 }
 
 static void ndisc_router_discovery(struct sk_buff *skb)
@@ -1153,10 +1052,11 @@
 
 	__u8 * opt = (__u8 *)(ra_msg + 1);
 
+	NDBG(("ndisc_router_discovery(%p)\n", skb));
+
 	optlen = (skb->tail - skb->h.raw) - sizeof(struct ra_msg);
 
-	if (skb->nh.ipv6h->hop_limit != 255)
-	{
+	if (skb->nh.ipv6h->hop_limit != 255) {
 		printk(KERN_INFO
 		       "NDISC: fake router advertisment received\n");
 		return;
@@ -1167,14 +1067,12 @@
 	 */
 
 	in6_dev = ipv6_get_idev(skb->dev);
-	if (in6_dev == NULL)
-	{
+	if (in6_dev == NULL) {
 		printk(KERN_DEBUG "RA: can't find in6 device\n");
 		return;
 	}
 	
-	if (in6_dev->if_flags & IF_RS_SENT)
-	{
+	if (in6_dev->if_flags & IF_RS_SENT) {
 		/*
 		 *	flag that an RA was received after an RS was sent
 		 *	out on this interface.
@@ -1184,94 +1082,57 @@
 
 	lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
 
-	rt = ndisc_get_dflt_router(skb->dev, &skb->nh.ipv6h->saddr);
+	rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
 
-	if (rt && lifetime == 0)
-	{
-		ndisc_del_dflt_router(rt);
+	if (rt && lifetime == 0) {
+		ip6_del_rt(rt);
 		rt = NULL;
 	}
 
-	if (rt == NULL && lifetime)
-	{
-		struct in6_addr *saddr;
-		
+	if (rt == NULL && lifetime) {
 #if ND_DEBUG >= 2
-		printk(KERN_DEBUG "ndisc_rdisc: new default router\n");
+		printk(KERN_DEBUG "ndisc_rdisc: adding default router\n");
 #endif
 
-		rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
-						 GFP_ATOMIC);
+		rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
 
-		if (rt == NULL)
-		{
-			/* We are out-of-memory. Ignore it */
+		if (rt == NULL) {
+#if ND_DEBUG >= 1
+			printk(KERN_DEBUG "route_add failed\n");
+#endif
 			return;
 		}
 
-		saddr = &skb->nh.ipv6h->saddr;
-		neigh_table_lock(&nd_tbl);
-		
-		ndn = (struct nd_neigh *) neigh_lookup(&nd_tbl, saddr,
-						       sizeof(struct in6_addr),
-						       skb->dev);
-
-		if (ndn == NULL)
-		{
-			ndn = ndisc_new_neigh(skb->dev, saddr);
-			
-			if (ndn == NULL)
-			{
-				kfree(rt);
-				neigh_table_unlock(&nd_tbl);
-				return;
-			}
+		ndn = (struct nd_neigh *) rt->rt6i_nexthop;
+		if (ndn == NULL) {
+#if ND_DEBUG >= 1
+			printk(KERN_DEBUG "nd: add default router: null "
+			       "neighbour\n");
+#endif
+			return;
 		}
-		
-		neigh_table_unlock(&nd_tbl);
-
-		atomic_inc(&ndn->ndn_refcnt);
-
 		ndn->ndn_flags |= NCF_ROUTER;
-
-		memset(rt, 0, sizeof(struct rt6_info));
-
-		ipv6_addr_copy(&rt->rt_dst, &skb->nh.ipv6h->saddr);
-		rt->rt_metric = 1;
-		rt->rt_flags = RTF_GATEWAY | RTF_DYNAMIC;
-		rt->rt_dev = skb->dev;
-		rt->rt_nexthop = (struct neighbour *) ndn;
-
-		ndisc_add_dflt_router(rt);
 	}
 
 	if (rt)
-	{
-		rt->rt_expires = jiffies + (HZ * lifetime);
-        }
+		rt->rt6i_expires = jiffies + (HZ * lifetime);
 
 	if (ra_msg->icmph.icmp6_hop_limit)
-	{
-		ipv6_hop_limit = ra_msg->icmph.icmp6_hop_limit;
-	}
+		ipv6_config.hop_limit = ra_msg->icmph.icmp6_hop_limit;
 
 	/*
 	 *	Update Reachable Time and Retrans Timer
 	 */
 
 	if (ra_msg->retrans_timer)
-	{
-		nd_retrans_timer = ntohl(ra_msg->retrans_timer);
-	}
+		ipv6_config.nd_retrans_time = ntohl(ra_msg->retrans_timer);
 
-	if (ra_msg->reachable_time)
-	{
+	if (ra_msg->reachable_time) {
 		__u32 rtime = ntohl(ra_msg->reachable_time);
 
-		if (rtime != nd_base_reachable_time)
-		{
-			nd_base_reachable_time = rtime;
-			nd_gc_staletime	= 3 * nd_base_reachable_time;
+		if (rtime != ipv6_config.nd_base_reachable_time) {
+			ipv6_config.nd_base_reachable_time = rtime;
+			nd_gc_staletime	= 3 * rtime;
 			nd_reachable_time = rand_reach_time();
 		}
 		
@@ -1286,22 +1147,22 @@
 
                 len = (opt[1] << 3);
 
-		if (len == 0)
-		{
+		if (len == 0) {
 			printk(KERN_DEBUG "RA: opt has 0 len\n");
 			break;
 		}
 
                 switch(*opt) {
                 case ND_OPT_SOURCE_LL_ADDR:
-			
+
 			if (rt == NULL)
 				break;
 			
-			ndn = (struct nd_neigh *) rt->rt_nexthop;
+			ndn = (struct nd_neigh *) rt->rt6i_nexthop;
 
-			ndisc_ll_addr_update(ndn, opt, len,
-					     ND_OPT_SOURCE_LL_ADDR);
+			if (ndn)
+				ndisc_ll_addr_update(ndn, opt, len,
+						     ND_OPT_SOURCE_LL_ADDR);
 			break;
 
                 case ND_OPT_PREFIX_INFO:
@@ -1309,17 +1170,17 @@
                         break;
 
                 case ND_OPT_MTU:
-
-			if (rt)
-			{
+			if (rt) {
 				int mtu;
 				struct device *dev;
 				
 				mtu = htonl(*(__u32 *)(opt+4));
-				dev = rt->rt_nexthop->dev;
+				dev = rt->rt6i_dev;
 
-				if (mtu < 576)
-				{
+				if (dev == NULL)
+					break;
+
+				if (mtu < 576) {
 					printk(KERN_DEBUG "NDISC: router "
 					       "announcement with mtu = %d\n",
 					       mtu);
@@ -1327,13 +1188,9 @@
 				}
 
 				if (dev->change_mtu)
-				{
 					dev->change_mtu(dev, mtu);
-				}
 				else
-				{
 					dev->mtu = mtu;
-				}
 			}
                         break;
 
@@ -1343,32 +1200,32 @@
 			break;
 		default:
 			printk(KERN_DEBUG "unkown option in RA\n");
-                }
+                };
                 optlen -= len;
                 opt += len;
         }
-        
 }
 
 void ndisc_forwarding_on(void)
 {
+
 	/*
-	 *	forwarding was turned on
+	 *	Forwarding was turned on.
 	 */
 
-	ndisc_purge_dflt_routers();
+	rt6_purge_dflt_routers(0);
 }
 
 void ndisc_forwarding_off(void)
 {
 	/*
-	 *	forwarding was turned off
+	 *	Forwarding was turned off.
 	 */
 }
 
 static void ndisc_redirect_rcv(struct sk_buff *skb)
 {
-	struct icmpv6hdr *icmph;
+	struct icmp6hdr *icmph;
 	struct in6_addr *dest;
 	struct in6_addr *target;	/* new first hop to destination */
 	struct nd_neigh *ndn;
@@ -1377,75 +1234,66 @@
 	int optlen;
 	u8 * opt;
 
-	if (skb->nh.ipv6h->hop_limit != 255)
-	{
+	NDBG(("ndisc_redirect_rcv(%p)\n", skb));
+
+	if (skb->nh.ipv6h->hop_limit != 255) {
 		printk(KERN_WARNING
 		       "NDISC: fake ICMP redirect received\n");
 		return;
 	}
 
-	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL))
-	{
+	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) {
 		printk(KERN_WARNING
 		       "ICMP redirect: source address is not linklocal\n");
 		return;
 	}
 
 	optlen = skb->tail - skb->h.raw;
-	optlen -= sizeof(struct icmpv6hdr) + 2 * sizeof(struct in6_addr);
+	optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
 
-	if (optlen < 0)
-	{
+	if (optlen < 0) {
 		printk(KERN_WARNING "ICMP redirect: packet too small\n");
 		return;
 	}
 
-	icmph = (struct icmpv6hdr *) skb->h.raw;
+	icmph = (struct icmp6hdr *) skb->h.raw;
 	target = (struct in6_addr *) (icmph + 1);
 	dest = target + 1;
 
-	if (ipv6_addr_type(dest) & IPV6_ADDR_MULTICAST)
-	{
+	if (ipv6_addr_type(dest) & IPV6_ADDR_MULTICAST) {
 		printk(KERN_WARNING "ICMP redirect for multicast addr\n");
 		return;
 	}
 
-	if (ipv6_addr_cmp(dest, target) == 0)
-	{
+	if (ipv6_addr_cmp(dest, target) == 0) {
 		on_link = 1;
-	}
-	else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL))
-	{
+	} else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL)) {
 		printk(KERN_WARNING
 		       "ICMP redirect: target address is not linklocal\n");
 		return;
 	}
 
 	/* passed validation tests */
+	rt = rt6_redirect(dest, &skb->nh.ipv6h->saddr, target, skb->dev,
+			  on_link);
 
-	rt = ipv6_rt_redirect(skb->dev, dest, target, on_link);
-
-	if (rt == NULL)
-	{
+	if (rt == NULL) {
 		printk(KERN_WARNING "ICMP redirect: no route to host\n");
 		return;
 	}
 
-	ndn = (struct nd_neigh *) rt->rt_nexthop;
+	ndn = (struct nd_neigh *) rt->rt6i_nexthop;
 
 	opt = (u8 *) (dest + 1);
 
-	while (optlen > 0)
-	{
+	while (optlen > 0) {
 		int len;
 
 		len = (opt[1] << 3);
 
 		if (*opt == ND_OPT_TARGET_LL_ADDR)
-		{
 			ndisc_ll_addr_update(ndn, opt, len,
 					     ND_OPT_TARGET_LL_ADDR);
-		}
 
 		opt += len;
 		optlen -= len;
@@ -1456,12 +1304,13 @@
 			 struct in6_addr *target)
 {
 	struct sock *sk = ndisc_socket->sk;
-	int len = sizeof(struct icmpv6hdr) + 2 * sizeof(struct in6_addr);
+	int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
 	struct sk_buff *buff;
 	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
 	struct inet6_ifaddr *ifp;
-	struct icmpv6hdr *icmph;
+	struct icmp6hdr *icmph;
 	struct in6_addr *addrp;
+	struct device *dev;
 	struct rt6_info *rt;
 	int ta_len = 0;
 	u8 *opt;
@@ -1469,19 +1318,25 @@
 	int err;
 	int hlen;
 
-	rt = fibv6_lookup(&skb->nh.ipv6h->saddr, skb->dev, 0);
-	
-	if (rt->rt_flags & RTF_GATEWAY)
-	{
+	dev = skb->dev;
+	rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev, 0);
+
+	if (rt == NULL || rt->u.dst.error) {
+#if ND_DEBUG >= 1
+		printk(KERN_DEBUG "ndisc_send_redirect: hostunreach\n");
+#endif
+		return;
+	}
+
+	if (rt->rt6i_flags & RTF_GATEWAY) {
 #if ND_DEBUG >= 1
 		printk(KERN_DEBUG "ndisc_send_redirect: not a neighbour\n");
 #endif
 		return;
 	}
 
-	if (ndn->ndn_nud_state == NUD_REACHABLE)
-	{
-		ta_len  = ((neigh->dev->addr_len + 1) >> 3) + 1;
+	if (ndn->ndn_nud_state == NUD_REACHABLE) {
+		ta_len  = ((dev->addr_len + 1) >> 3) + 1;
 		len += (ta_len << 3);
 	}
 
@@ -1489,20 +1344,18 @@
 	rd_len &= ~0x7;
 	len += rd_len;
 
-	ifp = ipv6_get_lladdr(skb->dev);
+	ifp = ipv6_get_lladdr(dev);
 
-	if (ifp == NULL)
-	{
+	if (ifp == NULL) {
 #if ND_DEBUG >= 1
 		printk(KERN_DEBUG "redirect: no link_local addr for dev\n");
 #endif
 		return;
 	}
 
-	buff = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
-
-	if (buff == NULL)
-	{
+	buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+				   0, 0, &err);
+	if (buff == NULL) {
 #if ND_DEBUG >= 2
 		printk(KERN_DEBUG "ndisc_send_redirect: alloc_skb failed\n");
 #endif
@@ -1510,17 +1363,23 @@
 	}
 	
 	hlen = 0;
-	if (skb->dev->hard_header_len)
-	{
-		hlen = (skb->dev->hard_header_len + 15) & ~15;
-	}
 
-	skb_reserve(buff, hlen + sizeof(struct ipv6hdr));
+	if (dev->hard_header_len) {
+		skb_reserve(buff, (dev->hard_header_len + 15) & ~15);
+		if (dev->hard_header) {
+			dev->hard_header(buff, dev, ETH_P_IPV6, ndn->ndn_ha,
+					 NULL, len);
+			buff->arp = 1;
+		}
+	}
 	
-	icmph = (struct icmpv6hdr *) skb_put(buff, len);
+	ip6_nd_hdr(sk, buff, dev, &ifp->addr, &skb->nh.ipv6h->saddr,
+		   IPPROTO_ICMPV6, len);
+
+	icmph = (struct icmp6hdr *) skb_put(buff, len);
 
-	memset(icmph, 0, sizeof(struct icmpv6hdr));
-	icmph->type = NDISC_REDIRECT;
+	memset(icmph, 0, sizeof(struct icmp6hdr));
+	icmph->icmp6_type = NDISC_REDIRECT;
 
 	/*
 	 *	copy target and destination addresses
@@ -1537,8 +1396,7 @@
 	 *	include target_address option
 	 */
 
-	if (ta_len)
-	{
+	if (ta_len) {
 		int zb;
 		
 		*(opt++) = ND_OPT_TARGET_LL_ADDR;
@@ -1553,8 +1411,7 @@
 		 */
 
 		zb = (neigh->dev->addr_len + 2) & 0x7; 
-		if (zb)
-		{
+		if (zb) {
 			int comp;
 
 			comp = 8 - zb;
@@ -1574,12 +1431,11 @@
 
 	memcpy(opt, &skb->nh.ipv6h, rd_len - 8);
 	
-	icmph->checksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
-					  len, IPPROTO_ICMPV6,
-					  csum_partial((u8 *) icmph, len, 0));
+	icmph->icmp6_cksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
+					     len, IPPROTO_ICMPV6,
+					     csum_partial((u8 *) icmph, len, 0));
 
-	ipv6_xmit(sk, buff, &ifp->addr, &skb->nh.ipv6h->saddr, NULL,
-		  IPPROTO_ICMPV6);
+	dev_queue_xmit(buff);
 }
 
 /* Called by upper layers to validate neighbour cache entries. */
@@ -1587,14 +1443,15 @@
 void ndisc_validate(struct neighbour *neigh)
 {
 	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
-	
+
+	if (neigh == NULL)
+		return;
+
         if (ndn->ndn_nud_state == NUD_INCOMPLETE)
                 return;
 
         if (ndn->ndn_nud_state == NUD_DELAY) 
-	{
                 ndisc_del_timer(ndn);
-        }
 
         nd_stats.rcv_upper_conf++;
         ndn->ndn_nud_state = NUD_REACHABLE;
@@ -1609,26 +1466,30 @@
 	struct nd_neigh *ndn;
 	struct inet6_ifaddr *ifp;
 
-	switch (msg->icmph.type) {
+	NDBG(("ndisc_rcv(type=%d) ", msg->icmph.icmp6_type));
+	switch (msg->icmph.icmp6_type) {
 	case NDISC_NEIGHBOUR_SOLICITATION:
-		if ((ifp = ipv6_chk_addr(&msg->target)))
-		{
+		NDBG(("NS "));
+		if ((ifp = ipv6_chk_addr(&msg->target))) {
 			int addr_type;
 
-			if (ifp->flags & DAD_INCOMPLETE)
-			{
+			if (ifp->flags & DAD_INCOMPLETE) {
 				/*
-				 *	DAD failed 
+				 *	DAD failed
 				 */
 
-				printk(KERN_DEBUG "duplicate address\n");
+				/* XXX Check if this came in over same interface
+				 * XXX we just sent an NS from!  That is valid! -DaveM
+				 */
+
+				printk(KERN_DEBUG "%s: duplicate address\n",
+				       ifp->idev->dev->name);
 				del_timer(&ifp->timer);
 				return 0;
 			}
 
 			addr_type = ipv6_addr_type(saddr);
-			if (addr_type & IPV6_ADDR_UNICAST)
-			{
+			if (addr_type & IPV6_ADDR_UNICAST) {
 				int inc;
 
 				/* 
@@ -1637,25 +1498,18 @@
 				 */
 
 				nd_stats.rcv_probes_ucast++;
-				ndisc_event_ns(saddr, skb);
-
-				neigh_table_lock(&nd_tbl);
 
-				ndn = (struct nd_neigh *)
-					neigh_lookup(&nd_tbl, saddr,
-						     sizeof(struct in6_addr),
-						     dev);
+				ndn = ndisc_event_ns(saddr, skb);
 
-				neigh_table_unlock(&nd_tbl);
+				if (ndn == NULL)
+					return 0;
 
 				inc = ipv6_addr_type(daddr);
 				inc &= IPV6_ADDR_MULTICAST;
 
 				ndisc_send_na(dev, ndn, saddr, &ifp->addr, 
 					      ifp->idev->router, 1, inc, inc);
-			}
-			else
-			{
+			} else {
 #if ND_DEBUG >= 1
 				/* FIXME */
 				printk(KERN_DEBUG "ns: non unicast saddr\n");
@@ -1665,38 +1519,30 @@
 		break;
 
 	case NDISC_NEIGHBOUR_ADVERTISEMENT:
-		
+		NDBG(("NA "));
 		neigh_table_lock(&nd_tbl);	       
 		ndn = (struct nd_neigh *) 
 			neigh_lookup(&nd_tbl, (void *) &msg->target,
 				     sizeof(struct in6_addr), skb->dev);
 		neigh_table_unlock(&nd_tbl);
 
-		if (ndn)
-		{
-			if (ndn->ndn_flags & NCF_ROUTER)
-			{
-				if (msg->icmph.icmp6_router == 0)
-				{
+		if (ndn) {
+			if (ndn->ndn_flags & NCF_ROUTER) {
+				if (msg->icmph.icmp6_router == 0) {
 					/*
 					 *	Change: router to host
 					 */
-					
+#if 0					
 					struct rt6_info *rt;
 					rt = ndisc_get_dflt_router(skb->dev,
 								   saddr);
 					if (rt)
-					{
 						ndisc_del_dflt_router(rt);
-					}
+#endif
 				}
-			}
-			else
-			{
+			} else {
 				if (msg->icmph.icmp6_router)
-				{
 					ndn->ndn_flags |= NCF_ROUTER;
-				}
 			}
 			ndisc_event_na(ndn, (unsigned char *) &msg->opt,
 				       skb->tail - (u8 *)&msg->opt /*opt_len*/,
@@ -1705,24 +1551,28 @@
 		}
 		break;
 
-	}
+	};
 
-	if (ipv6_forwarding == 0)
-	{
-		switch (msg->icmph.type) {
+	if (ipv6_config.forwarding == 0) {
+		switch (msg->icmph.icmp6_type) {
 		case NDISC_ROUTER_ADVERTISEMENT:
-			ndisc_router_discovery(skb);
+			NDBG(("RA "));
+			if (ipv6_config.accept_ra)
+				ndisc_router_discovery(skb);
 			break;
 
 		case NDISC_REDIRECT:
-			ndisc_redirect_rcv(skb);
+			NDBG(("REDIR "));
+			if (ipv6_config.accept_redirects)
+				ndisc_redirect_rcv(skb);
 			break;
-		}
+		};
 	}
 
 	return 0;
 }
 
+#ifdef CONFIG_PROC_FS
 int ndisc_get_info(char *buffer, char **start, off_t offset, int length,
 		   int dummy)
 {
@@ -1732,8 +1582,7 @@
 
 	neigh_table_lock(&nd_tbl);
 
-	for (i = 0; i < nd_tbl.tbl_size; i++)
-	{
+	for (i = 0; i < nd_tbl.tbl_size; i++) {
 		struct neighbour *neigh, *head;
 		head = nd_tbl.hash_buckets[i];
 		
@@ -1744,36 +1593,37 @@
 			struct nd_neigh *ndn = (struct nd_neigh *) neigh;
 			int j;
 
-			for (j=0; j<16; j++)
-			{
+			for (j=0; j<16; j++) {
 				sprintf(buffer + len, "%02x",
 					ndn->ndn_addr.s6_addr[j]);
 				len += 2;
 			}
 
 			len += sprintf(buffer + len,
-				       " %02x %02x %08lx %08lx %04x %04lx ", i,
+				       " %02x %02x %02x %02x %08lx %08lx %08lx %04x %04x %04lx %8s ", i,
+				       ndn->ndn_plen,
+				       ndn->ndn_type,
 				       ndn->ndn_nud_state,
-				       ndn->ndn_expires - now,
+				       ndn->ndn_expires ? ndn->ndn_expires - now : 0,
 				       now - ndn->ndn_tstamp,
+				       nd_reachable_time,
+				       nd_gc_staletime,
 				       ndn->ndn_refcnt,
-				       ndn->ndn_flags);
+				       ndn->ndn_flags,
+				       ndn->ndn_dev ? ndn->ndn_dev->name : "NULLDEV");
 
-			if ((ndn->ndn_flags & NTF_COMPLETE))
-			{
-				for (j=0; j< neigh->dev->addr_len; j++)
-				{
+			if ((ndn->ndn_flags & NTF_COMPLETE)) {
+				for (j=0; j< neigh->dev->addr_len; j++) {
 					sprintf(buffer + len, "%02x",
 						neigh->ha[j]);
 					len += 2;
 				}
-			}
-			else
+			} else {
                                 len += sprintf(buffer + len, "000000000000");
+			}
 			len += sprintf(buffer + len, "\n");
 			
 			neigh = neigh->next;
-
 		} while (neigh != head);
 	}
 
@@ -1795,6 +1645,7 @@
         0, NULL,
         &ndisc_get_info
 };
+#endif	/* CONFIG_PROC_FS */
 
 void ndisc_init(struct net_proto_family *ops)
 {
@@ -1845,22 +1696,13 @@
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&ndisc_proc_entry);
 #endif
-#ifdef CONFIG_IPV6_MODULE
-	ndisc_eth_hook = ndisc_eth_resolv;
-#endif
 }
 
-#ifdef CONFIG_IPV6_MODULE
+#ifdef MODULE
 void ndisc_cleanup(void)
 {
-	ndisc_eth_hook = NULL;
-
 #ifdef CONFIG_PROC_FS
-#ifdef CONFIG_IPV6_MODULE
-        proc_unregister(&proc_net, ndisc_proc_entry.low_ino);
-#else
-	proc_net_unregister(PROC_NET_NDISC);
-#endif
+        proc_net_unregister(ndisc_proc_entry.low_ino);
 #endif
 	del_timer(&ndisc_gc_timer);
 	del_timer(&ndisc_timer);
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/proc.c linux/net/ipv6/proc.c
--- v2.1.29/linux/net/ipv6/proc.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ipv6/proc.c	Thu Mar 20 18:17:15 1997
@@ -0,0 +1,142 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		This file implements the various access functions for the
+ *		PROC file system.  This is very similar to the IPv4 version,
+ *		except it reports the sockets in the INET6 address family.
+ *
+ * Version:	$Id: proc.c,v 1.1 1997/03/16 08:47:18 davem Exp $
+ *
+ * Authors:	David S. Miller (davem@caip.rutgers.edu)
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ */
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <net/sock.h>
+#include <net/transp_v6.h>
+
+/* This is the main implementation workhorse of all these routines. */
+static int get__netinfo6(struct proto *pro, char *buffer, int format, char **start,
+			 off_t offset, int length)
+{
+	struct sock *sp;
+	struct tcp_opt *tp;
+	int timer_active, timer_active1, timer_active2;
+	unsigned long timer_expires;
+	struct in6_addr *dest, *src;
+	unsigned short destp, srcp;
+	int len = 0, i = 0;
+	off_t pos = 0;
+	off_t begin;
+	char tmpbuf[150];
+
+	if(offset < 149)
+		len += sprintf(buffer, "%-148s\n",
+			       "  sl  "						/* 6 */
+			       "local_address                         "		/* 38 */
+			       "remote_address                        "		/* 38 */
+			       "st tx_queue rx_queue tr tm->when retrnsmt"	/* 41 */
+			       "   uid  timeout inode");			/* 21 */
+										/*----*/
+										/*144 */
+
+	pos = 149;
+	SOCKHASH_LOCK();
+	sp = pro->sklist_next;
+	while(sp != (struct sock *)pro) {
+		pos += 149;
+		if(pos < offset)
+			goto next;
+		tp = &(sp->tp_pinfo.af_tcp);
+		dest  = &sp->net_pinfo.af_inet6.daddr;
+		src   = &sp->net_pinfo.af_inet6.rcv_saddr;
+		destp = ntohs(sp->dummy_th.dest);
+		srcp  = ntohs(sp->dummy_th.source);
+
+		timer_active1 = del_timer(&sp->retransmit_timer);
+		timer_active2 = del_timer(&sp->timer);
+		if(!timer_active1) sp->retransmit_timer.expires = 0;
+		if(!timer_active2) sp->timer.expires = 0;
+		timer_active = 0;
+		timer_expires = (unsigned) -1;
+		if(timer_active1 && sp->retransmit_timer.expires < timer_expires) {
+			timer_active = timer_active1;
+			timer_expires = sp->retransmit_timer.expires;
+		}
+		if(timer_active2 && sp->timer.expires < timer_expires) {
+			timer_active = timer_active2;
+			timer_expires = sp->timer.expires;
+		}
+		sprintf(tmpbuf, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
+			"%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
+			i,
+			src->s6_addr32[0], src->s6_addr32[1],
+			src->s6_addr32[2], src->s6_addr32[3], srcp,
+			dest->s6_addr32[0], dest->s6_addr32[1],
+			dest->s6_addr32[2], dest->s6_addr32[3], destp,
+			sp->state,
+			format==0?sp->write_seq-tp->snd_una:sp->wmem_alloc,
+			format==0?tp->rcv_nxt-sp->copied_seq:sp->rmem_alloc,
+			timer_active, timer_expires-jiffies, (unsigned) sp->retransmits,
+			sp->socket ? sp->socket->inode->i_uid:0,
+			timer_active?sp->timeout:0,
+			sp->socket ? sp->socket->inode->i_ino:0);
+
+		if(timer_active1) add_timer(&sp->retransmit_timer);
+		if(timer_active2) add_timer(&sp->timer);
+		len += sprintf(buffer+len, "%-148s\n", tmpbuf);
+		if(len >= length)
+			break;
+	next:
+		sp = sp->sklist_next;
+		i++;
+	}
+	SOCKHASH_UNLOCK();
+
+	begin = len - (pos - offset);
+	*start = buffer + begin;
+	len -= begin;
+	if(len > length)
+		len = length;
+	return len;
+}
+
+/* These get exported and registered with procfs in af_inet6.c at init time. */
+int tcp6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	return get__netinfo6(&tcpv6_prot, buffer, 0, start, offset, length);
+}
+
+int udp6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	return get__netinfo6(&udpv6_prot, buffer, 1, start, offset, length);
+}
+
+int raw6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	return get__netinfo6(&rawv6_prot, buffer, 1, start, offset, length);
+}
+
+int afinet6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	int len = 0;
+	len += sprintf(buffer+len, "TCP6: inuse %d highest %d\n",
+		       tcpv6_prot.inuse, tcpv6_prot.highestinuse);
+	len += sprintf(buffer+len, "UDP6: inuse %d highest %d\n",
+		       udpv6_prot.inuse, udpv6_prot.highestinuse);
+	len += sprintf(buffer+len, "RAW6: inuse %d highest %d\n",
+		       rawv6_prot.inuse, rawv6_prot.highestinuse);
+	*start = buffer + offset;
+	len -= offset;
+	if(len > length)
+		len = length;
+	return len;
+}
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/protocol.c linux/net/ipv6/protocol.c
--- v2.1.29/linux/net/ipv6/protocol.c	Sun Nov  3 01:04:46 1996
+++ linux/net/ipv6/protocol.c	Thu Mar 20 18:17:15 1997
@@ -1,3 +1,20 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		AF_INET6 protocol dispatch tables.
+ *
+ * Version:	$Id: protocol.c,v 1.5 1997/03/18 18:24:44 davem Exp $
+ *
+ * Authors:	Pedro Roque	<roque@di.fc.ul.pt>
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ */
+
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -27,8 +44,7 @@
 	struct inet6_protocol *p;
 
 	hash = prot & (MAX_INET_PROTOS - 1);
-	for (p = inet6_protos[hash] ; p != NULL; p=p->next) 
-	{
+	for (p = inet6_protos[hash] ; p != NULL; p=p->next) {
 		if (p->protocol == prot) 
 			return((struct inet6_protocol *) p);
 	}
@@ -41,7 +57,7 @@
 	struct inet6_protocol *p2;
 
 	hash = prot->protocol & (MAX_INET_PROTOS - 1);
-	prot ->next = inet6_protos[hash];
+	prot->next = inet6_protos[hash];
 	inet6_protos[hash] = prot;
 	prot->copy = 0;
 
@@ -50,10 +66,8 @@
 	 */
 	 
 	p2 = (struct inet6_protocol *) prot->next;
-	while(p2 != NULL) 
-	{
-		if (p2->protocol == prot->protocol) 
-		{
+	while(p2 != NULL) {
+		if (p2->protocol == prot->protocol) {
 			prot->copy = 1;
 			break;
 		}
@@ -72,22 +86,19 @@
 	unsigned char hash;
 
 	hash = prot->protocol & (MAX_INET_PROTOS - 1);
-	if (prot == inet6_protos[hash]) 
-	{
+	if (prot == inet6_protos[hash]) {
 		inet6_protos[hash] = (struct inet6_protocol *) inet6_protos[hash]->next;
 		return(0);
 	}
 
 	p = (struct inet6_protocol *) inet6_protos[hash];
-	while(p != NULL) 
-	{
+	while(p != NULL) {
 		/*
 		 * We have to worry if the protocol being deleted is
 		 * the last one on the list, then we may need to reset
 		 * someone's copied bit.
 		 */
-		if (p->next != NULL && p->next == prot) 
-		{
+		if (p->next != NULL && p->next == prot) {
 			/*
 			 * if we are the last one with this protocol and
 			 * there is a previous one, reset its copy bit.
@@ -104,9 +115,3 @@
 	}
 	return(-1);
 }
-
-/*
- * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h  -c -o protocol.o protocol.c"
- * End:
- */
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/raw.c linux/net/ipv6/raw.c
--- v2.1.29/linux/net/ipv6/raw.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv6/raw.c	Thu Mar 20 18:17:15 1997
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/raw.c
  *
- *	$Id: raw.c,v 1.8 1997/02/28 09:56:34 davem Exp $
+ *	$Id: raw.c,v 1.11 1997/03/18 18:24:46 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -29,13 +29,10 @@
 #include <net/sock.h>
 #include <net/snmp.h>
 
-#include <net/ip.h>
-#include <net/udp.h>
-
 #include <net/ipv6.h>
 #include <net/ndisc.h>
 #include <net/protocol.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
 #include <net/addrconf.h>
 #include <net/transp_v6.h>
 
@@ -189,15 +186,12 @@
 {
 	if (sk == NULL) 
 		return;
-
 }
 
 static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb)
 {
 	/* Charge it to the socket. */
-	
-	if (sock_queue_rcv_skb(sk,skb)<0)
-	{
+	if (sock_queue_rcv_skb(sk,skb)<0) {
 		/* ip_statistics.IpInDiscards++; */
 		kfree_skb(skb, FREE_READ);
 		return 0;
@@ -211,7 +205,7 @@
  *	This is next to useless... 
  *	if we demultiplex in network layer we don't need the extra call
  *	just to queue the skb... 
- *	maybe we could have the network decide uppon an hint if it 
+ *	maybe we could have the network decide uppon a hint if it 
  *	should call raw_rcv for demultiplexing
  */
 int rawv6_rcv(struct sk_buff *skb, struct device *dev,
@@ -222,22 +216,10 @@
 
 	sk = skb->sk;
 
-#if 1
-/*
- *	It was wrong for IPv4. It breaks NRL too [ANK]
- *	Actually i think this is the option that  does make more 
- *	sense with IPv6 nested headers. [Pedro]
- */
-
 	if (sk->ip_hdrincl)
-	{
 		skb->h.raw = skb->nh.raw;
-	}
-#else
-        skb->h.raw = skb->nh.raw;
-#endif
 
-	if (sk->users) {
+	if (sk->sock_readers) {
 		__skb_queue_tail(&sk->back_log, skb);
 		return 0;
 	}
@@ -283,8 +265,7 @@
 		return err;
 
 	/* Copy the address. */
-	if (sin6) 
-	{
+	if (sin6) {
 		sin6->sin6_family = AF_INET6;
 		memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr, 
 		       sizeof(struct in6_addr));
@@ -329,8 +310,7 @@
 	hdr->cksum = csum_partial_copy_fromiovecend(buff, hdr->iov, offset, 
 						    len, hdr->cksum);
 	
-	if (offset == 0)
-	{
+	if (offset == 0) {
 		struct sock *sk;
 		struct raw6_opt *opt;
 		struct in6_addr *daddr;
@@ -339,26 +319,19 @@
 		opt = &sk->tp_pinfo.tp_raw;
 
 		if (hdr->daddr)
-		{
 			daddr = hdr->daddr;
-		}
 		else
-		{
 			daddr = addr + 1;
-		}
 		
 		hdr->cksum = csum_ipv6_magic(addr, daddr, hdr->len,
 					     hdr->proto, hdr->cksum);
 		
-		if (opt->offset < len)
-		{
+		if (opt->offset < len) {
 			__u16 *csum;
 
 			csum = (__u16 *) (buff + opt->offset);
 			*csum = hdr->cksum;
-		}
-		else
-		{
+		} else {
 			/* 
 			 *  FIXME 
 			 *  signal an error to user via sk->err
@@ -378,11 +351,12 @@
 	struct ipv6_options *opt = NULL;
 	struct device *dev = NULL;
 	struct in6_addr *saddr = NULL;
+	struct flowi fl;
 	int addr_len = msg->msg_namelen;
 	struct in6_addr *daddr;
 	struct raw6_opt *raw_opt;
+	int hlimit = -1;
 	u16 proto;
-	int hlimit = 0;
 	int err;
 	
 
@@ -396,8 +370,7 @@
 	 *	Get and verify the address. 
 	 */
 
-	if (sin6) 
-	{
+	if (sin6) {
 		if (addr_len < sizeof(struct sockaddr_in6)) 
 			return(-EINVAL);
 
@@ -415,14 +388,11 @@
 
 		daddr = &sin6->sin6_addr;
 		
-		if (np->dest && ipv6_addr_cmp(daddr, &np->daddr))
-		{
-			ipv6_dst_unlock(np->dest);
-			np->dest = NULL;
+		if (np->dst && ipv6_addr_cmp(daddr, &np->daddr)) {
+			dst_release(np->dst);
+			np->dst = NULL;
 		}		
-	}
-	else 
-	{
+	} else {
 		if (sk->state != TCP_ESTABLISHED) 
 			return(-EINVAL);
 		
@@ -430,8 +400,7 @@
 		daddr = &(sk->net_pinfo.af_inet6.daddr);
 	}
 
-	if (ipv6_addr_any(daddr))
-	{
+	if (ipv6_addr_any(daddr)) {
 		/* 
 		 * unspecfied destination address 
 		 * treated as error... is this correct ?
@@ -445,14 +414,12 @@
 	if (len + (sk->ip_hdrincl ? 0 : sizeof(struct ipv6hdr)) > 65535)
 		return -EMSGSIZE;
 
-	if (msg->msg_controllen)
-	{
+	if (msg->msg_controllen) {
 		opt = &opt_space;
 		memset(opt, 0, sizeof(struct ipv6_options));
 
 		err = datagram_send_ctl(msg, &dev, &saddr, opt, &hlimit);
-		if (err < 0)
-		{
+		if (err < 0) {
 			printk(KERN_DEBUG "invalid msg_control\n");
 			return err;
 		}		
@@ -460,9 +427,14 @@
 
 	raw_opt = &sk->tp_pinfo.tp_raw;
 
+	fl.proto = proto;
+	fl.nl_u.ip6_u.daddr = daddr;
+	fl.nl_u.ip6_u.saddr = saddr;
+	fl.dev = dev;
+	fl.uli_u.icmpt.type = 0;
+	fl.uli_u.icmpt.code = 0;
 	
-	if (raw_opt->checksum)
-	{
+	if (raw_opt->checksum) {
 		struct rawv6_fakehdr hdr;
 		
 		hdr.iov = msg->msg_iov;
@@ -472,23 +444,15 @@
 		hdr.proto = proto;
 
 		if (opt && opt->srcrt)
-		{
 			hdr.daddr = daddr;
-		}
 		else
-		{
 			hdr.daddr = NULL;
-		}
 
-		err = ipv6_build_xmit(sk, rawv6_frag_cksum, &hdr, daddr, len,
-				      saddr, dev, opt, proto, hlimit,
-				      msg->msg_flags&MSG_DONTWAIT);
-	}
-	else
-	{
-		err = ipv6_build_xmit(sk, rawv6_getfrag, msg->msg_iov, daddr,
-				      len, saddr, dev, opt, proto, hlimit,
-				      msg->msg_flags&MSG_DONTWAIT);
+		err = ip6_build_xmit(sk, rawv6_frag_cksum, &hdr, &fl, len,
+				     opt, hlimit, msg->msg_flags);
+	} else {
+		err = ip6_build_xmit(sk, rawv6_getfrag, msg->msg_iov, &fl, len,
+				     opt, hlimit, msg->msg_flags);
 	}
 
 	return err<0?err:len;
@@ -520,8 +484,7 @@
 	struct raw6_opt *opt = &sk->tp_pinfo.tp_raw;
 	int val, err;
 
-	switch(level)
-	{
+	switch(level) {
 		case SOL_RAW:
 			break;
 
@@ -536,7 +499,7 @@
 		default:
 			return ipv6_setsockopt(sk, level, optname, optval,
 					       optlen);
-	}
+	};
 
   	if (optval == NULL)
   		return(-EINVAL);
@@ -545,15 +508,11 @@
   	if(err)
   		return err;
 
-	switch (optname)
-	{
+	switch (optname) {
 		case IPV6_CHECKSUM:
-			if (val < 0)
-			{
+			if (val < 0) {
 				opt->checksum = 0;
-			}
-			else
-			{
+			} else {
 				opt->checksum = 1;
 				opt->offset = val;
 			}
@@ -572,11 +531,10 @@
 
 	sk->state = TCP_CLOSE;
 
-	if (np->dest)
-	{
-		ipv6_dst_unlock(np->dest);
-	}
+	if (np->dst)
+		dst_release(np->dst);
 
+	ipv6_sock_mc_close(sk);
 	destroy_sock(sk);
 }
 
@@ -616,10 +574,3 @@
 	0,				/* inuse */
 	0				/* highestinuse */
 };
-
-/*
- * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h  -c -o rawv6.o rawv6.c"
- *  c-file-style: "Linux"
- * End:
- */
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/reassembly.c linux/net/ipv6/reassembly.c
--- v2.1.29/linux/net/ipv6/reassembly.c	Thu Jan  2 04:07:39 1997
+++ linux/net/ipv6/reassembly.c	Thu Mar 20 18:17:15 1997
@@ -5,6 +5,8 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
+ *	$Id: reassembly.c,v 1.7 1997/03/18 18:24:47 davem Exp $
+ *
  *	Based on: net/ipv4/ip_fragment.c
  *
  *	This program is free software; you can redistribute it and/or
@@ -32,7 +34,6 @@
 #include <net/transp_v6.h>
 #include <net/rawv6.h>
 #include <net/ndisc.h>
-#include <net/ipv6_route.h>
 #include <net/addrconf.h>
 
 
@@ -61,9 +62,7 @@
 	int nh;
 
 	if (del_timer(&fq->timer))
-	{
 		expires = fq->timer.expires;
-	}
 
 	/*
 	 *	We queue the packet even if it's the last.
@@ -75,14 +74,12 @@
 	 */
 	reasm_queue(fq, *skb, fhdr);
 
-	if ((fhdr->frag_off & __constant_htons(0x0001)) == 0)
-	{
+	if ((fhdr->frag_off & __constant_htons(0x0001)) == 0) {
 		fq->last_in = 1;
 		fq->nhptr = nhptr;
 	}
 
-	if (fq->last_in)
-	{
+	if (fq->last_in) {
 		if ((nh = reasm_frag_1(fq, skb)))
 			return nh;
 	}
@@ -99,17 +96,13 @@
 	struct frag_hdr *fhdr = (struct frag_hdr *) ((*skb)->h.raw);
 	struct frag_queue *fq;
 	
-	for (fq = ipv6_frag_queue.next; fq != &ipv6_frag_queue; fq = fq->next)
-	{
+	for (fq = ipv6_frag_queue.next; fq != &ipv6_frag_queue; fq = fq->next) {
 		if (fq->id == fhdr->identification)
-		{			
 			return reasm_frag(fq, skb, nhptr,fhdr);
-		}
 	}
 	
 	create_frag_entry(*skb, dev, nhptr, fhdr);
 
-
 	return 0;
 }
 
@@ -118,8 +111,7 @@
 {
 	struct ipv6_frag *fp, *back;
 
-	for(fp = fq->fragments; fp; )
-	{
+	for(fp = fq->fragments; fp; ) {
 		kfree_skb(fp->skb, FREE_READ);		
 		back = fp;
 		fp=fp->next;
@@ -132,7 +124,6 @@
 	fq->prev = fq->next = NULL;
 	
 	kfree(fq);
-
 }
 
 static void frag_expire(unsigned long data)
@@ -146,13 +137,12 @@
 
 	frag = fq->fragments;
 
-	if (frag == NULL)
-	{
+	if (frag == NULL) {
 		printk(KERN_DEBUG "invalid fragment queue\n");
 		return;
 	}
 
-	icmpv6_send(frag->skb, ICMPV6_TIME_EXCEEDED, ICMPV6_EXC_FRAGTIME, 0,
+	icmpv6_send(frag->skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0,
 		    frag->skb->dev);
 	
 	fq_free(fq);
@@ -168,8 +158,7 @@
 	fq = (struct frag_queue *) kmalloc(sizeof(struct frag_queue), 
 					   GFP_ATOMIC);
 
-	if (fq == NULL)
-	{
+	if (fq == NULL) {
 		kfree_skb(skb, FREE_READ);
 		return;
 	}
@@ -188,8 +177,7 @@
 	fq->nexthdr = fhdr->nexthdr;
 
 
-	if ((fhdr->frag_off & __constant_htons(0x0001)) == 0)
-	{
+	if ((fhdr->frag_off & __constant_htons(0x0001)) == 0) {
 		fq->last_in = 1;
 		fq->nhptr = nhptr;
 	}
@@ -212,13 +200,11 @@
 	nfp = (struct ipv6_frag *) kmalloc(sizeof(struct ipv6_frag), 
 					   GFP_ATOMIC);
 
-	if (nfp == NULL)
-	{
+	if (nfp == NULL) {
 		kfree_skb(skb, FREE_READ);
 		return;
 	}
 
-	
 	nfp->offset = ntohs(fhdr->frag_off) & ~0x7;
 	nfp->len = (ntohs(skb->nh.ipv6h->payload_len) -
 		    ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
@@ -231,18 +217,14 @@
 
 	bptr = &fq->fragments;
 	
-
-	for (fp = fq->fragments; fp; fp=fp->next)
-	{
+	for (fp = fq->fragments; fp; fp=fp->next) {
 		if (nfp->offset <= fp->offset)
 			break;
 		bptr = &fp->next;
 	}
 	
-	if (fp && fp->offset == nfp->offset)
-	{
-		if (fp->len != nfp->len)
-		{
+	if (fp && fp->offset == nfp->offset) {
+		if (fp->len != nfp->len) {
 			/* this cannot happen */
 			printk(KERN_DEBUG "reasm_queue: dup with wrong len\n");
 		}
@@ -253,7 +235,6 @@
 		return;
 	}
 	
-
 	*bptr = nfp;
 	nfp->next = fp;
 }
@@ -273,9 +254,7 @@
 	__u16  copy;
 	int    nh;
 
-
-	for(fp = fq->fragments; fp; fp=fp->next)
-	{
+	for(fp = fq->fragments; fp; fp=fp->next) {
 		if (offset != fp->offset)
 			return 0;
 
@@ -296,8 +275,7 @@
 
 	printk(KERN_DEBUG "reasm: payload len = %d\n", payload_len);
 
-	if ((skb = dev_alloc_skb(sizeof(struct ipv6hdr) + payload_len))==NULL)
-	{
+	if ((skb = dev_alloc_skb(sizeof(struct ipv6hdr) + payload_len))==NULL) {
 		printk(KERN_DEBUG "reasm_frag: no memory for reassembly\n");
 		fq_free(fq);
 		return 1;
@@ -309,7 +287,6 @@
 
 	skb->dev = fq->dev;
 
-	
 	nh = fq->nexthdr;
 
 	*(fq->nhptr) = nh;
@@ -325,8 +302,7 @@
 	 *	FIXME: If we don't have a checksum we ought to be able
 	 *	to defragment and checksum in this pass. [AC]
 	 */
-	for(fp = fq->fragments; fp; )
-	{
+	for(fp = fq->fragments; fp; ) {
 		struct ipv6_frag *back;
 
 		memcpy(skb_put(skb, fp->len), (__u8*)(fp->fhdr + 1), fp->len);
@@ -345,12 +321,3 @@
 
 	return nh;
 }
-
-
-
-/*
- * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h  -c -o reassembly.o reassembly.c"
- * c-file-style: "Linux"
- * End:
- */
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/route.c linux/net/ipv6/route.c
--- v2.1.29/linux/net/ipv6/route.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ipv6/route.c	Thu Mar 20 18:17:15 1997
@@ -0,0 +1,1599 @@
+/*
+ *	Linux INET6 implementation
+ *	FIB front-end.
+ *
+ *	Authors:
+ *	Pedro Roque		<roque@di.fc.ul.pt>	
+ *
+ *	$Id: route.c,v 1.9 1997/03/19 14:56:25 davem Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/route.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+
+#ifdef 	CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
+#include <net/snmp.h>
+#include <net/ipv6.h>
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+#include <net/ndisc.h>
+#include <net/addrconf.h>
+#include <net/netlink.h>
+
+#include <asm/uaccess.h>
+
+#undef CONFIG_RT6_POLICY
+
+/* Set to 3 to get tracing. */
+#define RT6_DEBUG 2
+
+#if RT6_DEBUG >= 3
+#define RDBG(x) printk x
+#else
+#define RDBG(x)
+#endif
+
+static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
+static struct dst_entry	*ip6_dst_reroute(struct dst_entry *dst,
+					 struct sk_buff *skb);
+
+static int		ip6_pkt_discard(struct sk_buff *skb);
+
+struct dst_ops ip6_dst_ops = {
+	AF_INET6,
+	ip6_dst_check,
+	ip6_dst_reroute,
+	NULL
+};
+
+struct rt6_info ip6_null_entry = {
+	{{NULL, 0, 0, NULL,
+	  0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL,
+	  ip6_pkt_discard, ip6_pkt_discard, &ip6_dst_ops}},
+	NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0UL,
+	0, {NULL}, {{{{0}}}, 128}, {{{{0}}}, 128}
+};
+
+struct fib6_node ip6_routing_table = {
+	NULL, NULL, NULL, NULL,
+	&ip6_null_entry,
+	0, RTN_ROOT|RTN_TL_ROOT, 0
+};
+
+#ifdef CONFIG_RT6_POLICY
+int	ip6_rt_policy = 0;
+
+struct pol_chain *rt6_pol_list = NULL;
+
+
+static int rt6_flow_match_in(struct rt6_info *rt, struct sk_buff *skb);
+static int rt6_flow_match_out(struct rt6_info *rt, struct sock *sk);
+
+static struct rt6_info	*rt6_flow_lookup(struct rt6_info *rt,
+					 struct in6_addr *daddr,
+					 struct in6_addr *saddr,
+					 struct fl_acc_args *args);
+
+#else
+#define ip6_rt_policy (0)
+#endif
+
+static atomic_t	rt6_tbl_lock	= 0;
+static int	rt6_bh_mask	= 0;
+
+#define RT_BH_REQUEST		1
+#define RT_BH_GC		2
+
+static void __rt6_run_bh(void);
+
+/*
+ *	request queue operations
+ *	FIFO queue/dequeue
+ */
+
+static struct rt6_req request_queue = {
+	0, NULL, &request_queue, &request_queue
+};
+
+static __inline__ void rtreq_queue(struct rt6_req * req)
+{
+	unsigned long flags;
+	struct rt6_req *next = &request_queue;
+
+	save_flags(flags);
+	cli();
+
+	req->prev = next->prev;
+	req->prev->next = req;
+	next->prev = req;
+	req->next = next;
+	restore_flags(flags);
+}
+
+static __inline__ struct rt6_req * rtreq_dequeue(void)
+{
+	struct rt6_req *next = &request_queue;
+	struct rt6_req *head;
+
+	head = next->next;
+
+	if (head == next)
+		return NULL;
+
+	head->next->prev = head->prev;
+	next->next = head->next;
+
+	head->next = NULL;
+	head->prev = NULL;
+
+	return head;
+}
+
+void rtreq_add(struct rt6_info *rt, int operation)
+{
+	struct rt6_req *rtreq;
+
+	rtreq = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
+	
+	if (rtreq == NULL)
+		return;
+
+	memset(rtreq, 0, sizeof(struct rt6_req));
+
+	rtreq->operation = operation;
+	rtreq->ptr = rt;
+	rtreq_queue(rtreq);
+
+	rt6_bh_mask |= RT_BH_REQUEST;
+}
+
+static __inline__ void rt6_lock(void)
+{
+	atomic_inc(&rt6_tbl_lock);
+}
+
+static __inline__ void rt6_unlock(void)
+{
+	if (atomic_dec_and_test(&rt6_tbl_lock) && rt6_bh_mask) {
+		start_bh_atomic();
+		__rt6_run_bh();
+		end_bh_atomic();
+	}
+}
+
+/*
+ *	Route lookup
+ */
+
+static __inline__ struct rt6_info *rt6_device_match(struct rt6_info *rt,
+						    struct device *dev,
+						    int strict)
+{
+	struct rt6_info *sprt;
+
+	RDBG(("rt6_device_match: (%p,%p,%d) ", rt, dev, strict));
+	if (dev) {
+		for (sprt = rt; sprt; sprt = sprt->u.next) {
+			if (sprt->rt6i_dev == dev) {
+				RDBG(("match --> %p\n", sprt));
+				return sprt;
+			}
+		}
+
+		if (strict) {
+			RDBG(("nomatch & STRICT --> ip6_null_entry\n"));
+			return &ip6_null_entry;
+		}
+	}
+	RDBG(("!dev or (no match and !strict) --> rt(%p)\n", rt));
+	return rt;
+}
+
+/*
+ *	pointer to the last default router chosen
+ */
+static struct rt6_info *rt6_dflt_pointer = NULL;
+
+static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, struct device *dev)
+{
+	struct rt6_info *match = NULL;
+	struct rt6_info *sprt;
+	int mpri = 0;
+
+	RDBG(("rt6_best_dflt(%p,%p): ", rt, dev));
+	for (sprt = rt; sprt; sprt = sprt->u.next) {
+		struct nd_neigh *ndn;
+
+		RDBG(("sprt(%p): ", sprt));
+		if ((ndn = (struct nd_neigh *) sprt->rt6i_nexthop)) {
+			int m = -1;
+
+			RDBG(("nxthop(%p,%d) ", ndn, ndn->ndn_nud_state));
+			switch (ndn->ndn_nud_state) {
+			case NUD_REACHABLE:
+				RDBG(("NUD_REACHABLE "));
+				if (sprt != rt6_dflt_pointer) {
+					rt = sprt;
+					RDBG(("sprt!=dflt_ptr -> %p\n",
+					      sprt));
+					goto out;
+				}
+				RDBG(("m=2, "));
+				m = 2;
+				break;
+
+			case NUD_DELAY:
+				RDBG(("NUD_DELAY, m=1, "));
+				m = 1;
+				break;
+
+			case NUD_STALE:
+				RDBG(("NUD_STALE, m=1, "));
+				m = 1;
+				break;
+			};
+
+			if (dev && sprt->rt6i_dev == dev) {
+				RDBG(("dev&&sprt->rt6i_dev==dev(%p), m+=2, ", dev));
+				m += 2;
+			}
+
+			if (m >= mpri) {
+				RDBG(("m>=mpri setmatch, "));
+				mpri = m;
+				match = sprt;
+			}
+		}
+	}
+
+	if (match) {
+		RDBG(("match, set rt, "));
+		rt = match;
+	} else {
+		/*
+		 *	No default routers are known to be reachable.
+		 *	SHOULD round robin
+		 */
+		RDBG(("!match, trying rt6_dflt_pointer, "));
+		if (rt6_dflt_pointer) {
+			struct rt6_info *next;
+
+			if ((next = rt6_dflt_pointer->u.next) &&
+			    next->u.dst.error == 0)
+				rt = next;
+		}
+	}
+
+out:
+	rt6_dflt_pointer = rt;
+	RDBG(("returning %p, dflt_ptr set\n", rt));
+	return rt;
+}
+
+struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr,
+			    struct device *dev, int flags)
+{
+	struct fib6_node *fn;
+	struct rt6_info *rt;
+
+	RDBG(("rt6_lookup(%p,%p,%p,%x) from %p\n",
+	      daddr, saddr, dev, flags, __builtin_return_address(0)));
+	rt6_lock();
+	fn = fib6_lookup(&ip6_routing_table, daddr, saddr);
+
+	rt = rt6_device_match(fn->leaf, dev, 0);
+	rt6_unlock();
+	return rt;
+}
+
+static struct rt6_info *rt6_cow(struct rt6_info *rt, struct in6_addr *daddr,
+				struct in6_addr *saddr)
+{
+	/*
+	 *	Clone the route.
+	 */
+
+	rt = ip6_rt_copy(rt);
+
+	if (rt) {
+		ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
+
+		rt->rt6i_dst.plen = 128;
+		rt->rt6i_flags |= RTF_CACHE;
+
+		if (rt->rt6i_src.plen) {
+			ipv6_addr_copy(&rt->rt6i_src.addr, saddr);
+			rt->rt6i_src.plen = 128;
+		}
+
+		rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, daddr);
+
+		rtreq_add(rt, RT_OPER_ADD);
+	} else {
+		rt = &ip6_null_entry;
+	}
+	return rt;
+}
+
+#ifdef CONFIG_RT6_POLICY
+static __inline__ struct rt6_info *rt6_flow_lookup_in(struct rt6_info *rt,
+						      struct sk_buff *skb)
+{
+	struct in6_addr *daddr, *saddr;
+	struct fl_acc_args arg;
+
+	arg.type = FL_ARG_FORWARD;
+	arg.fl_u.skb = skb;
+
+	saddr = &skb->nh.ipv6h->saddr;
+	daddr = &skb->nh.ipv6h->daddr;
+
+	return rt6_flow_lookup(rt, daddr, saddr, &arg);
+}
+
+static __inline__ struct rt6_info *rt6_flow_lookup_out(struct rt6_info *rt,
+						       struct sock *sk,
+						       struct flowi *fl)
+{
+	struct fl_acc_args arg;
+
+	arg.type = FL_ARG_ORIGIN;
+	arg.fl_u.fl_o.sk = sk;
+	arg.fl_u.fl_o.flow = fl;
+
+	return rt6_flow_lookup(rt, fl->nl_u.ip6_u.daddr, fl->nl_u.ip6_u.saddr,
+			       &arg);
+}
+
+#endif
+
+void ip6_route_input(struct sk_buff *skb)
+{
+	struct fib6_node *fn;
+	struct rt6_info *rt;
+	struct dst_entry *dst;
+
+	RDBG(("ip6_route_input(%p) from %p\n", skb, __builtin_return_address(0)));
+	rt6_lock();
+	fn = fib6_lookup(&ip6_routing_table, &skb->nh.ipv6h->daddr,
+			 &skb->nh.ipv6h->saddr);
+
+	rt = fn->leaf;
+
+	if ((rt->rt6i_flags & RTF_CACHE)) {
+		if (ip6_rt_policy == 0) {
+			rt = rt6_device_match(rt, skb->dev, 0);
+			goto out;
+		}
+
+#ifdef CONFIG_RT6_POLICY
+		if ((rt->rt6i_flags & RTF_FLOW)) {
+			struct rt6_info *sprt;
+
+			for (sprt = rt; sprt; sprt = sprt->u.next) {
+				if (rt6_flow_match_in(sprt, skb)) {
+					rt = sprt;
+					goto out;
+				}
+			}
+		}
+#endif
+	}
+
+	rt = rt6_device_match(rt, skb->dev, 0);
+
+	if (ip6_rt_policy == 0) {
+		if (!rt->rt6i_nexthop && rt->rt6i_dev &&
+		    ((rt->rt6i_flags & RTF_NONEXTHOP) == 0)) {
+			rt = rt6_cow(rt, &skb->nh.ipv6h->daddr,
+				     &skb->nh.ipv6h->saddr);
+		}
+	} else {
+#ifdef CONFIG_RT6_POLICY
+		rt = rt6_flow_lookup_in(rt, skb);
+#endif
+	}
+
+out:
+	dst = dst_clone((struct dst_entry *) rt);
+	rt6_unlock();
+
+	skb->dst = dst;
+	dst->input(skb);
+}
+
+struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl)
+{
+	struct fib6_node *fn;
+	struct rt6_info *rt;
+	struct dst_entry *dst;
+	int strict;
+
+	RDBG(("ip6_route_output(%p,%p) from(%p)", sk, fl,
+	      __builtin_return_address(0)));
+	strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & IPV6_ADDR_MULTICAST;
+
+	rt6_lock();
+#if RT6_DEBUG >= 3
+	RDBG(("lkup("));
+	if(fl->nl_u.ip6_u.daddr) {
+		struct in6_addr *addr = fl->nl_u.ip6_u.daddr;
+		int i;
+		RDBG(("daddr["));
+		for(i = 0; i < 8; i++) {
+			RDBG(("%04x%c", addr->s6_addr16[i],
+			      i == 7 ? ']' : ':'));
+		}
+	}
+	if(fl->nl_u.ip6_u.saddr) {
+		struct in6_addr *addr = fl->nl_u.ip6_u.saddr;
+		int i;
+		RDBG(("saddr["));
+		for(i = 0; i < 8; i++) {
+			RDBG(("%04x%c", addr->s6_addr16[i],
+			      i == 7 ? ']' : ':'));
+		}
+	}
+#endif
+	fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr,
+			 fl->nl_u.ip6_u.saddr);
+
+	RDBG(("-->(%p[%s])) ", fn, fn == &ip6_routing_table ? "ROOT" : "!ROOT"));
+
+	rt = fn->leaf;
+
+	if ((rt->rt6i_flags & RTF_CACHE)) {
+		RDBG(("RTF_CACHE "));
+		if (ip6_rt_policy == 0) {
+			rt = rt6_device_match(rt, fl->dev, strict);
+			RDBG(("devmatch(%p) ", rt));
+			goto out;
+		}
+
+#ifdef CONFIG_RT6_POLICY
+		if ((rt->rt6i_flags & RTF_FLOW)) {
+			struct rt6_info *sprt;
+
+			for (sprt = rt; sprt; sprt = sprt->u.next) {
+				if (rt6_flow_match_out(sprt, sk)) {
+					rt = sprt;
+					goto out;
+				}
+			}
+		}
+#endif
+	}
+	RDBG(("!RTF_CACHE "));
+	if (rt->rt6i_flags & RTF_DEFAULT) {
+		RDBG(("RTF_DEFAULT "));
+		if (rt->rt6i_metric >= IP6_RT_PRIO_ADDRCONF) {
+			rt = rt6_best_dflt(rt, fl->dev);
+			RDBG(("best_dflt(%p) ", rt));
+		}
+	} else {
+		rt = rt6_device_match(rt, fl->dev, strict);
+		RDBG(("!RTF_DEFAULT devmatch(%p) ", rt));
+	}
+
+	if (ip6_rt_policy == 0) {
+		if (!rt->rt6i_nexthop && rt->rt6i_dev &&
+		    ((rt->rt6i_flags & RTF_NONEXTHOP) == 0)) {
+			rt = rt6_cow(rt, fl->nl_u.ip6_u.daddr,
+				     fl->nl_u.ip6_u.saddr);
+			RDBG(("(!nhop&&rt6i_dev&&!RTF_NONEXTHOP) cow(%p) ", rt));
+		}
+	} else {
+#ifdef CONFIG_RT6_POLICY
+		rt = rt6_flow_lookup_out(rt, sk, fl);
+#endif
+	}
+
+out:
+	dst = dst_clone((struct dst_entry *) rt);
+	rt6_unlock();
+	RDBG(("dclone/ret(%p)\n", dst));
+	return dst;
+}
+
+
+void rt6_ins(struct rt6_info *rt)
+{
+	start_bh_atomic();
+	if (rt6_tbl_lock == 1)
+		fib6_add(&ip6_routing_table, rt);
+	else
+		rtreq_add(rt, RT_OPER_ADD);
+	end_bh_atomic();
+}
+
+/*
+ *	Destination cache support functions
+ */
+
+struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
+{
+	struct rt6_info *rt;
+
+	RDBG(("ip6dstchk(%p,%08x)[%p]\n", dst, cookie,
+	      __builtin_return_address(0)));
+
+	rt = (struct rt6_info *) dst;
+
+	if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
+		if (rt->rt6i_nexthop)
+			ndisc_event_send(rt->rt6i_nexthop, NULL);
+
+		return dst;
+	}
+
+	dst_release(dst);
+	return NULL;
+}
+
+struct dst_entry *ip6_dst_reroute(struct dst_entry *dst, struct sk_buff *skb)
+{
+	/*
+	 *	FIXME
+	 */
+	RDBG(("ip6_dst_reroute(%p,%p)[%p] (AIEEE)\n", dst, skb,
+	      __builtin_return_address(0)));
+	return NULL;
+}
+
+/*
+ *
+ */
+
+struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err)
+{
+	struct rt6_info *rt;
+	struct device *dev = NULL;
+	int addr_type;
+	
+	RDBG(("ip6_route_add(%p)[%p] ", rtmsg, __builtin_return_address(0)));
+	*err = 0;
+	
+	rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
+
+	if (rt == NULL) {
+		RDBG(("dalloc fails, "));
+		*err = -ENOMEM;
+		goto out;
+	}
+
+	/*
+	 *	default... this should be chosen according to route flags
+	 */
+
+#if RT6_DEBUG >= 3
+	{
+		struct in6_addr *addr = &rtmsg->rtmsg_dst;
+		int i;
+
+		RDBG(("daddr["));
+		for(i = 0; i < 8; i++) {
+			RDBG(("%04x%c", addr->s6_addr16[i],
+			      i == 7 ? ']' : ':'));
+		}
+		addr = &rtmsg->rtmsg_src;
+		RDBG(("saddr["));
+		for(i = 0; i < 8; i++) {
+			RDBG(("%04x%c", addr->s6_addr16[i],
+			      i == 7 ? ']' : ':'));
+		}
+	}
+#endif
+
+	addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst);
+	
+	if (addr_type & IPV6_ADDR_MULTICAST) {
+		RDBG(("MCAST, "));
+		rt->u.dst.input = ip6_mc_input;
+	} else {
+		RDBG(("!MCAST "));
+		rt->u.dst.input = ip6_forward;
+	}
+	
+	rt->u.dst.output = dev_queue_xmit;
+	
+	if (rtmsg->rtmsg_ifindex)
+		dev = dev_get_by_index(rtmsg->rtmsg_ifindex);
+	if(dev)
+		RDBG(("d[%s] ", dev->name));
+
+	ipv6_addr_copy(&rt->rt6i_dst.addr, &rtmsg->rtmsg_dst);
+	rt->rt6i_dst.plen = rtmsg->rtmsg_dst_len;
+
+	/* XXX Figure out what really is supposed to be happening here -DaveM */
+	ipv6_addr_copy(&rt->rt6i_src.addr, &rtmsg->rtmsg_src);
+	rt->rt6i_src.plen = rtmsg->rtmsg_src_len;
+	
+	if ((rt->rt6i_src.plen = rtmsg->rtmsg_src_len)) {
+		RDBG(("splen, "));
+		ipv6_addr_copy(&rt->rt6i_src.addr, &rtmsg->rtmsg_src);
+	} else {
+		RDBG(("!splen, "));
+	}
+	/* XXX */
+
+	if (rtmsg->rtmsg_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
+		struct rt6_info *grt;
+		struct in6_addr *gw_addr;
+		u32 flags = 0;
+
+		RDBG(("RTF_GATEWAY, "));
+		/*
+		 *	1. gateway route lookup
+		 *	2. ndisc_get_neigh
+		 */
+
+		gw_addr = &rtmsg->rtmsg_gateway;
+
+#if RT6_DEBUG >= 3
+		{
+			struct in6_addr *addr = gw_addr;
+			int i;
+
+			RDBG(("gwaddr["));
+			for(i = 0; i < 8; i++) {
+				RDBG(("%04x%c", addr->s6_addr16[i],
+				      i == 7 ? ']' : ':'));
+			}
+		}
+#endif
+
+		if ((rtmsg->rtmsg_flags & RTF_GATEWAY) &&
+		    (rtmsg->rtmsg_flags & RTF_ADDRCONF) == 0) {
+			RDBG(("RTF_GATEWAY && !RTF_ADDRCONF, "));
+			if (dev)
+				flags |= RTF_LINKRT;
+
+			grt = rt6_lookup(gw_addr, NULL, dev, flags);
+
+			if (grt == NULL)
+			{
+				RDBG(("!grt, "));
+				*err = -EHOSTUNREACH;
+				goto out;
+			}
+			dev = grt->rt6i_dev;
+			RDBG(("grt(d=%s), ", dev ? dev->name : "NULL"));
+		}
+
+		rt->rt6i_nexthop = ndisc_get_neigh(dev, gw_addr);
+
+		if (rt->rt6i_nexthop == NULL) {
+			RDBG(("!nxthop, "));
+			*err = -ENOMEM;
+			goto out;
+		}
+		RDBG(("nxthop, "));
+	}
+
+	if (dev == NULL) {
+		RDBG(("!dev, "));
+		*err = -ENODEV;
+		goto out;
+	}
+
+	rt->rt6i_metric = rtmsg->rtmsg_metric;
+
+	rt->rt6i_dev = dev;
+	rt->u.dst.pmtu = dev->mtu;
+	rt->rt6i_flags = rtmsg->rtmsg_flags;
+
+	RDBG(("rt6ins(%p) ", rt));
+
+	rt6_lock();
+	rt6_ins(rt);
+	rt6_unlock();
+
+out:
+	if (*err) {
+		RDBG(("dfree(%p) ", rt));
+		dst_free((struct dst_entry *) rt);
+		rt = NULL;
+	}
+	RDBG(("ret(%p)\n", rt));
+	return rt;
+}
+
+int ip6_del_rt(struct rt6_info *rt)
+{
+	rt6_lock();
+
+	start_bh_atomic();
+
+	rt6_dflt_pointer = NULL;
+
+	if (rt6_tbl_lock == 1)
+		fib6_del(rt);
+	else
+		rtreq_add(rt, RT_OPER_DEL);
+	end_bh_atomic();
+	rt6_unlock();
+	return 0;
+}
+
+int ip6_route_del(struct in6_rtmsg *rtmsg)
+{
+	return 0;
+}
+
+
+/*
+ *	bottom handler, runs with atomic_bh protection
+ */
+void __rt6_run_bh(void)
+{
+	struct rt6_req *rtreq;
+
+	while ((rtreq = rtreq_dequeue())) {
+		switch (rtreq->operation) {
+		case RT_OPER_ADD:
+			fib6_add(&ip6_routing_table, rtreq->ptr);
+			break;
+		case RT_OPER_DEL:
+			fib6_del(rtreq->ptr);
+			break;
+		};
+		kfree(rtreq);
+	}
+	rt6_bh_mask = 0;
+}
+
+/*
+ *	NETLINK interface
+ *	routing socket moral equivalent
+ */
+
+static int rt6_msgrcv(int unit, struct sk_buff *skb)
+{
+	int count = 0;
+	struct in6_rtmsg *rtmsg;
+	int err;
+
+	while (skb->len) {
+		if (skb->len < sizeof(struct in6_rtmsg)) {
+			count = -EINVAL;
+			goto out;
+		}
+		
+		rtmsg = (struct in6_rtmsg *) skb->data;
+		skb_pull(skb, sizeof(struct in6_rtmsg));
+		count += sizeof(struct in6_rtmsg);
+
+		switch (rtmsg->rtmsg_type) {
+		case RTMSG_NEWROUTE:
+			ip6_route_add(rtmsg, &err);
+			break;
+		case RTMSG_DELROUTE:
+			ip6_route_del(rtmsg);
+			break;
+		default:
+			count = -EINVAL;
+			goto out;
+		};
+	}
+
+out:
+	kfree_skb(skb, FREE_READ);	
+	return count;
+}
+
+static void rt6_sndrtmsg(struct in6_rtmsg *rtmsg)
+{
+	struct sk_buff *skb;
+	
+	skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
+	if (skb == NULL)
+		return;
+
+	memcpy(skb_put(skb, sizeof(struct in6_rtmsg)), &rtmsg,
+	       sizeof(struct in6_rtmsg));
+	
+	if (netlink_post(NETLINK_ROUTE6, skb))
+		kfree_skb(skb, FREE_WRITE);
+}
+
+void rt6_sndmsg(int type, struct in6_addr *dst, struct in6_addr *src,
+		struct in6_addr *gw, struct device *dev, 
+		int dstlen, int srclen,	int metric, __u32 flags)
+{
+	struct sk_buff *skb;
+	struct in6_rtmsg *msg;
+	
+	skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
+	if (skb == NULL)
+		return;
+
+	msg = (struct in6_rtmsg *) skb_put(skb, sizeof(struct in6_rtmsg));
+
+	memset(msg, 0, sizeof(struct in6_rtmsg));
+	
+	msg->rtmsg_type = type;
+
+	if (dst)
+		ipv6_addr_copy(&msg->rtmsg_dst, dst);
+
+	if (src) {
+		ipv6_addr_copy(&msg->rtmsg_src, src);
+		msg->rtmsg_src_len = srclen;
+	}
+
+	if (gw)
+		ipv6_addr_copy(&msg->rtmsg_gateway, gw);
+
+	msg->rtmsg_dst_len = dstlen;
+	msg->rtmsg_metric = metric;
+
+	if (dev)
+		msg->rtmsg_ifindex = dev->ifindex;
+
+	msg->rtmsg_flags = flags;
+
+	if (netlink_post(NETLINK_ROUTE6, skb))
+		kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ *	Handle redirects
+ */
+struct rt6_info *rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr,
+			      struct in6_addr *target, struct device *dev,
+			      int on_link)
+{
+	struct rt6_info *rt, *tgtr, *nrt;
+
+	RDBG(("rt6_redirect(%s)[%p]: ",
+	      dev ? dev->name : "NULL",
+	      __builtin_return_address(0)));
+	rt = rt6_lookup(dest, NULL, dev, 0);
+
+	if (rt == NULL || rt->u.dst.error) {
+		RDBG(("!rt\n"));
+		printk(KERN_DEBUG "rt6_redirect: no route to destination\n");
+		return NULL;
+	}
+
+	if (rt->rt6i_flags & RTF_GATEWAY) {
+		/*
+		 *	This can happen due to misconfiguration
+		 *	if we are dealing with an "on link" redirect.
+		 */
+		RDBG(("RTF_GATEWAY\n"));
+		printk(KERN_DEBUG "rt6_redirect: destination not directly "
+		       "connected\n");
+		return NULL;
+	}
+	RDBG(("tgt_lkup, "));
+	tgtr = rt6_lookup(target, NULL, dev, 0);
+
+	if (tgtr == NULL || tgtr->u.dst.error) {
+		/*
+		 *	duh?! no route to redirect target.
+		 *	How where we talking to it in the first place ?
+		 */
+		RDBG(("!tgtr||dsterr\n"));
+		printk(KERN_DEBUG "rt6_redirect: no route to target\n");
+		return NULL;
+	}
+
+	if ((tgtr->rt6i_flags & RTF_GATEWAY) &&
+	    ipv6_addr_cmp(dest, &tgtr->rt6i_gateway) == 0) {
+		RDBG(("tgt RTF_GATEWAY && dstmatch, dup\n"));
+		/*
+		 *	Check if we already have the right route.
+		 */
+#if RT6_DEBUG >= 1
+		printk(KERN_DEBUG "rt6_redirect: duplicate\n");
+#endif
+		return NULL;
+	}
+
+	/*
+	 *	RFC 1970 specifies that redirects should only be
+	 *	accepted if they come from the nexthop to the target.
+	 *	Due to the way default routers are chosen, this notion
+	 *	is a bit fuzzy and one might need to check all default
+	 *	routers.
+	 */
+
+	if (ipv6_addr_cmp(saddr, &tgtr->rt6i_gateway)) {
+		RDBG(("saddr/tgt->gway match, "));
+		if (tgtr->rt6i_flags & RTF_DEFAULT) {
+			tgtr = ip6_routing_table.leaf;
+
+			for (; tgtr; tgtr = tgtr->u.next) {
+				if (!ipv6_addr_cmp(saddr, &tgtr->rt6i_gateway)) {
+					RDBG(("found srcok, "));
+					goto source_ok;
+				}
+			}
+		}
+		RDBG(("!dflt||!srcok, "));
+		printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
+		       "for redirect target\n");
+	}
+
+source_ok:
+
+	/*
+	 *	We have finally decided to accept it.
+	 */
+	RDBG(("srcok: "));
+	if ((tgtr->rt6i_flags & RTF_HOST)) {
+		/*
+		 *	Already a host route.
+		 *
+		 */
+		RDBG(("hralready, "));
+		if (tgtr->rt6i_nexthop) {
+			RDBG(("nrel(nxthop) "));
+			neigh_release(tgtr->rt6i_nexthop);
+		}
+		/*
+		 *	purge hh_cache
+		 */
+		tgtr->rt6i_flags |= RTF_MODIFIED | RTF_CACHE;
+		ipv6_addr_copy(&tgtr->rt6i_gateway, dest);
+		tgtr->rt6i_nexthop = ndisc_get_neigh(tgtr->rt6i_dev, dest);
+		RDBG(("hhpurge, getnewneigh, ret(%p)\n", tgtr));
+		return tgtr;
+	}
+
+	nrt = ip6_rt_copy(tgtr);
+	nrt->rt6i_flags = RTF_GATEWAY|RTF_HOST|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
+
+	ipv6_addr_copy(&nrt->rt6i_dst.addr, target);
+	nrt->rt6i_dst.plen = 128;
+
+	ipv6_addr_copy(&nrt->rt6i_gateway, dest);
+	nrt->rt6i_nexthop = ndisc_get_neigh(nrt->rt6i_dev, dest);
+	nrt->rt6i_dev = dev;
+	nrt->u.dst.pmtu = dev->mtu;
+
+	RDBG(("rt6_ins(%p)\n", nrt));
+
+	rt6_lock();
+	rt6_ins(nrt);
+	rt6_unlock();
+
+	return nrt;
+}
+
+/*
+ *	Handle ICMP "packet too big" messages
+ *	i.e. Path MTU discovery
+ */
+
+void rt6_pmtu_discovery(struct in6_addr *addr, struct device *dev, int pmtu)
+{
+	struct rt6_info *rt;
+
+	if (pmtu < 576 || pmtu > 65536) {
+#if RT6_DEBUG >= 1
+		printk(KERN_DEBUG "rt6_pmtu_discovery: invalid MTU value %d\n",
+		       pmtu);
+#endif
+		return;
+	}
+
+	rt = rt6_lookup(addr, NULL, dev, 0);
+
+	if (rt == NULL || rt->u.dst.error) {
+#if RT6_DEBUG >= 2
+		printk(KERN_DEBUG "rt6_pmtu_discovery: no route to host\n");
+#endif
+		return;
+	}
+
+	if (rt->rt6i_flags & RTF_HOST) {
+		/*
+		 *	host route
+		 */
+		rt->u.dst.pmtu = pmtu;
+		rt->rt6i_flags |= RTF_MODIFIED;
+
+		return;
+	}
+
+	rt = ip6_rt_copy(rt);
+	ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
+	rt->rt6i_dst.plen = 128;
+
+	rt->rt6i_flags |= (RTF_HOST | RTF_DYNAMIC | RTF_CACHE);
+
+	rt6_lock();
+	rt6_ins(rt);
+	rt6_unlock();
+}
+
+/*
+ *	Misc support functions
+ */
+
+struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
+{
+	struct rt6_info *rt;
+
+	rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
+	
+	if (rt) {
+		rt->u.dst.input = ort->u.dst.input;
+		rt->u.dst.output = ort->u.dst.output;
+
+		rt->u.dst.pmtu = ort->u.dst.pmtu;
+		rt->rt6i_dev = ort->rt6i_dev;
+		
+		ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
+		rt->rt6i_keylen = ort->rt6i_keylen;
+		rt->rt6i_flags = ort->rt6i_flags;
+		rt->rt6i_metric = ort->rt6i_metric;
+		
+		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
+		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
+	}
+	return rt;
+}
+
+struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct device *dev)
+{	
+	struct rt6_info *rt;
+	struct fib6_node *fn;
+
+	RDBG(("rt6_get_dflt_router(%p,%p)[%p]", addr, dev,
+	      __builtin_return_address(0)));
+#if RT6_DEBUG >= 3
+	{
+		int i;
+
+		RDBG(("addr["));
+		for(i = 0; i < 8; i++) {
+			RDBG(("%04x%c", addr->s6_addr16[i],
+			      i == 7 ? ']' : ':'));
+		}
+	}
+#endif
+	RDBG(("\n"));
+	rt6_lock();
+
+	fn = &ip6_routing_table;
+
+	for (rt = fn->leaf; rt; rt=rt->u.next) {
+		if (dev == rt->rt6i_dev &&
+		    ipv6_addr_cmp(&rt->rt6i_dst.addr, addr) == 0)
+			break;
+	}
+
+	rt6_unlock();
+	return rt;
+}
+
+struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr,
+				     struct device *dev)
+{
+	struct in6_rtmsg rtmsg;
+	struct rt6_info *rt;
+	int err;
+
+	RDBG(("rt6_add_dflt_router(%p,%p)[%p] ", gwaddr, dev,
+	      __builtin_return_address(0)));
+#if RT6_DEBUG >= 3
+	{
+		struct in6_addr *addr = gwaddr;
+		int i;
+
+		RDBG(("gwaddr["));
+		for(i = 0; i < 8; i++) {
+			RDBG(("%04x%c", addr->s6_addr16[i],
+			      i == 7 ? ']' : ':'));
+		}
+	}
+#endif
+	RDBG(("\n"));
+
+	memset(&rtmsg, 0, sizeof(struct in6_rtmsg));
+	rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+	ipv6_addr_copy(&rtmsg.rtmsg_gateway, gwaddr);
+	rtmsg.rtmsg_metric = 1024;
+	rtmsg.rtmsg_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | RTF_UP;
+
+	rtmsg.rtmsg_ifindex = dev->ifindex;
+
+	rt = ip6_route_add(&rtmsg, &err);
+
+	if (err) {
+		printk(KERN_DEBUG "rt6_add_dflt: ip6_route_add error %d\n",
+		       err);
+	}
+	return rt;
+}
+
+void rt6_purge_dflt_routers(int last_resort)
+{
+	struct rt6_info *rt;
+	struct fib6_node *fn;
+	u32 flags;
+
+	RDBG(("rt6_purge_dflt_routers(%d)[%p]\n", last_resort,
+	      __builtin_return_address(0)));
+	fn = &ip6_routing_table;
+
+	rt6_dflt_pointer = NULL;
+
+	if (last_resort)
+		flags = RTF_ALLONLINK;
+	else
+		flags = RTF_DEFAULT | RTF_ADDRCONF;	
+
+	for (rt = fn->leaf; rt; ) {
+		if ((rt->rt6i_flags & flags)) {
+			struct rt6_info *drt;
+#if RT6_DEBUG >= 2
+			printk(KERN_DEBUG "rt6_purge_dflt: deleting entry\n");
+#endif
+			drt = rt;
+			rt = rt->u.next;
+			ip6_del_rt(drt);
+			continue;
+		}
+		rt = rt->u.next;
+	}
+}
+
+int ipv6_route_ioctl(unsigned int cmd, void *arg)
+{
+	struct in6_rtmsg rtmsg;
+	int err;
+
+	RDBG(("ipv6_route_ioctl(%d,%p)\n", cmd, arg));
+	switch(cmd) {
+	case SIOCADDRT:		/* Add a route */
+	case SIOCDELRT:		/* Delete a route */
+		if (!suser())
+			return -EPERM;
+		err = copy_from_user(&rtmsg, arg,
+				     sizeof(struct in6_rtmsg));
+		if (err)
+			return -EFAULT;
+			
+		switch (cmd) {
+		case SIOCADDRT:
+			ip6_route_add(&rtmsg, &err);
+			break;
+		case SIOCDELRT:
+			err = ip6_route_del(&rtmsg);
+			break;
+		default:
+			err = -EINVAL;
+		};
+
+		if (err == 0)
+				rt6_sndrtmsg(&rtmsg);
+		return err;
+	};
+
+	return -EINVAL;
+}
+
+/*
+ *	Drop the packet on the floor
+ */
+
+int ip6_pkt_discard(struct sk_buff *skb)
+{	
+	ipv6_statistics.Ip6OutNoRoutes++;
+	kfree_skb(skb, FREE_WRITE);
+	return 0;
+}
+
+/*
+ *	Add address
+ */
+
+int ip6_rt_addr_add(struct in6_addr *addr, struct device *dev)
+{
+	struct rt6_info *rt;
+
+	RDBG(("ip6_rt_addr_add(%p,%p)[%p]\n", addr, dev,
+	      __builtin_return_address(0)));
+#if RT6_DEBUG >= 3
+	{
+		int i;
+
+		RDBG(("addr["));
+		for(i = 0; i < 8; i++) {
+			RDBG(("%04x%c", addr->s6_addr16[i],
+			      i == 7 ? ']' : ':'));
+		}
+	}
+#endif
+	RDBG(("\n"));
+
+	rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
+	if (rt == NULL)
+		return -ENOMEM;
+	
+	memset(rt, 0, sizeof(struct rt6_info));
+	
+	rt->u.dst.input = ip6_input;
+	rt->u.dst.output = dev_queue_xmit;
+	rt->rt6i_dev = dev_get("lo");
+	rt->u.dst.pmtu = rt->rt6i_dev->mtu;
+
+	rt->rt6i_flags = RTF_HOST | RTF_LOCAL | RTF_UP | RTF_NONEXTHOP;
+	
+	ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
+	rt->rt6i_dst.plen = 128;
+
+	rt6_lock();
+	rt6_ins(rt);
+	rt6_unlock();
+
+	return 0;
+}
+
+#ifdef CONFIG_RT6_POLICY
+
+static int rt6_flow_match_in(struct rt6_info *rt, struct sk_buff *skb)
+{
+	struct flow_filter *frule;
+	struct pkt_filter *filter;
+	int res = 1;
+
+	if ((frule = rt->rt6i_filter) == NULL)
+		goto out;
+
+	if (frule->type != FLR_INPUT) {
+		res = 0;
+		goto out;
+	}
+
+	for (filter = frule->u.filter; filter; filter = filter->next) {
+		__u32 *word;
+
+		word = (__u32 *) skb->h.raw;
+		word += filter->offset;
+
+		if ((*word ^ filter->value) & filter->mask) {
+			res = 0;
+			break;
+		}
+	}
+
+out:
+	return res;
+}
+
+static int rt6_flow_match_out(struct rt6_info *rt, struct sock *sk)
+{
+	struct flow_filter *frule;
+	int res = 1;
+
+	if ((frule = rt->rt6i_filter) == NULL)
+		goto out;
+
+	if (frule->type != FLR_INPUT) {
+		res = 0;
+		goto out;
+	}
+
+	if (frule->u.sk != sk)
+		res = 0;
+out:
+	return res;
+}
+
+static struct rt6_info *rt6_flow_lookup(struct rt6_info *rt,
+					struct in6_addr *daddr,
+					struct in6_addr *saddr,
+					struct fl_acc_args *args)
+{
+	struct flow_rule *frule;
+	struct rt6_info *nrt = NULL;
+	struct pol_chain *pol;
+
+	for (pol = rt6_pol_list; pol; pol = pol->next) {
+		struct fib6_node *fn;
+		struct rt6_info *sprt;
+
+		fn = fib6_lookup(pol->rules, daddr, saddr);
+
+		do {
+			for (sprt = fn->leaf; sprt; sprt=sprt->u.next) {
+				int res;
+
+				frule = sprt->rt6i_flowr;
+#if RT6_DEBUG >= 2
+				if (frule == NULL) {
+					printk(KERN_DEBUG "NULL flowr\n");
+					goto error;
+				}
+#endif
+				res = frule->ops->accept(rt, sprt, args, &nrt);
+
+				switch (res) {
+				case FLOWR_SELECT:
+					goto found;
+				case FLOWR_CLEAR:
+					goto next_policy;
+				case FLOWR_NODECISION:
+					break;
+				default:
+					goto error;
+				};
+			}
+
+			fn = fn->parent;
+
+		} while ((fn->fn_flags & RTN_TL_ROOT) == 0);
+
+	next_policy:
+	}
+
+error:
+	return &ip6_null_entry;
+
+found:
+
+	if (nrt == NULL)
+		goto error;
+
+	nrt->rt6i_flags |= RTF_CACHE;
+	rt6_ins(nrt);
+
+	return nrt;
+}
+#endif
+
+/*
+ *	/proc
+ */
+
+#ifdef CONFIG_PROC_FS
+
+#define RT6_INFO_LEN (32 + 4 + 32 + 4 + 32 + 40 + 5 + 1)
+
+struct rt6_proc_arg {
+	char *buffer;
+	int offset;
+	int length;
+	int skip;
+	int len;
+};
+
+static void rt6_info_node(struct fib6_node *fn, void *p_arg)
+{
+	struct rt6_info *rt;
+	struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
+
+	for (rt = fn->leaf; rt; rt = rt->u.next) {
+		int i;
+
+		if (arg->skip < arg->offset / RT6_INFO_LEN) {
+			arg->skip++;
+			continue;
+		}
+
+		if (arg->len >= arg->length)
+			return;
+		
+		for (i=0; i<16; i++) {
+			sprintf(arg->buffer + arg->len, "%02x",
+				rt->rt6i_dst.addr.s6_addr[i]);
+			arg->len += 2;
+		}
+		arg->len += sprintf(arg->buffer + arg->len, " %02x ",
+				    rt->rt6i_dst.plen);
+
+		for (i=0; i<16; i++) {
+			sprintf(arg->buffer + arg->len, "%02x",
+				rt->rt6i_src.addr.s6_addr[i]);
+			arg->len += 2;
+		}
+		arg->len += sprintf(arg->buffer + arg->len, " %02x ",
+				    rt->rt6i_src.plen);
+		
+		if (rt->rt6i_nexthop) {
+			for (i=0; i<16; i++) {
+				struct nd_neigh *ndn;
+
+				ndn = (struct nd_neigh *) rt->rt6i_nexthop;
+				sprintf(arg->buffer + arg->len, "%02x",
+					ndn->ndn_addr.s6_addr[i]);
+				arg->len += 2;
+			}
+		} else {
+			sprintf(arg->buffer + arg->len,
+				"00000000000000000000000000000000");
+			arg->len += 32;
+		}
+		arg->len += sprintf(arg->buffer + arg->len,
+				    " %08lx %08x %08x %08lx %8s\n",
+				    rt->rt6i_metric, rt->rt6i_use,
+				    rt->rt6i_ref, rt->rt6i_flags, 
+				    rt->rt6i_dev ? rt->rt6i_dev->name : "");
+	}
+}
+
+static int rt6_proc_info(char *buffer, char **start, off_t offset, int length,
+			 int dummy)
+{
+	struct rt6_proc_arg arg;
+	arg.buffer = buffer;
+	arg.offset = offset;
+	arg.length = length;
+	arg.skip = 0;
+	arg.len = 0;
+
+	fib6_walk_tree(&ip6_routing_table, rt6_info_node, &arg,
+		       RT6_FILTER_RTNODES);
+
+	rt6_info_node(&ip6_routing_table, &arg);
+
+	*start = buffer;
+	if (offset)
+		*start += offset % RT6_INFO_LEN;
+
+	arg.len -= offset % RT6_INFO_LEN;
+
+	if(arg.len > length)
+		arg.len = length;
+	if(arg.len < 0)
+		arg.len = 0;
+
+	return arg.len;
+}
+
+#define PTR_SZ (sizeof(void *) * 2)
+#define FI_LINE_SZ (2 * (PTR_SZ) + 7 + 32 + 4 + 32 + 4)
+
+static void rt6_tree_node(struct fib6_node *fn, void *p_arg)
+{
+	struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
+	struct rt6_info *rt;
+	char f;
+	int i;
+
+	rt = fn->leaf;
+
+	if (arg->skip < arg->offset / FI_LINE_SZ) {
+		arg->skip++;
+		return;
+	}
+
+	if (arg->len + FI_LINE_SZ >= arg->length)
+		return;
+
+	f = (fn->fn_flags & RTN_RTINFO) ? 'r' : 'n';
+	arg->len += sprintf(arg->buffer + arg->len, "%p %p %02x %c ",
+			    fn, fn->parent, fn->fn_bit, f);
+
+	for (i=0; i<16; i++) {
+		sprintf(arg->buffer + arg->len, "%02x",
+			rt->rt6i_dst.addr.s6_addr[i]);
+		arg->len += 2;
+	}
+	arg->len += sprintf(arg->buffer + arg->len, " %02x ",
+			    rt->rt6i_dst.plen);
+	
+	for (i=0; i<16; i++) {
+		sprintf(arg->buffer + arg->len, "%02x",
+			rt->rt6i_src.addr.s6_addr[i]);
+		arg->len += 2;
+	}
+	arg->len += sprintf(arg->buffer + arg->len, " %02x\n",
+			    rt->rt6i_src.plen);
+
+}
+
+static int rt6_proc_tree(char *buffer, char **start, off_t offset, int length,
+			 int dummy)
+{
+	struct rt6_proc_arg arg;
+	arg.buffer = buffer;
+	arg.offset = offset;
+	arg.length = length;
+	arg.skip = 0;
+	arg.len = 0;
+
+	fib6_walk_tree(&ip6_routing_table, rt6_tree_node, &arg, 0);
+
+	*start = buffer;
+	if (offset)
+		*start += offset % RT6_INFO_LEN;
+
+	arg.len -= offset % RT6_INFO_LEN;
+
+	if(arg.len > length)
+		arg.len = length;
+	if(arg.len < 0)
+		arg.len = 0;
+
+	return arg.len;
+}
+
+extern struct rt6_statistics rt6_stats;
+
+static int rt6_proc_stats(char *buffer, char **start, off_t offset, int length,
+			  int dummy)
+{
+	int len;
+
+	len = sprintf(buffer, "%04x %04x %04x %04x %04x\n",
+		      rt6_stats.fib_nodes, rt6_stats.fib_route_nodes,
+		      rt6_stats.fib_rt_alloc, rt6_stats.fib_rt_entries,
+		      rt6_stats.fib_rt_cache);
+
+	len -= offset;
+
+	if (len > length)
+		len = length;
+	if(len < 0)
+		len = 0;
+
+	*start = buffer + offset;
+
+	return len;
+}
+
+static struct proc_dir_entry proc_rt6_info = {
+	PROC_NET_RT6, 10, "ipv6_route",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	rt6_proc_info
+};
+static struct proc_dir_entry proc_rt6_stats = {
+	PROC_NET_RT6_STATS, 9, "rt6_stats",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	rt6_proc_stats
+};
+static struct proc_dir_entry proc_rt6_tree = {
+	PROC_NET_RT6_TREE, 7, "ip6_fib",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	rt6_proc_tree
+};
+#endif	/* CONFIG_PROC_FS */
+
+void ip6_route_init(void)
+{
+#ifdef 	CONFIG_PROC_FS
+	proc_net_register(&proc_rt6_info);
+	proc_net_register(&proc_rt6_stats);
+	proc_net_register(&proc_rt6_tree);
+#endif
+	netlink_attach(NETLINK_ROUTE6, rt6_msgrcv);
+}
+
+#ifdef MODULE
+void ip6_route_cleanup(void)
+{
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_RT6);
+	proc_net_unregister(PROC_NET_RT6_TREE);
+	proc_net_unregister(PROC_NET_RT6_STATS);
+#endif
+	netlink_detach(NETLINK_ROUTE6);
+#if 0
+	fib6_flush();
+#endif
+}
+#endif	/* MODULE */
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/sit.c linux/net/ipv6/sit.c
--- v2.1.29/linux/net/ipv6/sit.c	Sun Jan 19 05:47:28 1997
+++ linux/net/ipv6/sit.c	Thu Mar 20 18:17:15 1997
@@ -5,6 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
+ *	$Id: sit.c,v 1.13 1997/03/18 18:24:50 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -30,7 +31,6 @@
 #include <net/protocol.h>
 #include <net/transp_v6.h>
 #include <net/ndisc.h>
-#include <net/ipv6_route.h>
 #include <net/addrconf.h>
 #include <net/ip.h>
 #include <net/udp.h>
@@ -57,7 +57,7 @@
 static int			sit_open(struct device *dev);
 static int			sit_close(struct device *dev);
 
-static struct enet_statistics *	sit_get_stats(struct device *dev);
+static struct net_device_stats *sit_get_stats(struct device *dev);
 
 extern void	udp_err(struct sk_buff *, unsigned char *);
 
@@ -118,10 +118,8 @@
 
 	hash = sit_addr_hash(addr);
 
-	for(iter = sit_mtu_cache[hash]; iter; iter=iter->next)
-	{
-		if (iter->addr == addr)
-		{
+	for(iter = sit_mtu_cache[hash]; iter; iter=iter->next) {
+		if (iter->addr == addr) {
 			iter->tstamp = jiffies;
 			break;
 		}
@@ -131,8 +129,7 @@
 	 *	run garbage collector
 	 */
 
-	if (jiffies - sit_gc_last_run > SIT_GC_FREQUENCY)
-	{
+	if (jiffies - sit_gc_last_run > SIT_GC_FREQUENCY) {
 		sit_mtu_cache_gc();
 		sit_gc_last_run = jiffies;
 	}
@@ -146,26 +143,19 @@
 	unsigned long now = jiffies;
 	int i;
 
-	for (i=0; i < SIT_NUM_BUCKETS; i++)
-	{
+	for (i=0; i < SIT_NUM_BUCKETS; i++) {
 		back = NULL;
-		for (iter = sit_mtu_cache[i]; iter;)
-		{
-			if (now - iter->tstamp > SIT_GC_TIMEOUT)
-			{
+		for (iter = sit_mtu_cache[i]; iter;) {
+			if (now - iter->tstamp > SIT_GC_TIMEOUT) {
 				struct sit_mtu_info *old;
 
 				old = iter;
 				iter = iter->next;
 
 				if (back)
-				{
 					back->next = iter;
-				}
 				else
-				{
 					sit_mtu_cache[i] = iter;
-				}
 
 				kfree(old);
 				continue;
@@ -186,12 +176,12 @@
 	dev->hard_start_xmit	= sit_xmit;
 	dev->get_stats		= sit_get_stats;
 
-	dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+	dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
 
 	if (dev->priv == NULL)
 		return -ENOMEM;
 
-	memset(dev->priv, 0, sizeof(struct enet_statistics));
+	memset(dev->priv, 0, sizeof(struct net_device_stats));
 
 
 	for (i = 0; i < DEV_NUMBUFFS; i++)
@@ -230,12 +220,12 @@
 	int i;
 
 	dev->flags = IFF_NOARP|IFF_POINTOPOINT|IFF_MULTICAST;
-	dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+	dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
 
 	if (dev->priv == NULL)
 		return -ENOMEM;
 
-	memset(dev->priv, 0, sizeof(struct enet_statistics));
+	memset(dev->priv, 0, sizeof(struct net_device_stats));
 
 	for (i = 0; i < DEV_NUMBUFFS; i++)
 		skb_queue_head_init(&dev->buffs[i]);
@@ -253,7 +243,6 @@
 	return 0;
 }
 
-
 int sit_init(void)
 {
 	int i;
@@ -261,9 +250,7 @@
 	/* register device */
 
 	if (register_netdev(&sit_device) != 0)
-	{
 		return -EIO;
-	}
 
 	inet_add_protocol(&sit_protocol);
 
@@ -314,9 +301,8 @@
 void sit_cleanup(void)
 {
 	struct sit_vif *vif;
-	
-	for (vif = vif_list; vif;)
-	{
+
+	for (vif = vif_list; vif;) {
 		struct device *dev = vif->dev;
 		struct sit_vif *cur;
 
@@ -335,8 +321,6 @@
 	
 }
 
-
-
 /*
  *	receive IPv4 ICMP messages
  */
@@ -347,8 +331,7 @@
 	int type = skb->h.icmph->type;
 	int code = skb->h.icmph->code;
 
-	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
-	{
+	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
 		struct sit_mtu_info *minfo;
 		unsigned short info = skb->h.icmph->un.frag.mtu - sizeof(struct iphdr);
 
@@ -356,8 +339,8 @@
 
 		printk(KERN_DEBUG "sit: %08lx pmtu = %ul\n", ntohl(iph->saddr),
 		       info);
-		if (minfo == NULL)
-		{
+
+		if (minfo == NULL) {
 			minfo = kmalloc(sizeof(struct sit_mtu_info),
 					GFP_ATOMIC);
 
@@ -367,9 +350,7 @@
 			start_bh_atomic();
 			sit_cache_insert(iph->daddr, info);
 			end_bh_atomic();
-		}
-		else
-		{
+		} else {
 			minfo->mtu = info;
 		}
 	}
@@ -377,7 +358,7 @@
 
 static int sit_rcv(struct sk_buff *skb, unsigned short len)
 {
-	struct enet_statistics *stats;
+	struct net_device_stats *stats;
 	struct device *dev = NULL;
 	struct sit_vif *vif;	
 	__u32  saddr = skb->nh.iph->saddr;
@@ -386,24 +367,21 @@
 
 	skb->protocol = __constant_htons(ETH_P_IPV6);
 
-	for (vif = vif_list; vif; vif = vif->next)
-	{
-		if (saddr == vif->dev->pa_dstaddr)
-		{
+	for (vif = vif_list; vif; vif = vif->next) {
+		if (saddr == vif->dev->pa_dstaddr) {
 			dev = vif->dev;
 			break;
 		}
 	}
 
 	if (dev == NULL)
-	{
 		dev = &sit_device;
-	}
 
 	skb->dev = dev;
 	skb->ip_summed = CHECKSUM_NONE;
 
-	stats = (struct enet_statistics *)dev->priv;
+	stats = (struct net_device_stats *)dev->priv;
+	stats->rx_bytes += len;
 	stats->rx_packets++;
 
 	ipv6_rcv(skb, dev, NULL);
@@ -412,7 +390,7 @@
 
 static int sit_xmit(struct sk_buff *skb, struct device *dev)
 {
-	struct enet_statistics *stats;
+	struct net_device_stats *stats;
 	struct sit_mtu_info *minfo;
 	struct in6_addr *addr6;	
 	struct rtable *rt;
@@ -427,16 +405,16 @@
 	 *	Make sure we are not busy (check lock variable) 
 	 */
 
-	stats = (struct enet_statistics *)dev->priv;
+	stats = (struct net_device_stats *)dev->priv;
 
 	daddr = dev->pa_dstaddr;
-	if (daddr == 0)
-	{
-		struct nd_neigh *neigh;
-
-		neigh = (struct nd_neigh *) skb->nexthop;
-		if (neigh == NULL)
-		{
+	if (daddr == 0) {
+		struct nd_neigh *neigh = NULL;
+
+		if (skb->dst)
+			neigh = (struct nd_neigh *) skb->dst->neighbour;
+
+		if (neigh == NULL) {
 			printk(KERN_DEBUG "sit: nexthop == NULL\n");
 			goto on_error;
 		}
@@ -444,14 +422,12 @@
 		addr6 = &neigh->ndn_addr;
 		addr_type = ipv6_addr_type(addr6);
 
-		if (addr_type == IPV6_ADDR_ANY)
-		{
+		if (addr_type == IPV6_ADDR_ANY) {
 			addr6 = &skb->nh.ipv6h->daddr;
 			addr_type = ipv6_addr_type(addr6);
 		}
 
-		if ((addr_type & IPV6_ADDR_COMPATv4) == 0)
-		{
+		if ((addr_type & IPV6_ADDR_COMPATv4) == 0) {
 			printk(KERN_DEBUG "sit_xmit: non v4 address\n");
 			goto on_error;
 		}
@@ -475,8 +451,7 @@
 #endif
 		mtu = rt->u.dst.pmtu;
 
-	if (mtu > 576 && skb->tail - (skb->data + sizeof(struct ipv6hdr)) > mtu)
-	{
+	if (mtu > 576 && skb->tail - (skb->data + sizeof(struct ipv6hdr)) > mtu) {
 		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
 		ip_rt_put(rt);
 		goto on_error;
@@ -522,25 +497,18 @@
 
 	ip_send(skb);
 
+	stats->tx_bytes += skb->len;
 	stats->tx_packets++;
 
 	return 0;
 
-  on_error:
+on_error:
 	dev_kfree_skb(skb, FREE_WRITE);
 	stats->tx_errors++;
 	return 0;	
 }
 
-static struct enet_statistics *sit_get_stats(struct device *dev)
+static struct net_device_stats *sit_get_stats(struct device *dev)
 {
-	return((struct enet_statistics*) dev->priv);
+	return((struct net_device_stats *) dev->priv);
 }
-
-
-/*
- * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h  -c -o sit.o sit.c"
- * c-file-style: "Linux"
- * End:
- */
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/sysctl_net_ipv6.c linux/net/ipv6/sysctl_net_ipv6.c
--- v2.1.29/linux/net/ipv6/sysctl_net_ipv6.c	Sun Nov  3 01:04:46 1996
+++ linux/net/ipv6/sysctl_net_ipv6.c	Thu Mar 20 18:17:15 1997
@@ -10,39 +10,105 @@
 #include <net/ipv6.h>
 #include <net/addrconf.h>
 
+struct ipv6_config ipv6_config =
+{
+	0,				/* forwarding		*/
+	IPV6_DEFAULT_HOPLIMIT,		/* hop limit		*/
+	1,				/* accept RAs		*/
+	1,				/* accept redirects	*/
+
+	3,				/* nd_max_mcast_solicit	*/
+	3,				/* nd_max_ucast_solicit	*/
+	RETRANS_TIMER,			/* nd_retrans_time	*/
+	RECHABLE_TIME,			/* nd_base_reach_time	*/
+	(5 * HZ),			/* nd_delay_probe_time	*/
+
+	1,				/* autoconfiguration	*/
+	1,				/* dad transmits	*/
+	MAX_RTR_SOLICITATIONS,		/* router solicits	*/
+	RTR_SOLICITATION_INTERVAL,	/* rtr solicit interval	*/
+	MAX_RTR_SOLICITATION_DELAY,	/* rtr solicit delay	*/
 
-int ipv6_hop_limit = IPV6_DEFAULT_HOPLIMIT;
+	60*HZ,				/* rt cache timeout	*/
+	30*HZ,				/* rt gc period		*/
+};
 
 int ipv6_sysctl_forwarding(ctl_table *ctl, int write, struct file * filp,
 			   void *buffer, size_t *lenp)
 {
-	int val = ipv6_forwarding;
+	int val = ipv6_config.forwarding;
 	int retv;
 
 	retv = proc_dointvec(ctl, write, filp, buffer, lenp);
 
-	if (write)
-	{
-		if (ipv6_forwarding && val == 0) {
+	if (write) {
+		if (ipv6_config.forwarding && val == 0) {
 			printk(KERN_DEBUG "sysctl: IPv6 forwarding enabled\n");
 			ndisc_forwarding_on();
 			addrconf_forwarding_on();		       
 		}
 
-		if (ipv6_forwarding == 0 && val) {
+		if (ipv6_config.forwarding == 0 && val)
 			ndisc_forwarding_off();
-		}
 	}
 	return retv;
 }
 
 ctl_table ipv6_table[] = {
-        {NET_IPV6_FORWARDING, "ipv6_forwarding",
-         &ipv6_forwarding, sizeof(int), 0644, NULL,
+        {NET_IPV6_FORWARDING, "forwarding",
+         &ipv6_config.forwarding, sizeof(int), 0644, NULL,
          &ipv6_sysctl_forwarding},
 
-	{NET_IPV6_HOPLIMIT, "ipv6_hop_limit",
-         &ipv6_hop_limit, sizeof(int), 0644, NULL,
+	{NET_IPV6_HOPLIMIT, "hop_limit",
+         &ipv6_config.hop_limit, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_ACCEPT_RA, "accept_ra",
+         &ipv6_config.accept_ra, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_ACCEPT_REDIRECTS, "accept_redirects",
+         &ipv6_config.accept_redirects, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_ND_MAX_MCAST_SOLICIT, "nd_max_mcast_solicit",
+         &ipv6_config.nd_max_mcast_solicit, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_ND_MAX_UCAST_SOLICIT, "nd_max_ucast_solicit",
+         &ipv6_config.nd_max_ucast_solicit, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_ND_RETRANS_TIME, "nd_retrans_time",
+         &ipv6_config.nd_retrans_time, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_ND_REACHABLE_TIME, "nd_base_reachble_time",
+         &ipv6_config.nd_base_reachable_time, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_ND_DELAY_PROBE_TIME, "nd_delay_first_probe_time",
+         &ipv6_config.nd_delay_probe_time, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_AUTOCONF, "autoconf",
+         &ipv6_config.autoconf, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_DAD_TRANSMITS, "dad_transmits",
+         &ipv6_config.dad_transmits, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_RTR_SOLICITS, "router_solicitations",
+         &ipv6_config.rtr_solicits, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_RTR_SOLICIT_INTERVAL, "router_solicitation_interval",
+         &ipv6_config.rtr_solicit_interval, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_RTR_SOLICIT_DELAY, "router_solicitation_delay",
+         &ipv6_config.rtr_solicit_delay, sizeof(int), 0644, NULL,
          &proc_dointvec},
 
 	{0}
@@ -73,6 +139,4 @@
 {
 	unregister_sysctl_table(ipv6_sysctl_header);
 }
-
-#endif
-
+#endif	/* MODULE */
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.1.29/linux/net/ipv6/tcp_ipv6.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv6/tcp_ipv6.c	Thu Mar 20 18:17:15 1997
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.11 1997/03/03 18:27:31 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.15 1997/03/18 18:24:56 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -37,20 +37,22 @@
 #include <net/ipv6.h>
 #include <net/transp_v6.h>
 #include <net/addrconf.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
 
 #include <asm/uaccess.h>
 
-static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr, 
-			      struct tcphdr *th, struct proto *prot, 
-			      struct ipv6_options *opt,
-			      struct device *dev, int pri, int hop_limit);
-
-static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len, 
-			      struct sk_buff *skb);
-
-static int tcp_v6_backlog_rcv(struct sock *sk, struct sk_buff *skb);
-static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb);
+static void	tcp_v6_send_reset(struct in6_addr *saddr,
+				  struct in6_addr *daddr,
+				  struct tcphdr *th, struct proto *prot,
+				  struct ipv6_options *opt,
+				  struct device *dev, int pri, int hop_limit);
+
+static void	tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len, 
+				  struct sk_buff *skb);
+
+static int	tcp_v6_backlog_rcv(struct sock *sk, struct sk_buff *skb);
+static int	tcp_v6_build_header(struct sock *sk, struct sk_buff *skb);
+static void	tcp_v6_xmit(struct sk_buff *skb);
 
 static struct tcp_func ipv6_mapped;
 static struct tcp_func ipv6_specific;
@@ -86,14 +88,14 @@
 
 	SOCKHASH_LOCK();
 	sk2 = tcp_bound_hash[tcp_sk_bhashfn(sk)];
-	for(; sk2 != NULL; sk2 = sk2->prev) {
+	for(; sk2 != NULL; sk2 = sk2->bind_next) {
 		if((sk2->num == snum) && (sk2 != sk)) {
 			unsigned char state = sk2->state;
 			int sk2_reuse = sk2->reuse;
 			if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
 				if((!sk2_reuse)			||
 				   (!sk_reuse)			||
-				   (state != TCP_LISTEN)) {
+				   (state == TCP_LISTEN)) {
 					retval = 1;
 					break;
 				}
@@ -120,18 +122,16 @@
 	SOCKHASH_LOCK();
 	state = sk->state;
 	if(state != TCP_CLOSE) {
-		struct sock **htable;
+		struct sock **skp;
 
-		if(state == TCP_LISTEN) {
-			sk->hashent = tcp_sk_listen_hashfn(sk);
-			htable = &tcp_listening_hash[0];
-		} else {
-			sk->hashent = tcp_v6_sk_hashfn(sk);
-			htable = &tcp_established_hash[0];
-		}
-		sk->next = htable[sk->hashent];
-		htable[sk->hashent] = sk;
-		sk->hashtable = htable;
+		if(state == TCP_LISTEN)
+			skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+		else
+			skp = &tcp_established_hash[tcp_v6_sk_hashfn(sk)];
+		if((sk->next = *skp) != NULL)
+			(*skp)->pprev = &sk->next;
+		*skp = sk;
+		sk->pprev = skp;
 		tcp_sk_bindify(sk);
 	}
 	SOCKHASH_UNLOCK();
@@ -139,20 +139,12 @@
 
 static void tcp_v6_unhash(struct sock *sk)
 {
-	struct sock **htable;
-
 	SOCKHASH_LOCK();
-	htable = sk->hashtable;
-	if(htable) {
-		struct sock **skp = &(htable[sk->hashent]);
-		while(*skp != NULL) {
-			if(*skp == sk) {
-				*skp = sk->next;
-				break;
-			}
-			skp = &((*skp)->next);
-		}
-		sk->hashtable = NULL;
+	if(sk->pprev) {
+		if(sk->next)
+			sk->next->pprev = sk->pprev;
+		*sk->pprev = sk->next;
+		sk->pprev = NULL;
 		tcp_sk_unbindify(sk);
 	}
 	SOCKHASH_UNLOCK();
@@ -160,36 +152,31 @@
 
 static void tcp_v6_rehash(struct sock *sk)
 {
-	struct sock **htable;
 	unsigned char state;
 
 	SOCKHASH_LOCK();
-	htable = &(sk->hashtable[sk->hashent]);
 	state = sk->state;
-	if(htable) {
-		while(*htable != NULL) {
-			if(*htable == sk) {
-				*htable = sk->next;
-				break;
-			}
-			htable = &((*htable)->next);
-		}
+	if(sk->pprev) {
+		if(sk->next)
+			sk->next->pprev = sk->pprev;
+		*sk->pprev = sk->next;
+		sk->pprev = NULL;
+		tcp_sk_unbindify(sk);
 	}
-	htable = NULL;
 	if(state != TCP_CLOSE) {
-		if(state == TCP_LISTEN) {
-			sk->hashent = tcp_sk_listen_hashfn(sk);
-			htable = &tcp_listening_hash[0];
-		} else {
-			sk->hashent = tcp_v6_sk_hashfn(sk);
-			htable = &tcp_established_hash[0];
-		}
-		sk->next = htable[sk->hashent];
-		htable[sk->hashent] = sk;
-	} else {
-		tcp_sk_unbindify(sk);
+		struct sock **skp;
+
+		if(state == TCP_LISTEN)
+			skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+		else
+			skp = &tcp_established_hash[tcp_v6_sk_hashfn(sk)];
+
+		if((sk->next = *skp) != NULL)
+			(*skp)->pprev = &sk->next;
+		*skp = sk;
+		sk->pprev = skp;
+		tcp_sk_bindify(sk);
 	}
-	sk->hashtable = htable;
 	SOCKHASH_UNLOCK();
 }
 
@@ -254,13 +241,10 @@
 	__u32 si;
 	__u32 di;
 
-	if (skb->protocol == __constant_htons(ETH_P_IPV6))
-	{
+	if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
 		si = skb->nh.ipv6h->saddr.s6_addr32[3];
 		di = skb->nh.ipv6h->daddr.s6_addr32[3];
-	}
-	else
-	{
+	} else {
 		si = skb->nh.iph->saddr;
 		di = skb->nh.iph->daddr;
 	}
@@ -276,14 +260,15 @@
 	struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
-	struct dest_entry *dc;
 	struct inet6_ifaddr *ifa;
+	struct in6_addr *saddr = NULL;
+	struct flowi fl;
+	struct dst_entry *dst;
 	struct tcphdr *th;
-	__u8 *ptr;
 	struct sk_buff *buff;
 	struct sk_buff *skb1;
+	__u8 *ptr;
 	int addr_type;
-	int tmp;
 
 	if (sk->state != TCP_CLOSE) 
 		return(-EISCONN);
@@ -311,9 +296,7 @@
 	addr_type = ipv6_addr_type(&usin->sin6_addr);
 
 	if(addr_type & IPV6_ADDR_MULTICAST)
-	{
 		return -ENETUNREACH;
-	}
 
 	/*
 	 *	connect to self not allowed
@@ -321,9 +304,7 @@
 
 	if (ipv6_addr_cmp(&usin->sin6_addr, &np->saddr) == 0 &&
 	    usin->sin6_port == sk->dummy_th.source)
-	{
 		return (-EINVAL);
-	}
 
 	memcpy(&np->daddr, &usin->sin6_addr, sizeof(struct in6_addr));
 
@@ -331,24 +312,22 @@
 	 *	TCP over IPv4
 	 */
 
-	if (addr_type == IPV6_ADDR_MAPPED)
-	{
+	if (addr_type == IPV6_ADDR_MAPPED) {
 		struct sockaddr_in sin;
 		int err;
 
-		printk(KERN_DEBUG "connect: ipv4 mapped\n");
+		SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
 
 		sin.sin_family = AF_INET;
 		sin.sin_port = usin->sin6_port;
 		sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
 
 		sk->tp_pinfo.af_tcp.af_specific = &ipv6_mapped;
-		sk->backlog_rcv = tcp_v4_backlog_rcv;
+		sk->backlog_rcv = tcp_v4_do_rcv;
 
 		err = tcp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
 
-		if (err)
-		{
+		if (err) {
 			sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
 			sk->backlog_rcv = tcp_v6_backlog_rcv;
 		}
@@ -356,31 +335,47 @@
 		return err;
 	}
 
-	dc = ipv6_dst_route(&np->daddr, NULL, (sk->localroute ? RTI_GATEWAY : 0));
+	if (!ipv6_addr_any(&np->rcv_saddr))
+		saddr = &np->rcv_saddr;
+
+	fl.proto = IPPROTO_TCP;
+	fl.nl_u.ip6_u.daddr = &np->daddr;
+	fl.nl_u.ip6_u.saddr = saddr;
+	fl.dev = NULL;
+	fl.uli_u.ports.dport = usin->sin6_port;
+	fl.uli_u.ports.sport = sk->dummy_th.source;
+
+	dst = ip6_route_output(sk, &fl);
 	
-	if (dc == NULL)
-	{
-		return -ENETUNREACH;
+	if (dst->error) {
+		dst_release(dst);
+		return dst->error;
 	}
 	
-	np->dest = dc;
-	np->dc_sernum = (dc->rt.fib_node ? dc->rt.fib_node->fn_sernum : 0);
+	ip6_dst_store(sk, dst);
 
-	ifa = ipv6_get_saddr((struct rt6_info *)dc, &np->daddr);
+	np->oif = dst->dev;
 	
-	if (ifa == NULL)
-	{
-		return -ENETUNREACH;
+	if (saddr == NULL) {
+		ifa = ipv6_get_saddr(dst, &np->daddr);
+	
+		if (ifa == NULL)
+			return -ENETUNREACH;
+		
+		saddr = &ifa->addr;
+
+		/* set the source address */
+		ipv6_addr_copy(&np->rcv_saddr, saddr);
+		ipv6_addr_copy(&np->saddr, saddr);
 	}
 
-	
 	/*
 	 *	Init variables
 	 */
 
 	lock_sock(sk);
 
-	sk->dummy_th.dest = usin->sin6_port;	
+	sk->dummy_th.dest = usin->sin6_port;
 	sk->write_seq = secure_tcp_sequence_number(np->saddr.s6_addr32[3],
 						   np->daddr.s6_addr32[3],
 						   sk->dummy_th.source,
@@ -400,17 +395,11 @@
 	buff = sock_wmalloc(sk, MAX_SYN_SIZE, 0, GFP_KERNEL);	
 
 	if (buff == NULL) 
-	{
 		return(-ENOMEM);
-	}
+
 	lock_sock(sk);
-	
-	tmp = tcp_v6_build_header(sk, buff);
 
-	/* set the source address */
-                
-	memcpy(&np->saddr, &ifa->addr, sizeof(struct in6_addr));
-	memcpy(&np->rcv_saddr, &ifa->addr, sizeof(struct in6_addr));
+	tcp_v6_build_header(sk, buff);
 
 	/* build the tcp header */
 	th = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr));
@@ -426,13 +415,9 @@
 	th->syn = 1;
 	th->doff = 6;
 
-	sk->window_clamp=0;
-
-	if ((dc->dc_flags & DCF_PMTU))
-		sk->mtu = dc->dc_pmtu;
-	else
-		sk->mtu = dc->rt.rt_dev->mtu;
+	sk->window_clamp = 0;
 
+	sk->mtu = dst->pmtu;
 	sk->mss = sk->mtu - sizeof(struct ipv6hdr) - sizeof(struct tcphdr);
 
 	/*
@@ -447,9 +432,9 @@
 	buff->csum = csum_partial(ptr, 4, 0);
 
 	tcp_v6_send_check(sk, th, sizeof(struct tcphdr) + 4, buff);
-	
+
 	tcp_set_state(sk, TCP_SYN_SENT);
-	
+
 	/* Socket identity change complete, no longer
 	 * in TCP_CLOSE, so rehash.
 	 */
@@ -468,17 +453,17 @@
 	skb1 = skb_clone(buff, GFP_KERNEL);
 	skb_set_owner_w(skb1, sk);
 
-	tmp = ipv6_xmit(sk, skb1, &np->saddr, &np->daddr, NULL, IPPROTO_TCP);
+	tcp_v6_xmit(skb1);
 
 	/* Timer for repeating the SYN until an answer  */
 
 	tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 	tcp_statistics.TcpActiveOpens++;
 	tcp_statistics.TcpOutSegs++;
-  
+
 	release_sock(sk);
-	
-	return(tmp);
+
+	return(0);
 }
 
 static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
@@ -513,7 +498,7 @@
 
 	lock_sock(sk);
 	retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov, 
-				len, msg->msg_flags);
+				msg->msg_flags);
 
 	release_sock(sk);
 
@@ -534,45 +519,52 @@
 	sk = tcp_v6_lookup(saddr, th->source, daddr, th->dest);
 
 	if (sk == NULL)
-	{
 		return;
-	}
 
 	np = &sk->net_pinfo.af_inet6;
 
-	if (type == ICMPV6_PKT_TOOBIG)
-	{
+	if (type == ICMPV6_PKT_TOOBIG) {
 		/* icmp should have updated the destination cache entry */
 
-		np->dest = ipv6_dst_check(np->dest, &np->daddr, np->dc_sernum,
-					  0);
+		dst_check(&np->dst, np->dst_cookie);
 
-		np->dc_sernum = (np->dest->rt.fib_node ?
-				 np->dest->rt.fib_node->fn_sernum : 0);
+		if (np->dst == NULL) {
+			struct flowi fl;
+			struct dst_entry *dst;
+			
+			fl.proto = IPPROTO_TCP;
+			fl.nl_u.ip6_u.daddr = &np->daddr;
+			fl.nl_u.ip6_u.saddr = &np->saddr;
+			fl.dev = np->oif;
+			fl.uli_u.ports.dport = sk->dummy_th.dest;
+			fl.uli_u.ports.sport = sk->dummy_th.source;
 
-		if (np->dest->dc_flags & DCF_PMTU)
-			sk->mtu = np->dest->dc_pmtu;
+			dst = ip6_route_output(sk, &fl);
 
-		sk->mtu = (sk->mtu - sizeof(struct ipv6hdr) - 
-			   sizeof(struct tcphdr));
+			ip6_dst_store(sk, dst);
+		}
+
+		if (np->dst->error)
+			sk->err_soft = np->dst->error;
+		else
+			sk->mtu = np->dst->pmtu;
 
 		return;
 	}
 
 	opening = (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV);
 	
-	if (icmpv6_err_convert(type, code, &err) || opening)
-	{
+	if (icmpv6_err_convert(type, code, &err) || opening) {
 		sk->err = err;
-		if (opening)
-		{
+
+		if (opening) {
 			tcp_statistics.TcpAttemptFails++;
 			tcp_set_state(sk,TCP_CLOSE);
 			sk->error_report(sk);
 		}
-	}
-	else
+	} else {
 		sk->err_soft = err;
+	}
 }
 
 
@@ -583,37 +575,34 @@
 	struct sk_buff * skb;
 	struct tcphdr *th;
 	unsigned char *ptr;
-	struct dest_entry *dc;
-	int mss;
+	struct dst_entry *dst;
+	struct flowi fl;
 
 	skb = sock_wmalloc(sk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
 	
 	if (skb == NULL)
-	{
 		return;
-	}
-
-	skb_reserve(skb, (MAX_HEADER + 15) & ~15);
-	skb->nh.ipv6h = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
 
-	dc = ipv6_dst_route(&af_req->rmt_addr, af_req->dev, 0);
+	fl.proto = IPPROTO_TCP;
+	fl.nl_u.ip6_u.daddr = &af_req->rmt_addr;
+	fl.nl_u.ip6_u.saddr = &af_req->loc_addr;
+	fl.dev = af_req->dev;
+	fl.uli_u.ports.dport = req->rmt_port;
+	fl.uli_u.ports.sport = sk->dummy_th.source;
 
-	skb->dev = af_req->dev;
-	
-	if (dc)
-	{
-		if (dc->dc_flags & DCF_PMTU)
-			mss = dc->dc_pmtu;
-		else
-			mss = dc->dc_nexthop->dev->mtu;
-		mss -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
+	dst = ip6_route_output(sk, &fl);
 
-		ipv6_dst_unlock(dc);
+	if (dst->error) {
+		kfree_skb(skb, FREE_WRITE);
+		dst_release(dst);
+		return;
 	}
-	else
-		mss = 516;
 
-	th =(struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
+	skb->dev = dst->dev;
+	skb_reserve(skb, (skb->dev->hard_header_len + 15) & ~15);
+	skb->nh.ipv6h = (struct ipv6hdr *) skb_put(skb,sizeof(struct ipv6hdr));
+
+	th = (struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
 	skb->h.th = th;
 	memset(th, 0, sizeof(struct tcphdr));
 	
@@ -635,19 +624,20 @@
 	ptr = skb_put(skb, TCPOLEN_MSS);
 	ptr[0] = TCPOPT_MSS;
 	ptr[1] = TCPOLEN_MSS;
-	ptr[2] = (mss >> 8) & 0xff;
-	ptr[3] = mss & 0xff;
+	ptr[2] = (dst->pmtu >> 8) & 0xff;
+	ptr[3] = dst->pmtu & 0xff;
 	skb->csum = csum_partial(ptr, TCPOLEN_MSS, 0);
 
-	th->check = tcp_v6_check(th, sizeof(*th) + TCPOLEN_MSS, &af_req->loc_addr, 
-				 &af_req->rmt_addr,
-				 csum_partial((char *)th, sizeof(*th), skb->csum));
-
-	ipv6_xmit(sk, skb, &af_req->loc_addr, &af_req->rmt_addr, af_req->opt,
-		  IPPROTO_TCP);
-				 
+	th->check = tcp_v6_check(th, sizeof(*th) + TCPOLEN_MSS,
+				 &af_req->loc_addr, &af_req->rmt_addr,
+				 csum_partial((char *)th, sizeof(*th),
+					      skb->csum));
+
+	ip6_dst_store(sk, dst);
+	ip6_xmit(sk, skb, &fl, af_req->opt);
+	dst_release(dst);
+
 	tcp_statistics.TcpOutSegs++;
-					      
 }
 
 static void tcp_v6_or_free(struct open_request *req)
@@ -666,23 +656,19 @@
 	struct open_request *req;
 	
 	/* If the socket is dead, don't accept the connection.	*/
-	if (sk->dead)
-	{
-		SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n",sk);
+	if (sk->dead) {
+		SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n", sk);
 		tcp_statistics.TcpAttemptFails++;
-		return -ENOTCONN;		
+		return -ENOTCONN;
 	}
 
 	if (skb->protocol == __constant_htons(ETH_P_IP))
-	{
 		return tcp_v4_conn_request(sk, skb, ptr, isn);
-	}
 
 	/*
 	 *	There are no SYN attacks on IPv6, yet...
 	 */
-	if (sk->ack_backlog >= sk->max_ack_backlog)
-	{
+	if (sk->ack_backlog >= sk->max_ack_backlog) {
 		printk(KERN_DEBUG "droping syn ack:%d max:%d\n",
 		       sk->ack_backlog, sk->max_ack_backlog);
 		tcp_statistics.TcpAttemptFails++;
@@ -690,9 +676,8 @@
 	}
 
 	af_req = kmalloc(sizeof(struct tcp_v6_open_req), GFP_ATOMIC);
-	
-	if (af_req == NULL)
-	{
+
+	if (af_req == NULL) {
 		tcp_statistics.TcpAttemptFails++;
 		goto exit;		
 	}
@@ -709,9 +694,7 @@
 	req->mss = tcp_parse_options(skb->h.th);
 
 	if (!req->mss)
-	{
 		req->mss = 536;
-	}
 
 	req->rmt_port = skb->h.th->source;
 
@@ -733,7 +716,7 @@
 
 	sk->data_ready(sk, 0);
 
-  exit:
+exit:
 	kfree_skb(skb, FREE_READ);
 	return 0;
 }
@@ -754,22 +737,21 @@
 {
 	struct tcp_v6_open_req *af_req = (struct tcp_v6_open_req *) req;
 	struct ipv6_pinfo *np;
-	struct dest_entry *dc;
+	struct dst_entry *dst;
+	struct flowi fl;
 	struct tcp_opt *newtp;
 	struct sock *newsk;
-	
 
-	if (skb->protocol == __constant_htons(ETH_P_IP))
-	{
-		/* 
-		 *	v6 mapped 
+	if (skb->protocol == __constant_htons(ETH_P_IP)) {
+		/*
+		 *	v6 mapped
 		 */
-		
+
 		newsk = tcp_v4_syn_recv_sock(sk, skb, req);
 
 		if (newsk == NULL)
 			return NULL;
-		
+
 		np = &newsk->net_pinfo.af_inet6;
 
 		ipv6_addr_set(&np->daddr, 0, 0, __constant_htonl(0x0000FFFF),
@@ -781,16 +763,14 @@
 		ipv6_addr_copy(&np->rcv_saddr, &np->saddr);
 
 		newsk->tp_pinfo.af_tcp.af_specific = &ipv6_mapped;
-		newsk->backlog_rcv = tcp_v4_backlog_rcv;
+		newsk->backlog_rcv = tcp_v4_do_rcv;
 
 		return newsk;
 	}
 
 	newsk = sk_alloc(GFP_ATOMIC);
 	if (newsk == NULL)
-	{
 		return NULL;
-	}
 
 	memcpy(newsk, sk, sizeof(*newsk));
 
@@ -803,13 +783,12 @@
 	skb_queue_head_init(&newsk->receive_queue);
 	skb_queue_head_init(&newsk->out_of_order_queue);
 	skb_queue_head_init(&newsk->error_queue);
-	
+
 	/*
 	 *	Unused
 	 */
 
 	newsk->send_head = NULL;
-	newsk->send_tail = NULL;
 
 	newtp = &(newsk->tp_pinfo.af_tcp);
 	np = &newsk->net_pinfo.af_inet6;
@@ -826,7 +805,6 @@
 	newsk->cong_count = 0;
 	newsk->ssthresh = 0;
 	newtp->backoff = 0;
-	newsk->blog = 0;
 	newsk->intr = 0;
 	newsk->proc = 0;
 	newsk->done = 0;
@@ -870,7 +848,7 @@
 
 	newsk->dummy_th.source = sk->dummy_th.source;
 	newsk->dummy_th.dest = req->rmt_port;
-	newsk->users=0;
+	newsk->sock_readers=0;
 
 	newtp->rcv_nxt = req->rcv_isn + 1;
 	newtp->rcv_wup = req->rcv_isn + 1;
@@ -881,30 +859,38 @@
 	ipv6_addr_copy(&np->daddr, &af_req->rmt_addr);
 	ipv6_addr_copy(&np->saddr, &af_req->loc_addr);
 	ipv6_addr_copy(&np->rcv_saddr, &af_req->loc_addr);
-	
+	np->oif = af_req->dev;
+
 	/*
-	 *	options / mss
+	 *	options / mss / route cache
 	 */
-	
-	dc = ipv6_dst_route(&af_req->rmt_addr, af_req->dev, 0);
-	np->dest = dc;
 
-	if (np->dest && (np->dest->dc_flags & DCF_PMTU))
-		newsk->mtu = np->dest->dc_pmtu;
-	else
+	fl.proto = IPPROTO_TCP;
+	fl.nl_u.ip6_u.daddr = &np->daddr;
+	fl.nl_u.ip6_u.saddr = &np->saddr;
+	fl.dev = np->oif;
+	fl.uli_u.ports.dport = newsk->dummy_th.dest;
+	fl.uli_u.ports.sport = newsk->dummy_th.source;
+
+	dst = ip6_route_output(newsk, &fl);
+
+	ip6_dst_store(newsk, dst);
+
+	if (dst->error)
 		newsk->mtu = af_req->dev->mtu;
+	else
+		newsk->mtu = dst->pmtu;
 
-	newsk->mss = min(req->mss, (newsk->mtu - sizeof(struct ipv6hdr) - 
+	newsk->mss = min(req->mss, (newsk->mtu - sizeof(struct ipv6hdr) -
 				    sizeof(struct tcphdr)));
-	
+
 	newsk->daddr	= LOOPBACK4_IPV6;
 	newsk->saddr	= LOOPBACK4_IPV6;
 	newsk->rcv_saddr= LOOPBACK4_IPV6;
-	
-	newsk->prot->hash(newsk);
 
+	newsk->prot->hash(newsk);
+	add_to_prot_sklist(newsk);
 	return newsk;
-
 }
 
 static void tcp_v6_reply_reset(struct sk_buff *skb)
@@ -918,6 +904,7 @@
 {
 	struct sk_buff *buff;
 	struct tcphdr *t1;
+	struct flowi fl;
 
 	if(th->rst)
 		return;
@@ -947,12 +934,9 @@
 	t1->doff = sizeof(*t1)/4;
 	t1->rst = 1;
   
-	if(th->ack)
-	{
+	if(th->ack) {
 	  	t1->seq = th->ack_seq;
-	}
-	else
-	{
+	} else {
 		t1->ack = 1;
 	  	if(!th->syn)
 			t1->ack_seq = th->seq;
@@ -965,9 +949,14 @@
 	t1->check = csum_ipv6_magic(saddr, daddr, sizeof(*t1), IPPROTO_TCP,
 				    buff->csum);
 
-	
-	ipv6_xmit(NULL, buff, saddr, daddr, NULL, IPPROTO_TCP);
-	
+	fl.proto = IPPROTO_TCP;
+	fl.nl_u.ip6_u.daddr = daddr;
+	fl.nl_u.ip6_u.saddr = saddr;
+	fl.dev = dev;
+	fl.uli_u.ports.dport = th->dest;
+	fl.uli_u.ports.sport = th->source;
+
+	ip6_xmit(NULL, buff, &fl, NULL);
 	tcp_statistics.TcpOutSegs++;
 }
 
@@ -975,22 +964,18 @@
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct open_request *req;
-	
 
 	/*
 	 *	assumption: the socket is not in use.
 	 *	as we checked the user count on tcp_rcv and we're
 	 *	running from a soft interrupt.
 	 */
-		
+
 	req = tp->syn_wait_queue;
-	
 
 	if (!req)
-	{
 		return sk;
-	}
-	
+
 	do {
 		struct tcp_v6_open_req *af_req;
 
@@ -998,12 +983,10 @@
 
 		if (!ipv6_addr_cmp(&af_req->rmt_addr, &skb->nh.ipv6h->saddr) &&
 		    !ipv6_addr_cmp(&af_req->loc_addr, &skb->nh.ipv6h->daddr) &&
-		    req->rmt_port == skb->h.th->source)
-		{
+		    req->rmt_port == skb->h.th->source) {
 			u32 flg;
-				
-			if (req->sk)
-			{
+
+			if (req->sk) {
 				printk(KERN_DEBUG "BUG: syn_recv:"
 				       "socket exists\n");
 				break;
@@ -1016,10 +999,9 @@
 			 */
 			flg = *(((u32 *)skb->h.th) + 3);
 			flg &= __constant_htonl(0x002f0000);
-				
+
 			if ((flg == __constant_htonl(0x00020000)) &&
-			    (!after(skb->seq, req->rcv_isn)))
-			{
+			    (!after(skb->seq, req->rcv_isn))) {
 				/*
 				 *	retransmited syn
 				 *	FIXME: must send an ack
@@ -1033,9 +1015,7 @@
 			tcp_dec_slow_timer(TCP_SLT_SYNACK);
 
 			if (sk == NULL)
-			{
 				return NULL;
-			}
 
 			skb_set_owner_r(skb, sk);
 			req->expires = 0UL;
@@ -1045,10 +1025,8 @@
 
 		req = req->dl_next;
 	} while (req != tp->syn_wait_queue);
-	
 
 	return sk;
-
 }
 
 int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
@@ -1070,42 +1048,37 @@
 
 	sk = skb->sk;
 
-	if (!redo)
-	{
-
+	if (!redo) {
 		if (skb->pkt_type != PACKET_HOST)
 			goto discard_it;
 
 		/*
 		 *	Pull up the IP header.
 		 */
-	
+
 		skb_pull(skb, skb->h.raw - skb->data);
 
 		/*
 		 *	Try to use the device checksum if provided.
 		 */
-		
-		switch (skb->ip_summed) 
-		{
-			case CHECKSUM_NONE:
-				skb->csum = csum_partial((char *)th, len, 0);
-			case CHECKSUM_HW:
-				if (tcp_v6_check(th,len,saddr,daddr,skb->csum))
-				{
-					printk(KERN_DEBUG "tcp csum failed\n");
-					goto discard_it;
-				}
-			default:
-				/* CHECKSUM_UNNECESSARY */
-		}
+
+		switch (skb->ip_summed) {
+		case CHECKSUM_NONE:
+			skb->csum = csum_partial((char *)th, len, 0);
+		case CHECKSUM_HW:
+			if (tcp_v6_check(th,len,saddr,daddr,skb->csum)) {
+				printk(KERN_DEBUG "tcp csum failed\n");
+				goto discard_it;
+			}
+		default:
+			/* CHECKSUM_UNNECESSARY */
+		};
 
 		tcp_statistics.TcpInSegs++;
-		
+
 		sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest);
 
-		if (!sk) 
-		{
+		if (!sk) {
 			printk(KERN_DEBUG "socket not found\n");
 			goto no_tcp_socket;
 		}
@@ -1117,14 +1090,13 @@
 
 		skb->acked = 0;
 		skb->used = 0;
-	}		
+	}
 
 	/*
-	 * We may need to add it to the backlog here. 
+	 * We may need to add it to the backlog here.
 	 */
 
-	if (sk->users) 
-	{
+	if (sk->sock_readers) {
 		__skb_queue_tail(&sk->back_log, skb);
 		return(0);
 	}
@@ -1133,46 +1105,38 @@
 	 *	Signal NDISC that the connection is making
 	 *	"forward progress"
 	 */
-	if (sk->state != TCP_LISTEN)
-	{
+	if (sk->state != TCP_LISTEN) {
 		struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 		struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
 
 		if (after(skb->seq, tp->rcv_nxt) ||
-		    after(skb->ack_seq, tp->snd_una))
-		{
-			if (np->dest)
-				ndisc_validate(np->dest->dc_nexthop);
+		    after(skb->ack_seq, tp->snd_una)) {
+			if (np->dst)
+				ndisc_validate(np->dst->neighbour);
 		}
 	}
 
-	if (!sk->prot) 
-	{
+	if (!sk->prot) {
 		printk(KERN_DEBUG "tcp_rcv: sk->prot == NULL\n");
 		return(0);
 	}
 
 	skb_set_owner_r(skb, sk);
 
-	if (sk->state == TCP_ESTABLISHED)
-	{
+	if (sk->state == TCP_ESTABLISHED) {
 		if (tcp_rcv_established(sk, skb, th, len))
 			goto no_tcp_socket;
 		return 0;
 	}
-	
-	if (sk->state == TCP_LISTEN)
-	{
+
+	if (sk->state == TCP_LISTEN) {
 		/*
 		 *	find possible connection requests
 		 */
 		sk = tcp_v6_check_req(sk, skb);
 
 		if (sk == NULL)
-		{
 			goto discard_it;
-		}
-
 	}
 
 	if (tcp_rcv_state_process(sk, skb, th, opt, len) == 0)
@@ -1181,11 +1145,11 @@
 no_tcp_socket:
 
 	/*
-	 *	No such TCB. If th->rst is 0 send a reset 
+	 *	No such TCB. If th->rst is 0 send a reset
 	 *	(checked in tcp_send_reset)
 	 */
 
-	tcp_v6_send_reset(daddr, saddr, th, &tcpv6_prot, opt, dev, 
+	tcp_v6_send_reset(daddr, saddr, th, &tcpv6_prot, opt, dev,
 			  skb->nh.ipv6h->priority, 255);
 
 discard_it:
@@ -1196,37 +1160,38 @@
 
 	kfree_skb(skb, FREE_READ);
 	return 0;
-
 }
 
 static int tcp_v6_rebuild_header(struct sock *sk, struct sk_buff *skb)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-	
-	if (np->dest)
-	{
-		np->dest = ipv6_dst_check(np->dest, &np->daddr,
-					  np->dc_sernum, 0);
-					  
-	}
-	else
-	{
-		np->dest = ipv6_dst_route(&np->daddr, NULL, 0);
+
+	if (np->dst)
+		dst_check(&np->dst, np->dst_cookie);
+
+	if (np->dst == NULL) {
+		struct flowi fl;
+		struct dst_entry *dst;
+
+		fl.proto = IPPROTO_TCP;
+		fl.nl_u.ip6_u.daddr = &np->daddr;
+		fl.nl_u.ip6_u.saddr = &np->saddr;
+		fl.dev = np->oif;
+		fl.uli_u.ports.dport = sk->dummy_th.dest;
+		fl.uli_u.ports.sport = sk->dummy_th.source;
+
+		dst = ip6_route_output(sk, &fl);
+		ip6_dst_store(sk, dst);
 	}
 
-	if (!np->dest)
-	{
+	if (np->dst->error) {
 		/*
 		 *	lost route to destination
 		 */
-		return -1;
+		return -EHOSTUNREACH;
 	}
-	
-	np->dc_sernum = (np->dest->rt.fib_node ?
-			 np->dest->rt.fib_node->fn_sernum : 0);
 
-	ipv6_redo_mac_hdr(skb, np->dest->dc_nexthop,
-			  skb->tail - (u8*) skb->nh.ipv6h);
+	skb_pull(skb, skb->nh.raw - skb->data);
 	return 0;
 }
 
@@ -1237,7 +1202,7 @@
 	res = tcp_v6_rcv(skb, skb->dev,
 			 &skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
 			 (struct ipv6_options *) skb->cb,
-			 skb->len, 1, 
+			 skb->len, 1,
 			 (struct inet6_protocol *) sk->pair);
 	return res;
 }
@@ -1246,20 +1211,16 @@
 {
 	struct in6_addr *saddr;
 	struct in6_addr *daddr;
-	struct sock *sk;
 
 	saddr = &skb->nh.ipv6h->saddr;
 	daddr = &skb->nh.ipv6h->daddr;
-
-	sk = tcp_v6_lookup(saddr, th->source, daddr, th->dest);
-
-	return sk;
+	return tcp_v6_lookup(saddr, th->source, daddr, th->dest);
 }
-	
+
 static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb)
 {
 	skb_reserve(skb, (MAX_HEADER + 15) & ~15);
-	skb->nh.ipv6h = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
+	skb->nh.raw = skb_put(skb, sizeof(struct ipv6hdr));
 
 	/*
 	 *	FIXME: reserve space for option headers
@@ -1273,28 +1234,33 @@
 {
 	struct sock *sk = skb->sk;
 	struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6;
+	struct flowi fl;
 	int err;
 
-	err = ipv6_xmit(sk, skb, &np->saddr, &np->daddr, NULL, IPPROTO_TCP);
-	
+	fl.proto = IPPROTO_TCP;
+	fl.nl_u.ip6_u.daddr = &np->daddr;
+	fl.nl_u.ip6_u.saddr = &np->saddr;
+	fl.dev = np->oif;
+	fl.uli_u.ports.sport = sk->dummy_th.source;
+	fl.uli_u.ports.dport = sk->dummy_th.dest;
+
+	err = ip6_xmit(sk, skb, &fl, np->opt);
+
 	/*
 	 *	FIXME: check error handling.
 	 */
 
 	sk->err_soft = err;
 }
-		     
-
 
 static void v6_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
 {
 	struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6;
 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) uaddr;
-	
+
 	sin6->sin6_family = AF_INET6;
 	memcpy(&sin6->sin6_addr, &np->daddr, sizeof(struct in6_addr));
 	sin6->sin6_port	= sk->dummy_th.dest;
-
 }
 
 static struct tcp_func ipv6_specific = {
@@ -1359,19 +1325,16 @@
 	/* this is how many unacked bytes we will accept for this socket.  */
 	sk->max_unacked = 2048; /* needs to be at most 2 full packets. */
 	sk->max_ack_backlog = SOMAXCONN;
-	
+
 	sk->mtu = 576;
 	sk->mss = 516;
 
 	sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
-	
 
 	/*
-	 *	Speed up by setting some standard state for the dummy_th
-	 *	if TCP uses it (maybe move to tcp_init later)
+	 *	Speed up by setting some standard state for the dummy_th.
 	 */
-  	
-  	sk->dummy_th.ack=1;	
+  	sk->dummy_th.ack=1;
   	sk->dummy_th.doff=sizeof(struct tcphdr)>>2;
 
 	sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
@@ -1385,16 +1348,14 @@
 	struct sk_buff *skb;
 
 	tcp_clear_xmit_timers(sk);
-	
+
 	if (sk->keepopen)
-	{
 		tcp_dec_slow_timer(TCP_SLT_KEEPALIVE);
-	}
 
 	/*
-	 *	Cleanup up the write buffer. 
+	 *	Cleanup up the write buffer.
 	 */
-	 
+
   	while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
 		IS_SKB(skb);
 		kfree_skb(skb, FREE_WRITE);
@@ -1413,15 +1374,12 @@
 	 *	Release destination entry
 	 */
 
-	if (np->dest)
-	{
-		ipv6_dst_unlock(np->dest);
-	}
+	if (np->dst)
+		dst_release(np->dst);
 
 	return 0;
 }
 
-
 struct proto tcpv6_prot = {
 	(struct sock *)&tcpv6_prot,	/* sklist_next */
 	(struct sock *)&tcpv6_prot,	/* sklist_prev */
@@ -1454,7 +1412,7 @@
 	0				/* highestinuse */
 };
 
-static struct inet6_protocol tcpv6_protocol = 
+static struct inet6_protocol tcpv6_protocol =
 {
 	tcp_v6_rcv,		/* TCP handler		*/
 	tcp_v6_err,		/* TCP error control	*/
@@ -1465,16 +1423,8 @@
 	"TCPv6"			/* name			*/
 };
 
-
 void tcpv6_init(void)
 {
 	/* register inet6 protocol */
 	inet6_add_protocol(&tcpv6_protocol);
 }
-
-/*
- * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h  -c -o tcp_ipv6.o tcp_ipv6.c"
- * c-file-style: "Linux"
- * End:
- */
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.1.29/linux/net/ipv6/udp.c	Mon Mar 17 14:54:36 1997
+++ linux/net/ipv6/udp.c	Thu Mar 20 18:17:15 1997
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.9 1997/03/04 10:41:59 davem Exp $
+ *	$Id: udp.c,v 1.12 1997/03/18 18:24:59 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -34,7 +34,7 @@
 #include <net/ndisc.h>
 #include <net/protocol.h>
 #include <net/transp_v6.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
 #include <net/addrconf.h>
 #include <net/ip.h>
 #include <net/udp.h>
@@ -60,7 +60,7 @@
 			if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
 				if((!sk2_reuse)			||
 				   (!sk_reuse)			||
-				   (state != TCP_LISTEN)) {
+				   (state == TCP_LISTEN)) {
 					retval = 1;
 					break;
 				}
@@ -184,9 +184,10 @@
 {
 	struct sockaddr_in6	*usin = (struct sockaddr_in6 *) uaddr;
 	struct in6_addr		*daddr;
-	struct dest_entry	*dest;
+	struct dst_entry	*dst;
 	struct ipv6_pinfo      	*np;
 	struct inet6_ifaddr	*ifa;
+	struct flowi		fl;
 	int			addr_type;
 
 	if (addr_len < sizeof(*usin)) 
@@ -198,8 +199,7 @@
 	addr_type = ipv6_addr_type(&usin->sin6_addr);
 	np = &sk->net_pinfo.af_inet6;
 
-	if (addr_type == IPV6_ADDR_ANY)
-	{
+	if (addr_type == IPV6_ADDR_ANY) {
 		/*
 		 *	connect to self
 		 */
@@ -208,8 +208,7 @@
 
 	daddr = &usin->sin6_addr;
 
-	if (addr_type == IPV6_ADDR_MAPPED)
-	{
+	if (addr_type == IPV6_ADDR_MAPPED) {
 		struct sockaddr_in sin;
 		int err;
 
@@ -219,22 +218,18 @@
 		err = udp_connect(sk, (struct sockaddr*) &sin, sizeof(sin));
 		
 		if (err < 0)
-		{
 			return err;
-		}
 		
 		ipv6_addr_copy(&np->daddr, daddr);
 		
-		if(ipv6_addr_any(&np->saddr))
-		{
+		if(ipv6_addr_any(&np->saddr)) {
 			ipv6_addr_set(&np->saddr, 0, 0, 
 				      __constant_htonl(0x0000ffff),
 				      sk->saddr);
 
 		}
 
-		if(ipv6_addr_any(&np->rcv_saddr))
-		{
+		if(ipv6_addr_any(&np->rcv_saddr)) {
 			ipv6_addr_set(&np->rcv_saddr, 0, 0, 
 				      __constant_htonl(0x0000ffff),
 				      sk->rcv_saddr);
@@ -244,35 +239,41 @@
 
 	ipv6_addr_copy(&np->daddr, daddr);
 
+	sk->dummy_th.dest = usin->sin6_port;
+
 	/*
 	 *	Check for a route to destination an obtain the
 	 *	destination cache for it.
 	 */
 
-	dest = ipv6_dst_route(daddr, NULL, sk->localroute ? RTI_GATEWAY : 0);
-
-	np->dest = dest;
+	fl.proto = IPPROTO_UDP;
+	fl.nl_u.ip6_u.daddr = daddr;
+	fl.nl_u.ip6_u.saddr = NULL;
+	fl.dev = NULL;
+	fl.uli_u.ports.dport = sk->dummy_th.dest;
+	fl.uli_u.ports.sport = sk->dummy_th.source;
+
+	dst = ip6_route_output(sk, &fl);
+       
+	if (dst->error) {
+		dst_release(dst);
+		return dst->error;
+	}
 
-	if (dest == NULL)
-		return -ENETUNREACH;
+	ip6_dst_store(sk, dst);
 
 	/* get the source adddress used in the apropriate device */
 
-	ifa = ipv6_get_saddr((struct rt6_info *) dest, daddr);
+	ifa = ipv6_get_saddr(dst, daddr);
 
 	if(ipv6_addr_any(&np->saddr))
-	{
 		ipv6_addr_copy(&np->saddr, &ifa->addr);
-	}
 
-	if(ipv6_addr_any(&np->rcv_saddr))
-	{
+	if(ipv6_addr_any(&np->rcv_saddr)) {
 		ipv6_addr_copy(&np->rcv_saddr, &ifa->addr);
 		sk->rcv_saddr = 0xffffffff;
 	}
 
-	sk->dummy_th.dest = usin->sin6_port;
-
 	sk->state = TCP_ESTABLISHED;
 
 	return(0);
@@ -285,10 +286,10 @@
 	lock_sock(sk);
 	sk->state = TCP_CLOSE;
 
-	if (np->dest)
-	{
-		ipv6_dst_unlock(np->dest);
-	}
+	if (np->dst)
+		dst_release(np->dst);
+
+	ipv6_sock_mc_close(sk);
 
 	release_sock(sk);
 	destroy_sock(sk);
@@ -306,7 +307,6 @@
   	int truesize;
   	struct sk_buff *skb;
   	int err;
-  	
 
 	/*
 	 *	Check any passed addresses
@@ -328,8 +328,7 @@
   	
   	copied=truesize;
 
-  	if(copied>len)
-  	{
+  	if(copied>len) {
   		copied=len;
   		msg->msg_flags|=MSG_TRUNC;
   	}
@@ -346,8 +345,7 @@
 	sk->stamp=skb->stamp;
 
 	/* Copy the address. */
-	if (msg->msg_name) 
-	{
+	if (msg->msg_name) {
 		struct sockaddr_in6 *sin6;
 	  
 		sin6 = (struct sockaddr_in6 *) msg->msg_name;
@@ -355,13 +353,10 @@
 		sin6->sin6_family = AF_INET6;
 		sin6->sin6_port = skb->h.uh->source;
 
-		if (skb->protocol == __constant_htons(ETH_P_IP))
-		{
+		if (skb->protocol == __constant_htons(ETH_P_IP)) {
 			ipv6_addr_set(&sin6->sin6_addr, 0, 0,
 				      __constant_htonl(0xffff), skb->nh.iph->saddr);
-		}
-		else
-		{
+		} else {
 			memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
 			       sizeof(struct in6_addr));
 
@@ -386,22 +381,20 @@
 
 	sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest);
    
-	if (sk == NULL)
-	{
+	if (sk == NULL) {
 		printk(KERN_DEBUG "icmp for unkown sock\n");
 		return;
 	}
 
-	if (icmpv6_err_convert(type, code, &err))
-	{
+	if (icmpv6_err_convert(type, code, &err)) {
 		if(sk->bsdism && sk->state!=TCP_ESTABLISHED)
 			return;
 		
 		sk->err = err;
 		sk->error_report(sk);
-	}
-	else
+	} else {
 		sk->err_soft = err;
+	}
 }
 
 static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
@@ -507,16 +500,14 @@
 
 	ulen = ntohs(uh->len);
 	
-	if (ulen > len || len < sizeof(*uh))
-	{
+	if (ulen > len || len < sizeof(*uh)) {
 		printk(KERN_DEBUG "UDP: short packet: %d/%d\n", ulen, len);
 		udp_stats_in6.UdpInErrors++;
 		kfree_skb(skb, FREE_READ);
 		return(0);
 	}
 
-	if (uh->check == 0)
-	{
+	if (uh->check == 0) {
 		printk(KERN_DEBUG "IPv6: udp checksum is 0\n");
 		goto discard;
 	}
@@ -525,12 +516,11 @@
 	case CHECKSUM_NONE:
 		skb->csum = csum_partial((char*)uh, len, 0);
 	case CHECKSUM_HW:
-		if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, skb->csum))
-		{
+		if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, skb->csum)) {
 			printk(KERN_DEBUG "IPv6: udp checksum error\n");
 			goto discard;
 		}
-	}
+	};
 	
 	len = ulen;
 
@@ -551,12 +541,10 @@
 
 	sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest);
 
-	if (sk == NULL)
-	{
+	if (sk == NULL) {
 		udp_stats_in6.UdpNoPorts++;
 
-		icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH,
-			    0, dev);
+		icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev);
 		
 		kfree_skb(skb, FREE_READ);
 		return(0);
@@ -564,18 +552,14 @@
 
 	/* deliver */
 
-	if (sk->users)
-	{
+	if (sk->sock_readers)
 		__skb_queue_tail(&sk->back_log, skb);
-	}
 	else
-	{
 		udpv6_queue_rcv_skb(sk, skb);
-	}
 	
 	return(0);
 
-  discard:
+discard:
 	udp_stats_in6.UdpInErrors++;
 	kfree_skb(skb, FREE_READ);
 	return(0);	
@@ -608,12 +592,9 @@
 
 	dst = buff;
 
-	if (offset)
-	{
+	if (offset) {
 		offset -= sizeof(struct udphdr);
-	}
-	else
-	{
+	} else {
 		dst += sizeof(struct udphdr);
 		final = 1;
 		clen -= sizeof(struct udphdr);
@@ -622,19 +603,15 @@
 	udh->wcheck = csum_partial_copy_fromiovecend(dst, udh->iov, offset,
 						     clen, udh->wcheck);
 
-	if (final)
-	{
+	if (final) {
 		struct in6_addr *daddr;
 		
 		udh->wcheck = csum_partial((char *)udh, sizeof(struct udphdr),
 					   udh->wcheck);
 
-		if (udh->daddr)
-		{
+		if (udh->daddr) {
 			daddr = udh->daddr;
-		}
-		else
-		{
+		} else {
 			/*
 			 *	use packet destination address
 			 *	this should improve cache locality
@@ -654,27 +631,26 @@
 
 static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
 {
-	
 	struct ipv6_options opt_space;
 	struct udpv6fakehdr udh;
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name;
 	struct ipv6_options *opt = NULL;
 	struct device *dev = NULL;
+	struct flowi fl;
 	int addr_len = msg->msg_namelen;
 	struct in6_addr *daddr;
 	struct in6_addr *saddr = NULL;
 	int len = ulen + sizeof(struct udphdr);
 	int addr_type;
-	int hlimit = 0;
-	int err;
+	int hlimit = -1;
 
+	int err;
 	
 	if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
 		return(-EINVAL);
 
-	if (sin6)
-	{
+	if (sin6) {
 		if (addr_len < sizeof(*sin6))
 			return(-EINVAL);
 		
@@ -687,14 +663,11 @@
 		udh.uh.dest = sin6->sin6_port;
 		daddr = &sin6->sin6_addr;
 
-		if (np->dest && ipv6_addr_cmp(daddr, &np->daddr))
-		{
-			ipv6_dst_unlock(np->dest);
-			np->dest = NULL;
+		if (np->dst && ipv6_addr_cmp(daddr, &np->daddr)) {
+			dst_release(np->dst);
+			np->dst = NULL;
 		}
-	}
-	else
-	{
+	} else {
 		if (sk->state != TCP_ESTABLISHED)
 			return(-EINVAL);
 		
@@ -704,8 +677,7 @@
 
 	addr_type = ipv6_addr_type(daddr);
 
-	if (addr_type == IPV6_ADDR_MAPPED)
-	{
+	if (addr_type == IPV6_ADDR_MAPPED) {
 		struct sockaddr_in sin;
 		
 		sin.sin_family = AF_INET;
@@ -716,22 +688,18 @@
 
 	udh.daddr = NULL;
 	
-	if (msg->msg_controllen)
-	{
+	if (msg->msg_controllen) {
 		opt = &opt_space;
 		memset(opt, 0, sizeof(struct ipv6_options));
 
 		err = datagram_send_ctl(msg, &dev, &saddr, opt, &hlimit);
-		if (err < 0)
-		{
+		if (err < 0) {
 			printk(KERN_DEBUG "invalid msg_control\n");
 			return err;
 		}
 		
 		if (opt->srcrt)
-		{			
 			udh.daddr = daddr;
-		}
 	}
 	
 	udh.uh.source = sk->dummy_th.source;
@@ -740,11 +708,17 @@
 	udh.iov = msg->msg_iov;
 	udh.wcheck = 0;
 	udh.pl_len = len;
-	
-	err = ipv6_build_xmit(sk, udpv6_getfrag, &udh, daddr, len,
-			      saddr, dev, opt, IPPROTO_UDP, hlimit,
-			      msg->msg_flags&MSG_DONTWAIT);
-	
+
+	fl.proto = IPPROTO_UDP;
+	fl.nl_u.ip6_u.daddr = daddr;
+	fl.nl_u.ip6_u.saddr = saddr;
+	fl.dev = dev;
+	fl.uli_u.ports.dport = udh.uh.dest;
+	fl.uli_u.ports.sport = udh.uh.source;
+
+	err = ip6_build_xmit(sk, udpv6_getfrag, &udh, &fl, len, opt, hlimit,
+			     msg->msg_flags);
+
 	if (err < 0)
 		return err;
 
diff -u --recursive --new-file v2.1.29/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.1.29/linux/net/ipx/af_ipx.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ipx/af_ipx.c	Thu Mar 20 18:17:15 1997
@@ -714,19 +714,16 @@
 	struct ipxhdr	*ipx = skb->nh.ipxh;
 	ipx_interface	*i;
 
-#ifdef CONFIG_FIREWALL
 	/*
 	 *	We firewall first, ask questions later.
 	 */
 
-	if (call_in_firewall(PF_IPX, skb->dev, ipx, NULL)!=FW_ACCEPT)
+	if (call_in_firewall(PF_IPX, skb->dev, ipx, NULL, &skb)!=FW_ACCEPT)
 	{
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
 
-#endif
-
 	/* See if we should update our network number */
 	if ((intrfc->if_netnum == 0L) &&
 		(ipx->ipx_source.net == ipx->ipx_dest.net) &&
@@ -818,16 +815,15 @@
 					printk( "IPX: Forward PPROP onto net num %08x\n", (unsigned int) htonl(ifcs->if_netnum) );
 #endif
 					skb2 = skb_clone(skb, GFP_ATOMIC);
-#ifdef CONFIG_FIREWALL
+
 					/*
 					 *	See if we are allowed to firewall forward
 					 */
-					if (call_fw_firewall(PF_IPX, skb2->dev, ipx, NULL)!=FW_ACCEPT)
+					if (call_fw_firewall(PF_IPX, skb2->dev, ipx, NULL, &skb)!=FW_ACCEPT)
 					{
 						kfree_skb(skb, FREE_READ);
 						return 0;
 					}
-#endif
 					ipxrtr_route_skb(skb2);
 				}
 #ifdef DEBUG_IPX_PPROP_ROUTING
@@ -851,16 +847,15 @@
 
 	if (intrfc->if_netnum != ipx->ipx_dest.net)
 	{
-#ifdef CONFIG_FIREWALL
 		/*
 		 *	See if we are allowed to firewall forward
 		 */
-		if (call_fw_firewall(PF_IPX, skb->dev, ipx, NULL)!=FW_ACCEPT)
+		if (call_fw_firewall(PF_IPX, skb->dev, ipx, NULL, &skb)!=FW_ACCEPT)
 		{
 			kfree_skb(skb, FREE_READ);
 			return 0;
 		}
-#endif
+
 		/* We only route point-to-point packets. */
 		if (skb->pkt_type == PACKET_HOST)
 		{
@@ -1451,13 +1446,11 @@
 	else
 		ipx->ipx_checksum=ipx_set_checksum(ipx, len+sizeof(struct ipxhdr));
 
-#ifdef CONFIG_FIREWALL	
-	if(call_out_firewall(PF_IPX, skb->dev, ipx, NULL)!=FW_ACCEPT)
+	if(call_out_firewall(PF_IPX, skb->dev, ipx, NULL, &skb)!=FW_ACCEPT)
 	{
 		kfree_skb(skb, FREE_WRITE);
 		return -EPERM;
 	}
-#endif
 	
 	return ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ? 
 				rt->ir_router_node : ipx->ipx_dest.node);
diff -u --recursive --new-file v2.1.29/linux/net/lapb/lapb_timer.c linux/net/lapb/lapb_timer.c
--- v2.1.29/linux/net/lapb/lapb_timer.c	Thu Feb 27 10:57:32 1997
+++ linux/net/lapb/lapb_timer.c	Thu Mar 20 18:17:15 1997
@@ -53,7 +53,7 @@
 
 	lapb->timer.data     = (unsigned long)lapb;
 	lapb->timer.function = &lapb_timer;
-	lapb->timer.expires  = jiffies + 10;
+	lapb->timer.expires  = jiffies + (HZ / 10);
 
 	add_timer(&lapb->timer);
 }
diff -u --recursive --new-file v2.1.29/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v2.1.29/linux/net/netrom/af_netrom.c	Thu Feb 27 10:57:32 1997
+++ linux/net/netrom/af_netrom.c	Thu Mar 20 18:17:15 1997
@@ -418,19 +418,18 @@
 	char *optval, int optlen)
 {
 	struct sock *sk = sock->sk;
-	int err, opt;
+	int opt;
 
 	if (level != SOL_NETROM)
 		return -ENOPROTOOPT;
 
-	if(optlen<sizeof(int))
+	if (optlen < sizeof(int))
 		return -EINVAL;
-		
-	if(get_user(opt, (int *)optval))
+
+	if (get_user(opt, (int *)optval))
 		return -EFAULT;
 
-	switch (optname) 
-	{
+	switch (optname) {
 		case NETROM_T1:
 			if (opt < 1)
 				return -EINVAL;
@@ -480,7 +479,7 @@
 	if (level != SOL_NETROM)
 		return -ENOPROTOOPT;
 	
-	if(get_user(len,optlen))
+	if (get_user(len, optlen))
 		return -EFAULT;
 
 	switch (optname) {
@@ -512,11 +511,14 @@
 			return -ENOPROTOOPT;
 	}
 
-	len=min(len,sizeof(int));
-	if(put_user(len, optlen))
+	len = min(len, sizeof(int));
+
+	if (put_user(len, optlen))
 		return -EFAULT;
-	if(copy_to_user(optval,&val,len))
+
+	if (copy_to_user(optval, &val, len))
 		return -EFAULT;
+
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.29/linux/net/netrom/nr_dev.c linux/net/netrom/nr_dev.c
--- v2.1.29/linux/net/netrom/nr_dev.c	Thu Feb 27 10:57:32 1997
+++ linux/net/netrom/nr_dev.c	Thu Mar 20 18:17:15 1997
@@ -175,7 +175,9 @@
 {
 	dev->tbusy = 0;
 	dev->start = 1;
+
 	MOD_INC_USE_COUNT;
+
 	ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
 
 	return 0;
@@ -185,8 +187,11 @@
 {
 	dev->tbusy = 1;
 	dev->start = 0;
+
 	ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+
 	MOD_DEC_USE_COUNT;
+
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.29/linux/net/netrom/nr_route.c linux/net/netrom/nr_route.c
--- v2.1.29/linux/net/netrom/nr_route.c	Thu Feb 27 10:57:32 1997
+++ linux/net/netrom/nr_route.c	Thu Mar 20 18:17:15 1997
@@ -686,12 +686,10 @@
 	struct device *dev;
 	unsigned char *dptr;
 
-#ifdef CONFIG_FIREWALL
-	if (ax25 != NULL && call_in_firewall(PF_NETROM, skb->dev, skb->data, NULL) != FW_ACCEPT)
+	if (ax25 != NULL && call_in_firewall(PF_NETROM, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
 		return 0;
-	if (ax25 == NULL && call_out_firewall(PF_NETROM, skb->dev, skb->data, NULL) != FW_ACCEPT)
+	if (ax25 == NULL && call_out_firewall(PF_NETROM, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
 		return 0;
-#endif
 
 	nr_src  = (ax25_address *)(skb->data + 0);
 	nr_dest = (ax25_address *)(skb->data + 7);
@@ -722,10 +720,8 @@
 	if ((dev = nr_dev_first()) == NULL)
 		return 0;
 
-#ifdef CONFIG_FIREWALL
-	if (ax25 != NULL && call_fw_firewall(PF_NETROM, skb->dev, skb->data, NULL) != FW_ACCEPT)
+	if (ax25 != NULL && call_fw_firewall(PF_NETROM, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
 		return 0;
-#endif
 
 	dptr  = skb_push(skb, 1);
 	*dptr = AX25_P_NETROM;
diff -u --recursive --new-file v2.1.29/linux/net/netrom/nr_timer.c linux/net/netrom/nr_timer.c
--- v2.1.29/linux/net/netrom/nr_timer.c	Sun Feb  2 05:18:52 1997
+++ linux/net/netrom/nr_timer.c	Thu Mar 20 18:17:15 1997
@@ -55,7 +55,7 @@
 
 	sk->timer.data     = (unsigned long)sk;
 	sk->timer.function = &nr_timer;
-	sk->timer.expires  = jiffies+10;
+	sk->timer.expires  = jiffies + (HZ / 10);
 
 	add_timer(&sk->timer);
 }
diff -u --recursive --new-file v2.1.29/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.29/linux/net/netsyms.c	Tue Mar  4 10:25:27 1997
+++ linux/net/netsyms.c	Thu Mar 20 18:17:16 1997
@@ -36,6 +36,7 @@
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
 #include <linux/in6.h>
 #include <net/ndisc.h>
+#include <net/dst.h>
 #include <net/transp_v6.h>
 #endif
 
@@ -121,6 +122,11 @@
 EXPORT_SYMBOL(ntbl_walk_table);
 EXPORT_SYMBOL(neigh_tbl_run_bh);
 
+/*	dst_entry	*/
+EXPORT_SYMBOL(dst_alloc);
+EXPORT_SYMBOL(__dst_free);
+EXPORT_SYMBOL(dst_total);
+
 /* Needed by smbfs.o */
 EXPORT_SYMBOL(__scm_destroy);
 EXPORT_SYMBOL(__scm_send);
@@ -137,6 +143,10 @@
 EXPORT_SYMBOL(sklist_insert_socket);
 #endif
 
+#ifdef CONFIG_SMB_FS_MODULE
+EXPORT_SYMBOL(scm_detach_fds);
+#endif
+
 #ifdef CONFIG_INET
 /* Internet layer registration */
 EXPORT_SYMBOL(inet_add_protocol);
@@ -182,7 +192,6 @@
 EXPORT_SYMBOL(ip_queue_xmit);
 EXPORT_SYMBOL(csum_partial);
 EXPORT_SYMBOL(dev_lockct);
-EXPORT_SYMBOL(ndisc_eth_hook);
 EXPORT_SYMBOL(memcpy_fromiovecend);
 EXPORT_SYMBOL(csum_partial_copy_fromiovecend);
 EXPORT_SYMBOL(__release_sock);
@@ -218,7 +227,7 @@
 EXPORT_SYMBOL(tcp_v4_send_check);
 EXPORT_SYMBOL(tcp_v4_conn_request);
 EXPORT_SYMBOL(tcp_v4_syn_recv_sock);
-EXPORT_SYMBOL(tcp_v4_backlog_rcv);
+EXPORT_SYMBOL(tcp_v4_do_rcv);
 EXPORT_SYMBOL(tcp_v4_connect);
 EXPORT_SYMBOL(__ip_chk_addr);
 EXPORT_SYMBOL(net_reset_timer);
@@ -277,6 +286,7 @@
 EXPORT_SYMBOL(unregister_netdev);
 EXPORT_SYMBOL(ether_setup);
 EXPORT_SYMBOL(dev_new_index);
+EXPORT_SYMBOL(dev_get_by_index);
 EXPORT_SYMBOL(eth_type_trans);
 EXPORT_SYMBOL(eth_copy_and_sum);
 EXPORT_SYMBOL(alloc_skb);
diff -u --recursive --new-file v2.1.29/linux/net/rose/af_rose.c linux/net/rose/af_rose.c
--- v2.1.29/linux/net/rose/af_rose.c	Thu Feb 27 10:57:32 1997
+++ linux/net/rose/af_rose.c	Thu Mar 20 18:17:16 1997
@@ -469,13 +469,13 @@
 	if (level != SOL_ROSE)
 		return -ENOPROTOOPT;
 
-	if(optlen<sizeof(int))
+	if (optlen < sizeof(int))
 		return -EINVAL;
-	if(get_user(opt, (int *)optval))
+
+	if (get_user(opt, (int *)optval))
 		return -EFAULT;
 
-	switch (optname) 
-	{
+	switch (optname) {
 		case ROSE_T1:
 			if (opt < 1)
 				return -EINVAL;
@@ -525,7 +525,7 @@
 	if (level != SOL_ROSE)
 		return -ENOPROTOOPT;
 		
-	if(get_user(len,optlen))
+	if (get_user(len, optlen))
 		return -EFAULT;
 	
 	switch (optname) {
@@ -557,11 +557,14 @@
 			return -ENOPROTOOPT;
 	}
 
-	len=min(len,sizeof(int));
-	if(put_user(len,optlen))
+	len = min(len, sizeof(int));
+
+	if (put_user(len, optlen))
 		return -EFAULT;
-	if(copy_to_user(optval,&val,len))
+
+	if (copy_to_user(optval, &val, len))
 		return -EFAULT;
+
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.29/linux/net/rose/rose_dev.c linux/net/rose/rose_dev.c
--- v2.1.29/linux/net/rose/rose_dev.c	Thu Feb 27 10:57:32 1997
+++ linux/net/rose/rose_dev.c	Thu Mar 20 18:17:16 1997
@@ -145,10 +145,11 @@
 
 static int rose_open(struct device *dev)
 {
-	MOD_INC_USE_COUNT;
 	dev->tbusy = 0;
 	dev->start = 1;
 
+	MOD_INC_USE_COUNT;
+
 	ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
 
 	return 0;
@@ -156,9 +157,10 @@
 
 static int rose_close(struct device *dev)
 {
-	MOD_DEC_USE_COUNT;
 	dev->tbusy = 1;
 	dev->start = 0;
+
+	MOD_DEC_USE_COUNT;
 
 	ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
 
diff -u --recursive --new-file v2.1.29/linux/net/rose/rose_link.c linux/net/rose/rose_link.c
--- v2.1.29/linux/net/rose/rose_link.c	Thu Feb 27 10:57:32 1997
+++ linux/net/rose/rose_link.c	Thu Mar 20 18:17:16 1997
@@ -56,7 +56,7 @@
 
 	neigh->timer.data     = (unsigned long)neigh;
 	neigh->timer.function = &rose_link_timer;
-	neigh->timer.expires  = jiffies + 10;
+	neigh->timer.expires  = jiffies + (HZ / 10);
 
 	add_timer(&neigh->timer);
 }
@@ -278,10 +278,8 @@
 {
 	unsigned char *dptr;
 
-#ifdef CONFIG_FIREWALL
-	if (call_fw_firewall(PF_ROSE, skb->dev, skb->data, NULL) != FW_ACCEPT)
+	if (call_fw_firewall(PF_ROSE, skb->dev, skb->data, NULL,&skb) != FW_ACCEPT)
 		return;
-#endif
 
 	if (!rose_link_up(neigh))
 		neigh->restarted = 0;
diff -u --recursive --new-file v2.1.29/linux/net/rose/rose_route.c linux/net/rose/rose_route.c
--- v2.1.29/linux/net/rose/rose_route.c	Thu Feb 27 10:57:32 1997
+++ linux/net/rose/rose_route.c	Thu Mar 20 18:17:16 1997
@@ -545,10 +545,8 @@
 	struct device *dev;
 	unsigned long flags;
 
-#ifdef CONFIG_FIREWALL
-	if (call_in_firewall(PF_ROSE, skb->dev, skb->data, NULL) != FW_ACCEPT)
+	if (call_in_firewall(PF_ROSE, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
 		return 0;
-#endif
 
 	frametype = skb->data[2];
 	lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
diff -u --recursive --new-file v2.1.29/linux/net/rose/rose_timer.c linux/net/rose/rose_timer.c
--- v2.1.29/linux/net/rose/rose_timer.c	Sun Feb  2 05:18:52 1997
+++ linux/net/rose/rose_timer.c	Thu Mar 20 18:17:16 1997
@@ -55,7 +55,7 @@
 
 	sk->timer.data     = (unsigned long)sk;
 	sk->timer.function = &rose_timer;
-	sk->timer.expires  = jiffies + 10;
+	sk->timer.expires  = jiffies + (HZ / 10);
 
 	add_timer(&sk->timer);
 }
diff -u --recursive --new-file v2.1.29/linux/net/socket.c linux/net/socket.c
--- v2.1.29/linux/net/socket.c	Thu Feb 27 10:57:32 1997
+++ linux/net/socket.c	Thu Mar 20 18:17:16 1997
@@ -130,7 +130,7 @@
  *	The protocol list. Each protocol is registered in here.
  */
 
-static struct net_proto_family *net_families[NPROTO];
+struct net_proto_family *net_families[NPROTO];
 
 /*
  *	Statistics counters of the socket lists
@@ -272,6 +272,7 @@
 	inode->i_gid = current->gid;
 
 	sock->inode = inode;
+	init_waitqueue(&sock->wait);
 	sock->fasync_list = NULL;
 	sock->state = SS_UNCONNECTED;
 	sock->flags = 0;
diff -u --recursive --new-file v2.1.29/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.1.29/linux/net/unix/af_unix.c	Mon Mar 17 14:54:36 1997
+++ linux/net/unix/af_unix.c	Thu Mar 20 18:17:16 1997
@@ -115,17 +115,17 @@
 
 extern __inline__ void unix_lock(unix_socket *sk)
 {
-	sk->users++;
+	sk->sock_readers++;
 }
 
 extern __inline__ int unix_unlock(unix_socket *sk)
 {
-	return sk->users--;
+	return sk->sock_readers--;
 }
 
 extern __inline__ int unix_locked(unix_socket *sk)
 {
-	return sk->users;
+	return sk->sock_readers;
 }
 
 extern __inline__ void unix_release_addr(struct unix_address *addr)
@@ -348,7 +348,7 @@
 	
 	sk->protinfo.af_unix.family=AF_UNIX;
 	sk->protinfo.af_unix.inode=NULL;
-	sk->users=1;				/* Us */
+	sk->sock_readers=1;				/* Us */
 	sk->protinfo.af_unix.readsem=MUTEX;	/* single task reading lock */
 	sk->mtu=4096;
 	sk->protinfo.af_unix.list=&unix_sockets_unbound;
@@ -1303,6 +1303,8 @@
 {
 	struct sock *sk = sock->sk;
 	unix_socket *other=unix_peer(sk);
+	
+	mode++;
 
 	if (mode&SEND_SHUTDOWN)
 	{
@@ -1382,7 +1384,7 @@
 	{
 		len+=sprintf(buffer+len,"%p: %08X %08X %08lX %04X %02X %5ld",
 			s,
-			s->users,
+			s->sock_readers,
 			0,
 			s->socket ? s->socket->flags : 0,
 			s->type,
diff -u --recursive --new-file v2.1.29/linux/net/x25/af_x25.c linux/net/x25/af_x25.c
--- v2.1.29/linux/net/x25/af_x25.c	Thu Feb 27 10:57:32 1997
+++ linux/net/x25/af_x25.c	Thu Mar 20 18:17:16 1997
@@ -352,21 +352,22 @@
 	char *optval, int optlen)
 {
 	struct sock *sk = sock->sk;
-	int err, opt;
+	int opt;
 
 	if (level != SOL_X25)
 		return -ENOPROTOOPT;
 
-	if(optlen<sizeof(int))
+	if (optlen < sizeof(int))
 		return-EINVAL;
-	if(get_user(opt, (int *)optval))
+
+	if (get_user(opt, (int *)optval))
 		return -EFAULT;
 
-	switch (optname) 
-	{
+	switch (optname) {
 		case X25_QBITINCL:
 			sk->protinfo.x25->qbitincl = opt ? 1 : 0;
 			return 0;
+
 		default:
 			return -ENOPROTOOPT;
 	}
@@ -382,11 +383,10 @@
 	if (level != SOL_X25)
 		return -ENOPROTOOPT;
 
-	if(get_user(len,optlen))
+	if (get_user(len, optlen))
 		return -EFAULT;
-		
-	switch (optname) 
-	{
+
+	switch (optname) {
 		case X25_QBITINCL:
 			val = sk->protinfo.x25->qbitincl;
 			break;
@@ -395,11 +395,14 @@
 			return -ENOPROTOOPT;
 	}
 
-	len=min(len,sizeof(int));
-	if(put_user(len, optlen))
+	len = min(len, sizeof(int));
+
+	if (put_user(len, optlen))
 		return -EFAULT;
-	if(copy_to_user(optval,&val,len))
+
+	if (copy_to_user(optval, &val, len))
 		return -EFAULT;
+
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.29/linux/net/x25/x25_dev.c linux/net/x25/x25_dev.c
--- v2.1.29/linux/net/x25/x25_dev.c	Sun Jan 19 05:47:30 1997
+++ linux/net/x25/x25_dev.c	Thu Mar 20 18:17:16 1997
@@ -53,12 +53,10 @@
 	unsigned short frametype;
 	unsigned int lci;
 
-#ifdef CONFIG_FIREWALL
-	if (call_in_firewall(PF_X25, skb->dev, skb->data, NULL) != FW_ACCEPT) {
+	if (call_in_firewall(PF_X25, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
-#endif
 
 	frametype = skb->data[2];
         lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
diff -u --recursive --new-file v2.1.29/linux/net/x25/x25_link.c linux/net/x25/x25_link.c
--- v2.1.29/linux/net/x25/x25_link.c	Sun Feb  2 05:18:53 1997
+++ linux/net/x25/x25_link.c	Thu Mar 20 18:17:16 1997
@@ -58,7 +58,7 @@
 
 	neigh->timer.data     = (unsigned long)neigh;
 	neigh->timer.function = &x25_link_timer;
-	neigh->timer.expires  = jiffies + 100;
+	neigh->timer.expires  = jiffies + (HZ / 1);
 
 	add_timer(&neigh->timer);
 }
@@ -240,10 +240,8 @@
 
 void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *neigh)
 {
-#ifdef CONFIG_FIREWALL
-	if (call_fw_firewall(PF_X25, skb->dev, skb->data, NULL) != FW_ACCEPT)
+	if (call_fw_firewall(PF_X25, skb->dev, skb->data, NULL,&skb) != FW_ACCEPT)
 		return;
-#endif
 
 	switch (neigh->state) {
 		case X25_LINK_STATE_0:
diff -u --recursive --new-file v2.1.29/linux/net/x25/x25_timer.c linux/net/x25/x25_timer.c
--- v2.1.29/linux/net/x25/x25_timer.c	Sun Feb  2 05:18:53 1997
+++ linux/net/x25/x25_timer.c	Thu Mar 20 18:17:16 1997
@@ -54,7 +54,7 @@
 
 	sk->timer.data     = (unsigned long)sk;
 	sk->timer.function = &x25_timer;
-	sk->timer.expires  = jiffies + 100;
+	sk->timer.expires  = jiffies + (HZ / 1);
 
 	add_timer(&sk->timer);
 }
diff -u --recursive --new-file v2.1.29/linux/scripts/mkdep.c linux/scripts/mkdep.c
--- v2.1.29/linux/scripts/mkdep.c	Sun Jan 26 02:07:52 1997
+++ linux/scripts/mkdep.c	Mon Mar 24 16:41:58 1997
@@ -244,7 +244,8 @@
 	struct stat st;
 
 	if (fd < 0) {
-		perror("mkdep: open");
+		if (errno != ENOENT)
+			perror(filename);
 		return;
 	}
 	fstat(fd, &st);
