diff -u --recursive --new-file v2.1.78/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.78/linux/Documentation/Configure.help	Fri Jan  2 14:37:01 1998
+++ linux/Documentation/Configure.help	Mon Jan 12 14:46:16 1998
@@ -1,4 +1,4 @@
-# Maintained by Axel Boldt (boldt@math.ucsb.edu) 
+# Maintained by Axel Boldt (boldt@math.ucsb.edu)
 #
 # This version of the Linux kernel configuration help texts
 # corresponds to the kernel versions 2.1.x. Be aware that these are
@@ -333,17 +333,6 @@
   enable DMA for drives which were not enabled automatically.
   You can get the latest version of the hdparm utility via anonymous
   FTP from sunsite.unc.edu/pub/Linux/system/hardware/
-   It is safe to say Y to this question.
-
-  If your PCI system uses IDE drive(s) (as opposed to SCSI, say)
-  and is capable of bus-master DMA operation (most Pentium PCI
-  systems), 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/idedma.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
@@ -3526,28 +3515,6 @@
   sdla.o. If you want to compile it as a module, say M here and read
   Documentation/modules.txt.
 
-WAN Router
-CONFIG_WAN_ROUTER
-  Wide Area Networks (WANs), such as X.25, frame relay and leased
-  lines, are used to interconnect Local Area Networks (LANs) over vast
-  distances with data transfer rates significantly higher than those
-  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
-  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).
-  The module is called wanrouter.o.  For general information about 
-  modules read Documentation/modules.txt.
-
 CPU is too slow to handle full bandwidth
 CONFIG_CPU_IS_SLOW
 ###
@@ -3651,68 +3618,79 @@
 ### schedulers?
 ###
 
+WAN Router
+CONFIG_WAN_ROUTER
+      Wide Area Networks (WANs), such as X.25, frame relay and leased
+  lines, are used to interconnect Local Area Networks (LANs) over vast
+  distances with data transfer rates significantly higher than those
+  achievable with commonly used asynchronous modem connections.
+  Usually, a quite expensive external device called `WAN router' is
+  needed to connect to WAN.
+      As an alternative, WAN router can be build into Linux kernel.
+  With relatively inexpensive WAN interface cards available on the
+  market, a perfectly usable router can be built for less than half a
+  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 router 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.
+
 WAN Drivers
 CONFIG_WAN_DRIVERS
-  Say Y to this option if you are planning to use your Linux box as a
-  WAN ( = Wide Area Network) router ( = device used to interconnect
-  local area networks over wide area communication links, such as
-  leased lines and public data networks, e.g. X.25 and frame relay)
-  and you will be offered a list of WAN drivers currently available.
-  For more information, read
-  Documentation/networking/wan-router.txt. 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 WAN
-  drivers. If unsure, say N.
+      Say 'Y' to this option if you are planning to use your Linux box
+  as a WAN router ( = device used to interconnect local area networks
+  over wide area communication links, such as leased lines and public
+  data networks, e.g. X.25 and frame relay) and you will be offered a
+  list of WAN drivers currently available.  For more information, read
+  Documentation/networking/wan-router.txt.
 
 Sangoma WANPIPE(tm) multiprotocol cards
 CONFIG_VENDOR_SANGOMA
-  WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com; to
-  browse the WWW, you need to have access to a machine on the Internet
-  that has a program like lynx or netscape) 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. 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 and read
-  Documentation/networking/wanpipe.txt.  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.
+      WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com)
+  is a family of intelligent multiprotocol WAN adapter 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.
 
 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 than you
+      Enter number of WANPIPE adapters installed in your machine.  The
+  driver can support up to 8 cards.  You may enter more that 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 a WANPIPE card
-  to an X.25 network. You should then also have said Y to "CCITT X.25
-  Packet Layer" and "LAPB Data Link Driver", above. If you say N, the
-  X.25 support will not be included in the driver (saves about 16K of
-  kernel memory).
+      Say 'Y' to this option, if you are planning to connect 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 a WANPIPE card
-  to a frame relay network. You should then also have said Y to "Frame
-  Relay (DLCI) support", above. If you say N, the frame relay
+      Say 'Y' to this option, if you are planning to connect 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 a WANPIPE card
-  to a leased line using Point-to-Point protocol (PPP). You should
-  then also have said Y to "PPP (point-to-point) support", above. If
-  you say N, the PPP support will not be included in the driver (saves
+      Say 'Y' to this option, if you are planning to connect 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).
 
+
+ Sun LANCE Ethernet support
+ CONFIG_SUN_LANCE
+   This is support for lance ethernet cards on Sun workstations such as
 Sun LANCE Ethernet support
 CONFIG_SUN_LANCE
   This is support for lance ethernet cards on Sun workstations such as
@@ -7576,6 +7554,13 @@
   removed from the running kernel whenever you want). If you want to
   compile it as a module, say M here and read
   Documentation/modules.txt.
+
+Colour QuickCam Video For Linux
+CONFIG_VIDEO_CQCAM
+  This is the video4linux driver for the colour version of the Connectix
+  Quickcam.  If you have one of these cameras, say Y here, otherwise say N.
+  This driver does not work with the original monochrome Quickcam,
+  Quickcam VC or QuickClip.  It is also available as a module (c-qcam.o).
 
 Mediavision Pro Movie Studio Video For Linux
 CONFIG_VIDEO_PMS
diff -u --recursive --new-file v2.1.78/linux/Documentation/filesystems/fat_cvf.txt linux/Documentation/filesystems/fat_cvf.txt
--- v2.1.78/linux/Documentation/filesystems/fat_cvf.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/filesystems/fat_cvf.txt	Thu Jan  8 14:02:41 1998
@@ -0,0 +1,190 @@
+This is the main documentation for the CVF-FAT filesystem extension.  31DEC1997
+
+
+Table of Contents:
+
+1. The idea of CVF-FAT
+2. Restrictions
+3. Mount options
+4. Description of the CVF-FAT interface
+5. CVF Modules
+
+------------------------------------------------------------------------------
+
+
+1. The idea of CVF-FAT
+------------------------------------------------------------------------------
+
+CVF-FAT is a FAT filesystem extension that provides a generic interface for
+Compressed Volume Files in FAT partitions. Popular CVF software, for
+example, are Microsoft's Doublespace/Drivespace and Stac's Stacker.
+Using the CVF-FAT interface, it is possible to load a module that handles
+all the low-level disk access that has to do with on-the-fly compression
+and decompression. All other part of FAT filesystem access is still handled
+by the FAT, MSDOS or VFAT or even UMSDOS driver.
+
+CVF access works by redirecting certain low-level routines from the FAT
+driver to a loadable, CVF-format specific module. This module must fake
+a normal FAT filesystem to the FAT driver while doing all the extra stuff
+like compression and decompression silently.
+
+
+2. Restrictions
+------------------------------------------------------------------------------
+
+- BMAP problems
+
+  CVF filesystems cannot do bmap. It's impossible by principle. Thus
+  all actions that require bmap do not work (swapping, writable mmapping).
+  Read-only mmapping works because the FAT driver has a hack for this
+  situation :) Well, with some tricks writable mmapping could work,
+  (proof: they did under old dmsdos), but..... (hint: readpage/writepage
+  interface functions) ...... but the FAT driver has to support them 
+  first without bmap :-)
+
+  We'll see. If someone points me to an application that needs this, I
+  might be persuaded to implement it :). CVF-FAT is already prepared
+  for using readpage.
+  
+- DOSEMU users attention 
+
+  You may have to unmount all CVF partitions before running DOSEMU depending 
+  on your configuration. If DOSEMU is configured to use wholedisk or 
+  partition access (this is often the case to let DOSEMU access 
+  compressed partitions) there's a risk of destroying your compressed 
+  partitions or crashing your system because of confused drivers.
+  
+  Note that it is always safe to redirect the compressed partitions with 
+  lredir or emufs.sys. Refer to the DOSEMU documentation for details.
+
+
+3. Mount options
+------------------------------------------------------------------------------
+
+The CVF-FAT extension currently adds the following options to the FAT
+driver's standard options:
+
+  cvf_format=xxx
+    Forces the driver to use the CVF module "xxx" instead of auto-detection.
+    This is only necessary if the CVF format is not recognized corrrectly
+    because of bugs or incompatibilities in the CVF modules. (It skips
+    the detect_cvf call.) "xxx" may be the text "none" (without the quotes)
+    to inhibit using any of the loaded CVF modules, just in case a CVF
+    module insists on mounting plain FAT filesystems by misunderstanding :)
+
+  cvf_options=yyy
+    Option string passed to the CVF module. I.e. only the "yyy" is passed
+    (without the quotes). The documentation for each CVF module should 
+    explain it since it is interpreted only by the CVF module. Note that 
+    the string must not contain a comma (",") - this would lead to 
+    misinterpretation by the FAT driver, which would recognize the text 
+    after a comma as a FAT driver option and might get confused or print 
+    strange error messages. The documentation for the CVF module should 
+    offer a different seperation symbol, for example the dot ".", which
+    is only valid inside the string "yyy".
+
+
+4. Description of the CVF-FAT interface
+------------------------------------------------------------------------------
+
+Assuming you want to write your own CVF module, you need to write a lot of
+interface funtions. Most of them are covered in the kernel documentation
+you can find on the net, and thus won't be described here. They have been
+marked with "[...]" :-) Take a look at include/linux/fat_cvf.h.
+
+struct cvf_format
+{ int cvf_version;
+  char* cvf_version_text;
+  unsigned long int flags;
+  int (*detect_cvf) (struct super_block*sb);
+  int (*mount_cvf) (struct super_block*sb,char*options);
+  int (*unmount_cvf) (struct super_block*sb);
+  [...]
+  void (*cvf_zero_cluster) (struct inode*inode,int clusternr);
+}
+
+This structure defines the capabilities of a CVF module. It must be filled
+out completely by a CVF module. Consider it as a kind of form that is used
+to introduce the module to the FAT/CVF-FAT driver.
+
+It contains...
+  - cvf_version:
+      A version id which must be uniqe. Choose one.
+  - cvf_version_text:
+      A human readable version string that should be one short word 
+      describing the CVF format the module implements. This text is used
+      for the cvf_format option. This name must also be uniqe.
+  - flags:
+      Bit coded flags, currently only used for a readpage/mmap hack that 
+      provides both mmap and readpage functionality. If CVF_USE_READPAGE
+      is set, mmap is set to generic_file_mmap and readpage is caught
+      and redirected to the cvf_readpage function. If it is not set,
+      readpage is set to generic_readpage and mmap is caught and redirected
+      to cvf_mmap.
+  - detect_cvf:
+      A function that is called to decide whether the filesystem is a CVF of
+      the type the module supports. The detect_cvf function must return 0
+      for "NO, I DON'T KNOW THIS GARBAGE" or anything !=0 for "YES, THIS IS
+      THE KIND OF CVF I SUPPORT". The function must maintain the module
+      usage counters for safety, i.e. do MOD_INC_USE_COUNT at the beginning
+      and MOD_DEC_USE_COUNT at the end. The function *must not* assume that
+      successful recongition would lead to a call of the mount_cvf function
+      later. 
+  - mount_cvf:
+      A function that sets up some values or initializes something additional
+      to what has to be done when a CVF is mounted. This is called at the
+      end of fat_read_super and must return 0 on success. Definitely, this
+      function must increment the module usage counter by MOD_INC_USE_COUNT.
+      This mount_cvf function is also responsible for interpreting a CVF
+      module specific option string (the "yyy" from the FAT mount option
+      "cvf_options=yyy") which cannot contain a comma (use for example the
+      dot "." as option separator symbol).
+  - unmount_cvf:
+      A function that is called when the filesystem is unmounted. Most likely
+      it only frees up some memory and calls MOD_DEC_USE_COUNT. The return
+      value might be ignored (it currently is ignored).
+  - [...]:
+      All other interface functions are "caught" FAT driver functions, i.e.
+      are executed by the FAT driver *instead* of the original FAT driver
+      functions. NULL means use the original FAT driver functions instead.
+      If you really want "no action", write a function that does nothing and 
+      hang it in instead.
+  - cvf_zero_cluster:
+      The cvf_zero_cluster function is called when the fat driver wants to
+      zero out a (new) cluster. This is important for directories (mkdir).
+      If it is NULL, the FAT driver defaults to overwriting the whole
+      cluster with zeros. Note that clusternr is absolute, not relative
+      to the provided inode.
+
+Notes:
+  1. The cvf_bmap function should be ignored. It really should never
+     get called from somewhere. I recommend redirecting it to a panic
+     or fatal error message so bugs show up immediately.
+  2. The cvf_writepage function is ignored. This is because the fat
+     driver doesn't support it. This might change in future. I recommend
+     setting it to NULL (i.e use default).
+
+int register_cvf_format(struct cvf_format*cvf_format);
+  If you have just set up a variable containing the above structure,
+  call this function to introduce your CVF format to the FAT/CVF-FAT
+  driver. This is usually done in init_module. Be sure to check the
+  return value. Zero means success, everything else causes a kernel
+  message printed in the syslog describing the error that occured.
+  Typical errors are:
+    - a module with the same version id is already registered or 
+    - too many CVF formats. Hack fs/fat/cvf.c if you need more.
+
+int unregister_cvf_format(struct cvf_format*cvf_format);
+  This is usually called in cleanup_module. Return value =0 means
+  success. An error only occurs if you try to unregister a CVF format
+  that has not been previously registered. The code uses the version id
+  to distinguish the modules, so be sure to keep it uniqe.
+
+5. CVS Modules
+------------------------------------------------------------------------------
+
+Refer to the dmsdos module (the successor of the dmsdos filesystem) for a
+sample implementation.  It can currently be found at
+
+  ftp://fb9nt.uni-duisburg.de/pub/linux/dmsdos
+
diff -u --recursive --new-file v2.1.78/linux/Documentation/networking/wan-router.txt linux/Documentation/networking/wan-router.txt
--- v2.1.78/linux/Documentation/networking/wan-router.txt	Thu Jul 17 10:06:03 1997
+++ linux/Documentation/networking/wan-router.txt	Mon Jan 12 14:46:16 1998
@@ -1,6 +1,8 @@
 ------------------------------------------------------------------------------
 WAN Router for Linux Operating System
 ------------------------------------------------------------------------------
+Version 2.0.1 - Nov 28, 1997
+Version 2.0.0 - Nov 06, 1997
 Version 1.0.3 - June 3, 1997
 Version 1.0.1 - January 30, 1997
 Author: Jaspreet Singh <jaspreet@sangoma.com>
@@ -8,6 +10,11 @@
 Copyright (c) 1995-1997 Sangoma Technologies Inc.
 ------------------------------------------------------------------------------
 
+
+WARNING:  This Version of WANPIPE supports only the S508 and S508/FT1 cards.  
+IF YOU OWN A S502E OR A S508 CARD THEN PLEASE CONTACT SANGOMA TECHNOLOGIES FOR 
+AN UPGRADE.
+
 INTRODUCTION
 
 Wide Area Networks (WANs) are used to interconnect Local Area Networks (LANs)
@@ -73,10 +80,14 @@
 To ba able to use Linux WAN Router you will also need a WAN Tools package
 available from
 
-	ftp.sangoma.com/pub/linux/wantools-X.Y.Z.tgz
+	ftp.sangoma.com/pub/linux/vX.Y.Z/wantools-X.Y.Z.tgz
+			or
+	ftp.sangoma.com/pub/linux/vX.Y.Z/wanpipe-X.Y.Z.tgz
+
+where vX.Y.Z represent the linux kernel version number.
 
 For technical questions and/or comments regarding this product please e-mail
-to genek@compuserve.com or dm@sangoma.com.
+to jaspreet@sangoma.com or dm@sangoma.com.
 
 
 
@@ -117,19 +128,55 @@
 
 
 REVISION HISTORY
+
+2.0.1	Nov 28, 1997	      -	Protection of "enable_irq()" while
+                                "disable_irq()" has been enabled from any other
+                                routine (for Frame Relay, PPP and X25).
+			      - Added additional Stats for Fpipemon and Ppipemon                              - Improved Load Sharing for multiple boards.
+
+
+2.0.0   Nov 07, 1997          - Implemented protection of RACE conditions by
+                                critical flags for FRAME RELAY and PPP.
+                              - DLCI List interrupt mode implemented.
+                              - IPX support in FRAME RELAY and PPP.
+                              - IPX Server Support (MARS)
+                              - More driver specific stats included in FPIPEMON
+                                and PIPEMON.
+
+1.0.5   July 28, 1997         - Configurable T391,T392,N391,N392,N393 for Frame
+                                Relay in router.conf.
+                              - Configurable Memory Address through router.conf
+                                for Frame Relay, PPP and X.25. (commenting this
+                                out enables auto-detection).
+                              - Fixed freeing up received buffers using kfree()
+                                for Frame Relay and X.25.
+                              - Protect sdla_peek() by calling save_flags(),
+                                cli() and restore_flags().
+                              - Changed number of Trace elements from 32 to 20
+                              - Added DLCI specific data monitoring in FPIPEMON.
+
+1.0.4   July 10, 1997         - S508/FT1 monitoring capability in fpipemon and
+                                ppipemon utilities.
+                              - Configurable TTL for UDP packets.
+                              - Multicast and Broadcast IP source addresses are
+                                silently discarded.
+
 1.0.3	June 3, 1997
-	o UDP port for multiple boards (Frame relay, PPP)
-	o Continuous Transmission of Configure Request Packet for PPP (this
-	  support is only added for 508 cards)
-	o Connection Timeout for PPP changed from 900 to 0
-	o Flow Control for multiple boards and multiple channels (Frame Relay)
+	 		      -	UDP port for multiple boards (Frame relay, PPP)
+	 			Continuous Transmission of Configure Request 
+			      -	Packet for PPP (this support is only added for 
+			   	508 cards)
+	 		      -	Connection Timeout for PPP changed from 900 to 0
+	 	              -	Flow Control for multiple boards and multiple 
+				channels (Frame Relay)
 
 1.0.1	January 30, 1997
 
-	o Implemented user-readable status and statistics via /proc filesystem
+			      -	Implemented user-readable status and statistics
+				via /proc filesystem
 
 1.0.0	December 31, 1996
 
-	o Initial version
+			      -	Initial version
 
 >>>>>> END <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
diff -u --recursive --new-file v2.1.78/linux/Documentation/networking/wanpipe.txt linux/Documentation/networking/wanpipe.txt
--- v2.1.78/linux/Documentation/networking/wanpipe.txt	Sat Nov 29 11:25:08 1997
+++ linux/Documentation/networking/wanpipe.txt	Mon Jan 12 14:46:16 1998
@@ -1,9 +1,9 @@
 ------------------------------------------------------------------------------
 WANPIPE(tm) Multiprotocol WAN Driver for Linux WAN Router
 ------------------------------------------------------------------------------
-Release 3.1.0
-January 30, 1997
-Author: Gene Kozin <genek@compuserve.com>
+Release 4.1
+November 17, 1997
+Author: Jaspreet Singh <jaspreet@sangoma.com>
 Copyright (c) 1995-1997 Sangoma Technologies Inc.
 ------------------------------------------------------------------------------
 
@@ -23,8 +23,13 @@
 is available via the Internet from Sangoma Technologies' anonymous FTP server:
 
 	ftp.sangoma.com/pub/linux/wantools-X.Y.Z.tgz
+			or
+	ftp.sangoma.com/pub/linux/wanpipe-X.Y.Z.tgz
 
-For technical questions and/or comments please e-mail to genek@compuserve.com.
+The name of the package differ only due to naming convention.  The functionalityof wantools and wanpipe packages are the same.  The latest version of WAN 
+Drivers is wanpipe-2.0.0.
+
+For technical questions and/or comments please e-mail to jaspreet@sangoma.com.
 For general inquiries please contact Sangoma Technologies Inc. by
 
 	Hotline:	1-800-388-2475	(USA and Canada, toll free)
@@ -53,10 +58,13 @@
 
 NEW IN THIS RELEASE
 
- o Implemented as WAN Link Driver compliant with Linux WAN Router interface
- o Added support for X.25 protocol
- o Miscellaneous bug fixes and performance improvements
-
+ o This Version of WANPIPE supports only the S508 and S508/FT1 cards.  IF YOU
+   OWN A S502E OR A S508 CARD THEN PLEASE CONTACT SANGOMA TECHNOLOGIES FOR AN
+   UPGRADE.
+ o Protection of "enable_irq()" while "disable_irq()" has been enabled from 
+   any other routine (for Frame Relay, PPP and X25).
+ o Added additional Stats for Fpipemon and Ppipemon.
+ o Improved Load Sharing for multiple boards
 
 
 FILE LIST
@@ -64,22 +72,38 @@
 drivers/net:
 	README.wanpipe	This file
 	sdladrv.c	SDLA support module source code
-	wpmain.c	WANPIPE driver module main source code
-	wpx.c		WANPIPE driver module X.25 source code
-	wpf.c		WANPIPE driver module frame relay source code
-	wpp.c		WANPIPE driver module PPP source code
+	sdla_fr.c	SDLA Frame Relay source code
+	sdla_ppp.c	SDLA PPP source code
+	sdla_x25.c	SDLA X25 source code
+	sdlamain.c	SDLA support source code
+
+include/linux:
 	sdla_x25.h	SDLA X.25 firmware API definitions
 	sdla_fr.h	SDLA frame relay firmware API definitions
 	sdla_ppp.h	SDLA PPP firmware API definitions
-
-include/linux:
 	wanpipe.h	WANPIPE API definitions
 	sdladrv.h	SDLA support module API definitions
 	sdlasfm.h	SDLA firmware module definitions
-
+	router.h	
 
 
 REVISION HISTORY
+
+4.1	November 28, 1997
+        o Protection of "enable_irq()" while "disable_irq()" has been enabled 
+	  from any other routine (for Frame Relay, PPP and X25).
+        o Added additional Stats for Fpipemon and Ppipemon
+	o Improved Load Sharing for multiple boards
+
+
+4.0	November 06, 1997
+ 	o Implemented better protection of RACE conditions by critical flags for
+ 	  FRAME RELAY, PPP and X25.
+ 	o DLCI List interrupt mode implemented for DLCI specific CIR.
+ 	o IPX support for FRAME RELAY, PPP and X25.
+ 	o IPX Server Support (MARS) for FRAME RELAY, PPP and X25.
+ 	o More driver specific stats included. 
+	o MULTICAST for FRAME RELAY and PPP.
 
 3.1.0	January 30, 1997
 
diff -u --recursive --new-file v2.1.78/linux/Documentation/powerpc/00-INDEX linux/Documentation/powerpc/00-INDEX
--- v2.1.78/linux/Documentation/powerpc/00-INDEX	Sat Nov 29 11:25:09 1997
+++ linux/Documentation/powerpc/00-INDEX	Mon Jan 12 15:18:12 1998
@@ -7,5 +7,8 @@
 	- this file
 ppc_htab.txt
 	- info about the Linux/PPC /proc/ppc_htab entry
+smp.txt
+	- use and state info about Linux/PPC on MP machines
 sound.txt
 	- info on sound support under Linux/PPC
+
diff -u --recursive --new-file v2.1.78/linux/Documentation/powerpc/smp.txt linux/Documentation/powerpc/smp.txt
--- v2.1.78/linux/Documentation/powerpc/smp.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/powerpc/smp.txt	Mon Jan 12 15:18:12 1998
@@ -0,0 +1,20 @@
+                 Information about Linux/PPC SMP mode
+=====================================================================
+
+This document and the related code was written by me (Cort Dougan), please
+email me (cort@cs.nmt.edu) if you have questions, comments or corrections.
+
+SMP support for Linux/PPC is still in its early stages and likely to
+be buggy for a while.  If you want to help by writing code or testing
+different hardware please email me!
+
+1. State of Supported Hardware
+
+  UMAX s900
+    The second processor on this machine boots up just fine and
+    enters its idle loop.  Hopefully a completely working SMP kernel
+    on this machine will be done shortly.
+
+  BeBox
+    BeBox support hasn't been added to the 2.1.X kernels from 2.0.X
+    but work is being done and SMP support for BeBox is in the works.
diff -u --recursive --new-file v2.1.78/linux/Documentation/spinlocks.txt linux/Documentation/spinlocks.txt
--- v2.1.78/linux/Documentation/spinlocks.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/spinlocks.txt	Mon Jan 12 14:46:16 1998
@@ -0,0 +1,186 @@
+On Fri, 2 Jan 1998, Doug Ledford wrote:
+> 
+> I'm working on making the aic7xxx driver more SMP friendly (as well as
+> importing the latest FreeBSD sequencer code to have 7895 support) and wanted
+> to get some info from you.  The goal here is to make the various routines
+> SMP safe as well as UP safe during interrupts and other manipulating
+> routines.  So far, I've added a spin_lock variable to things like my queue
+> structs.  Now, from what I recall, there are some spin lock functions I can
+> use to lock these spin locks frmo other use as oppossed to a (nasty)
+> save_flags(); cli(); stuff; restore_flags(); construct.  Where do I find
+> these routines and go about making use of them?  Do they only lock on a
+> per-processor basis or can they also lock say an interrupt routine from
+> mucking with a queue if the queue routine was manipulating it when the
+> interrupt occured, or should I still use a cli(); based construct on that
+> one?
+
+See <asm/spinlock.h>. The basic version is:
+
+   spinlock_t xxx_lock = SPIN_LOCK_UNLOCKED;
+
+
+	unsigned long flags;
+
+	spin_lock_irqsave(&xxx_lock, flags);
+	... critical section here ..
+	spin_unlock_irqrestore(&xxx_lock, flags);
+
+and the above is always safe. It will disable interrupt _locally_, but the
+spinlock itself will guarantee the global lock, so it will guarantee that
+there is only one thread-of-control within the region(s) protected by that
+lock. 
+
+Note that it works well even under UP - the above sequence under UP
+essentially is just the same as doing a
+
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	 ... critical section ...
+	restore_flags(flags);
+
+so the code does _not_ need to worry about UP vs SMP issues: the spinlocks
+work correctly under both (and spinlocks are actually more efficient on
+architectures that allow doing the "save_flags + cli" in one go because I
+don't export that interface normally).
+
+NOTE NOTE NOTE! The reason the spinlock is so much faster than a global
+interrupt lock under SMP is exactly because it disables interrupts only on
+the local CPU. The spin-lock is safe only when you _also_ use the lock
+itself to do locking across CPU's, which implies that EVERYTHING that
+touches a shared variable has to agree about the spinlock they want to
+use.
+
+The above is usually pretty simple (you usually need and want only one
+spinlock for most things - using more than one spinlock can make things a
+lot more complex and even slower and is usually worth it only for
+sequences that you _know_ need to be split up: avoid it at all cost if you
+aren't sure). HOWEVER, it _does_ mean that if you have some code that does
+
+	cli();
+	.. critical section ..
+	sti();
+
+and another sequence that does
+
+	spin_lock_irqsave(flags);
+	.. critical section ..
+	spin_unlock_irqrestore(flags);
+
+then they are NOT mutually exclusive, and the critical regions can happen
+at the same time on two different CPU's. That's fine per se, but the
+critical regions had better be critical for different things (ie they
+can't stomp on each other). 
+
+The above is a problem mainly if you end up mixing code - for example the
+routines in ll_rw_block() tend to use cli/sti to protect the atomicity of
+their actions, and if a driver uses spinlocks instead then you should
+think about issues like the above..
+
+This is really the only really hard part about spinlocks: once you start
+using spinlocks they tend to expand to areas you might not have noticed
+before, because you have to make sure the spinlocks correctly protect the
+shared data structures _everywhere_ they are used. The spinlocks are most
+easily added to places that are completely independent of other code (ie
+internal driver data structures that nobody else ever touches, for
+example). 
+
+----
+
+Lesson 2: reader-writer spinlocks.
+
+If your data accesses have a very natural pattern where you usually tend
+to mostly read from the shared variables, the reader-writer locks
+(rw_lock) versions of the spinlocks are often nicer. They allow multiple
+readers to be in the same critical region at once, but if somebody wants
+to change the variables it has to get an exclusive write lock. The
+routines look the same as above:
+
+   rwlock_t xxx_lock = RW_LOCK_UNLOCKED;
+
+
+	unsigned long flags;
+
+	read_lock_irqsave(&xxx_lock, flags);
+	.. critical section that only reads the info ...
+	read_unlock_irqrestore(&xxx_lock, flags);
+
+	write_lock_irqsave(&xxx_lock, flags);
+	.. read and write exclusive access to the info ...
+	write_unlock_irqrestore(&xxx_lock, flags);
+
+The above kind of lock is useful for complex data structures like linked
+lists etc, especially when you know that most of the work is to just
+traverse the list searching for entries without changing the list itself,
+for example. Then you can use the read lock for that kind of list
+traversal, which allows many concurrent readers. Anything that _changes_
+the list will have to get the write lock. 
+
+Note: you cannot "upgrade" a read-lock to a write-lock, so if you at _any_
+time need to do any changes (even if you don't do it every time), you have
+to get the write-lock at the very beginning. I could fairly easily add a
+primitive to create a "upgradeable" read-lock, but it hasn't been an issue
+yet. Tell me if you'd want one. 
+
+----
+
+Lesson 3: spinlocks revisited.
+
+The single spin-lock primitives above are by no means the only ones. They
+are the most safe ones, and the ones that work under all circumstances,
+but partly _because_ they are safe they are also fairly slow. They are
+much faster than a generic global cli/sti pair, but slower than they'd
+need to be, because they do have to disable interrupts (which is just a
+single instruction on a x86, but it's an expensive one - and on other
+architectures it can be worse).
+
+If you have a case where you have to protect a data structure across
+several CPU's and you want to use spinlocks you can potentially use
+cheaper versions of the spinlocks. IFF you know that the spinlocks are
+never used in interrupt handlers, you can use the non-irq versions:
+
+	spin_lock(&lock);
+	...
+	spin_unlock(&lock);
+
+(and the equivalent read-write versions too, of course). The spinlock will
+guarantee the same kind of exclusive access, and it will be much faster. 
+This is useful if you know that the data in question is only ever
+manipulated from a "process context", ie no interrupts involved. 
+
+The reasons you mustn't use these versions if you have interrupts that
+play with the spinlock is that you can get deadlocks:
+
+	spin_lock(&lock);
+	...
+		<- interrupt comes in:
+			spin_lock(&lock);
+
+where an interrupt tries to lock an already locked variable. This is ok if
+the other interrupt happens on another CPU, but it is _not_ ok if the
+interrupt happens on the same CPU that already holds the lock, because the
+lock will obviously never be released (because the interrupt is waiting
+for the lock, and the lock-holder is interrupted by the interrupt and will
+not continue until the interrupt has been processed). 
+
+(This is also the reason why the irq-versions of the spinlocks only need
+to disable the _local_ interrupts - it's ok to use spinlocks in interrupts
+on other CPU's, because an interrupt on another CPU doesn't interrupt the
+CPU that holds the lock, so the lock-holder can continue and eventually
+releases the lock). 
+
+Note that you can be clever with read-write locks and interrupts. For
+example, if you know that the interrupt only ever gets a read-lock, then
+you can use a non-irq version of read locks everywhere - because they
+don't block on each other (and thus there is no dead-lock wrt interrupts. 
+But when you do the write-lock, you have to use the irq-safe version. 
+
+For an example of being clever with rw-locks, see the "waitqueue_lock" 
+handling in kernel/sched.c - nothing ever _changes_ a wait-queue from
+within an interrupt, they only read the queue in order to know whom to
+wake up. So read-locks are safe (which is good: they are very common
+indeed), while write-locks need to protect themselves against interrupts.
+
+		Linus
+
+
diff -u --recursive --new-file v2.1.78/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.78/linux/MAINTAINERS	Tue Jan  6 09:37:32 1998
+++ linux/MAINTAINERS	Mon Jan 12 15:21:04 1998
@@ -442,6 +442,12 @@
 M:	jmaurer@cck.uni-kl.de
 S:	Maintained
 
+PCI SUBSYSTEM
+P:	Martin Mares
+M:	mj@atrey.karlin.mff.cuni.cz
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
 PCNET32 NETWORK DRIVER
 P:	Thomas Bogendoerfer
 M:	tsbogend@alpha.franken.de
@@ -537,7 +543,7 @@
 
 SVGA HANDLING:
 P:	Martin Mares
-M:	mj@k332.feld.cvut.cz
+M:	mj@atrey.karlin.mff.cuni.cz
 L:	linux-video@atrey.karlin.mff.cuni.cz
 S:	Maintained
 
diff -u --recursive --new-file v2.1.78/linux/Makefile linux/Makefile
--- v2.1.78/linux/Makefile	Tue Jan  6 09:37:32 1998
+++ linux/Makefile	Mon Jan 12 14:47:55 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 78
+SUBLEVEL = 79
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/Makefile linux/arch/alpha/Makefile
--- v2.1.78/linux/arch/alpha/Makefile	Thu May 29 21:53:03 1997
+++ linux/arch/alpha/Makefile	Mon Jan 12 14:51:14 1998
@@ -44,3 +44,6 @@
 
 archdep:
 	@$(MAKEBOOT) dep
+
+bootpfile:
+	@$(MAKEBOOT) bootpfile
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/boot/Makefile linux/arch/alpha/boot/Makefile
--- v2.1.78/linux/arch/alpha/boot/Makefile	Sun Aug  4 03:37:59 1996
+++ linux/arch/alpha/boot/Makefile	Mon Jan 12 14:51:14 1998
@@ -26,6 +26,7 @@
 	$(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $<
 
 OBJECTS = head.o main.o
+BPOBJECTS = head.o bootp.o
 TARGETS = vmlinux.gz tools/objstrip # also needed by aboot & milo
 VMLINUX = $(TOPDIR)/vmlinux
 OBJSTRIP = tools/objstrip
@@ -44,6 +45,9 @@
 	( cat tools/lxboot tools/bootlx vmlinux.nh ) > bootimage
 	tools/mkbb bootimage tools/lxboot
 
+bootpfile:	tools/bootph vmlinux.nh
+	( cat tools/bootph vmlinux.nh ) > bootpfile
+
 srmboot:	bootdevice bootimage
 	dd if=bootimage of=$(BOOTDEV) bs=512 seek=1 skip=1
 	tools/mkbb $(BOOTDEV) tools/lxboot
@@ -59,6 +63,8 @@
 #
 main.o: ksize.h
 
+bootp.o: ksize.h
+
 ksize.h: $(OBJSTRIP) vmlinux.nh
 	echo "#define KERNEL_SIZE `$(OBJSTRIP) -p vmlinux.nh /dev/null`" > $@
 
@@ -82,6 +88,9 @@
 tools/bootlx: bootloader $(OBJSTRIP)
 	$(OBJSTRIP) -vb bootloader tools/bootlx
 
+tools/bootph: bootpheader $(OBJSTRIP)
+	$(OBJSTRIP) -vb bootpheader tools/bootph
+
 $(OBJSTRIP): $(OBJSTRIP).c
 	$(HOSTCC) $(OBJSTRIP).c -o $(OBJSTRIP)
 
@@ -95,8 +104,15 @@
 		-o bootloader && strip bootloader || \
 		(rm -f bootloader && exit 1)
 
+bootpheader: $(BPOBJECTS)
+	$(LD) $(LINKFLAGS) \
+		$(BPOBJECTS) \
+		$(LIBS) \
+		-o bootpheader && strip bootpheader || \
+		(rm -f bootpheader && exit 1)
+
 clean:
 	rm -f $(TARGETS) bootloader bootimage vmlinux.nh \
-		tools/mkbb tools/bootlx tools/lxboot
+		tools/mkbb tools/bootlx tools/lxboot ksize.h
 
 dep:
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/boot/bootp.c linux/arch/alpha/boot/bootp.c
--- v2.1.78/linux/arch/alpha/boot/bootp.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/alpha/boot/bootp.c	Mon Jan 12 14:51:14 1998
@@ -0,0 +1,222 @@
+/*
+ * arch/alpha/boot/bootp.c
+ *
+ * Copyright (C) 1997 Jay Estabrook
+ *
+ * This file is used for creating a bootp file for the Linux/AXP kernel
+ *
+ * based significantly on the arch/alpha/boot/main.c of Linus Torvalds
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/version.h>
+#include <linux/mm.h>
+
+#include <asm/system.h>
+#include <asm/console.h>
+#include <asm/hwrpb.h>
+#include <asm/pgtable.h>
+
+#include <stdarg.h>
+
+#include "ksize.h"
+
+extern int vsprintf(char *, const char *, va_list);
+extern unsigned long switch_to_osf_pal(unsigned long nr,
+	struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa,
+	unsigned long vptb, unsigned long *kstk);
+
+int printk(const char * fmt, ...)
+{
+	va_list args;
+	int i, j, written, remaining, num_nl;
+	static char buf[1024];
+	char * str;
+
+	va_start(args, fmt);
+	i = vsprintf(buf, fmt, args);
+	va_end(args);
+
+	/* expand \n into \r\n: */
+
+	num_nl = 0;
+	for (j = 0; j < i; ++j) {
+	    if (buf[j] == '\n')
+	    	++num_nl;
+	}
+	remaining = i + num_nl;
+	for (j = i - 1; j >= 0; --j) {
+	    buf[j + num_nl] = buf[j];
+	    if (buf[j] == '\n') {
+	    	--num_nl;
+		buf[j + num_nl] = '\r';
+	    }
+	}
+
+	str = buf;
+	do {
+	    written = puts(str, remaining);
+	    remaining -= written;
+	    str += written;
+	} while (remaining > 0);
+	return i;
+}
+
+#define hwrpb (*INIT_HWRPB)
+
+/*
+ * Find a physical address of a virtual object..
+ *
+ * This is easy using the virtual page table address.
+ */
+struct pcb_struct * find_pa(unsigned long *vptb, struct pcb_struct * pcb)
+{
+	unsigned long address = (unsigned long) pcb;
+	unsigned long result;
+
+	result = vptb[address >> 13];
+	result >>= 32;
+	result <<= 13;
+	result |= address & 0x1fff;
+	return (struct pcb_struct *) result;
+}	
+
+/*
+ * This function moves into OSF/1 pal-code, and has a temporary
+ * PCB for that. The kernel proper should replace this PCB with
+ * the real one as soon as possible.
+ *
+ * The page table muckery in here depends on the fact that the boot
+ * code has the L1 page table identity-map itself in the second PTE
+ * in the L1 page table. Thus the L1-page is virtually addressable
+ * itself (through three levels) at virtual address 0x200802000.
+ *
+ * As we don't want it there anyway, we also move the L1 self-map
+ * up as high as we can, so that the last entry in the L1 page table
+ * maps the page tables.
+ *
+ * As a result, the OSF/1 pal-code will instead use a virtual page table
+ * map located at 0xffffffe00000000.
+ */
+#define pcb_va ((struct pcb_struct *) 0x20000000)
+#define old_vptb (0x0000000200000000UL)
+#define new_vptb (0xfffffffe00000000UL)
+void pal_init(void)
+{
+	unsigned long i, rev, sum;
+	unsigned long *L1, *l;
+	struct percpu_struct * percpu;
+	struct pcb_struct * pcb_pa;
+
+	/* Find the level 1 page table and duplicate it in high memory */
+	L1 = (unsigned long *) 0x200802000UL; /* (1<<33 | 1<<23 | 1<<13) */
+	L1[1023] = L1[1];
+
+	percpu = (struct percpu_struct *)
+			(hwrpb.processor_offset + (unsigned long) &hwrpb),
+		
+	pcb_va->ksp = 0;
+	pcb_va->usp = 0;
+	pcb_va->ptbr = L1[1] >> 32;
+	pcb_va->asn = 0;
+	pcb_va->pcc = 0;
+	pcb_va->unique = 0;
+	pcb_va->flags = 1;
+	pcb_pa = find_pa((unsigned long *) old_vptb, pcb_va);
+	printk("Switching to OSF PAL-code .. ");
+	/*
+	 * a0 = 2 (OSF)
+	 * a1 = return address, but we give the asm the vaddr of the PCB
+	 * a2 = physical addr of PCB
+	 * a3 = new virtual page table pointer
+	 * a4 = KSP (but we give it 0, asm sets it)
+	 */
+	i = switch_to_osf_pal(
+		2,
+		pcb_va,
+		pcb_pa,
+		new_vptb,
+		0);
+	if (i) {
+		printk("failed, code %ld\n", i);
+		halt();
+	}
+	rev = percpu->pal_revision = percpu->palcode_avail[2];
+
+	hwrpb.vptb = new_vptb;
+
+	/* update checksum: */
+	sum = 0;
+	for (l = (unsigned long *) &hwrpb;
+	     l < (unsigned long *) &hwrpb.chksum;
+	     ++l)
+		sum += *l;
+	hwrpb.chksum = sum;
+
+	printk("Ok (rev %lx)\n", rev);
+	/* remove the old virtual page-table mapping */
+	L1[1] = 0;
+	flush_tlb_all();
+}
+
+static inline long load(unsigned long dst,
+			unsigned long src,
+			unsigned long count)
+{
+	extern void * memcpy(void *, const void *, size_t);
+
+	memcpy((void *)dst, (void *)src, count);
+	return count;
+}
+
+/*
+ * Start the kernel.
+ */
+static void runkernel(void)
+{
+	__asm__ __volatile__(
+		"bis %1,%1,$30\n\t"
+		"bis %0,%0,$27\n\t"
+		"jmp ($27)"
+		: /* no outputs: it doesn't even return */
+		: "r" (START_ADDR),
+		  "r" (PAGE_SIZE + INIT_STACK));
+}
+
+extern char _end;
+#define KERNEL_ORIGIN \
+	((((unsigned long)&_end) + 511) & ~511)
+
+void start_kernel(void)
+{
+	long i;
+	int nbytes;
+	char envval[256];
+
+	printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n");
+	if (hwrpb.pagesize != 8192) {
+		printk("Expected 8kB pages, got %ldkB\n", hwrpb.pagesize >> 10);
+		return;
+	}
+	pal_init();
+
+	nbytes = dispatch(CCB_GET_ENV, ENV_BOOTED_OSFLAGS,
+			  envval, sizeof(envval));
+	if (nbytes < 0) {
+		nbytes = 0;
+	}
+	envval[nbytes] = '\0';
+	strcpy((char*)ZERO_PAGE, envval);
+
+	printk("Loading the kernel ...\n");
+
+	/* NOTE: *no* callbacks or printouts from here on out!!! */
+
+	i = load(START_ADDR, KERNEL_ORIGIN, KERNEL_SIZE);
+
+	runkernel();
+
+	for (i = 0 ; i < 0x100000000 ; i++)
+		/* nothing */;
+	halt();
+}
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.1.78/linux/arch/alpha/config.in	Sun Dec 21 22:36:12 1997
+++ linux/arch/alpha/config.in	Mon Jan 12 14:51:14 1998
@@ -6,7 +6,10 @@
 
 # clear all implied options (don't want default values for those):
 unset CONFIG_CROSSCOMPILE CONFIG_NATIVE
-unset CONFIG_PCI CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS
+unset CONFIG_ALPHA_EV4 CONFIG_ALPHA_EV5
+unset CONFIG_PCI CONFIG_ALPHA_EISA
+unset CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS CONFIG_ALPHA_CIA
+unset CONFIG_ALPHA_T2 CONFIG_ALPHA_PYXIS
 unset CONFIG_ALPHA_NEED_ROUNDING_EMULATION
 
 mainmenu_option next_comment
@@ -46,19 +49,29 @@
 	 Jensen		CONFIG_ALPHA_JENSEN		\
 	 Noname		CONFIG_ALPHA_NONAME		\
 	 Mikasa		CONFIG_ALPHA_MIKASA		\
+	 Noritake	CONFIG_ALPHA_NORITAKE		\
 	 Alcor		CONFIG_ALPHA_ALCOR		\
+	 Miata		CONFIG_ALPHA_MIATA		\
+	 Sable		CONFIG_ALPHA_SABLE		\
+	 AlphaBook1	CONFIG_ALPHA_BOOK1		\
 	 Platform2000	CONFIG_ALPHA_P2K" Cabriolet
+
+if [ "$CONFIG_ALPHA_BOOK1" = "y" ]
+then
+	define_bool CONFIG_ALPHA_NONAME y
+fi
 if [ "$CONFIG_ALPHA_NONAME" = "y" -o "$CONFIG_ALPHA_EB66" = "y" \
 	-o "$CONFIG_ALPHA_EB66P" = "y" -o "$CONFIG_ALPHA_P2K" = "y" ]
 then
 	define_bool CONFIG_PCI y
+	define_bool CONFIG_ALPHA_EV4 y
 	define_bool CONFIG_ALPHA_LCA y
 fi
 if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \
-	-o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_MIKASA" = "y" \
-	-o "$CONFIG_ALPHA_XL" = "y" ]
+	-o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_XL" = "y" ]
 then
 	define_bool CONFIG_PCI y
+	define_bool CONFIG_ALPHA_EV4 y
 	define_bool CONFIG_ALPHA_APECS y
 fi
 if [ "$CONFIG_ALPHA_EB164" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \
@@ -67,16 +80,56 @@
 	define_bool CONFIG_PCI y
 	define_bool CONFIG_ALPHA_EV5 y
 	define_bool CONFIG_ALPHA_CIA y
-else
+fi
+if [ "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ]
+then
+	choice 'CPU daughtercard' \
+		"Pinnacle	CONFIG_ALPHA_PINNACLE \
+		Primo		CONFIG_ALPHA_PRIMO" Primo
+	if [ "$CONFIG_ALPHA_PRIMO" = "y" ]
+	then
+		define_bool CONFIG_ALPHA_EV5 y
+		define_bool CONFIG_ALPHA_CIA y
+	else
+		define_bool CONFIG_ALPHA_EV4 y
+		define_bool CONFIG_ALPHA_APECS y
+	fi
+	define_bool CONFIG_PCI y
+fi
+if [ "$CONFIG_ALPHA_SABLE" = "y" ]
+then
+	define_bool CONFIG_PCI y
+	define_bool CONFIG_ALPHA_EV4 y
+	define_bool CONFIG_ALPHA_T2 y
+fi
+if [ "$CONFIG_ALPHA_MIATA" = "y" ]
+then
+	define_bool CONFIG_PCI y
+	define_bool CONFIG_ALPHA_EV5 y
+	define_bool CONFIG_ALPHA_PYXIS y
+fi
+if [ "$CONFIG_ALPHA_JENSEN" = "y" ]
+then
+	define_bool CONFIG_ALPHA_EV4 y
+fi
+if [ "$CONFIG_ALPHA_EV4" = "y" ]
+then
 	# EV45 and older do not support all rounding modes in hw:
 	define_bool CONFIG_ALPHA_NEED_ROUNDING_EMULATION y
 fi
 
 if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \
 	-o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_JENSEN" = "y" \
-	-o "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_ALCOR" = "y" ]
+	-o "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_ALCOR" = "y" \
+	-o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_MIATA" = "y" \
+	-o "$CONFIG_ALPHA_NORITAKE" = "y" -o "$CONFIG_ALPHA_PC164" = "y" ]
 then
 	bool 'Using SRM as bootloader' CONFIG_ALPHA_SRM
+fi
+if [ "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_MIKASA" = "y" \
+	-o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ]
+then
+	define_bool CONFIG_ALPHA_EISA y
 fi
 if [ "$CONFIG_ALPHA_XL" = "y" ]
 then
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/kernel/Makefile linux/arch/alpha/kernel/Makefile
--- v2.1.78/linux/arch/alpha/kernel/Makefile	Fri Jan  3 01:33:25 1997
+++ linux/arch/alpha/kernel/Makefile	Mon Jan 12 14:51:14 1998
@@ -16,8 +16,25 @@
 
 O_TARGET := kernel.o
 O_OBJS   := entry.o traps.o process.o osf_sys.o irq.o signal.o setup.o \
-	    bios32.o ptrace.o time.o apecs.o lca.o cia.o
+	    bios32.o ptrace.o time.o
 OX_OBJS  := alpha_ksyms.o
+
+
+ifdef CONFIG_ALPHA_APECS
+O_OBJS   += apecs.o
+endif
+ifdef CONFIG_ALPHA_CIA
+O_OBJS   += cia.o
+endif
+ifdef CONFIG_ALPHA_LCA
+O_OBJS   += lca.o
+endif
+ifdef CONFIG_ALPHA_PYXIS
+O_OBJS   += pyxis.o
+endif
+ifdef CONFIG_ALPHA_T2
+O_OBJS   += t2.o
+endif
 
 all: kernel.o head.o
 
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/kernel/apecs.c linux/arch/alpha/kernel/apecs.c
--- v2.1.78/linux/arch/alpha/kernel/apecs.c	Mon Oct  7 02:40:22 1996
+++ linux/arch/alpha/kernel/apecs.c	Mon Jan 12 14:51:14 1998
@@ -21,12 +21,11 @@
 extern struct hwrpb_struct *hwrpb;
 extern asmlinkage void wrmces(unsigned long mces);
 extern int alpha_sys_type;
+
 /*
  * BIOS32-style PCI interface:
  */
 
-#ifdef CONFIG_ALPHA_APECS
-
 #ifdef DEBUG
 # define DBG(args)	printk args
 #else
@@ -88,7 +87,8 @@
 {
 	unsigned long addr;
 
-	DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p, type1=0x%p)\n",
+	DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x,"
+	     " pci_addr=0x%p, type1=0x%p)\n",
 	     bus, device_fn, where, pci_addr, type1));
 
 	if (bus == 0) {
@@ -97,7 +97,8 @@
 		/* type 0 configuration cycle: */
 
 		if (device > 20) {
-			DBG(("mk_conf_addr: device (%d) > 20, returning -1\n", device));
+			DBG(("mk_conf_addr: device (%d) > 20, returning -1\n",
+			     device));
 			return -1;
 		}
 
@@ -142,15 +143,15 @@
 	DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1));
 
 	/* reset status register to avoid losing errors: */
-	stat0 = *((volatile unsigned int *)APECS_IOC_DCSR);
-	*((volatile unsigned int *)APECS_IOC_DCSR) = stat0;
+	stat0 = *(vuip)APECS_IOC_DCSR;
+	*(vuip)APECS_IOC_DCSR = stat0;
 	mb();
 	DBG(("conf_read: APECS DCSR was 0x%x\n", stat0));
 	/* if Type1 access, must set HAE #2 */
 	if (type1) {
-		haxr2 = *((unsigned int *)APECS_IOC_HAXR2);
+		haxr2 = *(vuip)APECS_IOC_HAXR2;
 		mb();
-		*((unsigned int *)APECS_IOC_HAXR2) = haxr2 | 1;
+		*(vuip)APECS_IOC_HAXR2 = haxr2 | 1;
 		DBG(("conf_read: TYPE1 access\n"));
 	}
 
@@ -159,8 +160,7 @@
 	apecs_mcheck_taken = 0;
 	mb();
 	/* access configuration space: */
-	value = *((volatile unsigned int *)addr);
-	mb();
+	value = *(vuip)addr;
 	mb();
 	if (apecs_mcheck_taken) {
 		apecs_mcheck_taken = 0;
@@ -169,17 +169,18 @@
 	}
 	apecs_mcheck_expected = 0;
 	mb();
+
+#if 1
 	/*
 	 * david.rusling@reo.mts.dec.com.  This code is needed for the
 	 * EB64+ as it does not generate a machine check (why I don't
 	 * know).  When we build kernels for one particular platform
 	 * then we can make this conditional on the type.
 	 */
-#if 1
 	draina();
 
 	/* now look for any errors */
-	stat0 = *((unsigned int *)APECS_IOC_DCSR);
+	stat0 = *(vuip)APECS_IOC_DCSR;
 	DBG(("conf_read: APECS DCSR after read 0x%x\n", stat0));
 	if (stat0 & 0xffe0U) { /* is any error bit set? */
 		/* if not NDEV, print status */
@@ -188,7 +189,7 @@
 		}
 
 		/* reset error status: */
-		*((volatile unsigned long *)APECS_IOC_DCSR) = stat0;
+		*(vulp)APECS_IOC_DCSR = stat0;
 		mb();
 		wrmces(0x7);			/* reset machine check */
 		value = 0xffffffff;
@@ -197,7 +198,7 @@
 
 	/* if Type1 access, must reset HAE #2 so normal IO space ops work */
 	if (type1) {
-		*((unsigned int *)APECS_IOC_HAXR2) = haxr2 & ~1;
+		*(vuip)APECS_IOC_HAXR2 = haxr2 & ~1;
 		mb();
 	}
 	restore_flags(flags);
@@ -224,37 +225,37 @@
 	cli();
 
 	/* reset status register to avoid losing errors: */
-	stat0 = *((volatile unsigned int *)APECS_IOC_DCSR);
-	*((volatile unsigned int *)APECS_IOC_DCSR) = stat0;
+	stat0 = *(vuip)APECS_IOC_DCSR;
+	*(vuip)APECS_IOC_DCSR = stat0;
 	mb();
 
 	/* if Type1 access, must set HAE #2 */
 	if (type1) {
-		haxr2 = *((unsigned int *)APECS_IOC_HAXR2);
+		haxr2 = *(vuip)APECS_IOC_HAXR2;
 		mb();
-		*((unsigned int *)APECS_IOC_HAXR2) = haxr2 | 1;
+		*(vuip)APECS_IOC_HAXR2 = haxr2 | 1;
 	}
 
 	draina();
 	apecs_mcheck_expected = 1;
 	mb();
 	/* access configuration space: */
-	*((volatile unsigned int *)addr) = value;
-	mb();
+	*(vuip)addr = value;
 	mb();
 	apecs_mcheck_expected = 0;
 	mb();
+
+#if 1
 	/*
 	 * david.rusling@reo.mts.dec.com.  This code is needed for the
 	 * EB64+ as it does not generate a machine check (why I don't
 	 * know).  When we build kernels for one particular platform
 	 * then we can make this conditional on the type.
 	 */
-#if 1
 	draina();
 
 	/* now look for any errors */
-	stat0 = *((unsigned int *)APECS_IOC_DCSR);
+	stat0 = *(vuip)APECS_IOC_DCSR;
 	if (stat0 & 0xffe0U) { /* is any error bit set? */
 		/* if not NDEV, print status */
 		if (!(stat0 & 0x0800)) {
@@ -262,7 +263,7 @@
 		}
 
 		/* reset error status: */
-		*((volatile unsigned long *)APECS_IOC_DCSR) = stat0;
+		*(vulp)APECS_IOC_DCSR = stat0;
 		mb();
 		wrmces(0x7);			/* reset machine check */
 	}
@@ -270,7 +271,7 @@
 
 	/* if Type1 access, must reset HAE #2 so normal IO space ops work */
 	if (type1) {
-		*((unsigned int *)APECS_IOC_HAXR2) = haxr2 & ~1;
+		*(vuip)APECS_IOC_HAXR2 = haxr2 & ~1;
 		mb();
 	}
 	restore_flags(flags);
@@ -433,7 +434,7 @@
 #ifdef CONFIG_ALPHA_CABRIOLET
 	/*
 	 * JAE: HACK!!! for now, hardwire if configured...
-	 * davidm: Older miniloader versions don't set the clockfrequency
+	 * davidm: Older miniloader versions don't set the clock frequency
 	 * right, so hardcode it for now.
 	 */
 	if (hwrpb->sys_type == ST_DEC_EB64P) {
@@ -448,7 +449,9 @@
 	    unsigned long *l, sum;
 
 	    sum = 0;
-	    for (l = (unsigned long *) hwrpb; l < (unsigned long *) &hwrpb->chksum; ++l)
+	    for (l = (unsigned long *) hwrpb;
+		 l < (unsigned long *) &hwrpb->chksum;
+		 ++l)
 	      sum += *l;
 	    hwrpb->chksum = sum;
 	}
@@ -462,10 +465,10 @@
         */
        {
 #if 0
-         unsigned int haxr2 = *((unsigned int *)APECS_IOC_HAXR2); mb();
+         unsigned int haxr2 = *(vuip)APECS_IOC_HAXR2; mb();
          if (haxr2) printk("apecs_init: HAXR2 was 0x%x\n", haxr2);
 #endif
-         *((unsigned int *)APECS_IOC_HAXR2) = 0; mb();
+         *(vuip)APECS_IOC_HAXR2 = 0; mb();
        }
 
 
@@ -474,15 +477,15 @@
 
 int apecs_pci_clr_err(void)
 {
-	apecs_jd = *((unsigned long *)APECS_IOC_DCSR);
+	apecs_jd = *(vulp)APECS_IOC_DCSR;
 	if (apecs_jd & 0xffe0L) {
-		apecs_jd1 = *((unsigned long *)APECS_IOC_SEAR);
-		*((unsigned long *)APECS_IOC_DCSR) = apecs_jd | 0xffe1L;
-		apecs_jd = *((unsigned long *)APECS_IOC_DCSR);
+		apecs_jd1 = *(vulp)APECS_IOC_SEAR;
+		*(vulp)APECS_IOC_DCSR = apecs_jd | 0xffe1L;
+		apecs_jd = *(vulp)APECS_IOC_DCSR;
 		mb();
 	}
-	*((unsigned long *)APECS_IOC_TBIA) = APECS_IOC_TBIA;
-	apecs_jd2 = *((unsigned long *)APECS_IOC_TBIA);
+	*(vulp)APECS_IOC_TBIA = APECS_IOC_TBIA;
+	apecs_jd2 = *(vulp)APECS_IOC_TBIA;
 	mb();
 	return 0;
 }
@@ -547,7 +550,8 @@
 		wrmces(0x1f); /* disable correctable from now on */
 		mb();
 		draina();
-		printk("apecs_machine_check: HW correctable (0x%lx)\n", vector);
+		printk("apecs_machine_check: HW correctable (0x%lx)\n",
+		       vector);
 	}
 	else {
 		printk(KERN_CRIT "APECS machine check:\n");
@@ -572,4 +576,3 @@
 #endif
 	}
 }
-#endif /* CONFIG_ALPHA_APECS */
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/kernel/bios32.c linux/arch/alpha/kernel/bios32.c
--- v2.1.78/linux/arch/alpha/kernel/bios32.c	Tue Sep 23 16:48:46 1997
+++ linux/arch/alpha/kernel/bios32.c	Mon Jan 12 14:51:14 1998
@@ -40,13 +40,15 @@
 {
 	return 0;
 }
+
 asmlinkage int sys_pciconfig_read()
 {
-	return 0;
+	return -ENOSYS;
 }
+
 asmlinkage int sys_pciconfig_write()
 {
-	return 0;
+	return -ENOSYS;
 }
 
 #else /* CONFIG_PCI */
@@ -95,31 +97,73 @@
 
 extern struct hwrpb_struct *hwrpb;
 
+/* Forward declarations for some extra fixup routines for specific hardware. */
+#ifdef CONFIG_ALPHA_PC164
+static int SMCInit(void);
+#endif
+#ifdef CONFIG_ALPHA_MIATA
+static int es1888_init(void);
+#endif
 
 #if PCI_MODIFY
 
-/* NOTE: we can't just blindly use 64K for machines with EISA busses; they
-   may also have PCI-PCI bridges present, and then we'd configure the bridge
-   incorrectly */
-#if 0
-static unsigned int	io_base	 = 64*KB;	/* <64KB are (E)ISA ports */
+/*
+ * NOTE: we can't just blindly use 64K for machines with EISA busses; they
+ * may also have PCI-PCI bridges present, and then we'd configure the bridge
+ * incorrectly
+ *
+ * Also, we start at 0x8000 or 0x9000, in hopes to get all devices'
+ * IO space areas allocated *before* 0xC000; this is because certain
+ * BIOSes (Millennium for one) use PCI Config space "mechanism #2"
+ * accesses to probe the bus. If a device's registers appear at 0xC000,
+ * it may see an INx/OUTx at that address during BIOS emulation of the
+ * VGA BIOS, and some cards, notably Adaptec 2940UW, take mortal offense.
+ */
+#if defined(CONFIG_ALPHA_EISA)
+static unsigned int    io_base  = 0x9000;      /* start above 8th slot */
 #else
-static unsigned int	io_base	 = 0xb000;
+static unsigned int    io_base  = 0x8000;
 #endif
 
 #if defined(CONFIG_ALPHA_XL)
 /*
-   an AVANTI *might* be an XL, and an XL has only 27 bits of ISA address
-   that get passed through the PCI<->ISA bridge chip. Because this causes
-   us to set the PCI->Mem window bases lower than normal, we've gotta allocate
-   PCI bus devices' memory addresses *above* the PCI<->memory mapping windows,
-   so that CPU memory DMA addresses issued by a bus device don't conflict
-   with bus memory addresses, like frame buffer memory for graphics cards.
-*/
-static unsigned int	mem_base = 1024*MB;
-#else /* CONFIG_ALPHA_XL */
-static unsigned int	mem_base = 16*MB;	/* <16MB is ISA memory */
-#endif /* CONFIG_ALPHA_XL */
+ * An XL is AVANTI (APECS) family, *but* it has only 27 bits of ISA address
+ * that get passed through the PCI<->ISA bridge chip. Although this causes
+ * us to set the PCI->Mem window bases lower than normal, we still allocate
+ * PCI bus devices' memory addresses *below* the low DMA mapping window,
+ * and hope they fit below 64Mb (to avoid conflicts), and so that they can
+ * be accessed via SPARSE space.
+ *
+ * We accept the risk that a broken Myrinet card will be put into a true XL
+ * and thus can more easily run into the problem described below.
+ */
+static unsigned int mem_base = 16*MB + 2*MB; /* 16M to 64M-1 is avail */
+
+#elif defined(CONFIG_ALPHA_LCA) || defined(CONFIG_ALPHA_APECS)
+/*
+ * We try to make this address *always* have more than 1 bit set.
+ * this is so that devices like the broken Myrinet card will always have
+ * a PCI memory address that will never match a IDSEL address in
+ * PCI Config space, which can cause problems with early rev cards.
+ *
+ * However, APECS and LCA have only 34 bits for physical addresses, thus
+ * limiting PCI bus memory addresses for SPARSE access to be less than 128Mb.
+ */
+static unsigned int mem_base = 64*MB + 2*MB;
+
+#else
+/*
+ * We try to make this address *always* have more than 1 bit set.
+ * this is so that devices like the broken Myrinet card will always have
+ * a PCI memory address that will never match a IDSEL address in
+ * PCI Config space, which can cause problems with early rev cards.
+ *
+ * Because CIA and PYXIS and T2 have more bits for physical addresses,
+ * they support an expanded range of SPARSE memory addresses.
+ */
+static unsigned int	mem_base = 128*MB + 16*MB;
+
+#endif
 
 /*
  * Disable PCI device DEV so that it does not respond to I/O or memory
@@ -130,14 +174,14 @@
 	struct pci_bus *bus;
 	unsigned short cmd;
 
-#if defined(CONFIG_ALPHA_MIKASA) || defined(CONFIG_ALPHA_ALCOR)
+#if defined(CONFIG_ALPHA_EISA)
 	/*
 	 * HACK: the PCI-to-EISA bridge does not seem to identify
 	 *       itself as a bridge... :-(
 	 */
 	if (dev->vendor == 0x8086 && dev->device == 0x0482) {
-	  DBG_DEVS(("disable_dev: ignoring PCEB...\n"));
-	  return;
+		DBG_DEVS(("disable_dev: ignoring PCEB...\n"));
+		return;
 	}
 #endif
 
@@ -153,7 +197,7 @@
 /*
  * Layout memory and I/O for a device:
  */
-#define MAX(val1, val2) ( ((val1) > (val2)) ? val1 : val2)
+#define MAX(val1, val2) ((val1) > (val2) ? val1 : val2)
 
 static void layout_dev(struct pci_dev *dev)
 {
@@ -162,16 +206,15 @@
 	unsigned int base, mask, size, reg;
 	unsigned int alignto;
 
-#if defined(CONFIG_ALPHA_MIKASA) || defined(CONFIG_ALPHA_ALCOR)
 	/*
 	 * HACK: the PCI-to-EISA bridge does not seem to identify
 	 *       itself as a bridge... :-(
 	 */
-	if (dev->vendor == 0x8086 && dev->device == 0x0482) {
-	  DBG_DEVS(("layout_dev: ignoring PCEB...\n"));
-	  return;
+	if (dev->vendor == PCI_VENDOR_ID_INTEL &&
+	    dev->device == PCI_DEVICE_ID_INTEL_82375) {
+		DBG_DEVS(("layout_dev: ignoring PCEB...\n"));
+		return;
 	}
-#endif
 
 	bus = dev->bus;
 	pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
@@ -203,12 +246,23 @@
 			base &= PCI_BASE_ADDRESS_IO_MASK;
 			mask = (~base << 1) | 0x1;
 			size = (mask & base) & 0xffffffff;
-			/* align to multiple of size of minimum base */
-			alignto = MAX(0x400, size) ;
-			base = ALIGN(io_base, alignto );
+			/*
+			 * Aligning to 0x800 rather than the minimum base of
+			 * 0x400 is an attempt to avoid having devices in 
+			 * any 0x?C?? range, which is where the de4x5 driver
+			 * probes for EISA cards.
+			 *
+			 * Adaptecs, especially, resent such intrusions.
+			 */
+			alignto = MAX(0x800, size);
+			base = ALIGN(io_base, alignto);
 			io_base = base + size;
 			pcibios_write_config_dword(bus->number, dev->devfn, 
 						   reg, base | 0x1);
+			dev->base_address[(reg - PCI_BASE_ADDRESS_0)>>2]
+				= base | 0x1;
+			DBG_DEVS(("layout_dev: dev 0x%x IO @ 0x%x (0x%x)\n",
+				  dev->device, base, size));
 		} else {
 			unsigned int type;
 			/*
@@ -220,10 +274,10 @@
 			mask = (~base << 1) | 0x1;
 			size = (mask & base) & 0xffffffff;
 			switch (type) {
-			      case PCI_BASE_ADDRESS_MEM_TYPE_32:
+			case PCI_BASE_ADDRESS_MEM_TYPE_32:
 				break;
 
-			      case PCI_BASE_ADDRESS_MEM_TYPE_64:
+			case PCI_BASE_ADDRESS_MEM_TYPE_64:
 				printk("bios32 WARNING: "
 				       "ignoring 64-bit device in "
 				       "slot %d, function %d: \n",
@@ -232,7 +286,7 @@
 				reg += 4;	/* skip extra 4 bytes */
 				continue;
 
-			      case PCI_BASE_ADDRESS_MEM_TYPE_1M:
+			case PCI_BASE_ADDRESS_MEM_TYPE_1M:
 				/*
 				 * Allocating memory below 1MB is *very*
 				 * tricky, as there may be all kinds of
@@ -242,9 +296,9 @@
 				 * Alpha (or that the console has set it
 				 * up properly).
 				 */
-				printk("bios32 WARNING: slot %d, function %d "
-				       "requests memory below 1MB---don't "
-				       "know how to do that.\n",
+				printk("bios32 WARNING: slot %d, function %d"
+				       " requests memory below 1MB---don't"
+				       " know how to do that.\n",
 				       PCI_SLOT(dev->devfn),
 				       PCI_FUNC(dev->devfn));
 				continue;
@@ -265,13 +319,13 @@
 			 * dense memory space only!
 			 */
 			/* align to multiple of size of minimum base */
-			alignto = MAX(0x1000, size) ;
+			alignto = MAX(0x1000, size);
 			base = ALIGN(mem_base, alignto);
 			if (size > 7 * 16*MB) {
-				printk("bios32 WARNING: slot %d, function %d "
-				       "requests  %dB of contiguous address "
-				       " space---don't use sparse memory "
-				       " accesses on this device!!\n",
+				printk("bios32 WARNING: slot %d, function %d"
+				       " requests 0x%x bytes of contiguous"
+				       " address space---don't use sparse"
+				       " memory accesses on this device!!\n",
 				       PCI_SLOT(dev->devfn),
 				       PCI_FUNC(dev->devfn), size);
 			} else {
@@ -280,7 +334,7 @@
 					base += 16*MB;
 					base  = ALIGN(base, alignto);
 				}
-				if (base / (128*MB) != (base + size) / (128*MB)) {
+				if (base/(128*MB) != (base + size)/(128*MB)) {
 					base &= ~(128*MB - 1);
 					base += (128 + 16)*MB;
 					base  = ALIGN(base, alignto);
@@ -289,13 +343,17 @@
 			mem_base = base + size;
 			pcibios_write_config_dword(bus->number, dev->devfn,
 						   reg, base);
+			dev->base_address[(reg-PCI_BASE_ADDRESS_0)>>2] = base;
+			DBG_DEVS(("layout_dev: dev 0x%x MEM @ 0x%x (0x%x)\n",
+				  dev->device, base, size));
 		}
 	}
-	/* enable device: */
+
+	/* Enable device: */
 	if (dev->class >> 8 == PCI_CLASS_NOT_DEFINED ||
 	    dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA ||
-	    dev->class >> 8 == PCI_CLASS_DISPLAY_VGA ||
-	    dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)
+	    dev->class >> 8 == PCI_CLASS_STORAGE_IDE ||
+	    dev->class >> 16 == PCI_BASE_CLASS_DISPLAY)
 	{
 		/*
 		 * All of these (may) have I/O scattered all around
@@ -308,8 +366,11 @@
 
 	pcibios_write_config_word(bus->number, dev->devfn, PCI_COMMAND,
 				  cmd | PCI_COMMAND_MASTER);
-	DBG_DEVS(("layout_dev: bus %d  slot 0x%x  VID 0x%x  DID 0x%x  class 0x%x\n",
-		  bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device, dev->class));
+
+	DBG_DEVS(("layout_dev: bus %d slot %d VID 0x%x DID 0x%x"
+		  " class 0x%x cmd 0 x%x\n",
+		  bus->number, PCI_SLOT(dev->devfn), dev->vendor,
+		  dev->device, dev->class, cmd|PCI_COMMAND_MASTER));
 }
 
 
@@ -322,7 +383,7 @@
 	DBG_DEVS(("layout_bus: starting bus %d\n", bus->number));
 
 	if (!bus->devices && !bus->children)
-	  return;
+		return;
 
 	/*
 	 * Align the current bases on appropriate boundaries (4K for
@@ -341,8 +402,9 @@
 	 * decoders are programmed consistently.
 	 */
 	for (dev = bus->devices; dev; dev = dev->sibling) {
-		if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) {
-			disable_dev(dev) ;
+		if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
+		    (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) {
+			disable_dev(dev);
 		}
 	}
 
@@ -352,7 +414,8 @@
 	DBG_DEVS(("layout_bus: starting bus %d devices\n", bus->number));
 
 	for (dev = bus->devices; dev; dev = dev->sibling) {
-		if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) {
+		if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
+		    (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) {
 			layout_dev(dev);
 		}
 	}
@@ -378,7 +441,8 @@
 		 */
 		pcibios_read_config_dword(bridge->bus->number, bridge->devfn,
 					  0x1c, &l);
-		l = (l & 0xffff0000) | ((bio >> 8) & 0x00f0) | ((tio - 1) & 0xf000);
+		l &= 0xffff0000;
+		l |= ((bio >> 8) & 0x00f0) | ((tio - 1) & 0xf000);
 		pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
 					   0x1c, l);
 		/*
@@ -504,9 +568,10 @@
 }
 
 /* 
- * A small note about bridges and interrupts.    The DECchip 21050 (and later chips)
- * adheres to the PCI-PCI bridge specification.   This says that the interrupts on
- * the other side of a bridge are swizzled in the following manner:
+ * A small note about bridges and interrupts.    The DECchip 21050 (and later)
+ * adheres to the PCI-PCI bridge specification.   This says that the
+ * interrupts on the other side of a bridge are swizzled in the following
+ * manner:
  *
  * Dev    Interrupt   Interrupt 
  *        Pin on      Pin on 
@@ -538,64 +603,89 @@
  *   The following code is somewhat simplistic as it assumes only one bridge.
  *   I will fix it later (david.rusling@reo.mts.dec.com).
  */
-static inline unsigned char bridge_swizzle(unsigned char pin, unsigned int slot) 
+static inline unsigned char
+bridge_swizzle(unsigned char pin, unsigned int slot) 
 {
 	/* swizzle */
-	return (((pin-1) + slot) % 4) + 1 ;
+	return (((pin-1) + slot) % 4) + 1;
 }
 
 /*
- * Most evaluation boards share most of the fixup code, which is isolated here.
- * This function is declared "inline" as only one platform will ever be selected
- * in any given kernel.  If that platform doesn't need this code, we don't want
- * it around as dead code.
+ * Most evaluation boards share most of the fixup code, which is isolated
+ * here.  This function is declared "inline" as only one platform will ever
+ * be selected in any given kernel.  If that platform doesn't need this code,
+ * we don't want it around as dead code.
  */
-static inline void common_fixup(long min_idsel, long max_idsel, long irqs_per_slot,
-				char irq_tab[max_idsel - min_idsel + 1][irqs_per_slot],
-				long ide_base)
+static inline void
+common_fixup(long min_idsel, long max_idsel, long irqs_per_slot,
+	     char irq_tab[max_idsel - min_idsel + 1][irqs_per_slot],
+	     long ide_base)
 {
 	struct pci_dev *dev;
 	unsigned char pin;
-	unsigned char slot ;
+	unsigned char slot;
 
 	/*
 	 * Go through all devices, fixing up irqs as we see fit:
 	 */
 	for (dev = pci_devices; dev; dev = dev->next) {
-		if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE
-#if defined(CONFIG_ALPHA_MIKASA) || defined(CONFIG_ALPHA_ALCOR)
-		    /* PCEB (PCI to EISA bridge) does not identify
-		       itself as a bridge... :-( */
-		    && !((dev->vendor==0x8086) && (dev->device==0x482))
-#endif
-		    ) {
-			dev->irq = 0;
+		if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE
+		     /* PCEB (PCI to EISA bridge) does not identify
+		        itself as a bridge... :-P */
+		     && !(dev->vendor == PCI_VENDOR_ID_INTEL &&
+			  dev->device == PCI_DEVICE_ID_INTEL_82375))
+                    || dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA) {
 			/*
-			 * This device is not on the primary bus, we need to figure out which
-			 * interrupt pin it will come in on.   We know which slot it will come
-			 * in on 'cos that slot is where the bridge is.   Each time the interrupt
-			 * line passes through a PCI-PCI bridge we must apply the swizzle function
-			 * (see the inline static routine above).
+			 * This device is not on the primary bus, we need
+			 * to figure out which interrupt pin it will come
+			 * in on.   We know which slot it will come in on
+			 * 'cos that slot is where the bridge is.   Each
+			 * time the interrupt line passes through a PCI-PCI
+			 * bridge we must apply the swizzle function (see
+			 * the inline static routine above).
 			 */
+			dev->irq = 0;
 			if (dev->bus->number != 0) {
-				struct pci_dev *curr = dev ;
-				/* read the pin and do the PCI-PCI bridge interrupt pin swizzle */
-				pcibios_read_config_byte(dev->bus->number, dev->devfn,
-							 PCI_INTERRUPT_PIN, &pin);
+				struct pci_dev *curr = dev;
+				/* read the pin and do the PCI-PCI bridge
+				   interrupt pin swizzle */
+				pcibios_read_config_byte(dev->bus->number,
+							 dev->devfn,
+							 PCI_INTERRUPT_PIN,
+							 &pin);
 				/* cope with 0 */
-				if (pin == 0) pin = 1 ;
-				/* follow the chain of bridges, swizzling as we go */
+				if (pin == 0)
+					pin = 1;
+				/* follow the chain of bridges, swizzling
+				   as we go */
+#if defined(CONFIG_ALPHA_MIATA)
+				slot = PCI_SLOT(dev->devfn) + 5;
+				DBG_DEVS(("MIATA: bus 1 slot %d pin %d"
+					  " irq %d min_idsel %d\n",
+					  PCI_SLOT(dev->devfn), pin,
+					  irq_tab[slot - min_idsel][pin],
+					  min_idsel));
+#elif defined(CONFIG_ALPHA_NORITAKE)
+				/* WAG Alert! */
+				slot = PCI_SLOT(dev->devfn) + 14;
+				DBG_DEVS(("NORITAKE: bus 1 slot %d pin %d"
+					  " irq %d min_idsel %d\n",
+					  PCI_SLOT(dev->devfn), pin,
+					  irq_tab[slot - min_idsel][pin],
+					  min_idsel));
+#else
 				do {
 					/* swizzle */
-					pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn)) ;
+					pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn));
 					/* move up the chain of bridges */
-					curr = curr->bus->self ;
-				} while (curr->bus->self) ;
+					curr = curr->bus->self;
+				} while (curr->bus->self);
 				/* The slot is the slot of the last bridge. */
-				slot = PCI_SLOT(curr->devfn) ;
+				slot = PCI_SLOT(curr->devfn);
+#endif /* MIATA */
 			} else {
 				/* work out the slot */
-				slot = PCI_SLOT(dev->devfn) ;
+				slot = PCI_SLOT(dev->devfn);
 				/* read the pin */
 				pcibios_read_config_byte(dev->bus->number,
 							 dev->devfn,
@@ -606,20 +696,39 @@
 				dev->irq = irq_tab[slot - min_idsel][pin];
 #if PCI_MODIFY
 			/* tell the device: */
-			pcibios_write_config_byte(dev->bus->number, dev->devfn,
-						  PCI_INTERRUPT_LINE, dev->irq);
+			pcibios_write_config_byte(dev->bus->number,
+						  dev->devfn,
+						  PCI_INTERRUPT_LINE,
+						  dev->irq);
 #endif
+
+			DBG_DEVS(("common_fixup: bus %d slot 0x%x"
+				  " VID 0x%x DID 0x%x\n"
+				  "              int_slot 0x%x pin 0x%x"
+				  " pirq 0x%x\n",
+				  dev->bus->number, PCI_SLOT(dev->devfn),
+				  dev->vendor, dev->device,
+				  slot, pin, dev->irq));
+
 			/*
 			 * if it's a VGA, enable its BIOS ROM at C0000
 			 */
 			if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
-			  pcibios_write_config_dword(dev->bus->number,
-						     dev->devfn,
-						     PCI_ROM_ADDRESS,
-						     0x000c0000 | PCI_ROM_ADDRESS_ENABLE);
+				pcibios_write_config_dword(dev->bus->number,
+							   dev->devfn,
+							   PCI_ROM_ADDRESS,
+							   0x000c0000 | PCI_ROM_ADDRESS_ENABLE);
+			}
+			/*
+			 * if it's a SCSI, disable its BIOS ROM
+			 */
+			if ((dev->class >> 8) == PCI_CLASS_STORAGE_SCSI) {
+				pcibios_write_config_dword(dev->bus->number,
+							   dev->devfn,
+							   PCI_ROM_ADDRESS,
+							   0x0000000);
 			}
 		}
-
 	}
 	if (ide_base) {
 		enable_ide(ide_base);
@@ -641,12 +750,12 @@
  */
 static inline void eb66p_fixup(void)
 {
-	char irq_tab[5][5] = {
-		{16+0, 16+0, 16+5,  16+9, 16+13},	/* IdSel 6,  slot 0, J25 */
-		{16+1, 16+1, 16+6, 16+10, 16+14},	/* IdSel 7,  slot 1, J26 */
-		{  -1,   -1,   -1,    -1,    -1},	/* IdSel 8,  SIO         */
-		{16+2, 16+2, 16+7, 16+11, 16+15},	/* IdSel 9,  slot 2, J27 */
-		{16+3, 16+3, 16+8, 16+12,  16+6}	/* IdSel 10, slot 3, J28 */
+	static char irq_tab[5][5] = {
+		{16+0, 16+0, 16+5,  16+9, 16+13},  /* IdSel 6,  slot 0, J25 */
+		{16+1, 16+1, 16+6, 16+10, 16+14},  /* IdSel 7,  slot 1, J26 */
+		{  -1,   -1,   -1,    -1,    -1},  /* IdSel 8,  SIO         */
+		{16+2, 16+2, 16+7, 16+11, 16+15},  /* IdSel 9,  slot 2, J27 */
+		{16+3, 16+3, 16+8, 16+12,  16+6}   /* IdSel 10, slot 3, J28 */
 	};
 	common_fixup(6, 10, 5, irq_tab, 0x398);
 }
@@ -694,27 +803,24 @@
  * 
  */
 
+#ifdef CONFIG_ALPHA_PC164
 static inline void alphapc164_fixup(void)
 {
-	extern int SMCInit(void);
-	char irq_tab[7][5] = {
-	  /*
-	   *      int   intA  intB   intC   intD
-	   *      ----  ----  ----   ----   ----
-	   */
-		{ 16+2, 16+2, 16+9,  16+13, 16+17},	/* IdSel  5,  slot 2, J20 */
-		{ 16+0, 16+0, 16+7,  16+11, 16+15},	/* IdSel  6,  slot 0, J29 */
-		{ 16+1, 16+1, 16+8,  16+12, 16+16},	/* IdSel  7,  slot 1, J26 */
-		{   -1,   -1,   -1,    -1,    -1},	/* IdSel  8,  SIO         */
-		{ 16+3, 16+3, 16+10, 16+14, 16+18},	/* IdSel  9,  slot 3, J19 */
-		{ 16+6, 16+6, 16+6,  16+6,  16+6},	/* IdSel 10,  USB */
-		{ 16+5, 16+5, 16+5,  16+5,  16+5}	/* IdSel 11,  IDE */
+	static char irq_tab[7][5] = {
+		/*INT   INTA  INTB   INTC   INTD */
+		{ 16+2, 16+2, 16+9,  16+13, 16+17}, /* IdSel  5, slot 2, J20 */
+		{ 16+0, 16+0, 16+7,  16+11, 16+15}, /* IdSel  6, slot 0, J29 */
+		{ 16+1, 16+1, 16+8,  16+12, 16+16}, /* IdSel  7, slot 1, J26 */
+		{   -1,   -1,   -1,    -1,    -1},  /* IdSel  8, SIO */
+		{ 16+3, 16+3, 16+10, 16+14, 16+18}, /* IdSel  9, slot 3, J19 */
+		{ 16+6, 16+6, 16+6,  16+6,  16+6},  /* IdSel 10, USB */
+		{ 16+5, 16+5, 16+5,  16+5,  16+5}   /* IdSel 11, IDE */
 	};
 
 	common_fixup(5, 11, 5, irq_tab, 0);
-
 	SMCInit();
 }
+#endif
 
 /*
  * The AlphaPC64 is very similar to the EB66+ except that its slots
@@ -731,12 +837,12 @@
  */
 static inline void cabriolet_fixup(void)
 {
-	char irq_tab[5][5] = {
-		{ 16+2, 16+2, 16+7, 16+11, 16+15},      /* IdSel 5,  slot 2, J21 */
-		{ 16+0, 16+0, 16+5,  16+9, 16+13},      /* IdSel 6,  slot 0, J19 */
-		{ 16+1, 16+1, 16+6, 16+10, 16+14},      /* IdSel 7,  slot 1, J20 */
-		{   -1,   -1,   -1,    -1,    -1},	/* IdSel 8,  SIO         */
-		{ 16+3, 16+3, 16+8, 16+12, 16+16}       /* IdSel 9,  slot 3, J22 */
+	static char irq_tab[5][5] = {
+		{ 16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 5,  slot 2, J21 */
+		{ 16+0, 16+0, 16+5,  16+9, 16+13}, /* IdSel 6,  slot 0, J19 */
+		{ 16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7,  slot 1, J20 */
+		{   -1,   -1,   -1,    -1,    -1}, /* IdSel 8,  SIO         */
+		{ 16+3, 16+3, 16+8, 16+12, 16+16}  /* IdSel 9,  slot 3, J22 */
 	};
 
 	common_fixup(5, 9, 5, irq_tab, 0x398);
@@ -787,12 +893,12 @@
  */
 static inline void eb66_and_eb64p_fixup(void)
 {
-	char irq_tab[5][5] = {
-		{16+7, 16+7, 16+7, 16+7,  16+7},	/* IdSel 5,  slot ?, ?? */
-		{16+0, 16+0, 16+2, 16+4,  16+9},	/* IdSel 6,  slot ?, ?? */
-		{16+1, 16+1, 16+3, 16+8, 16+10},	/* IdSel 7,  slot ?, ?? */
-		{  -1,   -1,   -1,   -1,    -1},	/* IdSel 8,  SIO */
-		{16+6, 16+6, 16+6, 16+6,  16+6},	/* IdSel 9,  TULIP */
+	static char irq_tab[5][5] = {
+		{16+7, 16+7, 16+7, 16+7,  16+7},  /* IdSel 5,  slot ?, ?? */
+		{16+0, 16+0, 16+2, 16+4,  16+9},  /* IdSel 6,  slot ?, ?? */
+		{16+1, 16+1, 16+3, 16+8, 16+10},  /* IdSel 7,  slot ?, ?? */
+		{  -1,   -1,   -1,   -1,    -1},  /* IdSel 8,  SIO */
+		{16+6, 16+6, 16+6, 16+6,  16+6},  /* IdSel 9,  TULIP */
 	};
 	common_fixup(5, 9, 5, irq_tab, 0);
 }
@@ -836,21 +942,97 @@
  */
 static inline void mikasa_fixup(void)
 {
-	char irq_tab[8][5] = {
-	  /*INT    INTA   INTB   INTC   INTD */
-	  {16+12, 16+12, 16+12, 16+12, 16+12},	/* IdSel 17,  SCSI */
-	  {   -1,    -1,    -1,    -1,    -1},	/* IdSel 18,  PCEB */
-	  {   -1,    -1,    -1,    -1,    -1},	/* IdSel 19,  ???? */
-	  {   -1,    -1,    -1,    -1,    -1},	/* IdSel 20,  ???? */
-	  {   -1,    -1,    -1,    -1,    -1},	/* IdSel 21,  ???? */
-	  { 16+0,  16+0,  16+1,  16+2,  16+3},	/* IdSel 22,  slot 0 */
-	  { 16+4,  16+4,  16+5,  16+6,  16+7},	/* IdSel 23,  slot 1 */
-	  { 16+8,  16+8,  16+9, 16+10, 16+11},	/* IdSel 24,  slot 2 */
+	static char irq_tab[8][5] = {
+		/*INT    INTA   INTB   INTC   INTD */
+		{16+12, 16+12, 16+12, 16+12, 16+12},	/* IdSel 17,  SCSI */
+		{   -1,    -1,    -1,    -1,    -1},	/* IdSel 18,  PCEB */
+		{   -1,    -1,    -1,    -1,    -1},	/* IdSel 19,  ???? */
+		{   -1,    -1,    -1,    -1,    -1},	/* IdSel 20,  ???? */
+		{   -1,    -1,    -1,    -1,    -1},	/* IdSel 21,  ???? */
+		{ 16+0,  16+0,  16+1,  16+2,  16+3},	/* IdSel 22,  slot 0 */
+		{ 16+4,  16+4,  16+5,  16+6,  16+7},	/* IdSel 23,  slot 1 */
+		{ 16+8,  16+8,  16+9, 16+10, 16+11},	/* IdSel 24,  slot 2 */
 	};
 	common_fixup(6, 13, 5, irq_tab, 0);
 }
 
 /*
+ * Fixup configuration for NORITAKE (MIKASA is different)
+ *
+ * Summary @ 0x542, summary register #1:
+ * Bit      Meaning
+ * 0        All valid ints from summary regs 2 & 3
+ * 1        QLOGIC ISP1020A SCSI
+ * 2        Interrupt Line A from slot 0
+ * 3        Interrupt Line B from slot 0
+ * 4        Interrupt Line A from slot 1
+ * 5        Interrupt line B from slot 1
+ * 6        Interrupt Line A from slot 2
+ * 7        Interrupt Line B from slot 2
+ * 8        Interrupt Line A from slot 3
+ * 9        Interrupt Line B from slot 3
+ *10        Interrupt Line A from slot 4
+ *11        Interrupt Line B from slot 4
+ *12        Interrupt Line A from slot 5
+ *13        Interrupt Line B from slot 5
+ *14        Interrupt Line A from slot 6
+ *15        Interrupt Line B from slot 6
+ *
+ * Summary @ 0x544, summary register #2:
+ * Bit      Meaning
+ * 0        OR of all unmasked ints in SR #2
+ * 1        OR of secondary bus ints
+ * 2        Interrupt Line C from slot 0
+ * 3        Interrupt Line D from slot 0
+ * 4        Interrupt Line C from slot 1
+ * 5        Interrupt line D from slot 1
+ * 6        Interrupt Line C from slot 2
+ * 7        Interrupt Line D from slot 2
+ * 8        Interrupt Line C from slot 3
+ * 9        Interrupt Line D from slot 3
+ *10        Interrupt Line C from slot 4
+ *11        Interrupt Line D from slot 4
+ *12        Interrupt Line C from slot 5
+ *13        Interrupt Line D from slot 5
+ *14        Interrupt Line C from slot 6
+ *15        Interrupt Line D from slot 6
+ *
+ * The device to slot mapping looks like:
+ *
+ * Slot     Device
+ *  7       Intel PCI-EISA bridge chip
+ *  8       DEC PCI-PCI bridge chip
+ * 11       PCI on board slot 0
+ * 12       PCI on board slot 1
+ * 13       PCI on board slot 2
+ *   
+ *
+ * This two layered interrupt approach means that we allocate IRQ 16 and 
+ * above for PCI interrupts.  The IRQ relates to which bit the interrupt
+ * comes in on.  This makes interrupt processing much easier.
+ */
+static inline void noritake_fixup(void)
+{
+	static char irq_tab[13][5] = {
+		/*INT    INTA   INTB   INTC   INTD */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 18,  PCEB */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 19,  PPB  */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 20,  ???? */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 21,  ???? */
+		{ 16+2,  16+2,  16+3,  32+2,  32+3},  /* IdSel 22,  slot 0 */
+		{ 16+4,  16+4,  16+5,  32+4,  32+5},  /* IdSel 23,  slot 1 */
+		{ 16+6,  16+6,  16+7,  32+6,  32+7},  /* IdSel 24,  slot 2 */
+		/* The following are actually on bus 1, across the bridge */
+		{ 16+1,  16+1,  16+1,  16+1,  16+1},  /* IdSel 16,  QLOGIC */
+		{ 16+8,  16+8,  16+9,  32+8,  32+9},  /* IdSel 17,  slot 3 */
+		{16+10, 16+10, 16+11, 32+10, 32+11},  /* IdSel 18,  slot 4 */
+		{16+12, 16+12, 16+13, 32+12, 32+13},  /* IdSel 19,  slot 5 */
+		{16+14, 16+14, 16+15, 32+14, 32+15},  /* IdSel 20,  slot 6 */
+	};
+	common_fixup(7, 18, 5, irq_tab, 0);
+}
+
+/*
  * Fixup configuration for ALCOR
  *
  * Summary @ GRU_INT_REQ:
@@ -895,14 +1077,14 @@
  */
 static inline void alcor_fixup(void)
 {
-	char irq_tab[6][5] = {
-	  /*INT    INTA   INTB   INTC   INTD */
-	  { 16+8,  16+8,  16+9, 16+10, 16+11},	/* IdSel 18,  slot 0 */
-	  {16+16, 16+16, 16+17, 16+18, 16+19},	/* IdSel 19,  slot 3 */
-	  {16+12, 16+12, 16+13, 16+14, 16+15},	/* IdSel 20,  slot 4 */
-	  {   -1,    -1,    -1,    -1,    -1},	/* IdSel 21,  PCEB   */
-	  { 16+0,  16+0,  16+1,  16+2,  16+3},	/* IdSel 22,  slot 2 */
-	  { 16+4,  16+4,  16+5,  16+6,  16+7},	/* IdSel 23,  slot 1 */
+	static char irq_tab[6][5] = {
+		/*INT    INTA   INTB   INTC   INTD */
+		{ 16+8,  16+8,  16+9, 16+10, 16+11},	/* IdSel 18,  slot 0 */
+		{16+16, 16+16, 16+17, 16+18, 16+19},	/* IdSel 19,  slot 3 */
+		{16+12, 16+12, 16+13, 16+14, 16+15},	/* IdSel 20,  slot 4 */
+		{   -1,    -1,    -1,    -1,    -1},	/* IdSel 21,  PCEB   */
+		{ 16+0,  16+0,  16+1,  16+2,  16+3},	/* IdSel 22,  slot 2 */
+		{ 16+4,  16+4,  16+5,  16+6,  16+7},	/* IdSel 23,  slot 1 */
 	};
 	common_fixup(7, 12, 5, irq_tab, 0);
 }
@@ -950,19 +1132,183 @@
  */
 static inline void xlt_fixup(void)
 {
-	char irq_tab[7][5] = {
-	  /*INT    INTA   INTB   INTC   INTD */
-	  {16+13, 16+13, 16+13, 16+13, 16+13},	/* IdSel 17,  TULIP  */
-	  { 16+8,  16+8,  16+9, 16+10, 16+11},	/* IdSel 18,  slot 0 */
-	  {   -1,    -1,    -1,    -1,    -1},	/* IdSel 19,  none   */
-	  {16+12, 16+12, 16+12, 16+12, 16+12},	/* IdSel 20,  SCSI   */
-	  {   -1,    -1,    -1,    -1,    -1},	/* IdSel 21,  SIO    */
-	  { 16+0,  16+0,  16+1,  16+2,  16+3},	/* IdSel 22,  slot 2 */
-	  { 16+4,  16+4,  16+5,  16+6,  16+7},	/* IdSel 23,  slot 1 */
+	static char irq_tab[7][5] = {
+		/*INT    INTA   INTB   INTC   INTD */
+		{16+13, 16+13, 16+13, 16+13, 16+13},	/* IdSel 17,  TULIP  */
+		{ 16+8,  16+8,  16+9, 16+10, 16+11},	/* IdSel 18,  slot 0 */
+		{   -1,    -1,    -1,    -1,    -1},	/* IdSel 19,  none   */
+		{16+12, 16+12, 16+12, 16+12, 16+12},	/* IdSel 20,  SCSI   */
+		{   -1,    -1,    -1,    -1,    -1},	/* IdSel 21,  SIO    */
+		{ 16+0,  16+0,  16+1,  16+2,  16+3},	/* IdSel 22,  slot 2 */
+		{ 16+4,  16+4,  16+5,  16+6,  16+7},	/* IdSel 23,  slot 1 */
 	};
 	common_fixup(6, 12, 5, irq_tab, 0);
 }
 
+/*
+ * Fixup configuration for ALPHA SABLE (2100) - 2100A is different ??
+ *
+ * Summary Registers (536/53a/53c):
+ * Bit      Meaning
+ *-----------------
+ * 0        PCI slot 0
+ * 1        NCR810 (builtin)
+ * 2        TULIP (builtin)
+ * 3        mouse
+ * 4        PCI slot 1
+ * 5        PCI slot 2
+ * 6        keyboard
+ * 7        floppy
+ * 8        COM2
+ * 9        parallel port
+ *10        EISA irq 3
+ *11        EISA irq 4
+ *12        EISA irq 5
+ *13        EISA irq 6
+ *14        EISA irq 7
+ *15        COM1
+ *16        EISA irq 9
+ *17        EISA irq 10
+ *18        EISA irq 11
+ *19        EISA irq 12
+ *20        EISA irq 13
+ *21        EISA irq 14
+ *22        NC
+ *23        IIC
+ *
+ * The device to slot mapping looks like:
+ *
+ * Slot     Device
+ *  0       TULIP
+ *  1       SCSI
+ *  2       PCI-EISA bridge
+ *  3       none
+ *  4       none
+ *  5       none
+ *  6       PCI on board slot 0
+ *  7       PCI on board slot 1
+ *  8       PCI on board slot 2
+ *   
+ *
+ * This two layered interrupt approach means that we allocate IRQ 16 and 
+ * above for PCI interrupts.  The IRQ relates to which bit the interrupt
+ * comes in on.  This makes interrupt processing much easier.
+ */
+/* NOTE: the IRQ assignments below are arbitrary, but need to be consistent
+   with the values in the sable_irq_to_mask[] and sable_mask_to_irq[] tables
+   in irq.c
+ */
+static inline void sable_fixup(void)
+{
+        static char irq_tab[9][5] = {
+		/*INT    INTA   INTB   INTC   INTD */
+		{ 32+0,  32+0,  32+0,  32+0,  32+0},  /* IdSel 0,  TULIP  */
+		{ 32+1,  32+1,  32+1,  32+1,  32+1},  /* IdSel 1,  SCSI   */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 2,  SIO   */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 3,  none   */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 4,  none   */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 5,  none   */
+		{ 32+2,  32+2,  32+2,  32+2,  32+2},  /* IdSel 6,  slot 0 */
+		{ 32+3,  32+3,  32+3,  32+3,  32+3},  /* IdSel 7,  slot 1 */
+		{ 32+4,  32+4,  32+4,  32+4,  32+4},  /* IdSel 8,  slot 2 */
+        };
+        common_fixup(0, 8, 5, irq_tab, 0);
+}
+
+/*
+ * Fixup configuration for MIATA (EV56+PYXIS)
+ *
+ * Summary @ PYXIS_INT_REQ:
+ * Bit      Meaning
+ * 0        Fan Fault
+ * 1        NMI
+ * 2        Halt/Reset switch
+ * 3        none
+ * 4        CID0 (Riser ID)
+ * 5        CID1 (Riser ID)
+ * 6        Interval timer
+ * 7        PCI-ISA Bridge
+ * 8        Ethernet
+ * 9        EIDE (deprecated, ISA 14/15 used)
+ *10        none
+ *11        USB
+ *12        Interrupt Line A from slot 4
+ *13        Interrupt Line B from slot 4
+ *14        Interrupt Line C from slot 4
+ *15        Interrupt Line D from slot 4
+ *16        Interrupt Line A from slot 5
+ *17        Interrupt line B from slot 5
+ *18        Interrupt Line C from slot 5
+ *19        Interrupt Line D from slot 5
+ *20        Interrupt Line A from slot 1
+ *21        Interrupt Line B from slot 1
+ *22        Interrupt Line C from slot 1
+ *23        Interrupt Line D from slot 1
+ *24        Interrupt Line A from slot 2
+ *25        Interrupt Line B from slot 2
+ *26        Interrupt Line C from slot 2
+ *27        Interrupt Line D from slot 2
+ *27        Interrupt Line A from slot 3
+ *29        Interrupt Line B from slot 3
+ *30        Interrupt Line C from slot 3
+ *31        Interrupt Line D from slot 3
+ *
+ * The device to slot mapping looks like:
+ *
+ * Slot     Device
+ *  3       DC21142 Ethernet
+ *  4       EIDE CMD646
+ *  5       none
+ *  6       USB
+ *  7       PCI-ISA bridge
+ *  8       PCI-PCI Bridge      (SBU Riser)
+ *  9       none
+ * 10       none
+ * 11       PCI on board slot 4 (SBU Riser)
+ * 12       PCI on board slot 5 (SBU Riser)
+ *
+ *  These are behind the bridge, so I'm not sure what to do...
+ *
+ * 13       PCI on board slot 1 (SBU Riser)
+ * 14       PCI on board slot 2 (SBU Riser)
+ * 15       PCI on board slot 3 (SBU Riser)
+ *   
+ *
+ * This two layered interrupt approach means that we allocate IRQ 16 and 
+ * above for PCI interrupts.  The IRQ relates to which bit the interrupt
+ * comes in on.  This makes interrupt processing much easier.
+ */
+
+#ifdef CONFIG_ALPHA_MIATA
+static inline void miata_fixup(void)
+{
+        static char irq_tab[18][5] = {
+		/*INT    INTA   INTB   INTC   INTD */
+		{16+ 8, 16+ 8, 16+ 8, 16+ 8, 16+ 8},  /* IdSel 14,  DC21142  */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 15,  EIDE   */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 16,  none   */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 17,  none   */
+/*		{16+11, 16+11, 16+11, 16+11, 16+11},*//* IdSel 17,  USB ??  */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 18,  PCI-ISA */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 19,  PCI-PCI */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 20,  none    */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 21,  none    */
+		{16+12, 16+12, 16+13, 16+14, 16+15},  /* IdSel 22,  slot 4 */
+		{16+16, 16+16, 16+17, 16+18, 16+19},  /* IdSel 23,  slot 5 */
+		/* The following are actually on bus 1, across the bridge */
+		{16+20, 16+20, 16+21, 16+22, 16+23},  /* IdSel 24,  slot 1 */
+		{16+24, 16+24, 16+25, 16+26, 16+27},  /* IdSel 25,  slot 2 */
+		{16+28, 16+28, 16+29, 16+30, 16+31},  /* IdSel 26,  slot 3 */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 27,  none    */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 28,  none    */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 29,  none    */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 30,  none    */
+		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 31,  PCI-PCI */
+        };
+	common_fixup(3, 20, 5, irq_tab, 0);
+	es1888_init();
+}
+#endif
 
 /*
  * Fixup configuration for all boards that route the PCI interrupts
@@ -1011,6 +1357,8 @@
 		{ 0,  0,  0,  0,  0}, /* idsel 14 AS255 TULIP */
 #endif
 	};
+	const size_t pirq_tab_len = sizeof(pirq_tab)/sizeof(pirq_tab[0]);
+
 	/*
 	 * route_tab selects irq routing in PCI/ISA bridge so that:
 	 *		PIRQ0 -> irq 15
@@ -1021,7 +1369,12 @@
 	 * This probably ought to be configurable via MILO.  For
 	 * example, sound boards seem to like using IRQ 9.
 	 */
-#ifdef CONFIG_ALPHA_NONAME
+
+#if defined(CONFIG_ALPHA_BOOK1)
+        /* for the AlphaBook1, NCR810 SCSI is 14, PCMCIA controller is 15 */
+        const unsigned int route_tab = 0x0e0f0a0a;
+
+#elif defined(CONFIG_ALPHA_NONAME)
 	/*
 	 * For UDB, the only available PCI slot must not map to IRQ 9,
 	 *  since that's the builtin MSS sound chip. That PCI slot
@@ -1033,9 +1386,10 @@
 	 *  selected... :-(
 	 */
 	const unsigned int route_tab = 0x0b0a0f09;
-#else /* CONFIG_ALPHA_NONAME */
+#else
 	const unsigned int route_tab = 0x0b0a090f;
-#endif /* CONFIG_ALPHA_NONAME */
+#endif
+
 	unsigned int level_bits;
 	unsigned char pin, slot;
 	int pirq;
@@ -1047,11 +1401,13 @@
 	 */
 	level_bits = 0;
 	for (dev = pci_devices; dev; dev = dev->next) {
-		if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE)
+		if ((dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) &&
+		    (dev->class >> 8 != PCI_CLASS_BRIDGE_PCMCIA))
 			continue;
+
 		dev->irq = 0;
 		if (dev->bus->number != 0) {
-			struct pci_dev *curr = dev ;
+			struct pci_dev *curr = dev;
 			/*
 			 * read the pin and do the PCI-PCI bridge
 			 * interrupt pin swizzle
@@ -1059,41 +1415,44 @@
 			pcibios_read_config_byte(dev->bus->number, dev->devfn,
 						 PCI_INTERRUPT_PIN, &pin);
 			/* cope with 0 */
-			if (pin == 0) pin = 1 ;
+			if (pin == 0)
+				pin = 1;
 			/* follow the chain of bridges, swizzling as we go */
 			do {
 				/* swizzle */
-				pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn)) ;
+				pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn));
 				/* move up the chain of bridges */
-				curr = curr->bus->self ;
-			} while (curr->bus->self) ;
+				curr = curr->bus->self;
+			} while (curr->bus->self);
 			/* The slot is the slot of the last bridge. */
-			slot = PCI_SLOT(curr->devfn) ;
+			slot = PCI_SLOT(curr->devfn);
 		} else {
 			/* work out the slot */
-			slot = PCI_SLOT(dev->devfn) ;
+			slot = PCI_SLOT(dev->devfn);
 			/* read the pin */
 			pcibios_read_config_byte(dev->bus->number, dev->devfn,
 						 PCI_INTERRUPT_PIN, &pin);
 		}
 
-		if (slot < 6 || slot >= 6 + sizeof(pirq_tab)/sizeof(pirq_tab[0])) {
+		if (slot < 6 || slot >= 6 + pirq_tab_len) {
 			printk("bios32.sio_fixup: "
-			       "weird, found device %04x:%04x in non-existent slot %d!!\n",
+			       "weird, found device %04x:%04x in"
+			       " non-existent slot %d!!\n",
 			       dev->vendor, dev->device, slot);
 			continue;
 		}
 		pirq = pirq_tab[slot - 6][pin];
 
 		DBG_DEVS(("sio_fixup: bus %d  slot 0x%x  VID 0x%x  DID 0x%x\n"
-			  "           int_slot 0x%x  int_pin 0x%x,  pirq 0x%x\n",
-			  dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device,
-			  slot, pin, pirq));
+			  "           int_slot 0x%x  pin 0x%x  pirq 0x%x\n",
+			  dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor,
+			  dev->device, slot, pin, pirq));
 		/*
 		 * if it's a VGA, enable its BIOS ROM at C0000
 		 */
 		if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
-			pcibios_write_config_dword(dev->bus->number, dev->devfn,
+			pcibios_write_config_dword(dev->bus->number,
+						   dev->devfn,
 						   PCI_ROM_ADDRESS,
 						   0x000c0000 | PCI_ROM_ADDRESS_ENABLE);
 		}
@@ -1103,22 +1462,60 @@
 
 		if (pirq < 0) {
 			printk("bios32.sio_fixup: "
-			       "weird, device %04x:%04x coming in on slot %d has no irq line!!\n",
+			       "weird, device %04x:%04x coming in on"
+			       " slot %d has no irq line!!\n",
 			       dev->vendor, dev->device, slot);
 			continue;
 		}
 
 		dev->irq = (route_tab >> (8 * pirq)) & 0xff;
 
-		/* must set the PCI IRQs to level triggered */
-		level_bits |= (1 << dev->irq);
+#ifndef CONFIG_ALPHA_BOOK1
+                /* do not set *ANY* level triggers for AlphaBook1 */
+                /* must set the PCI IRQs to level triggered */
+                level_bits |= (1 << dev->irq);
+#endif /* !CONFIG_ALPHA_BOOK1 */
 
 #if PCI_MODIFY
 		/* tell the device: */
 		pcibios_write_config_byte(dev->bus->number, dev->devfn,
 					  PCI_INTERRUPT_LINE, dev->irq);
 #endif
-	}
+
+#ifdef CONFIG_ALPHA_BOOK1
+                /*
+                 * On the AlphaBook1, the PCMCIA chip (Cirrus 6729)
+                 * is sensitive to PCI bus bursts, so we must DISABLE
+                 * burst mode for the NCR 8xx SCSI... :-(
+		 *
+		 * Note that the NCR810 SCSI driver must preserve the
+		 * setting of the bit in order for this to work.  At the
+		 * moment (2.0.29), ncr53c8xx.c does NOT do this, but
+		 * 53c7,8xx.c DOES.
+                 */
+                if (dev->vendor == PCI_VENDOR_ID_NCR &&
+                    (dev->device == PCI_DEVICE_ID_NCR_53C810 ||
+                     dev->device == PCI_DEVICE_ID_NCR_53C815 ||
+                     dev->device == PCI_DEVICE_ID_NCR_53C820 ||
+                     dev->device == PCI_DEVICE_ID_NCR_53C825)) {
+			unsigned int io_port;
+			unsigned char ctest4;
+
+			pcibios_read_config_dword(dev->bus->number,
+						  dev->devfn,
+						  PCI_BASE_ADDRESS_0,
+						  &io_port);
+			io_port &= PCI_BASE_ADDRESS_IO_MASK;
+			ctest4 = inb(io_port+0x21);
+			if (!(ctest4 & 0x80)) {
+				printk("AlphaBook1 NCR init: setting"
+				       " burst disable\n");
+				outb(ctest4 | 0x80, io_port+0x21);
+			}
+                }
+#endif /* CONFIG_ALPHA_BOOK1 */
+	} /* end for-devs */
+
 	/*
 	 * Now, make all PCI interrupts level sensitive.  Notice:
 	 * these registers must be accessed byte-wise.  inw()/outw()
@@ -1128,11 +1525,34 @@
 	 *  so that the only bits getting set are for devices actually found.
 	 * Note that we do preserve the remainder of the bits, which we hope
 	 *  will be set correctly by ARC/SRM.
+	 *
+	 * Note: we at least preserve any level-set bits on AlphaBook1
 	 */
 	level_bits |= ((inb(0x4d0) | (inb(0x4d1) << 8)) & 0x71ff);
 	outb((level_bits >> 0) & 0xff, 0x4d0);
 	outb((level_bits >> 8) & 0xff, 0x4d1);
-	enable_ide(0x26e);
+
+#ifdef CONFIG_ALPHA_BOOK1
+        {
+		unsigned char orig, config;
+		/* On the AlphaBook1, make sure that register PR1
+		   indicates 1Mb mem */
+		outb(0x0f, 0x3ce); orig = inb(0x3cf);   /* read PR5  */
+		outb(0x0f, 0x3ce); outb(0x05, 0x3cf);   /* unlock PR0-4 */
+		outb(0x0b, 0x3ce); config = inb(0x3cf); /* read PR1 */
+		if ((config & 0xc0) != 0xc0) {
+			printk("AlphaBook1 VGA init: setting 1Mb memory\n");
+			config |= 0xc0;
+			outb(0x0b, 0x3ce); outb(config, 0x3cf); /* write PR1 */
+		}
+		outb(0x0f, 0x3ce); outb(orig, 0x3cf); /* (re)lock PR0-4 */
+        }
+#endif /* CONFIG_ALPHA_BOOK1 */
+
+#ifndef CONFIG_ALPHA_BOOK1
+        /* Do not do IDE init for AlphaBook1 */
+        enable_ide(0x26e);
+#endif
 }
 
 
@@ -1152,7 +1572,8 @@
 	/*
 	 * Now is the time to do all those dirty little deeds...
 	 */
-#if defined(CONFIG_ALPHA_NONAME) || defined(CONFIG_ALPHA_AVANTI) || defined(CONFIG_ALPHA_P2K)
+#if defined(CONFIG_ALPHA_NONAME) || defined(CONFIG_ALPHA_AVANTI) || \
+    defined(CONFIG_ALPHA_P2K)
 	sio_fixup();
 #elif defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB164)
 	cabriolet_fixup();
@@ -1170,72 +1591,82 @@
 	alcor_fixup();
 #elif defined(CONFIG_ALPHA_XLT)
 	xlt_fixup();
+#elif defined(CONFIG_ALPHA_SABLE)
+	sable_fixup();
+#elif defined(CONFIG_ALPHA_MIATA)
+	miata_fixup();
+#elif defined(CONFIG_ALPHA_NORITAKE)
+	noritake_fixup();
 #else
-#	error You must tell me what kind of platform you want.
+# error "You must tell me what kind of platform you want."
 #endif
 
+#ifndef CONFIG_ABSTRACT_CONSOLE
 #ifdef CONFIG_TGA_CONSOLE
 	tga_console_init();
-#endif /* CONFIG_TGA_CONSOLE */
+#endif
+#endif
 
 	return mem_start;
 }
 
 
-asmlinkage int sys_pciconfig_read(
-	unsigned long bus,
-	unsigned long dfn,
-	unsigned long off,
-	unsigned long len,
-	unsigned char *buf)
+asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn,
+				  unsigned long off, unsigned long len,
+				  unsigned char *buf)
 {
 	unsigned char ubyte;
 	unsigned short ushort;
 	unsigned int uint;
 	long err = 0;
 
+	if (!suser())
+		return -EPERM;
+
 	lock_kernel();
 	switch (len) {
-	    case 1:
+	case 1:
 		err = pcibios_read_config_byte(bus, dfn, off, &ubyte);
 		if (err != PCIBIOS_SUCCESSFUL)
-		    ubyte = 0xff;
+			ubyte = 0xff;
 		put_user(ubyte, buf);
 		break;
-	    case 2:
+	case 2:
 		err = pcibios_read_config_word(bus, dfn, off, &ushort);
 		if (err != PCIBIOS_SUCCESSFUL)
-		    ushort = 0xffff;
+			ushort = 0xffff;
 		put_user(ushort, (unsigned short *)buf);
 		break;
-	    case 4:
+	case 4:
 		err = pcibios_read_config_dword(bus, dfn, off, &uint);
 		if (err != PCIBIOS_SUCCESSFUL)
-		    uint = 0xffffffff;
+			uint = 0xffffffff;
 		put_user(uint, (unsigned int *)buf);
 		break;
-	    default:
+	default:
 		err = -EINVAL;
 		break;
 	}
 	unlock_kernel();
 	return err;
 }
-asmlinkage int sys_pciconfig_write(
-	unsigned long bus,
-	unsigned long dfn,
-	unsigned long off,
-	unsigned long len,
-	unsigned char *buf)
+
+
+asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned long dfn,
+				   unsigned long off, unsigned long len,
+				   unsigned char *buf)
 {
 	unsigned char ubyte;
 	unsigned short ushort;
 	unsigned int uint;
 	long err = 0;
 
+	if (!suser())
+		return -EPERM;
+
 	lock_kernel();
 	switch (len) {
-	    case 1:
+	case 1:
 		err = get_user(ubyte, buf);
 		if (err)
 			break;
@@ -1244,7 +1675,7 @@
 			err = -EFAULT;
 		}
 		break;
-	    case 2:
+	case 2:
 		err = get_user(ushort, (unsigned short *)buf);
 		if (err)
 			break;
@@ -1253,7 +1684,7 @@
 			err = -EFAULT;
 		}
 		break;
-	    case 4:
+	case 4:
 		err = get_user(uint, (unsigned int *)buf);
 		if (err)
 			break;
@@ -1262,7 +1693,7 @@
 			err = -EFAULT;
 		}
 		break;
-	    default:
+	default:
 		err = -EINVAL;
 		break;
 	}
@@ -1270,8 +1701,8 @@
 	return err;
 }
 
-#ifdef CONFIG_ALPHA_PC164
 
+#ifdef CONFIG_ALPHA_PC164
 /* device "activate" register contents */
 #define DEVICE_ON		1
 #define DEVICE_OFF		0
@@ -1326,206 +1757,233 @@
 
 #define SMC_DEBUG 0
 
-unsigned long SMCConfigState( unsigned long baseAddr )
+static unsigned long SMCConfigState(unsigned long baseAddr)
 {
-    unsigned char devId;
-    unsigned char devRev;
-
-    unsigned long configPort;
-    unsigned long indexPort;
-    unsigned long dataPort;
+	unsigned char devId;
+	unsigned char devRev;
 
-    configPort = indexPort = baseAddr;
-    dataPort = ( unsigned long )( ( char * )configPort + 1 );
-
-    outb(CONFIG_ON_KEY, configPort);
-    outb(CONFIG_ON_KEY, configPort);
-    outb(DEVICE_ID, indexPort);
-    devId = inb(dataPort);
-    if ( devId == VALID_DEVICE_ID ) {
-        outb(DEVICE_REV, indexPort);
-        devRev = inb(dataPort);
-    }
-    else {
-        baseAddr = 0;
-    }
-    return( baseAddr );
+	unsigned long configPort;
+	unsigned long indexPort;
+	unsigned long dataPort;
+
+	configPort = indexPort = baseAddr;
+	dataPort = configPort + 1;
+
+	outb(CONFIG_ON_KEY, configPort);
+	outb(CONFIG_ON_KEY, configPort);
+	outb(DEVICE_ID, indexPort);
+	devId = inb(dataPort);
+	if ( devId == VALID_DEVICE_ID ) {
+		outb(DEVICE_REV, indexPort);
+		devRev = inb(dataPort);
+	}
+	else {
+		baseAddr = 0;
+	}
+	return baseAddr;
 }
 
-void SMCRunState( unsigned long baseAddr )
+static void SMCRunState(unsigned long baseAddr)
 {
-    outb(CONFIG_OFF_KEY, baseAddr);
+	outb(CONFIG_OFF_KEY, baseAddr);
 }
 
-unsigned long SMCDetectUltraIO(void)
+static unsigned long SMCDetectUltraIO(void)
 {
-    unsigned long baseAddr;
+	unsigned long baseAddr;
 
-    baseAddr = 0x3F0;
-    if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x3F0 ) {
-        return( baseAddr );
-    }
-    baseAddr = 0x370;
-    if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x370 ) {
-        return( baseAddr );
-    }
-    return( ( unsigned long )0 );
+	baseAddr = 0x3F0;
+	if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x3F0 ) {
+		return( baseAddr );
+	}
+	baseAddr = 0x370;
+	if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x370 ) {
+		return( baseAddr );
+	}
+	return( ( unsigned long )0 );
 }
 
-void SMCEnableDevice( unsigned long baseAddr,
-		      unsigned long device,
-		      unsigned long portaddr,
-		      unsigned long interrupt)
+static void SMCEnableDevice(unsigned long baseAddr,
+			    unsigned long device,
+			    unsigned long portaddr,
+			    unsigned long interrupt)
 {
-    unsigned long indexPort;
-    unsigned long dataPort;
+	unsigned long indexPort;
+	unsigned long dataPort;
 
-    indexPort = baseAddr;
-    dataPort = ( unsigned long )( ( char * )baseAddr + 1 );
+	indexPort = baseAddr;
+	dataPort = baseAddr + 1;
 
-    outb(LOGICAL_DEVICE_NUMBER, indexPort);
-    outb(device, dataPort);
+	outb(LOGICAL_DEVICE_NUMBER, indexPort);
+	outb(device, dataPort);
 
-    outb(ADDR_LO, indexPort);
-    outb(( portaddr & 0xFF ), dataPort);
+	outb(ADDR_LO, indexPort);
+	outb(( portaddr & 0xFF ), dataPort);
 
-    outb(ADDR_HI, indexPort);
-    outb(( ( portaddr >> 8 ) & 0xFF ), dataPort);
+	outb(ADDR_HI, indexPort);
+	outb((portaddr >> 8) & 0xFF, dataPort);
 
-    outb(INTERRUPT_SEL, indexPort);
-    outb(interrupt, dataPort);
+	outb(INTERRUPT_SEL, indexPort);
+	outb(interrupt, dataPort);
 
-    outb(ACTIVATE, indexPort);
-    outb(DEVICE_ON, dataPort);
+	outb(ACTIVATE, indexPort);
+	outb(DEVICE_ON, dataPort);
 }
 
-void SMCEnableKYBD( unsigned long baseAddr )
+static void SMCEnableKYBD(unsigned long baseAddr)
 {
-    unsigned long indexPort;
-    unsigned long dataPort;
+	unsigned long indexPort;
+	unsigned long dataPort;
 
-    indexPort = baseAddr;
-    dataPort = ( unsigned long )( ( char * )baseAddr + 1 );
+	indexPort = baseAddr;
+	dataPort = baseAddr + 1;
 
-    outb(LOGICAL_DEVICE_NUMBER, indexPort);
-    outb(KYBD, dataPort);
+	outb(LOGICAL_DEVICE_NUMBER, indexPort);
+	outb(KYBD, dataPort);
 
-    outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */
-    outb(KYBD_INTERRUPT, dataPort);
+	outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */
+	outb(KYBD_INTERRUPT, dataPort);
 
-    outb(INTERRUPT_SEL_2, indexPort);/* Secondary interrupt select */
-    outb(MOUS_INTERRUPT, dataPort);
+	outb(INTERRUPT_SEL_2, indexPort); /* Secondary interrupt select */
+	outb(MOUS_INTERRUPT, dataPort);
 
-    outb(ACTIVATE, indexPort);
-    outb(DEVICE_ON, dataPort);
+	outb(ACTIVATE, indexPort);
+	outb(DEVICE_ON, dataPort);
 }
 
-void SMCEnableFDC( unsigned long baseAddr )
+static void SMCEnableFDC(unsigned long baseAddr)
 {
-    unsigned long indexPort;
-    unsigned long dataPort;
+	unsigned long indexPort;
+	unsigned long dataPort;
 
-    unsigned char oldValue;
+	unsigned char oldValue;
 
-    indexPort = baseAddr;
-    dataPort = ( unsigned long )( ( char * )baseAddr + 1 );
+	indexPort = baseAddr;
+	dataPort = baseAddr + 1;
 
-    outb(LOGICAL_DEVICE_NUMBER, indexPort);
-    outb(FDC, dataPort);
+	outb(LOGICAL_DEVICE_NUMBER, indexPort);
+	outb(FDC, dataPort);
 
-    outb(FDD_MODE_REGISTER, indexPort);
-    oldValue = inb(dataPort);
+	outb(FDD_MODE_REGISTER, indexPort);
+	oldValue = inb(dataPort);
 
-    oldValue |= 0x0E;                   /* Enable burst mode */
-    outb(oldValue, dataPort);
+	oldValue |= 0x0E;                   /* Enable burst mode */
+	outb(oldValue, dataPort);
 
-    outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */
-    outb(0x06, dataPort );
+	outb(INTERRUPT_SEL, indexPort);	    /* Primary interrupt select */
+	outb(0x06, dataPort );
 
-    outb(DMA_CHANNEL_SEL, indexPort);  /* DMA channel select */
-    outb(0x02, dataPort);
+	outb(DMA_CHANNEL_SEL, indexPort);   /* DMA channel select */
+	outb(0x02, dataPort);
 
-    outb(ACTIVATE, indexPort);
-    outb(DEVICE_ON, dataPort);
+	outb(ACTIVATE, indexPort);
+	outb(DEVICE_ON, dataPort);
 }
 
 #if SMC_DEBUG
-void SMCReportDeviceStatus( unsigned long baseAddr )
+static void SMCReportDeviceStatus(unsigned long baseAddr)
 {
-    unsigned long indexPort;
-    unsigned long dataPort;
-    unsigned char currentControl;
-
-    indexPort = baseAddr;
-    dataPort = ( unsigned long )( ( char * )baseAddr + 1 );
-
-    outb(POWER_CONTROL, indexPort);
-    currentControl = inb(dataPort);
-
-    if ( currentControl & ( 1 << FDC ) )
-        printk( "\t+FDC Enabled\n" );
-    else
-        printk( "\t-FDC Disabled\n" );
-
-    if ( currentControl & ( 1 << IDE1 ) )
-        printk( "\t+IDE1 Enabled\n" );
-    else
-        printk( "\t-IDE1 Disabled\n" );
-
-    if ( currentControl & ( 1 << IDE2 ) )
-        printk( "\t+IDE2 Enabled\n" );
-    else
-        printk( "\t-IDE2 Disabled\n" );
-
-    if ( currentControl & ( 1 << PARP ) )
-        printk( "\t+PARP Enabled\n" );
-    else
-        printk( "\t-PARP Disabled\n" );
-
-    if ( currentControl & ( 1 << SER1 ) )
-        printk( "\t+SER1 Enabled\n" );
-    else
-        printk( "\t-SER1 Disabled\n" );
+	unsigned long indexPort;
+	unsigned long dataPort;
+	unsigned char currentControl;
+
+	indexPort = baseAddr;
+	dataPort = baseAddr + 1;
+
+	outb(POWER_CONTROL, indexPort);
+	currentControl = inb(dataPort);
+
+	printk(currentControl & (1 << FDC)
+	       ? "\t+FDC Enabled\n" : "\t-FDC Disabled\n");
+	printk(currentControl & (1 << IDE1)
+	       ? "\t+IDE1 Enabled\n" : "\t-IDE1 Disabled\n");
+	printk(currentControl & (1 << IDE2)
+	       ? "\t+IDE2 Enabled\n" : "\t-IDE2 Disabled\n");
+	printk(currentControl & (1 << PARP)
+	       ? "\t+PARP Enabled\n" : "\t-PARP Disabled\n");
+	printk(currentControl & (1 << SER1)
+	       ? "\t+SER1 Enabled\n" : "\t-SER1 Disabled\n");
+	printk(currentControl & (1 << SER2)
+	       ? "\t+SER2 Enabled\n" : "\t-SER2 Disabled\n");
 
-    if ( currentControl & ( 1 << SER2 ) )
-        printk( "\t+SER2 Enabled\n" );
-    else
-        printk( "\t-SER2 Disabled\n" );
-
-    printk( "\n" );
+	printk( "\n" );
 }
 #endif
 
-int SMCInit(void)
+static int SMCInit(void)
 {
-    unsigned long SMCUltraBase;
+	unsigned long SMCUltraBase;
 
-    if ( ( SMCUltraBase = SMCDetectUltraIO( ) ) != ( unsigned long )0 ) {
-        printk( "SMC FDC37C93X Ultra I/O Controller found @ 0x%lx\n",
-		SMCUltraBase );
+	if ((SMCUltraBase = SMCDetectUltraIO()) != 0UL) {
+		printk("SMC FDC37C93X Ultra I/O Controller found @ 0x%lx\n",
+		       SMCUltraBase);
 #if SMC_DEBUG
-        SMCReportDeviceStatus( SMCUltraBase );
+		SMCReportDeviceStatus(SMCUltraBase);
 #endif
-        SMCEnableDevice( SMCUltraBase, SER1, COM1_BASE, COM1_INTERRUPT );
-        SMCEnableDevice( SMCUltraBase, SER2, COM2_BASE, COM2_INTERRUPT );
-        SMCEnableDevice( SMCUltraBase, PARP, PARP_BASE, PARP_INTERRUPT );
-	/* on PC164, IDE on the SMC is not enabled; CMD646 (PCI) on MB */
-        SMCEnableKYBD( SMCUltraBase );
-        SMCEnableFDC( SMCUltraBase );
+		SMCEnableDevice(SMCUltraBase, SER1, COM1_BASE, COM1_INTERRUPT);
+		SMCEnableDevice(SMCUltraBase, SER2, COM2_BASE, COM2_INTERRUPT);
+		SMCEnableDevice(SMCUltraBase, PARP, PARP_BASE, PARP_INTERRUPT);
+		/* On PC164, IDE on the SMC is not enabled;
+		   CMD646 (PCI) on MB */
+		SMCEnableKYBD(SMCUltraBase);
+		SMCEnableFDC(SMCUltraBase);
 #if SMC_DEBUG
-        SMCReportDeviceStatus( SMCUltraBase );
+		SMCReportDeviceStatus(SMCUltraBase);
 #endif
-        SMCRunState( SMCUltraBase );
-        return( 1 );
-    }
-    else {
+		SMCRunState(SMCUltraBase);
+		return 1;
+	}
+	else {
 #if SMC_DEBUG
-        printk( "No SMC FDC37C93X Ultra I/O Controller found\n" );
+		printk("No SMC FDC37C93X Ultra I/O Controller found\n");
 #endif
-        return( 0 );
-    }
+		return 0;
+	}
 }
-
 #endif /* CONFIG_ALPHA_PC164 */
+
+#ifdef CONFIG_ALPHA_MIATA
+/*
+ * Init the built-in ES1888 sound chip (SB16 compatible)
+ */
+static int es1888_init(void)
+{
+	/* Sequence of IO reads to init the audio controller */
+	inb(0x0229);
+	inb(0x0229);
+	inb(0x0229);
+	inb(0x022b);
+	inb(0x0229);
+	inb(0x022b);
+	inb(0x0229);
+	inb(0x0229);
+	inb(0x022b);
+	inb(0x0229);
+	inb(0x0220); /* This sets the base address to 0x220 */
+
+	/* Sequence to set DMA channels */
+	outb(0x01, 0x0226);		/* reset */
+	inb(0x0226);			/* pause */
+	outb(0x00, 0x0226);		/* release reset */
+	while (!(inb(0x022e) & 0x80))	/* wait for bit 7 to assert*/
+		continue;
+	inb(0x022a);			/* pause */
+	outb(0xc6, 0x022c);		/* enable extended mode */
+	while (inb(0x022c) & 0x80)	/* wait for bit 7 to deassert */
+		continue;
+	outb(0xb1, 0x022c);		/* setup for write to Interrupt CR */
+	while (inb(0x022c) & 0x80)	/* wait for bit 7 to deassert */
+		continue;
+	outb(0x14, 0x022c);		/* set IRQ 5 */
+	while (inb(0x022c) & 0x80)	/* wait for bit 7 to deassert */
+		continue;
+	outb(0xb2, 0x022c);		/* setup for write to DMA CR */
+	while (inb(0x022c) & 0x80)	/* wait for bit 7 to deassert */
+		continue;
+	outb(0x18, 0x022c);		/* set DMA channel 1 */
+    
+	return 0;
+}
+#endif /* CONFIG_ALPHA_MIATA */
 
 #endif /* CONFIG_PCI */
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/kernel/cia.c linux/arch/alpha/kernel/cia.c
--- v2.1.78/linux/arch/alpha/kernel/cia.c	Mon Oct  7 02:40:22 1996
+++ linux/arch/alpha/kernel/cia.c	Mon Jan 12 14:51:14 1998
@@ -6,7 +6,6 @@
  *
  */
 #include <linux/kernel.h>
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/bios32.h>
 #include <linux/pci.h>
@@ -38,12 +37,19 @@
  * BIOS32-style PCI interface:
  */
 
-#ifdef CONFIG_ALPHA_CIA
+/* #define DEBUG_MCHECK */
+/* #define DEBUG_CONFIG */
+/* #define DEBUG_DUMP_REGS */
 
-#ifdef DEBUG 
-# define DBG(args)	printk args
+#ifdef DEBUG_MCHECK
+# define DBGM(args)	printk args
 #else
-# define DBG(args)
+# define DBGM(args)
+#endif
+#ifdef DEBUG_CONFIG
+# define DBGC(args)	printk args
+#else
+# define DBGC(args)
 #endif
 
 #define vulp	volatile unsigned long *
@@ -51,7 +57,7 @@
 
 static volatile unsigned int CIA_mcheck_expected = 0;
 static volatile unsigned int CIA_mcheck_taken = 0;
-static unsigned long CIA_jd;
+static unsigned int CIA_jd;
 
 
 /*
@@ -101,8 +107,9 @@
 {
 	unsigned long addr;
 
-	DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p, type1=0x%p)\n",
-	     bus, device_fn, where, pci_addr, type1));
+	DBGC(("mk_conf_addr(bus=%d, device_fn=0x%x, where=0x%x, "
+	      "pci_addr=0x%p, type1=0x%p)\n",
+	      bus, device_fn, where, pci_addr, type1));
 
 	if (bus == 0) {
 		int device = device_fn >> 3;
@@ -110,7 +117,8 @@
 		/* type 0 configuration cycle: */
 
 		if (device > 20) {
-			DBG(("mk_conf_addr: device (%d) > 20, returning -1\n", device));
+			DBGC(("mk_conf_addr: device (%d) > 20, returning -1\n",
+			      device));
 			return -1;
 		}
 
@@ -122,7 +130,7 @@
 		addr = (bus << 16) | (device_fn << 8) | (where);
 	}
 	*pci_addr = addr;
-	DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
+	DBGC(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
 	return 0;
 }
 
@@ -133,22 +141,25 @@
 	unsigned int stat0, value;
 	unsigned int cia_cfg = 0; /* to keep gcc quiet */
 
+	value = 0xffffffffU;
+	mb();
+
 	save_flags(flags);	/* avoid getting hit by machine check */
 	cli();
 
-	DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1));
+	DBGC(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1));
 
 	/* reset status register to avoid losing errors: */
-	stat0 = *((volatile unsigned int *)CIA_IOC_CIA_ERR);
-	*((volatile unsigned int *)CIA_IOC_CIA_ERR) = stat0;
+	stat0 = *(vuip)CIA_IOC_CIA_ERR;
+	*(vuip)CIA_IOC_CIA_ERR = stat0;
 	mb();
-	DBG(("conf_read: CIA ERR was 0x%x\n", stat0));
+	DBGC(("conf_read: CIA ERR was 0x%x\n", stat0));
 	/* if Type1 access, must set CIA CFG */
 	if (type1) {
-		cia_cfg = *((unsigned int *)CIA_IOC_CFG);
+		cia_cfg = *(vuip)CIA_IOC_CFG;
+		*(vuip)CIA_IOC_CFG = cia_cfg | 1;
 		mb();
-		*((unsigned int *)CIA_IOC_CFG) = cia_cfg | 1;
-		DBG(("conf_read: TYPE1 access\n"));
+		DBGC(("conf_read: TYPE1 access\n"));
 	}
 
 	mb();
@@ -157,8 +168,7 @@
 	CIA_mcheck_taken = 0;
 	mb();
 	/* access configuration space: */
-	value = *((volatile unsigned int *)addr);
-	mb();
+	value = *(vuip)addr;
 	mb();
 	if (CIA_mcheck_taken) {
 		CIA_mcheck_taken = 0;
@@ -167,26 +177,25 @@
 	}
 	CIA_mcheck_expected = 0;
 	mb();
-	/*
-	 * david.rusling@reo.mts.dec.com.  This code is needed for the
-	 * EB64+ as it does not generate a machine check (why I don't
-	 * know).  When we build kernels for one particular platform
-	 * then we can make this conditional on the type.
-	 */
+
 #if 0
+	/*
+	  this code might be necessary if machine checks aren't taken,
+	  but I can't get it to work on CIA-2, so its disabled.
+	  */
 	draina();
 
 	/* now look for any errors */
-	stat0 = *((unsigned int *)CIA_IOC_CIA_ERR);
-	DBG(("conf_read: CIA ERR after read 0x%x\n", stat0));
-	if (stat0 & 0x8280U) { /* is any error bit set? */
-		/* if not NDEV, print status */
+	stat0 = *(vuip)CIA_IOC_CIA_ERR;
+	DBGC(("conf_read: CIA ERR after read 0x%x\n", stat0));
+	if (stat0 & 0x8FEF0FFFU) { /* is any error bit set? */
+		/* if not MAS_ABT, print status */
 		if (!(stat0 & 0x0080)) {
 			printk("CIA.c:conf_read: got stat0=%x\n", stat0);
 		}
 
 		/* reset error status: */
-		*((volatile unsigned long *)CIA_IOC_CIA_ERR) = stat0;
+		*(vuip)CIA_IOC_CIA_ERR = stat0;
 		mb();
 		wrmces(0x7);			/* reset machine check */
 		value = 0xffffffff;
@@ -195,18 +204,19 @@
 
 	/* if Type1 access, must reset IOC CFG so normal IO space ops work */
 	if (type1) {
-		*((unsigned int *)CIA_IOC_CFG) = cia_cfg & ~1;
+		*(vuip)CIA_IOC_CFG = cia_cfg & ~1;
 		mb();
 	}
 
-	DBG(("conf_read(): finished\n"));
+	DBGC(("conf_read(): finished\n"));
 
 	restore_flags(flags);
 	return value;
 }
 
 
-static void conf_write(unsigned long addr, unsigned int value, unsigned char type1)
+static void conf_write(unsigned long addr, unsigned int value,
+		       unsigned char type1)
 {
 	unsigned long flags;
 	unsigned int stat0;
@@ -216,47 +226,46 @@
 	cli();
 
 	/* reset status register to avoid losing errors: */
-	stat0 = *((volatile unsigned int *)CIA_IOC_CIA_ERR);
-	*((volatile unsigned int *)CIA_IOC_CIA_ERR) = stat0;
+	stat0 = *(vuip)CIA_IOC_CIA_ERR;
+	*(vuip)CIA_IOC_CIA_ERR = stat0;
 	mb();
-	DBG(("conf_write: CIA ERR was 0x%x\n", stat0));
+	DBGC(("conf_write: CIA ERR was 0x%x\n", stat0));
 	/* if Type1 access, must set CIA CFG */
 	if (type1) {
-		cia_cfg = *((unsigned int *)CIA_IOC_CFG);
+		cia_cfg = *(vuip)CIA_IOC_CFG;
+		*(vuip)CIA_IOC_CFG = cia_cfg | 1;
 		mb();
-		*((unsigned int *)CIA_IOC_CFG) = cia_cfg | 1;
-		DBG(("conf_read: TYPE1 access\n"));
+		DBGC(("conf_write: TYPE1 access\n"));
 	}
 
 	draina();
 	CIA_mcheck_expected = 1;
 	mb();
 	/* access configuration space: */
-	*((volatile unsigned int *)addr) = value;
-	mb();
+	*(vuip)addr = value;
 	mb();
+
 	CIA_mcheck_expected = 0;
 	mb();
+
+#if 0
 	/*
-	 * david.rusling@reo.mts.dec.com.  This code is needed for the
-	 * EB64+ as it does not generate a machine check (why I don't
-	 * know).  When we build kernels for one particular platform
-	 * then we can make this conditional on the type.
+	 * This code might be necessary if machine checks aren't taken,
+	 * but I can't get it to work on CIA-2, so its disabled.
 	 */
-#if 0
 	draina();
 
-	/* now look for any errors */
-	stat0 = *((unsigned int *)CIA_IOC_CIA_ERR);
-	DBG(("conf_write: CIA ERR after write 0x%x\n", stat0));
-	if (stat0 & 0x8280U) { /* is any error bit set? */
-		/* if not NDEV, print status */
+	/* Now look for any errors */
+	stat0 = *(vuip)CIA_IOC_CIA_ERR;
+	DBGC(("conf_write: CIA ERR after write 0x%x\n", stat0));
+	if (stat0 & 0x8FEF0FFFU) { /* is any error bit set? */
+		/* If not MAS_ABT, print status */
 		if (!(stat0 & 0x0080)) {
 			printk("CIA.c:conf_read: got stat0=%x\n", stat0);
 		}
 
 		/* reset error status: */
-		*((volatile unsigned long *)CIA_IOC_CIA_ERR) = stat0;
+		*(vulp)CIA_IOC_CIA_ERR = stat0;
 		mb();
 		wrmces(0x7);			/* reset machine check */
 		value = 0xffffffff;
@@ -265,11 +274,11 @@
 
 	/* if Type1 access, must reset IOC CFG so normal IO space ops work */
 	if (type1) {
-		*((unsigned int *)CIA_IOC_CFG) = cia_cfg & ~1;
+		*(vuip)CIA_IOC_CFG = cia_cfg & ~1;
 		mb();
 	}
 
-	DBG(("conf_write(): finished\n"));
+	DBGC(("conf_write(): finished\n"));
 	restore_flags(flags);
 }
 
@@ -390,15 +399,58 @@
 
 unsigned long cia_init(unsigned long mem_start, unsigned long mem_end)
 {
-        unsigned int cia_err ;
+        unsigned int cia_tmp;
+
+#ifdef DEBUG_DUMP_REGS
+	{
+		unsigned int temp;
+		temp = *(vuip)CIA_IOC_CIA_REV; mb();
+		printk("CIA_init: CIA_REV was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_PCI_LAT; mb();
+		printk("CIA_init: CIA_PCI_LAT was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_CIA_CTRL; mb();
+		printk("CIA_init: CIA_CTRL was 0x%x\n", temp);
+		temp = *(vuip)0xfffffc8740000140UL; mb();
+		printk("CIA_init: CIA_CTRL1 was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_HAE_MEM; mb();
+		printk("CIA_init: CIA_HAE_MEM was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_HAE_IO; mb();
+		printk("CIA_init: CIA_HAE_IO was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_CFG; mb();
+		printk("CIA_init: CIA_CFG was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_CACK_EN; mb();
+		printk("CIA_init: CIA_CACK_EN was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_CFG; mb();
+		printk("CIA_init: CIA_CFG was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_CIA_DIAG; mb();
+		printk("CIA_init: CIA_DIAG was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_DIAG_CHECK; mb();
+		printk("CIA_init: CIA_DIAG_CHECK was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_PERF_MONITOR; mb();
+		printk("CIA_init: CIA_PERF_MONITOR was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_PERF_CONTROL; mb();
+		printk("CIA_init: CIA_PERF_CONTROL was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_CIA_ERR; mb();
+		printk("CIA_init: CIA_ERR was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_CIA_STAT; mb();
+		printk("CIA_init: CIA_STAT was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_MCR; mb();
+		printk("CIA_init: CIA_MCR was 0x%x\n", temp);
+	}
+#endif /* DEBUG_DUMP_REGS */
 
         /* 
 	 * Set up error reporting.
 	 */
-	cia_err = *(vuip)CIA_IOC_CIA_ERR ;
-	cia_err |= (0x1 << 7) ;   /* master abort */
-	*(vuip)CIA_IOC_CIA_ERR = cia_err ;
-	mb() ;
+	cia_tmp = *(vuip)CIA_IOC_CIA_ERR;
+	cia_tmp |= 0x180;   /* master, target abort */
+	*(vuip)CIA_IOC_CIA_ERR = cia_tmp;
+	mb();
+
+	cia_tmp = *(vuip)CIA_IOC_CIA_CTRL;
+	cia_tmp |= 0x400;   /* turn on FILL_ERR to get mchecks */
+	*(vuip)CIA_IOC_CIA_CTRL = cia_tmp;
+	mb();
 
 	/*
 	 * Set up the PCI->physical memory translation windows.
@@ -411,16 +463,16 @@
  	*(vuip)CIA_IOC_PCI_W0_MASK = (CIA_DMA_WIN_SIZE - 1) & 0xfff00000U;
 	*(vuip)CIA_IOC_PCI_T0_BASE = 0;
 
-	*(vuip)CIA_IOC_PCI_W1_BASE = 0x0 ;
-	*(vuip)CIA_IOC_PCI_W2_BASE = 0x0 ;
-	*(vuip)CIA_IOC_PCI_W3_BASE = 0x0 ;
+	*(vuip)CIA_IOC_PCI_W1_BASE = 0x0;
+	*(vuip)CIA_IOC_PCI_W2_BASE = 0x0;
+	*(vuip)CIA_IOC_PCI_W3_BASE = 0x0;
 
 	/*
 	 * check ASN in HWRPB for validity, report if bad
 	 */
 	if (hwrpb->max_asn != MAX_ASN) {
 		printk("CIA_init: max ASN from HWRPB is bad (0x%lx)\n",
-			hwrpb->max_asn);
+		       hwrpb->max_asn);
 		hwrpb->max_asn = MAX_ASN;
 	}
 
@@ -432,20 +484,29 @@
          */
         {
 #if 0
-          unsigned int cia_cfg = *((unsigned int *)CIA_IOC_CFG); mb();
-          if (cia_cfg) printk("CIA_init: CFG was 0x%x\n", cia_cfg);
+		unsigned int cia_cfg = *(vuip)CIA_IOC_CFG; mb();
+		if (cia_cfg) printk("CIA_init: CFG was 0x%x\n", cia_cfg);
 #endif
-          *((unsigned int *)CIA_IOC_CFG) = 0; mb();
+		*(vuip)CIA_IOC_CFG = 0; mb();
         }
  
+#if 0
+	{
+		unsigned int temp;
+		temp = *(vuip)CIA_IOC_CIA_CTRL; mb();
+		printk("CIA_init: CIA_CTRL was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_ERR_MASK; mb();
+		printk("CIA_init: CIA_ERR_MASK was 0x%x\n", temp);
+	}
+#endif
 	return mem_start;
 }
 
 int cia_pci_clr_err(void)
 {
-	CIA_jd = *((unsigned int *)CIA_IOC_CIA_ERR);
-	DBG(("CIA_pci_clr_err: CIA ERR after read 0x%x\n", CIA_jd));
-	*((unsigned long *)CIA_IOC_CIA_ERR) = 0x0080;
+	CIA_jd = *(vuip)CIA_IOC_CIA_ERR;
+	DBGM(("CIA_pci_clr_err: CIA ERR after read 0x%x\n", CIA_jd));
+	*(vulp)CIA_IOC_CIA_ERR = 0x0180;
 	mb();
 	return 0;
 }
@@ -462,17 +523,21 @@
 	long i;
 
 	mchk_header = (struct el_common *)la_ptr;
-	mchk_procdata =
-		(struct el_procdata *)(la_ptr + mchk_header->proc_offset);
-	mchk_sysdata = 
-	  (struct el_CIA_sysdata_mcheck *)(la_ptr + mchk_header->sys_offset);
-
-	DBG(("cia_machine_check: vector=0x%lx la_ptr=0x%lx\n", vector, la_ptr));
-	DBG(("                     pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
-	     regs->pc, mchk_header->size, mchk_header->proc_offset, mchk_header->sys_offset));
-	DBG(("cia_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n",
-	     CIA_mcheck_expected, mchk_sysdata->epic_dcsr, mchk_sysdata->epic_pear));
-#ifdef DEBUG
+	mchk_procdata = (struct el_procdata *)
+		(la_ptr + mchk_header->proc_offset);
+	mchk_sysdata = (struct el_CIA_sysdata_mcheck *)
+		(la_ptr + mchk_header->sys_offset);
+
+	DBGM(("cia_machine_check: vector=0x%lx la_ptr=0x%lx\n",
+	      vector, la_ptr));
+	DBGM(("                     pc=0x%lx size=0x%x procoffset=0x%x "
+	      "sysoffset 0x%x\n", regs->pc, mchk_header->size,
+	      mchk_header->proc_offset, mchk_header->sys_offset));
+	DBGM(("cia_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n",
+	      CIA_mcheck_expected, mchk_sysdata->epic_dcsr,
+	      mchk_sysdata->epic_pear));
+
+#ifdef DEBUG_MCHECK
 	{
 		unsigned long *ptr;
 		int i;
@@ -483,15 +548,15 @@
 			       ptr[i], ptr[i+1]);
 		}
 	}
-#endif /* DEBUG */
+#endif
+
 	/*
 	 * Check if machine check is due to a badaddr() and if so,
 	 * ignore the machine check.
 	 */
 	mb();
-	mb();
-	if (CIA_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) {
-		DBG(("CIA machine check expected\n"));
+	if (CIA_mcheck_expected) {
+		DBGM(("CIA machine check expected\n"));
 		CIA_mcheck_expected = 0;
 		CIA_mcheck_taken = 1;
 		mb();
@@ -504,34 +569,34 @@
 	}
 
 	switch ((unsigned int) mchk_header->code) {
-	      case MCHK_K_TPERR:	reason = "tag parity error"; break;
-	      case MCHK_K_TCPERR:	reason = "tag control parity error"; break;
-	      case MCHK_K_HERR:		reason = "generic hard error"; break;
-	      case MCHK_K_ECC_C:	reason = "correctable ECC error"; break;
-	      case MCHK_K_ECC_NC:	reason = "uncorrectable ECC error"; break;
-	      case MCHK_K_OS_BUGCHECK:	reason = "OS-specific PAL bugcheck"; break;
-	      case MCHK_K_PAL_BUGCHECK:	reason = "callsys in kernel mode"; break;
-	      case 0x96: reason = "i-cache read retryable error"; break;
-	      case 0x98: reason = "processor detected hard error"; break;
+	case MCHK_K_TPERR:	reason = "tag parity error"; break;
+	case MCHK_K_TCPERR:	reason = "tag control parity error"; break;
+	case MCHK_K_HERR:		reason = "generic hard error"; break;
+	case MCHK_K_ECC_C:	reason = "correctable ECC error"; break;
+	case MCHK_K_ECC_NC:	reason = "uncorrectable ECC error"; break;
+	case MCHK_K_OS_BUGCHECK:	reason = "OS-specific PAL bugcheck"; break;
+	case MCHK_K_PAL_BUGCHECK:	reason = "callsys in kernel mode"; break;
+	case 0x96: reason = "i-cache read retryable error"; break;
+	case 0x98: reason = "processor detected hard error"; break;
 
 		/* system specific (these are for Alcor, at least): */
-	      case 0x203: reason = "system detected uncorrectable ECC error"; break;
-	      case 0x205: reason = "parity error detected by CIA"; break;
-	      case 0x207: reason = "non-existent memory error"; break;
-	      case 0x209: reason = "PCI SERR detected"; break;
-	      case 0x20b: reason = "PCI data parity error detected"; break;
-	      case 0x20d: reason = "PCI address parity error detected"; break;
-	      case 0x20f: reason = "PCI master abort error"; break;
-	      case 0x211: reason = "PCI target abort error"; break;
-	      case 0x213: reason = "scatter/gather PTE invalid error"; break;
-	      case 0x215: reason = "flash ROM write error"; break;
-	      case 0x217: reason = "IOA timeout detected"; break;
-	      case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break;
-	      case 0x21b: reason = "EISA fail-safe timer timeout"; break;
-	      case 0x21d: reason = "EISA bus time-out"; break;
-	      case 0x21f: reason = "EISA software generated NMI"; break;
-	      case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break;
-	      default:
+	case 0x203: reason = "system detected uncorrectable ECC error"; break;
+	case 0x205: reason = "parity error detected by CIA"; break;
+	case 0x207: reason = "non-existent memory error"; break;
+	case 0x209: reason = "PCI SERR detected"; break;
+	case 0x20b: reason = "PCI data parity error detected"; break;
+	case 0x20d: reason = "PCI address parity error detected"; break;
+	case 0x20f: reason = "PCI master abort error"; break;
+	case 0x211: reason = "PCI target abort error"; break;
+	case 0x213: reason = "scatter/gather PTE invalid error"; break;
+	case 0x215: reason = "flash ROM write error"; break;
+	case 0x217: reason = "IOA timeout detected"; break;
+	case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break;
+	case 0x21b: reason = "EISA fail-safe timer timeout"; break;
+	case 0x21d: reason = "EISA bus time-out"; break;
+	case 0x21f: reason = "EISA software generated NMI"; break;
+	case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break;
+	default:
 		sprintf(buf, "reason for machine-check unknown (0x%x)",
 			(unsigned int) mchk_header->code);
 		reason = buf;
@@ -542,14 +607,14 @@
 
 	printk(KERN_CRIT "  CIA machine check: %s%s\n",
 	       reason, mchk_header->retry ? " (retryable)" : "");
+	printk(KERN_CRIT "   vector=0x%lx la_ptr=0x%lx pc=0x%lx\n",
+	       vector, la_ptr, regs->pc);
 
 	/* dump the the logout area to give all info: */
 
 	ptr = (unsigned long *)la_ptr;
 	for (i = 0; i < mchk_header->size / sizeof(long); i += 2) {
-	    printk(KERN_CRIT " +%8lx %016lx %016lx\n",
-		   i*sizeof(long), ptr[i], ptr[i+1]);
+		printk(KERN_CRIT " +%8lx %016lx %016lx\n",
+		       i*sizeof(long), ptr[i], ptr[i+1]);
 	}
 }
-
-#endif /* CONFIG_ALPHA_CIA */
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S
--- v2.1.78/linux/arch/alpha/kernel/entry.S	Tue Dec 23 16:30:59 1997
+++ linux/arch/alpha/kernel/entry.S	Sat Jan 10 11:58:12 1998
@@ -29,11 +29,10 @@
 /*
  * task structure offsets
  */
-#define TASK_STATE	0
-#define TASK_COUNTER	8
-#define TASK_PRIORITY	16
-#define TASK_FLAGS	24
-#define TASK_SIGPENDING	32	
+#define TASK_STATE	 0
+#define TASK_FLAGS	 8
+#define TASK_SIGPENDING	16
+#define TASK_SIZE	24	
 
 /*
  * task flags (must match include/linux/sched.h):
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/kernel/head.S linux/arch/alpha/kernel/head.S
--- v2.1.78/linux/arch/alpha/kernel/head.S	Tue May 13 22:41:00 1997
+++ linux/arch/alpha/kernel/head.S	Mon Jan 12 14:51:14 1998
@@ -80,6 +80,48 @@
 	ret	($26)
 	.end wrmces
 
+	.align 3
+	.globl  whami
+	.ent    whami
+whami:
+	call_pal PAL_whami
+	ret     ($26)
+	.end whami
+ 
+	.align 3
+	.globl  wripir
+	.ent    wripir
+wripir:
+	call_pal PAL_wripir
+	ret     ($26)
+	.end wripir
+
+	#
+	# The following two functions are needed for supporting SRM PALcode
+	# on the PC164 (at least), since that PALcode manages the interrupt
+	# masking, and we cannot duplicate the effort without causing problems
+	#
+
+	.align 3
+	.globl	cserve_ena
+	.ent	cserve_ena
+cserve_ena:
+	bis	$16,$16,$17
+	lda	$16,52($31)
+	call_pal PAL_cserve
+	ret	($26)
+	.end	cserve_ena
+
+	.align 3
+	.globl	cserve_dis
+	.ent	cserve_dis
+cserve_dis:
+	bis	$16,$16,$17
+	lda	$16,53($31)
+	call_pal PAL_cserve
+	ret	($26)
+	.end	cserve_dis
+
 	#
 	# The following two functions don't need trapb/excb instructions
 	# around the mf_fpcr/mt_fpcr instructions because (a) the kernel
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c
--- v2.1.78/linux/arch/alpha/kernel/irq.c	Mon Oct 20 10:36:52 1997
+++ linux/arch/alpha/kernel/irq.c	Mon Jan 12 14:51:14 1998
@@ -26,6 +26,9 @@
 #include <asm/bitops.h>
 #include <asm/dma.h>
 
+#define vulp	volatile unsigned long *
+#define vuip	volatile unsigned int *
+
 #define RTC_IRQ    8
 #ifdef CONFIG_RTC
 #define TIMER_IRQ  0        /* timer is the pit */
@@ -58,8 +61,9 @@
  *  The bits are used as follows:
  *	 0.. 7	first (E)ISA PIC (irq level 0..7)
  *	 8..15	second (E)ISA PIC (irq level 8..15)
- *   Systems with PCI interrupt lines managed by GRU (e.g., Alcor, XLT):
- *	16..47	PCI interrupts 0..31 (int at GRU_INT_MASK)
+ *   Systems with PCI interrupt lines managed by GRU (e.g., Alcor, XLT)
+ *    or PYXIS (e.g. Miata, PC164-LX):
+ *	16..47	PCI interrupts 0..31 (int at xxx_INT_MASK)
  *   Mikasa:
  *	16..31	PCI interrupts 0..15 (short at I/O port 536)
  *   Other systems (not Mikasa) with 16 PCI interrupt lines:
@@ -67,53 +71,259 @@
  *	24..31	PCI interrupts 8..15 (char at I/O port 27)
  *   Systems with 17 PCI interrupt lines (e.g., Cabriolet and eb164):
  *	16..32	PCI interrupts 0..31 (int at I/O port 804)
+ *   For SABLE, which is really baroque, we manage 40 IRQ's, but the
+ *   hardware really only supports 24, not via normal ISA PIC,
+ *   but cascaded custom 8259's, etc.
+ *	 0-7  (char at 536)
+ *	 8-15 (char at 53a)
+ *	16-23 (char at 53c)
  */
 static unsigned long irq_mask = ~0UL;
 
+#ifdef CONFIG_ALPHA_SABLE
+/*
+ * Note that the vector reported by the SRM PALcode corresponds to the
+ * interrupt mask bits, but we have to manage via more normal IRQs.
+ *
+ * We have to be able to go back and forth between MASK bits and IRQ:
+ * these tables help us do so.
+ */
+static char sable_irq_to_mask[NR_IRQS] = {
+	-1,  6, -1,  8, 15, 12,  7,  9, /* pseudo PIC  0-7  */
+	-1, 16, 17, 18,  3, -1, 21, 22, /* pseudo PIC  8-15 */
+	-1, -1, -1, -1, -1, -1, -1, -1, /* pseudo EISA 0-7  */
+	-1, -1, -1, -1, -1, -1, -1, -1, /* pseudo EISA 8-15 */
+	2,  1,  0,  4,  5, -1, -1, -1, /* pseudo PCI */
+};
+#define IRQ_TO_MASK(irq) (sable_irq_to_mask[(irq)])
+static char sable_mask_to_irq[NR_IRQS] = {
+	34, 33, 32, 12, 35, 36,  1,  6, /* mask 0-7  */
+	3,  7, -1, -1,  5, -1, -1,  4, /* mask 8-15  */
+	9, 10, 11, -1, -1, 14, 15, -1, /* mask 16-23  */
+};
+#else /* CONFIG_ALPHA_SABLE */
+#define IRQ_TO_MASK(irq) (irq)
+#endif /* CONFIG_ALPHA_SABLE */
+
 /*
  * Update the hardware with the irq mask passed in MASK.  The function
  * exploits the fact that it is known that only bit IRQ has changed.
  */
-static void update_hw(unsigned long irq, unsigned long mask)
+
+static inline void 
+sable_update_hw(unsigned long irq, unsigned long mask)
 {
+	/* The "irq" argument is really the mask bit number */
 	switch (irq) {
-#if NR_IRQS == 48
-	      default:
-		/* note inverted sense of mask bits: */
-		*(unsigned int *)GRU_INT_MASK = ~(mask >> 16); mb();
+	default: /* 16 ... 23 */
+		outb(mask >> 16, 0x53d);
+		break;
+	case 8 ... 15:
+		outb(mask >> 8, 0x53b);
+		break;
+	case 0 ... 7:
+		outb(mask, 0x537);
 		break;
+	}
+}
 
-#elif NR_IRQS == 33
-	      default:
-		outl(mask >> 16, 0x804);
+static inline void 
+noritake_update_hw(unsigned long irq, unsigned long mask)
+{
+	switch (irq) {
+	default: /* 32 ... 47 */
+		outw(~(mask >> 32), 0x54c);
+		break;
+	case 16 ... 31:
+		outw(~(mask >> 16), 0x54a);
+		break;
+	case  8 ... 15:	/* ISA PIC2 */
+		outb(mask >> 8, 0xA1);
+		break;
+	case  0 ... 7:	/* ISA PIC1 */
+		outb(mask, 0x21);
 		break;
+	}
+}
 
-#elif defined(CONFIG_ALPHA_MIKASA)
-	      default:
+#ifdef CONFIG_ALPHA_MIATA
+static inline void 
+miata_update_hw(unsigned long irq, unsigned long mask)
+{
+	switch (irq) {
+	default: /* 16 ... 47 */
+		/* Make CERTAIN none of the bogus ints get enabled... */
+		*(vulp)PYXIS_INT_MASK =
+			~((long)mask >> 16) & ~0x4000000000000e3bUL;
+		mb();
+		/* ... and read it back to make sure it got written.  */
+		*(vulp)PYXIS_INT_MASK;
+		break;
+	case  8 ... 15:	/* ISA PIC2 */
+		outb(mask >> 8, 0xA1);
+		break;
+	case  0 ... 7:	/* ISA PIC1 */
+		outb(mask, 0x21);
+		break;
+	}
+}
+#endif
+
+#if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
+static inline void 
+alcor_and_xlt_update_hw(unsigned long irq, unsigned long mask)
+{
+	switch (irq) {
+	default: /* 16 ... 47 */
+		/* On Alcor, at least, lines 20..30 are not connected and can
+		   generate spurrious interrupts if we turn them on while IRQ
+		   probing.  So explicitly mask them out. */
+		mask |= 0x7ff000000000UL;
+
+		/* Note inverted sense of mask bits: */
+		*(vuip)GRU_INT_MASK = ~(mask >> 16);
+		mb();
+		break;
+	case  8 ... 15:	/* ISA PIC2 */
+		outb(mask >> 8, 0xA1);
+		break;
+	case  0 ... 7:	/* ISA PIC1 */
+		outb(mask, 0x21);
+		break;
+	}
+}
+#endif
+
+static inline void
+mikasa_update_hw(unsigned long irq, unsigned long mask)
+{
+	switch (irq) {
+	default: /* 16 ... 31 */
 		outw(~(mask >> 16), 0x536); /* note invert */
 		break;
+	case  8 ... 15:	/* ISA PIC2 */
+		outb(mask >> 8, 0xA1);
+		break;
+	case  0 ... 7:	/* ISA PIC1 */
+		outb(mask, 0x21);
+		break;
+	}
+}
 
-#elif NR_IRQS == 32
-	      case 16 ... 23:
-		outb(mask >> 16, 0x26);
+/* Unlabeled mechanisms based on the number of irqs.  Someone should
+   probably document and name these.  */
+
+static inline void
+update_hw_33(unsigned long irq, unsigned long mask)
+{
+	switch (irq) {
+	default: /* 16 ... 32 */
+		outl(mask >> 16, 0x804);
 		break;
 
-	      default:
-		outb(mask >> 24, 0x27);
+	case  8 ... 15:	/* ISA PIC2 */
+		outb(mask >> 8, 0xA1);
 		break;
-#endif
-		/* handle ISA irqs last---fast devices belong on PCI... */
+	case  0 ... 7:	/* ISA PIC1 */
+		outb(mask, 0x21);
+		break;
+	}
+}
 
-	      case  0 ... 7:	/* ISA PIC1 */
+static inline void
+update_hw_32(unsigned long irq, unsigned long mask)
+{
+	switch (irq) {
+	default: /* 24 ... 31 */
+		outb(mask >> 24, 0x27);
+		break;
+	case 16 ... 23:
+		outb(mask >> 16, 0x26);
+		break;
+	case  8 ... 15:	/* ISA PIC2 */
+		outb(mask >> 8, 0xA1);
+		break;
+	case  0 ... 7:	/* ISA PIC1 */
 		outb(mask, 0x21);
 		break;
+	}
+}
 
-	      case  8 ...15:	/* ISA PIC2 */
+static inline void
+update_hw_16(unsigned long irq, unsigned long mask)
+{
+	switch (irq) {
+	default: /* 8 ... 15, ISA PIC2 */
 		outb(mask >> 8, 0xA1);
 		break;
+	case  0 ... 7: /* ISA PIC1 */
+		outb(mask, 0x21);
+		break;
 	}
 }
 
+#if defined(CONFIG_ALPHA_PC164) && defined(CONFIG_ALPHA_SRM)
+/*
+ * On the pc164, we cannot take over the IRQs from the SRM, 
+ * so we call down to do our dirty work.  Too bad the SRM
+ * isn't consistent across platforms otherwise we could do
+ * this always.
+ */
+
+extern void cserve_ena(unsigned long);
+extern void cserve_dis(unsigned long);
+
+static inline void mask_irq(unsigned long irq)
+{
+	irq_mask |= (1UL << irq);
+	cserve_dis(irq - 16);
+}
+
+static inline void unmask_irq(unsigned long irq)
+{
+	irq_mask &= ~(1UL << irq);
+	cserve_ena(irq - 16);
+}
+
+/* Since we are calling down to PALcode, no need to diddle IPL.  */
+void disable_irq(unsigned int irq_nr)
+{
+	mask_irq(IRQ_TO_MASK(irq_nr));
+}
+
+void enable_irq(unsigned int irq_nr)
+{
+	unmask_irq(IRQ_TO_MASK(irq_nr));
+}
+
+#else
+/*
+ * We manipulate the hardware ourselves.
+ */
+
+static void update_hw(unsigned long irq, unsigned long mask)
+{
+#if defined(CONFIG_ALPHA_SABLE)
+	sable_update_hw(irq, mask);
+#elif defined(CONFIG_ALPHA_MIATA)
+	miata_update_hw(irq, mask);
+#elif defined(CONFIG_ALPHA_NORITAKE)
+	noritake_update_hw(irq, mask);
+#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
+	alcor_and_xlt_update_hw(irq, mask);
+#elif defined(CONFIG_ALPHA_MIKASA)
+	mikasa_update_hw(irq, mask);
+#elif NR_IRQS == 33
+	update_hw_33(irq, mask);
+#elif NR_IRQS == 32
+	update_hw_32(irq, mask);
+#elif NR_IRQS == 16
+	update_hw_16(irq, mask);
+#else
+#error "How do I update the IRQ hardware?"
+#endif
+}
+
 static inline void mask_irq(unsigned long irq)
 {
 	irq_mask |= (1UL << irq);
@@ -132,7 +342,7 @@
 
 	save_flags(flags);
 	cli();
-	mask_irq(irq_nr);
+	mask_irq(IRQ_TO_MASK(irq_nr));
 	restore_flags(flags);
 }
 
@@ -142,9 +352,10 @@
 
 	save_flags(flags);
 	cli();
-	unmask_irq(irq_nr);
+	unmask_irq(IRQ_TO_MASK(irq_nr));
 	restore_flags(flags);
 }
+#endif /* PC164 && SRM */
 
 /*
  * Initial irq handlers.
@@ -157,18 +368,18 @@
 	int i, len = 0;
 	struct irqaction * action;
 
-	for (i = 0 ; i < NR_IRQS ; i++) {
+	for (i = 0; i < NR_IRQS; i++) {
 		action = irq_action[i];
 		if (!action) 
 			continue;
 		len += sprintf(buf+len, "%2d: %10u %c %s",
-			i, kstat.interrupts[i],
-			(action->flags & SA_INTERRUPT) ? '+' : ' ',
-			action->name);
+			       i, kstat.interrupts[i],
+			       (action->flags & SA_INTERRUPT) ? '+' : ' ',
+			       action->name);
 		for (action=action->next; action; action = action->next) {
 			len += sprintf(buf+len, ",%s %s",
-				(action->flags & SA_INTERRUPT) ? " +" : "",
-				action->name);
+				       (action->flags & SA_INTERRUPT) ? " +" : "",
+				       action->name);
 		}
 		len += sprintf(buf+len, "\n");
 	}
@@ -177,6 +388,23 @@
 
 static inline void ack_irq(int irq)
 {
+#ifdef CONFIG_ALPHA_SABLE
+	/* Note that the "irq" here is really the mask bit number */
+	switch (irq) {
+	case 0 ... 7:
+		outb(0xE0 | (irq - 0), 0x536);
+		outb(0xE0 | 1, 0x534); /* slave 0 */
+		break;
+	case 8 ... 15:
+		outb(0xE0 | (irq - 8), 0x53a);
+		outb(0xE0 | 3, 0x534); /* slave 1 */
+		break;
+	case 16 ... 24:
+		outb(0xE0 | (irq - 16), 0x53c);
+		outb(0xE0 | 4, 0x534); /* slave 2 */
+		break;
+	}
+#else /* CONFIG_ALPHA_SABLE */
 	if (irq < 16) {
 		/* ACK the interrupt making it the lowest priority */
 		/*  First the slave .. */
@@ -188,10 +416,11 @@
 		outb(0xE0 | irq, 0x20);
 #if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
 		/* on ALCOR/XLT, need to dismiss interrupt via GRU */
-		*(int *)GRU_INT_CLEAR = 0x80000000; mb();
-		*(int *)GRU_INT_CLEAR = 0x00000000; mb();
+		*(vuip)GRU_INT_CLEAR = 0x80000000; mb();
+		*(vuip)GRU_INT_CLEAR = 0x00000000; mb();
 #endif /* ALCOR || XLT */
 	}
+#endif /* CONFIG_ALPHA_SABLE */
 }
 
 int request_irq(unsigned int irq, 
@@ -221,7 +450,7 @@
 		if ((action->flags ^ irqflags) & SA_INTERRUPT)
 			return -EBUSY;
 
-		/* add new interrupt at end of irq queue */
+		/* Add new interrupt at end of irq queue */
 		do {
 			p = &action->next;
 			action = *p;
@@ -229,11 +458,11 @@
 		shared = 1;
 	}
 
-        if (irq == TIMER_IRQ)
- 		action = &timer_irq;
-        else
+	if (irq == TIMER_IRQ)
+		action = &timer_irq;
+	else
 		action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
-					     GFP_KERNEL);
+						     GFP_KERNEL);
 	if (!action)
 		return -ENOMEM;
 
@@ -252,7 +481,7 @@
 	*p = action;
 
 	if (!shared)
-		unmask_irq(irq);
+		unmask_irq(IRQ_TO_MASK(irq));
 
 	restore_flags(flags);
 	return 0;
@@ -280,7 +509,7 @@
 		cli();
 		*p = action->next;
 		if (!irq[irq_action])
-			mask_irq(irq);
+			mask_irq(IRQ_TO_MASK(irq));
 		restore_flags(flags);
 		kfree(action);
 		return;
@@ -298,7 +527,7 @@
 atomic_t __alpha_bh_counter;
 
 #ifdef __SMP__
-#error Me no hablo Alpha SMP
+#error "Me no hablo Alpha SMP"
 #else
 #define irq_enter(cpu, irq)	(++local_irq_count[cpu])
 #define irq_exit(cpu, irq)	(--local_irq_count[cpu])
@@ -319,9 +548,12 @@
 				action = action->next;
 			}
 	printk("\n");
+
 #if defined(CONFIG_ALPHA_JENSEN)
+	/* ??? Is all this just debugging, or are the inb's and outb's
+	   necessary to make things work?  */
 	printk("64=%02x, 60=%02x, 3fa=%02x 2fa=%02x\n",
-		inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa));
+	       inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa));
 	outb(0x0c, 0x3fc);
 	outb(0x0c, 0x2fc);
 	outb(0,0x61);
@@ -353,7 +585,7 @@
 	int cpu = smp_processor_id();
 
 	if ((unsigned) irq > NR_IRQS) {
-		printk("device_interrupt: unexpected interrupt %d\n", irq);
+		printk("device_interrupt: illegal interrupt %d\n", irq);
 		return;
 	}
 
@@ -365,7 +597,7 @@
 	 * This way another (more timing-critical) interrupt can
 	 * come through while we're doing this one.
 	 *
-	 * Note! A irq without a handler gets masked and acked, but
+	 * Note! An irq without a handler gets masked and acked, but
 	 * never unmasked. The autoirq stuff depends on this (it looks
 	 * at the masks before and after doing the probing).
 	 */
@@ -397,6 +629,8 @@
 #	define IACK_SC	LCA_IACK_SC
 #elif defined(CONFIG_ALPHA_CIA)
 #	define IACK_SC	CIA_IACK_SC
+#elif defined(CONFIG_ALPHA_PYXIS)
+#	define IACK_SC	PYXIS_IACK_SC
 #else
 	/*
 	 * This is bogus but necessary to get it to compile
@@ -413,14 +647,13 @@
 	 * Generate a PCI interrupt acknowledge cycle.  The PIC will
 	 * respond with the interrupt vector of the highest priority
 	 * interrupt that is pending.  The PALcode sets up the
-	 * interrupts vectors such that irq level L generates vector
-	 * L.
+	 * interrupts vectors such that irq level L generates vector L.
 	 */
 	j = *(volatile int *) IACK_SC;
 	j &= 0xff;
 	if (j == 7) {
 		if (!(inb(0x20) & 0x80)) {
-			/* it's only a passive release... */
+			/* It's only a passive release... */
 			return;
 		}
 	}
@@ -454,43 +687,44 @@
 }
 
 #if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
-/* we have to conditionally compile this because of GRU_xxx symbols */
-static inline void alcor_and_xlt_device_interrupt(unsigned long vector,
-                                                  struct pt_regs * regs)
-{
-        unsigned long pld;
-        unsigned int i;
-        unsigned long flags;
+/* We have to conditionally compile this because of GRU_xxx symbols */
+static inline void
+alcor_and_xlt_device_interrupt(unsigned long vector, struct pt_regs *regs)
+{
+	unsigned long pld;
+	unsigned int i;
+	unsigned long flags;
 
-        save_flags(flags);
-        cli();
+	save_flags(flags);
+	cli();
 
-        /* read the interrupt summary register of the GRU */
-        pld = (*(unsigned int *)GRU_INT_REQ) & GRU_INT_REQ_BITS;
+	/* read the interrupt summary register of the GRU */
+	pld = (*(vuip)GRU_INT_REQ) & GRU_INT_REQ_BITS;
 
 #if 0
-        printk("[0x%08lx/0x%04x]", pld, inb(0x20) | (inb(0xA0) << 8));
+	printk("[0x%08lx/0x%04x]", pld, inb(0x20) | (inb(0xA0) << 8));
 #endif
 
-        /*
-         * Now for every possible bit set, work through them and call
-         * the appropriate interrupt handler.
-         */
-        while (pld) {
-                i = ffz(~pld);
-                pld &= pld - 1; /* clear least bit set */
-                if (i == 31) {
-                        isa_device_interrupt(vector, regs);
-                } else {
-                        device_interrupt(16 + i, 16 + i, regs);
-                }
-        }
-        restore_flags(flags);
+	/*
+	 * Now for every possible bit set, work through them and call
+	 * the appropriate interrupt handler.
+	 */
+	while (pld) {
+		i = ffz(~pld);
+		pld &= pld - 1; /* clear least bit set */
+		if (i == 31) {
+			isa_device_interrupt(vector, regs);
+		} else {
+			device_interrupt(16 + i, 16 + i, regs);
+		}
+	}
+	restore_flags(flags);
 }
 #endif /* ALCOR || XLT */
 
-static inline void cabriolet_and_eb66p_device_interrupt(unsigned long vector,
-							struct pt_regs * regs)
+static inline void 
+cabriolet_and_eb66p_device_interrupt(unsigned long vector,
+				     struct pt_regs *regs)
 {
 	unsigned long pld;
 	unsigned int i;
@@ -522,8 +756,8 @@
 	restore_flags(flags);
 }
 
-static inline void mikasa_device_interrupt(unsigned long vector,
-					   struct pt_regs * regs)
+static inline void 
+mikasa_device_interrupt(unsigned long vector, struct pt_regs *regs)
 {
 	unsigned long pld;
 	unsigned int i;
@@ -532,20 +766,20 @@
 	save_flags(flags);
 	cli();
 
-        /* read the interrupt summary registers */
-        pld = (((unsigned long) (~inw(0x534)) & 0x0000ffffUL) << 16) |
-	       (((unsigned long) inb(0xa0))  <<  8) |
-	       ((unsigned long) inb(0x20));
+	/* read the interrupt summary registers */
+	pld = (((unsigned long) (~inw(0x534)) & 0x0000ffffUL) << 16) |
+		(((unsigned long) inb(0xa0))  <<  8) |
+		((unsigned long) inb(0x20));
 
 #if 0
-        printk("[0x%08lx]", pld);
+	printk("[0x%08lx]", pld);
 #endif
 
-        /*
-         * Now for every possible bit set, work through them and call
-         * the appropriate interrupt handler.
-         */
-        while (pld) {
+	/*
+	 * Now for every possible bit set, work through them and call
+	 * the appropriate interrupt handler.
+	 */
+	while (pld) {
 		i = ffz(~pld);
 		pld &= pld - 1; /* clear least bit set */
 		if (i < 16) {
@@ -553,12 +787,12 @@
 		} else {
 			device_interrupt(i, i, regs);
 		}
-        }
+	}
 	restore_flags(flags);
 }
 
-static inline void eb66_and_eb64p_device_interrupt(unsigned long vector,
-						   struct pt_regs * regs)
+static inline void 
+eb66_and_eb64p_device_interrupt(unsigned long vector, struct pt_regs *regs)
 {
 	unsigned long pld;
 	unsigned int i;
@@ -586,6 +820,93 @@
 	restore_flags(flags);
 }
 
+#if defined(CONFIG_ALPHA_MIATA)
+/* We have to conditionally compile this because of PYXIS_xxx symbols */
+static inline void 
+miata_device_interrupt(unsigned long vector, struct pt_regs *regs)
+{
+	unsigned long pld, tmp;
+	unsigned int i;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	/* read the interrupt summary register of PYXIS */
+	pld = (*(vulp)PYXIS_INT_REQ);
+
+#if 0
+	printk("[0x%08lx/0x%08lx/0x%04x]", pld,
+	       *(vulp)PYXIS_INT_MASK, inb(0x20) | (inb(0xA0) << 8));
+#endif
+
+#if 1
+	/*
+	 * For now, AND off any bits we are not interested in:
+	 *   HALT (2), timer (6), ISA Bridge (7), 21142/3 (8)
+	 * then all the PCI slots/INTXs (12-31).
+	 */
+	/* Maybe HALT should only be used for SRM console boots? */
+	pld &= 0x00000000fffff1c4UL;
+#endif
+
+	/*
+	 * Now for every possible bit set, work through them and call
+	 * the appropriate interrupt handler.
+	 */
+	while (pld) {
+		i = ffz(~pld);
+		pld &= pld - 1; /* clear least bit set */
+		if (i == 7) {
+			isa_device_interrupt(vector, regs);
+		} else if (i == 6)
+			continue;
+		else { /* if not timer int */
+			device_interrupt(16 + i, 16 + i, regs);
+		}
+		*(vulp)PYXIS_INT_REQ = 1UL << i; mb();
+		tmp = *(vulp)PYXIS_INT_REQ;
+	}
+	restore_flags(flags);
+}
+#endif /* MIATA */
+
+static inline void 
+noritake_device_interrupt(unsigned long vector, struct pt_regs *regs)
+{
+	unsigned long pld;
+	unsigned int i;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	/* read the interrupt summary registers of NORITAKE */
+	pld = ((unsigned long) inw(0x54c) << 32) |
+		((unsigned long) inw(0x54a) << 16) |
+		((unsigned long) inb(0xa0)  <<  8) |
+		((unsigned long) inb(0x20));
+
+#if 0
+	printk("[0x%08lx]", pld);
+#endif
+
+	/*
+	 * Now for every possible bit set, work through them and call
+	 * the appropriate interrupt handler.
+	 */
+	while (pld) {
+		i = ffz(~pld);
+		pld &= pld - 1; /* clear least bit set */
+		if (i < 16) {
+			isa_device_interrupt(vector, regs);
+		} else {
+			device_interrupt(i, i, regs);
+		}
+	}
+	restore_flags(flags);
+}
+
 #endif /* CONFIG_PCI */
 
 /*
@@ -611,7 +932,8 @@
  * "ack" to a different interrupt than we report to the rest of the
  * world.
  */
-static inline void srm_device_interrupt(unsigned long vector, struct pt_regs * regs)
+static inline void 
+srm_device_interrupt(unsigned long vector, struct pt_regs * regs)
 {
 	int irq, ack;
 	unsigned long flags;
@@ -624,25 +946,69 @@
 
 #ifdef CONFIG_ALPHA_JENSEN
 	switch (vector) {
-	      case 0x660: handle_nmi(regs); return;
+	case 0x660: handle_nmi(regs); return;
 		/* local device interrupts: */
-	      case 0x900: handle_irq(4, regs); return;	/* com1 -> irq 4 */
-	      case 0x920: handle_irq(3, regs); return;	/* com2 -> irq 3 */
-	      case 0x980: handle_irq(1, regs); return;	/* kbd -> irq 1 */
-	      case 0x990: handle_irq(9, regs); return;	/* mouse -> irq 9 */
-	      default:
+	case 0x900: handle_irq(4, regs); return;	/* com1 -> irq 4 */
+	case 0x920: handle_irq(3, regs); return;	/* com2 -> irq 3 */
+	case 0x980: handle_irq(1, regs); return;	/* kbd -> irq 1 */
+	case 0x990: handle_irq(9, regs); return;	/* mouse -> irq 9 */
+	default:
 		if (vector > 0x900) {
 			printk("Unknown local interrupt %lx\n", vector);
 		}
 	}
-	/* irq1 is supposed to be the keyboard, silly Jensen (is this really needed??) */
+	/* irq1 is supposed to be the keyboard, silly Jensen
+	   (is this really needed??) */
 	if (irq == 1)
 		irq = 7;
 #endif /* CONFIG_ALPHA_JENSEN */
 
+#ifdef CONFIG_ALPHA_MIATA
+	/*
+	 * I really hate to do this, but the MIATA SRM console ignores the
+	 *  low 8 bits in the interrupt summary register, and reports the
+	 *  vector 0x80 *lower* than I expected from the bit numbering in
+	 *  the documentation.
+	 * This was done because the low 8 summary bits really aren't used
+	 *  for reporting any interrupts (the PCI-ISA bridge, bit 7, isn't
+	 *  used for this purpose, as PIC interrupts are delivered as the
+	 *  vectors 0x800-0x8f0).
+	 * But I really don't want to change the fixup code for allocation
+	 *  of IRQs, nor the irq_mask maintenance stuff, both of which look
+	 *  nice and clean now.
+	 * So, here's this grotty hack... :-(
+	 */
+	if (irq >= 16)
+		ack = irq = irq + 8;
+#endif /* CONFIG_ALPHA_MIATA */
+
+#ifdef CONFIG_ALPHA_NORITAKE
+	/*
+	 * I really hate to do this, but the NORITAKE SRM console reports
+	 *  PCI vectors *lower* than I expected from the bit numbering in
+	 *  the documentation.
+	 * But I really don't want to change the fixup code for allocation
+	 *  of IRQs, nor the irq_mask maintenance stuff, both of which look
+	 *  nice and clean now.
+	 * So, here's this additional grotty hack... :-(
+	 */
+	if (irq >= 16)
+		ack = irq = irq + 1;
+#endif /* CONFIG_ALPHA_NORITAKE */
+
+#ifdef CONFIG_ALPHA_SABLE
+	irq = sable_mask_to_irq[(ack)];
+#if 0
+	if (irq == 5 || irq == 9 || irq == 10 || irq == 11 ||
+	    irq == 14 || irq == 15)
+		printk("srm_device_interrupt: vector=0x%lx  ack=0x%x"
+		       "  irq=0x%x\n", vector, ack, irq);
+#endif
+#endif /* CONFIG_ALPHA_SABLE */
+
 	device_interrupt(irq, ack, regs);
 
-	restore_flags(flags) ;
+	restore_flags(flags);
 }
 
 /*
@@ -665,6 +1031,7 @@
 			irqs |= (1UL << i);
 		}
 	}
+
 	/*
 	 * Wait about 100ms for spurious interrupts to mask themselves
 	 * out again...
@@ -695,66 +1062,154 @@
 	return i;
 }
 
-static void machine_check(unsigned long vector, unsigned long la, struct pt_regs * regs)
+extern void lca_machine_check (unsigned long vector, unsigned long la,
+			       struct pt_regs *regs);
+extern void apecs_machine_check(unsigned long vector, unsigned long la,
+				struct pt_regs * regs);
+extern void cia_machine_check(unsigned long vector, unsigned long la,
+			      struct pt_regs * regs);
+extern void pyxis_machine_check(unsigned long vector, unsigned long la,
+				struct pt_regs * regs);
+extern void t2_machine_check(unsigned long vector, unsigned long la,
+			     struct pt_regs * regs);
+
+static void 
+machine_check(unsigned long vector, unsigned long la, struct pt_regs *regs)
 {
 #if defined(CONFIG_ALPHA_LCA)
-	extern void lca_machine_check (unsigned long vector, unsigned long la,
-				       struct pt_regs *regs);
 	lca_machine_check(vector, la, regs);
 #elif defined(CONFIG_ALPHA_APECS)
-	extern void apecs_machine_check(unsigned long vector, unsigned long la,
-					struct pt_regs * regs);
 	apecs_machine_check(vector, la, regs);
 #elif defined(CONFIG_ALPHA_CIA)
-	extern void cia_machine_check(unsigned long vector, unsigned long la,
-					struct pt_regs * regs);
 	cia_machine_check(vector, la, regs);
+#elif defined(CONFIG_ALPHA_PYXIS)
+	pyxis_machine_check(vector, la, regs);
+#elif defined(CONFIG_ALPHA_T2)
+	t2_machine_check(vector, la, regs);
 #else
 	printk("Machine check\n");
 #endif
 }
 
-asmlinkage void do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr,
-	unsigned long a3, unsigned long a4, unsigned long a5,
-	struct pt_regs regs)
+asmlinkage void 
+do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr,
+	  unsigned long a3, unsigned long a4, unsigned long a5,
+	  struct pt_regs regs)
 {
 	switch (type) {
-		case 0:
-			printk("Interprocessor interrupt? You must be kidding\n");
-			break;
-		case 1:
-		        handle_irq(RTC_IRQ, &regs);
-			return;
-		case 2:
-			machine_check(vector, la_ptr, &regs);
-			return;
-		case 3:
+	case 0:
+		printk("Interprocessor interrupt? You must be kidding\n");
+		break;
+	case 1:
+		handle_irq(RTC_IRQ, &regs);
+		return;
+	case 2:
+		machine_check(vector, la_ptr, &regs);
+		return;
+	case 3:
 #if defined(CONFIG_ALPHA_JENSEN) || defined(CONFIG_ALPHA_NONAME) || \
     defined(CONFIG_ALPHA_P2K) || defined(CONFIG_ALPHA_SRM)
-			srm_device_interrupt(vector, &regs);
-#elif NR_IRQS == 48
-			alcor_and_xlt_device_interrupt(vector, &regs);
+		srm_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_MIATA)
+		miata_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_NORITAKE)
+		noritake_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
+		alcor_and_xlt_device_interrupt(vector, &regs);
 #elif NR_IRQS == 33
-			cabriolet_and_eb66p_device_interrupt(vector, &regs);
+		cabriolet_and_eb66p_device_interrupt(vector, &regs);
 #elif defined(CONFIG_ALPHA_MIKASA)
-			mikasa_device_interrupt(vector, &regs);
+		mikasa_device_interrupt(vector, &regs);
 #elif NR_IRQS == 32
-			eb66_and_eb64p_device_interrupt(vector, &regs);
+		eb66_and_eb64p_device_interrupt(vector, &regs);
 #elif NR_IRQS == 16
-			isa_device_interrupt(vector, &regs);
+		isa_device_interrupt(vector, &regs);
 #endif
-			return;
-		case 4:
-			printk("Performance counter interrupt\n");
-			break;;
-		default:
-			printk("Hardware intr %ld %lx? Huh?\n", type, vector);
+		return;
+	case 4:
+		printk("Performance counter interrupt\n");
+		break;
+	default:
+		printk("Hardware intr %ld %lx? Huh?\n", type, vector);
 	}
 	printk("PC = %016lx PS=%04lx\n", regs.pc, regs.ps);
 }
 
 extern asmlinkage void entInt(void);
 
+static inline void sable_init_IRQ(void)
+{
+	outb(irq_mask      , 0x537);	/* slave 0 */
+	outb(irq_mask >>  8, 0x53b);	/* slave 1 */
+	outb(irq_mask >> 16, 0x53d);	/* slave 2 */
+	outb(0x44, 0x535);		/* enable cascades in master */
+}
+
+#ifdef CONFIG_ALPHA_MIATA
+static inline void miata_init_IRQ(void)
+{
+	/* note invert on MASK bits */
+	*(vulp)PYXIS_INT_MASK = ~((long)irq_mask >> 16); mb(); /* invert */
+	*(vulp)PYXIS_INT_HILO = 0x000000B2UL; mb();	/* ISA/NMI HI */
+	*(vulp)PYXIS_RT_COUNT = 0UL; mb();		/* clear count */
+	*(vulp)PYXIS_INT_REQ  = 0x4000000000000000UL; mb(); /* clear upper timer */
+#if 0
+	*(vulp)PYXIS_INT_ROUTE = 0UL; mb();		/* all are level */
+	*(vulp)PYXIS_INT_CNFG  = 0UL; mb();		/* all clear */
+#endif
+	enable_irq(16 + 2);	/* enable HALT switch - SRM only? */
+	enable_irq(16 + 6);     /* enable timer */
+	enable_irq(16 + 7);     /* enable ISA PIC cascade */
+	enable_irq(2);		/* enable cascade */
+}
+#endif
+
+static inline void noritake_init_IRQ(void)
+{
+	outw(~(irq_mask >> 16), 0x54a); /* note invert */
+	outw(~(irq_mask >> 32), 0x54c); /* note invert */
+	enable_irq(2);			/* enable cascade */
+}
+
+#if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
+static inline void alcor_and_xlt_init_IRQ(void)
+{
+	*(vuip)GRU_INT_MASK  = ~(irq_mask >> 16); mb();	/* note invert */
+	*(vuip)GRU_INT_EDGE  = 0U; mb();		/* all are level */
+	*(vuip)GRU_INT_HILO  = 0x80000000U; mb();	/* ISA only HI */
+	*(vuip)GRU_INT_CLEAR = 0UL; mb();		/* all clear */
+
+	enable_irq(16 + 31);		/* enable (E)ISA PIC cascade */
+	enable_irq(2);			/* enable cascade */
+}
+#endif
+
+static inline void mikasa_init_IRQ(void)
+{
+	outw(~(irq_mask >> 16), 0x536); /* note invert */
+	enable_irq(2);			/* enable cascade */
+}
+
+static inline void init_IRQ_33(void)
+{
+	outl(irq_mask >> 16, 0x804);
+	enable_irq(16 + 4);		/* enable SIO cascade */
+	enable_irq(2);			/* enable cascade */
+}
+
+static inline void init_IRQ_32(void)
+{
+	outb(irq_mask >> 16, 0x26);
+	outb(irq_mask >> 24, 0x27);
+	enable_irq(16 + 5);		/* enable SIO cascade */
+	enable_irq(2);			/* enable cascade */
+}
+
+static inline void init_IRQ_16(void)
+{
+	enable_irq(2);			/* enable cascade */
+}
+
 void init_IRQ(void)
 {
 	wrent(entInt, 0);
@@ -762,21 +1217,27 @@
 	dma_outb(0, DMA2_RESET_REG);
 	dma_outb(0, DMA1_CLR_MASK_REG);
 	dma_outb(0, DMA2_CLR_MASK_REG);
-#if NR_IRQS == 48
-	*(unsigned int *)GRU_INT_MASK  = ~(irq_mask >> 16); mb();/* invert */
-	*(unsigned int *)GRU_INT_EDGE  = 0UL; mb();/* all are level */
-	*(unsigned int *)GRU_INT_HILO  = 0x80000000UL; mb();/* ISA only HI */
-	*(unsigned int *)GRU_INT_CLEAR = 0UL; mb();/* all clear */
-	enable_irq(16 + 31);	/* enable (E)ISA PIC cascade */
-#elif NR_IRQS == 33
-	outl(irq_mask >> 16, 0x804);
-	enable_irq(16 +  4);	/* enable SIO cascade */
+
+#if defined(CONFIG_ALPHA_SABLE)
+	sable_init_IRQ();
+#elif defined(CONFIG_ALPHA_MIATA)
+	miata_init_IRQ();
+#elif defined(CONFIG_ALPHA_NORITAKE)
+	noritake_init_IRQ();
+#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
+	alcor_and_xlt_init_IRQ();
+#elif defined(CONFIG_ALPHA_PC164) && defined(CONFIG_ALPHA_SRM)
+	/* Disable all the PCI interrupts?  Otherwise, everthing was
+	   done by SRM already.  */
 #elif defined(CONFIG_ALPHA_MIKASA)
-	outw(~(irq_mask >> 16), 0x536); /* note invert */
+	mikasa_init_IRQ();
+#elif NR_IRQS == 33
+	init_IRQ_33();
 #elif NR_IRQS == 32
-	outb(irq_mask >> 16, 0x26);
-	outb(irq_mask >> 24, 0x27);
-	enable_irq(16 +  5);	/* enable SIO cascade */
+	init_IRQ_32();
+#elif NR_IRQS == 16
+	init_IRQ_16();
+#else
+#error "How do I initialize the interrupt hardware?"
 #endif
-	enable_irq(2);		/* enable cascade */
 }
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/kernel/lca.c linux/arch/alpha/kernel/lca.c
--- v2.1.78/linux/arch/alpha/kernel/lca.c	Wed Oct  9 22:54:43 1996
+++ linux/arch/alpha/kernel/lca.c	Mon Jan 12 14:51:14 1998
@@ -6,7 +6,6 @@
  * bios code.
  */
 #include <linux/kernel.h>
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/bios32.h>
 #include <linux/pci.h>
@@ -19,9 +18,8 @@
  * BIOS32-style PCI interface:
  */
 
-#ifdef CONFIG_ALPHA_LCA
-
 #define vulp	volatile unsigned long *
+#define vuip	volatile unsigned int *
 
 /*
  * Machine check reasons.  Defined according to PALcode sources
@@ -102,11 +100,11 @@
 			return -1;
 		}
 
-		*((volatile unsigned long*) LCA_IOC_CONF) = 0;
+		*((vulp) LCA_IOC_CONF) = 0;
 		addr = (1 << (11 + device)) | (func << 8) | where;
 	} else {
 		/* type 1 configuration cycle: */
-		*((volatile unsigned long*) LCA_IOC_CONF) = 1;
+		*((vulp) LCA_IOC_CONF) = 1;
 		addr = (bus << 16) | (device_fn << 8) | where;
 	}
 	*pci_addr = addr;
@@ -123,13 +121,13 @@
 	cli();
 
 	/* reset status register to avoid loosing errors: */
-	stat0 = *((volatile unsigned long*)LCA_IOC_STAT0);
-	*((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
+	stat0 = *(vulp)LCA_IOC_STAT0;
+	*(vulp)LCA_IOC_STAT0 = stat0;
 	mb();
 
 	/* access configuration space: */
 
-	value = *((volatile unsigned int*)addr);
+	value = *(vuip)addr;
 	draina();
 
 	stat0 = *((unsigned long*)LCA_IOC_STAT0);
@@ -141,7 +139,7 @@
 		}
 
 		/* reset error status: */
-		*((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
+		*(vulp)LCA_IOC_STAT0 = stat0;
 		mb();
 		wrmces(0x7);			/* reset machine check */
 
@@ -160,13 +158,13 @@
 	cli();
 
 	/* reset status register to avoid loosing errors: */
-	stat0 = *((volatile unsigned long*)LCA_IOC_STAT0);
-	*((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
+	stat0 = *(vulp)LCA_IOC_STAT0;
+	*(vulp)LCA_IOC_STAT0 = stat0;
 	mb();
 
 	/* access configuration space: */
 
-	*((volatile unsigned int*)addr) = value;
+	*(vuip)addr = value;
 	draina();
 
 	stat0 = *((unsigned long*)LCA_IOC_STAT0);
@@ -178,7 +176,7 @@
 		}
 
 		/* reset error status: */
-		*((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
+		*(vulp)LCA_IOC_STAT0 = stat0;
 		mb();
 		wrmces(0x7);			/* reset machine check */
 	}
@@ -310,13 +308,12 @@
 }
 
 
-
-
 /*
  * Constants used during machine-check handling.  I suppose these
  * could be moved into lca.h but I don't see much reason why anybody
  * else would want to use them.
  */
+
 #define ESR_EAV		(1UL<< 0)	/* error address valid */
 #define ESR_CEE		(1UL<< 1)	/* correctable error */
 #define ESR_UEE		(1UL<< 2)	/* uncorrectable error */
@@ -338,53 +335,60 @@
 
 void mem_error (unsigned long esr, unsigned long ear)
 {
-    printk("    %s %s error to %s occurred at address %x\n",
-	   (esr & ESR_CEE) ? "Correctable" : ((esr & ESR_UEE) ? "Uncorrectable" : "A"),
-	   (esr & ESR_WRE) ? "write" : "read",
-	   (esr & ESR_SOR) ? "memory" : "b-cache",
-	   (unsigned) (ear & 0x1ffffff8));
-    if (esr & ESR_CTE) {
-	printk("    A b-cache tag parity error was detected.\n");
-    }
-    if (esr & ESR_MSE) {
-	printk("    Several other correctable errors occurred.\n");
-    }
-    if (esr & ESR_MHE) {
-	printk("    Several other uncorrectable errors occurred.\n");
-    }
-    if (esr & ESR_NXM) {
-	printk("    Attempted to access non-existent memory.\n");
-    }
+	printk("    %s %s error to %s occurred at address %x\n",
+	       *((esr & ESR_CEE) ? "Correctable" :
+		 (esr & ESR_UEE) ? "Uncorrectable" : "A"),
+	       (esr & ESR_WRE) ? "write" : "read",
+	       (esr & ESR_SOR) ? "memory" : "b-cache",
+	       (unsigned) (ear & 0x1ffffff8));
+	if (esr & ESR_CTE) {
+		printk("    A b-cache tag parity error was detected.\n");
+	}
+	if (esr & ESR_MSE) {
+		printk("    Several other correctable errors occurred.\n");
+	}
+	if (esr & ESR_MHE) {
+		printk("    Several other uncorrectable errors occurred.\n");
+	}
+	if (esr & ESR_NXM) {
+		printk("    Attempted to access non-existent memory.\n");
+	}
 }
 
 
 void ioc_error (__u32 stat0, __u32 stat1)
 {
-    const char *pci_cmd[] = {
-	"Interrupt Acknowledge", "Special", "I/O Read", "I/O Write",
-	"Rsvd 1", "Rsvd 2", "Memory Read", "Memory Write", "Rsvd3", "Rsvd4",
-	"Configuration Read", "Configuration Write", "Memory Read Multiple",
-	"Dual Address", "Memory Read Line", "Memory Write and Invalidate"
-    };
-    const char *err_name[] = {
-	"exceeded retry limit", "no device", "bad data parity", "target abort",
-	"bad address parity", "page table read error", "invalid page", "data error"
-    };
-    unsigned code = (stat0 & IOC_CODE) >> IOC_CODE_SHIFT;
-    unsigned cmd  = (stat0 & IOC_CMD)  >> IOC_CMD_SHIFT;
-
-    printk("    %s initiated PCI %s cycle to address %x failed due to %s.\n",
-	   code > 3 ? "PCI" : "CPU", pci_cmd[cmd], stat1, err_name[code]);
-    if (code == 5 || code == 6) {
-	printk("    (Error occurred at PCI memory address %x.)\n", (stat0 & ~IOC_P_NBR));
-    }
-    if (stat0 & IOC_LOST) {
-	printk("    Other PCI errors occurred simultaneously.\n");
-    }
+	static const char * const pci_cmd[] = {
+		"Interrupt Acknowledge", "Special", "I/O Read", "I/O Write",
+		"Rsvd 1", "Rsvd 2", "Memory Read", "Memory Write", "Rsvd3",
+		"Rsvd4", "Configuration Read", "Configuration Write",
+		"Memory Read Multiple", "Dual Address", "Memory Read Line",
+		"Memory Write and Invalidate"
+	};
+	static const char * const err_name[] = {
+		"exceeded retry limit", "no device", "bad data parity",
+		"target abort", "bad address parity", "page table read error",
+		"invalid page", "data error"
+	};
+	unsigned code = (stat0 & IOC_CODE) >> IOC_CODE_SHIFT;
+	unsigned cmd  = (stat0 & IOC_CMD)  >> IOC_CMD_SHIFT;
+
+	printk("    %s initiated PCI %s cycle to address %x"
+	       " failed due to %s.\n",
+	       code > 3 ? "PCI" : "CPU", pci_cmd[cmd], stat1, err_name[code]);
+
+	if (code == 5 || code == 6) {
+		printk("    (Error occurred at PCI memory address %x.)\n",
+		       (stat0 & ~IOC_P_NBR));
+	}
+	if (stat0 & IOC_LOST) {
+		printk("    Other PCI errors occurred simultaneously.\n");
+	}
 }
 
 
-void lca_machine_check (unsigned long vector, unsigned long la, struct pt_regs *regs)
+void lca_machine_check (unsigned long vector, unsigned long la,
+			struct pt_regs *regs)
 {
 	unsigned long * ptr;
 	const char * reason;
@@ -403,21 +407,21 @@
 	 * revision level, which we ignore for now.
 	 */
 	switch (el.c->code & 0xffffffff) {
-	      case MCHK_K_TPERR:	reason = "tag parity error"; break;
-	      case MCHK_K_TCPERR:	reason = "tag control parity error"; break;
-	      case MCHK_K_HERR:		reason = "access to non-existent memory"; break;
-	      case MCHK_K_ECC_C:	reason = "correctable ECC error"; break;
-	      case MCHK_K_ECC_NC:	reason = "non-correctable ECC error"; break;
-	      case MCHK_K_CACKSOFT:	reason = "MCHK_K_CACKSOFT"; break; /* what's this? */
-	      case MCHK_K_BUGCHECK:	reason = "illegal exception in PAL mode"; break;
-	      case MCHK_K_OS_BUGCHECK:	reason = "callsys in kernel mode"; break;
-	      case MCHK_K_DCPERR:	reason = "d-cache parity error"; break;
-	      case MCHK_K_ICPERR:	reason = "i-cache parity error"; break;
-	      case MCHK_K_SIO_SERR:	reason = "SIO SERR occurred on on PCI bus"; break;
-	      case MCHK_K_SIO_IOCHK:	reason = "SIO IOCHK occurred on ISA bus"; break;
-	      case MCHK_K_DCSR:		reason = "MCHK_K_DCSR"; break;
-	      case MCHK_K_UNKNOWN:
-	      default:
+	case MCHK_K_TPERR:	reason = "tag parity error"; break;
+	case MCHK_K_TCPERR:	reason = "tag control parity error"; break;
+	case MCHK_K_HERR:	reason = "access to non-existent memory"; break;
+	case MCHK_K_ECC_C:	reason = "correctable ECC error"; break;
+	case MCHK_K_ECC_NC:	reason = "non-correctable ECC error"; break;
+	case MCHK_K_CACKSOFT:	reason = "MCHK_K_CACKSOFT"; break; /* what's this? */
+	case MCHK_K_BUGCHECK:	reason = "illegal exception in PAL mode"; break;
+	case MCHK_K_OS_BUGCHECK: reason = "callsys in kernel mode"; break;
+	case MCHK_K_DCPERR:	reason = "d-cache parity error"; break;
+	case MCHK_K_ICPERR:	reason = "i-cache parity error"; break;
+	case MCHK_K_SIO_SERR:	reason = "SIO SERR occurred on on PCI bus"; break;
+	case MCHK_K_SIO_IOCHK:	reason = "SIO IOCHK occurred on ISA bus"; break;
+	case MCHK_K_DCSR:	reason = "MCHK_K_DCSR"; break;
+	case MCHK_K_UNKNOWN:
+	default:
 		sprintf(buf, "reason for machine-check unknown (0x%lx)",
 			el.c->code & 0xffffffff);
 		reason = buf;
@@ -427,19 +431,20 @@
 	wrmces(rdmces());	/* reset machine check pending flag */
 
 	switch (el.c->size) {
-	      case sizeof(struct el_lca_mcheck_short):
+	case sizeof(struct el_lca_mcheck_short):
 		printk(KERN_CRIT
 		       "  Reason: %s (short frame%s, dc_stat=%lx):\n",
-		       reason, el.c->retry ? ", retryable" : "", el.s->dc_stat);
+		       reason, el.c->retry ? ", retryable" : "",
+		       el.s->dc_stat);
 		if (el.s->esr & ESR_EAV) {
-		    mem_error(el.s->esr, el.s->ear);
+			mem_error(el.s->esr, el.s->ear);
 		}
 		if (el.s->ioc_stat0 & IOC_ERR) {
-		    ioc_error(el.s->ioc_stat0, el.s->ioc_stat1);
+			ioc_error(el.s->ioc_stat0, el.s->ioc_stat1);
 		}
 		break;
 
-	      case sizeof(struct el_lca_mcheck_long):
+	case sizeof(struct el_lca_mcheck_long):
 		printk(KERN_CRIT "  Reason: %s (long frame%s):\n",
 		       reason, el.c->retry ? ", retryable" : "");
 		printk(KERN_CRIT
@@ -447,14 +452,14 @@
 		       el.l->pt[0], el.l->exc_addr, el.l->dc_stat);
 		printk(KERN_CRIT "    car: %lx\n", el.l->car);
 		if (el.l->esr & ESR_EAV) {
-		    mem_error(el.l->esr, el.l->ear);
+			mem_error(el.l->esr, el.l->ear);
 		}
 		if (el.l->ioc_stat0 & IOC_ERR) {
-		    ioc_error(el.l->ioc_stat0, el.l->ioc_stat1);
+			ioc_error(el.l->ioc_stat0, el.l->ioc_stat1);
 		}
 		break;
 
-	      default:
+	default:
 		printk(KERN_CRIT "  Unknown errorlog size %d\n", el.c->size);
 	}
 
@@ -462,9 +467,51 @@
 
 	ptr = (unsigned long *) la;
 	for (i = 0; i < el.c->size / sizeof(long); i += 2) {
-	    printk(KERN_CRIT " +%8lx %016lx %016lx\n",
-		   i*sizeof(long), ptr[i], ptr[i+1]);
+		printk(KERN_CRIT " +%8lx %016lx %016lx\n",
+		       i*sizeof(long), ptr[i], ptr[i+1]);
 	}
 }
 
-#endif /* CONFIG_ALPHA_LCA */
+/*
+ * The following routines are needed to support the SPEED changing
+ * necessary to successfully manage the thermal problem on the AlphaBook1.
+ */
+
+void
+lca_clock_print(void)
+{
+        long    pmr_reg;
+
+        pmr_reg = READ_PMR;
+
+        printk("Status of clock control:\n");
+        printk("\tPrimary clock divisor\t0x%x\n", GET_PRIMARY(pmr_reg));
+        printk("\tOverride clock divisor\t0x%x\n", GET_OVERRIDE(pmr_reg));
+        printk("\tInterrupt override is %s\n",
+	       (pmr_reg & LCA_PMR_INTO) ? "on" : "off"); 
+        printk("\tDMA override is %s\n",
+	       (pmr_reg & LCA_PMR_DMAO) ? "on" : "off"); 
+
+}
+
+int
+lca_get_clock(void)
+{
+        long    pmr_reg;
+
+        pmr_reg = READ_PMR;
+        return(GET_PRIMARY(pmr_reg));
+
+}
+
+void
+lca_clock_fiddle(int divisor)
+{
+        long    pmr_reg;
+
+        pmr_reg = READ_PMR;
+        SET_PRIMARY_CLOCK(pmr_reg, divisor);
+	/* lca_norm_clock = divisor; */
+        WRITE_PMR(pmr_reg);
+        mb();
+}
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/kernel/osf_sys.c linux/arch/alpha/kernel/osf_sys.c
--- v2.1.78/linux/arch/alpha/kernel/osf_sys.c	Fri Jan  2 14:37:01 1998
+++ linux/arch/alpha/kernel/osf_sys.c	Mon Jan 12 14:49:36 1998
@@ -846,10 +846,6 @@
 	return -EOPNOTSUPP;
 }
 
-/* Dummy functions for now */
-#define wrfpcr(x)	do { } while (0)
-#define rdfpcr()	0
-
 asmlinkage unsigned long osf_setsysinfo(unsigned long op, void *buffer,
 					unsigned long nbytes,
 					int *start, void *arg)
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/kernel/process.c linux/arch/alpha/kernel/process.c
--- v2.1.78/linux/arch/alpha/kernel/process.c	Thu Jul 17 10:06:03 1997
+++ linux/arch/alpha/kernel/process.c	Mon Jan 12 14:51:14 1998
@@ -38,6 +38,7 @@
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/pgtable.h>
+#include <asm/hwrpb.h>
 
 /*
  * Initial task structure. Make this a per-architecture thing,
@@ -86,7 +87,7 @@
 	return ret;
 }
 
-void machine_restart(char * __unused)
+static void finish_shutdown(void)
 {
 #ifdef CONFIG_RTC  /* reset rtc to defaults */
 	unsigned char control;
@@ -105,7 +106,6 @@
         CMOS_READ(RTC_INTR_FLAGS);
 	restore_flags(flags);
 #endif
-
 #if defined(CONFIG_ALPHA_SRM) && defined(CONFIG_ALPHA_ALCOR)
 	/* who said DEC engineer's have no sense of humor? ;-)) */
 	*(int *) GRU_RESET = 0x0000dead;
@@ -114,12 +114,50 @@
 	halt();
 }
 
+void machine_restart(char * __unused)
+{
+#if defined(CONFIG_ALPHA_SRM)
+	extern struct hwrpb_struct *hwrpb;
+	struct percpu_struct *cpup;
+	unsigned long flags;
+	
+	cpup = (struct percpu_struct *)
+	  ((unsigned long)hwrpb + hwrpb->processor_offset);
+	flags = cpup->flags;
+	flags &= ~0x0000000000ff0001UL; /* clear reason to "default" */
+	flags |=  0x0000000000020000UL; /* this is "cold bootstrap" */
+/*	flags |=  0x0000000000030000UL; *//* this is "warm bootstrap" */
+	cpup->flags = flags;					       
+	mb();						
+#endif /* SRM */                                        
+
+	finish_shutdown();
+}
+
 void machine_halt(void)
 {
+#if defined(CONFIG_ALPHA_SRM)
+	extern struct hwrpb_struct *hwrpb;
+	struct percpu_struct *cpup;
+	unsigned long flags;
+	
+	cpup = (struct percpu_struct *)
+	  ((unsigned long)hwrpb + hwrpb->processor_offset);
+	flags = cpup->flags;
+	flags &= ~0x0000000000ff0001UL; /* clear reason to "default" */
+	flags |=  0x0000000000040000UL; /* this is "remain halted" */
+	cpup->flags = flags;					       
+	mb();						
+
+	finish_shutdown();
+#endif /* SRM */                                        
 }
 
 void machine_power_off(void)
 {
+	/* None of the machines we support, at least, has switchable 
+	   power supplies.  */
+	machine_halt();
 }
 
 void show_regs(struct pt_regs * regs)
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/kernel/pyxis.c linux/arch/alpha/kernel/pyxis.c
--- v2.1.78/linux/arch/alpha/kernel/pyxis.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/alpha/kernel/pyxis.c	Mon Jan 12 14:51:14 1998
@@ -0,0 +1,503 @@
+/*
+ * Code common to all PYXIS chips.
+ *
+ * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com).
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/hwrpb.h>
+#include <asm/ptrace.h>
+#include <asm/mmu_context.h>
+
+extern struct hwrpb_struct *hwrpb;
+extern asmlinkage void wrmces(unsigned long mces);
+extern int alpha_sys_type;
+
+/*
+ * BIOS32-style PCI interface:
+ */
+
+#ifdef DEBUG 
+# define DBG(args)	printk args
+#else
+# define DBG(args)
+#endif
+
+#define DEBUG_MCHECK
+#ifdef DEBUG_MCHECK
+# define DBG_MCK(args)	printk args
+#else
+# define DBG_MCK(args)
+#endif
+
+#define vulp	volatile unsigned long *
+#define vuip	volatile unsigned int  *
+
+static volatile unsigned int PYXIS_mcheck_expected = 0;
+static volatile unsigned int PYXIS_mcheck_taken = 0;
+static unsigned int PYXIS_jd;
+
+
+/*
+ * Given a bus, device, and function number, compute resulting
+ * configuration space address and setup the PYXIS_HAXR2 register
+ * accordingly.  It is therefore not safe to have concurrent
+ * invocations to configuration space access routines, but there
+ * really shouldn't be any need for this.
+ *
+ * Type 0:
+ *
+ *  3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 
+ *  3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *	31:11	Device select bit.
+ * 	10:8	Function number
+ * 	 7:2	Register number
+ *
+ * Type 1:
+ *
+ *  3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 
+ *  3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *	31:24	reserved
+ *	23:16	bus number (8 bits = 128 possible buses)
+ *	15:11	Device number (5 bits)
+ *	10:8	function number
+ *	 7:2	register number
+ *  
+ * Notes:
+ *	The function number selects which function of a multi-function device 
+ *	(e.g., scsi and ethernet).
+ * 
+ *	The register selects a DWORD (32 bit) register offset.  Hence it
+ *	doesn't get shifted by 2 bits as we want to "drop" the bottom two
+ *	bits.
+ */
+static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
+			unsigned char where, unsigned long *pci_addr,
+			unsigned char *type1)
+{
+	unsigned long addr;
+
+	DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x,"
+	     " pci_addr=0x%p, type1=0x%p)\n",
+	     bus, device_fn, where, pci_addr, type1));
+
+	if (bus == 0) {
+		int device;
+
+		device = device_fn >> 3;
+		/* type 0 configuration cycle: */
+#if NOT_NOW
+		if (device > 20) {
+			DBG(("mk_conf_addr: device (%d) > 20, returning -1\n",
+			     device));
+			return -1;
+		}
+#endif
+		*type1 = 0;
+		addr = (device_fn << 8) | (where);
+	} else {
+		/* type 1 configuration cycle: */
+		*type1 = 1;
+		addr = (bus << 16) | (device_fn << 8) | (where);
+	}
+	*pci_addr = addr;
+	DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
+	return 0;
+}
+
+
+static unsigned int conf_read(unsigned long addr, unsigned char type1)
+{
+	unsigned long flags;
+	unsigned int stat0, value;
+	unsigned int pyxis_cfg = 0; /* to keep gcc quiet */
+
+	save_flags(flags);	/* avoid getting hit by machine check */
+	cli();
+
+	DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1));
+
+	/* reset status register to avoid losing errors: */
+	stat0 = *(vuip)PYXIS_ERR;
+	*(vuip)PYXIS_ERR = stat0;
+	mb();
+	DBG(("conf_read: PYXIS ERR was 0x%x\n", stat0));
+	/* if Type1 access, must set PYXIS CFG */
+	if (type1) {
+		pyxis_cfg = *(vuip)PYXIS_CFG;
+		*(vuip)PYXIS_CFG = pyxis_cfg | 1;
+		mb();
+		DBG(("conf_read: TYPE1 access\n"));
+	}
+
+	mb();
+	draina();
+	PYXIS_mcheck_expected = 1;
+	PYXIS_mcheck_taken = 0;
+	mb();
+	/* access configuration space: */
+	value = *(vuip)addr;
+	mb();
+	if (PYXIS_mcheck_taken) {
+		PYXIS_mcheck_taken = 0;
+		value = 0xffffffffU;
+		mb();
+	}
+	PYXIS_mcheck_expected = 0;
+	mb();
+	/*
+	 * david.rusling@reo.mts.dec.com.  This code is needed for the
+	 * EB64+ as it does not generate a machine check (why I don't
+	 * know).  When we build kernels for one particular platform
+	 * then we can make this conditional on the type.
+	 */
+#if 0
+	draina();
+
+	/* now look for any errors */
+	stat0 = *(vuip)PYXIS_IOC_PYXIS_ERR;
+	DBG(("conf_read: PYXIS ERR after read 0x%x\n", stat0));
+	if (stat0 & 0x8280U) { /* is any error bit set? */
+		/* if not NDEV, print status */
+		if (!(stat0 & 0x0080)) {
+			printk("PYXIS.c:conf_read: got stat0=%x\n", stat0);
+		}
+
+		/* reset error status: */
+		*(vulp)PYXIS_IOC_PYXIS_ERR = stat0;
+		mb();
+		wrmces(0x7);			/* reset machine check */
+		value = 0xffffffff;
+	}
+#endif
+
+	/* if Type1 access, must reset IOC CFG so normal IO space ops work */
+	if (type1) {
+		*(vuip)PYXIS_CFG = pyxis_cfg & ~1;
+		mb();
+	}
+
+	DBG(("conf_read(): finished\n"));
+
+	restore_flags(flags);
+	return value;
+}
+
+
+static void conf_write(unsigned long addr, unsigned int value,
+		       unsigned char type1)
+{
+	unsigned long flags;
+	unsigned int stat0;
+	unsigned int pyxis_cfg = 0; /* to keep gcc quiet */
+
+	save_flags(flags);	/* avoid getting hit by machine check */
+	cli();
+
+	/* reset status register to avoid losing errors: */
+	stat0 = *(vuip)PYXIS_ERR;
+	*(vuip)PYXIS_ERR = stat0;
+	mb();
+	DBG(("conf_write: PYXIS ERR was 0x%x\n", stat0));
+	/* if Type1 access, must set PYXIS CFG */
+	if (type1) {
+		pyxis_cfg = *(vuip)PYXIS_CFG;
+		*(vuip)PYXIS_CFG = pyxis_cfg | 1;
+		mb();
+		DBG(("conf_read: TYPE1 access\n"));
+	}
+
+	draina();
+	PYXIS_mcheck_expected = 1;
+	mb();
+	/* access configuration space: */
+	*(vuip)addr = value;
+	mb();
+	PYXIS_mcheck_expected = 0;
+	mb();
+
+	/* if Type1 access, must reset IOC CFG so normal IO space ops work */
+	if (type1) {
+		*(vuip)PYXIS_CFG = pyxis_cfg & ~1;
+		mb();
+	}
+
+	DBG(("conf_write(): finished\n"));
+	restore_flags(flags);
+}
+
+
+int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn,
+			      unsigned char where, unsigned char *value)
+{
+	unsigned long addr = PYXIS_CONF;
+	unsigned long pci_addr;
+	unsigned char type1;
+
+	*value = 0xff;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	addr |= (pci_addr << 5) + 0x00;
+
+	*value = conf_read(addr, type1) >> ((where & 3) * 8);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_read_config_word (unsigned char bus, unsigned char device_fn,
+			      unsigned char where, unsigned short *value)
+{
+	unsigned long addr = PYXIS_CONF;
+	unsigned long pci_addr;
+	unsigned char type1;
+
+	*value = 0xffff;
+
+	if (where & 0x1) {
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	addr |= (pci_addr << 5) + 0x08;
+
+	*value = conf_read(addr, type1) >> ((where & 3) * 8);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn,
+			       unsigned char where, unsigned int *value)
+{
+	unsigned long addr = PYXIS_CONF;
+	unsigned long pci_addr;
+	unsigned char type1;
+
+	*value = 0xffffffff;
+	if (where & 0x3) {
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+	addr |= (pci_addr << 5) + 0x18;
+	*value = conf_read(addr, type1);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn,
+			       unsigned char where, unsigned char value)
+{
+	unsigned long addr = PYXIS_CONF;
+	unsigned long pci_addr;
+	unsigned char type1;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+	addr |= (pci_addr << 5) + 0x00;
+	conf_write(addr, value << ((where & 3) * 8), type1);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_word (unsigned char bus, unsigned char device_fn,
+			       unsigned char where, unsigned short value)
+{
+	unsigned long addr = PYXIS_CONF;
+	unsigned long pci_addr;
+	unsigned char type1;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+	addr |= (pci_addr << 5) + 0x08;
+	conf_write(addr, value << ((where & 3) * 8), type1);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
+				unsigned char where, unsigned int value)
+{
+	unsigned long addr = PYXIS_CONF;
+	unsigned long pci_addr;
+	unsigned char type1;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+	addr |= (pci_addr << 5) + 0x18;
+	conf_write(addr, value << ((where & 3) * 8), type1);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+unsigned long pyxis_init(unsigned long mem_start, unsigned long mem_end)
+{
+	unsigned int pyxis_err ;
+
+	/* 
+	 * Set up error reporting.
+	 */
+	pyxis_err = *(vuip)PYXIS_ERR ;
+	pyxis_err |= 0x180;   /* master/target abort */
+	*(vuip)PYXIS_ERR = pyxis_err ;
+	mb() ;
+	pyxis_err = *(vuip)PYXIS_ERR ;
+
+	/*
+	 * Set up the PCI->physical memory translation windows.
+	 * For now, windows 1,2 and 3 are disabled.  In the future, we may
+	 * want to use them to do scatter/gather DMA.  Window 0
+	 * goes at 1 GB and is 1 GB large.
+	 */
+
+	*(vuip)PYXIS_W0_BASE = 1U | (PYXIS_DMA_WIN_BASE & 0xfff00000U);
+	*(vuip)PYXIS_W0_MASK = (PYXIS_DMA_WIN_SIZE - 1) & 0xfff00000U;
+	*(vuip)PYXIS_T0_BASE = 0;
+
+	*(vuip)PYXIS_W1_BASE = 0x0 ;
+	*(vuip)PYXIS_W2_BASE = 0x0 ;
+	*(vuip)PYXIS_W3_BASE = 0x0 ;
+	mb();
+
+	/*
+	 * check ASN in HWRPB for validity, report if bad
+	 */
+	if (hwrpb->max_asn != MAX_ASN) {
+		printk("PYXIS_init: max ASN from HWRPB is bad (0x%lx)\n",
+		       hwrpb->max_asn);
+		hwrpb->max_asn = MAX_ASN;
+	}
+
+	/*
+	 * Finally, clear the PYXIS_CFG register, which gets used
+	 *  for PCI Config Space accesses. That is the way
+	 *  we want to use it, and we do not want to depend on
+	 *  what ARC or SRM might have left behind...
+	 */
+	{
+		unsigned int pyxis_cfg;
+		pyxis_cfg = *(vuip)PYXIS_CFG; mb();
+#if 0
+		printk("PYXIS_init: CFG was 0x%x\n", pyxis_cfg);
+#endif
+		*(vuip)PYXIS_CFG = 0; mb();
+	}
+ 
+	{
+		unsigned int pyxis_hae_mem = *(vuip)PYXIS_HAE_MEM;
+		unsigned int pyxis_hae_io = *(vuip)PYXIS_HAE_IO;
+#if 0
+		printk("PYXIS_init: HAE_MEM was 0x%x\n", pyxis_hae_mem);
+		printk("PYXIS_init: HAE_IO was 0x%x\n", pyxis_hae_io);
+#endif
+		*(vuip)PYXIS_HAE_MEM = 0; mb();
+		pyxis_hae_mem = *(vuip)PYXIS_HAE_MEM;
+		*(vuip)PYXIS_HAE_IO = 0; mb();
+		pyxis_hae_io = *(vuip)PYXIS_HAE_IO;
+	}
+
+	return mem_start;
+}
+
+int pyxis_pci_clr_err(void)
+{
+	PYXIS_jd = *(vuip)PYXIS_ERR;
+	DBG(("PYXIS_pci_clr_err: PYXIS ERR after read 0x%x\n", PYXIS_jd));
+	*(vuip)PYXIS_ERR = 0x0180;
+	mb();
+	PYXIS_jd = *(vuip)PYXIS_ERR;
+	return 0;
+}
+
+void pyxis_machine_check(unsigned long vector, unsigned long la_ptr,
+			 struct pt_regs * regs)
+{
+	struct el_common *mchk_header;
+	struct el_PYXIS_sysdata_mcheck *mchk_sysdata;
+
+	mchk_header = (struct el_common *)la_ptr;
+
+	mchk_sysdata = (struct el_PYXIS_sysdata_mcheck *)
+		(la_ptr + mchk_header->sys_offset);
+
+#if 0
+	DBG_MCK(("pyxis_machine_check: vector=0x%lx la_ptr=0x%lx\n",
+		 vector, la_ptr));
+	DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
+		 regs->pc, mchk_header->size, mchk_header->proc_offset,
+		 mchk_header->sys_offset));
+	DBG_MCK(("pyxis_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n",
+		 PYXIS_mcheck_expected, mchk_sysdata->epic_dcsr,
+		 mchk_sysdata->epic_pear));
+#endif
+#ifdef DEBUG_MCHECK_DUMP
+	{
+		unsigned long *ptr;
+		int i;
+
+		ptr = (unsigned long *)la_ptr;
+		for (i = 0; i < mchk_header->size / sizeof(long); i += 2) {
+			printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]);
+		}
+	}
+#endif /* DEBUG_MCHECK_DUMP */
+
+	/*
+	 * Check if machine check is due to a badaddr() and if so,
+	 * ignore the machine check.
+	 */
+	mb();
+	if (PYXIS_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) {
+		DBG(("PYXIS machine check expected\n"));
+		PYXIS_mcheck_expected = 0;
+		PYXIS_mcheck_taken = 1;
+		mb();
+		draina();
+		pyxis_pci_clr_err();
+		wrmces(0x7);
+		mb();
+	}
+#if 1
+	else {
+		printk("PYXIS machine check NOT expected\n") ;
+		DBG_MCK(("pyxis_machine_check: vector=0x%lx la_ptr=0x%lx\n",
+			 vector, la_ptr));
+		DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
+			 regs->pc, mchk_header->size, mchk_header->proc_offset,
+			 mchk_header->sys_offset));
+		PYXIS_mcheck_expected = 0;
+		PYXIS_mcheck_taken = 1;
+		mb();
+		draina();
+		pyxis_pci_clr_err();
+		wrmces(0x7);
+		mb();
+	}
+#endif
+}
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/kernel/setup.c linux/arch/alpha/kernel/setup.c
--- v2.1.78/linux/arch/alpha/kernel/setup.c	Tue May 13 22:41:00 1997
+++ linux/arch/alpha/kernel/setup.c	Mon Jan 12 14:51:14 1998
@@ -62,6 +62,18 @@
  * code think we're on a VGA color display.
  */
 struct screen_info screen_info = {
+#if defined(CONFIG_ALPHA_BOOK1)
+  /* the AlphaBook1 has LCD video fixed at 800x600, 37 rows and 100 cols */
+	0, 37,			/* orig-x, orig-y */
+	{ 0, 0 },		/* unused */
+	0,			/* orig-video-page */
+	0,			/* orig-video-mode */
+        100,			/* orig-video-cols */
+	0,0,0,			/* ega_ax, ega_bx, ega_cx */
+        37,			/* orig-video-lines */
+	1,			/* orig-video-isVGA */
+	16			/* orig-video-points */
+#else
 	0, 25,			/* orig-x, orig-y */
 	{ 0, 0 },		/* unused */
 	0,			/* orig-video-page */
@@ -71,6 +83,7 @@
 	25,			/* orig-video-lines */
 	1,			/* orig-video-isVGA */
 	16			/* orig-video-points */
+#endif
 };
 
 /*
@@ -166,6 +179,10 @@
 	*memory_start_p = apecs_init(*memory_start_p, *memory_end_p);
 #elif defined(CONFIG_ALPHA_CIA)
 	*memory_start_p = cia_init(*memory_start_p, *memory_end_p);
+#elif defined(CONFIG_ALPHA_PYXIS)
+	*memory_start_p = pyxis_init(*memory_start_p, *memory_end_p);
+#elif defined(CONFIG_ALPHA_T2)
+	*memory_start_p = t2_init(*memory_start_p, *memory_end_p);
 #endif
 }
 
@@ -175,7 +192,8 @@
 int get_cpuinfo(char *buffer)
 {
 	const char *cpu_name[] = {
-		"EV3", "EV4", "Unknown 1", "LCA4", "EV5", "EV45"
+		"EV3", "EV4", "Unknown 1", "LCA4", "EV5", "EV45", "EV56",
+		"EV6", "PCA56"
 	};
 #	define SYSTYPE_NAME_BIAS	20
 	const char *systype_name[] = {
@@ -185,8 +203,9 @@
 		"ADU", "Cobra", "Ruby", "Flamingo", "5", "Jensen",
 		"Pelican", "8", "Sable", "AXPvme", "Noname",
 		"Turbolaser", "Avanti", "Mustang", "Alcor", "16",
-		"Mikasa", "18", "EB66", "EB64+", "21", "22", "23",
-		"24", "25", "EB164"
+		"Mikasa", "18", "EB66", "EB64+", "AlphaBook1",
+		"Rawhide", "Lego", "Lynx", "25", "EB164", "Noritake",
+		"Cortex", "29", "Miata", "31", "Takara", "Yukon"
 	};
 	struct percpu_struct *cpu;
 	unsigned int cpu_index;
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/kernel/t2.c linux/arch/alpha/kernel/t2.c
--- v2.1.78/linux/arch/alpha/kernel/t2.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/alpha/kernel/t2.c	Mon Jan 12 14:51:14 1998
@@ -0,0 +1,591 @@
+/*
+ * Code common to all T2 chips.
+ *
+ * Written by Jay A Estabrook (jestabro@amt.tay1.dec.com).
+ * December 1996.
+ *
+ * based on CIA code by David A Rusling (david.rusling@reo.mts.dec.com)
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/hwrpb.h>
+#include <asm/ptrace.h>
+#include <asm/mmu_context.h>
+
+extern struct hwrpb_struct *hwrpb;
+extern asmlinkage void wrmces(unsigned long mces);
+extern asmlinkage unsigned long whami(void);
+extern int alpha_sys_type;
+
+#define CPUID whami()
+
+
+/*
+ * Machine check reasons.  Defined according to PALcode sources
+ * (osf.h and platform.h).
+ */
+#define MCHK_K_TPERR		0x0080
+#define MCHK_K_TCPERR		0x0082
+#define MCHK_K_HERR		0x0084
+#define MCHK_K_ECC_C		0x0086
+#define MCHK_K_ECC_NC		0x0088
+#define MCHK_K_OS_BUGCHECK	0x008A
+#define MCHK_K_PAL_BUGCHECK	0x0090
+
+/*
+ * BIOS32-style PCI interface:
+ */
+
+#ifdef DEBUG_CONF
+# define DBG(args)	printk args
+#else
+# define DBG(args)
+#endif
+
+#ifdef DEBUG_MCHECK
+# define DBGMC(args)	printk args
+#else
+# define DBGMC(args)
+#endif
+
+#define vulp	volatile unsigned long *
+#define vuip	volatile unsigned int  *
+
+static volatile unsigned int T2_mcheck_expected = 0;
+static volatile unsigned int T2_mcheck_taken = 0;
+static unsigned long T2_jd;
+
+
+/*
+ * Given a bus, device, and function number, compute resulting
+ * configuration space address and setup the T2_HAXR2 register
+ * accordingly.  It is therefore not safe to have concurrent
+ * invocations to configuration space access routines, but there
+ * really shouldn't be any need for this.
+ *
+ * Type 0:
+ *
+ *  3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 
+ *  3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *	31:11	Device select bit.
+ * 	10:8	Function number
+ * 	 7:2	Register number
+ *
+ * Type 1:
+ *
+ *  3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 
+ *  3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *	31:24	reserved
+ *	23:16	bus number (8 bits = 128 possible buses)
+ *	15:11	Device number (5 bits)
+ *	10:8	function number
+ *	 7:2	register number
+ *  
+ * Notes:
+ *	The function number selects which function of a multi-function device 
+ *	(e.g., scsi and ethernet).
+ * 
+ *	The register selects a DWORD (32 bit) register offset.  Hence it
+ *	doesn't get shifted by 2 bits as we want to "drop" the bottom two
+ *	bits.
+ */
+static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
+			unsigned char where, unsigned long *pci_addr,
+			unsigned char *type1)
+{
+	unsigned long addr;
+
+	DBG(("mk_conf_addr(bus=%d, dfn=0x%x, where=0x%x,"
+	     " addr=0x%lx, type1=0x%x)\n",
+	     bus, device_fn, where, pci_addr, type1));
+
+	if (bus == 0) {
+		int device = device_fn >> 3;
+
+		/* type 0 configuration cycle: */
+
+		if (device > 8) {
+			DBG(("mk_conf_addr: device (%d)>20, returning -1\n",
+			     device));
+			return -1;
+		}
+
+		*type1 = 0;
+		addr = (0x0800L << device) | ((device_fn & 7) << 8) | (where);
+	} else {
+		/* type 1 configuration cycle: */
+		*type1 = 1;
+		addr = (bus << 16) | (device_fn << 8) | (where);
+	}
+	*pci_addr = addr;
+	DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
+	return 0;
+}
+
+
+static unsigned int conf_read(unsigned long addr, unsigned char type1)
+{
+	unsigned long flags;
+	unsigned int stat0, value;
+	unsigned int t2_cfg = 0; /* to keep gcc quiet */
+
+	save_flags(flags);	/* avoid getting hit by machine check */
+	cli();
+
+	DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1));
+
+#if 0
+	/* reset status register to avoid losing errors: */
+	stat0 = *(vuip)T2_IOCSR;
+	*(vuip)T2_IOCSR = stat0;
+	mb();
+	DBG(("conf_read: T2 IOCSR was 0x%x\n", stat0));
+	/* if Type1 access, must set T2 CFG */
+	if (type1) {
+		t2_cfg = *(vuip)T2_IOC_CFG;
+		mb();
+		*(vuip)T2_IOC_CFG = t2_cfg | 1;
+		DBG(("conf_read: TYPE1 access\n"));
+	}
+	mb();
+	draina();
+#endif
+
+	T2_mcheck_expected = 1;
+	T2_mcheck_taken = 0;
+	mb();
+	/* access configuration space: */
+	value = *(vuip)addr;
+	mb();
+	if (T2_mcheck_taken) {
+		T2_mcheck_taken = 0;
+		value = 0xffffffffU;
+		mb();
+	}
+	T2_mcheck_expected = 0;
+	mb();
+
+#if 0
+	/* if Type1 access, must reset IOC CFG so normal IO space ops work */
+	if (type1) {
+		*(vuip)T2_IOC_CFG = t2_cfg & ~1;
+		mb();
+	}
+#endif
+	DBG(("conf_read(): finished\n"));
+
+	restore_flags(flags);
+	return value;
+}
+
+
+static void conf_write(unsigned long addr, unsigned int value,
+		       unsigned char type1)
+{
+	unsigned long flags;
+	unsigned int stat0;
+	unsigned int t2_cfg = 0; /* to keep gcc quiet */
+
+	save_flags(flags);	/* avoid getting hit by machine check */
+	cli();
+
+#if 0
+	/* reset status register to avoid losing errors: */
+	stat0 = *(vuip)T2_IOCSR;
+	*(vuip)T2_IOCSR = stat0;
+	mb();
+	DBG(("conf_write: T2 ERR was 0x%x\n", stat0));
+	/* if Type1 access, must set T2 CFG */
+	if (type1) {
+		t2_cfg = *(vuip)T2_IOC_CFG;
+		mb();
+		*(vuip)T2_IOC_CFG = t2_cfg | 1;
+		DBG(("conf_write: TYPE1 access\n"));
+	}
+	draina();
+#endif
+
+	T2_mcheck_expected = 1;
+	mb();
+	/* access configuration space: */
+	*(vuip)addr = value;
+	mb();
+	T2_mcheck_expected = 0;
+	mb();
+
+#if 0
+	/* if Type1 access, must reset IOC CFG so normal IO space ops work */
+	if (type1) {
+		*(vuip)T2_IOC_CFG = t2_cfg & ~1;
+		mb();
+	}
+#endif
+	DBG(("conf_write(): finished\n"));
+	restore_flags(flags);
+}
+
+
+int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn,
+			      unsigned char where, unsigned char *value)
+{
+	unsigned long addr = T2_CONF;
+	unsigned long pci_addr;
+	unsigned char type1;
+
+	*value = 0xff;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	addr |= (pci_addr << 5) + 0x00;
+
+	*value = conf_read(addr, type1) >> ((where & 3) * 8);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_read_config_word (unsigned char bus, unsigned char device_fn,
+			      unsigned char where, unsigned short *value)
+{
+	unsigned long addr = T2_CONF;
+	unsigned long pci_addr;
+	unsigned char type1;
+
+	*value = 0xffff;
+
+	if (where & 0x1) {
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	addr |= (pci_addr << 5) + 0x08;
+
+	*value = conf_read(addr, type1) >> ((where & 3) * 8);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn,
+			       unsigned char where, unsigned int *value)
+{
+	unsigned long addr = T2_CONF;
+	unsigned long pci_addr;
+	unsigned char type1;
+
+	*value = 0xffffffff;
+	if (where & 0x3) {
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+	addr |= (pci_addr << 5) + 0x18;
+	*value = conf_read(addr, type1);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn,
+			       unsigned char where, unsigned char value)
+{
+	unsigned long addr = T2_CONF;
+	unsigned long pci_addr;
+	unsigned char type1;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+	addr |= (pci_addr << 5) + 0x00;
+	conf_write(addr, value << ((where & 3) * 8), type1);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_word (unsigned char bus, unsigned char device_fn,
+			       unsigned char where, unsigned short value)
+{
+	unsigned long addr = T2_CONF;
+	unsigned long pci_addr;
+	unsigned char type1;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+	addr |= (pci_addr << 5) + 0x08;
+	conf_write(addr, value << ((where & 3) * 8), type1);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
+				unsigned char where, unsigned int value)
+{
+	unsigned long addr = T2_CONF;
+	unsigned long pci_addr;
+	unsigned char type1;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+	addr |= (pci_addr << 5) + 0x18;
+	conf_write(addr, value << ((where & 3) * 8), type1);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+unsigned long t2_init(unsigned long mem_start, unsigned long mem_end)
+{
+	unsigned int t2_err;
+	struct percpu_struct *cpu;
+	int i;
+
+#if 0
+	/* 
+	 * Set up error reporting.
+	 */
+	t2_err = *(vuip)T2_IOCSR ;
+	t2_err |= (0x1 << 7) ;   /* master abort */
+	*(vuip)T2_IOC_T2_ERR = t2_err ;
+	mb() ;
+#endif
+
+	printk("t2_init: HBASE was 0x%lx\n", *(vulp)T2_HBASE);
+#if 0
+	printk("t2_init: WBASE1=0x%lx WMASK1=0x%lx TBASE1=0x%lx\n",
+	       *(vulp)T2_WBASE1,
+	       *(vulp)T2_WMASK1,
+	       *(vulp)T2_TBASE1);
+	printk("t2_init: WBASE2=0x%lx WMASK2=0x%lx TBASE2=0x%lx\n",
+	       *(vulp)T2_WBASE2,
+	       *(vulp)T2_WMASK2,
+	       *(vulp)T2_TBASE2);
+#endif
+
+	/*
+	 * Set up the PCI->physical memory translation windows.
+	 * For now, window 2 is  disabled.  In the future, we may
+	 * want to use it to do scatter/gather DMA.  Window 1
+	 * goes at 1 GB and is 1 GB large.
+	 */
+
+	/* WARNING!! must correspond to the DMA_WIN params!!! */
+	*(vuip)T2_WBASE1 = 0x400807ffU;
+	*(vuip)T2_WMASK1 = 0x3ff00000U;
+	*(vuip)T2_TBASE1 = 0;
+
+	*(vuip)T2_WBASE2 = 0x0;
+
+	*(vuip)T2_HBASE = 0x0;
+
+	/*
+	 * check ASN in HWRPB for validity, report if bad
+	 */
+	if (hwrpb->max_asn != MAX_ASN) {
+		printk("T2_init: max ASN from HWRPB is bad (0x%lx)\n",
+		       hwrpb->max_asn);
+		hwrpb->max_asn = MAX_ASN;
+	}
+
+	/*
+	 * Finally, clear the T2_HAE_3 register, which gets used
+	 *  for PCI Config Space accesses. That is the way
+	 *  we want to use it, and we do not want to depend on
+	 *  what ARC or SRM might have left behind...
+	 */
+	{
+#if 0
+		printk("T2_init: HAE1 was 0x%lx\n", *(vulp)T2_HAE_1);
+		printk("T2_init: HAE2 was 0x%lx\n", *(vulp)T2_HAE_2);
+		printk("T2_init: HAE3 was 0x%lx\n", *(vulp)T2_HAE_3);
+		printk("T2_init: HAE4 was 0x%lx\n", *(vulp)T2_HAE_4);
+#endif
+#if 0
+		*(vuip)T2_HAE_1 = 0; mb();
+		*(vuip)T2_HAE_2 = 0; mb();
+		*(vuip)T2_HAE_3 = 0; mb();
+		*(vuip)T2_HAE_4 = 0; mb();
+#endif
+	}
+ 
+#if 1
+	if (hwrpb->nr_processors > 1) {
+		printk("T2_init: nr_processors 0x%lx\n",
+		       hwrpb->nr_processors);
+		printk("T2_init: processor_size 0x%lx\n",
+		       hwrpb->processor_size);
+		printk("T2_init: processor_offset 0x%lx\n",
+		       hwrpb->processor_offset);
+
+		cpu = (struct percpu_struct *)
+			((char*)hwrpb + hwrpb->processor_offset);
+
+		for (i = 0; i < hwrpb->nr_processors; i++ ) {
+			printk("T2_init: CPU 0x%x: flags 0x%lx type 0x%lx\n",
+			       i, cpu->flags, cpu->type);
+			cpu = (struct percpu_struct *)
+				((char *)cpu + hwrpb->processor_size);
+		}
+	}
+#endif
+
+	return mem_start;
+}
+
+#define SIC_SEIC (1UL << 33)    /* System Event Clear */
+
+static struct sable_cpu_csr *sable_cpu_regs[4] = {
+	(struct sable_cpu_csr *)CPU0_BASE,
+	(struct sable_cpu_csr *)CPU1_BASE,
+	(struct sable_cpu_csr *)CPU2_BASE,
+	(struct sable_cpu_csr *)CPU3_BASE,
+};
+
+int t2_clear_errors(void)
+{
+	DBGMC(("???????? t2_clear_errors\n"));
+
+	sable_cpu_regs[CPUID]->sic &= ~SIC_SEIC;
+
+	/* 
+	 * clear cpu errors
+	 */
+	sable_cpu_regs[CPUID]->bcce |= sable_cpu_regs[CPUID]->bcce;
+	sable_cpu_regs[CPUID]->cbe  |= sable_cpu_regs[CPUID]->cbe;
+	sable_cpu_regs[CPUID]->bcue |= sable_cpu_regs[CPUID]->bcue;
+	sable_cpu_regs[CPUID]->dter |= sable_cpu_regs[CPUID]->dter;
+
+	*(vulp)T2_CERR1 |= *(vulp)T2_CERR1;
+	*(vulp)T2_PERR1 |= *(vulp)T2_PERR1;
+
+	mb();
+	return 0;
+}
+
+void t2_machine_check(unsigned long vector, unsigned long la_ptr,
+		      struct pt_regs * regs)
+{
+	struct el_t2_logout_header *mchk_header;
+	struct el_t2_procdata_mcheck *mchk_procdata;
+	struct el_t2_sysdata_mcheck *mchk_sysdata;
+	unsigned long * ptr;
+	const char * reason;
+	char buf[128];
+	long i;
+
+	DBGMC(("t2_machine_check: vector=0x%lx la_ptr=0x%lx\n",
+	       vector, la_ptr));
+
+	mchk_header = (struct el_t2_logout_header *)la_ptr;
+
+	DBGMC(("t2_machine_check: susoffset=0x%lx procoffset=0x%lx\n",
+	       mchk_header->elfl_sysoffset, mchk_header->elfl_procoffset));
+
+	mchk_sysdata = (struct el_t2_sysdata_mcheck *)
+	  (la_ptr + mchk_header->elfl_sysoffset);
+	mchk_procdata = (struct el_t2_procdata_mcheck *)
+	  (la_ptr + mchk_header->elfl_procoffset - sizeof(unsigned long)*32);
+
+	DBGMC(("         pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
+	       regs->pc, mchk_header->elfl_size, mchk_header->elfl_procoffset,
+	       mchk_header->elfl_sysoffset));
+	DBGMC(("t2_machine_check: expected %d\n", T2_mcheck_expected));
+
+#ifdef DEBUG_DUMP
+	{
+		unsigned long *ptr;
+		int i;
+
+		ptr = (unsigned long *)la_ptr;
+		for (i = 0; i < mchk_header->elfl_size/sizeof(long); i += 2) {
+			printk(" +%lx %lx %lx\n", i*sizeof(long),
+			       ptr[i], ptr[i+1]);
+		}
+	}
+#endif /* DEBUG_DUMP */
+
+	/*
+	 * Check if machine check is due to a badaddr() and if so,
+	 * ignore the machine check.
+	 */
+	mb();
+	if (T2_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) {
+		DBGMC(("T2 machine check expected\n"));
+		T2_mcheck_taken = 1;
+		t2_clear_errors();
+		T2_mcheck_expected = 0;
+		mb();
+		wrmces(rdmces()|1);/* ??? */
+		draina();
+		return;
+	}
+
+	switch ((unsigned int) mchk_header->elfl_error_type) {
+	case MCHK_K_TPERR:	reason = "tag parity error"; break;
+	case MCHK_K_TCPERR:	reason = "tag control parity error"; break;
+	case MCHK_K_HERR:	reason = "generic hard error"; break;
+	case MCHK_K_ECC_C:	reason = "correctable ECC error"; break;
+	case MCHK_K_ECC_NC:	reason = "uncorrectable ECC error"; break;
+	case MCHK_K_OS_BUGCHECK: reason = "OS-specific PAL bugcheck"; break;
+	case MCHK_K_PAL_BUGCHECK: reason = "callsys in kernel mode"; break;
+	case 0x96: reason = "i-cache read retryable error"; break;
+	case 0x98: reason = "processor detected hard error"; break;
+
+	/* System specific (these are for Alcor, at least): */
+	case 0x203: reason = "system detected uncorrectable ECC error"; break;
+	case 0x205: reason = "parity error detected by T2"; break;
+	case 0x207: reason = "non-existent memory error"; break;
+	case 0x209: reason = "PCI SERR detected"; break;
+	case 0x20b: reason = "PCI data parity error detected"; break;
+	case 0x20d: reason = "PCI address parity error detected"; break;
+	case 0x20f: reason = "PCI master abort error"; break;
+	case 0x211: reason = "PCI target abort error"; break;
+	case 0x213: reason = "scatter/gather PTE invalid error"; break;
+	case 0x215: reason = "flash ROM write error"; break;
+	case 0x217: reason = "IOA timeout detected"; break;
+	case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break;
+	case 0x21b: reason = "EISA fail-safe timer timeout"; break;
+	case 0x21d: reason = "EISA bus time-out"; break;
+	case 0x21f: reason = "EISA software generated NMI"; break;
+	case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break;
+	default:
+		sprintf(buf, "reason for machine-check unknown (0x%x)",
+			(unsigned int) mchk_header->elfl_error_type);
+		reason = buf;
+		break;
+	}
+	wrmces(rdmces()|1);	/* reset machine check pending flag */
+	mb();
+
+	printk(KERN_CRIT "  T2 machine check: %s%s\n",
+	       reason, mchk_header->elfl_retry ? " (retryable)" : "");
+
+	/* dump the the logout area to give all info: */
+
+	ptr = (unsigned long *)la_ptr;
+	for (i = 0; i < mchk_header->elfl_size / sizeof(long); i += 2) {
+		printk(KERN_CRIT " +%8lx %016lx %016lx\n",
+		       i*sizeof(long), ptr[i], ptr[i+1]);
+	}
+}
diff -u --recursive --new-file v2.1.78/linux/arch/alpha/lib/csum_ipv6_magic.S linux/arch/alpha/lib/csum_ipv6_magic.S
--- v2.1.78/linux/arch/alpha/lib/csum_ipv6_magic.S	Thu Feb  6 02:44:41 1997
+++ linux/arch/alpha/lib/csum_ipv6_magic.S	Mon Jan 12 14:49:36 1998
@@ -58,10 +58,14 @@
 	addq	$3,$1,$3	# .. e1 :
 	addq	$0,$3,$0	# e0    :
 	unop			#       :
-	extwl	$0,2,$1		# e0    :
+	extwl	$0,2,$1		# e0    : fold 18-bit value
 	zapnot	$0,3,$0		# .. e1 :
 	addq	$0,$1,$0	# e0    :
-	not	$0,$0		# e1    :
+	unop			#       :
+	extwl	$0,2,$1		# e0    : fold 17-bit value
+	zapnot	$0,3,$0		# .. e1 :
+	addq	$0,$1,$0	# e0    :
+	not	$0,$0		# e1    : and compliment.
 	zapnot	$0,3,$0		# e0    :
 	ret			# .. e1 :
 
diff -u --recursive --new-file v2.1.78/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.78/linux/arch/i386/defconfig	Tue Jan  6 09:37:32 1998
+++ linux/arch/i386/defconfig	Mon Jan 12 15:29:37 1998
@@ -84,7 +84,6 @@
 # CONFIG_NETLINK is not set
 # CONFIG_FIREWALL is not set
 # CONFIG_NET_ALIAS is not set
-# CONFIG_FILTER is not set
 CONFIG_UNIX=y
 CONFIG_INET=y
 # CONFIG_IP_MULTICAST is not set
@@ -267,7 +266,6 @@
 # CONFIG_RTC is not set
 # CONFIG_VIDEO_DEV is not set
 # CONFIG_VIDEO_BT848 is not set
-# CONFIG_VIDEO_BWQCAM is not set
 # CONFIG_VIDEO_PMS is not set
 # CONFIG_NVRAM is not set
 # CONFIG_JOYSTICK is not set
diff -u --recursive --new-file v2.1.78/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.1.78/linux/arch/i386/kernel/entry.S	Tue Dec  2 16:45:17 1997
+++ linux/arch/i386/kernel/entry.S	Sat Jan 10 11:57:29 1998
@@ -71,13 +71,10 @@
  * these are offsets into the task-struct.
  */
 state		=  0
-counter		=  4
-priority	=  8
-flags		= 12
-sigpending	= 16
-dbgreg6		= 44
-dbgreg7		= 48
-exec_domain	= 52
+flags		=  4
+sigpending	=  8
+addr_limit	= 12
+exec_domain	= 16
 
 ENOSYS = 38
 
diff -u --recursive --new-file v2.1.78/linux/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c
--- v2.1.78/linux/arch/i386/kernel/i386_ksyms.c	Sun Dec 21 22:36:12 1997
+++ linux/arch/i386/kernel/i386_ksyms.c	Sat Jan 10 18:04:59 1998
@@ -47,6 +47,20 @@
 EXPORT_SYMBOL(__delay);
 EXPORT_SYMBOL(__const_udelay);
 
+EXPORT_SYMBOL_NOVERS(__get_user_1);
+EXPORT_SYMBOL_NOVERS(__get_user_2);
+EXPORT_SYMBOL_NOVERS(__get_user_4);
+EXPORT_SYMBOL_NOVERS(__put_user_1);
+EXPORT_SYMBOL_NOVERS(__put_user_2);
+EXPORT_SYMBOL_NOVERS(__put_user_4);
+
+EXPORT_SYMBOL(strncpy_from_user);
+EXPORT_SYMBOL(__strncpy_from_user);
+EXPORT_SYMBOL(clear_user);
+EXPORT_SYMBOL(__clear_user);
+EXPORT_SYMBOL(__generic_copy_from_user);
+EXPORT_SYMBOL(__generic_copy_to_user);
+EXPORT_SYMBOL(strlen_user);
 
 #ifdef __SMP__
 EXPORT_SYMBOL(apic_reg);	/* Needed internally for the I386 inlines */
@@ -79,3 +93,4 @@
 EXPORT_SYMBOL(mca_isenabled);
 EXPORT_SYMBOL(mca_isadapter);
 #endif
+
diff -u --recursive --new-file v2.1.78/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.1.78/linux/arch/i386/kernel/smp.c	Fri Jan  2 14:37:01 1998
+++ linux/arch/i386/kernel/smp.c	Mon Jan 12 14:47:46 1998
@@ -55,7 +55,7 @@
 extern unsigned long start_kernel, _etext;
 extern void update_one_process( struct task_struct *p,
 				unsigned long ticks, unsigned long user,
-				unsigned long system);
+				unsigned long system, int cpu);
 /*
  *	Some notes on processor bugs:
  *
@@ -1346,8 +1346,7 @@
 
 		irq_enter(cpu, 0);
 		if (p->pid) {
-
-			update_one_process(p, 1, user, system);
+			update_one_process(p, 1, user, system, cpu);
 
 			p->counter -= 1;
 			if (p->counter < 0) {
@@ -1360,6 +1359,7 @@
 				kstat.cpu_user += user;
 
 			kstat.cpu_system += system;
+			kstat.per_cpu_system[cpu] += system;
 
 		} else {
 #ifdef __SMP_PROF__
@@ -1382,7 +1382,7 @@
 	 * we might want to decouple profiling from the 'long path',
 	 * and do the profiling totally in assembly.
 	 *
-	 * Currently this isnt too much of an issue (performancewise),
+	 * Currently this isnt too much of an issue (performance wise),
 	 * we can take more than 100K local irqs per second on a 100 MHz P5.
 	 */
 }
diff -u --recursive --new-file v2.1.78/linux/arch/i386/lib/Makefile linux/arch/i386/lib/Makefile
--- v2.1.78/linux/arch/i386/lib/Makefile	Sat Nov 29 11:25:09 1997
+++ linux/arch/i386/lib/Makefile	Sat Jan 10 14:51:20 1998
@@ -11,6 +11,6 @@
 endif
 
 L_TARGET = lib.a
-L_OBJS  = checksum.o semaphore.o locks.o delay.o
+L_OBJS  = checksum.o semaphore.o locks.o delay.o usercopy.o getuser.o putuser.o
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.78/linux/arch/i386/lib/getuser.S linux/arch/i386/lib/getuser.S
--- v2.1.78/linux/arch/i386/lib/getuser.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/i386/lib/getuser.S	Mon Jan 12 13:42:52 1998
@@ -0,0 +1,73 @@
+/*
+ * __get_user functions.
+ *
+ * (C) Copyright 1998 Linus Torvalds
+ *
+ * These functions have a non-standard call interface
+ * to make them more efficient, especially as they
+ * return an error value in addition to the "real"
+ * return value.
+ */
+
+/*
+ * __get_user_X
+ *
+ * Inputs:	%eax contains the address
+ *
+ * Outputs:	%eax is error code (0 or -EFAULT)
+ *		%edx contains zero-extended value
+ *
+ * These functions should not modify any other registers,
+ * as they get called from within inline assembly.
+ */
+
+addr_limit = 12
+
+.text
+.align 4
+.globl __get_user_1
+__get_user_1:
+	movl %esp,%edx
+	andl $0xffffe000,%edx
+	cmpl addr_limit(%edx),%eax
+	jae bad_get_user
+1:	movzbl (%eax),%edx
+	xorl %eax,%eax
+	ret
+
+.align 4
+.globl __get_user_2
+__get_user_2:
+	addl $1,%eax
+	movl %esp,%edx
+	jc bad_get_user
+	andl $0xffffe000,%edx
+	cmpl addr_limit(%edx),%eax
+	jae bad_get_user
+2:	movzwl -1(%eax),%edx
+	xorl %eax,%eax
+	ret
+
+.align 4
+.globl __get_user_4
+__get_user_4:
+	addl $3,%eax
+	movl %esp,%edx
+	jc bad_get_user
+	andl $0xffffe000,%edx
+	cmpl addr_limit(%edx),%eax
+	jae bad_get_user
+3:	movl -3(%eax),%edx
+	xorl %eax,%eax
+	ret
+
+bad_get_user:
+	xorl %edx,%edx
+	movl $-14,%eax
+	ret
+
+.section __ex_table,"a"
+	.long 1b,bad_get_user
+	.long 2b,bad_get_user
+	.long 3b,bad_get_user
+.previous
diff -u --recursive --new-file v2.1.78/linux/arch/i386/lib/putuser.S linux/arch/i386/lib/putuser.S
--- v2.1.78/linux/arch/i386/lib/putuser.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/i386/lib/putuser.S	Mon Jan 12 13:37:26 1998
@@ -0,0 +1,71 @@
+/*
+ * __put_user functions.
+ *
+ * (C) Copyright 1998 Linus Torvalds
+ *
+ * These functions have a non-standard call interface
+ * to make them more efficient.
+ */
+
+/*
+ * __put_user_X
+ *
+ * Inputs:	%eax contains the address
+ *		%edx contains the value
+ *
+ * Outputs:	%eax is error code (0 or -EFAULT)
+ *		%ecx is corrupted (will contain "current_task").
+ *
+ * These functions should not modify any other registers,
+ * as they get called from within inline assembly.
+ */
+
+addr_limit = 12
+
+.text
+.align 4
+.globl __put_user_1
+__put_user_1:
+	movl %esp,%ecx
+	andl $0xffffe000,%ecx
+	cmpl addr_limit(%ecx),%eax
+	jae bad_put_user
+1:	movb %dl,(%eax)
+	xorl %eax,%eax
+	ret
+
+.align 4
+.globl __put_user_2
+__put_user_2:
+	addl $1,%eax
+	movl %esp,%ecx
+	jc bad_put_user
+	andl $0xffffe000,%ecx
+	cmpl addr_limit(%ecx),%eax
+	jae bad_put_user
+2:	movw %dx,-1(%eax)
+	xorl %eax,%eax
+	ret
+
+.align 4
+.globl __put_user_4
+__put_user_4:
+	addl $3,%eax
+	movl %esp,%ecx
+	jc bad_put_user
+	andl $0xffffe000,%ecx
+	cmpl addr_limit(%ecx),%eax
+	jae bad_put_user
+3:	movl %edx,-3(%eax)
+	xorl %eax,%eax
+	ret
+
+bad_put_user:
+	movl $-14,%eax
+	ret
+
+.section __ex_table,"a"
+	.long 1b,bad_put_user
+	.long 2b,bad_put_user
+	.long 3b,bad_put_user
+.previous
diff -u --recursive --new-file v2.1.78/linux/arch/i386/lib/usercopy.c linux/arch/i386/lib/usercopy.c
--- v2.1.78/linux/arch/i386/lib/usercopy.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/i386/lib/usercopy.c	Sat Jan 10 21:28:41 1998
@@ -0,0 +1,136 @@
+/* 
+ * User address space access functions.
+ * The non inlined parts of asm-i386/uaccess.h are here.
+ *
+ * Copyright 1997 Andi Kleen <ak@muc.de>
+ * Copyright 1997 Linus Torvalds
+ */
+#include <asm/uaccess.h>
+
+inline unsigned long
+__generic_copy_to_user(void *to, const void *from, unsigned long n)
+{
+	if (access_ok(VERIFY_WRITE, to, n))
+		__copy_user(to,from,n);
+	return n;
+}
+
+inline unsigned long
+__generic_copy_from_user(void *to, const void *from, unsigned long n)
+{
+	if (access_ok(VERIFY_READ, from, n))
+		__copy_user(to,from,n);
+	return n;
+}
+
+
+/*
+ * Copy a null terminated string from userspace.
+ */
+
+#define __do_strncpy_from_user(dst,src,count,res)			   \
+	__asm__ __volatile__(						   \
+		"	testl %1,%1\n"					   \
+		"	jz 2f\n"					   \
+		"0:	lodsb\n"					   \
+		"	stosb\n"					   \
+		"	testb %%al,%%al\n"				   \
+		"	jz 1f\n"					   \
+		"	decl %1\n"					   \
+		"	jnz 0b\n"					   \
+		"1:	subl %1,%0\n"					   \
+		"2:\n"							   \
+		".section .fixup,\"ax\"\n"				   \
+		"3:	movl %2,%0\n"					   \
+		"	jmp 2b\n"					   \
+		".previous\n"						   \
+		".section __ex_table,\"a\"\n"				   \
+		"	.align 4\n"					   \
+		"	.long 0b,3b\n"					   \
+		".previous"						   \
+		: "=d"(res), "=c"(count)				   \
+		: "i"(-EFAULT), "0"(count), "1"(count), "S"(src), "D"(dst) \
+		: "si", "di", "ax", "memory")
+
+long
+__strncpy_from_user(char *dst, const char *src, long count)
+{
+	long res;
+	__do_strncpy_from_user(dst, src, count, res);
+	return res;
+}
+
+long
+strncpy_from_user(char *dst, const char *src, long count)
+{
+	long res = -EFAULT;
+	if (access_ok(VERIFY_READ, src, 1))
+		__do_strncpy_from_user(dst, src, count, res);
+	return res;
+}
+
+
+/*
+ * Zero Userspace
+ */
+
+#define __do_clear_user(addr,size)						\
+	__asm__ __volatile__(						\
+		"0:	rep; stosl\n"					\
+		"	movl %1,%0\n"					\
+		"1:	rep; stosb\n"					\
+		"2:\n"							\
+		".section .fixup,\"ax\"\n"				\
+		"3:	lea 0(%1,%0,4),%0\n"				\
+		"	jmp 2b\n"					\
+		".previous\n"						\
+		".section __ex_table,\"a\"\n"				\
+		"	.align 4\n"					\
+		"	.long 0b,3b\n"					\
+		"	.long 1b,2b\n"					\
+		".previous"						\
+		: "=c"(size)						\
+		: "r"(size & 3), "0"(size / 4), "D"(addr), "a"(0)	\
+		: "di")
+
+unsigned long
+clear_user(void *to, unsigned long n)
+{
+	if (access_ok(VERIFY_WRITE, to, n))
+		__do_clear_user(to, n);
+	return n;
+}
+
+unsigned long
+__clear_user(void *to, unsigned long n)
+{
+	__do_clear_user(to, n);
+	return n;
+}
+
+/*
+ * Return the size of a string (including the ending 0)
+ *
+ * Return 0 for error
+ */
+
+long strlen_user(const char *s)
+{
+	unsigned long res;
+
+	__asm__ __volatile__(
+		"0:	repne; scasb\n"
+		"	notl %0\n"
+		"1:\n"
+		".section .fixup,\"ax\"\n"
+		"2:	xorl %0,%0\n"
+		"	jmp 1b\n"
+		".previous\n"
+		".section __ex_table,\"a\"\n"
+		"	.align 4\n"
+		"	.long 0b,2b\n"
+		".previous"
+		:"=c" (res), "=D" (s)
+		:"1" (s), "a" (0), "0" (-__addr_ok(s)));
+	return res & -__addr_ok(s);
+}
diff -u --recursive --new-file v2.1.78/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- v2.1.78/linux/arch/i386/mm/fault.c	Sun Dec 21 22:36:12 1997
+++ linux/arch/i386/mm/fault.c	Wed Jan  7 12:04:52 1998
@@ -192,11 +192,6 @@
 
 	/* Are we prepared to handle this kernel fault?  */
 	if ((fixup = search_exception_table(regs->eip)) != 0) {
-		printk(KERN_DEBUG "%s: Exception at [<%lx>] cr2=%lx (fixup: %lx)\n",
-			tsk->comm,
-			regs->eip,
-			address,
-			fixup);
 		regs->eip = fixup;
 		goto out;
 	}
diff -u --recursive --new-file v2.1.78/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.1.78/linux/arch/i386/mm/init.c	Fri Jan  2 14:37:01 1998
+++ linux/arch/i386/mm/init.c	Mon Jan 12 14:33:20 1998
@@ -72,7 +72,7 @@
 void show_mem(void)
 {
 	int i,free = 0,total = 0,reserved = 0;
-	int shared = 0;
+	int shared = 0, cached = 0;
 
 	printk("Mem-info:\n");
 	show_free_areas();
@@ -82,6 +82,8 @@
 		total++;
 		if (PageReserved(mem_map+i))
 			reserved++;
+		if (PageSwapCache(mem_map+i))
+			cached++;
 		else if (!atomic_read(&mem_map[i].count))
 			free++;
 		else
@@ -91,6 +93,7 @@
 	printk("%d free pages\n",free);
 	printk("%d reserved pages\n",reserved);
 	printk("%d pages shared\n",shared);
+	printk("%d pages swap cached\n",cached);
 	show_buffers();
 #ifdef CONFIG_NET
 	show_net_buffers();
diff -u --recursive --new-file v2.1.78/linux/arch/mips/kernel/irixelf.c linux/arch/mips/kernel/irixelf.c
--- v2.1.78/linux/arch/mips/kernel/irixelf.c	Fri Dec 19 15:52:51 1997
+++ linux/arch/mips/kernel/irixelf.c	Mon Jan 12 14:52:46 1998
@@ -691,7 +691,6 @@
 	/* OK, This is the point of no return */
 	current->mm->end_data = 0;
 	current->mm->end_code = 0;
-	current->mm->start_mmap = ELF_START_MMAP;
 	current->mm->mmap = NULL;
 	elf_entry = (unsigned int) elf_ex.e_entry;
 	
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/Makefile linux/arch/ppc/Makefile
--- v2.1.78/linux/arch/ppc/Makefile	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/Makefile	Mon Jan 12 15:18:12 1998
@@ -12,10 +12,13 @@
 # Rewritten by Cort Dougan and Paul Mackerras
 #
 
-ifeq ($(CONFIG_PMAC),y)
-KERNELBASE	=0xc0000000
-else
+ifdef CONFIG_CHRP
+# XXX for now
 KERNELBASE	=0x90000000
+KERNELLOAD	=0x90010000
+else
+KERNELBASE	=0xc0000000 
+KERNELLOAD	=0xc0000000 
 endif
 
 # PowerPC (cross) tools
@@ -26,10 +29,11 @@
 endif
 
 ASFLAGS		=
-LINKFLAGS	= -T arch/ppc/vmlinux.lds -Ttext $(KERNELBASE) -Bstatic
+LINKFLAGS	= -T arch/ppc/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic
 CFLAGSINC	= -D__KERNEL__ -I$(TOPDIR)/include -D__powerpc__
 CFLAGS		:= $(CFLAGS) -D__powerpc__ -fsigned-char -msoft-float -pipe \
-		-fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring
+		-fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring \
+		-DKERNELBASE=$(KERNELBASE)
 CPP		= $(CC) -E $(CFLAGS)
 
 ifdef CONFIG_601
@@ -56,30 +60,39 @@
 CORE_FILES += arch/ppc/xmon/x.o
 endif
 
-ifdef CONFIG_PMAC
-MAKEBOOT = $(MAKE) -C arch/$(ARCH)/coffboot
-else
-# PReP and CHRP systems
 MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
-endif
+MAKECOFFBOOT = $(MAKE) -C arch/$(ARCH)/coffboot
+MAKECHRPBOOT = $(MAKE) -C arch/$(ARCH)/chrpboot
 
 checks:
 	@$(MAKE) -C arch/$(ARCH)/kernel checks
 
 BOOT_TARGETS = netboot znetboot zImage floppy install \
-	vmlinux.coff znetboot.initrd zImage.initrd
+	vmlinux.coff znetboot.initrd zImage.initrd vmlinux.coff.initrd
 
 $(BOOT_TARGETS): $(CHECKS) vmlinux
+	@$(MAKECOFFBOOT) $@
 	@$(MAKEBOOT) $@
+	@$(MAKECHRPBOOT) $@
+
+pmac_config:
+	rm -f .config arch/ppc/defconfig
+	ln -s pmac_defconfig arch/ppc/defconfig
+
+prep_config:
+	rm -f .config arch/ppc/defconfig
+	ln -s prep_defconfig arch/ppc/defconfig
 
 tags:
 	etags */*.c include/{asm,linux}/*.h arch/ppc/kernel/*.{c,h}
 
 archclean:
-	rm -f arch/ppc/kernel/mk_defs arch/ppc/kernel/ppc_defs.h
-	rm -f arch/ppc/kernel/checks
+	rm -f arch/ppc/kernel/{mk_defs,ppc_defs.h,find_name,checks}
+	@$(MAKECOFFBOOT) clean
 	@$(MAKEBOOT) clean
+	@$(MAKECHRPBOOT) clean
 
 archdep:
 	$(MAKEBOOT) fastdep
+	$(MAKECHRPBOOT) fastdep
 
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/Makefile linux/arch/ppc/boot/Makefile
--- v2.1.78/linux/arch/ppc/boot/Makefile	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/boot/Makefile	Mon Jan 12 15:18:12 1998
@@ -9,94 +9,118 @@
 # Adapted for PowerPC by Gary Thomas
 # modified by Cort (cort@cs.nmt.edu)
 #
-
 .c.s:
 	$(CC) $(CFLAGS) -S -o $*.s $<
 .s.o:
 	$(AS) -o $*.o $<
 .c.o:
-	$(CC) $(CFLAGS) -c -o $*.o $<
+	$(CC) $(CFLAGS)  -DINITRD_OFFSET=$(IOFF) -DINITRD_SIZE=$(ISZ) -DZIMAGE_OFFSET=$(ZOFF) -DZIMAGE_SIZE=$(ZSZ) -DKERNELBASE=$(KERNELBASE) -c -o $*.o $<
 .S.s:
 	$(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $<
 .S.o:
 	$(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $<
 
 
-ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000
+ZOFF = 0
+ZSZ = 0
+IOFF = 0
+ISZ = 0
+#ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000
+ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00600000
 GZIP_FLAGS = -v9
 
-SYSTEM = $(TOPDIR)/vmlinux
-OBJECTS := head.o inflate.o unzip.o misc.o vreset.o kbd.o
-CFLAGS = -O2 -DSTDC_HEADERS -I$(TOPDIR)/include
+OBJECTS := head.o misc.o vreset.o kbd.o ../coffboot/zlib.o # inflate.o unzip.o
+#OBJECTS := crt0.o start.o vreset.o
+CFLAGS = -O2 -DSTDC_HEADERS -fno-builtin -I$(TOPDIR)/include
 OBJCOPY = $(CROSS_COMPILE)objcopy
 OBJCOPY_ARGS = -O elf32-powerpc
 
+all:	zImage
 
-all:	$(TOPDIR)/zImage
 
+ifeq ($(CONFIG_PREP),y)
 mkprep : mkprep.c
-	$(HOSTCC) $(CFLAGSINC) -o mkprep mkprep.c
-
-piggyback : piggyback.c
-	$(HOSTCC) $(CFLAGSINC) -o piggyback piggyback.c
-
-find_name : find_name.c
-	$(HOSTCC) $(CFLAGSINC) -o find_name find_name.c
-
-floppy: $(TOPDIR)/vmlinux zImage 
-	dd if=$(TOPDIR)/zImage of=/dev/fd0H1440 bs=64b
+	$(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o mkprep mkprep.c
 
-floppy.initrd: $(TOPDIR)/vmlinux zImage 
-	dd if=$(TOPDIR)/zImage.initrd of=/dev/fd0H1440 bs=64b
+floppy: $(TOPDIR)/vmlinux zImage
+	dd if=zImage of=/dev/fd0H1440 bs=64b
 
-znetboot : zImage mkprep
-	cp $(TOPDIR)/zImage /usr/local/tftpboot/vmlinux
+znetboot : zImage
+	cp zImage /tftpboot/zImage.prep
 
-znetboot.initrd : zImage.initrd mkprep
-	cp $(TOPDIR)/zImage.initrd /usr/local/tftpboot/vmlinux
+znetboot.initrd : zImage.initrd
+	cp zImage.initrd /tftpboot/zImage.prep
 
+zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz
 #
-# This really needs to go away.  Perhaps a 
-# zImage.prep and zImage.chrp might be better.
-# Once we're able to get a lilo-ish program 
-# on prep systems this won't be a problem.
-#   -- Cort
+# build the boot loader image and then compute the offset into it
+# for the kernel image
 #
-ifdef CONFIG_CHRP
-zImage: zvmlinux
-	cp zvmlinux $(TOPDIR)/zImage
-
-zImage.initrd: zvmlinux.initrd
-	cp zvmlinux.initrd $(TOPDIR)/zImage.initrd
-
-zvmlinux: $(OBJECTS) $(SYSTEM) find_name vmlinux.gz piggyback
-	./piggyback < vmlinux.gz | $(AS) -o piggy.o
-	$(LD) $(ZLINKFLAGS) -o $@ $(OBJECTS) piggy.o
-	rm -f piggy.o
-else
+	$(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS)
+	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \
+		zvmlinux.tmp $@
+#
+# then with the offset rebuild the bootloader so we know where the kernel is
+#
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \
+		-DZIMAGE_OFFSET=`./offset zvmlinux image` \
+		-DZIMAGE_SIZE=`./size zvmlinux image` -DKERNELBASE=$(KERNELBASE) \
+		-c -o misc.o misc.c
+	$(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS)
+	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \
+		zvmlinux.tmp $@
+	rm zvmlinux.tmp
+
+zvmlinux.initrd: zvmlinux
+	$(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
+	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
+		--add-section=initrd=ramdisk.image.gz \
+		--add-section=image=../coffboot/vmlinux.gz \
+		zvmlinux.initrd.tmp zvmlinux.initrd
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=`./offset zvmlinux.initrd initrd` \
+		-DINITRD_SIZE=`./size zvmlinux.initrd initrd` \
+		-DZIMAGE_OFFSET=`./offset zvmlinux.initrd image` \
+		-DZIMAGE_SIZE=`./size zvmlinux.initrd image` \
+		-DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c
+	$(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
+	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
+		--add-section=initrd=ramdisk.image.gz \
+		--add-section=image=../coffboot/vmlinux.gz \
+		zvmlinux.initrd.tmp $@
+	rm zvmlinux.initrd.tmp
+
 zImage: zvmlinux mkprep
-	mkprep -pbp zvmlinux $(TOPDIR)/zImage
+	./mkprep -pbp zvmlinux zImage
 
 zImage.initrd: zvmlinux.initrd mkprep
-	mkprep -pbp zvmlinux.initrd $(TOPDIR)/zImage.initrd
+	./mkprep -pbp zvmlinux.initrd zImage.initrd
+else
+mkprep:
 
-zvmlinux: $(OBJECTS) $(SYSTEM) mkprep  find_name vmlinux.gz
-	$(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS)
-	$(OBJCOPY) $(OBJCOPY_ARGS) --add-section=image=vmlinux.gz \
-		zvmlinux.tmp $@
-	rm zvmlinux.tmp
+floppy:
+
+znetboot:
+
+znetboot.initrd:
+
+zvmlinux:
+
+zvmlinux.initrd:
+
+zImage:
+
+zImage.initrd:
 endif
 
-vmlinux.gz: $(TOPDIR)/vmlinux
-	dd bs=64k skip=1 if=$(TOPDIR)/vmlinux | gzip -vf9 - > vmlinux.gz
 
-zvmlinux.initrd: zvmlinux
-	$(OBJCOPY) $(OBJCOPY_ARGS) --add-section=initrd=ramdisk.image.gz \
-		zvmlinux $@
+
+# just here to match coffboot/Makefile
+vmlinux.coff:
+
+vmlinux.coff.initrd:
 
 clean:
-	rm -f vmlinux* znetboot* zImage* zvmlinux* mkprep find_name
-	rm -f $(TOPDIR)/{zImage*,znetboot*,zvmlinux*,vmlinux*}
+	rm -f vmlinux* zvmlinux* mkprep zImage*
 
 fastdep:
 	$(TOPDIR)/scripts/mkdep *.[Sch] > .depend
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/crypt.h linux/arch/ppc/boot/crypt.h
--- v2.1.78/linux/arch/ppc/boot/crypt.h	Wed Dec 18 00:49:52 1996
+++ linux/arch/ppc/boot/crypt.h	Wed Dec 31 16:00:00 1969
@@ -1,12 +0,0 @@
-/* crypt.h (dummy version) -- do not perform encryption
- * Hardly worth copyrighting :-)
- */
-
-#ifdef CRYPT
-#  undef CRYPT      /* dummy version */
-#endif
-
-#define RAND_HEAD_LEN  12  /* length of encryption random header */
-
-#define zencode
-#define zdecode
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/find_name.c linux/arch/ppc/boot/find_name.c
--- v2.1.78/linux/arch/ppc/boot/find_name.c	Mon Aug  4 16:25:36 1997
+++ linux/arch/ppc/boot/find_name.c	Wed Dec 31 16:00:00 1969
@@ -1,47 +0,0 @@
-#include <stdio.h>
-#include <asm/page.h>
-#include <sys/mman.h>
-/*
- * Finds a given address in the System.map and prints it out
- * with its neighbors.  -- Cort
- */
-
-void main(int argc, char **argv)
-{
-	unsigned long addr, cmp, i;
-	FILE *f;
-	char *ptr;
-	char s[256], last[256];
-	
-	if ( argc < 2 )
-	{
-		fprintf(stderr, "Usage: %s <address>\n", argv[0]);
-		exit(-1);
-	}
-
-	for ( i = 1 ; argv[i] ; i++ )
-	{
-		sscanf( argv[i], "%0x", &addr );
-		/* adjust if addr is relative to kernelbase */
-		if ( addr < PAGE_OFFSET )
-			addr += PAGE_OFFSET;
-		
-		if ( (f = fopen( "System.map", "r" )) == NULL )
-		{
-			perror("fopen()\n");
-			exit(-1);
-		}
-		
-		while ( !feof(f) )
-		{
-			fgets(s, 255 , f);
-			sscanf( s, "%0x", &cmp );
-			if ( addr < cmp )
-				break;
-			strcpy( last, s);
-		}
-		
-		printf( "%s", last);
-	}		
-	fclose(f);
-}
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/gzip.h linux/arch/ppc/boot/gzip.h
--- v2.1.78/linux/arch/ppc/boot/gzip.h	Mon Aug  4 16:25:36 1997
+++ linux/arch/ppc/boot/gzip.h	Wed Dec 31 16:00:00 1969
@@ -1,284 +0,0 @@
-/* gzip.h -- common declarations for all gzip modules
- * Copyright (C) 1992-1993 Jean-loup Gailly.
- * This is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License, see the file COPYING.
- */
-
-#if defined(__STDC__) || defined(PROTO)
-#  define OF(args)  args
-#else
-#  define OF(args)  ()
-#endif
-
-#ifdef __STDC__
-   typedef void *voidp;
-#else
-   typedef char *voidp;
-#endif
-
-/* I don't like nested includes, but the string functions are used too often */
-#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
-#  include <string.h>
-#  define memzero(s, n)     memset ((s), 0, (n))
-#else
-#  include <strings.h>
-#  define strchr            index 
-#  define strrchr           rindex
-#  define memcpy(d, s, n)   bcopy((s), (d), (n)) 
-#  define memcmp(s1, s2, n) bcmp((s1), (s2), (n)) 
-#  define memzero(s, n)     bzero((s), (n))
-#endif
-
-#if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H)
-#  include <memory.h>
-#endif
-
-#ifndef RETSIGTYPE
-#  define RETSIGTYPE void
-#endif
-
-#define local static
-
-typedef unsigned char  uch;
-typedef unsigned short ush;
-typedef unsigned long  ulg;
-
-/* Return codes from gzip */
-#define OK      0
-#define ERROR   1
-#define WARNING 2
-
-/* Compression methods (see algorithm.doc) */
-#define STORED     0
-#define COMPRESSED 1
-#define PACKED     2
-/* methods 3 to 7 reserved */
-#define DEFLATED   8
-extern int method;         /* compression method */
-
-/* To save memory for 16 bit systems, some arrays are overlayed between
- * the various modules:
- * deflate:  prev+head   window      d_buf  l_buf  outbuf
- * unlzw:    tab_prefix  tab_suffix  stack  inbuf  outbuf
- * inflate:              window             inbuf
- * unpack:               window             inbuf
- * For compression, input is done in window[]. For decompression, output
- * is done in window except for unlzw.
- */
-
-#ifndef	INBUFSIZ
-#  define INBUFSIZ  0x8000  /* input buffer size */
-#endif
-#define INBUF_EXTRA  64     /* required by unlzw() */
-
-#ifndef	OUTBUFSIZ
-#  define OUTBUFSIZ  16384  /* output buffer size */
-#endif
-#define OUTBUF_EXTRA 2048   /* required by unlzw() */
-
-#define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */
-
-#ifdef DYN_ALLOC
-#  define EXTERN(type, array)  extern type * near array
-#  define DECLARE(type, array, size)  type * near array
-#  define ALLOC(type, array, size) { \
-      array = (type*)fcalloc((unsigned)(((size)+1L)/2), 2*sizeof(type)); \
-      if (array == NULL) error("insufficient memory"); \
-   }
-#  define FREE(array) {if (array != NULL) fcfree(array), array=NULL;}
-#else
-#  define EXTERN(type, array)  extern type array[]
-#  define DECLARE(type, array, size)  type array[size]
-#  define ALLOC(type, array, size)
-#  define FREE(array)
-#endif
-
-EXTERN(uch, inbuf);          /* input buffer */
-EXTERN(uch, outbuf);         /* output buffer */
-EXTERN(ush, d_buf);          /* buffer for distances, see trees.c */
-EXTERN(uch, window);         /* Sliding window and suffix table (unlzw) */
-#define tab_suffix window
-#ifndef MAXSEG_64K
-#  define tab_prefix prev    /* hash link (see deflate.c) */
-#  define head (prev+WSIZE)  /* hash head (see deflate.c) */
-   EXTERN(ush, tab_prefix);  /* prefix code (see unlzw.c) */
-#else
-#  define tab_prefix0 prev
-#  define head tab_prefix1
-   EXTERN(ush, tab_prefix0); /* prefix for even codes */
-   EXTERN(ush, tab_prefix1); /* prefix for odd  codes */
-#endif
-
-extern unsigned insize; /* valid bytes in inbuf */
-extern unsigned inptr;  /* index of next byte to be processed in inbuf */
-extern unsigned outcnt; /* bytes in output buffer */
-
-extern long bytes_in;   /* number of input bytes */
-extern long bytes_out;  /* number of output bytes */
-extern long overhead;   /* number of bytes in gzip header */
-
-#define isize bytes_in
-/* for compatibility with old zip sources (to be cleaned) */
-
-extern int  ifd;        /* input file descriptor */
-extern int  ofd;        /* output file descriptor */
-extern char ifname[];   /* input filename or "stdin" */
-extern char ofname[];   /* output filename or "stdout" */
-
-extern ulg time_stamp;  /* original time stamp (modification time) */
-extern long ifile_size; /* input file size, -1 for devices (debug only) */
-
-extern int exit_code;   /* program exit code */
-
-typedef int file_t;     /* Do not use stdio */
-#define NO_FILE  (-1)   /* in memory compression */
-
-
-#define	GZIP_MAGIC     "\037\213" /* Magic header for gzip files, 1F 8B */
-#define	OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */
-#define	PKZIP_MAGIC  "PK\003\004" /* Magic header for pkzip files */
-#define	PACK_MAGIC     "\037\036" /* Magic header for packed files */
-
-/* gzip flag byte */
-#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
-#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
-#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
-#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
-#define COMMENT      0x10 /* bit 4 set: file comment present */
-#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
-#define RESERVED     0xC0 /* bit 6,7:   reserved */
-
-/* internal file attribute */
-#define UNKNOWN (-1)
-#define BINARY  0
-#define ASCII   1
-
-#ifndef WSIZE
-#  define WSIZE 0x8000     /* window size--must be a power of two, and */
-#endif                     /*  at least 32K for zip's deflate method */
-
-#define MIN_MATCH  3
-#define MAX_MATCH  258
-/* The minimum and maximum match lengths */
-
-#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
-/* Minimum amount of lookahead, except at the end of the input file.
- * See deflate.c for comments about the MIN_MATCH+1.
- */
-
-#define MAX_DIST  (WSIZE-MIN_LOOKAHEAD)
-/* In order to simplify the code, particularly on 16 bit machines, match
- * distances are limited to MAX_DIST instead of WSIZE.
- */
-
-extern int decrypt;        /* flag to turn on decryption */
-extern int save_orig_name; /* set if original name must be saved */
-extern int verbose;        /* be verbose (-v) */
-extern int level;          /* compression level */
-extern int test;           /* check .z file integrity */
-extern int to_stdout;      /* output to stdout (-c) */
-
-#define get_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf())
-
-/* put_byte is used for the compressed output, put_char for the
- * uncompressed output. However unlzw() uses window for its
- * suffix table instead of its output buffer, so it does not use put_char.
- * (to be cleaned up).
- */
-#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\
-   flush_outbuf();}
-#define put_char(c) {window[outcnt++]=(uch)(c); if (outcnt==WSIZE)\
-   flush_window();}
-
-/* Output a 16 bit value, lsb first */
-#define put_short(w) \
-{ if (outcnt < OUTBUFSIZ-2) { \
-    outbuf[outcnt++] = (uch) ((w) & 0xff); \
-    outbuf[outcnt++] = (uch) ((ush)(w) >> 8); \
-  } else { \
-    put_byte((uch)((w) & 0xff)); \
-    put_byte((uch)((ush)(w) >> 8)); \
-  } \
-}
-
-/* Output a 32 bit value to the bit stream, lsb first */
-#define put_long(n) { \
-    put_short((n) & 0xffff); \
-    put_short(((ulg)(n)) >> 16); \
-}
-
-#define seekable()    0  /* force sequential output */
-#define translate_eol 0  /* no option -a yet */
-
-#define tolow(c)  (isupper(c) ? (c)-'A'+'a' : (c))    /* force to lower case */
-
-/* Macros for getting two-byte and four-byte header values */
-#define SH(p) ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8))
-#define LG(p) ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16))
-
-/* Diagnostic functions */
-#ifdef DEBUG
-#  define Assert(cond,msg) {if(!(cond)) error(msg);}
-#  define Trace(x) fprintf x
-#  define Tracev(x) {if (verbose) fprintf x ;}
-#  define Tracevv(x) {if (verbose>1) fprintf x ;}
-#  define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
-#  define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
-#else
-#  define Assert(cond,msg)
-#  define Trace(x)
-#  define Tracev(x)
-#  define Tracevv(x)
-#  define Tracec(c,x)
-#  define Tracecv(c,x)
-#endif
-
-	/* in zip.c: */
-extern void zip    OF((int in, int out));
-extern int file_read  OF((char *buf,  unsigned size));
-
-	/* in unzip.c */
-extern void unzip        OF((int in, int out));
-extern int check_zipfile OF((int in));
-
-	/* in unpack.c */
-extern void unpack        OF((int in, int out));
-
-	/* in gzip.c */
-RETSIGTYPE abort_gzip   OF((void));
-
-        /* in deflate.c */
-void lm_init OF((int pack_level, ush *flags));
-ulg  deflate OF((void));
-
-        /* in trees.c */
-void ct_init     OF((ush *attr, int *method));
-int  ct_tally    OF((int dist, int lc));
-ulg  flush_block OF((char *buf, ulg stored_len, int eof));
-
-        /* in bits.c */
-void     bi_init    OF((file_t zipfile));
-void     send_bits  OF((int value, int length));
-unsigned bi_reverse OF((unsigned value, int length));
-void     bi_windup  OF((void));
-void     copy_block OF((char *buf, unsigned len, int header));
-extern   int (*read_buf) OF((char *buf, unsigned size));
-
-	/* in util.c: */
-extern ulg  updcrc        OF((uch *s, unsigned n));
-extern void clear_bufs    OF((void));
-extern int  fill_inbuf    OF((void));
-extern void flush_outbuf  OF((void));
-extern void flush_window  OF((void));
-extern char *strlwr       OF((char *s));
-/*extern char *basename     OF((char *fname));*/
-extern char *add_envopt   OF((int *argcp, char ***argvp, char *env));
-extern void error         OF((char *m));
-extern void warn          OF((char *a, char *b));
-extern void read_error    OF((void));
-extern void write_error   OF((void));
-extern void display_ratio OF((long num, long den));
-extern voidp xmalloc      OF((unsigned int size));
-
-	/* in inflate.c */
-extern int inflate OF((void));
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/head.S linux/arch/ppc/boot/head.S
--- v2.1.78/linux/arch/ppc/boot/head.S	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/boot/head.S	Mon Jan 12 15:18:12 1998
@@ -46,8 +46,13 @@
 	mr	r8,r3
 	lis	r4,start@h
 	ori	r4,r4,start@l
+#if 0	
 	lis	r5,edata@h
 	ori	r5,r5,edata@l
+#else
+	lis	r5,end@h
+	ori	r5,r5,end@l
+#endif		
 	addi	r5,r5,3			/* Round up - just in case */
 	sub	r5,r5,r4		/* Compute # longwords to move */
 	srwi	r5,r5,2
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/inflate.c linux/arch/ppc/boot/inflate.c
--- v2.1.78/linux/arch/ppc/boot/inflate.c	Mon Aug  4 16:25:36 1997
+++ linux/arch/ppc/boot/inflate.c	Wed Dec 31 16:00:00 1969
@@ -1,810 +0,0 @@
-#define DEBG(x)
-#define DEBG1(x)
-/* inflate.c -- Not copyrighted 1992 by Mark Adler
-   version c10p1, 10 January 1993 */
-
-/* 
- * Adapted for booting Linux by Hannu Savolainen 1993
- * based on gzip-1.0.3 
- */
-
-#ifndef lint
-static char rcsid[] = "$Id: inflate.c,v 1.1.1.1 1997/02/25 05:18:11 cort Exp $";
-#endif
-
-#include "gzip.h"
-#define slide window
-
-#if defined(STDC_HEADERS) || defined(HAVE_STDLIB_H)
-#  include <sys/types.h>
-#  include <stdlib.h>
-#endif
-
-struct huft {
-  uch e;                /* number of extra bits or operation */
-  uch b;                /* number of bits in this code or subcode */
-  union {
-    ush n;              /* literal, length base, or distance base */
-    struct huft *t;     /* pointer to next level of table */
-  } v;
-};
-
-
-/* Function prototypes */
-int huft_build OF((unsigned *, unsigned, unsigned, ush *, ush *,
-                   struct huft **, int *));
-int huft_free OF((struct huft *));
-int inflate_codes OF((struct huft *, struct huft *, int, int));
-int inflate_stored OF((void));
-int inflate_fixed OF((void));
-int inflate_dynamic OF((void));
-int inflate_block OF((int *));
-int inflate OF((void));
-
-
-#define wp outcnt
-#define flush_output(w) (wp=(w),flush_window())
-
-/* Tables for deflate from PKZIP's appnote.txt. */
-static unsigned border[] = {    /* Order of the bit length code lengths */
-        16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
-static ush cplens[] = {         /* Copy lengths for literal codes 257..285 */
-        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
-        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
-        /* note: see note #13 above about the 258 in this list. */
-static ush cplext[] = {         /* Extra bits for literal codes 257..285 */
-        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
-        3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */
-static ush cpdist[] = {         /* Copy offsets for distance codes 0..29 */
-        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
-        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
-        8193, 12289, 16385, 24577};
-static ush cpdext[] = {         /* Extra bits for distance codes */
-        0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
-        7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
-        12, 12, 13, 13};
-
-
-ulg bb;                         /* bit buffer */
-unsigned bk;                    /* bits in bit buffer */
-
-ush mask_bits[] = {
-    0x0000,
-    0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
-    0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
-};
-
-#ifdef CRYPT
-  uch cc;
-#  define NEXTBYTE() \
-     (decrypt ? (cc = get_byte(), zdecode(cc), cc) : get_byte())
-#else
-#  define NEXTBYTE()  (uch)get_byte()
-#endif
-#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<<k;k+=8;}}
-#define DUMPBITS(n) {b>>=(n);k-=(n);}
-
-int lbits = 9;          /* bits in base literal/length lookup table */
-int dbits = 6;          /* bits in base distance lookup table */
-
-
-/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
-#define BMAX 16         /* maximum bit length of any code (16 for explode) */
-#define N_MAX 288       /* maximum number of codes in any set */
-
-
-unsigned hufts;         /* track memory usage */
-
-
-int huft_build(b, n, s, d, e, t, m)
-unsigned *b;            /* code lengths in bits (all assumed <= BMAX) */
-unsigned n;             /* number of codes (assumed <= N_MAX) */
-unsigned s;             /* number of simple-valued codes (0..s-1) */
-ush *d;                 /* list of base values for non-simple codes */
-ush *e;                 /* list of extra bits for non-simple codes */
-struct huft **t;        /* result: starting table */
-int *m;                 /* maximum lookup bits, returns actual */
-/* Given a list of code lengths and a maximum table size, make a set of
-   tables to decode that set of codes.  Return zero on success, one if
-   the given code set is incomplete (the tables are still built in this
-   case), two if the input is invalid (all zero length codes or an
-   oversubscribed set of lengths), and three if not enough memory. */
-{
-  unsigned a;                   /* counter for codes of length k */
-  unsigned c[BMAX+1];           /* bit length count table */
-  unsigned f;                   /* i repeats in table every f entries */
-  int g;                        /* maximum code length */
-  int h;                        /* table level */
-  register unsigned i;          /* counter, current code */
-  register unsigned j;          /* counter */
-  register int k;               /* number of bits in current code */
-  int l;                        /* bits per table (returned in m) */
-  register unsigned *p;         /* pointer into c[], b[], or v[] */
-  register struct huft *q;      /* points to current table */
-  struct huft r;                /* table entry for structure assignment */
-  struct huft *u[BMAX];         /* table stack */
-  unsigned v[N_MAX];            /* values in order of bit length */
-  register int w;               /* bits before this table == (l * h) */
-  unsigned x[BMAX+1];           /* bit offsets, then code stack */
-  unsigned *xp;                 /* pointer into x */
-  int y;                        /* number of dummy codes added */
-  unsigned z;                   /* number of entries in current table */
-
-DEBG("huft1 ");
-
-  /* Generate counts for each bit length */
-  memzero(c, sizeof(c));
-  p = b;  i = n;
-  do {
-    c[*p++]++;                  /* assume all entries <= BMAX */
-  } while (--i);
-  if (c[0] == n)                /* null input--all zero length codes */
-  {
-    *t = (struct huft *)NULL;
-    *m = 0;
-    return 0;
-  }
-
-DEBG("huft2 ");
-
-  /* Find minimum and maximum length, bound *m by those */
-  l = *m;
-  for (j = 1; j <= BMAX; j++)
-    if (c[j])
-      break;
-  k = j;                        /* minimum code length */
-  if ((unsigned)l < j)
-    l = j;
-  for (i = BMAX; i; i--)
-    if (c[i])
-      break;
-  g = i;                        /* maximum code length */
-  if ((unsigned)l > i)
-    l = i;
-  *m = l;
-
-DEBG("huft3 ");
-
-  /* Adjust last length count to fill out codes, if needed */
-  for (y = 1 << j; j < i; j++, y <<= 1)
-    if ((y -= c[j]) < 0)
-      return 2;                 /* bad input: more codes than bits */
-  if ((y -= c[i]) < 0)
-    return 2;
-  c[i] += y;
-
-DEBG("huft4 ");
-
-  /* Generate starting offsets into the value table for each length */
-  x[1] = j = 0;
-  p = c + 1;  xp = x + 2;
-  while (--i) {                 /* note that i == g from above */
-    *xp++ = (j += *p++);
-  }
-
-DEBG("huft5 ");
-
-  /* Make a table of values in order of bit lengths */
-  p = b;  i = 0;
-  do {
-    if ((j = *p++) != 0)
-      v[x[j]++] = i;
-  } while (++i < n);
-
-DEBG("h6 ");
-
-  /* Generate the Huffman codes and for each, make the table entries */
-  x[0] = i = 0;                 /* first Huffman code is zero */
-  p = v;                        /* grab values in bit order */
-  h = -1;                       /* no tables yet--level -1 */
-  w = -l;                       /* bits decoded == (l * h) */
-  u[0] = (struct huft *)NULL;   /* just to keep compilers happy */
-  q = (struct huft *)NULL;      /* ditto */
-  z = 0;                        /* ditto */
-DEBG("h6a ");
-
-  /* go through the bit lengths (k already is bits in shortest code) */
-  for (; k <= g; k++)
-  {
-DEBG("h6b ");
-    a = c[k];
-    while (a--)
-    {
-DEBG("h6b1 ");
-      /* here i is the Huffman code of length k bits for value *p */
-      /* make tables up to required level */
-      while (k > w + l)
-      {
-DEBG1("1 ");
-        h++;
-        w += l;                 /* previous table always l bits */
-
-        /* compute minimum size table less than or equal to l bits */
-        z = (z = g - w) > (unsigned)l ? l : z;  /* upper limit on table size */
-        if ((f = 1 << (j = k - w)) > a + 1)     /* try a k-w bit table */
-        {                       /* too few codes for k-w bit table */
-DEBG1("2 ");
-          f -= a + 1;           /* deduct codes from patterns left */
-          xp = c + k;
-          while (++j < z)       /* try smaller tables up to z bits */
-          {
-            if ((f <<= 1) <= *++xp)
-              break;            /* enough codes to use up j bits */
-            f -= *xp;           /* else deduct codes from patterns */
-          }
-        }
-DEBG1("3 ");
-        z = 1 << j;             /* table entries for j-bit table */
-
-        /* allocate and link in new table */
-        q = (struct huft *)malloc((z + 1)*sizeof(struct huft));
-DEBG1("4 ");
-        hufts += z + 1;         /* track memory usage */
-        *t = q + 1;             /* link to list for huft_free() */
-        *(t = &(q->v.t)) = (struct huft *)NULL;
-        u[h] = ++q;             /* table starts after link */
-
-DEBG1("5 ");
-        /* connect to last table, if there is one */
-        if (h)
-        {
-          x[h] = i;             /* save pattern for backing up */
-          r.b = (uch)l;         /* bits to dump before this table */
-          r.e = (uch)(16 + j);  /* bits in this table */
-          r.v.t = q;            /* pointer to this table */
-          j = i >> (w - l);     /* (get around Turbo C bug) */
-          u[h-1][j] = r;        /* connect to last table */
-        }
-DEBG1("6 ");
-      }
-DEBG("h6c ");
-
-      /* set up table entry in r */
-      r.b = (uch)(k - w);
-      if (p >= v + n)
-        r.e = 99;               /* out of values--invalid code */
-      else if (*p < s)
-      {
-        r.e = (uch)(*p < 256 ? 16 : 15);    /* 256 is end-of-block code */
-        r.v.n = *p++;           /* simple code is just the value */
-      }
-      else
-      {
-        r.e = (uch)e[*p - s];   /* non-simple--look up in lists */
-        r.v.n = d[*p++ - s];
-      }
-DEBG("h6d ");
-
-      /* fill code-like entries with r */
-      f = 1 << (k - w);
-      for (j = i >> w; j < z; j += f)
-        q[j] = r;
-
-      /* backwards increment the k-bit code i */
-      for (j = 1 << (k - 1); i & j; j >>= 1)
-        i ^= j;
-      i ^= j;
-
-      /* backup over finished tables */
-      while ((i & ((1 << w) - 1)) != x[h])
-      {
-        h--;                    /* don't need to update q */
-        w -= l;
-      }
-DEBG("h6e ");
-    }
-DEBG("h6f ");
-  }
-
-DEBG("huft7 ");
-
-  /* Return true (1) if we were given an incomplete table */
-  return y != 0 && g != 1;
-}
-
-
-
-int huft_free(t)
-struct huft *t;         /* table to free */
-/* Free the malloc'ed tables built by huft_build(), which makes a linked
-   list of the tables it made, with the links in a dummy first entry of
-   each table. */
-{
-  register struct huft *p, *q;
-
-
-  /* Go through linked list, freeing from the malloced (t[-1]) address. */
-  p = t;
-  while (p != (struct huft *)NULL)
-  {
-    q = (--p)->v.t;
-    free(p);
-    p = q;
-  } 
-  return 0;
-}
-
-
-int inflate_codes(tl, td, bl, bd)
-struct huft *tl, *td;   /* literal/length and distance decoder tables */
-int bl, bd;             /* number of bits decoded by tl[] and td[] */
-/* inflate (decompress) the codes in a deflated (compressed) block.
-   Return an error code or zero if it all goes ok. */
-{
-  register unsigned e;  /* table entry flag/number of extra bits */
-  unsigned n, d;        /* length and index for copy */
-  unsigned w;           /* current window position */
-  struct huft *t;       /* pointer to table entry */
-  unsigned ml, md;      /* masks for bl and bd bits */
-  register ulg b;       /* bit buffer */
-  register unsigned k;  /* number of bits in bit buffer */
-
-
-  /* make local copies of globals */
-  b = bb;                       /* initialize bit buffer */
-  k = bk;
-  w = wp;                       /* initialize window position */
-
-  /* inflate the coded data */
-  ml = mask_bits[bl];           /* precompute masks for speed */
-  md = mask_bits[bd];
-  for (;;)                      /* do until end of block */
-  {
-    NEEDBITS((unsigned)bl)
-    if ((e = (t = tl + ((unsigned)b & ml))->e) > 16)
-      do {
-        if (e == 99)
-          return 1;
-        DUMPBITS(t->b)
-        e -= 16;
-        NEEDBITS(e)
-      } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
-    DUMPBITS(t->b)
-    if (e == 16)                /* then it's a literal */
-    {
-      slide[w++] = (uch)t->v.n;
-      if (w == WSIZE)
-      {
-        flush_output(w);
-        w = 0;
-      }
-    }
-    else                        /* it's an EOB or a length */
-    {
-      /* exit if end of block */
-      if (e == 15)
-        break;
-
-      /* get length of block to copy */
-      NEEDBITS(e)
-      n = t->v.n + ((unsigned)b & mask_bits[e]);
-      DUMPBITS(e);
-
-      /* decode distance of block to copy */
-      NEEDBITS((unsigned)bd)
-      if ((e = (t = td + ((unsigned)b & md))->e) > 16)
-        do {
-          if (e == 99)
-            return 1;
-          DUMPBITS(t->b)
-          e -= 16;
-          NEEDBITS(e)
-        } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
-      DUMPBITS(t->b)
-      NEEDBITS(e)
-      d = w - t->v.n - ((unsigned)b & mask_bits[e]);
-      DUMPBITS(e)
-
-      /* do the copy */
-      do {
-        n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e);
-#if !defined(NOMEMCPY) && !defined(DEBUG)
-        if (w - d >= e)         /* (this test assumes unsigned comparison) */
-        {
-          memcpy(slide + w, slide + d, e);
-          w += e;
-          d += e;
-        }
-        else                      /* do it slow to avoid memcpy() overlap */
-#endif /* !NOMEMCPY */
-          do {
-            slide[w++] = slide[d++];
-          } while (--e);
-        if (w == WSIZE)
-        {
-          flush_output(w);
-          w = 0;
-        }
-      } while (n);
-    }
-  }
-
-
-  /* restore the globals from the locals */
-  wp = w;                       /* restore global window pointer */
-  bb = b;                       /* restore global bit buffer */
-  bk = k;
-
-  /* done */
-  return 0;
-}
-
-
-
-int inflate_stored()
-/* "decompress" an inflated type 0 (stored) block. */
-{
-  unsigned n;           /* number of bytes in block */
-  unsigned w;           /* current window position */
-  register ulg b;       /* bit buffer */
-  register unsigned k;  /* number of bits in bit buffer */
-
-DEBG("<stor");
-
-  /* make local copies of globals */
-  b = bb;                       /* initialize bit buffer */
-  k = bk;
-  w = wp;                       /* initialize window position */
-
-
-  /* go to byte boundary */
-  n = k & 7;
-  DUMPBITS(n);
-
-
-  /* get the length and its complement */
-  NEEDBITS(16)
-  n = ((unsigned)b & 0xffff);
-  DUMPBITS(16)
-  NEEDBITS(16)
-  if (n != (unsigned)((~b) & 0xffff))
-    return 1;                   /* error in compressed data */
-  DUMPBITS(16)
-
-
-  /* read and output the compressed data */
-  while (n--)
-  {
-    NEEDBITS(8)
-    slide[w++] = (uch)b;
-    if (w == WSIZE)
-    {
-      flush_output(w);
-      w = 0;
-    }
-    DUMPBITS(8)
-  }
-
-
-  /* restore the globals from the locals */
-  wp = w;                       /* restore global window pointer */
-  bb = b;                       /* restore global bit buffer */
-  bk = k;
-
-  DEBG(">");
-  return 0;
-}
-
-
-
-int inflate_fixed()
-/* decompress an inflated type 1 (fixed Huffman codes) block.  We should
-   either replace this with a custom decoder, or at least precompute the
-   Huffman tables. */
-{
-  int i;                /* temporary variable */
-  struct huft *tl;      /* literal/length code table */
-  struct huft *td;      /* distance code table */
-  int bl;               /* lookup bits for tl */
-  int bd;               /* lookup bits for td */
-  unsigned l[288];      /* length list for huft_build */
-
-DEBG("<fix");
-
-  /* set up literal table */
-  for (i = 0; i < 144; i++)
-    l[i] = 8;
-  for (; i < 256; i++)
-    l[i] = 9;
-  for (; i < 280; i++)
-    l[i] = 7;
-  for (; i < 288; i++)          /* make a complete, but wrong code set */
-    l[i] = 8;
-  bl = 7;
-  if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0)
-    return i;
-
-
-  /* set up distance table */
-  for (i = 0; i < 30; i++)      /* make an incomplete code set */
-    l[i] = 5;
-  bd = 5;
-  if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1)
-  {
-    huft_free(tl);
-
-    DEBG(">");
-    return i;
-  }
-
-
-  /* decompress until an end-of-block code */
-  if (inflate_codes(tl, td, bl, bd))
-    return 1;
-
-
-  /* free the decoding tables, return */
-  huft_free(tl);
-  huft_free(td);
-  return 0;
-}
-
-
-
-int inflate_dynamic()
-/* decompress an inflated type 2 (dynamic Huffman codes) block. */
-{
-  int i;                /* temporary variables */
-  unsigned j;
-  unsigned l;           /* last length */
-  unsigned m;           /* mask for bit lengths table */
-  unsigned n;           /* number of lengths to get */
-  struct huft *tl;      /* literal/length code table */
-  struct huft *td;      /* distance code table */
-  int bl;               /* lookup bits for tl */
-  int bd;               /* lookup bits for td */
-  unsigned nb;          /* number of bit length codes */
-  unsigned nl;          /* number of literal/length codes */
-  unsigned nd;          /* number of distance codes */
-#ifdef PKZIP_BUG_WORKAROUND
-  unsigned ll[288+32];  /* literal/length and distance code lengths */
-#else
-  unsigned ll[286+30];  /* literal/length and distance code lengths */
-#endif
-  register ulg b;       /* bit buffer */
-  register unsigned k;  /* number of bits in bit buffer */
-
-DEBG("<dyn");
-
-  /* make local bit buffer */
-  b = bb;
-  k = bk;
-
-
-  /* read in table lengths */
-  NEEDBITS(5)
-  nl = 257 + ((unsigned)b & 0x1f);      /* number of literal/length codes */
-  DUMPBITS(5)
-  NEEDBITS(5)
-  nd = 1 + ((unsigned)b & 0x1f);        /* number of distance codes */
-  DUMPBITS(5)
-  NEEDBITS(4)
-  nb = 4 + ((unsigned)b & 0xf);         /* number of bit length codes */
-  DUMPBITS(4)
-#ifdef PKZIP_BUG_WORKAROUND
-  if (nl > 288 || nd > 32)
-#else
-  if (nl > 286 || nd > 30)
-#endif
-    return 1;                   /* bad lengths */
-
-DEBG("dyn1 ");
-
-  /* read in bit-length-code lengths */
-  for (j = 0; j < nb; j++)
-  {
-    NEEDBITS(3)
-    ll[border[j]] = (unsigned)b & 7;
-    DUMPBITS(3)
-  }
-  for (; j < 19; j++)
-    ll[border[j]] = 0;
-
-DEBG("dyn2 ");
-
-  /* build decoding table for trees--single level, 7 bit lookup */
-  bl = 7;
-  if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0)
-  {
-    if (i == 1)
-      huft_free(tl);
-    return i;                   /* incomplete code set */
-  }
-
-DEBG("dyn3 ");
-
-  /* read in literal and distance code lengths */
-  n = nl + nd;
-  m = mask_bits[bl];
-  i = l = 0;
-  while ((unsigned)i < n)
-  {
-    NEEDBITS((unsigned)bl)
-    j = (td = tl + ((unsigned)b & m))->b;
-    DUMPBITS(j)
-    j = td->v.n;
-    if (j < 16)                 /* length of code in bits (0..15) */
-      ll[i++] = l = j;          /* save last length in l */
-    else if (j == 16)           /* repeat last length 3 to 6 times */
-    {
-      NEEDBITS(2)
-      j = 3 + ((unsigned)b & 3);
-      DUMPBITS(2)
-      if ((unsigned)i + j > n)
-        return 1;
-      while (j--)
-        ll[i++] = l;
-    }
-    else if (j == 17)           /* 3 to 10 zero length codes */
-    {
-      NEEDBITS(3)
-      j = 3 + ((unsigned)b & 7);
-      DUMPBITS(3)
-      if ((unsigned)i + j > n)
-        return 1;
-      while (j--)
-        ll[i++] = 0;
-      l = 0;
-    }
-    else                        /* j == 18: 11 to 138 zero length codes */
-    {
-      NEEDBITS(7)
-      j = 11 + ((unsigned)b & 0x7f);
-      DUMPBITS(7)
-      if ((unsigned)i + j > n)
-        return 1;
-      while (j--)
-        ll[i++] = 0;
-      l = 0;
-    }
-  }
-
-DEBG("dyn4 ");
-
-  /* free decoding table for trees */
-  huft_free(tl);
-
-DEBG("dyn5 ");
-
-  /* restore the global bit buffer */
-  bb = b;
-  bk = k;
-
-DEBG("dyn5a ");
-
-  /* build the decoding tables for literal/length and distance codes */
-  bl = lbits;
-  if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0)
-  {
-DEBG("dyn5b ");
-    if (i == 1) {
-      error(" incomplete literal tree\n");
-      huft_free(tl);
-    }
-    return i;                   /* incomplete code set */
-  }
-DEBG("dyn5c ");
-  bd = dbits;
-  if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0)
-  {
-DEBG("dyn5d ");
-    if (i == 1) {
-      error(" incomplete distance tree\n");
-#ifdef PKZIP_BUG_WORKAROUND
-      i = 0;
-    }
-#else
-      huft_free(td);
-    }
-    huft_free(tl);
-    return i;                   /* incomplete code set */
-#endif
-  }
-
-DEBG("dyn6 ");
-
-  /* decompress until an end-of-block code */
-  if (inflate_codes(tl, td, bl, bd))
-    return 1;
-
-DEBG("dyn7 ");
-
-  /* free the decoding tables, return */
-  huft_free(tl);
-  huft_free(td);
-
-  DEBG(">");
-  return 0;
-}
-
-
-
-int inflate_block(e)
-int *e;                 /* last block flag */
-/* decompress an inflated block */
-{
-  unsigned t;           /* block type */
-  register ulg b;       /* bit buffer */
-  register unsigned k;  /* number of bits in bit buffer */
-
-  DEBG("<blk");
-
-  /* make local bit buffer */
-  b = bb;
-  k = bk;
-
-
-  /* read in last block bit */
-  NEEDBITS(1)
-  *e = (int)b & 1;
-  DUMPBITS(1)
-
-
-  /* read in block type */
-  NEEDBITS(2)
-  t = (unsigned)b & 3;
-  DUMPBITS(2)
-
-
-  /* restore the global bit buffer */
-  bb = b;
-  bk = k;
-
-  /* inflate that block type */
-  if (t == 2)
-    return inflate_dynamic();
-  if (t == 0)
-    return inflate_stored();
-  if (t == 1)
-    return inflate_fixed();
-
-  DEBG(">");
-
-  /* bad block type */
-  return 2;
-}
-
-
-
-int inflate()
-/* decompress an inflated entry */
-{
-  int e;                /* last block flag */
-  int r;                /* result code */
-  unsigned h;           /* maximum struct huft's malloc'ed */
-
-
-  /* initialize window, bit buffer */
-  wp = 0;
-  bk = 0;
-  bb = 0;
-
-
-  /* decompress until the last block */
-  h = 0;
-  do {
-    hufts = 0;
-    if ((r = inflate_block(&e)) != 0)
-      return r;
-    if (hufts > h)
-      h = hufts;
-  } while (!e);
-
-  /* Undo too much lookahead. The next read will be byte aligned so we
-   * can discard unused bits in the last meaningful byte.
-   */
-  while (bk >= 8) {
-    bk -= 8;
-    inptr--;
-  }
-
-  /* flush out slide */
-  flush_output(wp);
-
-
-  /* return success */
-#ifdef DEBUG
-  fprintf(stderr, "<%u> ", h);
-#endif /* DEBUG */
-  return 0;
-}
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/kbd.c linux/arch/ppc/boot/kbd.c
--- v2.1.78/linux/arch/ppc/boot/kbd.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/boot/kbd.c	Mon Jan 12 15:18:13 1998
@@ -1,19 +1,10 @@
-/* Keyboard handler */
 
-#include <../drivers/char/defkeymap.c> /* yeah I know it's bad */
+#include <linux/keyboard.h>
 
-#define	L		0x0001	/* locking function */
-#define	SHF		0x0002	/* keyboard shift */
-#define	ALT		0x0004	/* alternate shift -- alternate chars */
-#define	NUM		0x0008	/* numeric shift  cursors vs. numeric */
-#define	CTL		0x0010	/* control shift  -- allows ctl function */
-#define	CPS		0x0020	/* caps shift -- swaps case of letter */
-#define	ASCII		0x0040	/* ascii code for this key */
-#define	STP		0x0080	/* stop output */
-#define	FUNC		0x0100	/* function key */
-#define	SCROLL		0x0200	/* scroll lock key */
+#include <../drivers/char/defkeymap.c>	/* yeah I know it's bad */
 
-unsigned char shfts, ctls, alts, caps, num, stp;
+
+unsigned char shfts, ctls, alts, caps;
 
 #define	KBDATAP		0x60	/* kbd data port */
 #define	KBSTATUSPORT	0x61	/* kbd status */
@@ -21,37 +12,15 @@
 #define	KBINRDY		0x01
 #define	KBOUTRDY	0x02
 
-#define _x__ 0x00  /* Unknown / unmapped */
 
-const unsigned short action[] = {
-	0,     ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,	/* scan  0- 7 */
-	ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,	/* scan  8-15 */
-	ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,	/* scan 16-23 */
-	ASCII, ASCII, ASCII, ASCII, ASCII,   CTL, ASCII, ASCII,	/* scan 24-31 */
-	ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,	/* scan 32-39 */
-	ASCII, ASCII,   SHF, ASCII, ASCII, ASCII, ASCII, ASCII,	/* scan 40-47 */
-	ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,   SHF, ASCII,	/* scan 48-55 */
-	  ALT, ASCII,   CPS,  FUNC,  FUNC,  FUNC,  FUNC,  FUNC,	/* scan 56-63 */
-	 FUNC,  FUNC,  FUNC,  FUNC,  FUNC,   NUM,SCROLL, ASCII,	/* scan 64-71 */
-	ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,	/* scan 72-79 */
-	ASCII, ASCII, ASCII, ASCII,     0,     0,     0,     0,	/* scan 80-87 */
-	0,0,0,0,0,0,0,0,	/* scan 88-95 */
-	0,0,0,0,0,0,0,0,	/* scan 96-103 */
-	0,0,0,0,0,0,0,0,	/* scan 104-111 */
-	0,0,0,0,0,0,0,0,	/* scan 112-119 */
-	0,0,0,0,0,0,0,0,	/* scan 120-127 */
-};
-
-static int
-kbd(noblock)
-	int noblock;
+static int kbd(int noblock)
 {
-	unsigned char dt, brk, act;
-	int first = 1;	
+	unsigned char dt, brk, val;
+	unsigned code;
 loop:
 	if (noblock) {
-		if ((inb(KBSTATP) & KBINRDY) == 0)
-			return (-1);
+	    if ((inb(KBSTATP) & KBINRDY) == 0)
+		return (-1);
 	} else while((inb(KBSTATP) & KBINRDY) == 0) ;
 
 	dt = inb(KBDATAP);
@@ -59,72 +28,102 @@
 	brk = dt & 0x80;	/* brk == 1 on key release */
 	dt = dt & 0x7f;		/* keycode */
 
-	act = action[dt];
-	if (/*act&SHF*/ dt == 54)
-		shfts = brk ? 0 : 1;
-	if (/*act&ALT*/ dt == 48)
-		alts = brk ? 0 : 1;
-	if (/*act&NUM*/ dt == 69)
-		if (act&L) {
-			/* NUM lock */
-			if(!brk)
-				num = !num;
-		} else
-			num = brk ? 0 : 1;
-	if (/*act&CTL*/ dt == 29)
-		ctls = brk ? 0 : 1;
-	if (/*act&CPS*/ dt == 58)
-		if (act&L) {
-			/* CAPS lock */
-			if(!brk)
-				caps = !caps;
-		} else
-			caps = brk ? 0 : 1;
-	if (0/*act&STP*/)
-		if (act&L) {
-			if(!brk)
-				stp = !stp;
-		} else
-			stp = brk ? 0 : 1;
-
-	if ((act&ASCII) && !brk) {
-		unsigned char chr;
-		if (shfts)
-			chr = shift_map[dt];
-		else if (ctls)
-			chr = ctrl_map[dt];
-		else
-			chr = plain_map[dt];
+	if (shfts)
+	    code = shift_map[dt];
+	else if (ctls)
+	    code = ctrl_map[dt];
+	else
+	    code = plain_map[dt];
+
+	val = KVAL(code);
+	switch (KTYP(code) & 0x0f) {
+	    case KT_LATIN:
+		if (brk)
+		    break;
 		if (alts)
-			chr |= 0x80;
+		    val |= 0x80;
+		if (val == 0x7f)	/* map delete to backspace */
+		    val = '\b';
+		return val;
+
+	    case KT_LETTER:
+		if (brk)
+		    break;
+		if (caps)
+		    val -= 'a'-'A';
+		return val;
+
+	    case KT_SPEC:
+		if (brk)
+		    break;
+		if (val == KVAL(K_CAPS))
+		    caps = !caps;
+		else if (val == KVAL(K_ENTER)) {
+enter:		    /* Wait for key up */
+		    while (1) {
+			while((inb(KBSTATP) & KBINRDY) == 0) ;
+			dt = inb(KBDATAP);
+			if (dt & 0x80) /* key up */ break;
+		    }
+		    return 10;
+		}
+		break;
+
+	    case KT_PAD:
+		if (brk)
+		    break;
+		if (val < 10)
+		    return val;
+		if (val == KVAL(K_PENTER))
+		    goto enter;
+		break;
+
+	    case KT_SHIFT:
+		switch (val) {
+		    case KG_SHIFT:
+		    case KG_SHIFTL:
+		    case KG_SHIFTR:
+			shfts = brk ? 0 : 1;
+			break;
+		    case KG_ALT:
+		    case KG_ALTGR:
+			alts = brk ? 0 : 1;
+			break;
+		    case KG_CTRL:
+		    case KG_CTRLL:
+		    case KG_CTRLR:
+			ctls = brk ? 0 : 1;
+			break;
+		}
+		break;
 
-		if (caps && (chr >= 'a' && chr <= 'z'))
-			chr -= 'a' - 'A' ;
-		if ( chr == 0x01 ) chr = '\n'; /* hack */
-#define CTRL(s) (s & 0x1F)			
-		if ((chr == '\r') || (chr == '\n') || (chr == CTRL('A')) || (chr == CTRL('S')))
-		{
-			/* Wait for key up */
-			while (1)
-			{
-				while((inb(KBSTATP) & KBINRDY) == 0) ;
-				dt = inb(KBDATAP);
-				if (dt & 0x80) /* key up */ break;
-			}
+	    case KT_LOCK:
+		switch (val) {
+		    case KG_SHIFT:
+		    case KG_SHIFTL:
+		    case KG_SHIFTR:
+			if (brk)
+			    shfts = !shfts;
+			break;
+		    case KG_ALT:
+		    case KG_ALTGR:
+			if (brk)
+			    alts = !alts;
+			break;
+		    case KG_CTRL:
+		    case KG_CTRLL:
+		    case KG_CTRLR:
+			if (brk)
+			    ctls = !ctls;
+			break;
 		}
-		return (chr);
+		break;
 	}
-	if (first && brk) return (0);  /* Ignore initial 'key up' codes */
+	if (brk) return (0);  /* Ignore initial 'key up' codes */
 	goto loop;
 }
 
-static
-scankbd(void) {
-	return (kbd(1) != -1);
-}
-
-static 
-kbdreset(void)
+static void kbdreset(void)
 {
 	unsigned char c;
 	int i;
@@ -151,7 +150,7 @@
 
 static int kbd_reset = 0;
 
-CRT_getc(void)
+int CRT_getc(void)
 {
 	int c;
 	if (!kbd_reset) {kbdreset(); kbd_reset++; }
@@ -159,7 +158,7 @@
 	return(c);
 }
 
-CRT_tstc(void)
+int CRT_tstc(void)
 {
 	if (!kbd_reset) {kbdreset(); kbd_reset++; }
 	return ((inb(KBSTATP) & KBINRDY) != 0);
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/lzw.h linux/arch/ppc/boot/lzw.h
--- v2.1.78/linux/arch/ppc/boot/lzw.h	Wed Dec 18 00:49:52 1996
+++ linux/arch/ppc/boot/lzw.h	Wed Dec 31 16:00:00 1969
@@ -1,42 +0,0 @@
-/* lzw.h -- define the lzw functions.
- * Copyright (C) 1992-1993 Jean-loup Gailly.
- * This is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License, see the file COPYING.
- */
-
-#if !defined(OF) && defined(lint)
-#  include "gzip.h"
-#endif
-
-#ifndef BITS
-#  define BITS 16
-#endif
-#define INIT_BITS 9              /* Initial number of bits per code */
-
-#define	LZW_MAGIC  "\037\235"   /* Magic header for lzw files, 1F 9D */
-
-#define BIT_MASK    0x1f /* Mask for 'number of compression bits' */
-/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free.
- * It's a pity that old uncompress does not check bit 0x20. That makes
- * extension of the format actually undesirable because old compress
- * would just crash on the new format instead of giving a meaningful
- * error message. It does check the number of bits, but it's more
- * helpful to say "unsupported format, get a new version" than
- * "can only handle 16 bits".
- */
-
-#define BLOCK_MODE  0x80
-/* Block compression: if table is full and compression rate is dropping,
- * clear the dictionary.
- */
-
-#define LZW_RESERVED 0x60 /* reserved bits */
-
-#define	CLEAR  256       /* flush the dictionary */
-#define FIRST  (CLEAR+1) /* first free entry */
-
-extern int maxbits;      /* max bits per code for LZW */
-extern int block_mode;   /* block compress mode -C compatible with 2.0 */
-
-extern void lzw    OF((int in, int out));
-extern void unlzw  OF((int in, int out));
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/misc.c linux/arch/ppc/boot/misc.c
--- v2.1.78/linux/arch/ppc/boot/misc.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/boot/misc.c	Mon Jan 12 15:18:13 1998
@@ -1,65 +1,29 @@
 /*
  * misc.c
  * 
- * This is a collection of several routines from gzip-1.0.3 
- * adapted for Linux.
- *
- * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
- * puts by Nick Holloway 1993
- *
  * Adapted for PowerPC by Gary Thomas
- * Updated and modified by Cort Dougan (cort@cs.nmt.edu)
+ *
+ * Rewritten by Cort Dougan (cort@cs.nmt.edu)
+ * Soon to be replaced by a single bootloader for chrp/prep/pmac. -- Cort
  */
 
-#include "gzip.h"
-#include "lzw.h"
+#include "../coffboot/zlib.h"
 #include "asm/residual.h"
 #include <elf.h>
 
+/* this is where the INITRD gets moved to for safe keeping */
+#define INITRD_DESTINATION /*0x00f00000*/ 0x01f00000
+/* this will do for now - Cort */
+char *avail_ram = (char *) 0x00800000; /* start with 8M */
+/* assume 15M max since this is where we copy the initrd to -- Cort */
+char *end_avail = (char *) INITRD_DESTINATION; 
+
 RESIDUAL hold_residual;
 unsigned long initrd_start = 0, initrd_end = 0;
 char *zimage_start;
 int zimage_size;
-extern char input_data[];
-extern int input_len;
-void cksum_text(void);
-void verify_data(unsigned long load_addr);
-
-void dump_buf(unsigned char *p, int s);
-#define EOF -1
-
-DECLARE(uch, inbuf, INBUFSIZ);
-DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
-DECLARE(uch, window, WSIZE);
-
-unsigned outcnt;
-unsigned insize;
-unsigned inptr;
-
 char cmd_line[256];
 
-int input_ptr;
-
-int method, exit_code, part_nb, last_member;
-int test = 0;
-int force = 0;
-int verbose = 1;
-long bytes_in, bytes_out;
-
-char *output_data;
-unsigned long output_ptr;
-
-extern int end;
-long free_mem_ptr = (long)&end;
-
-int to_stdout = 0;
-int hard_math = 0;
-
-void (*work)(int inf, int outf);
-void makecrc(void);
-
-local int get_method(int);
-
 char *vidmem = (char *)0xC00B8000;
 int lines, cols;
 int orig_x, orig_y;
@@ -68,43 +32,19 @@
 void putc(const char c);
 void puthex(unsigned long val);
 void _bcopy(char *src, char *dst, int len);
+void * memcpy(void * __dest, __const void * __src,
+			    int __n);
+void gunzip(void *, int, unsigned char *, int *);
 
-void *malloc(int size)
+void pause()
 {
-	void *p;
-
-	if (size <0) error("Malloc error\n");
-	if (free_mem_ptr <= 0) error("Memory error\n");
-
-   while(1) {
-	free_mem_ptr = (free_mem_ptr + 3) & ~3;	/* Align */
-
-	p = (void *)free_mem_ptr;
-	free_mem_ptr += size;
-
-	/*
-  	 * The part of the compressed kernel which has already been expanded
-	 * is no longer needed. Therefore we can reuse it for malloc.
-	 * With bigger kernels, this is necessary.
-	 */
-          
-	if (free_mem_ptr < (long)&end) {
-		if (free_mem_ptr > (long)&zimage_start[input_ptr])
-			error("\nOut of memory\n");
-
-		return p;
-	}
-#if 0	
-	if (free_mem_ptr < 0x90000)
-#endif	
-	return p;
-	puts("large kernel, low 1M tight...");
-	free_mem_ptr = (long)zimage_start;
-	}
+	puts("pause\n");
 }
 
-void free(void *where)
-{	/* Don't care */
+void exit()
+{
+	puts("exit\n");
+	while(1); 
 }
 
 static void clear_screen()
@@ -205,16 +145,8 @@
 	orig_y = y;
 }
 
-__ptr_t memset(__ptr_t s, int c, size_t n)
-{
-	int i;
-	char *ss = (char*)s;
-
-	for (i=0;i<n;i++) ss[i] = c;
-}
-
-__ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src,
-			    size_t __n)
+void * memcpy(void * __dest, __const void * __src,
+			    int __n)
 {
 	int i;
 	char *d = (char *)__dest, *s = (char *)__src;
@@ -222,8 +154,8 @@
 	for (i=0;i<__n;i++) d[i] = s[i];
 }
 
-int memcmp(__ptr_t __dest, __const __ptr_t __src,
-			    size_t __n)
+int memcmp(__const void * __dest, __const void * __src,
+			    int __n)
 {
 	int i;
 	char *d = (char *)__dest, *s = (char *)__src;
@@ -238,503 +170,195 @@
 	return (0);
 }
 
-extern ulg crc_32_tab[];   /* crc table, defined below */
-
-/* ===========================================================================
- * Run a set of bytes through the crc shift register.  If s is a NULL
- * pointer, then initialize the crc shift register contents instead.
- * Return the current crc in either case.
- */
-ulg updcrc(s, n)
-    uch *s;                 /* pointer to bytes to pump through */
-    unsigned n;             /* number of bytes in s[] */
+void error(char *x)
 {
-    register ulg c;         /* temporary variable */
-
-    static ulg crc = (ulg)0xffffffffL; /* shift register contents */
-
-    if (s == NULL) {
-	c = 0xffffffffL;
-    } else {
-	c = crc;
-	while (n--) {
-	    c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8);
-	}
-    }
-    crc = c;
-    return c ^ 0xffffffffL;       /* (instead of ~c for 64-bit machines) */
-}
+	puts("\n\n");
+	puts(x);
+	puts("\n\n -- System halted");
 
-/* ===========================================================================
- * Clear input and output buffers
- */
-void clear_bufs()
-{
-    outcnt = 0;
-    insize = inptr = 0;
-    bytes_in = bytes_out = 0L;
+	while(1);	/* Halt */
 }
 
-/* ===========================================================================
- * Fill the input buffer. This is called only when the buffer is empty
- * and at least one byte is really needed.
- */
-int fill_inbuf()
-{
-    int len, i;
-
-    /* Read as much as possible */
-puts("*");    
-    insize = 0;
-    do {
-	len = INBUFSIZ-insize;
-	if (len > (zimage_size-input_ptr+1)) len=zimage_size-input_ptr+1;
-        if (len == 0 || len == EOF) break;
-
-        for (i=0;i<len;i++) inbuf[insize+i] = zimage_start[input_ptr+i];
-	insize += len;
-	input_ptr += len;
-    } while (insize < INBUFSIZ);
-
-    if (insize == 0) {
-	error("unable to fill buffer\n");
-    }
-    bytes_in += (ulg)insize;
-    inptr = 1;
-    return inbuf[0];
-}
-
-/* ===========================================================================
- * Write the output window window[0..outcnt-1] and update crc and bytes_out.
- * (Used for the decompressed data only.)
- */
-void flush_window()
+void *zalloc(void *x, unsigned items, unsigned size)
 {
-    if (outcnt == 0) return;
-    updcrc(window, outcnt);
-
-    memcpy(&output_data[output_ptr], (char *)window, outcnt);
-
-    bytes_out += (ulg)outcnt;
-    output_ptr += (ulg)outcnt;
-    outcnt = 0;
+	void *p = avail_ram;
+	
+	size *= items;
+	size = (size + 7) & -8;
+	avail_ram += size;
+	if (avail_ram > end_avail) {
+		puts("oops... out of memory\n");
+		pause();
+	}
+	return p;
 }
 
-/*
- * Code to compute the CRC-32 table. Borrowed from 
- * gzip-1.0.3/makecrc.c.
- */
-
-ulg crc_32_tab[256];
-
-void
-makecrc(void)
+void zfree(void *x, void *addr, unsigned nb)
 {
-/* Not copyrighted 1990 Mark Adler	*/
-
-  unsigned long c;      /* crc shift register */
-  unsigned long e;      /* polynomial exclusive-or pattern */
-  int i;                /* counter for all possible eight bit values */
-  int k;                /* byte being shifted into crc apparatus */
-
-  /* terms of polynomial defining this crc (except x^32): */
-  static int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
-
-  /* Make exclusive-or pattern from polynomial */
-  e = 0;
-  for (i = 0; i < sizeof(p)/sizeof(int); i++)
-    e |= 1L << (31 - p[i]);
-
-  crc_32_tab[0] = 0;
-
-  for (i = 1; i < 256; i++)
-  {
-    c = 0;
-    for (k = i | 256; k != 1; k >>= 1)
-    {
-      c = c & 1 ? (c >> 1) ^ e : c >> 1;
-      if (k & 1)
-        c ^= e;
-    }
-    crc_32_tab[i] = c;
-  }
 }
 
-void error(char *x)
-{
-	puts("\n\n");
-	puts(x);
-	puts("\n\n -- System halted");
-
-	while(1);	/* Halt */
+#define HEAD_CRC	2
+#define EXTRA_FIELD	4
+#define ORIG_NAME	8
+#define COMMENT		0x10
+#define RESERVED	0xe0
+
+#define DEFLATED	8
+
+
+void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
+{
+	z_stream s;
+	int r, i, flags;
+	
+	/* skip header */
+	i = 10;
+	flags = src[3];
+	if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
+		puts("bad gzipped data\n");
+		exit();
+	}
+	if ((flags & EXTRA_FIELD) != 0)
+		i = 12 + src[10] + (src[11] << 8);
+	if ((flags & ORIG_NAME) != 0)
+		while (src[i++] != 0)
+			;
+	if ((flags & COMMENT) != 0)
+		while (src[i++] != 0)
+			;
+	if ((flags & HEAD_CRC) != 0)
+		i += 2;
+	if (i >= *lenp) {
+		puts("gunzip: ran out of data in header\n");
+		exit();
+	}
+	
+	s.zalloc = zalloc;
+	s.zfree = zfree;
+	r = inflateInit2(&s, -MAX_WBITS);
+	if (r != Z_OK) {
+		puts("inflateInit2 returned %d\n");
+		exit();
+	}
+	s.next_in = src + i;
+	s.avail_in = *lenp - i;
+	s.next_out = dst;
+	s.avail_out = dstlen;
+	r = inflate(&s, Z_FINISH);
+	if (r != Z_OK && r != Z_STREAM_END) {
+		puts("inflate returned %d\n");
+		exit();
+	}
+	*lenp = s.next_out - (unsigned char *) dst;
+	inflateEnd(&s);
 }
 
 unsigned long
 decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual)
 {
-  int timer;
-  char *cp, ch;
-  Elf32_Ehdr *eh;
-  Elf32_Shdr *sh, *strtab_shdr;
-  char *strtab;
-  unsigned long i;
-  extern unsigned long start(void);
-
+	int timer;
+	extern unsigned long start;
+	char *cp, ch;
+	unsigned long i;
+	
+	
+	lines = 25;
+	cols = 80;
+	orig_x = 0;
+	orig_y = 24;
+	
+	
+	/* Turn off MMU.  Since we are mapped 1-1, this is OK. */
+	flush_instruction_cache();
+	_put_HID0(_get_HID0() & ~0x0000C000);
+	_put_MSR(_get_MSR() & ~0x0030);
+
+	vga_init(0xC0000000);
+	/*clear_screen();*/
+
+	puts("loaded at:    "); puthex(load_addr);
+	puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n");
+	
+	puts("relocated to: "); puthex((unsigned long)&start);
+	puts(" "); puthex((unsigned long)((unsigned long)&start + (4*num_words))); puts("\n");
+	
+	zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET);
+	zimage_size = ZIMAGE_SIZE;
+	puts("zimage at:    "); puthex((unsigned long)zimage_start);
+	puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n");
+
+	if ( INITRD_OFFSET )
+		initrd_start = load_addr - 0x10000 + INITRD_OFFSET;
+	else
+		initrd_start = 0;
+	initrd_end = INITRD_SIZE + initrd_start;
   
-  output_data = (char *)0x0;	/* Points to 0 */
-  lines = 25;
-  cols = 80;
-  orig_x = 0;
-  orig_y = 24;
-
-  
-  /* Turn off MMU.  Since we are mapped 1-1, this is OK. */
-  flush_instruction_cache();
-  _put_HID0(_get_HID0() & ~0x0000C000);
-  _put_MSR(_get_MSR() & ~0x0030);
-
-  vga_init(0xC0000000);
-  /*clear_screen();*/
-
-  output_ptr = 0;
-
-  exit_code = 0;
-  test = 0;
-  input_ptr = 0;
-  part_nb = 0;
-
-  clear_bufs();
-  makecrc();
-
-  puts("Cksum: ");  puthex(cksum);  puts("\n");
-  puts("Loaded at: ");  puthex(load_addr); puts(" ");  puthex(num_words+load_addr);
-  puts("\n");
-  puts("Boot code relocated to: ");  puthex((unsigned long)start); puts(" ");
-  puthex((unsigned long)(num_words+start));
-  puts("\n");
-  if (residual) {
-    _bcopy((char *)residual, (char *)&hold_residual, sizeof(hold_residual));
-    puts("Residual data at: ");  puthex((unsigned long)residual); puts(" ");
-    puthex((unsigned long)((unsigned long)(residual->ResidualLength) + residual));  puts("\n");
-    puts("Residual data relocated to: ");  puthex((unsigned long)&hold_residual); puts("\n");
-  }
-
-  /*
-   * Take care of initrd if we have one
-   */
-  /*
-   * the _actual_ load addr is 64k (elf hdr size) lower but the
-   * firmware gives us the starting exec point not the load ptr
-   * -- Cort
-   */
-  eh = (Elf32_Ehdr *)(load_addr - 65536);
-  sh = (Elf32_Shdr *)((unsigned long)eh + eh->e_shoff );
-  /*puts("Entry point: "); puthex(eh->e_entry); puts("\n");*/
-  
-  /* find string table */
-  for ( i = 0 ; i <= eh->e_shnum ; i++,sh++)
-  {
-	  /*puts("Section: "); puthex(i);
-	  puts(" type: "); puthex(sh->sh_type);
-	  puts(" offset: "); puthex(sh->sh_offset);
-	  puts("\n");*/
-
-	  if ( sh->sh_type == SHT_STRTAB )
-	  {
-		  strtab_shdr = sh;
-		  strtab = (char *)(sh->sh_offset + (unsigned long)eh);
-		  /*puts("Found strtab at: "); puthex((unsigned long)strtab);
-		  puts("\n");*/
-		  break;
-	  }
-  }  
+	/* relocate initrd */
+	if ( initrd_start )
+	{
+		puts("initrd at:    "); puthex(initrd_start);
+		puts(" "); puthex(initrd_end); puts("\n");
+		
+		memcpy ((void *)INITRD_DESTINATION,(void *)initrd_start,
+			INITRD_SIZE );
+		initrd_end = INITRD_DESTINATION + INITRD_SIZE;
+		initrd_start = INITRD_DESTINATION;
+		puts("Moved initrd to: "); puthex(initrd_start);
+		puts(" "); puthex(initrd_end); puts("\n");
+	}
+	
+	CRT_tstc();  /* Forces keyboard to be initialized */
+	puts("\nLinux/PPC load: ");
+	timer = 0;
+	cp = cmd_line;
+	while (timer++ < 5*1000) {
+		if (tstc()) {
+			while ((ch = getc()) != '\n' && ch != '\r') {
+				if (ch == '\b') {
+					if (cp != cmd_line) {
+						cp--;
+						puts("\b \b");
+					}
+				} else {
+					*cp++ = ch;
+					putc(ch);
+				}
+			}
+			break;  /* Exit 'timer' loop */
+		}
+		udelay(1000);  /* 1 msec */
+	}
+	*cp = 0;
+	puts("\n");
 
-  /* find the initrd and image sections */
-  if ( strtab_shdr )
-  {
-	  sh = (Elf32_Shdr *)((unsigned long)eh + eh->e_shoff );
-	  for ( i = 0 ; i <= eh->e_shnum ; i++,sh++)
-	  {
-		  if ( !memcmp("initrd", (char *)(strtab+sh->sh_name), 6 ) )
-		  {
-			  initrd_start = (unsigned long)eh + sh->sh_offset;
-			  initrd_end =  initrd_start + sh->sh_size;
-			  puts("Found initrd at: "); puthex(initrd_start);
-			  puts(" "); puthex(initrd_end);
-			  puts("\n");
-		  }
-		  if ( !memcmp("image", (char *)(strtab+sh->sh_name), 5 ) )
-		  {
-			  zimage_start = (char *)((unsigned long)eh + sh->sh_offset);
-			  zimage_size =  sh->sh_size;
-			  puts("Found zimage at: "); puthex((unsigned long)zimage_start);
-			  puts(" "); puthex((unsigned long)(zimage_size+zimage_start));
-			  puts("\n");
-		  }
-	  }
-  }
-  else
-  {
-	  puts("Couldn't find string table for boot image!\n");
-  }
-  
-  /* relocate initrd */
-  if ( initrd_start )
-  {
-	  memcpy ((void *)0x00D00000,(void *)initrd_start,
-		  initrd_end - initrd_start );
-	  initrd_end = 0x00D00000 + initrd_end - initrd_start;
-	  initrd_start = 0x00D00000;
-	  puts("Moved initrd to: "); puthex(initrd_start);
-	  puts(" "); puthex(initrd_end); puts("\n");
-  }
-
-
-  CRT_tstc();  /* Forces keyboard to be initialized */
-  puts("\nLinux/PPC load: ");
-  timer = 0;
-  cp = cmd_line;
-  while (timer++ < 5*1000) {
-	  if (tstc()) {
-		  while ((ch = getc()) != '\n' && ch != '\r') {
-			  if (ch == '\b') {
-				  if (cp != cmd_line) {
-					  cp--;
-					  puts("\b \b");
-				  }
-			  } else {
-				  *cp++ = ch;
-				  putc(ch);
-			  }
-		  }
-		  break;  /* Exit 'timer' loop */
-	  }
-	  udelay(1000);  /* 1 msec */
-  }
-  *cp = 0;
-  puts("\n");
-
-  /* mappings on early boot can only handle 16M */
-  if ( (int)(&cmd_line[0]) > (16<<20))
-	  puts("cmd_line > 16M\n");
-  if ( (int)&hold_residual > (16<<20))
-	  puts("hold_residual > 16M\n");
-  if ( initrd_start > (16<<20))
-	  puts("initrd_start > 16M\n");
+	/* mappings on early boot can only handle 16M */
+	if ( (int)(&cmd_line[0]) > (16<<20))
+		puts("cmd_line located > 16M\n");
+	if ( (int)&hold_residual > (16<<20))
+		puts("hold_residual located > 16M\n");
+	if ( initrd_start > (16<<20))
+		puts("initrd_start located > 16M\n");
        
   
-  puts("Uncompressing Linux...");
-  method = get_method(0);
-  work(0, 0);
-  puts("done.\n");
-  
-  puts("Now booting the kernel\n");
-  return (unsigned long)&hold_residual;
-}
-
-show_residual_data(RESIDUAL *res)
-{
-  puts("Residual data: ");  puthex(res->ResidualLength);  puts(" bytes\n");
-#if 0
-  puts("Residual structure = ");  puthex(sizeof(*res));  puts(" bytes\n");
-  dump_buf(&hold_residual, 32);
-  dump_buf(res, 32);
-#endif
-}
-
-do_cksum(unsigned long loc)
-{
-  unsigned int ptr, cksum;
-  puts("cksum[");  puthex(loc);  puts("] = ");  
-  cksum = 0;
-  for (ptr = loc;  ptr < (loc+0x40000);  ptr += 4)
-    {
-      cksum ^= *(unsigned long *)ptr;
-    }
-  puthex(cksum);  puts("  ");
-  cksum = 0;  loc += 0x40000;
-  for (ptr = loc;  ptr < (loc+0x40000);  ptr += 4)
-    {
-      cksum ^= *(unsigned long *)ptr;
-    }
-  puthex(cksum);  puts("  ");
-  cksum = 0;  loc += 0x40000;
-  for (ptr = loc;  ptr < (loc+0x40000);  ptr += 4)
-    {
-      cksum ^= *(unsigned long *)ptr;
-    }
-  puthex(cksum);  puts("  ");
-  cksum = 0;  loc += 0x40000;
-  for (ptr = loc;  ptr < (loc+0x40000);  ptr += 4)
-    {
-      cksum ^= *(unsigned long *)ptr;
-    }
-  puthex(cksum);  puts("\n");
-}
-
-cksum_data()
-{
-  unsigned int *ptr, len, cksum, cnt;
-  cksum = cnt = 0;
-  ptr = (unsigned int *)zimage_start;
-  puts("Checksums: ");
-  for (len = 0;  len < zimage_size;  len += 4) {
-    cksum ^= *ptr++;
-    if (len && ((len & 0x0FFF) == 0)) {
-      if (cnt == 0) {
-	puts("\n  [");
-	puthex((unsigned long)ptr-1);
-	puts("] ");
-      }
-      puthex(cksum);
-      if (++cnt == 6) {
-	cnt = 0;
-      } else {
-	puts(" ");
-      }
-    }
-  }
-  puts("\n");
-  puts("Data cksum = ");  puthex(cksum);  puts("\n");
-}
-
-void cksum_text(void)
-{
-  extern int start, etext;
-  unsigned int *ptr, len, text_len, cksum, cnt;
-  cksum = cnt = 0;
-  ptr = &start;
-  text_len = (unsigned int)&etext - (unsigned int)&start;
-  puts("Checksums: ");
-  for (len = 0;  len < text_len;  len += 4) {
-    cksum ^= *ptr++;
-    if (len && ((len & 0x0FFF) == 0)) {
-      if (cnt == 0) {
-	puts("\n  [");
-	puthex((unsigned long)ptr-1);
-	puts("] ");
-      }
-      puthex(cksum);
-      if (++cnt == 6) {
-	cnt = 0;
-      } else {
-	puts(" ");
-      }
-    }
-  }
-  puts("\n");
-  puts("TEXT cksum = ");  puthex(cksum);  puts("\n");
-}
-
-void verify_data(unsigned long load_addr)
-{
-  extern int start, etext;
-  unsigned int *orig_ptr, *copy_ptr, len, errors;
-  errors = 0;
-  copy_ptr = (unsigned int *)zimage_start;
-  orig_ptr = (unsigned int *)(load_addr + ((unsigned int)zimage_start - (unsigned int)&start));
-  for (len = 0;  len < zimage_size;  len += 4) {
-    if (*copy_ptr++ != *orig_ptr++) {
-      errors++;
-    }    
-  }
-  copy_ptr = (unsigned int *)zimage_start;
-  orig_ptr = (unsigned int *)(load_addr + ((unsigned int)zimage_start - (unsigned int)&start));
-  for (len = 0;  len < zimage_size;  len += 4) {
-    if (*copy_ptr++ != *orig_ptr++) {
-      dump_buf((unsigned char *) (copy_ptr-1), 128);
-      dump_buf((unsigned char *) (orig_ptr-1), 128);
-      puts("Total errors = ");  puthex(errors*4);  puts("\n");
-      while (1) ;
-    }    
-  }
-}
-
-test_data(unsigned long load_addr)
-{
-  extern int start, etext;
-  unsigned int *orig_ptr, *copy_ptr, len, errors;
-  errors = 0;
-  copy_ptr = (unsigned int *)zimage_start;
-  orig_ptr = (unsigned int *)(load_addr + ((unsigned int)zimage_start - (unsigned int)&start));
-  for (len = 0;  len < zimage_size;  len += 4) {
-    if (*copy_ptr++ != *orig_ptr++) {
-      errors++;
-    }    
-  }
-  return (errors == 0);
+	puts("Uncompressing Linux...");
+	gunzip(0, 0x400000, zimage_start, &zimage_size);  
+	puts("done.\n");
+	puts("Now booting the kernel\n");
+	return (unsigned long)&hold_residual;
 }
 
 void puthex(unsigned long val)
 {
-  unsigned char buf[10];
-  int i;
-  for (i = 7;  i >= 0;  i--)
-    {
-      buf[i] = "0123456789ABCDEF"[val & 0x0F];
-      val >>= 4;
-    }
-  buf[8] = '\0';
-  puts(buf);
-}
-
-#if 1
-void puthex2(unsigned long val)
-{
-  unsigned char buf[4];
-  int i;
-  for (i = 1;  i >= 0;  i--)
-    {
-      buf[i] = "0123456789ABCDEF"[val & 0x0F];
-      val >>= 4;
-    }
-  buf[2] = '\0';
-  puts(buf);
-}
-
-void dump_buf(unsigned char *p, int s)
-{
-   int i, c;
-   if ((unsigned int)s > (unsigned int)p)
-   {
-   	s = (unsigned int)s - (unsigned int)p;
-   }
-   while (s > 0)
-   {
-      puthex((unsigned long)p);  puts(": ");
-      for (i = 0;  i < 16;  i++)
-      {
-         if (i < s)
-         {
-            puthex2(p[i] & 0xFF);
-         } else
-         {
-            puts("  ");
-         }
-         if ((i % 2) == 1) puts(" ");
-         if ((i % 8) == 7) puts(" ");
-      }
-      puts(" |");
-      for (i = 0;  i < 16;  i++)
-      {
-	 char buf[2];
-         if (i < s)
-         {
-            c = p[i] & 0xFF;
-            if ((c < 0x20) || (c >= 0x7F)) c = '.';
-         } else
-         {
-            c = ' ';
-         }
-	 buf[0] = c;  buf[1] = '\0';
-         puts(buf);
-      }
-      puts("|\n");
-      s -= 16;
-      p += 16;
-   }
+	unsigned char buf[10];
+	int i;
+	for (i = 7;  i >= 0;  i--)
+	{
+		buf[i] = "0123456789ABCDEF"[val & 0x0F];
+		val >>= 4;
+	}
+	buf[8] = '\0';
+	puts(buf);
 }
-#endif
 
 /*
  * PCI/ISA I/O support
@@ -765,79 +389,8 @@
 	return ((addr & 0x7FFFFFFF) | 0x80000000);
 }
 
-/* ========================================================================
- * Check the magic number of the input file and update ofname if an
- * original name was given and to_stdout is not set.
- * Return the compression method, -1 for error, -2 for warning.
- * Set inptr to the offset of the next byte to be processed.
- * This function may be called repeatedly for an input file consisting
- * of several contiguous gzip'ed members.
- * IN assertions: there is at least one remaining compressed member.
- *   If the member is a zip file, it must be the only one.
- */
-local int get_method(in)
-    int in;        /* input file descriptor */
-{
-    uch flags;
-    char magic[2]; /* magic header */
-
-    magic[0] = (char)get_byte();
-    magic[1] = (char)get_byte();
-
-    method = -1;                 /* unknown yet */
-    part_nb++;                   /* number of parts in gzip file */
-    last_member = 0;
-    /* assume multiple members in gzip file except for record oriented I/O */
-
-    if (memcmp(magic, GZIP_MAGIC, 2) == 0
-        || memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) {
-
-	work = unzip;
-	method = (int)get_byte();
-	flags  = (uch)get_byte();
-	if ((flags & ENCRYPTED) != 0)
-	    error("Input is encrypted\n");
-	if ((flags & CONTINUATION) != 0)
-	       error("Multi part input\n");
-	if ((flags & RESERVED) != 0) {
-	    error("Input has invalid flags\n");
-	    exit_code = ERROR;
-	    if (force <= 1) return -1;
-	}
-	(ulg)get_byte();	/* Get timestamp */
-	((ulg)get_byte()) << 8;
-	((ulg)get_byte()) << 16;
-	((ulg)get_byte()) << 24;
-
-	(void)get_byte();  /* Ignore extra flags for the moment */
-	(void)get_byte();  /* Ignore OS type for the moment */
-
-	if ((flags & EXTRA_FIELD) != 0) {
-	    unsigned len = (unsigned)get_byte();
-	    len |= ((unsigned)get_byte())<<8;
-	    while (len--) (void)get_byte();
-	}
-
-	/* Get original file name if it was truncated */
-	if ((flags & ORIG_NAME) != 0) {
-	    if (to_stdout || part_nb > 1) {
-		/* Discard the old name */
-		while (get_byte() != 0) /* null */ ;
-	    } else {
-	    } /* to_stdout */
-	} /* orig_name */
-
-	/* Discard file comment if any */
-	if ((flags & COMMENT) != 0) {
-	    while (get_byte() != 0) /* null */ ;
-	}
-    } else
-	error("unknown compression method");
-    return method;
-}
-
 void
 _bcopy(char *src, char *dst, int len)
 {
-  while (len--) *dst++ = *src++;
+	while (len--) *dst++ = *src++;
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/mkprep.c linux/arch/ppc/boot/mkprep.c
--- v2.1.78/linux/arch/ppc/boot/mkprep.c	Mon Aug 18 18:19:43 1997
+++ linux/arch/ppc/boot/mkprep.c	Mon Jan 12 15:18:13 1998
@@ -131,8 +131,8 @@
   argptr++;
 
   /* skip elf header in input file */
-  if ( !prep )
-	  lseek(in_fd, elfhdr_size, SEEK_SET);
+  /*if ( !prep )*/
+  lseek(in_fd, elfhdr_size, SEEK_SET);
   
   /* write prep partition if necessary */
   if ( prep )
@@ -164,7 +164,7 @@
   bzero( block, sizeof block );
  
   /* set entry point and boot image size skipping over elf header */
-  *entry = cpu_to_le32(0x400+65536);
+  *entry = cpu_to_le32(0x400/*+65536*/);
   *length = cpu_to_le32(info.st_size+0x400);
 
   /* sets magic number for msdos partition (used by linux) */
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/offset linux/arch/ppc/boot/offset
--- v2.1.78/linux/arch/ppc/boot/offset	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/boot/offset	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+OFFSET=`objdump -h $1  | grep $2 | grep -v zvmlinux| awk '{print $6}'`
+echo "0x"$OFFSET
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/piggyback.c linux/arch/ppc/boot/piggyback.c
--- v2.1.78/linux/arch/ppc/boot/piggyback.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/boot/piggyback.c	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,64 @@
+#include <stdio.h>
+
+extern long ce_exec_config[];
+
+main(int argc, char *argv[])
+{
+	int i, cnt, pos, len;
+	unsigned int cksum, val;
+	unsigned char *lp;
+	unsigned char buf[8192];
+	if (argc != 1)
+	{
+		fprintf(stderr, "usage: %s <in-file >out-file\n", argv[0]);
+		exit(1);
+	}
+	fprintf(stdout, "#\n");
+	fprintf(stdout, "# Miscellaneous data structures:\n");
+	fprintf(stdout, "# WARNING - this file is automatically generated!\n");
+	fprintf(stdout, "#\n");
+	fprintf(stdout, "\n");
+	fprintf(stdout, "\t.data\n");
+	fprintf(stdout, "\t.globl input_data\n");
+	fprintf(stdout, "input_data:\n");
+	pos = 0;
+	cksum = 0;
+	while ((len = read(0, buf, sizeof(buf))) > 0)
+	{
+		cnt = 0;
+		lp = (unsigned char *)buf;
+		len = (len + 3) & ~3;  /* Round up to longwords */
+		for (i = 0;  i < len;  i += 4)
+		{
+			if (cnt == 0)
+			{
+				fprintf(stdout, "\t.long\t");
+			}
+			fprintf(stdout, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]);
+			val = *(unsigned long *)lp;
+			cksum ^= val;
+			lp += 4;
+			if (++cnt == 4)
+			{
+				cnt = 0;
+				fprintf(stdout, " # %x \n", pos+i-12);
+				fflush(stdout);
+			} else
+			{
+				fprintf(stdout, ",");
+			}
+		}
+		if (cnt)
+		{
+			fprintf(stdout, "0\n");
+		}
+		pos += len;
+	}
+	fprintf(stdout, "\t.globl input_len\n");
+	fprintf(stdout, "input_len:\t.long\t0x%x\n", pos);
+	fflush(stdout);
+	fclose(stdout);
+	fprintf(stderr, "cksum = %x\n", cksum);
+	exit(0);
+}
+
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/size linux/arch/ppc/boot/size
--- v2.1.78/linux/arch/ppc/boot/size	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/boot/size	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+OFFSET=`objdump -h $1  | grep $2 | grep -v zvmlinux | awk '{print $3}'`
+echo "0x"$OFFSET
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/boot/unzip.c linux/arch/ppc/boot/unzip.c
--- v2.1.78/linux/arch/ppc/boot/unzip.c	Mon Aug  4 16:25:36 1997
+++ linux/arch/ppc/boot/unzip.c	Wed Dec 31 16:00:00 1969
@@ -1,182 +0,0 @@
-/* unzip.c -- decompress files in gzip or pkzip format.
- * Copyright (C) 1992-1993 Jean-loup Gailly
- *
- * Adapted for Linux booting by Hannu Savolainen 1993
- *
- * This is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License, see the file COPYING.
- *
- * The code in this file is derived from the file funzip.c written
- * and put in the public domain by Mark Adler.
- */
-
-/*
-   This version can extract files in gzip or pkzip format.
-   For the latter, only the first entry is extracted, and it has to be
-   either deflated or stored.
- */
-
-#ifndef lint
-static char rcsid[] = "$Id: unzip.c,v 1.1.1.1 1997/02/25 05:18:11 cort Exp $";
-#endif
-
-#include "gzip.h"
-#include "crypt.h"
-
-#include <stdio.h>
-
-/* PKZIP header definitions */
-#define LOCSIG 0x04034b50L      /* four-byte lead-in (lsb first) */
-#define LOCFLG 6                /* offset of bit flag */
-#define  CRPFLG 1               /*  bit for encrypted entry */
-#define  EXTFLG 8               /*  bit for extended local header */
-#define LOCHOW 8                /* offset of compression method */
-#define LOCTIM 10               /* file mod time (for decryption) */
-#define LOCCRC 14               /* offset of crc */
-#define LOCSIZ 18               /* offset of compressed size */
-#define LOCLEN 22               /* offset of uncompressed length */
-#define LOCFIL 26               /* offset of file name field length */
-#define LOCEXT 28               /* offset of extra field length */
-#define LOCHDR 30               /* size of local header, including sig */
-#define EXTHDR 16               /* size of extended local header, inc sig */
-
-
-/* Globals */
-
-int decrypt;      /* flag to turn on decryption */
-char *key;        /* not used--needed to link crypt.c */
-int pkzip = 0;    /* set for a pkzip file */
-int extended = 0; /* set if extended local header */
-
-/* ===========================================================================
- * Check zip file and advance inptr to the start of the compressed data.
- * Get ofname from the local header if necessary.
- */
-int check_zipfile(in)
-    int in;   /* input file descriptors */
-{
-    uch *h = inbuf + inptr; /* first local header */
-
-    /* ifd = in; */
-
-    /* Check validity of local header, and skip name and extra fields */
-    inptr += LOCHDR + SH(h + LOCFIL) + SH(h + LOCEXT);
-
-    if (inptr > insize || LG(h) != LOCSIG) {
-	error("input not a zip");
-    }
-    method = h[LOCHOW];
-    if (method != STORED && method != DEFLATED) {
-	error("first entry not deflated or stored--can't extract");
-    }
-
-    /* If entry encrypted, decrypt and validate encryption header */
-    if ((decrypt = h[LOCFLG] & CRPFLG) != 0) {
-	error("encrypted file\n");
-	exit_code = ERROR;
-	return -1;
-    }
-
-    /* Save flags for unzip() */
-    extended = (h[LOCFLG] & EXTFLG) != 0;
-    pkzip = 1;
-
-    /* Get ofname and time stamp from local header (to be done) */
-    return 0;
-}
-
-/* ===========================================================================
- * Unzip in to out.  This routine works on both gzip and pkzip files.
- *
- * IN assertions: the buffer inbuf contains already the beginning of
- *   the compressed data, from offsets inptr to insize-1 included.
- *   The magic header has already been checked. The output buffer is cleared.
- */
-void unzip(in, out)
-    int in, out;   /* input and output file descriptors */
-{
-    ulg orig_crc = 0;       /* original crc */
-    ulg orig_len = 0;       /* original uncompressed length */
-    ulg _crc, _len;
-    int n;
-    uch buf[EXTHDR];        /* extended local header */
-
-    /* ifd = in;
-    ofd = out; */
-
-    updcrc(NULL, 0);           /* initialize crc */
-
-    if (pkzip && !extended) {  /* crc and length at the end otherwise */
-	orig_crc = LG(inbuf + LOCCRC);
-	orig_len = LG(inbuf + LOCLEN);
-    }
-
-    /* Decompress */
-    if (method == DEFLATED)  {
-
-	int res = inflate();
-
-	if (res == 3) {
-	    error("out of memory");
-	} else if (res != 0) {
-	    error("invalid compressed format");
-	}
-
-    } else if (pkzip && method == STORED) {
-
-	register ulg n = LG(inbuf + LOCLEN);
-
-	if (n != LG(inbuf + LOCSIZ) - (decrypt ? RAND_HEAD_LEN : 0)) {
-
-	    error("length mismatch");
-	}
-	while (n--) {
-	    uch c = (uch)get_byte();
-#ifdef CRYPT
-	    if (decrypt) zdecode(c);
-#endif
-	    if (!test) put_char(c);
-	}
-    } else {
-	error("internal error, invalid method");
-    }
-
-    /* Get the crc and original length */
-    if (!pkzip) {
-        /* crc32  (see algorithm.doc)
-	 * uncompressed input size modulo 2^32
-         */
-	for (n = 0; n < 8; n++) {
-	    buf[n] = (uch)get_byte(); /* may cause an error if EOF */
-	}
-	orig_crc = LG(buf);
-	orig_len = LG(buf+4);
-
-    } else if (extended) {  /* If extended header, check it */
-	/* signature - 4bytes: 0x50 0x4b 0x07 0x08
-	 * CRC-32 value
-         * compressed size 4-bytes
-         * uncompressed size 4-bytes
-	 */
-	for (n = 0; n < EXTHDR; n++) {
-	    buf[n] = (uch)get_byte(); /* may cause an error if EOF */
-	}
-	orig_crc = LG(buf+4);
-	orig_len = LG(buf+12);
-    }
-
-    /* Validate decompression */
-    if (orig_crc != (_crc = updcrc(outbuf, 0))) {
-      extern char input_data[];
-	error("crc error");
-    }
-    if (orig_len != bytes_out) {
-	error("length error");
-    }
-
-    /* Check if there are more entries in a pkzip file */
-    if (pkzip && inptr + 4 < insize && LG(inbuf+inptr) == LOCSIG) {
-	    error("zip file has more than one entry");
-    }
-    extended = pkzip = 0; /* for next file */
-}
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/chrpboot/Makefile linux/arch/ppc/chrpboot/Makefile
--- v2.1.78/linux/arch/ppc/chrpboot/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrpboot/Makefile	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,91 @@
+# Makefile for making ELF bootable images for booting on CHRP
+# using Open Firmware.
+#
+# Geert Uytterhoeven	September 1997
+#
+# Based on coffboot by Paul Mackerras
+
+.c.s:
+	$(CC) $(CFLAGS) -S -o $*.s $<
+.s.o:
+	$(AS) -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) -DKERNELBASE=$(KERNELBASE) -c -o $*.o $<
+.S.s:
+	$(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $<
+.S.o:
+	$(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $<
+
+CFLAGS	= -O -fno-builtin -DSTDC_HEADERS -I$(TOPDIR)/include
+LD_ARGS = -T ../vmlinux.lds -Ttext 0x00800000
+OBJCOPY = $(CROSS_COMPILE)objcopy
+
+OBJS = crt0.o start.o main.o misc.o string.o zlib.o image.o # initrd.o
+LIBS = $(TOPDIR)/lib/lib.a
+
+
+all:	$(TOPDIR)/zImage
+
+#
+# Only build anything here if we're configured for CHRP
+# -- cort
+#
+ifeq ($(CONFIG_CHRP),y)
+znetboot: zImage
+	cp zImage /tftpboot/zImage.chrp
+
+znetboot.initrd: zImage.initrd
+	cp zImage.initrd /tftpboot/zImage.chrp
+
+floppy: zImage
+	mcopy zImage a:zImage
+
+piggyback: piggyback.c
+	$(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o piggyback piggyback.c
+
+image.o: piggyback ../coffboot/vmlinux.gz
+	./piggyback image < ../coffboot/vmlinux.gz | $(AS) -o image.o
+
+initrd.o: ramdisk.image.gz piggyback
+	./piggyback initrd < ramdisk.image.gz | $(AS) -o initrd.o
+
+note.data : mknote
+	rm -f note.data
+	./mknote > note.data
+
+zImage: $(OBJS) no_initrd.o note.data
+	$(LD) $(LD_ARGS) -o $@ $(OBJS) no_initrd.o $(LIBS)
+	objcopy zImage --add-section=.note=note.data zImage
+
+zImage.initrd: $(OBJS) initrd.o
+	$(LD) $(LD_ARGS) -o $@ $(OBJS) initrd.o $(LIBS)
+
+else
+znetboot:
+
+znetboot.initrd:
+
+floppy:
+
+zImage:
+
+zImage.initrd:
+
+endif
+
+# just here to match coffboot/Makefile
+vmlinux.coff:
+
+vmlinux.coff.initrd:
+
+
+clean:
+	rm -f piggyback mknote note.data
+	rm -f $(OBJS) zImage
+
+fastdep:
+	$(TOPDIR)/scripts/mkdep *.[Sch] > .depend
+
+dep:
+	$(CPP) -M *.S *.c > .depend
+
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/chrpboot/crt0.S linux/arch/ppc/chrpboot/crt0.S
--- v2.1.78/linux/arch/ppc/chrpboot/crt0.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrpboot/crt0.S	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) Paul Mackerras 1997.
+ *
+ * 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.
+ */
+	.text
+	.globl	_start
+_start:
+	lis	9,_start@h
+	lis	8,_etext@ha
+	addi	8,8,_etext@l
+1:	dcbf	0,9
+	icbi	0,9
+	addi	9,9,0x20
+	cmplwi	0,9,8
+	blt	1b
+	b	start
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/chrpboot/main.c linux/arch/ppc/chrpboot/main.c
--- v2.1.78/linux/arch/ppc/chrpboot/main.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrpboot/main.c	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) Paul Mackerras 1997.
+ *
+ * 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 "nonstdio.h"
+#include "zlib.h"
+
+extern void *finddevice(const char *);
+extern int getprop(void *, const char *, void *, int);
+void gunzip(void *, int, unsigned char *, int *);
+
+#define get_16be(x)	(*(unsigned short *)(x))
+#define get_32be(x)	(*(unsigned *)(x))
+
+#define RAM_START	0x90000000
+#define RAM_END		0x90800000	/* only 8M mapped with BATs */
+
+#define RAM_FREE	0x90540000	/* after image of chrpboot */
+#define PROG_START	0x90010000
+
+char *avail_ram;
+char *end_avail;
+
+extern char image_data[];
+extern int image_len;
+extern char initrd_data[];
+extern int initrd_len;
+
+
+chrpboot(int a1, int a2, void *prom)
+{
+    int ns, oh, i;
+    unsigned sa, len;
+    void *dst;
+    unsigned char *im;
+    unsigned initrd_start, initrd_size;
+    
+    printf("chrpboot starting\n\r");
+    setup_bats();
+
+    if (initrd_len) {
+	initrd_size = initrd_len;
+	initrd_start = (RAM_END - initrd_size) & ~0xFFF;
+	a1 = initrd_start;
+	a2 = initrd_size;
+	printf("initial ramdisk at %x (%u bytes)\n\r", initrd_start,
+	       initrd_size);
+	memcpy((char *)initrd_start, initrd_data, initrd_size);
+	end_avail = (char *)initrd_start;
+    } else
+	end_avail = (char *) RAM_END;
+    im = image_data;
+    len = image_len;
+    dst = (void *) PROG_START;
+
+    if (im[0] == 0x1f && im[1] == 0x8b) {
+	void *cp = (void *) RAM_FREE;
+	avail_ram = (void *) (RAM_FREE + ((len + 7) & -8));
+	memcpy(cp, im, len);
+	printf("gunzipping... ");
+	gunzip(dst, 0x400000, cp, &len);
+	printf("done\n\r");
+
+    } else {
+	memmove(dst, im, len);
+    }
+
+    flush_cache(dst, len);
+
+    sa = PROG_START+12;
+    printf("start address = 0x%x\n\r", sa);
+
+#if 0
+    pause();
+#endif
+    (*(void (*)())sa)(a1, a2, prom, 0, 0);
+
+    printf("returned?\n\r");
+
+    pause();
+}
+
+void *zalloc(void *x, unsigned items, unsigned size)
+{
+    void *p = avail_ram;
+
+    size *= items;
+    size = (size + 7) & -8;
+    avail_ram += size;
+    if (avail_ram > end_avail) {
+	printf("oops... out of memory\n\r");
+	pause();
+    }
+    return p;
+}
+
+void zfree(void *x, void *addr, unsigned nb)
+{
+}
+
+#define HEAD_CRC	2
+#define EXTRA_FIELD	4
+#define ORIG_NAME	8
+#define COMMENT		0x10
+#define RESERVED	0xe0
+
+#define DEFLATED	8
+
+void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
+{
+    z_stream s;
+    int r, i, flags;
+
+    /* skip header */
+    i = 10;
+    flags = src[3];
+    if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
+	printf("bad gzipped data\n\r");
+	exit();
+    }
+    if ((flags & EXTRA_FIELD) != 0)
+	i = 12 + src[10] + (src[11] << 8);
+    if ((flags & ORIG_NAME) != 0)
+	while (src[i++] != 0)
+	    ;
+    if ((flags & COMMENT) != 0)
+	while (src[i++] != 0)
+	    ;
+    if ((flags & HEAD_CRC) != 0)
+	i += 2;
+    if (i >= *lenp) {
+	printf("gunzip: ran out of data in header\n\r");
+	exit();
+    }
+
+    s.zalloc = zalloc;
+    s.zfree = zfree;
+    r = inflateInit2(&s, -MAX_WBITS);
+    if (r != Z_OK) {
+	printf("inflateInit2 returned %d\n\r", r);
+	exit();
+    }
+    s.next_in = src + i;
+    s.avail_in = *lenp - i;
+    s.next_out = dst;
+    s.avail_out = dstlen;
+    r = inflate(&s, Z_FINISH);
+    if (r != Z_OK && r != Z_STREAM_END) {
+	printf("inflate returned %d\n\r", r);
+	exit();
+    }
+    *lenp = s.next_out - (unsigned char *) dst;
+    inflateEnd(&s);
+}
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/chrpboot/misc.S linux/arch/ppc/chrpboot/misc.S
--- v2.1.78/linux/arch/ppc/chrpboot/misc.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrpboot/misc.S	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) Paul Mackerras 1997.
+ *
+ * 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.
+ */
+	.text
+
+/*
+ * Use the BAT0 registers to map the 1st 8MB of RAM to 0x90000000.
+ */
+	.globl	setup_bats
+setup_bats:
+	mfpvr	3
+	rlwinm	3,3,16,16,31		/* r3 = 1 for 601, 4 for 604 */
+	cmpi	0,3,1
+	lis	4,0x9000
+	bne	4f
+	ori	4,4,4			/* set up BAT registers for 601 */
+	li	5,0x7f
+	b	5f
+4:	ori	4,4,0xff		/* set up BAT registers for 604 */
+	li	5,2
+	mtdbatu	0,4
+	mtdbatl	0,5
+5:	mtibatu	0,4
+	mtibatl	0,5
+	isync
+	blr
+
+/*
+ * Flush the dcache and invalidate the icache for a range of addresses.
+ *
+ * flush_cache(addr, len)
+ */
+	.global	flush_cache
+flush_cache:
+	addi	4,4,0x1f	/* len = (len + 0x1f) / 0x20 */
+	rlwinm.	4,4,27,5,31
+	mtctr	4
+	beqlr
+1:	dcbf	0,3
+	icbi	0,3
+	addi	3,3,0x20
+	bdnz	1b
+	sync
+	isync
+	blr
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/chrpboot/mknote.c linux/arch/ppc/chrpboot/mknote.c
--- v2.1.78/linux/arch/ppc/chrpboot/mknote.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrpboot/mknote.c	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,25 @@
+struct desc
+{
+	unsigned long namesz;
+	unsigned long descrsz;
+	unsigned long type;
+	char name[8];
+	unsigned long real_mode;
+	unsigned long real_base;
+	unsigned long real_size;
+	unsigned long virt_base;
+	unsigned long virt_size;
+	unsigned long load_base;
+};
+
+int main(void)
+{
+	struct desc ns;
+	ns.namesz = 8;
+	ns.descrsz = 24;
+	ns.type = 0x1275;
+	strcpy(ns.name, "PowerPC");
+	ns.load_base = -1;
+	write( 1, &ns, sizeof(struct desc));
+	return 0;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/chrpboot/no_initrd.c linux/arch/ppc/chrpboot/no_initrd.c
--- v2.1.78/linux/arch/ppc/chrpboot/no_initrd.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrpboot/no_initrd.c	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,2 @@
+char initrd_data[1];
+int initrd_len = 0;
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/chrpboot/nonstdio.h linux/arch/ppc/chrpboot/nonstdio.h
--- v2.1.78/linux/arch/ppc/chrpboot/nonstdio.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrpboot/nonstdio.h	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) Paul Mackerras 1997.
+ *
+ * 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.
+ */
+typedef int	FILE;
+extern FILE *stdin, *stdout;
+#define NULL	((void *)0)
+#define EOF	(-1)
+#define fopen(n, m)	NULL
+#define fflush(f)	0
+#define fclose(f)	0
+extern char *fgets();
+
+#define perror(s)	printf("%s: no files!\n", (s))
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/chrpboot/piggyback.c linux/arch/ppc/chrpboot/piggyback.c
--- v2.1.78/linux/arch/ppc/chrpboot/piggyback.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrpboot/piggyback.c	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,65 @@
+#include <stdio.h>
+
+extern long ce_exec_config[];
+
+main(int argc, char *argv[])
+{
+	int i, cnt, pos, len;
+	unsigned int cksum, val;
+	unsigned char *lp;
+	unsigned char buf[8192];
+	if (argc != 2)
+	{
+		fprintf(stderr, "usage: %s name <in-file >out-file\n",
+			argv[0]);
+		exit(1);
+	}
+	fprintf(stdout, "#\n");
+	fprintf(stdout, "# Miscellaneous data structures:\n");
+	fprintf(stdout, "# WARNING - this file is automatically generated!\n");
+	fprintf(stdout, "#\n");
+	fprintf(stdout, "\n");
+	fprintf(stdout, "\t.data\n");
+	fprintf(stdout, "\t.globl %s_data\n", argv[1]);
+	fprintf(stdout, "%s_data:\n", argv[1]);
+	pos = 0;
+	cksum = 0;
+	while ((len = read(0, buf, sizeof(buf))) > 0)
+	{
+		cnt = 0;
+		lp = (unsigned char *)buf;
+		len = (len + 3) & ~3;  /* Round up to longwords */
+		for (i = 0;  i < len;  i += 4)
+		{
+			if (cnt == 0)
+			{
+				fprintf(stdout, "\t.long\t");
+			}
+			fprintf(stdout, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]);
+			val = *(unsigned long *)lp;
+			cksum ^= val;
+			lp += 4;
+			if (++cnt == 4)
+			{
+				cnt = 0;
+				fprintf(stdout, " # %x \n", pos+i-12);
+				fflush(stdout);
+			} else
+			{
+				fprintf(stdout, ",");
+			}
+		}
+		if (cnt)
+		{
+			fprintf(stdout, "0\n");
+		}
+		pos += len;
+	}
+	fprintf(stdout, "\t.globl %s_len\n", argv[1]);
+	fprintf(stdout, "%s_len:\t.long\t0x%x\n", argv[1], pos);
+	fflush(stdout);
+	fclose(stdout);
+	fprintf(stderr, "cksum = %x\n", cksum);
+	exit(0);
+}
+
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/chrpboot/start.c linux/arch/ppc/chrpboot/start.c
--- v2.1.78/linux/arch/ppc/chrpboot/start.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrpboot/start.c	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) Paul Mackerras 1997.
+ *
+ * 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 <stdarg.h>
+
+int (*prom)();
+
+void *chosen_handle;
+void *stdin;
+void *stdout;
+void *stderr;
+
+void exit(void);
+void *finddevice(const char *name);
+int getprop(void *phandle, const char *name, void *buf, int buflen);
+
+void printk(char *fmt, ...);
+
+void
+start(int a1, int a2, void *promptr)
+{
+    prom = (int (*)()) promptr;
+    chosen_handle = finddevice("/chosen");
+    if (chosen_handle == (void *) -1)
+	exit();
+    if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4)
+	exit();
+    stderr = stdout;
+    if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4)
+	exit();
+
+    chrpboot(a1, a2, promptr);
+    for (;;)
+	exit();
+}
+
+int
+write(void *handle, void *ptr, int nb)
+{
+    struct prom_args {
+	char *service;
+	int nargs;
+	int nret;
+	void *ihandle;
+	void *addr;
+	int len;
+	int actual;
+    } args;
+
+    args.service = "write";
+    args.nargs = 3;
+    args.nret = 1;
+    args.ihandle = handle;
+    args.addr = ptr;
+    args.len = nb;
+    args.actual = -1;
+    (*prom)(&args);
+    return args.actual;
+}
+
+int
+read(void *handle, void *ptr, int nb)
+{
+    struct prom_args {
+	char *service;
+	int nargs;
+	int nret;
+	void *ihandle;
+	void *addr;
+	int len;
+	int actual;
+    } args;
+
+    args.service = "read";
+    args.nargs = 3;
+    args.nret = 1;
+    args.ihandle = handle;
+    args.addr = ptr;
+    args.len = nb;
+    args.actual = -1;
+    (*prom)(&args);
+    return args.actual;
+}
+
+void
+exit()
+{
+    struct prom_args {
+	char *service;
+    } args;
+
+    for (;;) {
+	args.service = "exit";
+	(*prom)(&args);
+    }
+}
+
+void
+pause()
+{
+    struct prom_args {
+	char *service;
+    } args;
+
+    args.service = "enter";
+    (*prom)(&args);
+}
+
+void *
+finddevice(const char *name)
+{
+    struct prom_args {
+	char *service;
+	int nargs;
+	int nret;
+	const char *devspec;
+	void *phandle;
+    } args;
+
+    args.service = "finddevice";
+    args.nargs = 1;
+    args.nret = 1;
+    args.devspec = name;
+    args.phandle = (void *) -1;
+    (*prom)(&args);
+    return args.phandle;
+}
+
+int
+getprop(void *phandle, const char *name, void *buf, int buflen)
+{
+    struct prom_args {
+	char *service;
+	int nargs;
+	int nret;
+	void *phandle;
+	const char *name;
+	void *buf;
+	int buflen;
+	int size;
+    } args;
+
+    args.service = "getprop";
+    args.nargs = 4;
+    args.nret = 1;
+    args.phandle = phandle;
+    args.name = name;
+    args.buf = buf;
+    args.buflen = buflen;
+    args.size = -1;
+    (*prom)(&args);
+    return args.size;
+}
+
+int
+putc(int c, void *f)
+{
+    char ch = c;
+
+    if (c == '\n')
+	putc('\r', f);
+    return write(f, &ch, 1) == 1? c: -1;
+}
+
+int
+putchar(int c)
+{
+    return putc(c, stdout);
+}
+
+int
+fputs(char *str, void *f)
+{
+    int n = strlen(str);
+
+    return write(f, str, n) == n? 0: -1;
+}
+
+int
+readchar()
+{
+    char ch;
+
+    for (;;) {
+	switch (read(stdin, &ch, 1)) {
+	case 1:
+	    return ch;
+	case -1:
+	    printk("read(stdin) returned -1\r\n");
+	    return -1;
+	}
+    }
+}
+
+static char line[256];
+static char *lineptr;
+static int lineleft;
+
+int
+getchar()
+{
+    int c;
+
+    if (lineleft == 0) {
+	lineptr = line;
+	for (;;) {
+	    c = readchar();
+	    if (c == -1 || c == 4)
+		break;
+	    if (c == '\r' || c == '\n') {
+		*lineptr++ = '\n';
+		putchar('\n');
+		break;
+	    }
+	    switch (c) {
+	    case 0177:
+	    case '\b':
+		if (lineptr > line) {
+		    putchar('\b');
+		    putchar(' ');
+		    putchar('\b');
+		    --lineptr;
+		}
+		break;
+	    case 'U' & 0x1F:
+		while (lineptr > line) {
+		    putchar('\b');
+		    putchar(' ');
+		    putchar('\b');
+		    --lineptr;
+		}
+		break;
+	    default:
+		if (lineptr >= &line[sizeof(line) - 1])
+		    putchar('\a');
+		else {
+		    putchar(c);
+		    *lineptr++ = c;
+		}
+	    }
+	}
+	lineleft = lineptr - line;
+	lineptr = line;
+    }
+    if (lineleft == 0)
+	return -1;
+    --lineleft;
+    return *lineptr++;
+}
+
+extern int vsprintf(char *buf, const char *fmt, va_list args);
+static char sprint_buf[1024];
+
+void
+printk(char *fmt, ...)
+{
+	va_list args;
+	int n;
+
+	va_start(args, fmt);
+	n = vsprintf(sprint_buf, fmt, args);
+	va_end(args);
+	write(stdout, sprint_buf, n);
+}
+
+int
+printf(char *fmt, ...)
+{
+	va_list args;
+	int n;
+
+	va_start(args, fmt);
+	n = vsprintf(sprint_buf, fmt, args);
+	va_end(args);
+	write(stdout, sprint_buf, n);
+	return n;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/chrpboot/string.S linux/arch/ppc/chrpboot/string.S
--- v2.1.78/linux/arch/ppc/chrpboot/string.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrpboot/string.S	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,206 @@
+/*
+ * String handling functions for PowerPC.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ * 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.
+ */
+#define r0	0
+#define r3	3
+#define r4	4
+#define r5	5
+#define r6	6
+#define r7	7
+#define r8	8
+
+	.globl	strcpy
+strcpy:
+	addi	r5,r3,-1
+	addi	r4,r4,-1
+1:	lbzu	r0,1(r4)
+	cmpwi	0,r0,0
+	stbu	r0,1(r5)
+	bne	1b
+	blr
+
+	.globl	strncpy
+strncpy:
+	cmpwi	0,r5,0
+	beqlr
+	mtctr	r5
+	addi	r6,r3,-1
+	addi	r4,r4,-1
+1:	lbzu	r0,1(r4)
+	cmpwi	0,r0,0
+	stbu	r0,1(r6)
+	bdnzf	2,1b		/* dec ctr, branch if ctr != 0 && !cr0.eq */
+	blr
+
+	.globl	strcat
+strcat:
+	addi	r5,r3,-1
+	addi	r4,r4,-1
+1:	lbzu	r0,1(r5)
+	cmpwi	0,r0,0
+	bne	1b
+	addi	r5,r5,-1
+1:	lbzu	r0,1(r4)
+	cmpwi	0,r0,0
+	stbu	r0,1(r5)
+	bne	1b
+	blr
+
+	.globl	strcmp
+strcmp:
+	addi	r5,r3,-1
+	addi	r4,r4,-1
+1:	lbzu	r3,1(r5)
+	cmpwi	1,r3,0
+	lbzu	r0,1(r4)
+	subf.	r3,r0,r3
+	beqlr	1
+	beq	1b
+	blr
+
+	.globl	strlen
+strlen:
+	addi	r4,r3,-1
+1:	lbzu	r0,1(r4)
+	cmpwi	0,r0,0
+	bne	1b
+	subf	r3,r3,r4
+	blr
+
+	.globl	memset
+memset:
+	rlwimi	r4,r4,8,16,23
+	rlwimi	r4,r4,16,0,15
+	addi	r6,r3,-4
+	cmplwi	0,r5,4
+	blt	7f
+	stwu	r4,4(r6)
+	beqlr
+	andi.	r0,r6,3
+	add	r5,r0,r5
+	subf	r6,r0,r6
+	rlwinm	r0,r5,32-2,2,31
+	mtctr	r0
+	bdz	6f
+1:	stwu	r4,4(r6)
+	bdnz	1b
+6:	andi.	r5,r5,3
+7:	cmpwi	0,r5,0
+	beqlr
+	mtctr	r5
+	addi	r6,r6,3
+8:	stbu	r4,1(r6)
+	bdnz	8b
+	blr
+
+	.globl	bcopy
+bcopy:
+	mr	r6,r3
+	mr	r3,r4
+	mr	r4,r6
+	b	memcpy
+
+	.globl	memmove
+memmove:
+	cmplw	0,r3,r4
+	bgt	backwards_memcpy
+	/* fall through */
+
+	.globl	memcpy
+memcpy:
+	rlwinm.	r7,r5,32-3,3,31		/* r0 = r5 >> 3 */
+	addi	r6,r3,-4
+	addi	r4,r4,-4
+	beq	2f			/* if less than 8 bytes to do */
+	andi.	r0,r6,3			/* get dest word aligned */
+	mtctr	r7
+	bne	5f
+1:	lwz	r7,4(r4)
+	lwzu	r8,8(r4)
+	stw	r7,4(r6)
+	stwu	r8,8(r6)
+	bdnz	1b
+	andi.	r5,r5,7
+2:	cmplwi	0,r5,4
+	blt	3f
+	lwzu	r0,4(r4)
+	addi	r5,r5,-4
+	stwu	r0,4(r6)
+3:	cmpwi	0,r5,0
+	beqlr
+	mtctr	r5
+	addi	r4,r4,3
+	addi	r6,r6,3
+4:	lbzu	r0,1(r4)
+	stbu	r0,1(r6)
+	bdnz	4b
+	blr
+5:	subfic	r0,r0,4
+	mtctr	r0
+6:	lbz	r7,4(r4)
+	addi	r4,r4,1
+	stb	r7,4(r6)
+	addi	r6,r6,1
+	bdnz	6b
+	subf	r5,r0,r5
+	rlwinm.	r7,r5,32-3,3,31
+	beq	2b
+	mtctr	r7
+	b	1b
+
+	.globl	backwards_memcpy
+backwards_memcpy:
+	rlwinm.	r7,r5,32-3,3,31		/* r0 = r5 >> 3 */
+	add	r6,r3,r5
+	add	r4,r4,r5
+	beq	2f
+	andi.	r0,r6,3
+	mtctr	r7
+	bne	5f
+1:	lwz	r7,-4(r4)
+	lwzu	r8,-8(r4)
+	stw	r7,-4(r6)
+	stwu	r8,-8(r6)
+	bdnz	1b
+	andi.	r5,r5,7
+2:	cmplwi	0,r5,4
+	blt	3f
+	lwzu	r0,-4(r4)
+	subi	r5,r5,4
+	stwu	r0,-4(r6)
+3:	cmpwi	0,r5,0
+	beqlr
+	mtctr	r5
+4:	lbzu	r0,-1(r4)
+	stbu	r0,-1(r6)
+	bdnz	4b
+	blr
+5:	mtctr	r0
+6:	lbzu	r7,-1(r4)
+	stbu	r7,-1(r6)
+	bdnz	6b
+	subf	r5,r0,r5
+	rlwinm.	r7,r5,32-3,3,31
+	beq	2b
+	mtctr	r7
+	b	1b
+
+	.globl	memcmp
+memcmp:
+	cmpwi	0,r5,0
+	blelr
+	mtctr	r5
+	addi	r6,r3,-1
+	addi	r4,r4,-1
+1:	lbzu	r3,1(r6)
+	lbzu	r0,1(r4)
+	subf.	r3,r0,r3
+	bdnzt	2,1b
+	blr
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/chrpboot/zlib.c linux/arch/ppc/chrpboot/zlib.c
--- v2.1.78/linux/arch/ppc/chrpboot/zlib.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrpboot/zlib.c	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,2143 @@
+/*
+ * This file is derived from various .h and .c files from the zlib-0.95
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets.  See zlib.h for conditions of
+ * distribution and use.
+ *
+ * Changes that have been made include:
+ * - changed functions not used outside this file to "local"
+ * - added minCompression parameter to deflateInit2
+ * - added Z_PACKET_FLUSH (see zlib.h for details)
+ * - added inflateIncomp
+ *
+ * $Id: zlib.c,v 1.1 1997/09/19 07:03:44 paulus Exp $
+ */
+
+/*+++++*/
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* From: zutil.h,v 1.9 1995/05/03 17:27:12 jloup Exp */
+
+#define _Z_UTIL_H
+
+#include "zlib.h"
+
+#ifndef local
+#  define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+#define FAR
+
+typedef unsigned char  uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long  ulg;
+
+extern char *z_errmsg[]; /* indexed by 1-zlib_error */
+
+#define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err)
+/* To be used only when the state is known to be valid */
+
+#ifndef NULL
+#define NULL	((void *) 0)
+#endif
+
+        /* common constants */
+
+#define DEFLATED   8
+
+#ifndef DEF_WBITS
+#  define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+#  define DEF_MEM_LEVEL 8
+#else
+#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES    2
+/* The three kinds of block type */
+
+#define MIN_MATCH  3
+#define MAX_MATCH  258
+/* The minimum and maximum match lengths */
+
+         /* functions */
+
+#include <string.h>
+#define zmemcpy memcpy
+#define zmemzero(dest, len)	memset(dest, 0, len)
+
+/* Diagnostic functions */
+#ifdef DEBUG_ZLIB
+#  include <stdio.h>
+#  ifndef verbose
+#    define verbose 0
+#  endif
+#  define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+#  define Trace(x) fprintf x
+#  define Tracev(x) {if (verbose) fprintf x ;}
+#  define Tracevv(x) {if (verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+
+typedef uLong (*check_func) OF((uLong check, Bytef *buf, uInt len));
+
+/* voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); */
+/* void   zcfree  OF((voidpf opaque, voidpf ptr)); */
+
+#define ZALLOC(strm, items, size) \
+           (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr, size)	\
+	   (*((strm)->zfree))((strm)->opaque, (voidpf)(addr), (size))
+#define TRY_FREE(s, p, n) {if (p) ZFREE(s, p, n);}
+
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/*+++++*/
+/* infblock.h -- header to use infblock.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_blocks_state;
+typedef struct inflate_blocks_state FAR inflate_blocks_statef;
+
+local inflate_blocks_statef * inflate_blocks_new OF((
+    z_stream *z,
+    check_func c,               /* check function */
+    uInt w));                   /* window size */
+
+local int inflate_blocks OF((
+    inflate_blocks_statef *,
+    z_stream *,
+    int));                      /* initial return code */
+
+local void inflate_blocks_reset OF((
+    inflate_blocks_statef *,
+    z_stream *,
+    uLongf *));                  /* check value on output */
+
+local int inflate_blocks_free OF((
+    inflate_blocks_statef *,
+    z_stream *,
+    uLongf *));                  /* check value on output */
+
+local int inflate_addhistory OF((
+    inflate_blocks_statef *,
+    z_stream *));
+
+local int inflate_packet_flush OF((
+    inflate_blocks_statef *));
+
+/*+++++*/
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* Huffman code lookup table entry--this entry is four bytes for machines
+   that have 16-bit pointers (e.g. PC's in the small or medium model). */
+
+typedef struct inflate_huft_s FAR inflate_huft;
+
+struct inflate_huft_s {
+  union {
+    struct {
+      Byte Exop;        /* number of extra bits or operation */
+      Byte Bits;        /* number of bits in this code or subcode */
+    } what;
+    uInt Nalloc;	/* number of these allocated here */
+    Bytef *pad;         /* pad structure to a power of 2 (4 bytes for */
+  } word;               /*  16-bit, 8 bytes for 32-bit machines) */
+  union {
+    uInt Base;          /* literal, length base, or distance base */
+    inflate_huft *Next; /* pointer to next level of table */
+  } more;
+};
+
+#ifdef DEBUG_ZLIB
+  local uInt inflate_hufts;
+#endif
+
+local int inflate_trees_bits OF((
+    uIntf *,                    /* 19 code lengths */
+    uIntf *,                    /* bits tree desired/actual depth */
+    inflate_huft * FAR *,       /* bits tree result */
+    z_stream *));               /* for zalloc, zfree functions */
+
+local int inflate_trees_dynamic OF((
+    uInt,                       /* number of literal/length codes */
+    uInt,                       /* number of distance codes */
+    uIntf *,                    /* that many (total) code lengths */
+    uIntf *,                    /* literal desired/actual bit depth */
+    uIntf *,                    /* distance desired/actual bit depth */
+    inflate_huft * FAR *,       /* literal/length tree result */
+    inflate_huft * FAR *,       /* distance tree result */
+    z_stream *));               /* for zalloc, zfree functions */
+
+local int inflate_trees_fixed OF((
+    uIntf *,                    /* literal desired/actual bit depth */
+    uIntf *,                    /* distance desired/actual bit depth */
+    inflate_huft * FAR *,       /* literal/length tree result */
+    inflate_huft * FAR *));     /* distance tree result */
+
+local int inflate_trees_free OF((
+    inflate_huft *,             /* tables to free */
+    z_stream *));               /* for zfree function */
+
+
+/*+++++*/
+/* infcodes.h -- header to use infcodes.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_codes_state;
+typedef struct inflate_codes_state FAR inflate_codes_statef;
+
+local inflate_codes_statef *inflate_codes_new OF((
+    uInt, uInt,
+    inflate_huft *, inflate_huft *,
+    z_stream *));
+
+local int inflate_codes OF((
+    inflate_blocks_statef *,
+    z_stream *,
+    int));
+
+local void inflate_codes_free OF((
+    inflate_codes_statef *,
+    z_stream *));
+
+
+/*+++++*/
+/* inflate.c -- zlib interface to inflate modules
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* inflate private state */
+struct internal_state {
+
+  /* mode */
+  enum {
+      METHOD,   /* waiting for method byte */
+      FLAG,     /* waiting for flag byte */
+      BLOCKS,   /* decompressing blocks */
+      CHECK4,   /* four check bytes to go */
+      CHECK3,   /* three check bytes to go */
+      CHECK2,   /* two check bytes to go */
+      CHECK1,   /* one check byte to go */
+      DONE,     /* finished check, done */
+      BAD}      /* got an error--stay here */
+    mode;               /* current inflate mode */
+
+  /* mode dependent information */
+  union {
+    uInt method;        /* if FLAGS, method byte */
+    struct {
+      uLong was;                /* computed check value */
+      uLong need;               /* stream check value */
+    } check;            /* if CHECK, check values to compare */
+    uInt marker;        /* if BAD, inflateSync's marker bytes count */
+  } sub;        /* submode */
+
+  /* mode independent information */
+  int  nowrap;          /* flag for no wrapper */
+  uInt wbits;           /* log2(window size)  (8..15, defaults to 15) */
+  inflate_blocks_statef 
+    *blocks;            /* current inflate_blocks state */
+
+};
+
+
+int inflateReset(z)
+z_stream *z;
+{
+  uLong c;
+
+  if (z == Z_NULL || z->state == Z_NULL)
+    return Z_STREAM_ERROR;
+  z->total_in = z->total_out = 0;
+  z->msg = Z_NULL;
+  z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
+  inflate_blocks_reset(z->state->blocks, z, &c);
+  Trace((stderr, "inflate: reset\n"));
+  return Z_OK;
+}
+
+
+int inflateEnd(z)
+z_stream *z;
+{
+  uLong c;
+
+  if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
+    return Z_STREAM_ERROR;
+  if (z->state->blocks != Z_NULL)
+    inflate_blocks_free(z->state->blocks, z, &c);
+  ZFREE(z, z->state, sizeof(struct internal_state));
+  z->state = Z_NULL;
+  Trace((stderr, "inflate: end\n"));
+  return Z_OK;
+}
+
+
+int inflateInit2(z, w)
+z_stream *z;
+int w;
+{
+  /* initialize state */
+  if (z == Z_NULL)
+    return Z_STREAM_ERROR;
+/*  if (z->zalloc == Z_NULL) z->zalloc = zcalloc; */
+/*  if (z->zfree == Z_NULL) z->zfree = zcfree; */
+  if ((z->state = (struct internal_state FAR *)
+       ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
+    return Z_MEM_ERROR;
+  z->state->blocks = Z_NULL;
+
+  /* handle undocumented nowrap option (no zlib header or check) */
+  z->state->nowrap = 0;
+  if (w < 0)
+  {
+    w = - w;
+    z->state->nowrap = 1;
+  }
+
+  /* set window size */
+  if (w < 8 || w > 15)
+  {
+    inflateEnd(z);
+    return Z_STREAM_ERROR;
+  }
+  z->state->wbits = (uInt)w;
+
+  /* create inflate_blocks state */
+  if ((z->state->blocks =
+       inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, 1 << w))
+      == Z_NULL)
+  {
+    inflateEnd(z);
+    return Z_MEM_ERROR;
+  }
+  Trace((stderr, "inflate: allocated\n"));
+
+  /* reset state */
+  inflateReset(z);
+  return Z_OK;
+}
+
+
+int inflateInit(z)
+z_stream *z;
+{
+  return inflateInit2(z, DEF_WBITS);
+}
+
+
+#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;}
+#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
+
+int inflate(z, f)
+z_stream *z;
+int f;
+{
+  int r;
+  uInt b;
+
+  if (z == Z_NULL || z->next_in == Z_NULL)
+    return Z_STREAM_ERROR;
+  r = Z_BUF_ERROR;
+  while (1) switch (z->state->mode)
+  {
+    case METHOD:
+      NEEDBYTE
+      if (((z->state->sub.method = NEXTBYTE) & 0xf) != DEFLATED)
+      {
+        z->state->mode = BAD;
+        z->msg = "unknown compression method";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
+      {
+        z->state->mode = BAD;
+        z->msg = "invalid window size";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      z->state->mode = FLAG;
+    case FLAG:
+      NEEDBYTE
+      if ((b = NEXTBYTE) & 0x20)
+      {
+        z->state->mode = BAD;
+        z->msg = "invalid reserved bit";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      if (((z->state->sub.method << 8) + b) % 31)
+      {
+        z->state->mode = BAD;
+        z->msg = "incorrect header check";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      Trace((stderr, "inflate: zlib header ok\n"));
+      z->state->mode = BLOCKS;
+    case BLOCKS:
+      r = inflate_blocks(z->state->blocks, z, r);
+      if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0)
+	  r = inflate_packet_flush(z->state->blocks);
+      if (r == Z_DATA_ERROR)
+      {
+        z->state->mode = BAD;
+        z->state->sub.marker = 0;       /* can try inflateSync */
+        break;
+      }
+      if (r != Z_STREAM_END)
+        return r;
+      r = Z_OK;
+      inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
+      if (z->state->nowrap)
+      {
+        z->state->mode = DONE;
+        break;
+      }
+      z->state->mode = CHECK4;
+    case CHECK4:
+      NEEDBYTE
+      z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+      z->state->mode = CHECK3;
+    case CHECK3:
+      NEEDBYTE
+      z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+      z->state->mode = CHECK2;
+    case CHECK2:
+      NEEDBYTE
+      z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+      z->state->mode = CHECK1;
+    case CHECK1:
+      NEEDBYTE
+      z->state->sub.check.need += (uLong)NEXTBYTE;
+
+      if (z->state->sub.check.was != z->state->sub.check.need)
+      {
+        z->state->mode = BAD;
+        z->msg = "incorrect data check";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      Trace((stderr, "inflate: zlib check ok\n"));
+      z->state->mode = DONE;
+    case DONE:
+      return Z_STREAM_END;
+    case BAD:
+      return Z_DATA_ERROR;
+    default:
+      return Z_STREAM_ERROR;
+  }
+
+ empty:
+  if (f != Z_PACKET_FLUSH)
+    return r;
+  z->state->mode = BAD;
+  z->state->sub.marker = 0;       /* can try inflateSync */
+  return Z_DATA_ERROR;
+}
+
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output.  The output buffer must be "caught up";
+ * i.e. no pending output (hence s->read equals s->write), and the state must
+ * be BLOCKS (i.e. we should be willing to see the start of a series of
+ * BLOCKS).  On exit, the output will also be caught up, and the checksum
+ * will have been updated if need be.
+ */
+
+int inflateIncomp(z)
+z_stream *z;
+{
+    if (z->state->mode != BLOCKS)
+	return Z_DATA_ERROR;
+    return inflate_addhistory(z->state->blocks, z);
+}
+
+
+int inflateSync(z)
+z_stream *z;
+{
+  uInt n;       /* number of bytes to look at */
+  Bytef *p;     /* pointer to bytes */
+  uInt m;       /* number of marker bytes found in a row */
+  uLong r, w;   /* temporaries to save total_in and total_out */
+
+  /* set up */
+  if (z == Z_NULL || z->state == Z_NULL)
+    return Z_STREAM_ERROR;
+  if (z->state->mode != BAD)
+  {
+    z->state->mode = BAD;
+    z->state->sub.marker = 0;
+  }
+  if ((n = z->avail_in) == 0)
+    return Z_BUF_ERROR;
+  p = z->next_in;
+  m = z->state->sub.marker;
+
+  /* search */
+  while (n && m < 4)
+  {
+    if (*p == (Byte)(m < 2 ? 0 : 0xff))
+      m++;
+    else if (*p)
+      m = 0;
+    else
+      m = 4 - m;
+    p++, n--;
+  }
+
+  /* restore */
+  z->total_in += p - z->next_in;
+  z->next_in = p;
+  z->avail_in = n;
+  z->state->sub.marker = m;
+
+  /* return no joy or set up to restart on a new block */
+  if (m != 4)
+    return Z_DATA_ERROR;
+  r = z->total_in;  w = z->total_out;
+  inflateReset(z);
+  z->total_in = r;  z->total_out = w;
+  z->state->mode = BLOCKS;
+  return Z_OK;
+}
+
+#undef NEEDBYTE
+#undef NEXTBYTE
+
+/*+++++*/
+/* infutil.h -- types and macros common to blocks and codes
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* inflate blocks semi-private state */
+struct inflate_blocks_state {
+
+  /* mode */
+  enum {
+      TYPE,     /* get type bits (3, including end bit) */
+      LENS,     /* get lengths for stored */
+      STORED,   /* processing stored block */
+      TABLE,    /* get table lengths */
+      BTREE,    /* get bit lengths tree for a dynamic block */
+      DTREE,    /* get length, distance trees for a dynamic block */
+      CODES,    /* processing fixed or dynamic block */
+      DRY,      /* output remaining window bytes */
+      DONEB,     /* finished last block, done */
+      BADB}      /* got a data error--stuck here */
+    mode;               /* current inflate_block mode */
+
+  /* mode dependent information */
+  union {
+    uInt left;          /* if STORED, bytes left to copy */
+    struct {
+      uInt table;               /* table lengths (14 bits) */
+      uInt index;               /* index into blens (or border) */
+      uIntf *blens;             /* bit lengths of codes */
+      uInt bb;                  /* bit length tree depth */
+      inflate_huft *tb;         /* bit length decoding tree */
+      int nblens;		/* # elements allocated at blens */
+    } trees;            /* if DTREE, decoding info for trees */
+    struct {
+      inflate_huft *tl, *td;    /* trees to free */
+      inflate_codes_statef 
+         *codes;
+    } decode;           /* if CODES, current state */
+  } sub;                /* submode */
+  uInt last;            /* true if this block is the last block */
+
+  /* mode independent information */
+  uInt bitk;            /* bits in bit buffer */
+  uLong bitb;           /* bit buffer */
+  Bytef *window;        /* sliding window */
+  Bytef *end;           /* one byte after sliding window */
+  Bytef *read;          /* window read pointer */
+  Bytef *write;         /* window write pointer */
+  check_func checkfn;   /* check function */
+  uLong check;          /* check on output */
+
+};
+
+
+/* defines for inflate input/output */
+/*   update pointers and return */
+#define UPDBITS {s->bitb=b;s->bitk=k;}
+#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
+#define UPDOUT {s->write=q;}
+#define UPDATE {UPDBITS UPDIN UPDOUT}
+#define LEAVE {UPDATE return inflate_flush(s,z,r);}
+/*   get bytes and bits */
+#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
+#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
+#define NEXTBYTE (n--,*p++)
+#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define DUMPBITS(j) {b>>=(j);k-=(j);}
+/*   output bytes */
+#define WAVAIL (q<s->read?s->read-q-1:s->end-q)
+#define LOADOUT {q=s->write;m=WAVAIL;}
+#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=WAVAIL;}}
+#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
+#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
+#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
+/*   load local pointers */
+#define LOAD {LOADIN LOADOUT}
+
+/* And'ing with mask[n] masks the lower n bits */
+local uInt inflate_mask[] = {
+    0x0000,
+    0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+    0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+/* copy as much as possible from the sliding window to the output area */
+local int inflate_flush OF((
+    inflate_blocks_statef *,
+    z_stream *,
+    int));
+
+/*+++++*/
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+local int inflate_fast OF((
+    uInt,
+    uInt,
+    inflate_huft *,
+    inflate_huft *,
+    inflate_blocks_statef *,
+    z_stream *));
+
+
+/*+++++*/
+/* infblock.c -- interpret and process block types to last block
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* Table for deflate from PKZIP's appnote.txt. */
+local uInt border[] = { /* Order of the bit length code lengths */
+        16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+/*
+   Notes beyond the 1.93a appnote.txt:
+
+   1. Distance pointers never point before the beginning of the output
+      stream.
+   2. Distance pointers can point back across blocks, up to 32k away.
+   3. There is an implied maximum of 7 bits for the bit length table and
+      15 bits for the actual data.
+   4. If only one code exists, then it is encoded using one bit.  (Zero
+      would be more efficient, but perhaps a little confusing.)  If two
+      codes exist, they are coded using one bit each (0 and 1).
+   5. There is no way of sending zero distance codes--a dummy must be
+      sent if there are none.  (History: a pre 2.0 version of PKZIP would
+      store blocks with no distance codes, but this was discovered to be
+      too harsh a criterion.)  Valid only for 1.93a.  2.04c does allow
+      zero distance codes, which is sent as one code of zero bits in
+      length.
+   6. There are up to 286 literal/length codes.  Code 256 represents the
+      end-of-block.  Note however that the static length tree defines
+      288 codes just to fill out the Huffman codes.  Codes 286 and 287
+      cannot be used though, since there is no length base or extra bits
+      defined for them.  Similarily, there are up to 30 distance codes.
+      However, static trees define 32 codes (all 5 bits) to fill out the
+      Huffman codes, but the last two had better not show up in the data.
+   7. Unzip can check dynamic Huffman blocks for complete code sets.
+      The exception is that a single code would not be complete (see #4).
+   8. The five bits following the block type is really the number of
+      literal codes sent minus 257.
+   9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
+      (1+6+6).  Therefore, to output three times the length, you output
+      three codes (1+1+1), whereas to output four times the same length,
+      you only need two codes (1+3).  Hmm.
+  10. In the tree reconstruction algorithm, Code = Code + Increment
+      only if BitLength(i) is not zero.  (Pretty obvious.)
+  11. Correction: 4 Bits: # of Bit Length codes - 4     (4 - 19)
+  12. Note: length code 284 can represent 227-258, but length code 285
+      really is 258.  The last length deserves its own, short code
+      since it gets used a lot in very redundant files.  The length
+      258 is special since 258 - 3 (the min match length) is 255.
+  13. The literal/length and distance code bit lengths are read as a
+      single stream of lengths.  It is possible (and advantageous) for
+      a repeat code (16, 17, or 18) to go across the boundary between
+      the two sets of lengths.
+ */
+
+
+local void inflate_blocks_reset(s, z, c)
+inflate_blocks_statef *s;
+z_stream *z;
+uLongf *c;
+{
+  if (s->checkfn != Z_NULL)
+    *c = s->check;
+  if (s->mode == BTREE || s->mode == DTREE)
+    ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt));
+  if (s->mode == CODES)
+  {
+    inflate_codes_free(s->sub.decode.codes, z);
+    inflate_trees_free(s->sub.decode.td, z);
+    inflate_trees_free(s->sub.decode.tl, z);
+  }
+  s->mode = TYPE;
+  s->bitk = 0;
+  s->bitb = 0;
+  s->read = s->write = s->window;
+  if (s->checkfn != Z_NULL)
+    s->check = (*s->checkfn)(0L, Z_NULL, 0);
+  Trace((stderr, "inflate:   blocks reset\n"));
+}
+
+
+local inflate_blocks_statef *inflate_blocks_new(z, c, w)
+z_stream *z;
+check_func c;
+uInt w;
+{
+  inflate_blocks_statef *s;
+
+  if ((s = (inflate_blocks_statef *)ZALLOC
+       (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL)
+    return s;
+  if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL)
+  {
+    ZFREE(z, s, sizeof(struct inflate_blocks_state));
+    return Z_NULL;
+  }
+  s->end = s->window + w;
+  s->checkfn = c;
+  s->mode = TYPE;
+  Trace((stderr, "inflate:   blocks allocated\n"));
+  inflate_blocks_reset(s, z, &s->check);
+  return s;
+}
+
+
+local int inflate_blocks(s, z, r)
+inflate_blocks_statef *s;
+z_stream *z;
+int r;
+{
+  uInt t;               /* temporary storage */
+  uLong b;              /* bit buffer */
+  uInt k;               /* bits in bit buffer */
+  Bytef *p;             /* input data pointer */
+  uInt n;               /* bytes available there */
+  Bytef *q;             /* output window write pointer */
+  uInt m;               /* bytes to end of window or read pointer */
+
+  /* copy input/output information to locals (UPDATE macro restores) */
+  LOAD
+
+  /* process input based on current state */
+  while (1) switch (s->mode)
+  {
+    case TYPE:
+      NEEDBITS(3)
+      t = (uInt)b & 7;
+      s->last = t & 1;
+      switch (t >> 1)
+      {
+        case 0:                         /* stored */
+          Trace((stderr, "inflate:     stored block%s\n",
+                 s->last ? " (last)" : ""));
+          DUMPBITS(3)
+          t = k & 7;                    /* go to byte boundary */
+          DUMPBITS(t)
+          s->mode = LENS;               /* get length of stored block */
+          break;
+        case 1:                         /* fixed */
+          Trace((stderr, "inflate:     fixed codes block%s\n",
+                 s->last ? " (last)" : ""));
+          {
+            uInt bl, bd;
+            inflate_huft *tl, *td;
+
+            inflate_trees_fixed(&bl, &bd, &tl, &td);
+            s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
+            if (s->sub.decode.codes == Z_NULL)
+            {
+              r = Z_MEM_ERROR;
+              LEAVE
+            }
+            s->sub.decode.tl = Z_NULL;  /* don't try to free these */
+            s->sub.decode.td = Z_NULL;
+          }
+          DUMPBITS(3)
+          s->mode = CODES;
+          break;
+        case 2:                         /* dynamic */
+          Trace((stderr, "inflate:     dynamic codes block%s\n",
+                 s->last ? " (last)" : ""));
+          DUMPBITS(3)
+          s->mode = TABLE;
+          break;
+        case 3:                         /* illegal */
+          DUMPBITS(3)
+          s->mode = BADB;
+          z->msg = "invalid block type";
+          r = Z_DATA_ERROR;
+          LEAVE
+      }
+      break;
+    case LENS:
+      NEEDBITS(32)
+      if (((~b) >> 16) != (b & 0xffff))
+      {
+        s->mode = BADB;
+        z->msg = "invalid stored block lengths";
+        r = Z_DATA_ERROR;
+        LEAVE
+      }
+      s->sub.left = (uInt)b & 0xffff;
+      b = k = 0;                      /* dump bits */
+      Tracev((stderr, "inflate:       stored length %u\n", s->sub.left));
+      s->mode = s->sub.left ? STORED : TYPE;
+      break;
+    case STORED:
+      if (n == 0)
+        LEAVE
+      NEEDOUT
+      t = s->sub.left;
+      if (t > n) t = n;
+      if (t > m) t = m;
+      zmemcpy(q, p, t);
+      p += t;  n -= t;
+      q += t;  m -= t;
+      if ((s->sub.left -= t) != 0)
+        break;
+      Tracev((stderr, "inflate:       stored end, %lu total out\n",
+              z->total_out + (q >= s->read ? q - s->read :
+              (s->end - s->read) + (q - s->window))));
+      s->mode = s->last ? DRY : TYPE;
+      break;
+    case TABLE:
+      NEEDBITS(14)
+      s->sub.trees.table = t = (uInt)b & 0x3fff;
+#ifndef PKZIP_BUG_WORKAROUND
+      if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
+      {
+        s->mode = BADB;
+        z->msg = "too many length or distance symbols";
+        r = Z_DATA_ERROR;
+        LEAVE
+      }
+#endif
+      t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
+      if (t < 19)
+        t = 19;
+      if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL)
+      {
+        r = Z_MEM_ERROR;
+        LEAVE
+      }
+      s->sub.trees.nblens = t;
+      DUMPBITS(14)
+      s->sub.trees.index = 0;
+      Tracev((stderr, "inflate:       table sizes ok\n"));
+      s->mode = BTREE;
+    case BTREE:
+      while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
+      {
+        NEEDBITS(3)
+        s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
+        DUMPBITS(3)
+      }
+      while (s->sub.trees.index < 19)
+        s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
+      s->sub.trees.bb = 7;
+      t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
+                             &s->sub.trees.tb, z);
+      if (t != Z_OK)
+      {
+        r = t;
+        if (r == Z_DATA_ERROR)
+          s->mode = BADB;
+        LEAVE
+      }
+      s->sub.trees.index = 0;
+      Tracev((stderr, "inflate:       bits tree ok\n"));
+      s->mode = DTREE;
+    case DTREE:
+      while (t = s->sub.trees.table,
+             s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
+      {
+        inflate_huft *h;
+        uInt i, j, c;
+
+        t = s->sub.trees.bb;
+        NEEDBITS(t)
+        h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
+        t = h->word.what.Bits;
+        c = h->more.Base;
+        if (c < 16)
+        {
+          DUMPBITS(t)
+          s->sub.trees.blens[s->sub.trees.index++] = c;
+        }
+        else /* c == 16..18 */
+        {
+          i = c == 18 ? 7 : c - 14;
+          j = c == 18 ? 11 : 3;
+          NEEDBITS(t + i)
+          DUMPBITS(t)
+          j += (uInt)b & inflate_mask[i];
+          DUMPBITS(i)
+          i = s->sub.trees.index;
+          t = s->sub.trees.table;
+          if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
+              (c == 16 && i < 1))
+          {
+            s->mode = BADB;
+            z->msg = "invalid bit length repeat";
+            r = Z_DATA_ERROR;
+            LEAVE
+          }
+          c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
+          do {
+            s->sub.trees.blens[i++] = c;
+          } while (--j);
+          s->sub.trees.index = i;
+        }
+      }
+      inflate_trees_free(s->sub.trees.tb, z);
+      s->sub.trees.tb = Z_NULL;
+      {
+        uInt bl, bd;
+        inflate_huft *tl, *td;
+        inflate_codes_statef *c;
+
+        bl = 9;         /* must be <= 9 for lookahead assumptions */
+        bd = 6;         /* must be <= 9 for lookahead assumptions */
+        t = s->sub.trees.table;
+        t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
+                                  s->sub.trees.blens, &bl, &bd, &tl, &td, z);
+        if (t != Z_OK)
+        {
+          if (t == (uInt)Z_DATA_ERROR)
+            s->mode = BADB;
+          r = t;
+          LEAVE
+        }
+        Tracev((stderr, "inflate:       trees ok\n"));
+        if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
+        {
+          inflate_trees_free(td, z);
+          inflate_trees_free(tl, z);
+          r = Z_MEM_ERROR;
+          LEAVE
+        }
+        ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt));
+        s->sub.decode.codes = c;
+        s->sub.decode.tl = tl;
+        s->sub.decode.td = td;
+      }
+      s->mode = CODES;
+    case CODES:
+      UPDATE
+      if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
+        return inflate_flush(s, z, r);
+      r = Z_OK;
+      inflate_codes_free(s->sub.decode.codes, z);
+      inflate_trees_free(s->sub.decode.td, z);
+      inflate_trees_free(s->sub.decode.tl, z);
+      LOAD
+      Tracev((stderr, "inflate:       codes end, %lu total out\n",
+              z->total_out + (q >= s->read ? q - s->read :
+              (s->end - s->read) + (q - s->window))));
+      if (!s->last)
+      {
+        s->mode = TYPE;
+        break;
+      }
+      if (k > 7)              /* return unused byte, if any */
+      {
+        Assert(k < 16, "inflate_codes grabbed too many bytes")
+        k -= 8;
+        n++;
+        p--;                    /* can always return one */
+      }
+      s->mode = DRY;
+    case DRY:
+      FLUSH
+      if (s->read != s->write)
+        LEAVE
+      s->mode = DONEB;
+    case DONEB:
+      r = Z_STREAM_END;
+      LEAVE
+    case BADB:
+      r = Z_DATA_ERROR;
+      LEAVE
+    default:
+      r = Z_STREAM_ERROR;
+      LEAVE
+  }
+}
+
+
+local int inflate_blocks_free(s, z, c)
+inflate_blocks_statef *s;
+z_stream *z;
+uLongf *c;
+{
+  inflate_blocks_reset(s, z, c);
+  ZFREE(z, s->window, s->end - s->window);
+  ZFREE(z, s, sizeof(struct inflate_blocks_state));
+  Trace((stderr, "inflate:   blocks freed\n"));
+  return Z_OK;
+}
+
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output.  The output buffer must be "caught up";
+ * i.e. no pending output (hence s->read equals s->write), and the state must
+ * be BLOCKS (i.e. we should be willing to see the start of a series of
+ * BLOCKS).  On exit, the output will also be caught up, and the checksum
+ * will have been updated if need be.
+ */
+local int inflate_addhistory(s, z)
+inflate_blocks_statef *s;
+z_stream *z;
+{
+    uLong b;              /* bit buffer */  /* NOT USED HERE */
+    uInt k;               /* bits in bit buffer */ /* NOT USED HERE */
+    uInt t;               /* temporary storage */
+    Bytef *p;             /* input data pointer */
+    uInt n;               /* bytes available there */
+    Bytef *q;             /* output window write pointer */
+    uInt m;               /* bytes to end of window or read pointer */
+
+    if (s->read != s->write)
+	return Z_STREAM_ERROR;
+    if (s->mode != TYPE)
+	return Z_DATA_ERROR;
+
+    /* we're ready to rock */
+    LOAD
+    /* while there is input ready, copy to output buffer, moving
+     * pointers as needed.
+     */
+    while (n) {
+	t = n;  /* how many to do */
+	/* is there room until end of buffer? */
+	if (t > m) t = m;
+	/* update check information */
+	if (s->checkfn != Z_NULL)
+	    s->check = (*s->checkfn)(s->check, q, t);
+	zmemcpy(q, p, t);
+	q += t;
+	p += t;
+	n -= t;
+	z->total_out += t;
+	s->read = q;    /* drag read pointer forward */
+/*      WRAP  */ 	/* expand WRAP macro by hand to handle s->read */
+	if (q == s->end) {
+	    s->read = q = s->window;
+	    m = WAVAIL;
+	}
+    }
+    UPDATE
+    return Z_OK;
+}
+
+
+/*
+ * At the end of a Deflate-compressed PPP packet, we expect to have seen
+ * a `stored' block type value but not the (zero) length bytes.
+ */
+local int inflate_packet_flush(s)
+    inflate_blocks_statef *s;
+{
+    if (s->mode != LENS)
+	return Z_DATA_ERROR;
+    s->mode = TYPE;
+    return Z_OK;
+}
+
+
+/*+++++*/
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+
+local int huft_build OF((
+    uIntf *,            /* code lengths in bits */
+    uInt,               /* number of codes */
+    uInt,               /* number of "simple" codes */
+    uIntf *,            /* list of base values for non-simple codes */
+    uIntf *,            /* list of extra bits for non-simple codes */
+    inflate_huft * FAR*,/* result: starting table */
+    uIntf *,            /* maximum lookup bits (returns actual) */
+    z_stream *));       /* for zalloc function */
+
+local voidpf falloc OF((
+    voidpf,             /* opaque pointer (not used) */
+    uInt,               /* number of items */
+    uInt));             /* size of item */
+
+local void ffree OF((
+    voidpf q,           /* opaque pointer (not used) */
+    voidpf p,           /* what to free (not used) */
+    uInt n));		/* number of bytes (not used) */
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+local uInt cplens[] = { /* Copy lengths for literal codes 257..285 */
+        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+        /* actually lengths - 2; also see note #13 above about 258 */
+local uInt cplext[] = { /* Extra bits for literal codes 257..285 */
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+        3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192}; /* 192==invalid */
+local uInt cpdist[] = { /* Copy offsets for distance codes 0..29 */
+        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+        8193, 12289, 16385, 24577};
+local uInt cpdext[] = { /* Extra bits for distance codes */
+        0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+        7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+        12, 12, 13, 13};
+
+/*
+   Huffman code decoding is performed using a multi-level table lookup.
+   The fastest way to decode is to simply build a lookup table whose
+   size is determined by the longest code.  However, the time it takes
+   to build this table can also be a factor if the data being decoded
+   is not very long.  The most common codes are necessarily the
+   shortest codes, so those codes dominate the decoding time, and hence
+   the speed.  The idea is you can have a shorter table that decodes the
+   shorter, more probable codes, and then point to subsidiary tables for
+   the longer codes.  The time it costs to decode the longer codes is
+   then traded against the time it takes to make longer tables.
+
+   This results of this trade are in the variables lbits and dbits
+   below.  lbits is the number of bits the first level table for literal/
+   length codes can decode in one step, and dbits is the same thing for
+   the distance codes.  Subsequent tables are also less than or equal to
+   those sizes.  These values may be adjusted either when all of the
+   codes are shorter than that, in which case the longest code length in
+   bits is used, or when the shortest code is *longer* than the requested
+   table size, in which case the length of the shortest code in bits is
+   used.
+
+   There are two different values for the two tables, since they code a
+   different number of possibilities each.  The literal/length table
+   codes 286 possible values, or in a flat code, a little over eight
+   bits.  The distance table codes 30 possible values, or a little less
+   than five bits, flat.  The optimum values for speed end up being
+   about one bit more than those, so lbits is 8+1 and dbits is 5+1.
+   The optimum values may differ though from machine to machine, and
+   possibly even between compilers.  Your mileage may vary.
+ */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
+#define BMAX 15         /* maximum bit length of any code */
+#define N_MAX 288       /* maximum number of codes in any set */
+
+#ifdef DEBUG_ZLIB
+  uInt inflate_hufts;
+#endif
+
+local int huft_build(b, n, s, d, e, t, m, zs)
+uIntf *b;               /* code lengths in bits (all assumed <= BMAX) */
+uInt n;                 /* number of codes (assumed <= N_MAX) */
+uInt s;                 /* number of simple-valued codes (0..s-1) */
+uIntf *d;               /* list of base values for non-simple codes */
+uIntf *e;               /* list of extra bits for non-simple codes */  
+inflate_huft * FAR *t;  /* result: starting table */
+uIntf *m;               /* maximum lookup bits, returns actual */
+z_stream *zs;           /* for zalloc function */
+/* Given a list of code lengths and a maximum table size, make a set of
+   tables to decode that set of codes.  Return Z_OK on success, Z_BUF_ERROR
+   if the given code set is incomplete (the tables are still built in this
+   case), Z_DATA_ERROR if the input is invalid (all zero length codes or an
+   over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */
+{
+
+  uInt a;                       /* counter for codes of length k */
+  uInt c[BMAX+1];               /* bit length count table */
+  uInt f;                       /* i repeats in table every f entries */
+  int g;                        /* maximum code length */
+  int h;                        /* table level */
+  register uInt i;              /* counter, current code */
+  register uInt j;              /* counter */
+  register int k;               /* number of bits in current code */
+  int l;                        /* bits per table (returned in m) */
+  register uIntf *p;            /* pointer into c[], b[], or v[] */
+  inflate_huft *q;              /* points to current table */
+  struct inflate_huft_s r;      /* table entry for structure assignment */
+  inflate_huft *u[BMAX];        /* table stack */
+  uInt v[N_MAX];                /* values in order of bit length */
+  register int w;               /* bits before this table == (l * h) */
+  uInt x[BMAX+1];               /* bit offsets, then code stack */
+  uIntf *xp;                    /* pointer into x */
+  int y;                        /* number of dummy codes added */
+  uInt z;                       /* number of entries in current table */
+
+
+  /* Generate counts for each bit length */
+  p = c;
+#define C0 *p++ = 0;
+#define C2 C0 C0 C0 C0
+#define C4 C2 C2 C2 C2
+  C4                            /* clear c[]--assume BMAX+1 is 16 */
+  p = b;  i = n;
+  do {
+    c[*p++]++;                  /* assume all entries <= BMAX */
+  } while (--i);
+  if (c[0] == n)                /* null input--all zero length codes */
+  {
+    *t = (inflate_huft *)Z_NULL;
+    *m = 0;
+    return Z_OK;
+  }
+
+
+  /* Find minimum and maximum length, bound *m by those */
+  l = *m;
+  for (j = 1; j <= BMAX; j++)
+    if (c[j])
+      break;
+  k = j;                        /* minimum code length */
+  if ((uInt)l < j)
+    l = j;
+  for (i = BMAX; i; i--)
+    if (c[i])
+      break;
+  g = i;                        /* maximum code length */
+  if ((uInt)l > i)
+    l = i;
+  *m = l;
+
+
+  /* Adjust last length count to fill out codes, if needed */
+  for (y = 1 << j; j < i; j++, y <<= 1)
+    if ((y -= c[j]) < 0)
+      return Z_DATA_ERROR;
+  if ((y -= c[i]) < 0)
+    return Z_DATA_ERROR;
+  c[i] += y;
+
+
+  /* Generate starting offsets into the value table for each length */
+  x[1] = j = 0;
+  p = c + 1;  xp = x + 2;
+  while (--i) {                 /* note that i == g from above */
+    *xp++ = (j += *p++);
+  }
+
+
+  /* Make a table of values in order of bit lengths */
+  p = b;  i = 0;
+  do {
+    if ((j = *p++) != 0)
+      v[x[j]++] = i;
+  } while (++i < n);
+
+
+  /* Generate the Huffman codes and for each, make the table entries */
+  x[0] = i = 0;                 /* first Huffman code is zero */
+  p = v;                        /* grab values in bit order */
+  h = -1;                       /* no tables yet--level -1 */
+  w = -l;                       /* bits decoded == (l * h) */
+  u[0] = (inflate_huft *)Z_NULL;        /* just to keep compilers happy */
+  q = (inflate_huft *)Z_NULL;   /* ditto */
+  z = 0;                        /* ditto */
+
+  /* go through the bit lengths (k already is bits in shortest code) */
+  for (; k <= g; k++)
+  {
+    a = c[k];
+    while (a--)
+    {
+      /* here i is the Huffman code of length k bits for value *p */
+      /* make tables up to required level */
+      while (k > w + l)
+      {
+        h++;
+        w += l;                 /* previous table always l bits */
+
+        /* compute minimum size table less than or equal to l bits */
+        z = (z = g - w) > (uInt)l ? l : z;      /* table size upper limit */
+        if ((f = 1 << (j = k - w)) > a + 1)     /* try a k-w bit table */
+        {                       /* too few codes for k-w bit table */
+          f -= a + 1;           /* deduct codes from patterns left */
+          xp = c + k;
+          if (j < z)
+            while (++j < z)     /* try smaller tables up to z bits */
+            {
+              if ((f <<= 1) <= *++xp)
+                break;          /* enough codes to use up j bits */
+              f -= *xp;         /* else deduct codes from patterns */
+            }
+        }
+        z = 1 << j;             /* table entries for j-bit table */
+
+        /* allocate and link in new table */
+        if ((q = (inflate_huft *)ZALLOC
+             (zs,z + 1,sizeof(inflate_huft))) == Z_NULL)
+        {
+          if (h)
+            inflate_trees_free(u[0], zs);
+          return Z_MEM_ERROR;   /* not enough memory */
+        }
+	q->word.Nalloc = z + 1;
+#ifdef DEBUG_ZLIB
+        inflate_hufts += z + 1;
+#endif
+        *t = q + 1;             /* link to list for huft_free() */
+        *(t = &(q->next)) = Z_NULL;
+        u[h] = ++q;             /* table starts after link */
+
+        /* connect to last table, if there is one */
+        if (h)
+        {
+          x[h] = i;             /* save pattern for backing up */
+          r.bits = (Byte)l;     /* bits to dump before this table */
+          r.exop = (Byte)j;     /* bits in this table */
+          r.next = q;           /* pointer to this table */
+          j = i >> (w - l);     /* (get around Turbo C bug) */
+          u[h-1][j] = r;        /* connect to last table */
+        }
+      }
+
+      /* set up table entry in r */
+      r.bits = (Byte)(k - w);
+      if (p >= v + n)
+        r.exop = 128 + 64;      /* out of values--invalid code */
+      else if (*p < s)
+      {
+        r.exop = (Byte)(*p < 256 ? 0 : 32 + 64);     /* 256 is end-of-block */
+        r.base = *p++;          /* simple code is just the value */
+      }
+      else
+      {
+        r.exop = (Byte)e[*p - s] + 16 + 64; /* non-simple--look up in lists */
+        r.base = d[*p++ - s];
+      }
+
+      /* fill code-like entries with r */
+      f = 1 << (k - w);
+      for (j = i >> w; j < z; j += f)
+        q[j] = r;
+
+      /* backwards increment the k-bit code i */
+      for (j = 1 << (k - 1); i & j; j >>= 1)
+        i ^= j;
+      i ^= j;
+
+      /* backup over finished tables */
+      while ((i & ((1 << w) - 1)) != x[h])
+      {
+        h--;                    /* don't need to update q */
+        w -= l;
+      }
+    }
+  }
+
+
+  /* Return Z_BUF_ERROR if we were given an incomplete table */
+  return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
+}
+
+
+local int inflate_trees_bits(c, bb, tb, z)
+uIntf *c;               /* 19 code lengths */
+uIntf *bb;              /* bits tree desired/actual depth */
+inflate_huft * FAR *tb; /* bits tree result */
+z_stream *z;            /* for zfree function */
+{
+  int r;
+
+  r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z);
+  if (r == Z_DATA_ERROR)
+    z->msg = "oversubscribed dynamic bit lengths tree";
+  else if (r == Z_BUF_ERROR)
+  {
+    inflate_trees_free(*tb, z);
+    z->msg = "incomplete dynamic bit lengths tree";
+    r = Z_DATA_ERROR;
+  }
+  return r;
+}
+
+
+local int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z)
+uInt nl;                /* number of literal/length codes */
+uInt nd;                /* number of distance codes */
+uIntf *c;               /* that many (total) code lengths */
+uIntf *bl;              /* literal desired/actual bit depth */
+uIntf *bd;              /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+z_stream *z;            /* for zfree function */
+{
+  int r;
+
+  /* build literal/length tree */
+  if ((r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z)) != Z_OK)
+  {
+    if (r == Z_DATA_ERROR)
+      z->msg = "oversubscribed literal/length tree";
+    else if (r == Z_BUF_ERROR)
+    {
+      inflate_trees_free(*tl, z);
+      z->msg = "incomplete literal/length tree";
+      r = Z_DATA_ERROR;
+    }
+    return r;
+  }
+
+  /* build distance tree */
+  if ((r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z)) != Z_OK)
+  {
+    if (r == Z_DATA_ERROR)
+      z->msg = "oversubscribed literal/length tree";
+    else if (r == Z_BUF_ERROR) {
+#ifdef PKZIP_BUG_WORKAROUND
+      r = Z_OK;
+    }
+#else
+      inflate_trees_free(*td, z);
+      z->msg = "incomplete literal/length tree";
+      r = Z_DATA_ERROR;
+    }
+    inflate_trees_free(*tl, z);
+    return r;
+#endif
+  }
+
+  /* done */
+  return Z_OK;
+}
+
+
+/* build fixed tables only once--keep them here */
+local int fixed_lock = 0;
+local int fixed_built = 0;
+#define FIXEDH 530      /* number of hufts used by fixed tables */
+local uInt fixed_left = FIXEDH;
+local inflate_huft fixed_mem[FIXEDH];
+local uInt fixed_bl;
+local uInt fixed_bd;
+local inflate_huft *fixed_tl;
+local inflate_huft *fixed_td;
+
+
+local voidpf falloc(q, n, s)
+voidpf q;        /* opaque pointer (not used) */
+uInt n;         /* number of items */
+uInt s;         /* size of item */
+{
+  Assert(s == sizeof(inflate_huft) && n <= fixed_left,
+         "inflate_trees falloc overflow");
+  if (q) s++; /* to make some compilers happy */
+  fixed_left -= n;
+  return (voidpf)(fixed_mem + fixed_left);
+}
+
+
+local void ffree(q, p, n)
+voidpf q;
+voidpf p;
+uInt n;
+{
+  Assert(0, "inflate_trees ffree called!");
+  if (q) q = p; /* to make some compilers happy */
+}
+
+
+local int inflate_trees_fixed(bl, bd, tl, td)
+uIntf *bl;               /* literal desired/actual bit depth */
+uIntf *bd;               /* distance desired/actual bit depth */
+inflate_huft * FAR *tl;  /* literal/length tree result */
+inflate_huft * FAR *td;  /* distance tree result */
+{
+  /* build fixed tables if not built already--lock out other instances */
+  while (++fixed_lock > 1)
+    fixed_lock--;
+  if (!fixed_built)
+  {
+    int k;              /* temporary variable */
+    unsigned c[288];    /* length list for huft_build */
+    z_stream z;         /* for falloc function */
+
+    /* set up fake z_stream for memory routines */
+    z.zalloc = falloc;
+    z.zfree = ffree;
+    z.opaque = Z_NULL;
+
+    /* literal table */
+    for (k = 0; k < 144; k++)
+      c[k] = 8;
+    for (; k < 256; k++)
+      c[k] = 9;
+    for (; k < 280; k++)
+      c[k] = 7;
+    for (; k < 288; k++)
+      c[k] = 8;
+    fixed_bl = 7;
+    huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z);
+
+    /* distance table */
+    for (k = 0; k < 30; k++)
+      c[k] = 5;
+    fixed_bd = 5;
+    huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z);
+
+    /* done */
+    fixed_built = 1;
+  }
+  fixed_lock--;
+  *bl = fixed_bl;
+  *bd = fixed_bd;
+  *tl = fixed_tl;
+  *td = fixed_td;
+  return Z_OK;
+}
+
+
+local int inflate_trees_free(t, z)
+inflate_huft *t;        /* table to free */
+z_stream *z;            /* for zfree function */
+/* Free the malloc'ed tables built by huft_build(), which makes a linked
+   list of the tables it made, with the links in a dummy first entry of
+   each table. */
+{
+  register inflate_huft *p, *q;
+
+  /* Go through linked list, freeing from the malloced (t[-1]) address. */
+  p = t;
+  while (p != Z_NULL)
+  {
+    q = (--p)->next;
+    ZFREE(z, p, p->word.Nalloc * sizeof(inflate_huft));
+    p = q;
+  } 
+  return Z_OK;
+}
+
+/*+++++*/
+/* infcodes.c -- process literals and length/distance pairs
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* inflate codes private state */
+struct inflate_codes_state {
+
+  /* mode */
+  enum {        /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+      START,    /* x: set up for LEN */
+      LEN,      /* i: get length/literal/eob next */
+      LENEXT,   /* i: getting length extra (have base) */
+      DIST,     /* i: get distance next */
+      DISTEXT,  /* i: getting distance extra */
+      COPY,     /* o: copying bytes in window, waiting for space */
+      LIT,      /* o: got literal, waiting for output space */
+      WASH,     /* o: got eob, possibly still output waiting */
+      END,      /* x: got eob and all data flushed */
+      BADCODE}  /* x: got error */
+    mode;               /* current inflate_codes mode */
+
+  /* mode dependent information */
+  uInt len;
+  union {
+    struct {
+      inflate_huft *tree;       /* pointer into tree */
+      uInt need;                /* bits needed */
+    } code;             /* if LEN or DIST, where in tree */
+    uInt lit;           /* if LIT, literal */
+    struct {
+      uInt get;                 /* bits to get for extra */
+      uInt dist;                /* distance back to copy from */
+    } copy;             /* if EXT or COPY, where and how much */
+  } sub;                /* submode */
+
+  /* mode independent information */
+  Byte lbits;           /* ltree bits decoded per branch */
+  Byte dbits;           /* dtree bits decoder per branch */
+  inflate_huft *ltree;          /* literal/length/eob tree */
+  inflate_huft *dtree;          /* distance tree */
+
+};
+
+
+local inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
+uInt bl, bd;
+inflate_huft *tl, *td;
+z_stream *z;
+{
+  inflate_codes_statef *c;
+
+  if ((c = (inflate_codes_statef *)
+       ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
+  {
+    c->mode = START;
+    c->lbits = (Byte)bl;
+    c->dbits = (Byte)bd;
+    c->ltree = tl;
+    c->dtree = td;
+    Tracev((stderr, "inflate:       codes new\n"));
+  }
+  return c;
+}
+
+
+local int inflate_codes(s, z, r)
+inflate_blocks_statef *s;
+z_stream *z;
+int r;
+{
+  uInt j;               /* temporary storage */
+  inflate_huft *t;      /* temporary pointer */
+  uInt e;               /* extra bits or operation */
+  uLong b;              /* bit buffer */
+  uInt k;               /* bits in bit buffer */
+  Bytef *p;             /* input data pointer */
+  uInt n;               /* bytes available there */
+  Bytef *q;             /* output window write pointer */
+  uInt m;               /* bytes to end of window or read pointer */
+  Bytef *f;             /* pointer to copy strings from */
+  inflate_codes_statef *c = s->sub.decode.codes;  /* codes state */
+
+  /* copy input/output information to locals (UPDATE macro restores) */
+  LOAD
+
+  /* process input and output based on current state */
+  while (1) switch (c->mode)
+  {             /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+    case START:         /* x: set up for LEN */
+#ifndef SLOW
+      if (m >= 258 && n >= 10)
+      {
+        UPDATE
+        r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
+        LOAD
+        if (r != Z_OK)
+        {
+          c->mode = r == Z_STREAM_END ? WASH : BADCODE;
+          break;
+        }
+      }
+#endif /* !SLOW */
+      c->sub.code.need = c->lbits;
+      c->sub.code.tree = c->ltree;
+      c->mode = LEN;
+    case LEN:           /* i: get length/literal/eob next */
+      j = c->sub.code.need;
+      NEEDBITS(j)
+      t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+      DUMPBITS(t->bits)
+      e = (uInt)(t->exop);
+      if (e == 0)               /* literal */
+      {
+        c->sub.lit = t->base;
+        Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+                 "inflate:         literal '%c'\n" :
+                 "inflate:         literal 0x%02x\n", t->base));
+        c->mode = LIT;
+        break;
+      }
+      if (e & 16)               /* length */
+      {
+        c->sub.copy.get = e & 15;
+        c->len = t->base;
+        c->mode = LENEXT;
+        break;
+      }
+      if ((e & 64) == 0)        /* next table */
+      {
+        c->sub.code.need = e;
+        c->sub.code.tree = t->next;
+        break;
+      }
+      if (e & 32)               /* end of block */
+      {
+        Tracevv((stderr, "inflate:         end of block\n"));
+        c->mode = WASH;
+        break;
+      }
+      c->mode = BADCODE;        /* invalid code */
+      z->msg = "invalid literal/length code";
+      r = Z_DATA_ERROR;
+      LEAVE
+    case LENEXT:        /* i: getting length extra (have base) */
+      j = c->sub.copy.get;
+      NEEDBITS(j)
+      c->len += (uInt)b & inflate_mask[j];
+      DUMPBITS(j)
+      c->sub.code.need = c->dbits;
+      c->sub.code.tree = c->dtree;
+      Tracevv((stderr, "inflate:         length %u\n", c->len));
+      c->mode = DIST;
+    case DIST:          /* i: get distance next */
+      j = c->sub.code.need;
+      NEEDBITS(j)
+      t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+      DUMPBITS(t->bits)
+      e = (uInt)(t->exop);
+      if (e & 16)               /* distance */
+      {
+        c->sub.copy.get = e & 15;
+        c->sub.copy.dist = t->base;
+        c->mode = DISTEXT;
+        break;
+      }
+      if ((e & 64) == 0)        /* next table */
+      {
+        c->sub.code.need = e;
+        c->sub.code.tree = t->next;
+        break;
+      }
+      c->mode = BADCODE;        /* invalid code */
+      z->msg = "invalid distance code";
+      r = Z_DATA_ERROR;
+      LEAVE
+    case DISTEXT:       /* i: getting distance extra */
+      j = c->sub.copy.get;
+      NEEDBITS(j)
+      c->sub.copy.dist += (uInt)b & inflate_mask[j];
+      DUMPBITS(j)
+      Tracevv((stderr, "inflate:         distance %u\n", c->sub.copy.dist));
+      c->mode = COPY;
+    case COPY:          /* o: copying bytes in window, waiting for space */
+#ifndef __TURBOC__ /* Turbo C bug for following expression */
+      f = (uInt)(q - s->window) < c->sub.copy.dist ?
+          s->end - (c->sub.copy.dist - (q - s->window)) :
+          q - c->sub.copy.dist;
+#else
+      f = q - c->sub.copy.dist;
+      if ((uInt)(q - s->window) < c->sub.copy.dist)
+        f = s->end - (c->sub.copy.dist - (q - s->window));
+#endif
+      while (c->len)
+      {
+        NEEDOUT
+        OUTBYTE(*f++)
+        if (f == s->end)
+          f = s->window;
+        c->len--;
+      }
+      c->mode = START;
+      break;
+    case LIT:           /* o: got literal, waiting for output space */
+      NEEDOUT
+      OUTBYTE(c->sub.lit)
+      c->mode = START;
+      break;
+    case WASH:          /* o: got eob, possibly more output */
+      FLUSH
+      if (s->read != s->write)
+        LEAVE
+      c->mode = END;
+    case END:
+      r = Z_STREAM_END;
+      LEAVE
+    case BADCODE:       /* x: got error */
+      r = Z_DATA_ERROR;
+      LEAVE
+    default:
+      r = Z_STREAM_ERROR;
+      LEAVE
+  }
+}
+
+
+local void inflate_codes_free(c, z)
+inflate_codes_statef *c;
+z_stream *z;
+{
+  ZFREE(z, c, sizeof(struct inflate_codes_state));
+  Tracev((stderr, "inflate:       codes free\n"));
+}
+
+/*+++++*/
+/* inflate_util.c -- data and routines common to blocks and codes
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* copy as much as possible from the sliding window to the output area */
+local int inflate_flush(s, z, r)
+inflate_blocks_statef *s;
+z_stream *z;
+int r;
+{
+  uInt n;
+  Bytef *p, *q;
+
+  /* local copies of source and destination pointers */
+  p = z->next_out;
+  q = s->read;
+
+  /* compute number of bytes to copy as far as end of window */
+  n = (uInt)((q <= s->write ? s->write : s->end) - q);
+  if (n > z->avail_out) n = z->avail_out;
+  if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+  /* update counters */
+  z->avail_out -= n;
+  z->total_out += n;
+
+  /* update check information */
+  if (s->checkfn != Z_NULL)
+    s->check = (*s->checkfn)(s->check, q, n);
+
+  /* copy as far as end of window */
+  zmemcpy(p, q, n);
+  p += n;
+  q += n;
+
+  /* see if more to copy at beginning of window */
+  if (q == s->end)
+  {
+    /* wrap pointers */
+    q = s->window;
+    if (s->write == s->end)
+      s->write = s->window;
+
+    /* compute bytes to copy */
+    n = (uInt)(s->write - q);
+    if (n > z->avail_out) n = z->avail_out;
+    if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+    /* update counters */
+    z->avail_out -= n;
+    z->total_out += n;
+
+    /* update check information */
+    if (s->checkfn != Z_NULL)
+      s->check = (*s->checkfn)(s->check, q, n);
+
+    /* copy */
+    zmemcpy(p, q, n);
+    p += n;
+    q += n;
+  }
+
+  /* update pointers */
+  z->next_out = p;
+  s->read = q;
+
+  /* done */
+  return r;
+}
+
+
+/*+++++*/
+/* inffast.c -- process literals and length/distance pairs fast
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* macros for bit input with no checking and for returning unused bytes */
+#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define UNGRAB {n+=(c=k>>3);p-=c;k&=7;}
+
+/* Called with number of bytes left to write in window at least 258
+   (the maximum string length) and number of input bytes available
+   at least ten.  The ten bytes are six bytes for the longest length/
+   distance pair plus four bytes for overloading the bit buffer. */
+
+local int inflate_fast(bl, bd, tl, td, s, z)
+uInt bl, bd;
+inflate_huft *tl, *td;
+inflate_blocks_statef *s;
+z_stream *z;
+{
+  inflate_huft *t;      /* temporary pointer */
+  uInt e;               /* extra bits or operation */
+  uLong b;              /* bit buffer */
+  uInt k;               /* bits in bit buffer */
+  Bytef *p;             /* input data pointer */
+  uInt n;               /* bytes available there */
+  Bytef *q;             /* output window write pointer */
+  uInt m;               /* bytes to end of window or read pointer */
+  uInt ml;              /* mask for literal/length tree */
+  uInt md;              /* mask for distance tree */
+  uInt c;               /* bytes to copy */
+  uInt d;               /* distance back to copy from */
+  Bytef *r;             /* copy source pointer */
+
+  /* load input, output, bit values */
+  LOAD
+
+  /* initialize masks */
+  ml = inflate_mask[bl];
+  md = inflate_mask[bd];
+
+  /* do until not enough input or output space for fast loop */
+  do {                          /* assume called with m >= 258 && n >= 10 */
+    /* get literal/length code */
+    GRABBITS(20)                /* max bits for literal/length code */
+    if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
+    {
+      DUMPBITS(t->bits)
+      Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+                "inflate:         * literal '%c'\n" :
+                "inflate:         * literal 0x%02x\n", t->base));
+      *q++ = (Byte)t->base;
+      m--;
+      continue;
+    }
+    do {
+      DUMPBITS(t->bits)
+      if (e & 16)
+      {
+        /* get extra bits for length */
+        e &= 15;
+        c = t->base + ((uInt)b & inflate_mask[e]);
+        DUMPBITS(e)
+        Tracevv((stderr, "inflate:         * length %u\n", c));
+
+        /* decode distance base of block to copy */
+        GRABBITS(15);           /* max bits for distance code */
+        e = (t = td + ((uInt)b & md))->exop;
+        do {
+          DUMPBITS(t->bits)
+          if (e & 16)
+          {
+            /* get extra bits to add to distance base */
+            e &= 15;
+            GRABBITS(e)         /* get extra bits (up to 13) */
+            d = t->base + ((uInt)b & inflate_mask[e]);
+            DUMPBITS(e)
+            Tracevv((stderr, "inflate:         * distance %u\n", d));
+
+            /* do the copy */
+            m -= c;
+            if ((uInt)(q - s->window) >= d)     /* offset before dest */
+            {                                   /*  just copy */
+              r = q - d;
+              *q++ = *r++;  c--;        /* minimum count is three, */
+              *q++ = *r++;  c--;        /*  so unroll loop a little */
+            }
+            else                        /* else offset after destination */
+            {
+              e = d - (q - s->window);  /* bytes from offset to end */
+              r = s->end - e;           /* pointer to offset */
+              if (c > e)                /* if source crosses, */
+              {
+                c -= e;                 /* copy to end of window */
+                do {
+                  *q++ = *r++;
+                } while (--e);
+                r = s->window;          /* copy rest from start of window */
+              }
+            }
+            do {                        /* copy all or what's left */
+              *q++ = *r++;
+            } while (--c);
+            break;
+          }
+          else if ((e & 64) == 0)
+            e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop;
+          else
+          {
+            z->msg = "invalid distance code";
+            UNGRAB
+            UPDATE
+            return Z_DATA_ERROR;
+          }
+        } while (1);
+        break;
+      }
+      if ((e & 64) == 0)
+      {
+        if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0)
+        {
+          DUMPBITS(t->bits)
+          Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+                    "inflate:         * literal '%c'\n" :
+                    "inflate:         * literal 0x%02x\n", t->base));
+          *q++ = (Byte)t->base;
+          m--;
+          break;
+        }
+      }
+      else if (e & 32)
+      {
+        Tracevv((stderr, "inflate:         * end of block\n"));
+        UNGRAB
+        UPDATE
+        return Z_STREAM_END;
+      }
+      else
+      {
+        z->msg = "invalid literal/length code";
+        UNGRAB
+        UPDATE
+        return Z_DATA_ERROR;
+      }
+    } while (1);
+  } while (m >= 258 && n >= 10);
+
+  /* not enough input or output--restore pointers and return */
+  UNGRAB
+  UPDATE
+  return Z_OK;
+}
+
+
+/*+++++*/
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* From: zutil.c,v 1.8 1995/05/03 17:27:12 jloup Exp */
+
+char *zlib_version = ZLIB_VERSION;
+
+char *z_errmsg[] = {
+"stream end",          /* Z_STREAM_END    1 */
+"",                    /* Z_OK            0 */
+"file error",          /* Z_ERRNO        (-1) */
+"stream error",        /* Z_STREAM_ERROR (-2) */
+"data error",          /* Z_DATA_ERROR   (-3) */
+"insufficient memory", /* Z_MEM_ERROR    (-4) */
+"buffer error",        /* Z_BUF_ERROR    (-5) */
+""};
+
+
+/*+++++*/
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* From: adler32.c,v 1.6 1995/05/03 17:27:08 jloup Exp */
+
+#define BASE 65521L /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf)  {s1 += *buf++; s2 += s1;}
+#define DO2(buf)  DO1(buf); DO1(buf);
+#define DO4(buf)  DO2(buf); DO2(buf);
+#define DO8(buf)  DO4(buf); DO4(buf);
+#define DO16(buf) DO8(buf); DO8(buf);
+
+/* ========================================================================= */
+uLong adler32(adler, buf, len)
+    uLong adler;
+    Bytef *buf;
+    uInt len;
+{
+    unsigned long s1 = adler & 0xffff;
+    unsigned long s2 = (adler >> 16) & 0xffff;
+    int k;
+
+    if (buf == Z_NULL) return 1L;
+
+    while (len > 0) {
+        k = len < NMAX ? len : NMAX;
+        len -= k;
+        while (k >= 16) {
+            DO16(buf);
+            k -= 16;
+        }
+        if (k != 0) do {
+            DO1(buf);
+        } while (--k);
+        s1 %= BASE;
+        s2 %= BASE;
+    }
+    return (s2 << 16) | s1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/chrpboot/zlib.h linux/arch/ppc/chrpboot/zlib.h
--- v2.1.78/linux/arch/ppc/chrpboot/zlib.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrpboot/zlib.h	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,432 @@
+/*	$Id: zlib.h,v 1.1 1997/09/19 07:03:47 paulus Exp $	*/
+
+/*
+ * This file is derived from zlib.h and zconf.h from the zlib-0.95
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets.
+ */
+
+/*
+ *  ==FILEVERSION 960122==
+ *
+ * This marker is used by the Linux installation script to determine
+ * whether an up-to-date version of this file is already installed.
+ */
+
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 0.95, Aug 16th, 1995.
+
+  Copyright (C) 1995 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  gzip@prep.ai.mit.edu    madler@alumni.caltech.edu
+ */
+
+#ifndef _ZLIB_H
+#define _ZLIB_H
+
+/* #include "zconf.h" */	/* included directly here */
+
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */
+
+/*
+     The library does not install any signal handler. It is recommended to
+  add at least a handler for SIGSEGV when decompressing; the library checks
+  the consistency of the input data whenever possible but may go nuts
+  for some forms of corrupted input.
+ */
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints
+ * at addresses which are not a multiple of their size.
+ * Under DOS, -DFAR=far or -DFAR=__far may be needed.
+ */
+
+#ifndef STDC
+#  if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus)
+#    define STDC
+#  endif
+#endif
+
+#ifdef	__MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */
+#  include <unix.h>
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+#  ifdef MAXSEG_64K
+#    define MAX_MEM_LEVEL 8
+#  else
+#    define MAX_MEM_LEVEL 9
+#  endif
+#endif
+
+#ifndef FAR
+#  define FAR
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2 */
+#ifndef MAX_WBITS
+#  define MAX_WBITS   15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+            1 << (windowBits+2)   +  1 << (memLevel+9)
+ that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+   The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+                        /* Type declarations */
+
+#ifndef OF /* function prototypes */
+#  ifdef STDC
+#    define OF(args)  args
+#  else
+#    define OF(args)  ()
+#  endif
+#endif
+
+typedef unsigned char  Byte;  /* 8 bits */
+typedef unsigned int   uInt;  /* 16 bits or more */
+typedef unsigned long  uLong; /* 32 bits or more */
+
+typedef Byte FAR Bytef;
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+   typedef void FAR *voidpf;
+   typedef void     *voidp;
+#else
+   typedef Byte FAR *voidpf;
+   typedef Byte     *voidp;
+#endif
+
+/* end of original zconf.h */
+
+#define ZLIB_VERSION "0.95P"
+
+/* 
+     The 'zlib' compression library provides in-memory compression and
+  decompression functions, including integrity checks of the uncompressed
+  data.  This version of the library supports only one compression method
+  (deflation) but other algorithms may be added later and will have the same
+  stream interface.
+
+     For compression the application must provide the output buffer and
+  may optionally provide the input buffer for optimization. For decompression,
+  the application must provide the input buffer and may optionally provide
+  the output buffer for optimization.
+
+     Compression can be done in a single step if the buffers are large
+  enough (for example if an input file is mmap'ed), or can be done by
+  repeated calls of the compression function.  In the latter case, the
+  application must provide more input and/or consume the output
+  (providing more output space) before each call.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void   (*free_func)  OF((voidpf opaque, voidpf address, uInt nbytes));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+    Bytef    *next_in;  /* next input byte */
+    uInt     avail_in;  /* number of bytes available at next_in */
+    uLong    total_in;  /* total nb of input bytes read so far */
+
+    Bytef    *next_out; /* next output byte should be put there */
+    uInt     avail_out; /* remaining free space at next_out */
+    uLong    total_out; /* total nb of bytes output so far */
+
+    char     *msg;      /* last error message, NULL if no error */
+    struct internal_state FAR *state; /* not visible by applications */
+
+    alloc_func zalloc;  /* used to allocate the internal state */
+    free_func  zfree;   /* used to free the internal state */
+    voidp      opaque;  /* private data object passed to zalloc and zfree */
+
+    Byte     data_type; /* best guess about the data type: ascii or binary */
+
+} z_stream;
+
+/*
+   The application must update next_in and avail_in when avail_in has
+   dropped to zero. It must update next_out and avail_out when avail_out
+   has dropped to zero. The application must initialize zalloc, zfree and
+   opaque before calling the init function. All other fields are set by the
+   compression library and must not be updated by the application.
+
+   The opaque value provided by the application will be passed as the first
+   parameter for calls of zalloc and zfree. This can be useful for custom
+   memory management. The compression library attaches no meaning to the
+   opaque value.
+
+   zalloc must return Z_NULL if there is not enough memory for the object.
+   On 16-bit systems, the functions zalloc and zfree must be able to allocate
+   exactly 65536 bytes, but will not be required to allocate more than this
+   if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+   pointers returned by zalloc for objects of exactly 65536 bytes *must*
+   have their offset normalized to zero. The default allocation function
+   provided by this library ensures this (see zutil.c). To reduce memory
+   requirements and avoid any allocation of 64K objects, at the expense of
+   compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+   The fields total_in and total_out can be used for statistics or
+   progress reports. After compression, total_in holds the total size of
+   the uncompressed data and may be saved for use in the decompressor
+   (particularly if the decompressor wants to decompress everything in
+   a single step).
+*/
+
+                        /* constants */
+
+#define Z_NO_FLUSH      0
+#define Z_PARTIAL_FLUSH 1
+#define Z_FULL_FLUSH    2
+#define Z_SYNC_FLUSH    3 /* experimental: partial_flush + byte align */
+#define Z_FINISH        4
+#define Z_PACKET_FLUSH	5
+/* See deflate() below for the usage of these constants */
+
+#define Z_OK            0
+#define Z_STREAM_END    1
+#define Z_ERRNO        (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR   (-3)
+#define Z_MEM_ERROR    (-4)
+#define Z_BUF_ERROR    (-5)
+/* error codes for the compression/decompression functions */
+
+#define Z_BEST_SPEED             1
+#define Z_BEST_COMPRESSION       9
+#define Z_DEFAULT_COMPRESSION  (-1)
+/* compression levels */
+
+#define Z_FILTERED            1
+#define Z_HUFFMAN_ONLY        2
+#define Z_DEFAULT_STRATEGY    0
+
+#define Z_BINARY   0
+#define Z_ASCII    1
+#define Z_UNKNOWN  2
+/* Used to set the data_type field */
+
+#define Z_NULL  0  /* for initializing zalloc, zfree, opaque */
+
+extern char *zlib_version;
+/* The application can compare zlib_version and ZLIB_VERSION for consistency.
+   If the first character differs, the library code actually used is
+   not compatible with the zlib.h header file used by the application.
+ */
+
+                        /* basic functions */
+
+extern int inflateInit OF((z_stream *strm));
+/* 
+     Initializes the internal stream state for decompression. The fields
+   zalloc and zfree must be initialized before by the caller.  If zalloc and
+   zfree are set to Z_NULL, inflateInit updates them to use default allocation
+   functions.
+
+     inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory.  msg is set to null if there is no error message.
+   inflateInit does not perform any decompression: this will be done by
+   inflate().
+*/
+
+
+extern int inflate OF((z_stream *strm, int flush));
+/*
+  Performs one or both of the following actions:
+
+  - Decompress more input starting at next_in and update next_in and avail_in
+    accordingly. If not all input can be processed (because there is not
+    enough room in the output buffer), next_in is updated and processing
+    will resume at this point for the next call of inflate().
+
+  - Provide more output starting at next_out and update next_out and avail_out
+    accordingly.  inflate() always provides as much output as possible
+    (until there is no more input data or no more space in the output buffer).
+
+  Before the call of inflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming
+  more output, and updating the next_* and avail_* values accordingly.
+  The application can consume the uncompressed output when it wants, for
+  example when the output buffer is full (avail_out == 0), or after each
+  call of inflate().
+
+    If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH,
+  inflate flushes as much output as possible to the output buffer. The
+  flushing behavior of inflate is not specified for values of the flush
+  parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the
+  current implementation actually flushes as much output as possible
+  anyway.  For Z_PACKET_FLUSH, inflate checks that once all the input data
+  has been consumed, it is expecting to see the length field of a stored
+  block; if not, it returns Z_DATA_ERROR.
+
+    inflate() should normally be called until it returns Z_STREAM_END or an
+  error. However if all decompression is to be performed in a single step
+  (a single call of inflate), the parameter flush should be set to
+  Z_FINISH. In this case all pending input is processed and all pending
+  output is flushed; avail_out must be large enough to hold all the
+  uncompressed data. (The size of the uncompressed data may have been saved
+  by the compressor for this purpose.) The next operation on this stream must
+  be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+  is never required, but can be used to inform inflate that a faster routine
+  may be used for the single inflate() call.
+
+    inflate() returns Z_OK if some progress has been made (more input
+  processed or more output produced), Z_STREAM_END if the end of the
+  compressed data has been reached and all uncompressed output has been
+  produced, Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if
+  the stream structure was inconsistent (for example if next_in or next_out
+  was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no
+  progress is possible or if there was not enough room in the output buffer
+  when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then
+  call inflateSync to look for a good compression block.  */
+
+
+extern int inflateEnd OF((z_stream *strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any
+   pending output.
+
+     inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+   was inconsistent. In the error case, msg may be set but then points to a
+   static string (which must not be deallocated).
+*/
+
+                        /* advanced functions */
+
+extern int inflateInit2 OF((z_stream *strm,
+                            int  windowBits));
+/*   
+     This is another version of inflateInit with more compression options. The
+   fields next_out, zalloc and zfree must be initialized before by the caller.
+
+     The windowBits parameter is the base two logarithm of the maximum window
+   size (the size of the history buffer).  It should be in the range 8..15 for
+   this version of the library (the value 16 will be allowed soon). The
+   default value is 15 if inflateInit is used instead. If a compressed stream
+   with a larger window size is given as input, inflate() will return with
+   the error code Z_DATA_ERROR instead of trying to allocate a larger window.
+
+     If next_out is not null, the library will use this buffer for the history
+   buffer; the buffer must either be large enough to hold the entire output
+   data, or have at least 1<<windowBits bytes.  If next_out is null, the
+   library will allocate its own buffer (and leave next_out null). next_in
+   need not be provided here but must be provided by the application for the
+   next call of inflate().
+
+     If the history buffer is provided by the application, next_out must
+   never be changed by the application since the decompressor maintains
+   history information inside this buffer from call to call; the application
+   can only reset next_out to the beginning of the history buffer when
+   avail_out is zero and all output has been consumed.
+
+      inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was
+   not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as
+   windowBits < 8). msg is set to null if there is no error message.
+   inflateInit2 does not perform any decompression: this will be done by
+   inflate().
+*/
+
+extern int inflateSync OF((z_stream *strm));
+/* 
+    Skips invalid compressed data until the special marker (see deflate()
+  above) can be found, or until all available input is skipped. No output
+  is provided.
+
+    inflateSync returns Z_OK if the special marker has been found, Z_BUF_ERROR
+  if no more input was provided, Z_DATA_ERROR if no marker has been found,
+  or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+  case, the application may save the current current value of total_in which
+  indicates where valid compressed data was found. In the error case, the
+  application may repeatedly call inflateSync, providing more input each time,
+  until success or end of the input data.
+*/
+
+extern int inflateReset OF((z_stream *strm));
+/*
+     This function is equivalent to inflateEnd followed by inflateInit,
+   but does not free and reallocate all the internal decompression state.
+   The stream will keep attributes that may have been set by inflateInit2.
+
+      inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+extern int inflateIncomp OF((z_stream *strm));
+/*
+     This function adds the data at next_in (avail_in bytes) to the output
+   history without performing any output.  There must be no pending output,
+   and the decompressor must be expecting to see the start of a block.
+   Calling this function is equivalent to decompressing a stored block
+   containing the data at next_in (except that the data is not output).
+*/
+
+                        /* checksum functions */
+
+/*
+     This function is not related to compression but is exported
+   anyway because it might be useful in applications using the
+   compression library.
+*/
+
+extern uLong adler32 OF((uLong adler, Bytef *buf, uInt len));
+
+/*
+     Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+   return the updated checksum. If buf is NULL, this function returns
+   the required initial value for the checksum.
+   An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+   much faster. Usage example:
+
+     uLong adler = adler32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       adler = adler32(adler, buffer, length);
+     }
+     if (adler != original_adler) error();
+*/
+
+#ifndef _Z_UTIL_H
+    struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+#endif /* _ZLIB_H */
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/coffboot/Makefile linux/arch/ppc/coffboot/Makefile
--- v2.1.78/linux/arch/ppc/coffboot/Makefile	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/coffboot/Makefile	Mon Jan 12 15:18:13 1998
@@ -8,6 +8,7 @@
 CROSS_COMPILE 	=powerpc-eabi-
 endif
 
+
 HOSTCC	= gcc
 HOSTCFLAGS = -O -I$(TOPDIR)/include
 
@@ -16,35 +17,71 @@
 CFLAGS	= -O -fno-builtin -I$(TOPDIR)/include
 OBJCOPY = $(CROSS_COMPILE)objcopy
 OBJCOPY_ARGS = -O aixcoff-rs6000 -R .stab -R .stabstr -R .comment \
-	--add-section=image=zImage
+	--add-section=image=vmlinux.gz
 LD_ARGS = -e _start -T ld.script -Ttext 500000 -Tdata 510000 -Bstatic
-GZIP = gzip -9
+GZ = gzip -9
 
 OBJS = crt0.o start.o main.o misc.o string.o zlib.o
 LIBS = $(TOPDIR)/lib/lib.a
 
-vmlinux.coff: coffboot hack-coff zImage
+ifeq ($(CONFIG_PMAC),y)
+hack-coff: hack-coff.c
+	$(HOSTCC) $(HOSTCFLAGS) -o hack-coff hack-coff.c
+
+floppy: zImage
+#	mount -t hfs /dev/fd0 /mnt
+#	cp vmlinux.coff /mnt
+#	umount /mnt
+
+znetboot: vmlinux.coff
+	cp vmlinux.coff /tftpboot/zImage.pmac
+
+znetboot.initrd: vmlinux.coff.initrd
+	cp vmlinux.coff.initrd /tftpboot/zImage.pmac
+
+coffboot: $(OBJS) ld.script
+	$(LD) -o coffboot $(LD_ARGS) $(OBJS) $(LIBS)
+
+zImage: vmlinux.coff
+
+zImage.initrd: vmlinux.coff.initrd
+
+vmlinux.coff: coffboot hack-coff vmlinux.gz
 	$(OBJCOPY) $(OBJCOPY_ARGS) coffboot $@
 	./hack-coff $@
+	ln -sf vmlinux.coff zImage
 
-vmlinux.coff.initrd: coffboot hack-coff zImage ramdisk.image.gz
+vmlinux.coff.initrd: coffboot hack-coff vmlinux.gz ramdisk.image.gz
 	$(OBJCOPY) $(OBJCOPY_ARGS) --add-section=initrd=ramdisk.image.gz \
 		coffboot $@
 	./hack-coff $@
 
-coffboot: $(OBJS) ld.script
-	$(LD) -o coffboot $(LD_ARGS) $(OBJS) $(LIBS)
+else
+znetboot: vmlinux.gz
 
-zImage: $(TOPDIR)/vmlinux elfextract
-	./elfextract $(TOPDIR)/vmlinux | $(GZIP) >zImage
+znetboot.initrd: vmlinux.gz
 
-hack-coff: hack-coff.c
-	$(HOSTCC) $(HOSTCFLAGS) -o hack-coff hack-coff.c
+coffboot: vmlinux.gz
+
+zImage: vmlinux.gz
+
+zImage.initrd: vmlinux.gz
+
+vmlinux.coff: vmlinux.gz
+
+vmlinux.coff.initrd: vmlinux.gz
+
+floppy: vmlinux.gz
+
+
+
+endif
 
-elfextract: elfextract.c
-	$(HOSTCC) $(HOSTCFLAGS) -o elfextract elfextract.c
+vmlinux.gz: $(TOPDIR)/vmlinux
+	$(OBJCOPY) -S -O binary $(TOPDIR)/vmlinux vmlinux
+	gzip -vf9 vmlinux
 
 clean:
-	rm -f elfextract hack-coff coffboot zImage vmlinux.coff
+	rm -f hack-coff coffboot zImage vmlinux.coff vmlinux.gz
 
 fastdep:
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/coffboot/elfextract.c linux/arch/ppc/coffboot/elfextract.c
--- v2.1.78/linux/arch/ppc/coffboot/elfextract.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/coffboot/elfextract.c	Wed Dec 31 16:00:00 1969
@@ -1,98 +0,0 @@
-/*
- * Extract the loadable program segment from an elf file.
- *
- * Copyright 1996 Paul Mackerras.
- *
- * 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 <stdio.h>
-#include <linux/elf.h>
-
-FILE *fi, *fo;
-char *ni, *no;
-char buf[65536];
-
-void
-rd(void *buf, int len)
-{
-    int nr;
-
-    nr = fread(buf, 1, len, fi);
-    if (nr == len)
-	return;
-    if (ferror(fi))
-	fprintf(stderr, "%s: read error\n", ni);
-    else
-	fprintf(stderr, "%s: short file\n", ni);
-    exit(1);
-}
-
-main(int ac, char **av)
-{
-    unsigned nb, len;
-    Elf32_Ehdr eh;
-    Elf32_Phdr ph;
-
-    if (ac > 3 || ac > 1 && av[1][0] == '-') {
-	fprintf(stderr, "Usage: %s [elf-file [image-file]]\n", av[0]);
-	exit(0);
-    }
-
-    fi = stdin;
-    ni = "(stdin)";
-    fo = stdout;
-    no = "(stdout)";
-
-    if (ac > 1) {
-	ni = av[1];
-	fi = fopen(ni, "rb");
-	if (fi == NULL) {
-	    perror(ni);
-	    exit(1);
-	}
-    }
-
-    rd(&eh, sizeof(eh));
-    if (eh.e_ident[EI_MAG0] != ELFMAG0
-	|| eh.e_ident[EI_MAG1] != ELFMAG1
-	|| eh.e_ident[EI_MAG2] != ELFMAG2
-	|| eh.e_ident[EI_MAG3] != ELFMAG3) {
-	fprintf(stderr, "%s: not an ELF file\n", ni);
-	exit(1);
-    }
-
-    fseek(fi, eh.e_phoff + (eh.e_phnum - 1) * sizeof(ph), 0);
-    rd(&ph, sizeof(ph));
-    if (ph.p_type != PT_LOAD) {
-	fprintf(stderr, "%s: doesn't have a loadable segment\n", ni);
-	exit(1);
-    }
-
-    if (ac > 2) {
-	no = av[2];
-	fo = fopen(no, "wb");
-	if (fo == NULL) {
-	    perror(no);
-	    exit(1);
-	}
-    }
-
-    fseek(fi, ph.p_offset, 0);
-    for (len = ph.p_filesz; len != 0; len -= nb) {
-	nb = len;
-	if (nb > sizeof(buf))
-	    nb = sizeof(buf);
-	rd(buf, nb);
-	if (fwrite(buf, 1, nb, fo) != nb) {
-	    fprintf(stderr, "%s: write error\n", no);
-	    exit(1);
-	}
-    }
-
-    fclose(fo);
-    fclose(fi);
-    exit(0);
-}
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/coffboot/start.c linux/arch/ppc/coffboot/start.c
--- v2.1.78/linux/arch/ppc/coffboot/start.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/coffboot/start.c	Mon Jan 12 15:18:13 1998
@@ -18,6 +18,7 @@
 void exit(void);
 void *finddevice(const char *name);
 int getprop(void *phandle, const char *name, void *buf, int buflen);
+void printk(char *fmt, ...);
 
 void
 start(int a1, int a2, void *promptr)
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.1.78/linux/arch/ppc/config.in	Thu Dec  4 14:53:54 1997
+++ linux/arch/ppc/config.in	Mon Jan 12 15:18:13 1998
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.19 1997/09/04 01:54:26 davem Exp $
+# $Id: config.in,v 1.36 1997/12/29 21:36:52 geert Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -7,13 +7,13 @@
 mainmenu_option next_comment
 comment 'Platform support'
 define_bool CONFIG_PPC y
-
 if [ "`uname`" != "Linux" -o "`uname -m`" != "ppc" ]; then
   define_bool CONFIG_CROSSCOMPILE y
 else
   define_bool CONFIG_NATIVE y
 fi
 
+define_bool CONFIG_MACH_SPECIFIC y
 bool 'Build PowerMac Kernel (not PReP or CHRP)?' CONFIG_PMAC
 bool 'Build PReP Kernel (not PowerMac or CHRP)?' CONFIG_PREP
 bool 'Build CHRP Kernel (not PReP or PowerMac)?' CONFIG_CHRP
@@ -39,9 +39,11 @@
 if [ "$CONFIG_PREP" = "y" ]; then
   bool 'PCI bridge optimization' CONFIG_PCI_OPTIMIZE
 fi
+bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC
 bool 'Networking support' CONFIG_NET
 bool 'Sysctl support' CONFIG_SYSCTL
 bool 'System V IPC' CONFIG_SYSVIPC
+bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
 
 # only elf supported, a.out is not -- Cort
 define_bool CONFIG_BINFMT_ELF y
@@ -49,39 +51,77 @@
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
 tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA
 
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  bool 'New unified console driver (EXPERIMENTAL)' CONFIG_ABSTRACT_CONSOLE
+fi
+
 if [ "$CONFIG_PMAC" = "y" ]; then
-  define_bool CONFIG_PMAC_CONSOLE y
+  if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then
+    define_bool CONFIG_FB y
+    bool 'Backward compatibility mode for Xpmac' CONFIG_FB_COMPAT_XPMAC
+  else
+    define_bool CONFIG_PMAC_CONSOLE y
+  fi
   define_bool CONFIG_MAC_KEYBOARD y
   define_bool CONFIG_MAC_FLOPPY	y
-  bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE
-  bool 'Include xmon kernel debugger' CONFIG_XMON
+else
+# if compiling specifically for prep or chrp, or supporting all arch's
+  if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then
+    bool 'Support for frame buffer devices' CONFIG_FB
+    bool 'Backward compatibility mode for Xpmac' CONFIG_FB_COMPAT_XPMAC
+  else
+    bool 'Support for PowerMac console' CONFIG_PMAC_CONSOLE
+  fi
+  bool 'Support for PowerMac keyboard' CONFIG_MAC_KEYBOARD
+  bool 'Support for PowerMac floppy' CONFIG_MAC_FLOPPY
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  bool 'Support for PowerMac mouse (EXPERIMENTAL)' CONFIG_MACMOUSE
 fi
+bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE
+bool 'Include xmon kernel debugger' CONFIG_XMON
 
-if [ "$CONFIG_PMAC_CONSOLE" = "y" ]; then
-  bool 'Support for ATI Mach64 display cards' CONFIG_ATY_VIDEO
-  bool 'Support for IMS Twin Turbo display card' CONFIG_IMSTT_VIDEO
+if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then
+  if [ "$CONFIG_FB" != "y" ]; then
+    define_bool CONFIG_VGA_CONSOLE y
+  fi
 else
-  define_bool CONFIG_VGA_CONSOLE y
+  if [ "$CONFIG_PMAC_CONSOLE" = "y" ]; then
+    bool 'Support for Apple "control" display' CONFIG_CONTROL_VIDEO
+    bool 'Support for Apple "platinum" display' CONFIG_PLATINUM_VIDEO
+    bool 'Support for Apple "valkyrie" display' CONFIG_VALKYRIE_VIDEO
+    bool 'Support for ATI Mach64 display cards' CONFIG_ATY_VIDEO
+    bool 'Support for IMS Twin Turbo display card' CONFIG_IMSTT_VIDEO
+    bool 'Support for Chips 65550 display' CONFIG_CHIPS_VIDEO
+  else
+    define_bool CONFIG_VGA_CONSOLE y
+  fi
 fi
 
 endmenu
 
 source drivers/pnp/Config.in
 source drivers/block/Config.in
+source drivers.new/Config.in
+
+if [ "$CONFIG_NET" = "y" ]; then
+  source net/Config.in
+fi
 
 mainmenu_option next_comment
 comment 'SCSI support'
+
 tristate 'SCSI support' CONFIG_SCSI
+
 if [ "$CONFIG_SCSI" != "n" ]; then
   source drivers/scsi/Config.in
 fi
 endmenu
 
-
 if [ "$CONFIG_NET" = "y" ]; then
   mainmenu_option next_comment
   comment 'Network device support'
-  source net/Config.in
+
   bool 'Network device support' CONFIG_NETDEVICES
   if [ "$CONFIG_NETDEVICES" = "y" ]; then
     source drivers/net/Config.in
@@ -93,6 +133,7 @@
 
 mainmenu_option next_comment
 comment 'ISDN subsystem'
+
 tristate 'ISDN support' CONFIG_ISDN
 if [ "$CONFIG_ISDN" != "n" ]; then
   source drivers/isdn/Config.in
@@ -101,6 +142,7 @@
 
 mainmenu_option next_comment
 comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
+
 bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI
 if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then
   source drivers/cdrom/Config.in
@@ -122,22 +164,17 @@
 
 source fs/nls/Config.in
 
+if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then
+  source drivers/video/Config.in
+fi
+
 source drivers/char/Config.in
 
 mainmenu_option next_comment
 comment 'Sound'
+
 tristate 'Sound card support' CONFIG_SOUND
 if [ "$CONFIG_SOUND" != "n" ]; then
   source drivers/sound/Config.in
 fi
 endmenu
-
-#mainmenu_option next_comment
-#comment 'Kernel hacking'
-#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
-#bool 'Kernel profiling support' CONFIG_PROFILE
-#if [ "$CONFIG_PROFILE" = "y" ]; then
-#  int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
-#fi
-#endmenu
-
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/defconfig linux/arch/ppc/defconfig
--- v2.1.78/linux/arch/ppc/defconfig	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/defconfig	Mon Jan 12 15:18:13 1998
@@ -7,9 +7,11 @@
 #
 CONFIG_PPC=y
 CONFIG_NATIVE=y
+CONFIG_MACH_SPECIFIC=y
 # CONFIG_PMAC is not set
 CONFIG_PREP=y
-CONFIG_MCOMMON=y
+# CONFIG_CHRP is not set
+CONFIG_COMMON=y
 
 #
 # General setup
@@ -19,14 +21,21 @@
 CONFIG_MODVERSIONS=y
 CONFIG_KERNELD=y
 CONFIG_PCI=y
-CONFIG_PCI_OPTIMIZE=y
+# CONFIG_PCI_OPTIMIZE is not set
+CONFIG_PCI_OLD_PROC=y
 CONFIG_NET=y
-CONFIG_SYSCTL=y
+# CONFIG_SYSCTL is not set
 CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
 CONFIG_BINFMT_ELF=y
 CONFIG_KERNEL_ELF=y
 # CONFIG_BINFMT_MISC is not set
 # CONFIG_BINFMT_JAVA is not set
+# CONFIG_PMAC_CONSOLE is not set
+# CONFIG_MAC_KEYBOARD is not set
+# CONFIG_MAC_FLOPPY is not set
+# CONFIG_PROC_DEVICETREE is not set
+# CONFIG_XMON is not set
 CONFIG_VGA_CONSOLE=y
 
 #
@@ -49,7 +58,8 @@
 # CONFIG_BLK_DEV_RZ1000 is not set
 # CONFIG_BLK_DEV_TRITON is not set
 # CONFIG_IDE_CHIPSETS is not set
-CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
@@ -58,6 +68,45 @@
 # CONFIG_BLK_DEV_HD is not set
 
 #
+# NEW devices (io_request, all ALPHA and dangerous)
+#
+# CONFIG_IO_REQUEST is not set
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+# CONFIG_NET_ALIAS is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_IP_ACCT is not set
+# CONFIG_IP_MASQUERADE is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_SYN_COOKIES is not set
+CONFIG_INET_RARP=y
+# CONFIG_IP_NOSR is not set
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_AX25 is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_CPU_IS_SLOW is not set
+# CONFIG_NET_SCHED is not set
+
+#
 # SCSI support
 #
 CONFIG_SCSI=y
@@ -67,7 +116,7 @@
 CONFIG_BLK_DEV_SR_VENDOR=y
 # CONFIG_CHR_DEV_SG is not set
 # CONFIG_SCSI_MULTI_LUN is not set
-CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_CONSTANTS is not set
 
 #
 # SCSI low-level drivers
@@ -111,46 +160,13 @@
 #
 # Network device support
 #
-
-#
-# Networking options
-#
-# CONFIG_NETLINK is not set
-# CONFIG_FIREWALL is not set
-# CONFIG_NET_ALIAS is not set
-CONFIG_INET=y
-# CONFIG_IP_MULTICAST is not set
-# CONFIG_IP_ACCT is not set
-# CONFIG_IP_ROUTER is not set
-# CONFIG_NET_IPIP is not set
-# CONFIG_SYN_COOKIES is not set
-# CONFIG_XTP is not set
-# CONFIG_INET_PCTCP is not set
-# CONFIG_INET_RARP is not set
-CONFIG_PATH_MTU_DISCOVERY=y
-# CONFIG_IP_NOSR is not set
-CONFIG_SKB_LARGE=y
-# CONFIG_IPV6 is not set
-# CONFIG_IPX is not set
-# CONFIG_ATALK is not set
-# CONFIG_AX25 is not set
-# CONFIG_X25 is not set
-# CONFIG_LAPB is not set
-# CONFIG_BRIDGE is not set
-# CONFIG_LLC is not set
-# CONFIG_WAN_ROUTER is not set
 CONFIG_NETDEVICES=y
 # CONFIG_ARCNET is not set
 # CONFIG_DUMMY is not set
 # CONFIG_EQUALIZER is not set
+# CONFIG_ETHERTAP is not set
 CONFIG_NET_ETHERNET=y
-CONFIG_NET_VENDOR_3COM=y
-# CONFIG_EL1 is not set
-# CONFIG_EL2 is not set
-# CONFIG_ELPLUS is not set
-# CONFIG_EL16 is not set
-CONFIG_EL3=y
-# CONFIG_VORTEX is not set
+# CONFIG_NET_VENDOR_3COM is not set
 CONFIG_LANCE=y
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_VENDOR_RACAL is not set
@@ -171,7 +187,7 @@
 # CONFIG_FDDI is not set
 # CONFIG_DLCI is not set
 # CONFIG_PLIP is not set
-CONFIG_PPP=y
+CONFIG_PPP=m
 # CONFIG_NET_RADIO is not set
 # CONFIG_SLIP is not set
 # CONFIG_TR is not set
@@ -193,25 +209,30 @@
 # CONFIG_QUOTA is not set
 # CONFIG_MINIX_FS is not set
 CONFIG_EXT2_FS=y
-CONFIG_FAT_FS=y
-CONFIG_MSDOS_FS=y
-# CONFIG_VFAT_FS is not set
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
 # CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
-# CONFIG_ROOT_NFS is not set
-CONFIG_NFSD=y
+# CONFIG_NFSD is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
 # CONFIG_SMB_FS is not set
-CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_ROMFS_FS is not set
 # CONFIG_AUTOFS_FS is not set
 # CONFIG_UFS_FS is not set
-CONFIG_MAC_PARTITION=y
+# CONFIG_MAC_PARTITION is not set
+
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
 
 #
 # Character devices
@@ -241,6 +262,8 @@
 # CONFIG_APM is not set
 # CONFIG_WATCHDOG is not set
 # CONFIG_RTC is not set
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_VIDEO_BT848 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_JOYSTICK is not set
 
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/ignore linux/arch/ppc/ignore
--- v2.1.78/linux/arch/ppc/ignore	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/ignore	Wed Dec 31 16:00:00 1969
@@ -1,65 +0,0 @@
-.config
-compile.h
-.version
-.objects
-.blurb
-*.cort
-*.paul
-*.old
-.defines
-version.h
-find_name
-checks
-#*
-.objects
-.object_files
-System.map
-asm
-.menuconfig*
-CVS
-ppc_defs.h
-mk_defs
-mkprep
-*.s
-.depend
-.hdepend
-*~
-*.o
-znetboot
-zvmlinux
-vmlinux
-zImage
-hack-coff
-coffboot
-vmlinux.coff
-.depend
-.cvsignore
-RCS
-SCCS
-CVS.adm
-RCSLOG
-cvslog.*
-tags
-TAGS
-.make.state
-.nse_depinfo
-*~
-#*
-.#*
-,*
-_$*
-*$
-*.old
-*.bak
-*.BAK
-*.orig
-*.rej
-*.a
-*.olb
-*.o
-*.obj
-*.so
-*.exe
-*.Z
-*.elc
-*.ln
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/Makefile linux/arch/ppc/kernel/Makefile
--- v2.1.78/linux/arch/ppc/kernel/Makefile	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/Makefile	Mon Jan 12 15:18:13 1998
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 .S.o:
-	$(CC) -D__ASSEMBLY__ -c $< -o $*.o
+	$(CC) $(CFLAGS) -D__ASSEMBLY__ -c $< -o $*.o
 
 O_TARGET := kernel.o
 O_OBJS := misc.o traps.o process.o signal.o syscalls.o \
@@ -19,6 +19,10 @@
 	  residual.o prom.o
 OX_OBJS := ppc_ksyms.o
 
+ifdef SMP
+O_OBJS += smp.o
+endif
+
 all: head.o kernel.o
 
 head.o: head.S $(TOPDIR)/include/linux/tasks.h ppc_defs.h
@@ -33,8 +37,11 @@
 	grep '^#define' mk_defs.s >>ppc_defs.h
 	rm mk_defs.s
 
+find_name : find_name.c
+	$(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o find_name find_name.c
+
 checks: checks.c
-	$(HOSTCC) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c
+	$(HOSTCC) -DKERNELBASE=$(KERNELBASE) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c
 	./checks
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/checks.c linux/arch/ppc/kernel/checks.c
--- v2.1.78/linux/arch/ppc/kernel/checks.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/checks.c	Mon Jan 12 15:18:13 1998
@@ -10,7 +10,6 @@
 #include <linux/malloc.h>
 #include <linux/user.h>
 #include <linux/a.out.h>
-#include <linux/config.h>
 
 #include <asm/pgtable.h>
 #include <asm/uaccess.h>
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/chrp_pci.c linux/arch/ppc/kernel/chrp_pci.c
--- v2.1.78/linux/arch/ppc/kernel/chrp_pci.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/chrp_pci.c	Mon Jan 12 15:18:13 1998
@@ -13,11 +13,17 @@
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/hydra.h>
+#include <asm/prom.h>
+#include <asm/gg2.h>
 
 /* LongTrail */
 #define pci_config_addr(bus, dev, offset) \
-	(0xfec00000 | ((bus)<<16) | ((dev)<<8) | (offset))
+	(GG2_PCI_CONFIG_BASE | ((bus)<<16) | ((dev)<<8) | (offset))
 
+volatile struct Hydra *Hydra = NULL;
+
+
+#if 1
 int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
 				  unsigned char offset, unsigned char *val)
 {
@@ -45,6 +51,7 @@
     return PCIBIOS_SUCCESSFUL;
 }
 
+
 int chrp_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
 				   unsigned char offset, unsigned int *val)
 {
@@ -77,80 +84,207 @@
 int chrp_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
 				    unsigned char offset, unsigned int val)
 {
-    if (bus > 7)
+   if (bus > 7)
 	return PCIBIOS_DEVICE_NOT_FOUND;
     out_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset), val);
     return PCIBIOS_SUCCESSFUL;
 }
+#else
+volatile unsigned int *pci_config_address=(volatile unsigned int *)0xfec00cf8;
+volatile unsigned char *pci_config_data=(volatile unsigned char *)0xfee00cfc;
+
+#define DEV_FN_MAX (31<<3)
+
+int chrp_pcibios_read_config_byte(unsigned char bus, 
+                                      unsigned char dev_fn,
+                                      unsigned char offset, 
+                                      unsigned char *val)
+{
+        if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND;
+        out_be32(pci_config_address,
+                 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24));
+        *val = in_8(pci_config_data+(offset&3));
+        return PCIBIOS_SUCCESSFUL;
+}
 
-int chrp_pcibios_find_device(unsigned short vendor, unsigned short dev_id,
-			     unsigned short index, unsigned char *bus_ptr,
-			     unsigned char *dev_fn_ptr)
-{
-    int num, devfn;
-    unsigned int x, vendev;
-
-    if (vendor == 0xffff)
-	return PCIBIOS_BAD_VENDOR_ID;
-    vendev = (dev_id << 16) + vendor;
-    num = 0;
-    for (devfn = 0;  devfn < 32;  devfn++) {
-	chrp_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x);
-	if (x == vendev) {
-	    if (index == num) {
-		*bus_ptr = 0;
-		*dev_fn_ptr = devfn<<3;
-		return PCIBIOS_SUCCESSFUL;
-	    }
-	    ++num;
-	}
-    }
-    return PCIBIOS_DEVICE_NOT_FOUND;
+int chrp_pcibios_read_config_word(unsigned char bus, 
+                                      unsigned char dev_fn,
+                                      unsigned char offset, 
+                                      unsigned short *val)
+{
+        if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND;
+        if (offset&1)return PCIBIOS_BAD_REGISTER_NUMBER;
+        out_be32(pci_config_address,
+                 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24));
+        *val = in_le16((volatile unsigned short *)
+                       (pci_config_data+(offset&3)));
+        return PCIBIOS_SUCCESSFUL;
 }
 
-int chrp_pcibios_find_class(unsigned int class_code, unsigned short index,
-			    unsigned char *bus_ptr, unsigned char *dev_fn_ptr)
+int chrp_pcibios_read_config_dword(unsigned char bus, 
+                                       unsigned char dev_fn,
+                                       unsigned char offset, 
+                                       unsigned int *val)
 {
-    int devnr, x, num;
+        if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND;
+        if (offset&3)return PCIBIOS_BAD_REGISTER_NUMBER;
+        out_be32(pci_config_address,
+                 0x80|(bus<<8)|(dev_fn<<16)|(offset<<24));
+        *val = in_le32((volatile unsigned int *)(pci_config_data));
+        return PCIBIOS_SUCCESSFUL;
+}
 
-    num = 0;
-    for (devnr = 0;  devnr < 32;  devnr++) {
-	chrp_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x);
-	if ((x>>8) == class_code) {
-	    if (index == num) {
-		*bus_ptr = 0;
-		*dev_fn_ptr = devnr<<3;
-		return PCIBIOS_SUCCESSFUL;
-	    }
-	    ++num;
+int chrp_pcibios_write_config_byte(unsigned char bus, 
+                                       unsigned char dev_fn,
+                                       unsigned char offset, 
+                                       unsigned char val) 
+{
+        if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND;
+        out_be32(pci_config_address,
+                 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24));
+        out_8(pci_config_data+(offset&3),val);
+        return PCIBIOS_SUCCESSFUL;
+}
+
+int chrp_pcibios_write_config_word(unsigned char bus, 
+                                       unsigned char dev_fn,
+                                       unsigned char offset, 
+                                       unsigned short val) 
+{
+        if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND;
+        if (offset&1)return PCIBIOS_BAD_REGISTER_NUMBER;
+        out_be32(pci_config_address,
+                 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24));
+        out_le16((volatile unsigned short *)(pci_config_data+(offset&3)),val);
+        return PCIBIOS_SUCCESSFUL;
+}
+
+int chrp_pcibios_write_config_dword(unsigned char bus, 
+                                        unsigned char dev_fn,
+                                        unsigned char offset, 
+                                        unsigned int val) 
+{
+        if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND;
+        if (offset&3)return PCIBIOS_BAD_REGISTER_NUMBER;
+        out_be32(pci_config_address,
+                 0x80|(bus<<8)|(dev_fn<<16)|(offset<<24));
+        out_le32((volatile unsigned int *)pci_config_data,val);
+        return PCIBIOS_SUCCESSFUL;
+}
+#endif
+
+    /*
+     *  Temporary fixes for PCI devices. These should be replaced by OF query
+     *  code -- Geert
+     */
+
+static u_char hydra_openpic_initsenses[] __initdata = {
+    1,	/* HYDRA_INT_SIO */
+    0,	/* HYDRA_INT_SCSI_DMA */
+    0,	/* HYDRA_INT_SCCA_TX_DMA */
+    0,	/* HYDRA_INT_SCCA_RX_DMA */
+    0,	/* HYDRA_INT_SCCB_TX_DMA */
+    0,	/* HYDRA_INT_SCCB_RX_DMA */
+    1,	/* HYDRA_INT_SCSI */
+    1,	/* HYDRA_INT_SCCA */
+    1,	/* HYDRA_INT_SCCB */
+    1,	/* HYDRA_INT_VIA */
+    1,	/* HYDRA_INT_ADB */
+    0,	/* HYDRA_INT_ADB_NMI */
+    	/* all others are 1 (= default) */
+};
+
+__initfunc(int hydra_init(void))
+{
+	struct device_node *np;
+
+	np = find_devices("mac-io");
+	if (np == NULL || np->n_addrs == 0) {
+		printk(KERN_WARNING "Warning: no mac-io found\n");
+		return 0;
 	}
-    }
-    return PCIBIOS_DEVICE_NOT_FOUND;
+	Hydra = ioremap(np->addrs[0].address, np->addrs[0].size);
+	printk("Hydra Mac I/O at %x\n", np->addrs[0].address);
+	out_le32(&Hydra->Feature_Control, (HYDRA_FC_SCC_CELL_EN |
+					   HYDRA_FC_SCSI_CELL_EN |
+					   HYDRA_FC_SCCA_ENABLE |
+					   HYDRA_FC_SCCB_ENABLE |
+					   HYDRA_FC_ARB_BYPASS |
+					   HYDRA_FC_MPIC_ENABLE |
+					   HYDRA_FC_SLOW_SCC_PCLK |
+					   HYDRA_FC_MPIC_IS_MASTER));
+	OpenPIC = (volatile struct OpenPIC *)&Hydra->OpenPIC;
+	OpenPIC_InitSenses = hydra_openpic_initsenses;
+	OpenPIC_NumInitSenses = sizeof(hydra_openpic_initsenses);
+	return 1;
 }
 
-__initfunc(volatile struct Hydra *find_hydra(void))
+
+extern int chrp_ide_irq;
+
+__initfunc(int w83c553f_init(void))
 {
     u_char bus, dev;
-    volatile struct Hydra *hydra = 0;
-    if (chrp_pcibios_find_device(PCI_VENDOR_ID_APPLE,
-				 PCI_DEVICE_ID_APPLE_HYDRA, 0, &bus, &dev)
-	== PCIBIOS_SUCCESSFUL)
-	chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0,
-				       (unsigned int *)&hydra);
-    return hydra;
-}
-
-__initfunc(void hydra_post_openpic_init(void))
-{
-    openpic_set_sense(HYDRA_INT_SCSI_DMA, 0);
-    openpic_set_sense(HYDRA_INT_SCCA_TX_DMA, 0);
-    openpic_set_sense(HYDRA_INT_SCCA_RX_DMA, 0);
-    openpic_set_sense(HYDRA_INT_SCCB_TX_DMA, 0);
-    openpic_set_sense(HYDRA_INT_SCCB_RX_DMA, 0);
-    openpic_set_sense(HYDRA_INT_SCSI, 1);
-    openpic_set_sense(HYDRA_INT_SCCA, 1);
-    openpic_set_sense(HYDRA_INT_SCCB, 1);
-    openpic_set_sense(HYDRA_INT_VIA, 1);
-    openpic_set_sense(HYDRA_INT_ADB, 1);
-    openpic_set_sense(HYDRA_INT_ADB_NMI, 0);
+    unsigned char t8;
+    unsigned short t16;
+    unsigned int t32;
+    if (pcibios_find_device(PCI_VENDOR_ID_WINBOND,
+			    PCI_DEVICE_ID_WINBOND_83C553, 0, &bus, &dev)
+	== PCIBIOS_SUCCESSFUL) {
+	dev++;
+	chrp_pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32);
+	if (t32 == (PCI_DEVICE_ID_WINBOND_82C105<<16) + PCI_VENDOR_ID_WINBOND) {
+#if 0
+	    printk("Enabling SL82C105 IDE on W83C553F\n");
+	    /*
+	     *  FIXME: this doesn't help :-(
+	     */
+
+	    /* I/O mapping */
+	    chrp_pcibios_read_config_word(bus, dev, PCI_COMMAND, &t16);
+	    t16 |= PCI_COMMAND_IO;
+	    chrp_pcibios_write_config_word(bus, dev, PCI_COMMAND, t16);
+
+	    /* Standard IDE registers */
+	    chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0,
+					    0xffffffff);
+	    chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32);
+	    chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0,
+					    0x000001f0 | 1);
+	    chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_1,
+					    0xffffffff);
+	    chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_1, &t32);
+	    chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_1,
+					    0x000003f4 | 1);
+	    chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_2,
+					    0xffffffff);
+	    chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_2, &t32);
+	    chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_2,
+					    0x00000170 | 1);
+	    chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_3,
+					    0xffffffff);
+	    chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_3, &t32);
+	    chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_3,
+					    0x00000374 | 1);
+
+	    /* IDE Bus Master Control */
+	    chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_4,
+					    0xffffffff);
+	    chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_4, &t32);
+	    chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_4,
+					    0x1000 | 1);
+	    chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_5,
+					    0xffffffff);
+	    chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_5, &t32);
+	    chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_5,
+					    0x1010 | 1);
+
+	    /* IDE Interrupt */
+	    chrp_pcibios_read_config_byte(bus, dev, PCI_INTERRUPT_LINE, &t8);
+	    chrp_ide_irq = t8;
+#endif
+	    return 1;
+	}
+    }
+    return 0;
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c
--- v2.1.78/linux/arch/ppc/kernel/chrp_setup.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/chrp_setup.c	Mon Jan 12 15:18:13 1998
@@ -28,11 +28,20 @@
 #include <linux/init.h>
 #include <linux/blk.h>
 #include <linux/ioport.h>
+#ifdef CONFIG_ABSTRACT_CONSOLE
+#include <linux/console.h>
+#endif
 
 #include <asm/mmu.h>
 #include <asm/processor.h>
 #include <asm/io.h>
 #include <asm/pgtable.h>
+#include <asm/ide.h>
+#include <asm/prom.h>
+#include <asm/gg2.h>
+
+extern void hydra_init(void);
+extern void w83c553f_init(void);
 
 /* for the mac fs */
 kdev_t boot_dev;
@@ -52,110 +61,103 @@
 #endif
 
 
-extern char saved_command_line[256];
+int chrp_ide_irq = 0;
 
-long TotalMemory;
-
-int
-chrp_get_cpuinfo(char *buffer)
+void chrp_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)
 {
-	int pvr = _get_PVR();
-	int len;
-	char *model;
-
-	switch (pvr>>16)
-	{
-	case 1:
-		model = "601";
-		break;
-	case 3:
-		model = "603";
-		break;
-	case 4:
-		model = "604";
-		break;
-	case 6:
-		model = "603e";
-		break;
-	case 7:
-		model = "603ev";
-		break;
-	case 9:
-		model = "604e";
-		break;
-	default:
-		model = "unknown";
-		break;
-	}
+	ide_ioreg_t port = base;
+	int i = 8;
 
-	len = sprintf(buffer, "PowerPC %s rev %d.%d\n", model,
-		      (pvr & 0xff00) >> 8, pvr & 0xff);
+	while (i--)
+		*p++ = port++;
+	*p++ = base + 0x206;
+	if (irq != NULL)
+		*irq = chrp_ide_irq;
+}
 
-	len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n",
-		       (loops_per_sec+2500)/500000,
-		       ((loops_per_sec+2500)/5000) % 100);
+static const char *gg2_memtypes[4] = {
+    "FPM", "SDRAM", "EDO", "BEDO"
+};
+static const char *gg2_cachesizes[4] = {
+    "256 KB", "512 KB", "1 MB", "Reserved"
+};
+static const char *gg2_cachetypes[4] = {
+    "Asynchronous", "Reserved", "Flow-Through Synchronous",
+    "Pipelined Synchronous"
+};
+static const char *gg2_cachemodes[4] = {
+    "Disabled", "Write-Through", "Copy-Back", "Transparent Mode"
+};
 
-#if 0
-	/*
-	 * Ooh's and aah's info about zero'd pages in idle task
-	 */
-	{
-		extern unsigned int zerocount, zerototal, zeropage_hits,zeropage_calls;
-		len += sprintf(buffer+len,"zero pages\t: total %u (%uKb) "
-			       "current: %u (%uKb) hits: %u/%u (%lu%%)\n",
-			       zerototal, (zerototal*PAGE_SIZE)>>10,
-			       zerocount, (zerocount*PAGE_SIZE)>>10,
-			       zeropage_hits,zeropage_calls,
-			       /* : 1 below is so we don't div by zero */
-			       (zeropage_hits*100) /
-				    ((zeropage_calls)?zeropage_calls:1));
+int
+chrp_get_cpuinfo(char *buffer)
+{
+	int i, len, sdramen;
+	unsigned int t;
+	struct device_node *root;
+	const char *model = "";
+
+	root = find_path_device("/");
+	if (root)
+	    model = get_property(root, "model", NULL);
+	len = sprintf(buffer,"machine\t\t: CHRP %s\n", model);
+
+	/* VLSI VAS96011/12 `Golden Gate 2' */
+	/* Memory banks */
+	sdramen = (in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_DRAM_CTRL))
+		   >>31) & 1;
+	for (i = 0; i < (sdramen ? 4 : 6); i++) {
+	    t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_DRAM_BANK0+
+				     i*4));
+	    if (!(t & 1))
+		continue;
+	    switch ((t>>8) & 0x1f) {
+		case 0x1f:
+		    model = "4 MB";
+		    break;
+		case 0x1e:
+		    model = "8 MB";
+		    break;
+		case 0x1c:
+		    model = "16 MB";
+		    break;
+		case 0x18:
+		    model = "32 MB";
+		    break;
+		case 0x10:
+		    model = "64 MB";
+		    break;
+		case 0x00:
+		    model = "128 MB";
+		    break;
+		default:
+		    model = "Reserved";
+		    break;
+	    }
+	    len += sprintf(buffer+len, "memory bank %d\t: %s %s\n", i, model,
+			   gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]);
 	}
-#endif
+	/* L2 cache */
+	t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL));
+	len += sprintf(buffer+len, "l2\t\t: %s %s (%s)\n",
+		       gg2_cachesizes[(t>>7) & 3], gg2_cachetypes[(t>>2) & 3],
+		       gg2_cachemodes[t & 3]);
 	return len;
 }
 
 __initfunc(void
-chrp_setup_arch(char **cmdline_p, unsigned long * memory_start_p,
-	   unsigned long * memory_end_p))
+chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
 	extern char cmd_line[];
-	extern char _etext[], _edata[], _end[];
-	extern int panic_timeout;
-
-	/* Save unparsed command line copy for /proc/cmdline */
-	strcpy( saved_command_line, cmd_line );
-	*cmdline_p = cmd_line;
-
-	*memory_start_p = (unsigned long) Hash+Hash_size;
-	(unsigned long *)*memory_end_p = (unsigned long *)(TotalMemory+KERNELBASE);
 
 	/* init to some ~sane value until calibrate_delay() runs */
 	loops_per_sec = 50000000;
 	
-	/* reboot on panic */	
-	panic_timeout = 180;
-	
-	init_task.mm->start_code = PAGE_OFFSET;
-	init_task.mm->end_code = (unsigned long) _etext;
-	init_task.mm->end_data = (unsigned long) _edata;
-	init_task.mm->brk = (unsigned long) _end;	
-	
 	aux_device_present = 0xaa;
-	
-	switch ( _machine )
-	{
-	case _MACH_chrp:
-		ROOT_DEV = to_kdev_t(0x0801); /* sda1 */
-		break;
-	}
+
+	ROOT_DEV = to_kdev_t(0x0802); /* sda2 (sda1 is for the kernel) */
 	
 #ifdef CONFIG_BLK_DEV_RAM
-#if 0
-	ROOT_DEV = to_kdev_t(0x0200); /* floppy */
-	rd_prompt = 1;
-	rd_doload = 1;
-	rd_image_start = 0;
-#endif
 	/* initrd_start and size are setup by boot/head.S and kernel/head.S */
 	if ( initrd_start )
 	{
@@ -177,4 +179,20 @@
 	request_region(0x40,0x20,"timer");
 	request_region(0x80,0x10,"dma page reg");
 	request_region(0xc0,0x20,"dma2");
+
+	/* PCI bridge config space access area -
+	 * appears to be not in devtree on longtrail. */
+	ioremap(GG2_PCI_CONFIG_BASE, 0x80000);
+
+	/*
+	 *  Temporary fixes for PCI devices.
+	 *  -- Geert
+	 */
+	hydra_init();		/* Mac I/O */
+	w83c553f_init();	/* PCI-ISA bridge and IDE */
+
+#ifdef CONFIG_ABSTRACT_CONSOLE
+	/* Frame buffer device based console */
+	conswitchp = &fb_con;
+#endif
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/chrp_time.c linux/arch/ppc/kernel/chrp_time.c
--- v2.1.78/linux/arch/ppc/kernel/chrp_time.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/chrp_time.c	Mon Jan 12 15:18:13 1998
@@ -8,7 +8,6 @@
  *  copied and modified from intel version
  *
  */
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -24,21 +23,41 @@
 #include <asm/io.h>
 #include <asm/processor.h>
 #include <asm/nvram.h>
-
+#include <asm/prom.h>
 #include "time.h"
 
+static int nvram_as1 = NVRAM_AS1;
+static int nvram_as0 = NVRAM_AS0;
+static int nvram_data = NVRAM_DATA;
+
+void chrp_time_init(void)
+{
+	struct device_node *rtcs;
+	int base;
+
+	rtcs = find_compatible_devices("rtc", "pnpPNP,b00");
+	if (rtcs == NULL || rtcs->addrs == NULL)
+		return;
+	base = ((int *)rtcs->addrs)[2];
+	nvram_as1 = 0;
+	nvram_as0 = base;
+	nvram_data = base + 1;
+}
+
 int chrp_cmos_clock_read(int addr)
 {
-	outb(addr>>8, NVRAM_AS1);
-	outb(addr, NVRAM_AS0);
-	return (inb(NVRAM_DATA));
+	if (nvram_as1 != 0)
+		outb(addr>>8, nvram_as1);
+	outb(addr, nvram_as0);
+	return (inb(nvram_data));
 }
 
 void chrp_cmos_clock_write(unsigned long val, int addr)
 {
-	outb(addr>>8, NVRAM_AS1);
-	outb(addr, NVRAM_AS0);
-	outb(val,NVRAM_DATA);
+	if (nvram_as1 != 0)
+		outb(addr>>8, nvram_as1);
+	outb(addr, nvram_as0);
+	outb(val, nvram_data);
 	return;
 }
 
@@ -50,7 +69,7 @@
 	unsigned char save_control, save_freq_select;
 	struct rtc_time tm;
 
-	to_tm(nowtime, &tm);
+	to_tm(nowtime + 10*60*60, &tm);	/* XXX for now */
 
 	save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */
 
@@ -127,6 +146,31 @@
 	  }
 	if ((year += 1900) < 1970)
 		year += 100;
-	return mktime(year, mon, day, hour, min, sec);
+	return mktime(year, mon, day, hour, min, sec) - 10*60*60 /* XXX for now */;
 }
 
+
+void chrp_calibrate_decr(void)
+{
+	struct device_node *cpu;
+	int freq, *fp, divisor;
+
+	/*
+	 * The cpu node should have a timebase-frequency property
+	 * to tell us the rate at which the decrementer counts.
+	 */
+	freq = 16666000;		/* hardcoded default */
+	cpu = find_type_devices("cpu");
+	if (cpu != 0) {
+		fp = (int *) get_property(cpu, "timebase-frequency", NULL);
+		if (fp != 0)
+			freq = *fp;
+	}
+
+	freq *= 60;	/* try to make freq/1e6 an integer */
+        divisor = 60;
+        printk("time_init: decrementer frequency = %d/%d\n", freq, divisor);
+        decrementer_count = freq / HZ / divisor;
+        count_period_num = divisor;
+        count_period_den = freq / 1000000;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/find_name.c linux/arch/ppc/kernel/find_name.c
--- v2.1.78/linux/arch/ppc/kernel/find_name.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/kernel/find_name.c	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <asm/page.h>
+#include <sys/mman.h>
+/*
+ * Finds a given address in the System.map and prints it out
+ * with its neighbors.  -- Cort
+ */
+
+void main(int argc, char **argv)
+{
+	unsigned long addr, cmp, i;
+	FILE *f;
+	char *ptr;
+	char s[256], last[256];
+	
+	if ( argc < 2 )
+	{
+		fprintf(stderr, "Usage: %s <address>\n", argv[0]);
+		exit(-1);
+	}
+
+	for ( i = 1 ; argv[i] ; i++ )
+	{
+		sscanf( argv[i], "%0x", &addr );
+		/* adjust if addr is relative to kernelbase */
+		if ( addr < PAGE_OFFSET )
+			addr += PAGE_OFFSET;
+		
+		if ( (f = fopen( "System.map", "r" )) == NULL )
+		{
+			perror("fopen()\n");
+			exit(-1);
+		}
+		
+		while ( !feof(f) )
+		{
+			fgets(s, 255 , f);
+			sscanf( s, "%0x", &cmp );
+			if ( addr < cmp )
+				break;
+			strcpy( last, s);
+		}
+		
+		printf( "%s", last);
+	}		
+	fclose(f);
+}
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S
--- v2.1.78/linux/arch/ppc/kernel/head.S	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/head.S	Mon Jan 12 15:18:13 1998
@@ -30,7 +30,50 @@
 #include <linux/errno.h>
 #include <linux/config.h>
 
-#define SYNC() \
+#ifdef CONFIG_APUS
+/* At CYBERBASEp we'll find the following sum:
+ * -KERNELBASE+CyberStormMemoryBase
+ */
+#define CYBERBASEp (0xfff00000)
+#endif
+
+/* optimization for 603 to load the tlb directly from the linux table */
+#define NO_RELOAD_HTAB 1
+	
+CACHE_LINE_SIZE = 32
+LG_CACHE_LINE_SIZE = 5
+
+#define TOPHYS(x)	(x - KERNELBASE)
+
+/*
+ * Macros for storing registers into and loading registers from
+ * exception frames.
+ */
+#define SAVE_GPR(n, base)	stw	n,GPR0+4*(n)(base)
+#define SAVE_2GPRS(n, base)	SAVE_GPR(n, base); SAVE_GPR(n+1, base)
+#define SAVE_4GPRS(n, base)	SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base)
+#define SAVE_8GPRS(n, base)	SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base)
+#define SAVE_10GPRS(n, base)	SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base)
+#define REST_GPR(n, base)	lwz	n,GPR0+4*(n)(base)
+#define REST_2GPRS(n, base)	REST_GPR(n, base); REST_GPR(n+1, base)
+#define REST_4GPRS(n, base)	REST_2GPRS(n, base); REST_2GPRS(n+2, base)
+#define REST_8GPRS(n, base)	REST_4GPRS(n, base); REST_4GPRS(n+4, base)
+#define REST_10GPRS(n, base)	REST_8GPRS(n, base); REST_2GPRS(n+8, base)
+
+#define SAVE_FPR(n, base)	stfd	n,TSS_FPR0+8*(n)(base)
+#define SAVE_2FPRS(n, base)	SAVE_FPR(n, base); SAVE_FPR(n+1, base)
+#define SAVE_4FPRS(n, base)	SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base)
+#define SAVE_8FPRS(n, base)	SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base)
+#define SAVE_16FPRS(n, base)	SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base)
+#define SAVE_32FPRS(n, base)	SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base)
+#define REST_FPR(n, base)	lfd	n,TSS_FPR0+8*(n)(base)
+#define REST_2FPRS(n, base)	REST_FPR(n, base); REST_FPR(n+1, base)
+#define REST_4FPRS(n, base)	REST_2FPRS(n, base); REST_2FPRS(n+2, base)
+#define REST_8FPRS(n, base)	REST_4FPRS(n, base); REST_4FPRS(n+4, base)
+#define REST_16FPRS(n, base)	REST_8FPRS(n, base); REST_8FPRS(n+8, base)
+#define REST_32FPRS(n, base)	REST_16FPRS(n, base); REST_16FPRS(n+16, base)
+
+#define SYNC \
 	sync; \
 	isync
 
@@ -43,94 +86,16 @@
 	addi	r4,r4,0x1000; \
 	bdnz	0b
 
-#define TOPHYS(x)	(x - KERNELBASE)
+#define LOAD_BAT(n, offset, reg, RA, RB) \
+	lwz	RA,offset+0(reg); \
+	lwz	RB,offset+4(reg);	\
+	mtspr	IBAT##n##U,RA;	\
+	mtspr	IBAT##n##L,RB;	\
+	lwz	RA,offset+8(reg);	\
+	lwz	RB,offset+12(reg);	\
+	mtspr	DBAT##n##U,RA;	\
+	mtspr	DBAT##n##L,RB
 
-/* this is a very kludgey way of loading up the BATs on the
-   prep system.  I'll kill this horrible macro and write
-   something clean when I have a chance -- Cort
- */	
-#define LOAD_BATS(RA,RB) \
-	mfspr	RA,PVR		; \
-	srwi	RA,RA,16	; \
-	cmpi	0,RA,1		; \
-	beq	199f		; \
-	/* load bats for 60x */	; \
-	lis	RA,BAT0@h	; \
-	ori	RA,RA,BAT0@l	; \
-	addis	RA,RA,-KERNELBASE@h;\
-	lwz	RB,0(RA)	; \
-	mtspr	IBAT0U,RB	; \
-	mtspr	DBAT0U,RB	; \
-	lwz	RB,4(RA)	; \
-	mtspr	IBAT0L,RB	; \
-	mtspr	DBAT0L,RB	; \
-	lis	RA,BAT1@h	; \
-	ori	RA,RA,BAT1@l	; \
-	addis	RA,RA,-KERNELBASE@h;\
-	lwz	RB,0(RA)	; \
-	mtspr	IBAT1U,RB	; \
-	mtspr	DBAT1U,RB	; \
-	lwz	RB,4(RA)	; \
-	mtspr	IBAT1L,RB	; \
-	mtspr	DBAT1L,RB	; \
-	lis	RA,BAT2@h	; \
-	ori	RA,RA,BAT2@l	; \
-	addis	RA,RA,-KERNELBASE@h;\
-	lwz	RB,0(RA)	; \
-	mtspr	IBAT2U,RB	; \
-	mtspr	DBAT2U,RB	; \
-	lwz	RB,4(RA)	; \
-	mtspr	IBAT2L,RB	; \
-	mtspr	DBAT2L,RB	; \
-	lis	RA,BAT3@h	; \
-	ori	RA,RA,BAT3@l	; \
-	addis	RA,RA,-KERNELBASE@h;\
-	lwz	RB,0(RA)	; \
-	mtspr	IBAT3U,RB	; \
-	mtspr	DBAT3U,RB	; \
-	lwz	RB,4(RA)	; \
-	mtspr	IBAT3L,RB	; \
-	mtspr	DBAT3L,RB	; \
-	b	200f		; \
-199:	/*load bats for 601 */	; \
-	lis	RA,BAT0_601@h	; \
-	ori	RA,RA,BAT0_601@l; \
-	addis	RA,RA,-KERNELBASE@h;\
-	lwz	RB,0(RA)	; \
-	mtspr	IBAT0U,RB	; \
-	mtspr	DBAT0U,RB	; \
-	lwz	RB,4(RA)	; \
-	mtspr	IBAT0L,RB	; \
-	mtspr	DBAT0L,RB	; \
-	lis	RA,BAT1_601@h	; \
-	ori	RA,RA,BAT1_601@l; \
-	addis	RA,RA,-KERNELBASE@h;\
-	lwz	RB,0(RA)	; \
-	mtspr	IBAT1U,RB	; \
-	mtspr	DBAT1U,RB	; \
-	lwz	RB,4(RA)	; \
-	mtspr	IBAT1L,RB	; \
-	mtspr	DBAT1L,RB	; \
-	lis	RA,BAT2_601@h	; \
-	ori	RA,RA,BAT2_601@l; \
-	addis	RA,RA,-KERNELBASE@h;\
-	lwz	RB,0(RA)	; \
-	mtspr	IBAT2U,RB	; \
-	mtspr	DBAT2U,RB	; \
-	lwz	RB,4(RA)	; \
-	mtspr	IBAT2L,RB	; \
-	mtspr	DBAT2L,RB	; \
-	lis	RA,BAT3_601@h	; \
-	ori	RA,RA,BAT3_601@l; \
-	addis	RA,RA,-KERNELBASE@h;\
-	lwz	RB,0(RA)	; \
-	mtspr	IBAT3U,RB	; \
-	mtspr	DBAT3U,RB	; \
-	lwz	RB,4(RA)	; \
-	mtspr	IBAT3L,RB	; \
-	mtspr	DBAT3L,RB	; \
-200:
-	
 	.text
 	.globl	_stext
 _stext:
@@ -152,8 +117,8 @@
  * managing the hash table.  Interrupts are disabled.  The stack
  * pointer (r1) points to just below the end of the half-meg region
  * from 0x380000 - 0x400000, which is mapped in already.
- */
-/* PREP
+ *
+ * PREP
  * This is jumped to on prep systems right after the kernel is relocated
  * to its proper place in memory by the boot loader.  The expected layout
  * of the regs is:	
@@ -172,30 +137,47 @@
 __start:
 
 /*
+ * We have to do any OF calls before we map ourselves to KERNELBASE,
+ * because OF may have I/O devices mapped in in that area
+ * (particularly on CHRP).
+ */
+	mr	r31,r3			/* save parameters */
+	mr	r30,r4
+	mr	r29,r5
+	mr	r28,r6
+	mr	r29,r7
+	bl	prom_init
+
+/*
  * Use the first pair of BAT registers to map the 1st 16MB
  * of RAM to KERNELBASE.
  */
-        mfspr   r9,PVR
-        rlwinm  r9,r9,16,16,31          /* r9 = 1 for 601, 4 for 604 */
-        cmpi    0,r9,1
-        lis     r11,KERNELBASE@h
-        bne     4f
-        ori     r11,r11,4               /* set up BAT registers for 601 */
-        li      r8,0x7f
-        ori     r11,r11,4               /* set up BAT registers for 601 */
-        li      r8,0x7f
-        oris    r9,r11,0x800000@h       /* set up BAT reg for 2nd 8M */
-        oris    r10,r8,0x800000@h       /* set up BAT reg for 2nd 8M */
-        mtspr   IBAT1U,r9
-        mtspr   IBAT1L,r10
-        b       5f
-4:      ori     r11,r11,0x1ff           /* set up BAT registers for 604 */
-        li      r8,2
-        mtspr   DBAT0U,r11
-        mtspr   DBAT0L,r8
-5:      mtspr   IBAT0U,r11
-        mtspr   IBAT0L,r8
-        isync
+	mfspr	r9,PVR
+	rlwinm	r9,r9,16,16,31		/* r9 = 1 for 601, 4 for 604 */
+	cmpi	0,r9,1
+	lis	r11,KERNELBASE@h
+	bne	4f
+	ori	r11,r11,4		/* set up BAT registers for 601 */
+	li	r8,0x7f
+	oris	r9,r11,0x800000@h	/* set up BAT reg for 2nd 8M */
+	oris	r10,r8,0x800000@h	/* set up BAT reg for 2nd 8M */
+	mtspr	IBAT1U,r9
+	mtspr	IBAT1L,r10
+	b	5f
+4:	ori	r11,r11,0x1ff		/* set up BAT registers for 604 */
+#ifndef CONFIG_APUS
+	li	r8,2
+#else
+	lis	r8,CYBERBASEp@h
+	lwz	r8,0(r8)
+	addis	r8,r8,KERNELBASE@h
+	addi	r8,r8,2
+#endif
+	mtspr	DBAT0U,r11
+	mtspr	DBAT0L,r8
+5:	mtspr	IBAT0U,r11
+	mtspr	IBAT0L,r8
+	isync
 /*
  * we now have the 1st 16M of ram mapped with the bats.
  * prep needs the mmu to be turned on here, but pmac already has it on.
@@ -247,6 +229,20 @@
 #define REST_16FPRS(n, base)	REST_8FPRS(n, base); REST_8FPRS(n+8, base)
 #define REST_32FPRS(n, base)	REST_16FPRS(n, base); REST_16FPRS(n+16, base)
 
+#ifndef CONFIG_APUS
+#define tophys(rd,rs,rt)	addis	rd,rs,-KERNELBASE@h
+#define tovirt(rd,rs,rt)	addis	rd,rs,KERNELBASE@h
+#else
+#define tophys(rd,rs,rt)	 \
+	lis	rt,CYBERBASEp@h; \
+	lwz	rt,0(rt);	 \
+	add	rd,rs,rt
+#define tovirt(rd,rs,rt)	 \
+	lis	rt,CYBERBASEp@h; \
+	lwz	rt,0(rt);	 \
+	sub	rd,rs,rt
+#endif
+	
 /*
  * Exception entry code.  This code runs with address translation
  * turned off, i.e. using physical addresses.
@@ -254,21 +250,15 @@
  * task's thread_struct.
  */
 #define EXCEPTION_PROLOG	\
-0:	mtspr	SPRG0,r20;	\
+	mtspr	SPRG0,r20;	\
 	mtspr	SPRG1,r21;	\
 	mfcr	r20;		\
-	mfspr	r21,SRR1;		/* test whether from user or kernel */\
-	andi.	r21,r21,MSR_PR;	\
-	mr	r21,r1;			/* from kernel - use current sp */\
-	beq	1f;		\
-	mfspr	r21,SPRG3;		/* from user - load kernel sp */\
-	lwz	r21,KSP(r21);	\
-1:	addis	r21,r21,-KERNELBASE@h;	/* convert sp to physical */	\
+	mfspr	r21,SPRG2;		/* exception stack to use from */ \
+	cmpwi	0,r21,0;		/* user mode or RTAS */ \
+	bne	1f;		\
+	tophys(r21,r1,r21);		/* use tophys(kernel sp) otherwise */ \
 	subi	r21,r21,INT_FRAME_SIZE+STACK_UNDERHEAD;	/* alloc exc. frame */\
-	stw	r1,GPR1(r21);	\
-	stw	r1,0(r21);	\
-	addis	r1,r21,KERNELBASE@h;	/* set new kernel sp */		\
-	stw	r20,_CCR(r21);		/* save registers */		\
+1:	stw	r20,_CCR(r21);		/* save registers */ \
 	stw	r22,GPR22(r21);	\
 	stw	r23,GPR23(r21);	\
 	mfspr	r20,SPRG0;	\
@@ -282,9 +272,12 @@
 	mfspr	r20,XER;	\
 	stw	r20,_XER(r21);	\
 	mfspr	r22,SRR0;	\
-	mfspr	r23,SRR1;		/* we can now take exceptions */\
+	mfspr	r23,SRR1;	\
 	stw	r0,GPR0(r21);	\
+	stw	r1,GPR1(r21);	\
 	stw	r2,GPR2(r21);	\
+	stw	r1,0(r21);	\
+	tovirt(r1,r21,r1);		/* set new kernel sp */	\
 	SAVE_4GPRS(3, r21);
 /*
  * Note: code which follows this uses cr0.eq (set if from kernel),
@@ -315,7 +308,7 @@
 DataAccess:
 	EXCEPTION_PROLOG
 	mfspr	r20,DSISR
-	andis.	r0,r20,0x8470		/* weird error? */
+	andis.	r0,r20,0xa470		/* weird error? */
 	bne	1f			/* if not, try to put a PTE */
 	mfspr	r3,DAR			/* into the hash table */
 	rlwinm	r4,r23,32-13,30,30	/* MSR_PR -> _PAGE_USER */
@@ -419,6 +412,47 @@
  */
 	. = 0x1000
 InstructionTLBMiss:
+#ifdef NO_RELOAD_HTAB
+/*
+ * r0:	stored ctr
+ * r1:	linux style pte ( later becomes ppc hardware pte )
+ * r2:	ptr to linux-style pte
+ * r3:	scratch
+ */
+	mfctr	r0
+	/* Get PTE (linux-style) and check access */
+	mfspr	r2,SPRG3
+	lwz	r2,PG_TABLES(r2)
+	tophys(r2,r2,r3)
+	mfspr	r3,IMISS
+	rlwimi	r2,r3,12,20,29		/* insert top 10 bits of address */
+	lwz	r2,0(r2)		/* get pmd entry */
+	rlwinm.	r2,r2,0,0,19		/* extract address of pte page */
+	beq-	InstructionAddressInvalid	/* return if no mapping */
+	tophys(r2,r2,r1)
+	rlwimi	r2,r3,22,20,29		/* insert next 10 bits of address */
+	lwz	r1,0(r2)		/* get linux-style pte */
+	/* setup access flags in r3 */
+	mfmsr	r3
+	rlwinm	r3,r3,32-13,30,30	/* MSR_PR -> _PAGE_USER */
+	ori	r3,r3,1			/* set _PAGE_PRESENT bit in access */
+	andc.	r3,r3,r1		/* check access & ~permission */
+	bne-	InstructionAddressInvalid /* return if access not permitted */
+	ori	r1,r1,0x100		/* set _PAGE_ACCESSED in pte */
+	stw	r1,0(r2)		/* update PTE (accessed bit) */
+	/* Convert linux-style PTE to low word of PPC-style PTE */
+	/* this computation could be done better -- Cort */
+	rlwinm	r3,r1,32-9,31,31	/* _PAGE_HWWRITE -> PP lsb */
+	rlwimi	r1,r1,32-1,31,31	/* _PAGE_USER -> PP (both bits now) */
+	ori	r3,r3,0xe04		/* clear out reserved bits */
+	andc	r1,r1,r3		/* PP=2 or 0, when _PAGE_HWWRITE */
+	mtspr	RPA,r1
+	mfspr	r3,IMISS
+	tlbli	r3
+	mfspr	r3,SRR1		/* Need to restore CR0 */
+	mtcrf	0x80,r3
+	rfi	
+#else
 	mfctr	r0		/* Need to save this - CTR can't be touched! */
 	mfspr	r2,HASH1	/* Get PTE pointer */
 	mfspr	r3,ICMP		/* Partial item compare value */
@@ -443,20 +477,25 @@
 	mfspr	r2,HASH2	/* Get hash table pointer */
 	ori	r3,r3,0x40	/* Set secondary hash */
 	b	00b		/* Try lookup again */
+#endif /* NO_RELOAD_HTAB */
 InstructionAddressInvalid:
 	mfspr	r3,SRR1
 	rlwinm	r1,r3,9,6,6	/* Get load/store bit */
-	addis	r1,r1,0x4000	/* Set bit 1 -> PTE not found */
-	mtspr	DSISR,r1
+#ifdef NO_RELOAD_HTAB
+	addis	r1,r1,0x2000
+#else
+	addis	r1,r1,0x4000	/* Set bit 1 -> PTE not found (in HTAB) */
+#endif /* NO_RELOAD_HTAB */
+	mtspr	DSISR,r1	/* (shouldn't be needed) */
 	mtctr	r0		/* Restore CTR */
 	andi.	r2,r3,0xFFFF	/* Clear upper bits of SRR1 */
 	or	r2,r2,r1
 	mtspr	SRR1,r2
 	mfspr	r1,IMISS	/* Get failing address */
 	rlwinm.	r2,r2,0,31,31	/* Check for little endian access */
-	beq	20f		/* Jump if big endian */
-	xori	r1,r1,3
-20:	mtspr	DAR,r1		/* Set fault address */
+	rlwimi	r2,r2,1,30,30	/* change 1 -> 3 */
+	xor	r1,r1,r2
+	mtspr	DAR,r1		/* Set fault address */
 	mfmsr	r0		/* Restore "normal" registers */
 	xoris	r0,r0,MSR_TGPR>>16
 	mtcrf	0x80,r3		/* Restore CR0 */
@@ -469,6 +508,48 @@
  */
 	. = 0x1100
 DataLoadTLBMiss:
+#ifdef NO_RELOAD_HTAB
+/*
+ * r0:	stored ctr
+ * r1:	linux style pte ( later becomes ppc hardware pte )
+ * r2:	ptr to linux-style pte
+ * r3:	scratch
+ */
+	mfctr	r0
+	/* Get PTE (linux-style) and check access */
+	mfspr	r2,SPRG3
+	lwz	r2,PG_TABLES(r2)		
+	tophys(r2,r2,r3)
+	mfspr	r3,DMISS
+	rlwimi	r2,r3,12,20,29		/* insert top 10 bits of address */
+	lwz	r2,0(r2)		/* get pmd entry */
+	rlwinm.	r2,r2,0,0,19		/* extract address of pte page */
+	beq-	DataAddressInvalid	/* return if no mapping */
+	tophys(r2,r2,r1)
+	rlwimi	r2,r3,22,20,29		/* insert next 10 bits of address */
+	lwz	r1,0(r2)		/* get linux-style pte */
+	/* setup access flags in r3 */
+	mfmsr	r3
+	rlwinm	r3,r3,32-13,30,30	/* MSR_PR -> _PAGE_USER */
+	ori	r3,r3,1			/* set _PAGE_PRESENT bit in access */
+	/* save r2 and use it as scratch for the andc. */	
+	andc.	r3,r3,r1		/* check access & ~permission */
+	bne-	DataAddressInvalid	/* return if access not permitted */
+	ori	r1,r1,0x100		/* set _PAGE_ACCESSED in pte */
+	stw	r1,0(r2)		/* update PTE (accessed bit) */
+	/* Convert linux-style PTE to low word of PPC-style PTE */
+	/* this computation could be done better -- Cort */
+	rlwinm	r3,r1,32-9,31,31	/* _PAGE_HWWRITE -> PP lsb */
+	rlwimi	r1,r1,32-1,31,31	/* _PAGE_USER -> PP (both bits now) */
+	ori	r3,r3,0xe04		/* clear out reserved bits */
+	andc	r1,r1,r3		/* PP=2 or 0, when _PAGE_HWWRITE */
+	mtspr	RPA,r1
+	mfspr	r3,DMISS
+	tlbld	r3
+	mfspr	r3,SRR1		/* Need to restore CR0 */
+	mtcrf	0x80,r3
+	rfi	
+#else
 	mfctr	r0		/* Need to save this - CTR can't be touched! */
 	mfspr	r2,HASH1	/* Get PTE pointer */
 	mfspr	r3,DCMP		/* Partial item compare value */
@@ -493,10 +574,15 @@
 	mfspr	r2,HASH2	/* Get hash table pointer */
 	ori	r3,r3,0x40	/* Set secondary hash */
 	b	00b		/* Try lookup again */
+#endif /* NO_RELOAD_HTAB */
 DataAddressInvalid:
 	mfspr	r3,SRR1
 	rlwinm	r1,r3,9,6,6	/* Get load/store bit */
+#ifdef NO_RELOAD_HTAB
+	addis	r1,r1,0x2000
+#else
 	addis	r1,r1,0x4000	/* Set bit 1 -> PTE not found */
+#endif /* NO_RELOAD_HTAB */
 	mtspr	DSISR,r1
 	mtctr	r0		/* Restore CTR */
 	andi.	r2,r3,0xFFFF	/* Clear upper bits of SRR1 */
@@ -518,6 +604,48 @@
  */
 	. = 0x1200
 DataStoreTLBMiss:
+#ifdef NO_RELOAD_HTAB
+/*
+ * r0:	stored ctr
+ * r1:	linux style pte ( later becomes ppc hardware pte )
+ * r2:	ptr to linux-style pte
+ * r3:	scratch
+ */
+	mfctr	r0
+	/* Get PTE (linux-style) and check access */
+	mfspr	r2,SPRG3
+	lwz	r2,PG_TABLES(r2)		
+	tophys(r2,r2,r3)
+	mfspr	r3,DMISS
+	rlwimi	r2,r3,12,20,29		/* insert top 10 bits of address */
+	lwz	r2,0(r2)		/* get pmd entry */
+	rlwinm.	r2,r2,0,0,19		/* extract address of pte page */
+	beq-	DataAddressInvalid	/* return if no mapping */
+	tophys(r2,r2,r1)
+	rlwimi	r2,r3,22,20,29		/* insert next 10 bits of address */
+	lwz	r1,0(r2)		/* get linux-style pte */
+	/* setup access flags in r3 */
+	mfmsr	r3
+	rlwinm	r3,r3,32-13,30,30	/* MSR_PR -> _PAGE_USER */
+	ori	r3,r3,0x5		/* _PAGE_PRESENT|_PAGE_RW */
+	/* save r2 and use it as scratch for the andc. */	
+	andc.	r3,r3,r1		/* check access & ~permission */
+	bne-	DataAddressInvalid	/* return if access not permitted */
+	ori	r1,r1,0x384		/* set _PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_RW|_PAGE_HWWRITE in pte */
+	stw	r1,0(r2)		/* update PTE (accessed bit) */
+	/* Convert linux-style PTE to low word of PPC-style PTE */
+	/* this computation could be done better -- Cort */
+	rlwinm	r3,r1,32-9,31,31	/* _PAGE_HWWRITE -> PP lsb */
+	rlwimi	r1,r1,32-1,31,31	/* _PAGE_USER -> PP (both bits now) */
+	ori	r3,r3,0xe04		/* clear out reserved bits */
+	andc	r1,r1,r3		/* PP=2 or 0, when _PAGE_HWWRITE */
+	mtspr	RPA,r1
+	mfspr	r3,DMISS
+	tlbld	r3
+	mfspr	r3,SRR1		/* Need to restore CR0 */
+	mtcrf	0x80,r3
+	rfi	
+#else	
 	mfctr	r0		/* Need to save this - CTR can't be touched! */
 	mfspr	r2,HASH1	/* Get PTE pointer */
 	mfspr	r3,DCMP		/* Partial item compare value */
@@ -542,6 +670,8 @@
 	mfspr	r2,HASH2	/* Get hash table pointer */
 	ori	r3,r3,0x40	/* Set secondary hash */
 	b	00b			/* Try lookup again */
+#endif /* NO_RELOAD_HTAB */
+	
 
 /* Instruction address breakpoint exception (on 603/604) */
 	STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint)
@@ -596,17 +726,23 @@
 	SAVE_8GPRS(12, r21)
 	SAVE_8GPRS(24, r21)
 	andi.	r23,r23,MSR_PR
-	mfspr	r23,SPRG3		/* if from user, fix up tss */
+	mfspr	r23,SPRG3		/* if from user, fix up tss.regs */
 	beq	2f
 	addi	r24,r1,STACK_FRAME_OVERHEAD
 	stw	r24,PT_REGS(r23)
 2:	addi	r2,r23,-TSS		/* set r2 to current */
-	addis	r2,r2,KERNELBASE@h
+	tovirt(r2,r2,r23)
 	mflr	r23
 	andi.	r24,r23,0x3f00		/* get vector offset */
 	stw	r24,TRAP(r21)
 	li	r22,0
 	stw	r22,RESULT(r21)
+	mtspr	SPRG2,r22		/* r1 is now kernel sp */
+	addi	r24,r2,TASK_STRUCT_SIZE	/* check for kernel stack overflow */
+	cmplw	0,r1,r2
+	cmplw	1,r1,r24
+	crand	1,1,4
+	bgt	stack_ovf		/* if r2 < r1 < r2+TASK_STRUCT_SIZE */
 	lwz	r24,0(r23)		/* virtual address of handler */
 	lwz	r23,4(r23)		/* where to go when done */
 	mtspr	SRR0,r24
@@ -616,28 +752,67 @@
 	rfi				/* jump to handler, enable MMU */
 
 /*
+ * On kernel stack overflow, load up an initial stack pointer
+ * and call StackOverflow(regs), which should not return.
+ */
+stack_ovf:
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	lis	r1,init_task_union@ha
+	addi	r1,r1,init_task_union@l
+	addi	r1,r1,TASK_UNION_SIZE-STACK_FRAME_OVERHEAD
+	lis	r24,StackOverflow@ha
+	addi	r24,r24,StackOverflow@l
+	li	r20,MSR_KERNEL
+	mtspr	SRR0,r24
+	mtspr	SRR1,r20
+	SYNC
+	rfi
+
+/*
  * Continuation of the floating-point unavailable handler.
  */
 load_up_fpu:
-	bl	giveup_fpu_unmapped
-	ori	r23,r23,MSR_FP		/* enable use of FP after return */
+
+/*
+ * Disable FP for the task which had the FPU previously,
+ * and save its floating-point registers in its thread_struct.
+ * Enables the FPU for use in the kernel on return.
+ */
+#ifndef CONFIG_APUS
+	lis	r6,-KERNELBASE@h
+#else
+	lis	r6,CYBERBASEp@h
+	lwz	r6,0(r6)
+#endif
+	addis	r3,r6,last_task_used_math@ha
+	lwz	r4,last_task_used_math@l(r3)
+	mfmsr	r5
+	ori	r5,r5,MSR_FP
+	SYNC
+	mtmsr	r5			/* enable use of fpu now */
+	SYNC
+	cmpi	0,r4,0
+	beq	1f
+	add	r4,r4,r6
+	addi	r4,r4,TSS	        /* want TSS of last_task_used_math */
+	SAVE_32FPRS(0, r4)
+	mffs	fr0
+	stfd	fr0,TSS_FPSCR-4(r4)
+	lwz	r5,PT_REGS(r4)
+	add	r5,r5,r6
+	lwz	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
+	li	r20,MSR_FP
+	andc	r4,r4,r20		/* disable FP for previous task */
+	stw	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
+
+1:	ori	r23,r23,MSR_FP		/* enable use of FP after return */
 	mfspr	r5,SPRG3		/* current task's TSS (phys) */
 	lfd	fr0,TSS_FPSCR-4(r5)
 	mtfsf	0xff,fr0
 	REST_32FPRS(0, r5)
-
-/* use last_task_used_math instead of fpu_tss */
-	lis	r3,last_task_used_math@ha
-	addis	r3,r3,-KERNELBASE@h
 	subi	r4,r5,TSS
-	addis	r4,r4,KERNELBASE@h
+	sub	r4,r4,r6
 	stw	r4,last_task_used_math@l(r3)
-#if 0	
-	lis	r3,fpu_tss@ha
-	addis	r4,r5,KERNELBASE@h
-	addis	r3,r3,-KERNELBASE@h
-	stw	r4,fpu_tss@l(r3)
-#endif	
 	/* restore registers and return */
 	lwz	r3,_CCR(r21)
 	lwz	r4,_LINK(r21)
@@ -672,27 +847,42 @@
  * physical address of the hash table are known.  These definitions
  * of Hash_base and Hash_bits below are just an example.
  */
+/*
+ * Note that the 603s won't come here, since the 603
+ * loads tlb directly into the tlb from the linux tables, while
+ * others (601, 604, etc.) call hash_page() to load entries from
+ * the linux tables into the hash table.  -- Cort
+ */	
 Hash_base = 0x180000
 Hash_bits = 12				/* e.g. 256kB hash table */
 Hash_msk = (((1 << Hash_bits) - 1) * 64)
 
 	.globl	hash_page
 hash_page:
+#ifdef __SMP__
+	lis	r6,hash_table_lock@h
+	ori	r6,r6,hash_table_lock@l
+	tophys(r6,r6,r2)
+1011:	lwarx	r0,0,r6
+	stwcx.	r6,0,r6
+	bne-	1011b
+	cmpi	0,r0,0
+	bne	1011b
+#endif /* __SMP__ */	
 	/* Get PTE (linux-style) and check access */
-	lwz	r5,MM-TSS(r5)		
-	addis	r5,r5,-KERNELBASE@h	/* get physical current->mm */
-	lwz	r5,PGD(r5)		/* get current->mm->pgd */	
-	addis	r5,r5,-KERNELBASE@h	/* convert to phys addr */
+	lwz	r5,PG_TABLES(r5)		
+	tophys(r5,r5,r2)		/* convert to phys addr */
 	rlwimi	r5,r3,12,20,29		/* insert top 10 bits of address */
 	lwz	r5,0(r5)		/* get pmd entry */
 	rlwinm.	r5,r5,0,0,19		/* extract address of pte page */
-	beqlr-				/* return if no mapping */
-	addis	r2,r5,-KERNELBASE@h
+	beq-	hash_page_out		/* return if no mapping */
+	tophys(r2,r5,r2)
 	rlwimi	r2,r3,22,20,29		/* insert next 10 bits of address */
 	lwz	r6,0(r2)		/* get linux-style pte */
 	ori	r4,r4,1			/* set _PAGE_PRESENT bit in access */
 	andc.	r0,r4,r6		/* check access & ~permission */
-	bnelr-				/* return if access not permitted */
+	bne-	hash_page_out		/* return if access not permitted */
+
 	ori	r6,r6,0x100		/* set _PAGE_ACCESSED in pte */
 	rlwinm	r5,r4,5,24,24		/* _PAGE_RW access -> _PAGE_DIRTY */
 	rlwimi	r5,r4,7,22,22		/* _PAGE_RW -> _PAGE_HWWRITE */
@@ -752,7 +942,7 @@
 10:	mtctr	r2
 	addi	r3,r4,-8		/* search primary PTEG */
 1:	lwzu	r0,8(r3)		/* get next PTE */
-	cmpi	0,r0,0			/* empty? */
+	srwi.	r0,r0,31		/* only want to check valid bit */
 	bdnzf	2,1b			/* loop while ctr != 0 && !cr0.eq */
 	beq+	found_empty
 
@@ -765,24 +955,71 @@
 	addi	r3,r3,-8
 	mtctr	r2
 2:	lwzu	r0,8(r3)
-	cmpi	0,r0,0
+	srwi.	r0,r0,31		/* only want to check valid bit */
 	bdnzf	2,2b
 	beq+	found_empty
 
 	/* Choose an arbitrary slot in the primary PTEG to overwrite */
+#if 0
 	xori	r5,r5,0x40		/* clear H bit again */
 	lwz	r2,next_slot@l(0)
 	addi	r2,r2,8
 	andi.	r2,r2,0x38
 	stw	r2,next_slot@l(0)
 	add	r3,r4,r2
+#else
+	/* now, allow 2nd hash as well as 1st */
+	lwz	r2,next_slot@l(0)
+	addi	r2,r2,8
+	andi.	r2,r2,0x78
+	stw	r2,next_slot@l(0)
+	cmpi	0,0,r2,0x38             /* if it's the 2nd hash */
+	bgt	second_evict
+first_evict:
+	xori	r5,r5,0x40		/* clear H bit again */
+	add	r3,r4,r2
+	b	11f
+second_evict:
+	.globl	hash_page_patch_D
+hash_page_patch_D:	
+	xoris	r3,r4,Hash_msk>>16	/* compute secondary hash */
+	xori	r3,r3,0xffc0
+	subi	r2,r2,0x40
+	addi	r3,r3,r2
+#endif
+11:		
+	/* update counter of evicted pages */
+	lis	r2,htab_evicts@h
+	ori	r2,r2,htab_evicts@l
+	tophys(r2,r2,r4)
+	lwz	r4,0(r2)
+	addi	r4,r4,1
+	stw	r4,0(r2)
 
 	/* Store PTE in PTEG */
 found_empty:
 	stw	r5,0(r3)
 found_slot:
 	stw	r6,4(r3)
+
+/*
+ * Update the hash table miss count.  We only want misses here
+ * that _are_ valid addresses and have a pte otherwise we don't
+ * count it as a reload.  do_page_fault() takes care of bad addrs
+ * and entries that need linux-style pte's created.
+ *
+ * safe to use r2 here since we're not using it as current yet 
+ * update the htab misses count
+ *   -- Cort
+ */
+	lis	r2,htab_reloads@h
+	ori	r2,r2,htab_reloads@l
+	tophys(r2,r2,r3)
+	lwz	r3,0(r2)
+	addi	r3,r3,1
+	stw	r3,0(r2)
 	SYNC
+
 	/* Return from the exception */
 	lwz	r3,_CCR(r21)
 	lwz	r4,_LINK(r21)
@@ -790,6 +1027,13 @@
 	mtcrf	0xff,r3
 	mtlr	r4
 	mtctr	r5
+#ifdef __SMP__
+	lis	r5,hash_table_lock@h
+	ori	r5,r5,hash_table_lock@l
+	tophys(r5,r5,r6)
+	li	r6,0
+	stw	r6,0(r5)
+#endif /* __SMP__ */	
 	REST_GPR(0, r21)
 	REST_2GPRS(1, r21)
 	REST_4GPRS(3, r21)
@@ -801,10 +1045,28 @@
 	lwz	r21,GPR21(r21)
 	SYNC
 	rfi
-
+	
+hash_page_out:
+#ifdef __SMP__
+	lis	r5,hash_table_lock@h
+	ori	r5,r5,hash_table_lock@l
+	tophys(r5,r5,r6)
+	li	r6,0
+	stw	r6,0(r5)
+#endif /* __SMP__ */	
+	blr
 next_slot:
 	.long	0
 
+#ifdef CONFIG_APUS
+	/* On APUS the first 0x4000 bytes of the kernel	will be mapped
+	 * at a different physical address than the rest. For this
+	 * reason, the exception code cannot use relative branches to
+	 * access the code below.
+	 */
+	. = 0x4000
+#endif
+
 /*
  * This is where the main kernel code starts.
  */
@@ -831,23 +1093,24 @@
 10:
 	sync
 	mtspr	HID0,r8			/* enable and invalidate caches */
+	sync
 	mtspr	HID0,r11		/* enable caches */
 	sync
 	isync
 	cmpi	0,r9,4			/* check for 604 */
 	cmpi	1,r9,9			/* or 604e */
+	cmpi	2,r9,10			/* or mach5 */
 	cror	2,2,6
+	cror	2,2,10
 	bne	4f
 	ori	r11,r11,HID0_SIED|HID0_BHTE /* for 604[e], enable */
-	mtspr	HID0,r11		/* superscalar exec & br history tbl */
+	bne	2,5f
+	ori	r11,r11,HID0_BTCD
+5:	mtspr	HID0,r11		/* superscalar exec & br history tbl */
 4:
 	/* ptr to current */
 	lis	r2,init_task_union@h
 	ori	r2,r2,init_task_union@l
-	/* ptr to phys current tss */
-	addis	r11,r2,-KERNELBASE@h
-	addi	r11,r11,TSS	/* init task's TSS */
-	mtspr	SPRG3,r11
 	/* stack */
 	addi	r1,r2,TASK_UNION_SIZE
 	li	r0,0
@@ -869,10 +1132,14 @@
 	bdnz	3b
 2:
 /*
- * Initialize the prom stuff and the MMU.
+ * Decide what sort of machine this is and initialize the MMU.
  */
+	mr	r3,r31
+	mr	r4,r30
+	mr	r5,r29
+	mr	r6,r28
+	mr	r7,r27
 	bl	identify_machine
-	bl	prom_init
 	bl	MMU_init
 
 /*
@@ -882,10 +1149,10 @@
  */
 	lis	r6,_SDR1@ha
 	lwz	r6,_SDR1@l(r6)
-	li	r3,MSR_KERNEL & ~(MSR_IR|MSR_DR)
 	lis	r4,2f@h
-	addis	r4,r4,-KERNELBASE@h
 	ori	r4,r4,2f@l
+	tophys(r4,r4,r3)
+	li	r3,MSR_KERNEL & ~(MSR_IR|MSR_DR)
 	mtspr	SRR0,r4
 	mtspr	SRR1,r3
 	rfi
@@ -902,38 +1169,55 @@
 	addi	r3,r3,1		/* increment VSID */
 	addis	r4,r4,0x1000	/* address of next segment */
 	bdnz	3b
-
-	lis	r3,_machine@ha
-	addis	r3,r3,-KERNELBASE@h
-	lwz	r0,_machine@l(r3)
-	cmpi	0,r0,_MACH_Pmac
-	beq	99f
 	
-/* on prep reload the bats now that MMU_init() has setup them up -- Cort */
-	LOAD_BATS(r3,r14)
-	b	100f
-	
-/* on pmac clear the bats out */	
-99:	li	r0,0		/* zot the BATs */
-#if 1
-	mtspr	IBAT0U,r0
-	mtspr	IBAT0L,r0
-	mtspr	DBAT0U,r0
-	mtspr	DBAT0L,r0
-#endif
-	mtspr	IBAT1U,r0
-	mtspr	IBAT1L,r0
-	mtspr	DBAT1U,r0
-	mtspr	DBAT1L,r0
-	mtspr	IBAT2U,r0
-	mtspr	IBAT2L,r0
-	mtspr	DBAT2U,r0
-	mtspr	DBAT2L,r0
-	mtspr	IBAT3U,r0
-	mtspr	IBAT3L,r0
-	mtspr	DBAT3U,r0
-	mtspr	DBAT3L,r0
-100:	
+/* Load the BAT registers with the values set up by MMU_init.
+   MMU_init takes care of whether we're on a 601 or not. */
+	lis	r3,BATS@ha
+	addi	r3,r3,BATS@l
+	tophys(r3,r3,r4)
+	LOAD_BAT(0,0,r3,r4,r5)
+	LOAD_BAT(1,16,r3,r4,r5)
+	LOAD_BAT(2,32,r3,r4,r5)
+	LOAD_BAT(3,48,r3,r4,r5)
+
+/* Set up for using our exception vectors */
+	/* ptr to phys current tss */
+	tophys(r4,r2,r4)
+	addi	r4,r4,TSS	/* init task's TSS */
+	mtspr	SPRG3,r4
+	li	r3,0
+	mtspr	SPRG2,r3	/* 0 => r1 has kernel sp */
+
+/* On CHRP copy exception vectors down to 0 */
+	lis	r5,_stext@ha
+	addi	r5,r5,_stext@l
+	addis	r5,r5,-KERNELBASE@h
+	cmpwi	0,r5,0
+	beq	77f		/* vectors are already at 0 */
+	li	r3,0x1000
+	mtctr	r3
+	li	r4,-4
+	addi	r5,r5,-4
+74:	lwzu	r0,4(r5)
+	stwu	r0,4(r4)
+	bdnz	74b
+	/* need to flush/invalidate caches too */
+	li	r3,0x4000/CACHE_LINE_SIZE
+	li	r4,0
+	mtctr	r3
+73:	dcbst	0,r4
+	addi	r4,r4,CACHE_LINE_SIZE
+	bdnz	73b
+	sync
+	li	r4,0
+	mtctr	r3
+72:	icbi	0,r4
+	addi	r4,r4,CACHE_LINE_SIZE
+	bdnz	72b
+	sync
+	isync
+77:
+
 /* Now turn on the MMU for real! */
 	li	r4,MSR_KERNEL
 	lis	r3,start_kernel@h
@@ -947,25 +1231,25 @@
 reset_SDR1:
 	lis	r6,_SDR1@ha
 	lwz	r6,_SDR1@l(r6)
-	mfmsr	r3
-	li	r4,MSR_IR|MSR_DR
-	andc	r3,r3,r4
+	mfmsr	r5
+	li	r4,0
+	ori	r4,r4,MSR_EE|MSR_IR|MSR_DR
+	andc	r3,r5,r4
 	lis	r4,2f@h
-	addis	r4,r4,-KERNELBASE@h
 	ori	r4,r4,2f@l
+	tophys(r4,r4,r5)
 	mtspr	SRR0,r4
 	mtspr	SRR1,r3
 	rfi
 2:	/* load new SDR1 */
-	tlbia			
+	tlbia
 	mtspr	SDR1,r6
 	/* turn the mmu back on */
-	li	r4,MSR_KERNEL
 	mflr	r3
 	mtspr	SRR0,r3
-	mtspr	SRR1,r4
+	mtspr	SRR1,r5
 	rfi
-	
+
 /*
  * FP unavailable trap from kernel - print a message, but let
  * the task use FP in the kernel until it returns to user mode.
@@ -987,19 +1271,10 @@
  * Disable FP for the task which had the FPU previously,
  * and save its floating-point registers in its thread_struct.
  * Enables the FPU for use in the kernel on return.
- * (If giveup_fpu_unmapped uses any integer registers other than
- * r3 - r6, the return code at load_up_fpu above will have
- * to be adjusted.)
  */
-giveup_fpu_unmapped:
-	lis	r6,-KERNELBASE@h
-	b	1f
-
 	.globl	giveup_fpu
 giveup_fpu:
-	li	r6,0
-1:
-	addis	r3,r6,last_task_used_math@ha
+	lis	r3,last_task_used_math@ha
 	lwz	r4,last_task_used_math@l(r3)
 	mfmsr	r5
 	ori	r5,r5,MSR_FP
@@ -1008,7 +1283,6 @@
 	SYNC
 	cmpi	0,r4,0
 	beqlr-				/* if no previous owner, done */
-	add	r4,r4,r6
 	addi	r4,r4,TSS	        /* want TSS of last_task_used_math */
 	li	r5,0
 	stw	r5,last_task_used_math@l(r3)
@@ -1016,7 +1290,6 @@
 	mffs	fr0
 	stfd	fr0,TSS_FPSCR-4(r4)
 	lwz	r5,PT_REGS(r4)
-	add	r5,r5,r6
 	lwz	r3,_MSR-STACK_FRAME_OVERHEAD(r5)
 	li	r4,MSR_FP
 	andc	r3,r3,r4		/* disable FP for previous task */
@@ -1104,12 +1377,12 @@
 	b	22b
 /* sys_sigreturn */
 10:	addi	r3,r1,STACK_FRAME_OVERHEAD
-	bl	_EXTERN(sys_sigreturn)
+	bl	sys_sigreturn
 	cmpi	0,r3,0		/* Check for restarted system call */
 	bge	int_return
 	b	20b
 /* Traced system call support */
-50:	bl	_EXTERN(syscall_trace)
+50:	bl	syscall_trace
 	lwz	r0,GPR0(r1)	/* Restore original registers */
 	lwz	r3,GPR3(r1)
 	lwz	r4,GPR4(r1)
@@ -1144,7 +1417,7 @@
 	oris	r10,r10,0x1000
 	stw	r10,_CCR(r1)
 60:	stw	r3,GPR3(r1)	/* Update return value */
-	bl	_EXTERN(syscall_trace)
+	bl	syscall_trace
 	b	int_return
 66:	li	r3,ENOSYS
 	b	52b
@@ -1198,7 +1471,7 @@
 	stw	r0,TRAP(r1)
 	stw	r1,KSP(r3)	/* Set old stack pointer */
 	sync
-	addis	r0,r4,-KERNELBASE@h
+	tophys(r0,r4,r3)
 	mtspr	SPRG3,r0	/* Update current TSS phys addr */
 	SYNC
 	lwz	r1,KSP(r4)	/* Load new stack pointer */
@@ -1220,6 +1493,8 @@
 /*
  * Trap exit.
  */
+	.globl	ret_from_syscall
+ret_from_syscall:	
 	.globl	int_return
 int_return:
 0:	mfmsr	r30		/* Disable interrupts */
@@ -1245,34 +1520,31 @@
 	lwz	r5,bh_active@l(r5)
 	and.	r4,r4,r5
 	beq+	2f
-	ori	r31,r30,MSR_EE	/* re-enable interrupts */
-	SYNC
-	mtmsr	r31
-	SYNC
-	bl	_EXTERN(do_bottom_half)
+	bl	do_bottom_half
 	SYNC
 	mtmsr	r30		/* disable interrupts again */
 	SYNC
 2:	lwz	r3,_MSR(r1)	/* Returning to user mode? */
 	andi.	r3,r3,MSR_PR
-	beq+	10f		/* no - no need to mess with stack */
+	beq+	10f		/* if so, check need_resched and signals */
 	lis	r3,need_resched@ha
 	lwz	r3,need_resched@l(r3)
 	cmpi	0,r3,0		/* check need_resched flag */
 	beq+	7f
-	bl	_EXTERN(schedule)
+	bl	schedule
 	b	0b
-7:	lwz	r3,BLOCKED(r2)	/* Check for pending unblocked signals */
-	lwz	r5,SIGNAL(r2)
-	andc.	r0,r5,r3	/* Lets thru any unblocked */
+7:	lwz	r5,SIGPENDING(r2) /* Check for pending unblocked signals */
+	cmpwi	0,r5,0
 	beq+	8f
+	li	r3,0
 	addi	r4,r1,STACK_FRAME_OVERHEAD
-	bl	_EXTERN(do_signal)
+	bl	do_signal
 	b	0b
 8:	addi	r4,r1,INT_FRAME_SIZE+STACK_UNDERHEAD	/* size of frame */
 	stw	r4,TSS+KSP(r2)	/* save kernel stack pointer */
-10:
-	lwz	r2,_CTR(r1)
+	tophys(r3,r1,r3)
+	mtspr	SPRG2,r3	/* phys exception stack pointer */
+10:	lwz	r2,_CTR(r1)
 	lwz	r0,_LINK(r1)
 	mtctr	r2
 	mtlr	r0
@@ -1355,8 +1627,6 @@
  *
  * flush_icache_range(unsigned long start, unsigned long stop)
  */
-CACHE_LINE_SIZE = 32
-LG_CACHE_LINE_SIZE = 5
 _GLOBAL(flush_icache_range)
 	mfspr	r5,PVR
 	rlwinm	r5,r5,16,16,31
@@ -1417,6 +1687,28 @@
  * given.
  */
 _GLOBAL(flush_hash_segments)
+#ifdef NO_RELOAD_HTAB
+/*
+ * Bitmask of PVR numbers of 603-like chips,
+ * for which we don't use the hash table at all.
+ */
+#define PVR_603_LIKE	0x13000000	/* bits 3, 6, 7 set */
+
+	mfspr	r0,PVR
+	rlwinm	r0,r0,16,27,31
+	lis	r9,PVR_603_LIKE@h
+	rlwnm.	r0,r9,r0,0,0
+	bne	99f
+#endif /* NO_RELOAD_HTAB */
+#ifdef __SMP__
+	lis	r6,hash_table_lock@h
+	ori	r6,r6,hash_table_lock@l
+1011:	lwarx	r0,0,r6
+	stwcx.	r6,0,r6
+	bne-	1011b
+	cmpi	0,r0,0
+	bne	1011b
+#endif /* __SMP__ */	
 	rlwinm	r3,r3,7,1,24		/* put VSID lower limit in position */
 	oris	r3,r3,0x8000		/* set V bit */
 	rlwinm	r4,r4,7,1,24		/* put VSID upper limit in position */
@@ -1438,7 +1730,13 @@
 	stw	r0,0(r5)		/* invalidate entry */
 2:	bdnz	1b			/* continue with loop */
 	sync
-	tlbia
+#ifdef __SMP__
+	lis	r5,hash_table_lock@h
+	ori	r5,r5,hash_table_lock@l
+	li	r6,0
+	stw	r6,0(r5)
+#endif /* __SMP__ */	
+99:	tlbia
 	isync
 	blr
 
@@ -1448,6 +1746,22 @@
  * flush_hash_page(unsigned context, unsigned long va)
  */
 _GLOBAL(flush_hash_page)
+#ifdef NO_RELOAD_HTAB
+	mfspr	r0,PVR
+	rlwinm	r0,r0,16,27,31
+	lis	r9,PVR_603_LIKE@h
+	rlwnm.	r0,r9,r0,0,0
+	bne	99f
+#endif /* NO_RELOAD_HTAB */		
+#ifdef __SMP__
+	lis	r6,hash_table_lock@h
+	ori	r6,r6,hash_table_lock@l
+1011:	lwarx	r0,0,r6
+	stwcx.	r6,0,r6
+	bne-	1011b
+	cmpi	0,r0,0
+	bne	1011b
+#endif /* __SMP__ */	
 	rlwinm	r3,r3,11,1,20		/* put context into vsid */
 	rlwimi	r3,r4,11,21,24		/* put top 4 bits of va into vsid */
 	oris	r3,r3,0x8000		/* set V (valid) bit */
@@ -1480,7 +1794,13 @@
 3:	li	r0,0
 	stw	r0,0(r7)		/* invalidate entry */
 4:	sync
-	tlbie	r4			/* in hw tlb too */
+#ifdef __SMP__
+	lis	r5,hash_table_lock@h
+	ori	r5,r5,hash_table_lock@l
+	li	r6,0
+	stw	r6,0(r5)
+#endif /* __SMP__ */	
+99:	tlbie	r4			/* in hw tlb too */
 	isync
 	blr
 
@@ -1491,200 +1811,221 @@
 	blr
 
 /*
- * These exception handlers are used when we have called a prom
- * routine after we have taken over the exception vectors and MMU.
+ * On CHRP, the Run-Time Abstraction Services (RTAS) have to be
+ * called with the MMU off.
  */
-	.globl	prom_exc_table
-prom_exc_table:
-	.long	TOPHYS(prom_exception)		/* 0 */
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)		/* 400 */
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)		/* 800 */
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)		/* c00 */
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)		/* 1000 */
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)		/* 1400 */
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)		/* 1800 */
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)		/* 1c00 */
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)		/* 1000 */
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)		/* 1400 */
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)		/* 1800 */
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)		/* 1c00 */
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-	.long	TOPHYS(prom_exception)
-
-/*
- * When we come in to these prom exceptions, r1 and lr have been
- * saved in sprg1 and sprg2, and lr points to a word containing
- * the vector offset.
- */
-prom_exception:
-	mr	r1,r21			/* save r21 */
-	lis	r21,prom_sp@ha		/* get a stack to use */
-	addis	r21,r21,-KERNELBASE@h
-	lwz	r21,prom_sp@l(r21)
-	addis	r21,r21,-KERNELBASE@h	/* convert to physical addr */
-	subi	r21,r21,INT_FRAME_SIZE+STACK_UNDERHEAD
-	stw	r0,GPR0(r21)
-	stw	r2,GPR2(r21)
-	stw	r3,GPR3(r21)
-	stw	r4,GPR4(r21)
-	stw	r5,GPR5(r21)
-	stw	r6,GPR6(r21)
-	stw	r20,GPR20(r21)
-	stw	r1,GPR21(r21)
-	stw	r22,GPR22(r21)
-	stw	r23,GPR23(r21)
-	mfspr	r1,SPRG1
-	stw	r1,GPR1(r21)
-	mfcr	r3
-	mfspr	r4,SPRG2
-	stw	r3,_CCR(r21)
-	stw	r4,_LINK(r21)
-	mfctr	r3
-	mfspr	r4,XER
-	stw	r3,_CTR(r21)
-	stw	r4,_XER(r21)
-	mfspr	r22,SRR0
-	mfspr	r23,SRR1
-
-	/* at this point we have set things up pretty much exactly
-	   how EXCEPTION_PROLOG does */
-	mflr	r3
-	lwz	r3,0(r3)		/* get exception vector */
-	stw	r3,TRAP(r21)
-	cmpi	0,r3,0x300		/* was it a dsi? */
-	bne	1f
-
-	mfspr	r20,DSISR		/* here on data access exc. */
-	andis.	r0,r20,0x8470		/* weird error? */
-	bne	3f			/* if not, try to put a PTE */
-	mfspr	r3,DAR			/* into the hash table */
-	rlwinm	r4,r23,32-13,30,30	/* MSR_PR -> _PAGE_USER */
-	rlwimi	r4,r20,32-23,29,29	/* DSISR_STORE -> _PAGE_RW */
-	b	2f
-
-1:	cmpi	0,r3,0x400		/* was it an isi? */
-	bne	3f
-	andis.	r0,r23,0x4000		/* if so, check if no pte found */
-	beq	3f			/* if so, try to put a PTE */
-	mr	r3,r22			/* into the hash table */
-	rlwinm	r4,r23,32-13,30,30	/* MSR_PR -> _PAGE_USER */
-	mr	r20,r23			/* SRR1 has reason bits */
-2:	lis	r5,prom_tss@ha		/* phys addr of TSS */
-	addis	r5,r5,-KERNELBASE@h
-	lwz	r5,prom_tss@l(r5)
-	bl	hash_page
-
-3:	addis	r1,r21,KERNELBASE@h	/* restore kernel stack ptr */
-	addi	r3,r1,INT_FRAME_SIZE+STACK_UNDERHEAD
-	stw	r3,0(r21)		/* set stack chain pointer */
-	lis	r5,prom_tss@ha
-	addis	r5,r5,-KERNELBASE@h
-	lwz	r5,prom_tss@l(r5)
-	mtspr	SPRG3,r5		/* reset phys TSS pointer */
-	lwz	r4,TRAP(r21)		/* the real exception vector */
-	addi	r3,r1,STACK_FRAME_OVERHEAD
-	li	r20,MSR_KERNEL
-	bl	transfer_to_handler
-	.long	PromException
-	.long	prom_int_return
-
-	.comm	prom_sp,4
-	.comm	prom_tss,4
-
-	.globl	prom_int_return
-prom_int_return:
-	lis	r3,prom_exc_table@ha	/* restore sprg3 for prom vectors */
-	addi	r3,r3,prom_exc_table@l
+	.globl	enter_rtas
+enter_rtas:
+	stwu	r1,-16(r1)
+	mflr	r0
+	stw	r0,20(r1)
 	addis	r3,r3,-KERNELBASE@h
-	mtspr	SPRG3,r3
-	b	int_return
+	lis	r4,rtas_data@ha
+	lwz	r4,rtas_data@l(r4)
+	lis	r6,1f@ha	/* physical return address for rtas */
+	addi	r6,r6,1f@l
+	addis	r6,r6,-KERNELBASE@h
+	subi	r7,r1,INT_FRAME_SIZE+STACK_UNDERHEAD
+	addis	r7,r7,-KERNELBASE@h
+	lis	r8,rtas_entry@ha
+	lwz	r8,rtas_entry@l(r8)
+	mfmsr	r9
+	stw	r9,8(r1)
+	li	r0,0
+	ori	r0,r0,MSR_EE|MSR_SE|MSR_BE
+	andc	r0,r9,r0
+	andi.	r9,r9,MSR_ME|MSR_RI
+	sync			/* disable interrupts so SRR0/1 */
+	mtmsr	r0		/* don't get trashed */
+	mtlr	r6
+	mtspr	SPRG2,r7
+	mtspr	SRR0,r8
+	mtspr	SRR1,r9
+	rfi
+1:	addis	r9,r1,-KERNELBASE@h
+	lwz	r8,20(r9)	/* get return address */
+	lwz	r9,8(r9)	/* original msr value */
+	li	r0,0
+	mtspr	SPRG2,r0
+	mtspr	SRR0,r8
+	mtspr	SRR1,r9
+	rfi			/* return to caller */
 
+	
+	.globl amhere
+amhere:	.long 0
+	
+#ifdef __SMP__
 /*
- * When entering the prom, we have to change to using a different
- * set of exception vectors.
+ * Secondary processor begins executing here.
  */
-	.globl	enter_prom
-enter_prom:
-	stwu	r1,-32(r1)
-	mflr	r0
-	stw	r0,36(r1)
-	stw	r29,20(r1)
-	stw	r30,24(r1)
-	stw	r31,28(r1)
-	lis	r8,prom_entry@ha
-	lwz	r8,prom_entry@l(r8)
-	mfmsr	r31
-	andi.	r0,r31,MSR_IP		/* using our own vectors yet? */
-	beq	1f			/* if so, have to switch */
-	mtlr	r8
-	blrl				/* if not, can just charge ahead */
-	b	2f
-1:	lis	r9,prom_sp@ha		/* save sp for exception handler */
-	stw	r1,prom_sp@l(r9)
-	mfspr	r29,SPRG3		/* save physical tss pointer */
-	lis	r9,prom_tss@ha
-	stw	r29,prom_tss@l(r9)
-	li	r9,0
-	ori	r9,r9,MSR_EE
-	andc	r30,r31,r9
-	lis	r9,prom_exc_table@ha	/* set pointer to exception table */
-	addi	r9,r9,prom_exc_table@l
-	addis	r9,r9,-KERNELBASE@h
-	ori	r0,r31,MSR_IP
+	.globl	secondary_entry
+secondary_entry:
+	lis	r0,amhere@h
+	ori	r0,r0,amhere@l
+	addis	r0,r0,-KERNELBASE@h
+	stw	r0,0(r0)
 	sync
-	mtmsr	r30			/* disable interrupts */
-	mtspr	SPRG3,r9		/* while we update MSR_IP and sprg3 */
+	isync	
+	/* just like __start() with a few changes -- Cort */
+	mfspr	r9,PVR
+	rlwinm	r9,r9,16,16,31		/* r9 = 1 for 601, 4 for 604 */
+	cmpi	0,r9,1
+	lis	r11,KERNELBASE@h
+	bne	4f
+	ori	r11,r11,4		/* set up BAT registers for 601 */
+	li	r8,0x7f
+	oris	r9,r11,0x800000@h	/* set up BAT reg for 2nd 8M */
+	oris	r10,r8,0x800000@h	/* set up BAT reg for 2nd 8M */
+	mtspr	IBAT1U,r9
+	mtspr	IBAT1L,r10
+	b	5f
+4:	ori	r11,r11,0x1ff		/* set up BAT registers for 604 */
+	li	r8,2
+	mtspr	DBAT0U,r11
+	mtspr	DBAT0L,r8
+5:	mtspr	IBAT0U,r11
+	mtspr	IBAT0L,r8
+	isync
+/*
+ * we now have the 1st 16M of ram mapped with the bats.
+ * prep needs the mmu to be turned on here, but pmac already has it on.
+ * this shouldn't bother the pmac since it just gets turned on again
+ * as we jump to our code at KERNELBASE. -- Cort
+ */
+	mfmsr	r0
+	ori	r0,r0,MSR_DR|MSR_IR
+	mtspr	SRR1,r0
+	lis	r0,100f@h
+	ori	r0,r0,100f@l
+	mtspr	SRR0,r0
+	SYNC
+	rfi				/* enables MMU */
+100:
+	/*
+	 * Enable caches and 604-specific features if necessary.
+	 */
+	mfspr	r9,PVR
+	rlwinm	r9,r9,16,16,31
+	cmpi	0,r9,1
+	beq	4f			/* not needed for 601 */
+	mfspr	r11,HID0
+	andi.	r0,r11,HID0_DCE
+	ori	r11,r11,HID0_ICE|HID0_DCE
+	ori	r8,r11,HID0_ICFI
+	bne	3f			/* don't invalidate the D-cache */
+	ori	r8,r8,HID0_DCI		/* unless it wasn't enabled */
+3:
+	/* turn on dpm for 603 */
+	cmpi	0,r9,3
+	bne	10f
+	oris	r11,r11,HID0_DPM@h
+10:
 	sync
-	mtmsr	r0			/* start using exc. vectors in prom */
-	mtlr	r8
-	blrl				/* call prom */
+	mtspr	HID0,r8			/* enable and invalidate caches */
 	sync
-	mtmsr	r30			/* disable interrupts again */
-	mtspr	SPRG3,r29		/* while we restore MSR_IP and sprg3 */
+	mtspr	HID0,r11		/* enable caches */
 	sync
-	mtmsr	r31			/* reenable interrupts */
-2:	lwz	r0,36(r1)
-	mtlr	r0
-	lwz	r29,20(r1)
-	lwz	r30,24(r1)
-	lwz	r31,28(r1)
-	lwz	r1,0(r1)
-	blr
+	isync
+	cmpi	0,r9,4			/* check for 604 */
+	cmpi	1,r9,9			/* or 604e */
+	cmpi	2,r9,10			/* or mach5 */
+	cror	2,2,6
+	cror	2,2,10
+	bne	4f
+	ori	r11,r11,HID0_SIED|HID0_BHTE /* for 604[e], enable */
+	bne	2,5f
+	ori	r11,r11,HID0_BTCD
+5:	mtspr	HID0,r11		/* superscalar exec & br history tbl */
+4:
+	/* get ptr to current */
+	lis	r2,current_set@h
+	ori	r2,r2,current_set@l
+	/* assume we're second processor for now */
+	lwz	r2,4(r2)
+	/* stack */
+	addi	r1,r2,TASK_UNION_SIZE
+	li	r0,0
+	stwu	r0,-STACK_FRAME_OVERHEAD(r1)
+		
+/*
+ * init_MMU on the first processor has setup the variables
+ * for us - all we need to do is load them -- Cort 
+ */
+	
+/*
+ * Go back to running unmapped so we can load up new values
+ * for SDR1 (hash table pointer) and the segment registers
+ * and change to using our exception vectors.
+ */
+	lis	r6,_SDR1@ha
+	lwz	r6,_SDR1@l(r6)
+	lis	r4,2f@h
+	ori	r4,r4,2f@l
+	tophys(r4,r4,r3)
+	li	r3,MSR_KERNEL & ~(MSR_IR|MSR_DR)
+	mtspr	SRR0,r4
+	mtspr	SRR1,r3
+	rfi
+/* Load up the kernel context */
+2:
+	SYNC			/* Force all PTE updates to finish */
+	tlbia			/* Clear all TLB entries */
+	mtspr	SDR1,r6
+	li	r0,16		/* load up segment register values */
+	mtctr	r0		/* for context 0 */
+	lis	r3,0x2000	/* Ku = 1, VSID = 0 */
+	li	r4,0
+3:	mtsrin	r3,r4
+	addi	r3,r3,1		/* increment VSID */
+	addis	r4,r4,0x1000	/* address of next segment */
+	bdnz	3b
+	
+/* Load the BAT registers with the values set up by MMU_init.
+   MMU_init takes care of whether we're on a 601 or not. */
+	lis	r3,BATS@ha
+	addi	r3,r3,BATS@l
+	tophys(r3,r3,r4)
+	LOAD_BAT(0,0,r3,r4,r5)
+	LOAD_BAT(1,16,r3,r4,r5)
+	LOAD_BAT(2,32,r3,r4,r5)
+	LOAD_BAT(3,48,r3,r4,r5)
 
+/* Set up for using our exception vectors */
+	/* ptr to phys current tss */
+	tophys(r4,r2,r4)
+	addi	r4,r4,TSS	/* init task's TSS */
+	mtspr	SPRG3,r4
+	li	r3,0
+	mtspr	SPRG2,r3	/* 0 => r1 has kernel sp */
+
+	/* need to flush/invalidate caches too */
+	li	r3,0x4000/CACHE_LINE_SIZE
+	li	r4,0
+	mtctr	r3
+73:	dcbst	0,r4
+	addi	r4,r4,CACHE_LINE_SIZE
+	bdnz	73b
+	sync
+	li	r4,0
+	mtctr	r3
+72:	icbi	0,r4
+	addi	r4,r4,CACHE_LINE_SIZE
+	bdnz	72b
+	sync
+	isync
+77:
+/* Now turn on the MMU for real! */
+	li	r4,MSR_KERNEL
+	lis	r3,start_secondary@h
+	ori	r3,r3,start_secondary@l
+	mtspr	SRR0,r3
+	mtspr	SRR1,r4
+	rfi			/* enable MMU and jump to start_kernel */
+/* should never return */
+	.long 0
+#endif /* __SMP__ */
+	
 /*
  * We put a few things here that have to be page-aligned.
  * This stuff goes at the beginning of the data segment,
@@ -1708,4 +2049,3 @@
 	.globl	cmd_line
 cmd_line:
 	.space	512
-
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/idle.c linux/arch/ppc/kernel/idle.c
--- v2.1.78/linux/arch/ppc/kernel/idle.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/idle.c	Mon Jan 12 15:18:13 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: idle.c,v 1.4 1997/08/23 22:46:01 cort Exp $
+ * $Id: idle.c,v 1.13 1998/01/06 06:44:55 cort Exp $
  *
  * Idle daemon for PowerPC.  Idle daemon will handle any action
  * that needs to be taken when the system becomes idle.
@@ -31,40 +31,126 @@
 #include <asm/io.h>
 #include <asm/smp_lock.h>
 #include <asm/processor.h>
+#include <asm/mmu.h>
 
 int zero_paged(void *unused);
-int power_saved(void *unused);
+void inline power_save(void);
+void inline htab_reclaim(void);
 
-asmlinkage int sys_idle(void)
+int idled(void *unused)
 {
 	int ret = -EPERM;
 
-	if (current->pid != 0)
-		goto out;
-
 	/*
 	 * want one per cpu since it would be nice to have all
 	 * processors who aren't doing anything
 	 * zero-ing pages since this daemon is lock-free
 	 * -- Cort
 	 */
-	kernel_thread(zero_paged, NULL, 0);
-	/* no powersaving modes on 601 */
-	/*if(  (_get_PVR()>>16) != 1 )
-		kernel_thread(power_saved, NULL, 0);*/
-
-	/* endless loop with no priority at all */
-	current->priority = -100;
-	current->counter = -100;
+	/* kernel_thread(zero_paged, NULL, 0); */
+
+#ifdef __SMP__	
+printk("SMP %d: in idle.  current = %s/%d\n",
+       current->processor,current->comm,current->pid);
+#endif /* __SMP__ */
 	for (;;)
 	{
+		/* endless loop with no priority at all */
+		current->priority = -100;
+		current->counter = -100;
+
+		/* endless idle loop with no priority at all */
+		/* htab_reclaim(); */
 		schedule();
+#ifndef __SMP__
+		/* can't do this on smp since second processor
+		   will never wake up -- Cort */
+		/* power_save(); */
+#endif /* __SMP__  */
 	}
 	ret = 0;
-out:
 	return ret;
 }
 
+
+/*
+ * Mark 'zombie' pte's in the hash table as invalid.
+ * This improves performance for the hash table reload code
+ * a bit since we don't consider unused pages as valid.
+ * I haven't done any rigorous performance analysis yet
+ * so it's still experimental and turned off here.
+ *  -- Cort
+ */
+void inline htab_reclaim(void)
+{
+	PTE *ptr, *start;
+	struct task_struct *p;
+	unsigned long valid = 0;
+	extern PTE *Hash, *Hash_end;
+	extern unsigned long Hash_size;
+
+	/* if we don't have a htab */
+	if ( Hash_size == 0 )
+		return;
+	/*lock_dcache();*/
+	
+	/* find a random place in the htab to start each time */
+	start = &Hash[jiffies%(Hash_size/sizeof(ptr))];
+	for ( ptr = start; ptr < Hash_end ; ptr++)
+	{
+		if ( ptr == start )
+			return;
+		if ( ptr == Hash_end )
+			ptr = Hash;
+		valid = 0;
+		if (!ptr->v)
+			continue;
+		for_each_task(p)
+		{
+			if ( need_resched )
+			{
+				/*unlock_dcache();*/
+				return;
+			}
+			/* if this vsid/context is in use */
+			if ( (ptr->vsid >> 4) == p->mm->context )
+			{
+				valid = 1;
+				break;
+			}
+		}
+		if ( valid )
+			continue;
+		/* this pte isn't used */
+		ptr->v = 0;
+	}
+	/*unlock_dcache();*/
+}
+
+/*
+ * Syscall entry into the idle task. -- Cort
+ */
+asmlinkage int sys_idle(void)
+{
+	if(current->pid != 0)
+		return -EPERM;
+
+	idled(NULL);
+	return 0; /* should never execute this but it makes gcc happy -- Cort */
+}
+
+#ifdef __SMP__
+/*
+ * SMP entry into the idle task - calls the same thing as the
+ * non-smp versions. -- Cort
+ */
+int cpu_idle(void *unused)
+{
+	idled(unused);
+	return 0; 
+}
+#endif /* __SMP__ */
+
 /*
  * vars for idle task zero'ing out pages
  */
@@ -105,7 +191,7 @@
 		atomic_inc((atomic_t *)&zeropage_hits);
 		atomic_dec((atomic_t *)&zerocount);
 		wake_up(&page_zerod_wait);
-		resched_force();
+		need_resched = 1;
 		
 		/* zero out the pointer to next in the page */
 		*(unsigned long *)page = 0;
@@ -120,7 +206,6 @@
  * Zero's out pages until we need to resched or
  * we've reached the limit of zero'd pages.
  */
-
 int zero_paged(void *unused)
 {
 	extern pte_t *get_pte( struct mm_struct *mm, unsigned long address );
@@ -129,14 +214,18 @@
 	pte_t *pte;
 
 	sprintf(current->comm, "zero_paged (idle)");
-	current->blocked = ~0UL;
+	/* current->blocked = ~0UL; */
 	
+#ifdef __SMP__
+	printk("Started zero_paged (cpu %d)\n", hard_smp_processor_id());
+#else
 	printk("Started zero_paged\n");
+#endif /* __SMP__ */
 	
 	__sti();
 	while ( 1 )
 	{
-		/* don't want to be pre-empted by swapper or power_saved */
+		/* don't want to be pre-empted by swapper or power_save */
 		current->priority = -98;
 		current->counter = -98;
 		/* we don't want to run until we have something to do */
@@ -149,12 +238,9 @@
 		 */
 		pageptr = __get_free_pages(GFP_ATOMIC, 0, 0 );
 		if ( !pageptr )
-		{
-			printk("!pageptr in zero_paged\n");
 			goto retry;
-		}
 		
-		if ( resched_needed() )
+		if ( need_resched )
 			schedule();
 		
 		/*
@@ -180,7 +266,7 @@
 		 */
 		for ( bytecount = 0; bytecount < PAGE_SIZE ; bytecount += 4 )
 		{
-			if ( resched_needed() )
+			if ( need_resched )
 				schedule();
 			*(unsigned long *)(bytecount + pageptr) = 0;
 		}
@@ -228,52 +314,30 @@
 	}
 }
 
-int power_saved(void *unused)
+void inline power_save(void)
 {
 	unsigned long msr, hid0;
-	sprintf(current->comm, "power_saved (idle)");
-	current->blocked = ~0UL;
-	
-	printk("Power saving daemon started\n");
+
+	/* no powersaving modes on the 601 */
+	if(  (_get_PVR()>>16) == 1 )
+		return;
 	
 	__sti();
-	while (1)
-	{
-		/* don't want to be pre-empted by swapper */
-		current->priority = -99;
-		current->counter = -99;
-		/* go ahead and wakeup page_zerod() */
-		wake_up(&page_zerod_wait);
-		schedule();
-		asm volatile(
-			/* clear powersaving modes and set nap mode */
-			"mfspr %3,1008 \n\t"
-			"andc  %3,%3,%4 \n\t"
-			"or    %3,%3,%5 \n\t"
-			"mtspr 1008,%3 \n\t"
-			/* enter the mode */
-			"mfmsr %0 \n\t"
-			"oris  %0,%0,%2 \n\t"
-			"sync \n\t"
-			"mtmsr %0 \n\t"
-			"isync \n\t"
-			: "=&r" (msr)
-			: "0" (msr), "i" (MSR_POW>>16),
-			"r" (hid0),
-			"r" (HID0_DOZE|HID0_NAP|HID0_SLEEP),
-			"r" (HID0_NAP));
-		/*
-		 * The ibm carolina spec says that the eagle memory
-		 * controller will detect the need for a snoop
-		 * and wake up the processor so we don't need to
-		 * check for cache operations that need to be
-		 * snooped.  The ppc book says the run signal
-		 * must be asserted while napping for this though.
-		 *
-		 * Paul, what do you know about the pmac here?
-		 * -- Cort
-		 */
-		schedule();
-	}
+	asm volatile(
+		/* clear powersaving modes and set nap mode */
+		"mfspr %3,1008 \n\t"
+		"andc  %3,%3,%4 \n\t"
+		"or    %3,%3,%5 \n\t"
+		"mtspr 1008,%3 \n\t"
+		/* enter the mode */
+		"mfmsr %0 \n\t"
+		"oris  %0,%0,%2 \n\t"
+		"sync \n\t"
+		"mtmsr %0 \n\t"
+		"isync \n\t"
+		: "=&r" (msr)
+		: "0" (msr), "i" (MSR_POW>>16),
+		"r" (hid0),
+		"r" (HID0_DOZE|HID0_NAP|HID0_SLEEP),
+		"r" (HID0_NAP));
 }
-
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c
--- v2.1.78/linux/arch/ppc/kernel/irq.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/irq.c	Mon Jan 12 15:18:13 1998
@@ -38,17 +38,22 @@
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/bitops.h>
+#include <asm/gg2.h>
 
 #undef SHOW_IRQ
-#define OPENPIC_DEBUG
 
 unsigned lost_interrupts = 0;
 unsigned int local_irq_count[NR_CPUS];
 static struct irqaction irq_action[NR_IRQS];
 static int spurious_interrupts = 0;
-int __ppc_bh_counter;
 static unsigned int cached_irq_mask = 0xffffffff;
 static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
+spinlock_t irq_controller_lock;
+#ifdef __SMP__
+atomic_t __ppc_bh_counter = ATOMIC_INIT(0);
+#else
+int __ppc_bh_counter = 0;
+#endif
 
 #define cached_21	(((char *)(&cached_irq_mask))[3])
 #define cached_A1	(((char *)(&cached_irq_mask))[2])
@@ -57,7 +62,8 @@
  * These are set to the appropriate functions by init_IRQ()
  */
 void (*mask_and_ack_irq)(int irq_nr);
-void (*set_irq_mask)(int irq_nr);
+void (*mask_irq)(unsigned int irq_nr);
+void (*unmask_irq)(unsigned int irq_nr);
 
 
 /* prep */
@@ -72,8 +78,6 @@
 #define KEYBOARD_IRQ	20	/* irq number for command-power interrupt */
 #define PMAC_IRQ_MASK	(~ld_le32(IRQ_ENABLE))
 
-/* chrp */
-volatile struct Hydra *Hydra = NULL;
 
 void i8259_mask_and_ack_irq(int irq_nr)
 {
@@ -97,10 +101,14 @@
 
 void pmac_mask_and_ack_irq(int irq_nr)
 {
+	unsigned long bit = 1UL << irq_nr;
+
 	spin_lock(&irq_controller_lock);
-	cached_irq_mask |= 1 << irq_nr;
-	out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) & ~(1 << irq_nr));
-	out_le32(IRQ_ACK, 1U << irq_nr);
+	cached_irq_mask |= bit;
+	lost_interrupts &= ~bit;
+	out_le32(IRQ_ACK, bit);
+	out_le32(IRQ_ENABLE, ~cached_irq_mask);
+	out_le32(IRQ_ACK, bit);
 	spin_unlock(&irq_controller_lock);
 }
 
@@ -109,11 +117,10 @@
 	/* spinlocks are done by i8259_mask_and_ack() - Cort */
 	if (is_8259_irq(irq_nr))
 	    i8259_mask_and_ack_irq(irq_nr);
-	openpic_eoi(0);	
 }
 
 
-void i8259_set_irq_mask(int irq_nr)
+static void i8259_set_irq_mask(int irq_nr)
 {
 	if (irq_nr > 7) {
 		outb(cached_A1,0xA1);
@@ -122,8 +129,10 @@
 	}
 }
 
-void pmac_set_irq_mask(int irq_nr)
+static void pmac_set_irq_mask(int irq_nr)
 {
+	unsigned long bit = 1UL << irq_nr;
+
 	/* this could be being enabled or disabled - so use cached_irq_mask */
 	out_le32(IRQ_ENABLE, ~cached_irq_mask /* enable all unmasked */ );
 	/*
@@ -131,39 +140,53 @@
 	 * when the device interrupt is already on *doesn't* set
 	 * the bit in the flag register or request another interrupt.
 	 */
-	if ((ld_le32(IRQ_LEVEL) & (1UL<<irq_nr)) && !(ld_le32(IRQ_FLAG) & (1UL<<irq_nr)))
-		lost_interrupts |= (1UL<<irq_nr);
-}
-
-void chrp_set_irq_mask(int irq_nr)
-{
-	if (is_8259_irq(irq_nr)) {
-		i8259_set_irq_mask(irq_nr);
-	} else
-	{
-		/* disable? */
-		if ( cached_irq_mask & (1UL<<irq_nr) )
-			openpic_disable_irq(irq_to_openpic(irq_nr));
-		/* enable */
-		else
-			openpic_disable_irq(irq_to_openpic(irq_nr));
-	}
+	if ((bit & ~cached_irq_mask)
+	    && (ld_le32(IRQ_LEVEL) & bit) && !(ld_le32(IRQ_FLAG) & bit))
+		lost_interrupts |= bit;
 }
 
 /*
  * These have to be protected by the spinlock
  * before being called.
  */
-static inline void mask_irq(unsigned int irq_nr)
+static void i8259_mask_irq(unsigned int irq_nr)
+{
+	cached_irq_mask |= 1 << irq_nr;
+	i8259_set_irq_mask(irq_nr);
+}
+
+static void i8259_unmask_irq(unsigned int irq_nr)
+{
+	cached_irq_mask &= ~(1 << irq_nr);
+	i8259_set_irq_mask(irq_nr);
+}
+
+static void pmac_mask_irq(unsigned int irq_nr)
 {
 	cached_irq_mask |= 1 << irq_nr;
-	set_irq_mask(irq_nr);
+	pmac_set_irq_mask(irq_nr);
 }
 
-static inline void unmask_irq(unsigned int irq_nr)
+static void pmac_unmask_irq(unsigned int irq_nr)
 {
 	cached_irq_mask &= ~(1 << irq_nr);
-	set_irq_mask(irq_nr);
+	pmac_set_irq_mask(irq_nr);
+}
+
+static void chrp_mask_irq(unsigned int irq_nr)
+{
+	if (is_8259_irq(irq_nr))
+		i8259_mask_irq(irq_nr);
+	else
+		openpic_disable_irq(irq_to_openpic(irq_nr));
+}
+
+static void chrp_unmask_irq(unsigned int irq_nr)
+{
+	if (is_8259_irq(irq_nr))
+		i8259_unmask_irq(irq_nr);
+	else
+		openpic_enable_irq(irq_to_openpic(irq_nr));
 }
 
 void disable_irq(unsigned int irq_nr)
@@ -205,11 +228,167 @@
 	len+=sprintf(buf+len, "IPI: %8lu received\n",
 		ipi_count);
 #endif
-	len += sprintf(buf+len, "99: %10u    spurious or short\n",
+	len += sprintf(buf+len, "99: %10u   spurious or short\n",
 		       spurious_interrupts);
 	return len;
 }
 
+
+#ifdef __SMP__
+/* Who has global_irq_lock. */
+unsigned char global_irq_holder = NO_PROC_ID;
+
+/* This protects IRQ's. */
+spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED;
+unsigned long previous_irqholder;
+
+/* This protects BH software state (masks, things like that). */
+spinlock_t global_bh_lock = SPIN_LOCK_UNLOCKED;
+
+/* Global IRQ locking depth. */
+atomic_t global_irq_count = ATOMIC_INIT(0);
+
+#undef INIT_STUCK
+#define INIT_STUCK 100000000
+
+#undef STUCK
+#define STUCK \
+if (!--stuck) {printk("wait_on_irq CPU#%d stuck at %08lx, waiting for %08lx (local=%d, global=%d)\n", cpu, where, previous_irqholder, local_count, atomic_read(&global_irq_count)); stuck = INIT_STUCK; }
+
+void wait_on_irq(int cpu, unsigned long where)
+{
+	int stuck = INIT_STUCK;
+	int local_count = local_irq_count[cpu];
+
+	/* Are we the only one in an interrupt context? */
+	while (local_count != atomic_read(&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);
+		spin_unlock(&global_irq_lock);
+
+		/*
+		 * Wait for everybody else to go away and release
+		 * their things before trying to get the lock again.
+		 */
+		for (;;) {
+			STUCK;
+			if (atomic_read(&global_irq_count))
+				continue;
+			if (*((unsigned char *)&global_irq_lock))
+				continue;
+			if (spin_trylock(&global_irq_lock))
+				break;
+		}
+		atomic_add(local_count, &global_irq_count);
+	}
+}
+
+#define irq_active(cpu) \
+	(global_irq_count != local_irq_count[cpu])
+
+/*
+ * This is called when we want to synchronize with
+ * interrupts. We may for example tell a device to
+ * stop sending interrupts: but to make sure there
+ * are no interrupts that are executing on another
+ * CPU we need to call this function.
+ *
+ * On UP this is a no-op.
+ */
+void synchronize_irq(void)
+{
+	int cpu = smp_processor_id();
+	int local_count = local_irq_count[cpu];
+
+	/* Do we need to wait? */
+	if (local_count != atomic_read(&global_irq_count)) {
+		/* The stupid way to do this */
+		cli();
+		sti();
+	}
+}
+
+#undef INIT_STUCK
+#define INIT_STUCK 10000000
+
+#undef STUCK
+#define STUCK \
+if (!--stuck) {\
+ll_printk("get_irqlock stuck at %08lx, waiting for %08lx\n", where, previous_irqholder); stuck = INIT_STUCK;}
+
+void get_irqlock(int cpu, unsigned long where)
+{
+	int stuck = INIT_STUCK;
+	if (!spin_trylock(&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;
+				barrier();
+			} while (*((unsigned char *)&global_irq_lock));
+		} while (!spin_trylock(&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. 
+	 */
+	wait_on_irq(cpu, where);
+	/*
+	 * Finally.
+	 */
+	global_irq_holder = cpu;
+	previous_irqholder = where;
+}
+
+void __global_cli(void)
+{
+	int cpu = smp_processor_id();
+	unsigned long where;
+	__asm__("mr %0,31" : "=r" (where)); /* get lr */
+	__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:
+		release_irqlock(smp_processor_id());
+		__sti();
+		break;
+	case 1:
+		__global_cli();
+		break;
+	default:
+		printk("global_restore_flags: %08lx (%08lx)\n",
+			flags, (&flags)[-1]);
+	}
+}
+#endif /* __SMP__ */
+
+
 asmlinkage void do_IRQ(struct pt_regs *regs)
 {
 	int irq;
@@ -217,9 +396,14 @@
 	struct irqaction *action;
 	int cpu = smp_processor_id();
 	int status;
+	int openpic_eoi_done = 0;
 
-	hardirq_enter(cpu);
+#ifdef __SMP__
+	if ( cpu != 0 )
+		panic("cpu %d received interrupt", cpu);
+#endif /* __SMP__ */		
 
+	hardirq_enter(cpu);
 
 	/*
 	 * I'll put this ugly mess of code into a function
@@ -232,7 +416,6 @@
 	{
 	case _MACH_Pmac:
 		bits = ld_le32(IRQ_FLAG) | lost_interrupts;
-		lost_interrupts = 0;
 		for (irq = NR_IRQS - 1; irq >= 0; --irq)
 			if (bits & (1U << irq))
 				break;
@@ -246,30 +429,37 @@
 			 * 
 			 * This should go in the above mask/ack code soon. -- Cort
 			 */
-			irq = (*(volatile unsigned char *)0xfec80000) & 0x0f;
+			irq = *(volatile unsigned char *)GG2_INT_ACK_SPECIAL;
+			/*
+			 * Acknowledge as soon as possible to allow i8259
+			 * interrupt nesting
+			 */
+			openpic_eoi(0);
+			openpic_eoi_done = 1;
 		}
-		else if (irq >= 64)
+		else if (irq >= OPENPIC_VEC_TIMER)
 		{
 			/*
 			 *  OpenPIC interrupts >64 will be used for other purposes
 			 *  like interprocessor interrupts and hardware errors
 			 */
-#ifdef OPENPIC_DEBUG
-			printk("OpenPIC interrupt %d\n", irq);
-#endif
-			if (irq==99)
+			if (irq == OPENPIC_VEC_SPURIOUS) {
+				/*
+				 * Spurious interrupts should never be
+				 * acknowledged
+				 */
 				spurious_interrupts++;
-		}
-		else {
-			/*
-			 *  Here we should process IPI timer
-			 *  for now the interrupt is dismissed.
-			 */
+				openpic_eoi_done = 1;
+			} else {
+				/*
+				 * Here we should process IPI timer
+				 * for now the interrupt is dismissed.
+				 */
+			}
 			goto out;
 		}
 		break;
-	case _MACH_IBM:
-	case _MACH_Motorola:
+	case _MACH_prep:
 #if 1
 		outb(0x0C, 0x20);
 		irq = inb(0x20) & 7;
@@ -314,12 +504,11 @@
 	status = 0;
 	action = irq_action + irq;
 	kstat.interrupts[irq]++;
-	if ( action && action->handler)
-	{
+	if (action->handler) {
 		if (!(action->flags & SA_INTERRUPT))
 			__sti();
 		status |= action->flags;
-			action->handler(irq, action->dev_id, regs);
+		action->handler(irq, action->dev_id, regs);
 		/*if (status & SA_SAMPLE_RANDOM)
 			add_interrupt_randomness(irq);*/
 		__cli(); /* in case the handler turned them on */
@@ -337,6 +526,8 @@
 		goto retry_cascade;
 	/* do_bottom_half is called if necessary from int_return in head.S */
 out:
+	if (_machine == _MACH_chrp && !openpic_eoi_done)
+		openpic_eoi(0);
 	hardirq_exit(cpu);
 }
 
@@ -369,6 +560,7 @@
 	restore_flags(flags);
 	return 0;
 }
+
 void free_irq(unsigned int irq, void *dev_id)
 {
 	struct irqaction * action = irq + irq_action;
@@ -411,17 +603,15 @@
 {
 	/* init master interrupt controller */
 	outb(0x11, 0x20); /* Start init sequence */
-	outb(0x40, 0x21); /* Vector base */
-	/*outb(0x04, 0x21);*/ /* edge tiggered, Cascade (slave) on IRQ2 */
-	outb(0x0C, 0x21); /* level triggered, Cascade (slave) on IRQ2 */
+	outb(0x00, 0x21); /* Vector base */
+	outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */
 	outb(0x01, 0x21); /* Select 8086 mode */
 	outb(0xFF, 0x21); /* Mask all */
 	
 	/* init slave interrupt controller */
 	outb(0x11, 0xA0); /* Start init sequence */
-	outb(0x48, 0xA1); /* Vector base */
-	/*outb(0x02, 0xA1);*/ /* edge triggered, Cascade (slave) on IRQ2 */
-	outb(0x0A, 0x21); /* level triggered, Cascade (slave) on IRQ2 */
+	outb(0x08, 0xA1); /* Vector base */
+	outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */
 	outb(0x01, 0xA1); /* Select 8086 mode */
 	outb(0xFF, 0xA1); /* Mask all */
 	outb(cached_A1, 0xA1);
@@ -439,17 +629,30 @@
 	{
 	case _MACH_Pmac:
 		mask_and_ack_irq = pmac_mask_and_ack_irq;
-		set_irq_mask = pmac_set_irq_mask;
+		mask_irq = pmac_mask_irq;
+		unmask_irq = pmac_unmask_irq;
 		
 		*IRQ_ENABLE = 0;
 #ifdef CONFIG_XMON
 		request_irq(KEYBOARD_IRQ, xmon_irq, 0, "NMI", 0);
 #endif	/* CONFIG_XMON */
 		break;
-	case _MACH_Motorola:
-	case _MACH_IBM:
+	case _MACH_chrp:
+		mask_and_ack_irq = chrp_mask_and_ack_irq;
+		mask_irq = chrp_mask_irq;
+		unmask_irq = chrp_unmask_irq;
+		ioremap(GG2_INT_ACK_SPECIAL, 1);
+		openpic_init();
+		i8259_init();
+#ifdef CONFIG_XMON
+		request_irq(openpic_to_irq(HYDRA_INT_ADB_NMI),
+			    xmon_irq, 0, "NMI", 0);
+#endif	/* CONFIG_XMON */
+		break;
+	case _MACH_prep:
 		mask_and_ack_irq = i8259_mask_and_ack_irq;
-		set_irq_mask = i8259_set_irq_mask;
+		mask_irq = i8259_mask_irq;
+		unmask_irq = i8259_unmask_irq;
 		
 		i8259_init();
 		route_pci_interrupts();
@@ -472,46 +675,20 @@
 			/*
 			 * On Carolina, irq 15 and 13 must be level (scsi/ide/net).
 			 */
-			if ( _machine == _MACH_IBM )
+			if ( _prep_type == _PREP_IBM )
 				irq_mode2 |= 0xa0;
 			/*
 			 * Sound on the Powerstack reportedly needs to be edge triggered
 			 */
-			if ( _machine == _MACH_Motorola )
+			if ( _prep_type == _PREP_Motorola )
 			{
-				/*irq_mode2 &= ~0x04L;
+				irq_mode2 &= ~0x04L;
+				irq_mode2 = 0xca;
 				outb( irq_mode1 , 0x4d0 );
-				outb( irq_mode2 , 0x4d1 );*/
+				outb( irq_mode2 , 0x4d1 );
 			}
 
 		}
-		break;
-	case _MACH_chrp:
-		mask_and_ack_irq = chrp_mask_and_ack_irq;
-		set_irq_mask = chrp_set_irq_mask;
-		if ((Hydra = find_hydra())) {
-			printk("Hydra Mac I/O at %p\n", Hydra);
-			out_le32(&Hydra->Feature_Control, HYDRA_FC_SCC_CELL_EN |
-				 HYDRA_FC_SCSI_CELL_EN |
-				 HYDRA_FC_SCCA_ENABLE |
-				 HYDRA_FC_SCCB_ENABLE |
-				 HYDRA_FC_ARB_BYPASS |
-				 HYDRA_FC_MPIC_ENABLE |
-				 HYDRA_FC_SLOW_SCC_PCLK |
-				 HYDRA_FC_MPIC_IS_MASTER);
-			OpenPIC = (volatile struct OpenPIC *)&Hydra->OpenPIC;
-		} else if (!OpenPIC /* && find_xxx */) {
-			printk("Unknown openpic implementation\n");
-			/* other OpenPIC implementations */
-			/* ... */
- 		}
-		if (OpenPIC)
-		    openpic_init();
-		else
-		    panic("No OpenPIC found");
-		if (Hydra)
-		    hydra_post_openpic_init();
-		i8259_init();
 		break;
 	}
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S
--- v2.1.78/linux/arch/ppc/kernel/misc.S	Wed Sep 24 20:05:45 1997
+++ linux/arch/ppc/kernel/misc.S	Mon Jan 12 15:18:13 1998
@@ -2,14 +2,16 @@
  * This file contains miscellaneous low-level functions.
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.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.
+ * Largely rewritten by Cort Dougan (cort@cs.nmt.edu)
+ * and Paul Mackerras.
+ *
+ * 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/sys.h>
 #include <asm/unistd.h>
 #include <asm/errno.h>
@@ -26,7 +28,21 @@
 	addi	r4,r4,0x1000; \
 	bdnz	0b
 	
-_TEXT()
+	.text
+
+/*
+ * Returns (address we're running at) - (address we were linked at)
+ * for use before the text and data are mapped to KERNELBASE.
+ */
+_GLOBAL(reloc_offset)
+	mflr	r0
+	bl	1f
+1:	mflr	r3
+	lis	r4,1b@ha
+	addi	r4,r4,1b@l
+	subf	r3,r4,r3
+	mtlr	r0
+	blr
 
 /*
  * Disable interrupts
@@ -64,24 +80,6 @@
 	mtmsr	r3		/* Update machine state */
 	blr
 
-#if 0
-/*
- * Restore 'flags'
- *	__restore_flags(long val)
- */
-_GLOBAL(__restore_flags)
-	andi.	r0,r3,MSR_EE	/* enabling interrupts? */
-	beq	2f
-	lis	r4,lost_interrupts@ha
-	lwz	r4,lost_interrupts@l(r4)
-	cmpi	0,r4,0
-	bne	do_lost_interrupts
-2:	sync			/* Some chip revs have problems here... */
-	mtmsr	r3
-	isync
-	blr
-#endif
-	
 /*
  * We were about to enable interrupts but we have to simulate
  * some interrupts that were lost by enable_irq first.
@@ -151,6 +149,13 @@
 	stwcx.	r5,0,r4		/* Update with new value */
 	bne-	10b		/* Retry if "reservation" (i.e. lock) lost */
 	blr
+_GLOBAL(atomic_add_return)
+10:	lwarx	r5,0,r4		/* Fetch old value & reserve */
+	add	r5,r5,r3	/* Perform 'add' operation */
+	stwcx.	r5,0,r4		/* Update with new value */
+	bne-	10b		/* Retry if "reservation" (i.e. lock) lost */
+	mr	r3,r5
+	blr
 _GLOBAL(atomic_sub)
 10:	lwarx	r5,0,r4		/* Fetch old value & reserve */
 	sub	r5,r5,r3	/* Perform 'add' operation */
@@ -209,11 +214,29 @@
 /*
  * I/O string operations
  *
+ * insb(port, buf, len)
+ * outsb(port, buf, len)
  * insw(port, buf, len)
  * outsw(port, buf, len)
  * insl(port, buf, len)
  * outsl(port, buf, len)
  */
+_GLOBAL(_insb)
+	mtctr	r5
+	subi	r4,r4,1
+00:	lbz	r5,0(r3)
+	stbu	r5,1(r4)
+	bdnz	00b
+	blr
+
+_GLOBAL(_outsb)
+	mtctr	r5
+	subi	r4,r4,1
+00:	lbzu	r5,1(r4)
+	stb	r5,0(r3)
+	bdnz	00b
+	blr	
+
 _GLOBAL(_insw)
 	mtctr	r5
 	subi	r4,r4,2
@@ -325,6 +348,33 @@
 	stfs	0,0(r4)
 	blr
 
+
+_GLOBAL(lock_dcache)
+	mfspr	r3,PVR		/* nop on 601 */
+	rlwinm	r3,r3,16,16,31
+	cmpwi	0,r3,1
+	beqlr-
+	mfspr	r3,HID0
+	ori	r3,r3,HID0_DLOCK
+	mtspr	HID0,r3
+	sync
+	isync
+	blr
+
+_GLOBAL(unlock_dcache)
+	mfspr	r3,PVR		/* nop on 601 */
+	rlwinm	r3,r3,16,16,31
+	cmpwi	0,r3,1
+	beqlr-
+	mfspr	r3,HID0
+	li	r4,HID0_DLOCK
+	andc	r3,r3,r4
+	mtspr	HID0,r3
+	sync
+	isync
+	blr
+	
+
 /*
  * Create a kernel thread
  *   __kernel_thread(flags, fn, arg)
@@ -387,7 +437,7 @@
 	.long sys_mknod
 	.long sys_chmod		/* 15 */
 	.long sys_chown
-	.long sys_break
+	.long /*sys_break*/ sys_ni_syscall
 	.long sys_stat
 	.long sys_lseek
 	.long sys_getpid	/* 20 */
@@ -401,11 +451,11 @@
 	.long sys_fstat
 	.long sys_pause
 	.long sys_utime		/* 30 */
-	.long sys_stty
-	.long sys_gtty
+	.long /*sys_stty*/ sys_ni_syscall
+	.long /*sys_gtty*/ sys_ni_syscall
 	.long sys_access
 	.long sys_nice
-	.long sys_ftime		/* 35 */
+	.long /*sys_ftime*/ sys_ni_syscall		/* 35 */
 	.long sys_sync
 	.long sys_kill
 	.long sys_rename
@@ -414,7 +464,7 @@
 	.long sys_dup
 	.long sys_pipe
 	.long sys_times
-	.long sys_prof
+	.long /*sys_prof*/ sys_ni_syscall
 	.long sys_brk		/* 45 */
 	.long sys_setgid
 	.long sys_getgid
@@ -422,13 +472,13 @@
 	.long sys_geteuid
 	.long sys_getegid	/* 50 */
 	.long sys_acct
-	.long sys_phys
-	.long sys_lock
+	.long /*sys_phys*/ sys_ni_syscall
+	.long /*sys_lock*/ sys_ni_syscall
 	.long sys_ioctl
 	.long sys_fcntl		/* 55 */
-	.long sys_mpx
+	.long /*sys_mpx*/ sys_ni_syscall
 	.long sys_setpgid
-	.long sys_ulimit
+	.long /*sys_ulimit*/ sys_ni_syscall
 	.long sys_olduname
 	.long sys_umask		/* 60 */
 	.long sys_chroot
@@ -468,7 +518,7 @@
 	.long sys_fchown	/* 95 */
 	.long sys_getpriority
 	.long sys_setpriority
-	.long sys_profil
+	.long /*sys_profil*/ sys_ni_syscall
 	.long sys_statfs
 	.long sys_fstatfs	/* 100 */
 	.long sys_ioperm
@@ -539,7 +589,8 @@
 	.long sys_query_module
 	.long sys_poll
 	.long sys_nfsservctl
+	.long sys_setresgid	
+	.long sys_getresgid	/* 170 */
 	.long sys_prctl
-	.long sys_debug
 	.space (NR_syscalls-171)*4
 
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/mk_defs.c linux/arch/ppc/kernel/mk_defs.c
--- v2.1.78/linux/arch/ppc/kernel/mk_defs.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/mk_defs.c	Mon Jan 12 15:18:13 1998
@@ -32,12 +32,12 @@
 	DEFINE(STATE, offsetof(struct task_struct, state));
 	DEFINE(NEXT_TASK, offsetof(struct task_struct, next_task));
 	DEFINE(COUNTER, offsetof(struct task_struct, counter));
-	DEFINE(BLOCKED, offsetof(struct task_struct, blocked));
-	DEFINE(SIGNAL, offsetof(struct task_struct, signal));
+	DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending));
 	DEFINE(TSS, offsetof(struct task_struct, tss));
-	DEFINE(KSP, offsetof(struct thread_struct, ksp));
-	/*DEFINE(PG_TABLES, offsetof(struct thread_struct, pg_tables));*/
 	DEFINE(MM, offsetof(struct task_struct, mm));
+	DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct));
+	DEFINE(KSP, offsetof(struct thread_struct, ksp));
+	DEFINE(PG_TABLES, offsetof(struct thread_struct, pg_tables));
 	DEFINE(PGD, offsetof(struct mm_struct, pgd));
 	DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall));
 	DEFINE(PT_REGS, offsetof(struct thread_struct, regs));
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/openpic.c linux/arch/ppc/kernel/openpic.c
--- v2.1.78/linux/arch/ppc/kernel/openpic.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/openpic.c	Mon Jan 12 15:18:13 1998
@@ -17,6 +17,7 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/init.h>
 #include <linux/openpic.h>
 #include <asm/ptrace.h>
 #include <asm/signal.h>
@@ -28,21 +29,12 @@
 #undef REGISTER_DEBUG
 
 
-#define VEC_TIMER	0x40	/* and up */
-#define VEC_IPI		0x50	/* and up */
-#define VEC_SOURCE	0x10	/* and up */
-#define VEC_SPURIOUS	99
+volatile struct OpenPIC *OpenPIC = NULL;
+u_int OpenPIC_NumInitSenses __initdata = 0;
+u_char *OpenPIC_InitSenses __initdata = NULL;
 
-
-volatile struct OpenPIC *OpenPIC;
-
-static u_int Version;
 static u_int NumProcessors;
 static u_int NumSources;
-static u_int VendorID;
-static u_int DeviceID;
-static u_int Stepping;
-static u_int TimerFrequency;
 
 
     /*
@@ -181,24 +173,17 @@
      *  Initialize the OpenPIC
      */
 
-void openpic_init(void)
+__initfunc(void openpic_init(void))
 {
     u_int t, i;
+    u_int vendorid, devid, stepping, timerfreq;
     const char *version, *vendor, *device;
 
-    if (!OpenPIC) {
-	printk("No OpenPIC present\n");
-	return;
-    }
+    if (!OpenPIC)
+	panic("No OpenPIC found");
 
     t = openpic_read(&OpenPIC->Global.Feature_Reporting0);
-    Version = t & OPENPIC_FEATURE_VERSION_MASK;
-    NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >>
-		     OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1;
-    NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >>
-		  OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1;
-
-    switch (Version) {
+    switch (t & OPENPIC_FEATURE_VERSION_MASK) {
 	case 1:
 	    version = "1.0";
 	    break;
@@ -206,19 +191,23 @@
 	    version = "1.2";
 	    break;
 	default:
-	    version = "?.?";
+	    version = "?";
 	    break;
     }
+    NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >>
+		     OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1;
+    NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >>
+		  OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1;
     printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version,
 	   NumProcessors, NumSources, OpenPIC);
 
     t = openpic_read(&OpenPIC->Global.Vendor_Identification);
-    VendorID = t & OPENPIC_VENDOR_ID_VENDOR_ID_MASK;
-    DeviceID = (t & OPENPIC_VENDOR_ID_DEVICE_ID_MASK) >>
-	       OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT;
-    Stepping = (t & OPENPIC_VENDOR_ID_STEPPING_MASK) >>
+    vendorid = t & OPENPIC_VENDOR_ID_VENDOR_ID_MASK;
+    devid = (t & OPENPIC_VENDOR_ID_DEVICE_ID_MASK) >>
+	    OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT;
+    stepping = (t & OPENPIC_VENDOR_ID_STEPPING_MASK) >>
 	       OPENPIC_VENDOR_ID_STEPPING_SHIFT;
-    switch (VendorID) {
+    switch (vendorid) {
 	case OPENPIC_VENDOR_ID_APPLE:
 	    vendor = "Apple";
 	    break;
@@ -226,7 +215,7 @@
 	    vendor = "Unknown";
 	    break;
     }
-    switch (DeviceID) {
+    switch (devid) {
 	case OPENPIC_DEVICE_ID_APPLE_HYDRA:
 	    device = "Hydra";
 	    break;
@@ -234,20 +223,20 @@
 	    device = "Unknown";
 	    break;
     }
-    printk("OpenPIC Vendor %d (%s), Device %d (%s), Stepping %d\n", VendorID,
-	   vendor, DeviceID, device, Stepping);
+    printk("OpenPIC Vendor %d (%s), Device %d (%s), Stepping %d\n", vendorid,
+	   vendor, devid, device, stepping);
 
-    TimerFrequency = openpic_read(&OpenPIC->Global.Timer_Frequency);
+    timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency);
     printk("OpenPIC timer frequency is ");
-    if (TimerFrequency)
-	printk("%d Hz\n", TimerFrequency);
+    if (timerfreq)
+	printk("%d Hz\n", timerfreq);
     else
 	printk("not set\n");
 
     /* Initialize timer interrupts */
     for (i = 0; i < OPENPIC_NUM_TIMERS; i++) {
 	/* Disabled, Priority 0 */
-	openpic_inittimer(i, 0, VEC_TIMER+i);
+	openpic_inittimer(i, 0, OPENPIC_VEC_TIMER+i);
 	/* No processor */
 	openpic_maptimer(i, 0);
     }
@@ -255,23 +244,24 @@
     /* Initialize IPI interrupts */
     for (i = 0; i < OPENPIC_NUM_IPI; i++) {
 	/* Disabled, Priority 0 */
-	openpic_initipi(i, 0, VEC_IPI+i);
+	openpic_initipi(i, 0, OPENPIC_VEC_IPI+i);
     }
 
     /* Initialize external interrupts */
     /* SIOint (8259 cascade) is special */
-    openpic_initirq(0, 8, VEC_SOURCE, 1, 1); /* 0,1 gives interrupt storm */
+    openpic_initirq(0, 8, OPENPIC_VEC_SOURCE, 1, 1);
     /* Processor 0 */
     openpic_mapirq(0, 1<<0);
     for (i = 1; i < NumSources; i++) {
 	/* Enabled, Priority 8 */
-	openpic_initirq(i, 8, VEC_SOURCE+i, 0, 1);
+	openpic_initirq(i, 8, OPENPIC_VEC_SOURCE+i, 0,
+			i < OpenPIC_NumInitSenses ? OpenPIC_InitSenses[i] : 1);
 	/* Processor 0 */
 	openpic_mapirq(i, 1<<0);
     }
 
     /* Initialize the spurious interrupt */
-    openpic_set_spurious(VEC_SPURIOUS);
+    openpic_set_spurious(OPENPIC_VEC_SPURIOUS);
 
     if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT,
 		    "OpenPIC cascade", NULL))
@@ -340,9 +330,6 @@
 void openpic_eoi(u_int cpu)
 #endif
 {
-#if 0
-printk("++openpic_eoi:\n");
-#endif
     check_arg_cpu(cpu);
     openpic_write(&OpenPIC->THIS_CPU.EOI, 0);
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c
--- v2.1.78/linux/arch/ppc/kernel/pci.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/pci.c	Mon Jan 12 15:18:13 1998
@@ -1,22 +1,27 @@
 /*
- * $Id: pci.c,v 1.12 1997/08/27 05:05:28 cort Exp $
+ * $Id: pci.c,v 1.18 1997/10/29 03:35:07 cort Exp $
  * Common pmac/prep/chrp pci routines. -- Cort
  */
 
 #include <linux/kernel.h>
 #include <linux/pci.h>
-/*#include <linux/bios32.h>*/
+#include <linux/bios32.h>
 #include <linux/delay.h>
 #include <linux/string.h>
 #include <linux/init.h>
+#include <linux/config.h>
+#include <linux/pci.h>
 
 #include <asm/processor.h>
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
 
-unsigned long io_base;
+#if !defined(CONFIG_MACH_SPECIFIC)
+unsigned long isa_io_base;
+unsigned long isa_mem_base;
 unsigned long pci_dram_offset;
+#endif /* CONFIG_MACH_SPECIFIC */
 
 /*
  * It would be nice if we could create a include/asm/pci.h and have just
@@ -29,77 +34,56 @@
  *   -- Cort
  */
 int (*ptr_pcibios_read_config_byte)(unsigned char bus, unsigned char dev_fn,
-			     unsigned char offset, unsigned char *val);
+				    unsigned char offset, unsigned char *val);
 int (*ptr_pcibios_read_config_word)(unsigned char bus, unsigned char dev_fn,
-			     unsigned char offset, unsigned short *val);
+				    unsigned char offset, unsigned short *val);
 int (*ptr_pcibios_read_config_dword)(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned int *val);
+				     unsigned char offset, unsigned int *val);
 int (*ptr_pcibios_write_config_byte)(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned char val);
+				     unsigned char offset, unsigned char val);
 int (*ptr_pcibios_write_config_word)(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned short val);
+				     unsigned char offset, unsigned short val);
 int (*ptr_pcibios_write_config_dword)(unsigned char bus, unsigned char dev_fn,
-			       unsigned char offset, unsigned int val);
-int (*ptr_pcibios_find_device)(unsigned short vendor, unsigned short dev_id,
-			unsigned short index, unsigned char *bus_ptr,
-			unsigned char *dev_fn_ptr);
-int (*ptr_pcibios_find_class)(unsigned int class_code, unsigned short index,
-		       unsigned char *bus_ptr, unsigned char *dev_fn_ptr);
+				      unsigned char offset, unsigned int val);
 
 extern int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
-			     unsigned char offset, unsigned char *val);
+					 unsigned char offset, unsigned char *val);
 extern int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
-			     unsigned char offset, unsigned short *val);
+					 unsigned char offset, unsigned short *val);
 extern int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned int *val);
+					  unsigned char offset, unsigned int *val);
 extern int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned char val);
+					  unsigned char offset, unsigned char val);
 extern int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned short val);
+					  unsigned char offset, unsigned short val);
 extern int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
-			       unsigned char offset, unsigned int val);
-extern int pmac_pcibios_find_device(unsigned short vendor, unsigned short dev_id,
-			unsigned short index, unsigned char *bus_ptr,
-			unsigned char *dev_fn_ptr);
-extern int pmac_pcibios_find_class(unsigned int class_code, unsigned short index,
-		       unsigned char *bus_ptr, unsigned char *dev_fn_ptr);
+					   unsigned char offset, unsigned int val);
 
 extern int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
-			     unsigned char offset, unsigned char *val);
+					 unsigned char offset, unsigned char *val);
 extern int chrp_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
-			     unsigned char offset, unsigned short *val);
+					 unsigned char offset, unsigned short *val);
 extern int chrp_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned int *val);
+					  unsigned char offset, unsigned int *val);
 extern int chrp_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned char val);
+					  unsigned char offset, unsigned char val);
 extern int chrp_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned short val);
+					  unsigned char offset, unsigned short val);
 extern int chrp_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
-			       unsigned char offset, unsigned int val);
-extern int chrp_pcibios_find_device(unsigned short vendor, unsigned short dev_id,
-			unsigned short index, unsigned char *bus_ptr,
-			unsigned char *dev_fn_ptr);
-extern int chrp_pcibios_find_class(unsigned int class_code, unsigned short index,
-		       unsigned char *bus_ptr, unsigned char *dev_fn_ptr);
+					   unsigned char offset, unsigned int val);
 
 extern int prep_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
-			     unsigned char offset, unsigned char *val);
+					 unsigned char offset, unsigned char *val);
 extern int prep_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
-			     unsigned char offset, unsigned short *val);
+					 unsigned char offset, unsigned short *val);
 extern int prep_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned int *val);
+					  unsigned char offset, unsigned int *val);
 extern int prep_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned char val);
+					  unsigned char offset, unsigned char val);
 extern int prep_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned short val);
+					  unsigned char offset, unsigned short val);
 extern int prep_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
-			       unsigned char offset, unsigned int val);
-extern int prep_pcibios_find_device(unsigned short vendor, unsigned short dev_id,
-			unsigned short index, unsigned char *bus_ptr,
-			unsigned char *dev_fn_ptr);
-extern int prep_pcibios_find_class(unsigned int class_code, unsigned short index,
-		       unsigned char *bus_ptr, unsigned char *dev_fn_ptr);
-
+					   unsigned char offset, unsigned int val);
 
 int pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
 			     unsigned char offset, unsigned char *val)
@@ -131,64 +115,94 @@
 {
 	return ptr_pcibios_write_config_dword(bus,dev_fn,offset,val);
 }
-int pcibios_find_device(unsigned short vendor, unsigned short dev_id,
-			unsigned short index, unsigned char *bus_ptr,
-			unsigned char *dev_fn_ptr)
+
+int pcibios_present(void)
 {
-	return ptr_pcibios_find_device(vendor,dev_id,index,bus_ptr,dev_fn_ptr);
+	return 1;
 }
-int pcibios_find_class(unsigned int class_code, unsigned short index,
-		       unsigned char *bus_ptr, unsigned char *dev_fn_ptr)
-{
-	return ptr_pcibios_find_class(class_code,index,bus_ptr,dev_fn_ptr);
+
+int pcibios_find_device (unsigned short vendor, unsigned short device_id,
+			 unsigned short index, unsigned char *bus,
+			 unsigned char *devfn)
+{
+	unsigned int curr = 0;
+	struct pci_dev *dev;
+	for (dev = pci_devices; dev; dev = dev->next) {
+		if (dev->vendor == vendor && dev->device == device_id) {
+			if (curr == index) {
+				*devfn = dev->devfn;
+				*bus = dev->bus->number;
+				return PCIBIOS_SUCCESSFUL;
+			}
+			++curr;
+		}
+	}
+	return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
-int pcibios_present(void)
+/*
+ * Given the class, find the n'th instance of that device
+ * in the system.
+ */
+int pcibios_find_class (unsigned int class_code, unsigned short index,
+			unsigned char *bus, unsigned char *devfn)
 {
-	return 1;
+	unsigned int curr = 0;
+	struct pci_dev *dev;
+
+	for (dev = pci_devices; dev; dev = dev->next) {
+		if (dev->class == class_code) {
+			if (curr == index) {
+				*devfn = dev->devfn;
+				*bus = dev->bus->number;
+				return PCIBIOS_SUCCESSFUL;
+			}
+			++curr;
+		}
+	}
+	return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
+
 __initfunc(unsigned long
-pcibios_init(unsigned long mem_start,unsigned long mem_end))
+	   pcibios_init(unsigned long mem_start,unsigned long mem_end))
+{
+	return mem_start;
+}
+
+__initfunc(void
+	   setup_pci_ptrs(void))
 {
 	switch (_machine) {
-	    case _MACH_Motorola:
-	    case _MACH_IBM:
+	case _MACH_prep:
 		ptr_pcibios_read_config_byte = prep_pcibios_read_config_byte;
 		ptr_pcibios_read_config_word = prep_pcibios_read_config_word;
 		ptr_pcibios_read_config_dword = prep_pcibios_read_config_dword;
 		ptr_pcibios_write_config_byte = prep_pcibios_write_config_byte;
 		ptr_pcibios_write_config_word = prep_pcibios_write_config_word;
 		ptr_pcibios_write_config_dword = prep_pcibios_write_config_dword;
-		ptr_pcibios_find_device = prep_pcibios_find_device;
-		ptr_pcibios_find_class = prep_pcibios_find_class;
 		break;
-	    case _MACH_Pmac:
+	case _MACH_Pmac:
 		ptr_pcibios_read_config_byte = pmac_pcibios_read_config_byte;
 		ptr_pcibios_read_config_word = pmac_pcibios_read_config_word;
 		ptr_pcibios_read_config_dword = pmac_pcibios_read_config_dword;
 		ptr_pcibios_write_config_byte = pmac_pcibios_write_config_byte;
 		ptr_pcibios_write_config_word = pmac_pcibios_write_config_word;
 		ptr_pcibios_write_config_dword = pmac_pcibios_write_config_dword;
-		ptr_pcibios_find_device = pmac_pcibios_find_device;
-		ptr_pcibios_find_class = pmac_pcibios_find_class;
 		break;
-	    case _MACH_chrp:
+	case _MACH_chrp:
 		ptr_pcibios_read_config_byte = chrp_pcibios_read_config_byte;
 		ptr_pcibios_read_config_word = chrp_pcibios_read_config_word;
 		ptr_pcibios_read_config_dword = chrp_pcibios_read_config_dword;
 		ptr_pcibios_write_config_byte = chrp_pcibios_write_config_byte;
 		ptr_pcibios_write_config_word = chrp_pcibios_write_config_word;
 		ptr_pcibios_write_config_dword = chrp_pcibios_write_config_dword;
-		ptr_pcibios_find_device = chrp_pcibios_find_device;
-		ptr_pcibios_find_class = chrp_pcibios_find_class;
 		break;
 	}
-	return mem_start;
 }
 
 __initfunc(unsigned long
-pcibios_fixup(unsigned long mem_start, unsigned long mem_end))
+	   pcibios_fixup(unsigned long mem_start, unsigned long mem_end))
 {
 	return mem_start;
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/pmac_pci.c linux/arch/ppc/kernel/pmac_pci.c
--- v2.1.78/linux/arch/ppc/kernel/pmac_pci.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/pmac_pci.c	Mon Jan 12 15:18:13 1998
@@ -135,11 +135,10 @@
 		bp = (struct bridge_data *) *mem_ptr;
 		*mem_ptr += sizeof(struct bridge_data);
 		bp->cfg_addr = (volatile unsigned int *)
-			(dev->addrs[0].address + 0x800000);
+			ioremap(dev->addrs[0].address + 0x800000, 0x1000);
 		bp->cfg_data = (volatile unsigned char *)
-			(dev->addrs[0].address + 0xc00000);
-		bp->io_base = (void *) dev->addrs[0].address;
-		ioremap(dev->addrs[0].address, 0x800000);
+			ioremap(dev->addrs[0].address + 0xc00000, 0x1000);
+		bp->io_base = (void *) ioremap(dev->addrs[0].address, 0x10000);
 		bp->bus_number = bus_range[0];
 		bp->max_bus = bus_range[1];
 		bp->next = bridge_list;
@@ -328,96 +327,4 @@
 	udelay(2);
 	out_le32((volatile unsigned int *)bp->cfg_data, val);
 	return PCIBIOS_SUCCESSFUL;
-}
-
-int pmac_pcibios_find_device(unsigned short vendor, unsigned short dev_id,
-			unsigned short index, unsigned char *bus_ptr,
-			unsigned char *dev_fn_ptr)
-{
-	int bus, unit, fn, num, devfn;
-	unsigned int x, vendev;
-	unsigned char h;
-
-	if (vendor == 0xffff)
-		return PCIBIOS_BAD_VENDOR_ID;
-	vendev = (dev_id << 16) + vendor;
-	num = 0;
-	for (bus = 0; bus <= max_bus; ++bus) {
-		if (bridges[bus] == 0)
-			continue;
-		unit = fn = 0;
-		if (bus == bridges[bus]->bus_number)
-			unit = 11;
-		while (unit < 32) {
-			devfn = PCI_DEVFN(unit, fn);
-			if (pcibios_read_config_dword(bus, devfn,
-						      PCI_VENDOR_ID, &x)
-			    == PCIBIOS_SUCCESSFUL && x == vendev) {
-				if (index == num) {
-					*bus_ptr = bus;
-					*dev_fn_ptr = devfn;
-					return PCIBIOS_SUCCESSFUL;
-				}
-				++num;
-			}
-			if (fn != 0) {
-				if (++fn >= 8) {
-					++unit;
-					fn = 0;
-				}
-				continue;
-			}
-			if (pcibios_read_config_byte(bus, devfn,
-						     PCI_HEADER_TYPE, &h)
-			    == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0)
-				++fn;
-			else
-				++unit;
-		}
-	}
-	return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-int pmac_pcibios_find_class(unsigned int class_code, unsigned short index,
-		       unsigned char *bus_ptr, unsigned char *dev_fn_ptr)
-{
-	int bus, unit, fn, num, devfn;
-	unsigned int x;
-	unsigned char h;
-
-	num = 0;
-	for (bus = 0; bus <= max_bus; ++bus) {
-		if (bridges[bus] == 0)
-			continue;
-		unit = fn = 0;
-		if (bus == bridges[bus]->bus_number)
-			unit = 11;
-		while (unit < 32) {
-			devfn = PCI_DEVFN(unit, fn);
-			if (pcibios_read_config_dword(bus, devfn,
-						      PCI_CLASS_REVISION, &x)
-			    == PCIBIOS_SUCCESSFUL && (x >> 8) == class_code) {
-				if (index == num) {
-					*bus_ptr = bus;
-					*dev_fn_ptr = devfn;
-					return PCIBIOS_SUCCESSFUL;
-				}
-				++num;
-			}
-			if (fn != 0) {
-				if (++fn >= 8) {
-					++unit;
-					fn = 0;
-				}
-				continue;
-			}
-			if (pcibios_read_config_byte(bus, devfn,
-						     PCI_HEADER_TYPE, &h)
-			    == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0)
-				++fn;
-			else
-				++unit;
-		}
-	}
-	return PCIBIOS_DEVICE_NOT_FOUND;
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c
--- v2.1.78/linux/arch/ppc/kernel/pmac_setup.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/pmac_setup.c	Mon Jan 12 15:18:13 1998
@@ -37,12 +37,16 @@
 #include <linux/delay.h>
 #include <linux/ioport.h>
 #include <linux/major.h>
+#ifdef CONFIG_ABSTRACT_CONSOLE
+#include <linux/console.h>
+#endif
 #include <asm/prom.h>
 #include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/io.h>
 #include <asm/ide.h>
 #include <asm/pci-bridge.h>
+#include <asm/adb.h>
 #include "time.h"
 
 /*
@@ -55,32 +59,18 @@
 
 extern int root_mountflags;
 
-extern char command_line[];
-extern char saved_command_line[256];
-
 unsigned char drive_info;
 
 #define DEFAULT_ROOT_DEVICE 0x0801	/* sda1 - slightly silly choice */
 
-extern unsigned long find_available_memory(void);
+static void gc_init(const char *, int);
 
-void pmac_setup_arch(char **cmdline_p,
-	unsigned long * memory_start_p, unsigned long * memory_end_p)
+void
+pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)
 {
-	extern unsigned long *end_of_DRAM;
 	struct device_node *cpu;
 	int *fp;
 
-	strcpy(saved_command_line, command_line);
-	*cmdline_p = command_line;
-
-	*memory_start_p = find_available_memory();
-	*memory_end_p = (unsigned long) end_of_DRAM;
-
-	set_prom_callback();
-
-	*memory_start_p = copy_device_tree(*memory_start_p, *memory_end_p);
-
 	/* Set loops_per_sec to a half-way reasonable value,
 	   for use until calibrate_delay gets called. */
 	cpu = find_type_devices("cpu");
@@ -90,6 +80,7 @@
 			switch (_get_PVR() >> 16) {
 			case 4:		/* 604 */
 			case 9:		/* 604e */
+			case 10:	/* mach V (604ev5) */
 			case 20:	/* 620 */
 				loops_per_sec = *fp;
 				break;
@@ -99,10 +90,33 @@
 		} else
 			loops_per_sec = 50000000;
 	}
+
+	*memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p);
+	gc_init("gc", 0);
+	gc_init("ohare", 1);
+
+#ifdef CONFIG_ABSTRACT_CONSOLE
+	/* Frame buffer device based console */
+	conswitchp = &fb_con;
+#endif
+}
+
+static void gc_init(const char *name, int isohare)
+{
+	struct device_node *np;
+
+	for (np = find_devices(name); np != NULL; np = np->next) {
+		if (np->n_addrs > 0)
+			ioremap(np->addrs[0].address, np->addrs[0].size);
+		if (isohare) {
+			printk(KERN_INFO "Twiddling the magic ohare bits\n");
+			out_le32(OMAGICPLACE, OMAGICCONT);
+		}
+	}
 }
 
-char *bootpath;
-char bootdevice[256];
+extern char *bootpath;
+extern char *bootdevice;
 void *boot_host;
 int boot_target;
 int boot_part;
@@ -111,31 +125,15 @@
 unsigned long
 powermac_init(unsigned long mem_start, unsigned long mem_end)
 {
-	struct device_node *chosen_np, *ohare_np;
-
-	mem_start = pmac_find_bridges(mem_start, mem_end);
-	ohare_np = find_devices("ohare");
-	if (ohare_np != NULL) {
-		printk(KERN_INFO "Twiddling the magic ohare bits\n");
-		out_le32(OMAGICPLACE, OMAGICCONT);
-	}
 	pmac_nvram_init();
-	via_cuda_init();
-	pmac_read_rtc_time();
-	pmac_find_display();
-	bootpath = NULL;
-	chosen_np = find_devices("chosen");
-	if (chosen_np != NULL)
-		bootpath = (char *) get_property(chosen_np, "bootpath", NULL);
-	if (bootpath != NULL) {
-		/*
-		 * There's a bug in the prom.  (Why am I not surprised.)
-		 * If you pass a path like scsi/sd@1:0 to canon, it returns
-		 * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0
-		 * That is, the scsi target number doesn't get preserved.
-		 */
-		call_prom("canon", 3, 1, bootpath, bootdevice, sizeof(bootdevice));
+	adb_init();
+	if (_machine == _MACH_Pmac) {
+		pmac_read_rtc_time();
 	}
+#ifdef CONFIG_PMAC_CONSOLE
+	pmac_find_display();
+#endif
+
 	return mem_start;
 }
 
@@ -146,9 +144,17 @@
 	char *p;
 
 	l = strlen(node->full_name);
-	if (strncmp(node->full_name, bootdevice, l) == 0
+	if (bootpath != NULL && bootdevice != NULL
+	    && strncmp(node->full_name, bootdevice, l) == 0
 	    && (bootdevice[l] == '/' || bootdevice[l] == 0)) {
 		boot_host = host;
+		/*
+		 * There's a bug in OF 1.0.5.  (Why am I not surprised.)
+		 * If you pass a path like scsi/sd@1:0 to canon, it returns
+		 * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0
+		 * That is, the scsi target number doesn't get preserved.
+		 * So we pick the target number out of bootpath and use that.
+		 */
 		p = strstr(bootpath, "/sd@");
 		if (p != NULL) {
 			p += 4;
@@ -160,6 +166,28 @@
 	}
 }
 
+#ifdef CONFIG_SCSI
+/* Find the device number for the disk (if any) at target tgt
+   on host adaptor host.
+   XXX this really really should be in drivers/scsi/sd.c. */
+#include <linux/blkdev.h>
+#include "../../../drivers/scsi/scsi.h"
+#include "../../../drivers/scsi/sd.h"
+#include "../../../drivers/scsi/hosts.h"
+
+int sd_find_target(void *host, int tgt)
+{
+    Scsi_Disk *dp;
+    int i;
+
+    for (dp = rscsi_disks, i = 0; i < sd_template.dev_max; ++i, ++dp)
+        if (dp->device != NULL && dp->device->host == host
+            && dp->device->id == tgt)
+            return MKDEV(SCSI_DISK_MAJOR, i << 4);
+    return 0;
+}
+#endif
+
 void find_boot_device(void)
 {
 	int dev;
@@ -230,42 +258,8 @@
 int
 pmac_get_cpuinfo(char *buffer)
 {
-	int pvr = _get_PVR();
-	char *model;
-	struct device_node *cpu;
-	int l, *fp;
-
-	l = 0;
-	cpu = find_type_devices("cpu");
-	if (cpu != 0) {
-		fp = (int *) get_property(cpu, "clock-frequency", NULL);
-		if (fp != 0)
-			l += sprintf(buffer, "%dMHz ", *fp / 1000000);
-	}
-
-	switch (pvr>>16) {
-	case 1:
-		model = "601";
-		break;
-	case 3:
-		model = "603";
-		break;
-	case 4:
-		model = "604";
-		break;
-	case 6:
-		model = "603e";
-		break;
-	case 7:
-		model = "603ev";
-		break;
-	case 9:
-		model = "604e";
-		break;
-	default:
-		model = "unknown";
-		break;
-	}
-	return l + sprintf(buffer+l, "PowerPC %s rev %d.%d\n", model,
-			   (pvr & 0xff00) >> 8, pvr & 0xff);
+	int len;
+	/* should find motherboard type here as well */
+	len = sprintf(buffer,"machine\t\t: PowerMac\n");
+	return len;
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/pmac_support.c linux/arch/ppc/kernel/pmac_support.c
--- v2.1.78/linux/arch/ppc/kernel/pmac_support.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/pmac_support.c	Mon Jan 12 15:18:13 1998
@@ -7,16 +7,18 @@
 #include <linux/nvram.h>
 #include <asm/ptrace.h>
 #include <asm/io.h>
-#include <asm/cuda.h>
 #include <asm/system.h>
 #include <asm/prom.h>
 
 /*
- * Read and write the non-volatile RAM on PowerMacs.
+ * Read and write the non-volatile RAM on PowerMacs and CHRP machines.
  */
 static int nvram_naddrs;
 static volatile unsigned char *nvram_addr;
 static volatile unsigned char *nvram_data;
+static int nvram_mult;
+
+#define NVRAM_SIZE	0x2000	/* 8kB of non-volatile RAM */
 
 void pmac_nvram_init(void)
 {
@@ -29,8 +31,13 @@
 		return;
 	}
 	nvram_naddrs = dp->n_addrs;
-	if (nvram_naddrs == 1) {
+	if (_machine == _MACH_chrp && nvram_naddrs == 1) {
+		/* XXX for now */
+		nvram_data = ioremap(0xf70e0000, NVRAM_SIZE);
+		nvram_mult = 1;
+	} else if (nvram_naddrs == 1) {
 		nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size);
+		nvram_mult = (dp->addrs[0].size + NVRAM_SIZE - 1) / NVRAM_SIZE;
 	} else if (nvram_naddrs == 2) {
 		nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size);
 		nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size);
@@ -44,7 +51,7 @@
 {
 	switch (nvram_naddrs) {
 	case 1:
-		return nvram_data[(addr & 0x1fff) << 4];
+		return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult];
 	case 2:
 		*nvram_addr = addr >> 5;
 		eieio();
@@ -57,7 +64,7 @@
 {
 	switch (nvram_naddrs) {
 	case 1:
-		nvram_data[(addr & 0x1fff) << 4] = val;
+		nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val;
 		break;
 	case 2:
 		*nvram_addr = addr >> 5;
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/pmac_time.c linux/arch/ppc/kernel/pmac_time.c
--- v2.1.78/linux/arch/ppc/kernel/pmac_time.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/pmac_time.c	Mon Jan 12 15:18:13 1998
@@ -2,9 +2,7 @@
  * Support for periodic interrupts (100 per second) and for getting
  * the current time from the RTC on Power Macintoshes.
  *
- * At present, we use the decrementer register in the 601 CPU
- * for our periodic interrupts.  This will probably have to be
- * changed for other processors.
+ * We use the decrementer register for our periodic interrupts.
  *
  * Paul Mackerras	August 1996.
  * Copyright (C) 1996 Paul Mackerras.
@@ -15,17 +13,79 @@
 #include <linux/param.h>
 #include <linux/string.h>
 #include <linux/mm.h>
+#include <asm/adb.h>
 #include <asm/cuda.h>
 #include <asm/prom.h>
 #include <asm/system.h>
 
 #include "time.h"
 
-
 /* Apparently the RTC stores seconds since 1 Jan 1904 */
 #define RTC_OFFSET	2082844800
 
 /*
+ * Calibrate the decrementer frequency with the VIA timer 1.
+ */
+#define VIA_TIMER_FREQ_6	4700000	/* time 1 frequency * 6 */
+
+/* VIA registers */
+#define RS		0x200		/* skip between registers */
+#define T1CL		(4*RS)		/* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH		(5*RS)		/* Timer 1 counter (high 8 bits) */
+#define T1LL		(6*RS)		/* Timer 1 latch (low 8 bits) */
+#define T1LH		(7*RS)		/* Timer 1 latch (high 8 bits) */
+#define ACR		(11*RS)		/* Auxiliary control register */
+#define IFR		(13*RS)		/* Interrupt flag register */
+
+/* Bits in ACR */
+#define T1MODE		0xc0		/* Timer 1 mode */
+#define T1MODE_CONT	0x40		/*  continuous interrupts */
+
+/* Bits in IFR and IER */
+#define T1_INT		0x40		/* Timer 1 interrupt */
+
+static int via_calibrate_decr(void)
+{
+	struct device_node *vias;
+	volatile unsigned char *via;
+	int count = VIA_TIMER_FREQ_6 / HZ;
+	unsigned int dstart, dend;
+
+	vias = find_devices("via-cuda");
+	if (vias == 0)
+		vias = find_devices("via-pmu");
+	if (vias == 0 || vias->n_addrs == 0)
+		return 0;
+	via = (volatile unsigned char *) vias->addrs[0].address;
+
+	/* set timer 1 for continuous interrupts */
+	out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT);
+	/* set the counter to a small value */
+	out_8(&via[T1CH], 2);
+	/* set the latch to `count' */
+	out_8(&via[T1LL], count);
+	out_8(&via[T1LH], count >> 8);
+	/* wait until it hits 0 */
+	while ((in_8(&via[IFR]) & T1_INT) == 0)
+		;
+	dstart = get_dec();
+	/* clear the interrupt & wait until it hits 0 again */
+	in_8(&via[T1CL]);
+	while ((in_8(&via[IFR]) & T1_INT) == 0)
+		;
+	dend = get_dec();
+
+	decrementer_count = (dstart - dend) / 6;
+	count_period_num = 60;
+	count_period_den = decrementer_count * 6 * HZ / 100000;
+
+	printk(KERN_INFO "via_calibrate_decr: decrementer_count = %u (%u ticks)\n",
+	       decrementer_count, dstart - dend);
+
+	return 1;
+}
+
+/*
  * Query the OF and get the decr frequency.
  * This was taken from the pmac time_init() when merging the prep/pmac
  * time functions.
@@ -35,6 +95,9 @@
 	struct device_node *cpu;
 	int freq, *fp, divisor;
 
+	if (via_calibrate_decr())
+		return;
+
 	/*
 	 * The cpu node should have a timebase-frequency property
 	 * to tell us the rate at which the decrementer counts.
@@ -57,11 +120,11 @@
 unsigned long
 pmac_get_rtc_time(void)
 {
-	struct cuda_request req;
+	struct adb_request req;
 
 	/* Get the time from the RTC */
 	cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME);
-	while (!req.got_reply)
+	while (!req.complete)
 		cuda_poll();
 	if (req.reply_len != 7)
 		printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
@@ -84,4 +147,5 @@
 {
 	xtime.tv_sec = pmac_get_rtc_time();
 	xtime.tv_usec = 0;
+	last_rtc_update = xtime.tv_sec;
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/ppc_asm.tmpl linux/arch/ppc/kernel/ppc_asm.tmpl
--- v2.1.78/linux/arch/ppc/kernel/ppc_asm.tmpl	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/ppc_asm.tmpl	Mon Jan 12 15:18:13 1998
@@ -1,27 +1,3 @@
-/*
- * This file contains all the macros and symbols which define
- * a PowerPC assembly language environment.
- */
-#include <linux/config.h>	
-#define _TEXT()\
-	.text
-
-#define _EXTERN(n) n	
-
-#define SYMBOL_NAME(x) x
-
-#define _GLOBAL(n)\
-	.globl n;\
-n:
-
-#ifndef FALSE
-#define FALSE	0
-#define TRUE	1
-#endif
-	
-#define _ORG(n)\
-	.org	n
-
 /* Register names */
 #define	r0	0
 #define	r1	1
@@ -88,102 +64,3 @@
 #define	fr29	29
 #define	fr30	30
 #define	fr31	31
-
-/* Some special registers */
-
-#define	TBRU	269	/* Time base Upper/Lower (Reading) */
-#define	TBRL	268
-#define TBWU	284	/* Time base Upper/Lower (Writing) */
-#define TBWL	285
-#define	XER	1
-#define LR	8
-#define CTR	9
-#define HID0	1008	/* Hardware Implementation */
-#define PVR	287	/* Processor Version */
-#define IBAT0U	528	/* Instruction BAT #0 Upper/Lower */
-#define IBAT0L	529
-#define IBAT1U	530	/* Instruction BAT #1 Upper/Lower */
-#define IBAT1L	531
-#define IBAT2U	532	/* Instruction BAT #2 Upper/Lower */
-#define IBAT2L	533
-#define IBAT3U	534	/* Instruction BAT #3 Upper/Lower */
-#define IBAT3L	535
-#define DBAT0U	536	/* Data BAT #0 Upper/Lower */
-#define DBAT0L	537
-#define DBAT1U	538	/* Data BAT #1 Upper/Lower */
-#define DBAT1L	539
-#define DBAT2U	540	/* Data BAT #2 Upper/Lower */
-#define DBAT2L	541
-#define DBAT3U	542	/* Data BAT #3 Upper/Lower */
-#define DBAT3L	543
-#define DMISS	976	/* TLB Lookup/Refresh registers */
-#define DCMP	977
-#define HASH1	978
-#define HASH2	979
-#define IMISS	980
-#define ICMP	981
-#define RPA	982
-#define SDR1	25	/* MMU hash base register */
-#define DAR	19	/* Data Address Register */
-#define SPR0	272	/* Supervisor Private Registers */
-#define SPRG0   272
-#define SPR1	273
-#define SPRG1   273
-#define SPR2	274
-#define SPRG2   274
-#define SPR3	275
-#define SPRG3   275
-#define DSISR	18
-#define SRR0	26	/* Saved Registers (exception) */
-#define SRR1	27
-#define IABR	1010	/* Instruction Address Breakpoint */
-#define DEC	22	/* Decrementer */
-#define EAR	282	/* External Address Register */
-
-/* Segment Registers */
-#define SR0	0
-#define SR1	1
-#define SR2	2
-#define SR3	3
-#define SR4	4
-#define SR5	5
-#define SR6	6
-#define SR7	7
-#define SR8	8
-#define SR9	9
-#define SR10	10
-#define SR11	11
-#define SR12	12
-#define SR13	13
-#define SR14	14
-#define SR15	15
-
-#define curptr r2
-
-/*
- * Macros for storing registers into and loading registers from
- * exception frames.
- */
-#define SAVE_GPR(n, base)	stw	n,GPR0+4*(n)(base)
-#define SAVE_2GPRS(n, base)	SAVE_GPR(n, base); SAVE_GPR(n+1, base)
-#define SAVE_4GPRS(n, base)	SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base)
-#define SAVE_8GPRS(n, base)	SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base)
-#define SAVE_10GPRS(n, base)	SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base)
-#define REST_GPR(n, base)	lwz	n,GPR0+4*(n)(base)
-#define REST_2GPRS(n, base)	REST_GPR(n, base); REST_GPR(n+1, base)
-#define REST_4GPRS(n, base)	REST_2GPRS(n, base); REST_2GPRS(n+2, base)
-#define REST_8GPRS(n, base)	REST_4GPRS(n, base); REST_4GPRS(n+4, base)
-#define REST_10GPRS(n, base)	REST_8GPRS(n, base); REST_2GPRS(n+8, base)
-
-#define SAVE_FPR(n, base)	stfd	n,TSS_FPR0+8*(n)(base)
-#define SAVE_2FPRS(n, base)	SAVE_FPR(n, base); SAVE_FPR(n+1, base)
-#define SAVE_4FPRS(n, base)	SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base)
-#define SAVE_8FPRS(n, base)	SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base)
-#define SAVE_16FPRS(n, base)	SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base)
-#define SAVE_32FPRS(n, base)	SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base)
-#define REST_FPR(n, base)	lfd	n,TSS_FPR0+8*(n)(base)
-#define REST_2FPRS(n, base)	REST_FPR(n, base); REST_FPR(n+1, base)
-#define REST_4FPRS(n, base)	REST_2FPRS(n, base); REST_2FPRS(n+2, base)
-#define REST_8FPRS(n, base)	REST_4FPRS(n, base); REST_4FPRS(n+4, base)
-#define REST_16FPRS(n, base)	REST_8FPRS(n, base); REST_8FPRS(n+8, base)
-#define REST_32FPRS(n, base)	REST_16FPRS(n, base); REST_16FPRS(n+16, base)
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/ppc_htab.c linux/arch/ppc/kernel/ppc_htab.c
--- v2.1.78/linux/arch/ppc/kernel/ppc_htab.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/ppc_htab.c	Mon Jan 12 15:18:13 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: ppc_htab.c,v 1.7 1997/08/24 19:33:32 cort Exp $
+ * $Id: ppc_htab.c,v 1.16 1997/11/17 18:25:04 cort Exp $
  *
  * PowerPC hash table management proc entry.  Will show information
  * about the current hash table and will allow changes to it.
@@ -25,16 +25,19 @@
 #include <asm/io.h>
 #include <asm/pgtable.h>
 
-static long ppc_htab_read(struct inode * inode, struct file * file,
-			   char * buf, unsigned long nbytes);
-static long ppc_htab_write(struct inode * inode, struct file * file,
-			    const char * buffer, unsigned long count);
-static long long ppc_htab_lseek(struct inode * inode, struct file * file, 
-				 long long offset, int orig);
+static ssize_t ppc_htab_read(struct file * file, char * buf,
+			     size_t count, loff_t *ppos);
+static ssize_t ppc_htab_write(struct file * file, const char * buffer,
+			      size_t count, loff_t *ppos);
+static long long ppc_htab_lseek(struct file * file, loff_t offset, int orig);
 
 extern PTE *Hash, *Hash_end;
 extern unsigned long Hash_size, Hash_mask;
 extern unsigned long _SDR1;
+extern unsigned long htab_reloads;
+extern unsigned long htab_evicts;
+extern unsigned long pte_misses;
+extern unsigned long pte_errors;
 
 static struct file_operations ppc_htab_operations = {
     ppc_htab_lseek,	/* lseek   */
@@ -72,24 +75,107 @@
     NULL	    /* permission  */
 };
 
+/* these will go into processor.h when I'm done debugging -- Cort */
+#define MMCR0 952
+#define MMCR0_PMC1_CYCLES (0x1<<7)
+#define MMCR0_PMC1_ICACHEMISS (0x5<<7)
+#define MMCR0_PMC1_DTLB (0x6<<7)
+#define MMCR0_PMC2_DCACHEMISS (0x6)
+#define MMCR0_PMC2_CYCLES (0x1)
+#define MMCR0_PMC2_ITLB (0x7)
+#define MMCR0_PMC2_LOADMISSTIME (0x5)
+
+#define PMC1 953
+#define PMC2 954
+
+char *pmc1_lookup(unsigned long mmcr0)
+{
+	switch ( mmcr0 & (0x7f<<7) )
+	{
+	case 0x0:
+		return "none";
+	case MMCR0_PMC1_CYCLES:
+		return "cycles";
+	case MMCR0_PMC1_ICACHEMISS:
+		return "ic miss";
+	case MMCR0_PMC1_DTLB:
+		return "dtlb miss";
+	default:
+		return "unknown";
+	}
+}	
+
+char *pmc2_lookup(unsigned long mmcr0)
+{
+	switch ( mmcr0 & 0x3f )
+	{
+	case 0x0:
+		return "none";
+	case MMCR0_PMC2_CYCLES:
+		return "cycles";
+	case MMCR0_PMC2_DCACHEMISS:
+		return "dc miss";
+	case MMCR0_PMC2_ITLB:
+		return "itlb miss";
+	case MMCR0_PMC2_LOADMISSTIME:
+		return "load miss time";
+	default:
+		return "unknown";
+	}
+}	
+
 
 /*
  * print some useful info about the hash table.  This function
  * is _REALLY_ slow (see the nested for loops below) but nothing
  * in here should be really timing critical. -- Cort
  */
-static long ppc_htab_read(struct inode * inode, struct file * file,
-			   char * buf, unsigned long nbytes)
+static ssize_t ppc_htab_read(struct file * file, char * buf,
+			     size_t count, loff_t *ppos)
 {
+	unsigned long mmcr0 = 0, pmc1 = 0, pmc2 = 0;
 	int n = 0, valid;
-	unsigned int kptes = 0, overflow = 0, uptes = 0;
+	unsigned int kptes = 0, overflow = 0, uptes = 0, zombie_ptes = 0;
 	PTE *ptr;
 	struct task_struct *p;
-	char buffer[128];
-	
-	if (nbytes < 0)
+	char buffer[512];
+
+	if (count < 0)
 		return -EINVAL;
 
+	switch ( _get_PVR()>>16 )
+	{
+	case 4:  /* 604 */
+	case 9:  /* 604e */
+	case 10: /* 604ev5 */
+		asm volatile ("mfspr %0,952 \n\t"
+		    "mfspr %1,953 \n\t"
+		    "mfspr %2,954 \n\t"
+		    : "=r" (mmcr0), "=r" (pmc1), "=r" (pmc2) );
+		n += sprintf( buffer + n,
+			      "604 Performance Monitoring\n"
+			      "MMCR0\t\t: %08lx %s%s ",
+			      mmcr0,
+			      ( mmcr0>>28 & 0x2 ) ? "(user mode counted)" : "",
+			      ( mmcr0>>28 & 0x4 ) ? "(kernel mode counted)" : "");
+		n += sprintf( buffer + n,
+			      "\nPMC1\t\t: %08lx (%s)\n"
+			      "PMC2\t\t: %08lx (%s)\n",
+			      pmc1, pmc1_lookup(mmcr0),
+			      pmc2, pmc2_lookup(mmcr0));
+		break;
+	default:
+		break;
+	}
+
+	
+	/* if we don't have a htab */
+	if ( Hash_size == 0 )
+	{
+		n += sprintf( buffer + n, "No Hash Table used\n");
+		goto return_string;
+	}
+	
 	/*
 	 * compute user/kernel pte's table this info can be
 	 * misleading since there can be valid (v bit set) entries
@@ -97,11 +183,12 @@
 	 * due to the way tlb invalidation is handled on the ppc
 	 * -- Cort
 	 */
-	for ( ptr = Hash ; ptr < Hash_end ; ptr += sizeof(PTE))
+	for ( ptr = Hash ; ptr < Hash_end ; ptr++)
 	{
 		if (ptr->v)
 		{
 			/* make sure someone is using this context/vsid */
+			valid = 0;
 			for_each_task(p)
 			{
 				if ( (ptr->vsid >> 4) == p->mm->context )
@@ -111,7 +198,10 @@
 				}
 			}
 			if ( !valid )
+			{
+				zombie_ptes++;
 				continue;
+			}
 			/* user not allowed read or write */
 			if (ptr->pp == PP_RWXX)
 				kptes++;
@@ -122,7 +212,8 @@
 		}
 	}
 	
-	n += sprintf( buffer,
+	n += sprintf( buffer + n,
+		      "PTE Hash Table Information\n"
 		      "Size\t\t: %luKb\n"
 		      "Buckets\t\t: %lu\n"
  		      "Address\t\t: %08lx\n"
@@ -130,6 +221,7 @@
 		      "User ptes\t: %u\n"
 		      "Kernel ptes\t: %u\n"
 		      "Overflows\t: %u\n"
+		      "Zombies\t\t: %u\n"
 		      "Percent full\t: %%%lu\n",
                       (unsigned long)(Hash_size>>10),
 		      (Hash_size/(sizeof(PTE)*8)),
@@ -138,30 +230,233 @@
                       uptes,
 		      kptes,
 		      overflow,
+		      zombie_ptes,
 		      ((kptes+uptes)*100) / (Hash_size/sizeof(PTE))
 		);
 
-	if (file->f_pos >= strlen(buffer))
+	n += sprintf( buffer + n,
+		      "Reloads\t\t: %08lx\n"
+		      "Evicts\t\t: %08lx\n"
+		      "Non-error misses: %08lx\n"
+		      "Error misses\t: %08lx\n",
+		      htab_reloads, htab_evicts, pte_misses, pte_errors);
+	
+return_string:
+	if (*ppos >= strlen(buffer))
 		return 0;
-	if (n > strlen(buffer) - file->f_pos)
-		n = strlen(buffer) - file->f_pos;
-	copy_to_user(buf, buffer + file->f_pos, n);
-	file->f_pos += n;
+	if (n > strlen(buffer) - *ppos)
+		n = strlen(buffer) - *ppos;
+	copy_to_user(buf, buffer + *ppos, n);
+	*ppos += n;
 	return n;
 }
 
 /*
- * Can't _yet_ adjust the hash table size while running. -- Cort
+ * Allow user to define performance counters and resize the hash table
  */
-static long
-ppc_htab_write(struct inode * inode, struct file * file,
-		const char * buffer, unsigned long count)
+static ssize_t ppc_htab_write(struct file * file, const char * buffer,
+			      size_t count, loff_t *ppos)
 {
-	unsigned long size;
-	extern void reset_SDR1(void);
-	
+	unsigned long tmp;
 	if ( current->uid != 0 )
 		return -EACCES;
+	/* don't set the htab size for now */
+	if ( !strncmp( buffer, "size ", 5) )
+		return -EBUSY;
+
+	/* turn off performance monitoring */
+	if ( !strncmp( buffer, "off", 3) )
+	{
+		switch ( _get_PVR()>>16 )
+		{
+		case 4:  /* 604 */
+		case 9:  /* 604e */
+		case 10: /* 604ev5 */
+			asm volatile ("mtspr %0, %3 \n\t"
+			    "mtspr %1, %3 \n\t"
+			    "mtspr %2, %3 \n\t"			    
+			    :: "i" (MMCR0), "i" (PMC1), "i" (PMC2), "r" (0));
+			break;
+		default:
+			break;
+		}
+			
+	}
+
+	if ( !strncmp( buffer, "reset", 5) )
+	{
+		switch ( _get_PVR()>>16 )
+		{
+		case 4:  /* 604 */
+		case 9:  /* 604e */
+		case 10: /* 604ev5 */
+			/* reset PMC1 and PMC2 */
+			asm volatile (
+				"mtspr 953, %0 \n\t"
+				"mtspr 954, %0 \n\t"
+				:: "r" (0));
+			break;
+		default:
+			break;
+		}
+		htab_reloads = 0;
+		htab_evicts = 0;
+		pte_misses = 0;
+		pte_errors = 0;
+	}
+
+	if ( !strncmp( buffer, "user", 4) )
+	{
+		switch ( _get_PVR()>>16 )
+		{
+		case 4:  /* 604 */
+		case 9:  /* 604e */
+		case 10: /* 604ev5 */
+			/* setup mmcr0 and clear the correct pmc */
+			asm("mfspr %0,%1\n\t"  : "=r" (tmp) : "i" (MMCR0));
+			tmp &= ~(0x60000000);
+			tmp |= 0x20000000;
+			asm volatile (
+				"mtspr %1,%0 \n\t"    /* set new mccr0 */
+				"mtspr %3,%4 \n\t"    /* reset the pmc */
+				"mtspr %5,%4 \n\t"    /* reset the pmc2 */
+				:: "r" (tmp), "i" (MMCR0), "i" (0),
+				"i" (PMC1),  "r" (0), "i"(PMC2) );
+			break;
+		default:
+			break;
+		}
+	}
+
+	if ( !strncmp( buffer, "kernel", 6) )
+	{
+		switch ( _get_PVR()>>16 )
+		{
+		case 4:  /* 604 */
+		case 9:  /* 604e */
+		case 10: /* 604ev5 */
+			/* setup mmcr0 and clear the correct pmc */
+			asm("mfspr %0,%1\n\t"  : "=r" (tmp) : "i" (MMCR0));
+			tmp &= ~(0x60000000);
+			tmp |= 0x40000000;
+			asm volatile (
+				"mtspr %1,%0 \n\t"    /* set new mccr0 */
+				"mtspr %3,%4 \n\t"    /* reset the pmc */
+				"mtspr %5,%4 \n\t"    /* reset the pmc2 */
+				:: "r" (tmp), "i" (MMCR0), "i" (0),
+				"i" (PMC1),  "r" (0), "i"(PMC2) );
+			break;
+		default:
+			break;
+		}
+	}
+	
+	/* PMC1 values */
+	if ( !strncmp( buffer, "dtlb", 4) )
+	{
+		switch ( _get_PVR()>>16 )
+		{
+		case 4:  /* 604 */
+		case 9:  /* 604e */
+		case 10: /* 604ev5 */
+			/* setup mmcr0 and clear the correct pmc */
+			asm("mfspr %0,%1\n\t"  : "=r" (tmp) : "i" (MMCR0));
+			tmp &= ~(0x7f<<7);
+			tmp |= MMCR0_PMC1_DTLB;
+			asm volatile (
+				"mtspr %1,%0 \n\t"    /* set new mccr0 */
+				"mtspr %3,%4 \n\t"    /* reset the pmc */
+				:: "r" (tmp), "i" (MMCR0), "i" (MMCR0_PMC1_DTLB),
+				"i" (PMC1),  "r" (0) );
+		}
+	}	
+
+	if ( !strncmp( buffer, "ic miss", 7) )
+	{
+		switch ( _get_PVR()>>16 )
+		{
+		case 4:  /* 604 */
+		case 9:  /* 604e */
+		case 10: /* 604ev5 */
+			/* setup mmcr0 and clear the correct pmc */
+			asm("mfspr %0,%1\n\t"  : "=r" (tmp) : "i" (MMCR0));
+			tmp &= ~(0x7f<<7);
+			tmp |= MMCR0_PMC1_ICACHEMISS;
+			asm volatile (
+				"mtspr %1,%0 \n\t"    /* set new mccr0 */
+				"mtspr %3,%4 \n\t"    /* reset the pmc */
+				:: "r" (tmp), "i" (MMCR0),
+				"i" (MMCR0_PMC1_ICACHEMISS), "i" (PMC1),  "r" (0));
+		}
+	}	
+
+	/* PMC2 values */
+	if ( !strncmp( buffer, "load miss time", 14) )
+	{
+		switch ( _get_PVR()>>16 )
+		{
+		case 4:  /* 604 */
+		case 9:  /* 604e */
+		case 10: /* 604ev5 */
+			/* setup mmcr0 and clear the correct pmc */
+		       asm volatile(
+			       "mfspr %0,%1\n\t"     /* get current mccr0 */
+			       "rlwinm %0,%0,0,0,31-6\n\t"  /* clear bits [26-31] */
+			       "ori   %0,%0,%2 \n\t" /* or in mmcr0 settings */
+			       "mtspr %1,%0 \n\t"    /* set new mccr0 */
+			       "mtspr %3,%4 \n\t"    /* reset the pmc */
+			       : "=r" (tmp)
+			       : "i" (MMCR0), "i" (MMCR0_PMC2_LOADMISSTIME),
+			       "i" (PMC2),  "r" (0) );
+		}
+	}
+	
+	if ( !strncmp( buffer, "itlb", 4) )
+	{
+		switch ( _get_PVR()>>16 )
+		{
+		case 4:  /* 604 */
+		case 9:  /* 604e */
+		case 10: /* 604ev5 */
+			/* setup mmcr0 and clear the correct pmc */
+		       asm volatile(
+			       "mfspr %0,%1\n\t"     /* get current mccr0 */
+			       "rlwinm %0,%0,0,0,31-6\n\t"  /* clear bits [26-31] */
+			       "ori   %0,%0,%2 \n\t" /* or in mmcr0 settings */
+			       "mtspr %1,%0 \n\t"    /* set new mccr0 */
+			       "mtspr %3,%4 \n\t"    /* reset the pmc */
+			       : "=r" (tmp)
+			       : "i" (MMCR0), "i" (MMCR0_PMC2_ITLB),
+			       "i" (PMC2),  "r" (0) );
+		}
+	}
+
+	if ( !strncmp( buffer, "dc miss", 7) )
+	{
+		switch ( _get_PVR()>>16 )
+		{
+		case 4:  /* 604 */
+		case 9:  /* 604e */
+		case 10: /* 604ev5 */
+			/* setup mmcr0 and clear the correct pmc */
+		       asm volatile(
+			       "mfspr %0,%1\n\t"     /* get current mccr0 */
+			       "rlwinm %0,%0,0,0,31-6\n\t"  /* clear bits [26-31] */
+			       "ori   %0,%0,%2 \n\t" /* or in mmcr0 settings */
+			       "mtspr %1,%0 \n\t"    /* set new mccr0 */
+			       "mtspr %3,%4 \n\t"    /* reset the pmc */
+			       : "=r" (tmp)
+			       : "i" (MMCR0), "i" (MMCR0_PMC2_DCACHEMISS),
+			       "i" (PMC2),  "r" (0) );
+		}
+	}	
+	
+
+	return count;
+	
+#if 0 /* resizing htab is a bit difficult right now -- Cort */
+	unsigned long size;
+	extern void reset_SDR1(void);
 	
 	/* only know how to set size right now */
 	if ( strncmp( buffer, "size ", 5) )
@@ -196,14 +491,13 @@
 	flush_tlb_all();
 
 	reset_SDR1();
-	printk("done\n");
+#endif	
 	return count;
 }
 
 
 static long long
-ppc_htab_lseek(struct inode * inode, struct file * file, 
-				 long long offset, int orig)
+ppc_htab_lseek(struct file * file, loff_t offset, int orig)
 {
     switch (orig) {
     case 0:
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/ppc_ksyms.c linux/arch/ppc/kernel/ppc_ksyms.c
--- v2.1.78/linux/arch/ppc/kernel/ppc_ksyms.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/ppc_ksyms.c	Mon Jan 12 15:18:13 1998
@@ -5,6 +5,7 @@
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/bios32.h>
+#include <linux/interrupt.h>
 
 #include <asm/semaphore.h>
 #include <asm/processor.h>
@@ -14,23 +15,24 @@
 #include <asm/bitops.h>
 #include <asm/checksum.h>
 #include <asm/pgtable.h>
+#include <asm/adb.h>
 #include <asm/cuda.h>
 #include <asm/prom.h>
 #include <asm/system.h>
 #include <asm/pci-bridge.h>
 
-void transfer_to_handler();
-void int_return();
-void syscall_trace();
-void do_IRQ();
-void MachineCheckException();
-void AlignmentException();
-void ProgramCheckException();
-void SingleStepException();
-void FloatingPointCheckException();
-void sys_sigreturn();
+extern void transfer_to_handler(void);
+extern void int_return(void);
+extern void syscall_trace(void);
+extern void do_IRQ(struct pt_regs *regs);
+extern void MachineCheckException(struct pt_regs *regs);
+extern void AlignmentException(struct pt_regs *regs);
+extern void ProgramCheckException(struct pt_regs *regs);
+extern void SingleStepException(struct pt_regs *regs);
+extern int sys_sigreturn(struct pt_regs *regs);
 extern unsigned lost_interrupts;
 extern void do_lost_interrupts(unsigned long);
+extern int do_signal(sigset_t *, struct pt_regs *);
 
 EXPORT_SYMBOL(do_signal);
 EXPORT_SYMBOL(syscall_trace);
@@ -45,6 +47,12 @@
 EXPORT_SYMBOL(sys_sigreturn);
 EXPORT_SYMBOL(lost_interrupts);
 EXPORT_SYMBOL(do_lost_interrupts);
+EXPORT_SYMBOL(__ppc_bh_counter);
+
+#if !defined(CONFIG_MACH_SPECIFIC)
+EXPORT_SYMBOL(isa_io_base);
+EXPORT_SYMBOL(pci_dram_offset);
+#endif
 
 EXPORT_SYMBOL(atomic_add);
 EXPORT_SYMBOL(atomic_sub);
@@ -126,11 +134,12 @@
 EXPORT_SYMBOL(flush_icache_range);
 EXPORT_SYMBOL(xchg_u32);
 
+EXPORT_SYMBOL(adb_request);
+EXPORT_SYMBOL(adb_autopoll);
+EXPORT_SYMBOL(adb_register);
 EXPORT_SYMBOL(cuda_request);
 EXPORT_SYMBOL(cuda_send_request);
-EXPORT_SYMBOL(adb_register);
 EXPORT_SYMBOL(abort);
-EXPORT_SYMBOL(call_prom);
 EXPORT_SYMBOL(find_devices);
 EXPORT_SYMBOL(find_type_devices);
 EXPORT_SYMBOL(find_path_device);
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/prep_pci.c linux/arch/ppc/kernel/prep_pci.c
--- v2.1.78/linux/arch/ppc/kernel/prep_pci.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/prep_pci.c	Mon Jan 12 15:18:13 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: prep_pci.c,v 1.7 1997/08/23 22:46:02 cort Exp $
+ * $Id: prep_pci.c,v 1.12 1997/10/29 03:35:08 cort Exp $
  * PReP pci functions.
  * Originally by Gary Thomas
  * rewritten and updated by Cort Dougan (cort@cs.nmt.edu)
@@ -31,6 +31,83 @@
 
 /* Tables for known hardware */   
 
+/* Motorola PowerStackII - Utah */
+static char Utah_pci_IRQ_map[23] =
+{
+        0,   /* Slot 0  - unused */
+        0,   /* Slot 1  - unused */
+        4,   /* Slot 2  - SCSI - NCR825A  */
+        0,   /* Slot 3  - unused */
+        1,   /* Slot 4  - Ethernet - DEC2114x */
+        0,   /* Slot 5  - unused */
+        2,   /* Slot 6  - PCI Card slot #1 */
+        3,   /* Slot 7  - PCI Card slot #2 */
+        4,   /* Slot 8  - PCI Card slot #3 */
+        4,   /* Slot 9  - PCI Bridge */
+             /* added here in case we ever support PCI bridges */
+             /* Secondary PCI bus cards are at slot-9,6 & slot-9,7 */
+        0,   /* Slot 10 - unused */
+        0,   /* Slot 11 - unused */
+        4,   /* Slot 12 - SCSI - NCR825A */
+        0,   /* Slot 13 - unused */
+        2,   /* Slot 14 - enet */
+        0,   /* Slot 15 - unused */
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+};
+
+static char Utah_pci_IRQ_routes[] =
+{
+        0,   /* Line 0 - Unused */
+        9,   /* Line 1 */
+        11,  /* Line 2 */
+        14,  /* Line 3 */
+        15,  /* Line 4 */
+};
+
+/* Motorola PowerStackII - Omaha */
+/* no integrated SCSI or ethernet */
+static char Omaha_pci_IRQ_map[23] =
+{
+        0,   /* Slot 0  - unused */
+        0,   /* Slot 1  - unused */
+        3,   /* Slot 2  - Winbond EIDE */
+        0,   /* Slot 3  - unused */
+        0,   /* Slot 4  - unused */
+        0,   /* Slot 5  - unused */
+        1,   /* Slot 6  - PCI slot 1 */
+        2,   /* Slot 7  - PCI slot 2  */
+        3,   /* Slot 8  - PCI slot 3 */
+        4,   /* Slot 9  - PCI slot 4 */ /* needs indirect access */
+        0,   /* Slot 10 - unused */
+        0,   /* Slot 11 - unused */
+        0,   /* Slot 12 - unused */
+        0,   /* Slot 13 - unused */
+        0,   /* Slot 14 - unused */
+        0,   /* Slot 15 - unused */
+        1,   /* Slot 16  - PCI slot 1 */
+        2,   /* Slot 17  - PCI slot 2  */
+        3,   /* Slot 18  - PCI slot 3 */
+        4,   /* Slot 19  - PCI slot 4 */ /* needs indirect access */
+        0,
+        0,
+        0,
+};
+
+static char Omaha_pci_IRQ_routes[] =
+{
+        0,   /* Line 0 - Unused */
+        9,   /* Line 1 */
+        11,  /* Line 2 */
+        14,  /* Line 3 */
+        15   /* Line 4 */
+};
+
 /* Motorola PowerStack */
 static char Blackhawk_pci_IRQ_map[16] =
 {
@@ -323,59 +400,13 @@
 	return PCIBIOS_SUCCESSFUL;
 }
 
-int prep_pcibios_find_device (unsigned short vendor, unsigned short device_id,
-			   unsigned short index, unsigned char *bus,
-			   unsigned char *devfn)
-{
-    unsigned int curr = 0;
-    struct pci_dev *dev;
-/*printk("pcibios_find_device(): vendor %04x devid %04x index %d\n",    
-       vendor,device_id,index);*/
-    for (dev = pci_devices; dev; dev = dev->next) {
-/*printk("   dev->vendor %04x dev->device %04x\n",
-       dev->vendor,dev->device);*/
-	if (dev->vendor == vendor && dev->device == device_id) {
-	    if (curr == index) {
-		*devfn = dev->devfn;
-		*bus = dev->bus->number;
-		return PCIBIOS_SUCCESSFUL;
-	    }
-	    ++curr;
-	}
-    }
-    return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-/*
- * Given the class, find the n'th instance of that device
- * in the system.
- */
-int prep_pcibios_find_class (unsigned int class_code, unsigned short index,
-			  unsigned char *bus, unsigned char *devfn)
-{
-    unsigned int curr = 0;
-    struct pci_dev *dev;
-
-    for (dev = pci_devices; dev; dev = dev->next) {
-	if (dev->class == class_code) {
-	    if (curr == index) {
-		*devfn = dev->devfn;
-		*bus = dev->bus->number;
-		return PCIBIOS_SUCCESSFUL;
-	    }
-	    ++curr;
-	}
-    }
-    return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
 __initfunc(unsigned long route_pci_interrupts(void))
 {
 	unsigned char *ibc_pirq = (unsigned char *)0x80800860;
 	unsigned char *ibc_pcicon = (unsigned char *)0x80800840;
 	int i;
 	
-	if ( _machine == _MACH_Motorola)
+	if ( _prep_type == _PREP_Motorola)
 	{ 
 		switch (inb(0x800) & 0xF0)
 		{
@@ -385,18 +416,28 @@
 			Motherboard_routes = Genesis_pci_IRQ_routes;
 			break;
 		case 0x20: /* Series E */
-			Motherboard_map_name = "Series E";
+			Motherboard_map_name = "Powerstack (Series E)";
 			Motherboard_map = Comet_pci_IRQ_map;
 			Motherboard_routes = Comet_pci_IRQ_routes;
 			break;
+		case 0x50: /* PowerStackII Pro3000 */
+			Motherboard_map_name = "Omaha (PowerStack II Pro3000)";
+			Motherboard_map = Omaha_pci_IRQ_map;
+			Motherboard_routes = Omaha_pci_IRQ_routes;
+		case 0x60: /* PowerStackII Pro4000 */
+			Motherboard_map_name = "Utah (Powerstack II Pro4000)";
+			Motherboard_map = Utah_pci_IRQ_map;
+			Motherboard_routes = Utah_pci_IRQ_routes;
+			break;
 		case 0x40: /* PowerStack */
 		default: /* Can't hurt, can it? */
+		  
 			Motherboard_map_name = "Blackhawk (Powerstack)";
 			Motherboard_map = Blackhawk_pci_IRQ_map;
 			Motherboard_routes = Blackhawk_pci_IRQ_routes;
 			break;
 		}
-	} else if ( _machine == _MACH_IBM )
+	} else if ( _prep_type == _PREP_IBM )
 	{
 		unsigned char pl_id;
 		
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/prep_setup.c linux/arch/ppc/kernel/prep_setup.c
--- v2.1.78/linux/arch/ppc/kernel/prep_setup.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/prep_setup.c	Mon Jan 12 15:18:13 1998
@@ -28,6 +28,9 @@
 #include <linux/init.h>
 #include <linux/blk.h>
 #include <linux/ioport.h>
+#ifdef CONFIG_ABSTRACT_CONSOLE
+#include <linux/console.h>
+#endif
 
 #include <asm/mmu.h>
 #include <asm/processor.h>
@@ -36,15 +39,21 @@
 #include <asm/pgtable.h>
 #include <asm/ide.h>
 
+#ifdef CONFIG_SOUND
+#include <../drivers/sound/sound_config.h>
+#include <../drivers/sound/dev_table.h>
+#endif
+
 /* for the mac fs */
 kdev_t boot_dev;
+/* used in nasty hack for sound - see prep_setup_arch() -- Cort */
+long ppc_cs4232_dma, ppc_cs4232_dma2;
+unsigned long empty_zero_page[1024];
 
 extern PTE *Hash, *Hash_end;
 extern unsigned long Hash_size, Hash_mask;
 extern int probingmem;
 extern unsigned long loops_per_sec;
-
-unsigned long empty_zero_page[1024];
 extern unsigned char aux_device_present;
 
 #ifdef CONFIG_BLK_DEV_RAM
@@ -53,9 +62,6 @@
 extern int rd_image_start;	/* starting block # of image */
 #endif
 
-
-extern char saved_command_line[256];
-
 void prep_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)
 {
 	ide_ioreg_t port = base;
@@ -75,68 +81,25 @@
 	extern char *Motherboard_map_name;
 	extern RESIDUAL res;
 	int i;
-	int pvr = _get_PVR();
 	int len;
-	char *model;
-  
-	switch (pvr>>16)
-	{
-	case 1:
-		model = "601";
-		break;
-	case 3:
-		model = "603";
-		break;
-	case 4:
-		model = "604";
-		break;
-	case 6:
-		model = "603e";
-		break;
-	case 7:
-		model = "603ev";
-		break;
-	default:
-		model = "unknown";
-		break;
-	}
   
 #ifdef __SMP__
 #define CD(X)		(cpu_data[n].X)  
 #else
 #define CD(X) (X)
-#define CPUN 0
 #endif
   
-	len = sprintf(buffer,"processor\t: %d\n"
-		      "cpu\t\t: %s\n"
-		      "revision\t: %d.%d\n"
-		      "upgrade\t\t: %s\n"
-		      "clock\t\t: %dMHz\n"
-		      "bus clock\t: %dMHz\n"
-		      "machine\t\t: %s (sn %s)\n"
-		      "pci map\t\t: %s\n",
-		      CPUN,
-		      model,
-		      MAJOR(pvr), MINOR(pvr),
-		      (inb(IBM_EQUIP_PRESENT) & 2) ? "not upgrade" : "upgrade",
-		      (res.VitalProductData.ProcessorHz > 1024) ?
-		      res.VitalProductData.ProcessorHz>>20 :
-		      res.VitalProductData.ProcessorHz,
-		      (res.VitalProductData.ProcessorBusHz > 1024) ?
-		      res.VitalProductData.ProcessorBusHz>>20 :
-		      res.VitalProductData.ProcessorBusHz,
-		      res.VitalProductData.PrintableModel,
-		      res.VitalProductData.Serial,
-		      Motherboard_map_name
-		);
-  
+	len = sprintf(buffer,"machine\t\t: PReP %s\n",Motherboard_map_name);
+	
+	if ( res.ResidualLength == 0 )
+		return len;
+
 	/* print info about SIMMs */
 	len += sprintf(buffer+len,"simms\t\t: ");
 	for ( i = 0 ; (res.ActualNumMemories) && (i < MAX_MEMS) ; i++ )
 	{
 		if ( res.Memories[i].SIMMSize != 0 )
-			len += sprintf(buffer+len,"%d:%dM ",i,
+			len += sprintf(buffer+len,"%d:%ldM ",i,
 				       (res.Memories[i].SIMMSize > 1024) ?
 				       res.Memories[i].SIMMSize>>20 :
 				       res.Memories[i].SIMMSize);
@@ -148,11 +111,11 @@
 	switch(res.VitalProductData.TLBAttrib)
 	{
 	case CombinedTLB:
-		len += sprintf(buffer+len," %d entries\n",
+		len += sprintf(buffer+len," %ld entries\n",
 			       res.VitalProductData.TLBSize);
 		break;
 	case SplitTLB:
-		len += sprintf(buffer+len," (split I/D) %d/%d entries\n",
+		len += sprintf(buffer+len," (split I/D) %ld/%ld entries\n",
 			       res.VitalProductData.I_TLBSize,
 			       res.VitalProductData.D_TLBSize);
 		break;
@@ -166,12 +129,12 @@
 	switch(res.VitalProductData.CacheAttrib)
 	{
 	case CombinedCAC:
-		len += sprintf(buffer+len,"%dkB LineSize\n",
+		len += sprintf(buffer+len,"%ldkB LineSize %ldB\n",
 			       res.VitalProductData.CacheSize,
 			       res.VitalProductData.CacheLineSize);
 		break;
 	case SplitCAC:
-		len += sprintf(buffer+len,"(split I/D) %dkB/%dkB Linesize %dB/%dB\n",
+		len += sprintf(buffer+len,"(split I/D) %ldkB/%ldkB Linesize %ldB/%ldB\n",
 			       res.VitalProductData.I_CacheSize,
 			       res.VitalProductData.D_CacheSize,
 			       res.VitalProductData.D_CacheLineSize,
@@ -194,55 +157,18 @@
 		len += sprintf(buffer+len,"l2\t\t: not present\n");
 	}
 
-
-	len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n",
-		       CD(loops_per_sec+2500)/500000,
-		       (CD(loops_per_sec+2500)/5000) % 100);
-  
-	/*
-	 * Ooh's and aah's info about zero'd pages in idle task
-	 */ 
-	{
-		extern unsigned int zerocount, zerototal, zeropage_hits,zeropage_calls;
-		len += sprintf(buffer+len,"zero pages\t: total %u (%uKb) "
-			       "current: %u (%uKb) hits: %u/%u (%lu%%)\n",
-			       zerototal, (zerototal*PAGE_SIZE)>>10,
-			       zerocount, (zerocount*PAGE_SIZE)>>10,
-			       zeropage_hits,zeropage_calls,
-			       /* : 1 below is so we don't div by zero */
-			       (zeropage_hits*100) /
-			            ((zeropage_calls)?zeropage_calls:1));
-	}
 	return len;
 }
 
 __initfunc(void
-prep_setup_arch(char **cmdline_p, unsigned long * memory_start_p,
-	   unsigned long * memory_end_p))
+prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
 	extern char cmd_line[];
-	extern char _etext[], _edata[], _end[];
-	extern int panic_timeout;
 	unsigned char reg;
 
-	/* Save unparsed command line copy for /proc/cmdline */
-	strcpy( saved_command_line, cmd_line );
-	*cmdline_p = cmd_line;
-  
-	*memory_start_p = (unsigned long) Hash+Hash_size;
-	(unsigned long *)*memory_end_p = (unsigned long *)(res.TotalMemory+KERNELBASE);
-
 	/* init to some ~sane value until calibrate_delay() runs */
 	loops_per_sec = 50000000;
 	
-	/* reboot on panic */	
-	panic_timeout = 180;
-	
-	init_task.mm->start_code = PAGE_OFFSET;
-	init_task.mm->end_code = (unsigned long) _etext;
-	init_task.mm->end_data = (unsigned long) _edata;
-	init_task.mm->brk = (unsigned long) _end;	
-	
 	aux_device_present = 0xaa;
 	/* Set up floppy in PS/2 mode */
 	outb(0x09, SIO_CONFIG_RA);
@@ -250,24 +176,19 @@
 	reg = (reg & 0x3F) | 0x40;
 	outb(reg, SIO_CONFIG_RD);
 	outb(reg, SIO_CONFIG_RD);	/* Have to write twice to change! */
-	
-	switch ( _machine )
+
+	/* we should determine this according to what we find! -- Cort */
+	switch ( _prep_type )
 	{
-	case _MACH_IBM:
+	case _PREP_IBM:
 		ROOT_DEV = to_kdev_t(0x0301); /* hda1 */
 		break;
-	case _MACH_Motorola:
+	case _PREP_Motorola:
 		ROOT_DEV = to_kdev_t(0x0801); /* sda1 */
 		break;
 	}
 	
 #ifdef CONFIG_BLK_DEV_RAM
-#if 0
-	ROOT_DEV = to_kdev_t(0x0200); /* floppy */  
-	rd_prompt = 1;
-	rd_doload = 1;
-	rd_image_start = 0;
-#endif
 	/* initrd_start and size are setup by boot/head.S and kernel/head.S */
 	if ( initrd_start )
 	{
@@ -282,13 +203,62 @@
 #endif
 
 	printk("Boot arguments: %s\n", cmd_line);
-
-	print_residual_device_info();
 	
-	request_region(0x20,0x20,"pic1");
+#ifdef CONFIG_CS4232
+	/*
+	 * setup proper values for the cs4232 driver so we don't have
+	 * to recompile for the motorola or ibm workstations sound systems.
+	 * This is a really nasty hack, but unless we change the driver
+	 * it's the only way to support both addrs from one binary.
+	 * -- Cort
+	 */
+	if ( is_prep )
+	{
+		extern struct card_info snd_installed_cards[];
+		struct card_info  *snd_ptr;
+
+		for ( snd_ptr = snd_installed_cards; 
+		      snd_ptr < &snd_installed_cards[num_sound_cards];
+		      snd_ptr++ )
+		{
+			if ( snd_ptr->card_type == SNDCARD_CS4232 )
+			{
+				if ( _prep_type == _PREP_Motorola )
+				{
+					snd_ptr->config.io_base = 0x830;
+					snd_ptr->config.irq = 10;
+					snd_ptr->config.dma = ppc_cs4232_dma = 6;
+					snd_ptr->config.dma2 = ppc_cs4232_dma2 = 7;
+				}
+				if ( _prep_type == _PREP_IBM )
+				{
+					snd_ptr->config.io_base = 0x530;
+					snd_ptr->config.irq =  5;
+					snd_ptr->config.dma = ppc_cs4232_dma = 1;
+					/* this is wrong - but leave it for now */
+					snd_ptr->config.dma2 = ppc_cs4232_dma2 = 7;
+				}
+			}
+		}
+	}
+#endif /* CONFIG_CS4232 */	
+	
+
+	/*print_residual_device_info();*/
+        request_region(0x20,0x20,"pic1");
 	request_region(0xa0,0x20,"pic2");
 	request_region(0x00,0x20,"dma1");
 	request_region(0x40,0x20,"timer");
 	request_region(0x80,0x10,"dma page reg");
 	request_region(0xc0,0x20,"dma2");
+
+#ifdef CONFIG_ABSTRACT_CONSOLE
+#ifdef CONFIG_VGA_CONSOLE
+        conswitchp = &vga_con;
+#endif
+#ifdef CONFIG_FB
+	/* Frame buffer device based console */
+	conswitchp = &fb_con;
+#endif
+#endif
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/prep_time.c linux/arch/ppc/kernel/prep_time.c
--- v2.1.78/linux/arch/ppc/kernel/prep_time.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/prep_time.c	Mon Jan 12 15:18:13 1998
@@ -56,9 +56,9 @@
 
 int prep_cmos_clock_read(int addr)
 {
-	if ( _machine == _MACH_IBM )
+	if ( _prep_type == _PREP_IBM )
 		return CMOS_READ(addr);
-	else if ( _machine == _MACH_Motorola )
+	else if ( _prep_type == _PREP_Motorola )
 	{
 		outb(clock_transl[addr]>>8, NVRAM_AS1);
 		outb(clock_transl[addr], NVRAM_AS0);
@@ -71,12 +71,12 @@
 
 void prep_cmos_clock_write(unsigned long val, int addr)
 {
-	if ( _machine == _MACH_IBM )
+	if ( _prep_type == _PREP_IBM )
 	{
 		CMOS_WRITE(val,addr);
 		return;
 	}
-	else if ( _machine == _MACH_Motorola )
+	else if ( _prep_type == _PREP_Motorola )
 	{
 		outb(clock_transl[addr]>>8, NVRAM_AS1);
 		outb(clock_transl[addr], NVRAM_AS0);
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c
--- v2.1.78/linux/arch/ppc/kernel/process.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/process.c	Mon Jan 12 15:18:13 1998
@@ -1,15 +1,16 @@
+
 /*
  *  linux/arch/ppc/kernel/process.c
  *
- *  PowerPC version 
- *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
- *
  *  Derived from "arch/i386/kernel/process.c"
  *    Copyright (C) 1995  Linus Torvalds
  *
  *  Updated and modified by Cort Dougan (cort@cs.nmt.edu) and
  *  Paul Mackerras (paulus@cs.anu.edu.au)
  *
+ *  PowerPC version 
+ *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.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
@@ -38,15 +39,17 @@
 #include <asm/io.h>
 #include <asm/smp_lock.h>
 #include <asm/processor.h>
+#include <asm/mmu.h>
+#include <asm/prom.h>
 
 int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs);
 void switch_to(struct task_struct *, struct task_struct *);
-extern unsigned long _get_SP(void);
 
+extern unsigned long _get_SP(void);
+extern spinlock_t scheduler_lock;
 
 #undef SHOW_TASK_SWITCHES 1
 #undef CHECK_STACK 1
-#undef IDLE_ZERO 1
 
 unsigned long
 kernel_stack_top(struct task_struct *tsk)
@@ -68,6 +71,9 @@
 struct mm_struct init_mm = INIT_MM;
 union task_union init_task_union = { INIT_TASK };
 
+/* only used to get secondary processor up */
+struct task_struct *current_set[NR_CPUS] = {&init_task, };
+
 int
 dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs)
 {
@@ -145,17 +151,30 @@
 {
 	struct thread_struct *new_tss, *old_tss;
 	int s = _disable_interrupts();
-
 #if CHECK_STACK
 	check_stack(prev);
 	check_stack(new);
 #endif
 
 #ifdef SHOW_TASK_SWITCHES
-	printk("%s/%d (%x) -> %s/%d (%x) ctx %x\n",
-	       prev->comm,prev->pid,prev->tss.regs->nip,
-	       new->comm,new->pid,new->tss.regs->nip,new->mm->context);
+	printk("%s/%d -> %s/%d cpu %d\n",
+	       prev->comm,prev->pid,
+	       new->comm,new->pid,new->processor);
 #endif
+#ifdef __SMP__
+	/* bad news if last_task_used_math changes processors right now -- Cort */
+	if ( (last_task_used_math == new) &&
+	     (new->processor != new->last_processor) )
+		panic("last_task_used_math switched processors");
+	/* be noisy about processor changes for debugging -- Cort */
+	if ( new->last_processor != new->processor )
+		printk("switch_to(): changing cpu's %d -> %d %s/%d\n",
+		       new->last_processor,new->processor,
+		       new->comm,new->pid);
+	
+	prev->last_processor = prev->processor;
+	current_set[smp_processor_id()] = new;
+#endif /* __SMP__ */
 	new_tss = &new->tss;
 	old_tss = &current->tss;
 	_switch(old_tss, new_tss, new->mm->context);
@@ -181,7 +200,13 @@
 	printk("TASK = %p[%d] '%s' mm->pgd %p ",
 	       current, current->pid, current->comm, current->mm->pgd);
 	printk("Last syscall: %ld ", current->tss.last_syscall);
-	printk("\nlast math %p\n", last_task_used_math);
+	printk("\nlast math %p", last_task_used_math);
+	
+#ifdef __SMP__	
+	printk(" CPU: %d last CPU: %d", current->processor,current->last_processor);
+#endif /* __SMP__ */
+	
+	printk("\n");
 	for (i = 0;  i < 32;  i++)
 	{
 		long r;
@@ -226,7 +251,7 @@
 	    struct task_struct * p, struct pt_regs * regs)
 {
 	struct pt_regs * childregs;
-
+	
 	/* Copy registers */
 	childregs = ((struct pt_regs *)
 		     ((unsigned long)p + sizeof(union task_union)
@@ -245,7 +270,6 @@
 		/* Provided stack is in user space */
 		childregs->gpr[1] = usp;
 	}
-
 	p->tss.last_syscall = -1;
 
 	/*
@@ -321,6 +345,10 @@
 
 	lock_kernel();
 	ret = do_fork(SIGCHLD, regs->gpr[1], regs);
+#if 0/*def __SMP__*/
+	if ( ret ) /* drop scheduler lock in child */
+		scheduler_lock.lock = 0L;
+#endif /* __SMP__ */	
 	unlock_kernel();
 	return ret;
 }
@@ -332,6 +360,7 @@
 	int error;
 	char * filename;
 	
+	lock_kernel();
 	filename = getname((char *) a0);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
@@ -353,6 +382,16 @@
 
 	lock_kernel();
 	res = do_fork(clone_flags, regs->gpr[1], regs);
+#ifdef __SMP__
+	/* When we clone the idle task we keep the same pid but
+	 * the return value of 0 for both causes problems.
+	 * -- Cort
+	 */
+	if ((current->pid == 0) && (current == &init_task))
+		res = 1;
+	if ( 0 /*res*/ ) /* drop scheduler lock in child */
+		scheduler_lock.lock = 0L;
+#endif /* __SMP__ */	
 	unlock_kernel();
 	return res;
 }
@@ -377,7 +416,6 @@
 	printk("\n");
 }
 
-#if 0
 /*
  * Low level print for debugging - Cort
  */
@@ -394,15 +432,37 @@
         return i;
 }
 
-char *vidmem = (char *)0xC00B8000;
 int lines = 24, cols = 80;
 int orig_x = 0, orig_y = 0;
 
 void ll_puts(const char *s)
 {
 	int x,y;
+	char *vidmem = (char *)(_ISA_MEM_BASE + 0xB8000) /*0xC00B8000*/;
 	char c;
+	extern int mem_init_done;
+
+	if ( mem_init_done ) /* assume this means we can printk */
+	{
+		printk(s);
+		return;
+	}
+
+#if 0	
+	if ( have_of )
+	{
+		prom_print(s);
+		return;
+	}
+#endif
 
+	/*
+	 * can't ll_puts on chrp without openfirmware yet.
+	 * vidmem just needs to be setup for it.
+	 * -- Cort
+	 */
+	if ( ! is_prep )
+		return;
 	x = orig_x;
 	y = orig_y;
 
@@ -430,4 +490,3 @@
 	orig_x = x;
 	orig_y = y;
 }
-#endif /* CONFIG_PREP */
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c
--- v2.1.78/linux/arch/ppc/kernel/prom.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/prom.c	Mon Jan 12 15:18:13 1998
@@ -8,33 +8,28 @@
  * Paul Mackerras	August 1996.
  * Copyright (C) 1996 Paul Mackerras.
  */
-
 #include <stdarg.h>
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
-#include <linux/blk.h>
+#include <linux/init.h>
 #include <asm/prom.h>
 #include <asm/page.h>
 #include <asm/processor.h>
 
-#define getpromprop(node, name, buf, len)	\
-	((int)call_prom("getprop", 4, 1, (node), (name), (buf), (len)))
-
-ihandle prom_stdout;
-ihandle prom_chosen;
-
-char command_line[256];
-int screen_initialized = 0;
-
-char prom_display_path[128];
+/*
+ * Properties whose value is longer than this get excluded from our
+ * copy of the device tree.  This way we don't waste space storing
+ * things like "driver,AAPL,MacOS,PowerPC" properties.
+ */
+#define MAX_PROPERTY_LENGTH	1024
 
 struct prom_args {
 	const char *service;
 	int nargs;
 	int nret;
 	void *args[10];
-} prom_args;
+};
 
 struct pci_address {
 	unsigned a_hi;
@@ -55,39 +50,83 @@
 	unsigned size_lo;
 };
 
-void (*prom_entry)(void *);
-extern int prom_trashed;
+char *prom_display_paths[FB_MAX] __initdata = { 0, };
+unsigned int prom_num_displays = 0;
+
+prom_entry prom = 0;
+ihandle prom_chosen = 0, prom_stdout = 0;
+
+extern char *klimit;
+char *bootpath = 0;
+char *bootdevice = 0;
 
-static int prom_callback(struct prom_args *);
+unsigned int rtas_data = 0;
+unsigned int rtas_entry = 0;
+
+static struct device_node *allnodes = 0;
+
+static void *call_prom(const char *service, int nargs, int nret, ...);
+static void prom_print(const char *msg);
+static void prom_exit(void);
+static unsigned long copy_device_tree(unsigned long, unsigned long);
 static unsigned long inspect_node(phandle, struct device_node *, unsigned long,
-				  unsigned long, unsigned long);
-static void check_display(void);
+				  unsigned long, struct device_node ***);
+static unsigned long finish_node(struct device_node *, unsigned long,
+				 unsigned long);
+static unsigned long check_display(unsigned long);
 static int prom_next_node(phandle *);
 
-extern int pmac_display_supported(const char *);
-extern void enter_prom(void *);
+extern void enter_rtas(void *);
+extern unsigned long reloc_offset(void);
 
-void
+/*
+ * prom_init() is called very early on, before the kernel text
+ * and data have been mapped to KERNELBASE.  At this point the code
+ * is running at whatever address it has been loaded at, so
+ * references to extern and static variables must be relocated
+ * explicitly.  The procedure reloc_offset() returns the the address
+ * we're currently running at minus the address we were linked at.
+ * (Note that strings count as static variables.)
+ *
+ * Because OF may have mapped I/O devices into the area starting at
+ * KERNELBASE, particularly on CHRP machines, we can't safely call
+ * OF once the kernel has been mapped to KERNELBASE.  Therefore all
+ * OF calls should be done within prom_init(), and prom_init()
+ * and all routines called within it must be careful to relocate
+ * references as necessary.
+ *
+ * Note that the bss is cleared *after* prom_init runs, so we have
+ * to make sure that any static or extern variables it accesses
+ * are put in the data segment.
+ */
+#define PTRRELOC(x)	((typeof(x))((unsigned long)(x) + offset))
+#define PTRUNRELOC(x)	((typeof(x))((unsigned long)(x) - offset))
+#define RELOC(x)	(*PTRRELOC(&(x)))
+
+#define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long))
+
+static void
 prom_exit()
 {
 	struct prom_args args;
+	unsigned long offset = reloc_offset();
 
 	args.service = "exit";
 	args.nargs = 0;
 	args.nret = 0;
-	enter_prom(&args);
+	RELOC(prom)(&args);
 	for (;;)			/* should never get here */
 		;
 }
 
-void *
+static void *
 call_prom(const char *service, int nargs, int nret, ...)
 {
 	va_list list;
 	int i;
+	unsigned long offset = reloc_offset();
+	struct prom_args prom_args;
 
-	if (prom_trashed)
-		panic("prom called after its memory was reclaimed");
 	prom_args.service = service;
 	prom_args.nargs = nargs;
 	prom_args.nret = nret;
@@ -97,26 +136,26 @@
 	va_end(list);
 	for (i = 0; i < nret; ++i)
 		prom_args.args[i + nargs] = 0;
-	enter_prom(&prom_args);
+	RELOC(prom)(&prom_args);
 	return prom_args.args[nargs];
 }
 
-void
+static void
 prom_print(const char *msg)
 {
 	const char *p, *q;
-	const char *crlf = "\r\n";
+	unsigned long offset = reloc_offset();
 
-	if (screen_initialized)
-		return;
 	for (p = msg; *p != 0; p = q) {
 		for (q = p; *q != 0 && *q != '\n'; ++q)
 			;
 		if (q > p)
-			call_prom("write", 3, 1, prom_stdout, p, q - p);
+			call_prom(RELOC("write"), 3, 1, RELOC(prom_stdout),
+				  p, q - p);
 		if (*q != 0) {
 			++q;
-			call_prom("write", 3, 1, prom_stdout, crlf, 2);
+			call_prom(RELOC("write"), 3, 1, RELOC(prom_stdout),
+				  RELOC("\r\n"), 2);
 		}
 	}
 }
@@ -126,285 +165,311 @@
  * handling exceptions and the MMU hash table for us.
  */
 void
-prom_init(char *params, int unused, void (*pp)(void *))
+prom_init(int r3, int r4, prom_entry pp)
 {
+	unsigned long mem;
+	ihandle prom_rtas;
+	unsigned int rtas_size;
+	unsigned long offset = reloc_offset();
+	int l;
+	char *p, *d;
+
 	/* First get a handle for the stdout device */
-	if ( ! have_of() )
-		return;
-	prom_entry = pp;
-	prom_chosen = call_prom("finddevice", 1, 1, "/chosen");
-	if (prom_chosen == (void *)-1)
+	RELOC(prom) = pp;
+	RELOC(prom_chosen) = call_prom(RELOC("finddevice"), 1, 1,
+				       RELOC("/chosen"));
+	if (RELOC(prom_chosen) == (void *)-1)
+		prom_exit();
+	if ((int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen),
+			    RELOC("stdout"), &RELOC(prom_stdout),
+			    sizeof(prom_stdout)) <= 0)
 		prom_exit();
-	call_prom("getprop", 4, 1, prom_chosen, "stdout", &prom_stdout,
-		  (void *) sizeof(prom_stdout));
 
-	/*
-	 * If we were booted via quik, params points to the physical address
-	 * of the command-line parameters.
-	 * If we were booted from an xcoff image (i.e. netbooted or
-	 * booted from floppy), we get the command line from the bootargs
-	 * property of the /chosen node.  If an initial ramdisk is present,
-	 * params and unused are used for initrd_start and initrd_size,
-	 * otherwise they contain 0xdeadbeef.  
-	 */
-	command_line[0] = 0;
-	if ((unsigned long) params >= 0x4000
-	    && (unsigned long) params < 0x800000
-	    && unused == 0) {
-		strncpy(command_line, params+KERNELBASE, sizeof(command_line));
-	} else {
-#ifdef CONFIG_BLK_DEV_INITRD
-		if ((unsigned long) params - KERNELBASE < 0x800000
-		    && unused != 0 && unused != 0xdeadbeef) {
-			initrd_start = (unsigned long) params;
-			initrd_end = initrd_start + unused;
-			ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);
+	/* Get the boot device and translate it to a full OF pathname. */
+	mem = (unsigned long) RELOC(klimit) + offset;
+	p = (char *) mem;
+	l = (int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen),
+			    RELOC("bootpath"), p, 1<<20);
+	if (l > 0) {
+		p[l] = 0;	/* should already be null-terminated */
+		RELOC(bootpath) = PTRUNRELOC(p);
+		mem += l + 1;
+		d = (char *) mem;
+		*d = 0;
+		call_prom(RELOC("canon"), 3, 1, p, d, 1<<20);
+		RELOC(bootdevice) = PTRUNRELOC(d);
+		mem = ALIGN(mem + strlen(d) + 1);
+	}
+
+	mem = check_display(mem);
+
+	prom_print(RELOC("copying OF device tree..."));
+	mem = copy_device_tree(mem, mem + (1<<20));
+	prom_print(RELOC("done\n"));
+
+	prom_rtas = call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas"));
+	if (prom_rtas != (void *) -1) {
+		rtas_size = 0;
+		call_prom(RELOC("getprop"), 4, 1, prom_rtas,
+			  RELOC("rtas-size"), &rtas_size, sizeof(rtas_size));
+		prom_print(RELOC("instantiating rtas..."));
+		if (rtas_size == 0) {
+			RELOC(rtas_data) = 0;
+		} else {
+			mem = (mem + 4095) & -4096; /* round to page bdry */
+			RELOC(rtas_data) = mem - KERNELBASE;
+			mem += rtas_size;
 		}
-#endif
-		call_prom("getprop", 4, 1, prom_chosen, "bootargs",
-			  command_line, sizeof(command_line));
+		RELOC(rtas_entry) = (unsigned int)
+			call_prom(RELOC("instantiate-rtas"), 1, 1,
+				  RELOC(rtas_data));
+		if (RELOC(rtas_entry) == -1)
+			prom_print(RELOC(" failed\n"));
+		else
+			prom_print(RELOC(" done\n"));
 	}
-	command_line[sizeof(command_line) - 1] = 0;
 
-	check_display();
+	RELOC(klimit) = (char *) (mem - offset);
 }
 
 /*
  * If we have a display that we don't know how to drive,
  * we will want to try to execute OF's open method for it
- * later.  However, OF may fall over if we do that after
- * we've taken over the MMU and done set_prom_callback.
+ * later.  However, OF will probably fall over if we do that
+ * we've taken over the MMU.
  * So we check whether we will need to open the display,
  * and if so, open it now.
  */
-static void
-check_display()
+static unsigned long
+check_display(unsigned long mem)
 {
 	phandle node;
 	ihandle ih;
-	char type[16], name[64], path[128];
+	unsigned long offset = reloc_offset();
+	char type[16], *path;
 
 	for (node = 0; prom_next_node(&node); ) {
 		type[0] = 0;
-		getpromprop(node, "device_type", type, sizeof(type));
-		if (strcmp(type, "display") != 0)
-			continue;
-		name[0] = 0;
-		getpromprop(node, "name", name, sizeof(name));
-		if (pmac_display_supported(name))
-			/* we have a supported display */
-			return;
-	}
-	printk(KERN_INFO "No supported display found\n");
-	for (node = 0; prom_next_node(&node); ) {
-		type[0] = 0;
-		getpromprop(node, "device_type", type, sizeof(type));
-		if (strcmp(type, "display") != 0)
+		call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"),
+			  type, sizeof(type));
+		if (strcmp(type, RELOC("display")) != 0)
 			continue;
 		/* It seems OF doesn't null-terminate the path :-( */
-		memset(path, 0, sizeof(path));
-		if ((int) call_prom("package-to-path", 3, 1,
-				    node, path, sizeof(path) - 1) < 0) {
-			printk(KERN_WARNING "can't get path for display %p\n",
-			       node);
+		path = (char *) mem;
+		memset(path, 0, 256);
+		if ((int) call_prom(RELOC("package-to-path"), 3, 1,
+				    node, path, 255) < 0)
 			continue;
-		}
-		ih = call_prom("open", 1, 1, path);
+		prom_print(RELOC("opening display "));
+		prom_print(path);
+		ih = call_prom(RELOC("open"), 1, 1, path);
 		if (ih == 0 || ih == (ihandle) -1) {
-			printk(KERN_WARNING "couldn't open display %s\n",
-			       path);
+			prom_print(RELOC("... failed\n"));
 			continue;
 		}
-		printk(KERN_INFO "Opened display device %s using "
-		       "Open Firmware\n", path);
-		strcpy(prom_display_path, path);
-		break;
+		prom_print(RELOC("... ok\n"));
+		mem += strlen(path) + 1;
+		RELOC(prom_display_paths[RELOC(prom_num_displays)++])
+			= PTRUNRELOC(path);
+		if (RELOC(prom_num_displays) >= FB_MAX)
+			break;
 	}
+	return ALIGN(mem);
 }
 
 static int
 prom_next_node(phandle *nodep)
 {
 	phandle node;
+	unsigned long offset = reloc_offset();
 
 	if ((node = *nodep) != 0
-	    && (*nodep = call_prom("child", 1, 1, node)) != 0)
+	    && (*nodep = call_prom(RELOC("child"), 1, 1, node)) != 0)
 		return 1;
-	if ((*nodep = call_prom("peer", 1, 1, node)) != 0)
+	if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0)
 		return 1;
 	for (;;) {
-		if ((node = call_prom("parent", 1, 1, node)) == 0)
+		if ((node = call_prom(RELOC("parent"), 1, 1, node)) == 0)
 			return 0;
-		if ((*nodep = call_prom("peer", 1, 1, node)) != 0)
+		if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0)
 			return 1;
 	}
 }
 
 /*
- * Callback routine for the PROM to call us.
- * No services are implemented yet :-)
- */
-static int
-prom_callback(struct prom_args *argv)
-{
-	printk("uh oh, prom callback '%s' (%d/%d)\n", argv->service,
-	       argv->nargs, argv->nret);
-	return -1;
-}
-
-/*
- * Register a callback with the Open Firmware PROM so it can ask
- * us to map/unmap memory, etc.
- */
-void
-set_prom_callback()
-{
-	call_prom("set-callback", 1, 1, prom_callback);
-}
-
-void
-abort()
-{
-#ifdef CONFIG_XMON
-	xmon(0);
-#endif
-	prom_exit();
-}
-
-/*
  * Make a copy of the device tree from the PROM.
  */
-
-static struct device_node *allnodes;
-static struct device_node **allnextp;
-
-#define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long))
-
-unsigned long
+static unsigned long
 copy_device_tree(unsigned long mem_start, unsigned long mem_end)
 {
 	phandle root;
-
-	root = call_prom("peer", 1, 1, (phandle)0);
-	if (root == (phandle)0)
-		panic("couldn't get device tree root\n");
-	allnextp = &allnodes;
-	mem_start = inspect_node(root, 0, 0, mem_start, mem_end);
+	unsigned long new_start;
+	struct device_node **allnextp;
+	unsigned long offset = reloc_offset();
+
+	root = call_prom(RELOC("peer"), 1, 1, (phandle)0);
+	if (root == (phandle)0) {
+		prom_print(RELOC("couldn't get device tree root\n"));
+		prom_exit();
+	}
+	allnextp = &RELOC(allnodes);
+	mem_start = ALIGN(mem_start);
+	new_start = inspect_node(root, 0, mem_start, mem_end, &allnextp);
 	*allnextp = 0;
-	return mem_start;
+	return new_start;
 }
 
 static unsigned long
-inspect_node(phandle node, struct device_node *dad, unsigned long base_address,
-	     unsigned long mem_start, unsigned long mem_end)
+inspect_node(phandle node, struct device_node *dad,
+	     unsigned long mem_start, unsigned long mem_end,
+	     struct device_node ***allnextpp)
 {
-	struct reg_property *reg, *rp;
-	struct pci_reg_property *pci_addrs;
-	int l, i;
+	int l;
 	phandle child;
 	struct device_node *np;
 	struct property *pp, **prev_propp;
-	char *prev_name;
+	char *prev_name, *namep;
+	unsigned char *valp;
+	unsigned long offset = reloc_offset();
 
 	np = (struct device_node *) mem_start;
 	mem_start += sizeof(struct device_node);
 	memset(np, 0, sizeof(*np));
 	np->node = node;
-	*allnextp = np;
-	allnextp = &np->allnext;
-	np->parent = dad;
+	**allnextpp = PTRUNRELOC(np);
+	*allnextpp = &np->allnext;
 	if (dad != 0) {
+		np->parent = PTRUNRELOC(dad);
 		/* we temporarily use the `next' field as `last_child'. */
 		if (dad->next == 0)
-			dad->child = np;
+			dad->child = PTRUNRELOC(np);
 		else
-			dad->next->sibling = np;
+			dad->next->sibling = PTRUNRELOC(np);
 		dad->next = np;
 	}
 
 	/* get and store all properties */
 	prev_propp = &np->properties;
-	prev_name = 0;
+	prev_name = RELOC("");
 	for (;;) {
 		pp = (struct property *) mem_start;
-		pp->name = (char *) (pp + 1);
-		if ((int) call_prom("nextprop", 3, 1, node, prev_name,
-				    pp->name) <= 0)
+		namep = (char *) (pp + 1);
+		pp->name = PTRUNRELOC(namep);
+		if ((int) call_prom(RELOC("nextprop"), 3, 1, node, prev_name,
+				    namep) <= 0)
 			break;
-		mem_start = ALIGN((unsigned long)pp->name
-				  + strlen(pp->name) + 1);
-		pp->value = (unsigned char *) mem_start;
+		mem_start = ALIGN((unsigned long)namep + strlen(namep) + 1);
+		prev_name = namep;
+		valp = (unsigned char *) mem_start;
+		pp->value = PTRUNRELOC(valp);
 		pp->length = (int)
-			call_prom("getprop", 4, 1, node, pp->name, pp->value,
-				  mem_end - mem_start);
+			call_prom(RELOC("getprop"), 4, 1, node, namep,
+				  valp, mem_end - mem_start);
 		if (pp->length < 0)
-			panic("hey, where did property %s go?", pp->name);
+			continue;
+#ifdef MAX_PROPERTY_LENGTH
+		if (pp->length > MAX_PROPERTY_LENGTH)
+			continue; /* ignore this property */
+#endif
 		mem_start = ALIGN(mem_start + pp->length);
-		prev_name = pp->name;
-		*prev_propp = pp;
+		*prev_propp = PTRUNRELOC(pp);
 		prev_propp = &pp->next;
 	}
 	*prev_propp = 0;
 
+	/* get the node's full name */
+	l = (int) call_prom(RELOC("package-to-path"), 3, 1, node,
+			    (char *) mem_start, mem_end - mem_start);
+	if (l >= 0) {
+		np->full_name = PTRUNRELOC((char *) mem_start);
+		*(char *)(mem_start + l) = 0;
+		mem_start = ALIGN(mem_start + l + 1);
+	}
+
+	/* do all our children */
+	child = call_prom(RELOC("child"), 1, 1, node);
+	while (child != (void *)0) {
+		mem_start = inspect_node(child, np, mem_start, mem_end,
+					 allnextpp);
+		child = call_prom(RELOC("peer"), 1, 1, child);
+	}
+
+	return mem_start;
+}
+
+void
+finish_device_tree(void)
+{
+	unsigned long mem = (unsigned long) klimit;
+
+	mem = finish_node(allnodes, mem, 0UL);
+	printk(KERN_INFO "device tree used %lu bytes\n",
+	       mem - (unsigned long) allnodes);
+	klimit = (char *) mem;
+}
+
+static unsigned long
+finish_node(struct device_node *np, unsigned long mem_start,
+	    unsigned long base_address)
+{
+	struct reg_property *rp;
+	struct pci_reg_property *pci_addrs;
+	struct address_range *adr;
+	struct device_node *child;
+	int i, l;
+
 	np->name = get_property(np, "name", 0);
 	np->type = get_property(np, "device_type", 0);
 
 	/* get all the device addresses and interrupts */
-	reg = (struct reg_property *) mem_start;
+	adr = (struct address_range *) mem_start;
 	pci_addrs = (struct pci_reg_property *)
 		get_property(np, "assigned-addresses", &l);
 	i = 0;
 	if (pci_addrs != 0) {
 		while ((l -= sizeof(struct pci_reg_property)) >= 0) {
 			/* XXX assumes PCI addresses mapped 1-1 to physical */
-			reg[i].address = pci_addrs[i].addr.a_lo;
-			reg[i].size = pci_addrs[i].size_lo;
+			adr[i].space = pci_addrs[i].addr.a_hi;
+			adr[i].address = pci_addrs[i].addr.a_lo;
+			adr[i].size = pci_addrs[i].size_lo;
 			++i;
 		}
 	} else {
 		rp = (struct reg_property *) get_property(np, "reg", &l);
 		if (rp != 0) {
 			while ((l -= sizeof(struct reg_property)) >= 0) {
-				reg[i].address = rp[i].address + base_address;
-				reg[i].size = rp[i].size;
+				adr[i].space = 0;
+				adr[i].address = rp[i].address + base_address;
+				adr[i].size = rp[i].size;
 				++i;
 			}
 		}
 	}
 	if (i > 0) {
-		np->addrs = reg;
+		np->addrs = adr;
 		np->n_addrs = i;
-		mem_start += i * sizeof(struct reg_property);
+		mem_start += i * sizeof(struct address_range);
 	}
 
 	np->intrs = (int *) get_property(np, "AAPL,interrupts", &l);
+	if (np->intrs == 0)
+		np->intrs = (int *) get_property(np, "interrupts", &l);
 	if (np->intrs != 0)
 		np->n_intrs = l / sizeof(int);
 
-	/* get the node's full name */
-	l = (int) call_prom("package-to-path", 3, 1, node,
-			    (char *) mem_start, mem_end - mem_start);
-	if (l >= 0) {
-		np->full_name = (char *) mem_start;
-		np->full_name[l] = 0;
-		mem_start = ALIGN(mem_start + l + 1);
-	}
-
-	if (np->type != 0 && strcmp(np->type, "dbdma") == 0 && np->n_addrs > 0)
+	if (np->type != 0 && np->n_addrs > 0
+	    && (strcmp(np->type, "dbdma") == 0
+		|| strcmp(np->type, "mac-io") == 0))
 		base_address = np->addrs[0].address;
 
-	child = call_prom("child", 1, 1, node);
-	while (child != (void *)0) {
-		mem_start = inspect_node(child, np, base_address,
-					 mem_start, mem_end);
-		child = call_prom("peer", 1, 1, child);
-	}
+	for (child = np->child; child != NULL; child = child->sibling)
+		mem_start = finish_node(child, mem_start, base_address);
 
 	return mem_start;
 }
 
 /*
- * Construct a return a list of the device_nodes with a given name.
+ * Construct and return a list of the device_nodes with a given name.
  */
 struct device_node *
 find_devices(const char *name)
@@ -423,7 +488,7 @@
 }
 
 /*
- * Construct a return a list of the device_nodes with a given type.
+ * Construct and return a list of the device_nodes with a given type.
  */
 struct device_node *
 find_type_devices(const char *type)
@@ -442,6 +507,31 @@
 }
 
 /*
+ * Construct and return a list of the device_nodes with a given type
+ * and compatible property.
+ */
+struct device_node *
+find_compatible_devices(const char *type, const char *compat)
+{
+	struct device_node *head, **prevp, *np;
+	const char *cp;
+
+	prevp = &head;
+	for (np = allnodes; np != 0; np = np->allnext) {
+		if (type != NULL
+		    && !(np->type != 0 && strcasecmp(np->type, type) == 0))
+			continue;
+		cp = (char *) get_property(np, "compatible", NULL);
+		if (cp != NULL && strcasecmp(cp, compat) == 0) {
+			*prevp = np;
+			prevp = &np->next;
+		}
+	}
+	*prevp = 0;
+	return head;
+}
+
+/*
  * Find the device_node with a given full_name.
  */
 struct device_node *
@@ -456,6 +546,20 @@
 }
 
 /*
+ * Find the device_node with a given phandle.
+ */
+struct device_node *
+find_phandle(phandle ph)
+{
+	struct device_node *np;
+
+	for (np = allnodes; np != 0; np = np->allnext)
+		if (np->node == ph)
+			return np;
+	return NULL;
+}
+
+/*
  * Find a property with a given name for a given node
  * and return the value.
  */
@@ -521,4 +625,49 @@
 				       pp->length);
 		}
 	}
+}
+
+int
+call_rtas(const char *service, int nargs, int nret,
+	  unsigned long *outputs, ...)
+{
+	va_list list;
+	int i;
+	struct device_node *rtas;
+	int *tokp;
+	union {
+		unsigned long words[16];
+		double align;
+	} u;
+
+	rtas = find_devices("rtas");
+	if (rtas == NULL)
+		return -1;
+	tokp = (int *) get_property(rtas, service, NULL);
+	if (tokp == NULL) {
+		printk(KERN_ERR "No RTAS service called %s\n", service);
+		return -1;
+	}
+	u.words[0] = *tokp;
+	u.words[1] = nargs;
+	u.words[2] = nret;
+	va_start(list, outputs);
+	for (i = 0; i < nargs; ++i)
+		u.words[i+3] = va_arg(list, unsigned long);
+	va_end(list);
+	enter_rtas(&u);
+	if (nret > 1 && outputs != NULL)
+		for (i = 0; i < nret-1; ++i)
+			outputs[i] = u.words[i+nargs+4];
+	return u.words[nargs+3];
+}
+
+void
+abort()
+{
+#ifdef CONFIG_XMON
+	extern void xmon(void *);
+	xmon(0);
+#endif
+	prom_exit();
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/ptrace.c linux/arch/ppc/kernel/ptrace.c
--- v2.1.78/linux/arch/ppc/kernel/ptrace.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/ptrace.c	Mon Jan 12 15:18:13 1998
@@ -32,6 +32,11 @@
 #include <asm/system.h>
 
 /*
+ * Set of msr bits that gdb can change on behalf of a process.
+ */
+#define MSR_DEBUGCHANGE	(MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1)
+
+/*
  * does not yet catch signals sent when the child dies.
  * in exit.c or in signal.c.
  */
@@ -41,7 +46,7 @@
  */
 static inline long get_reg(struct task_struct *task, int regno)
 {
-	if (regno <= PT_CCR)
+	if (regno <= PT_MQ)
 		return ((unsigned long *)task->tss.regs)[regno];
 	return (0);
 }
@@ -52,7 +57,10 @@
 static inline int put_reg(struct task_struct *task, int regno,
 			  unsigned long data)
 {
-	if (regno <= PT_CCR) {
+	if (regno <= PT_MQ) {
+		if (regno == PT_MSR)
+			data = (data & MSR_DEBUGCHANGE)
+				| (task->tss.regs->msr & ~MSR_DEBUGCHANGE);
 		((unsigned long *)task->tss.regs)[regno] = data;
 		return 0;
 	}
@@ -408,13 +416,6 @@
 			    
 			if (addr == PT_ORIG_R3)
 				goto out;
-#if 0 /* Let this check be in 'put_reg' */				
-			if (addr == PT_SR) {
-				data &= SR_MASK;
-				data <<= 16;
-				data |= get_reg(child, PT_SR) & ~(SR_MASK << 16);
-			}
-#endif			
 			if (addr < PT_FPR0) {
 				if (put_reg(child, addr, data))
 					goto out;
@@ -433,7 +434,7 @@
 		case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
 		case PTRACE_CONT: { /* restart after signal. */
 			ret = -EIO;
-			if ((unsigned long) data >= NSIG)
+			if ((unsigned long) data >= _NSIG)
 				goto out;
 			if (request == PTRACE_SYSCALL)
 				child->flags |= PF_TRACESYS;
@@ -465,7 +466,7 @@
 
 		case PTRACE_SINGLESTEP: {  /* set the trap flag. */
 			ret = -EIO;
-			if ((unsigned long) data >= NSIG)
+			if ((unsigned long) data >= _NSIG)
 				goto out;
 			child->flags &= ~PF_TRACESYS;
 			set_single_step(child);
@@ -478,7 +479,7 @@
 
 		case PTRACE_DETACH: { /* detach a process that was attached. */
 			ret = -EIO;
-			if ((unsigned long) data >= NSIG)
+			if ((unsigned long) data >= _NSIG)
 				goto out;
 			child->flags &= ~(PF_PTRACED|PF_TRACESYS);
 			wake_up_process(child);
@@ -516,9 +517,10 @@
 	 * for normal use.  strace only continues with a signal if the
 	 * stopping signal is not SIGTRAP.  -brl
 	 */
-	if (current->exit_code)
-		current->signal |= (1 << (current->exit_code - 1));
-	current->exit_code = 0;
+	if (current->exit_code) {
+		send_sig(current->exit_code, current, 1);
+		current->exit_code = 0;
+	}
 out:
 	unlock_kernel();
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/residual.c linux/arch/ppc/kernel/residual.c
--- v2.1.78/linux/arch/ppc/kernel/residual.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/residual.c	Mon Jan 12 15:18:13 1998
@@ -1,11 +1,12 @@
 /*
- * $Id: residual.c,v 1.2 1997/08/25 06:54:56 cort Exp $
+ * $Id: residual.c,v 1.5 1997/10/30 21:25:19 cort Exp $
  *
  * Code to deal with the PReP residual data.
  *
  * Written by: Cort Dougan (cort@cs.nmt.edu)
+ * Improved _greatly_ and rewritten by Gabriel Paubert (paubert@iram.es)
  */
-
+#if 0
 #include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
@@ -35,9 +36,585 @@
 #include <asm/pnp.h>
 
 
+const char * PnP_BASE_TYPES[]= {
+  "Reserved",
+  "MassStorageDevice",
+  "NetworkInterfaceController",
+  "DisplayController",
+  "MultimediaController",
+  "MemoryController",
+  "BridgeController",
+  "CommunicationsDevice",
+  "SystemPeripheral",
+  "InputDevice",
+  "ServiceProcessor"
+  };
+
+/* Device Sub Type Codes */
+
+const unsigned char * PnP_SUB_TYPES[] = {
+  "\001\000SCSIController",
+  "\001\001IDEController",
+  "\001\002FloppyController",
+  "\001\003IPIController",
+  "\001\200OtherMassStorageController",
+  "\002\000EthernetController",
+  "\002\001TokenRingController",
+  "\002\002FDDIController",
+  "\002\0x80OtherNetworkController",
+  "\003\000VGAController",
+  "\003\001SVGAController",
+  "\003\002XGAController",
+  "\003\200OtherDisplayController",
+  "\004\000VideoController",
+  "\004\001AudioController",
+  "\004\200OtherMultimediaController",
+  "\005\000RAM",
+  "\005\001FLASH",
+  "\005\200OtherMemoryDevice",
+  "\006\000HostProcessorBridge",
+  "\006\001ISABridge",
+  "\006\002EISABridge",
+  "\006\003MicroChannelBridge",
+  "\006\004PCIBridge",
+  "\006\005PCMCIABridge",
+  "\006\006VMEBridge",
+  "\006\200OtherBridgeDevice",
+  "\007\000RS232Device",
+  "\007\001ATCompatibleParallelPort",
+  "\007\200OtherCommunicationsDevice",
+  "\010\000ProgrammableInterruptController",
+  "\010\001DMAController",
+  "\010\002SystemTimer",
+  "\010\003RealTimeClock",
+  "\010\004L2Cache",
+  "\010\005NVRAM",
+  "\010\006PowerManagement",
+  "\010\007CMOS",
+  "\010\010OperatorPanel",
+  "\010\011ServiceProcessorClass1",
+  "\010\012ServiceProcessorClass2",
+  "\010\013ServiceProcessorClass3",
+  "\010\014GraphicAssist",
+  "\010\017SystemPlanar",
+  "\010\200OtherSystemPeripheral",
+  "\011\000KeyboardController",
+  "\011\001Digitizer",
+  "\011\002MouseController",
+  "\011\003TabletController",
+  "\011\0x80OtherInputController",
+  "\012\000GeneralMemoryController",
+  NULL
+};
+
+/* Device Interface Type Codes */
+
+const  unsigned char * PnP_INTERFACES[]= {
+  "\000\000\000General",
+  "\001\000\000GeneralSCSI",
+  "\001\001\000GeneralIDE",
+  "\001\001\001ATACompatible",
+
+  "\001\002\000GeneralFloppy",
+  "\001\002\001Compatible765",
+  "\001\002\002NS398_Floppy",         /* NS Super I/O wired to use index
+                                         register at port 398 and data
+                                         register at port 399               */
+  "\001\002\003NS26E_Floppy",         /* Ports 26E and 26F                  */
+  "\001\002\004NS15C_Floppy",         /* Ports 15C and 15D                  */
+  "\001\002\005NS2E_Floppy",          /* Ports 2E and 2F                    */
+  "\001\002\006CHRP_Floppy",          /* CHRP Floppy in PR*P system         */
+
+  "\001\003\000GeneralIPI",
+  
+  "\002\000\000GeneralEther",
+  "\002\001\000GeneralToken",
+  "\002\002\000GeneralFDDI",
+  
+  "\003\000\000GeneralVGA",
+  "\003\001\000GeneralSVGA",
+  "\003\002\000GeneralXGA",
+  
+  "\004\000\000GeneralVideo",
+  "\004\001\000GeneralAudio",
+  "\004\001\001CS4232Audio",            /* CS 4232 Plug 'n Play Configured    */
+
+  "\005\000\000GeneralRAM",
+  /* This one is obviously wrong ! */
+  "\005\000\000PCIMemoryController",    /* PCI Config Method                  */
+  "\005\000\001RS6KMemoryController",   /* RS6K Config Method                 */
+  "\005\001\000GeneralFLASH",
+  
+  "\006\000\000GeneralHostBridge",
+  "\006\001\000GeneralISABridge",
+  "\006\002\000GeneralEISABridge",
+  "\006\003\000GeneralMCABridge",
+  /* GeneralPCIBridge = 0, */
+  "\006\004\000PCIBridgeDirect",
+  "\006\004\001PCIBridgeIndirect",
+  "\006\004\002PCIBridgeRS6K",
+  "\006\005\000GeneralPCMCIABridge",
+  "\006\006\000GeneralVMEBridge",
+  
+  "\007\000\000GeneralRS232",
+  "\007\000\001COMx",
+  "\007\000\002Compatible16450",
+  "\007\000\003Compatible16550",
+  "\007\000\004NS398SerPort",         /* NS Super I/O wired to use index
+                                         register at port 398 and data
+                                         register at port 399               */
+  "\007\000\005NS26ESerPort",         /* Ports 26E and 26F                  */
+  "\007\000\006NS15CSerPort",         /* Ports 15C and 15D                  */
+  "\007\000\007NS2ESerPort",          /* Ports 2E and 2F                    */
+
+  "\007\001\000GeneralParPort",
+  "\007\001\001LPTx",
+  "\007\001\002NS398ParPort",         /* NS Super I/O wired to use index
+                                         register at port 398 and data
+                                         register at port 399               */
+  "\007\001\003NS26EParPort",         /* Ports 26E and 26F                  */
+  "\007\001\004NS15CParPort",         /* Ports 15C and 15D                  */
+  "\007\001\005NS2EParPort",          /* Ports 2E and 2F                    */
+  
+  "\010\000\000GeneralPIC",
+  "\010\000\001ISA_PIC",
+  "\010\000\002EISA_PIC",
+  "\010\000\003MPIC",
+  "\010\000\004RS6K_PIC",
+
+  "\010\001\000GeneralDMA",
+  "\010\001\001ISA_DMA",
+  "\010\001\002EISA_DMA",
+
+  "\010\002\000GeneralTimer",
+  "\010\002\001ISA_Timer",
+  "\010\002\002EISA_Timer",
+  "\010\003\000GeneralRTC",
+  "\010\003\001ISA_RTC",
+  
+  "\010\004\001StoreThruOnly",
+  "\010\004\002StoreInEnabled",
+  "\010\004\003RS6KL2Cache",
+
+  "\010\005\000IndirectNVRAM",        /* Indirectly addressed               */
+  "\010\005\001DirectNVRAM",          /* Memory Mapped                      */
+  "\010\005\002IndirectNVRAM24",      /* Indirectly addressed - 24 bit      */
+
+  "\010\006\000GeneralPowerManagement",
+  "\010\006\001EPOWPowerManagement",
+  "\010\006\002PowerControl",         // d1378
+  
+  "\010\007\000GeneralCMOS",
+
+  "\010\010\000GeneralOPPanel",
+  "\010\010\001HarddiskLight",
+  "\010\010\002CDROMLight",
+  "\010\010\003PowerLight",
+  "\010\010\004KeyLock",
+  "\010\010\005ANDisplay",            /* AlphaNumeric Display               */
+  "\010\010\006SystemStatusLED",      /* 3 digit 7 segment LED              */
+  "\010\010\007CHRP_SystemStatusLED", /* CHRP LEDs in PR*P system           */
+
+  "\010\011\000GeneralServiceProcessor",
+  "\010\012\000GeneralServiceProcessor",
+  "\010\013\000GeneralServiceProcessor",
+  
+  "\010\014\001TransferData",
+  "\010\014\002IGMC32",
+  "\010\014\003IGMC64",
+  
+  "\010\017\000GeneralSystemPlanar",   /* 10/5/95                            */
+  NULL
+  };
+
+static const unsigned char *PnP_SUB_TYPE_STR(unsigned char BaseType, 
+					     unsigned char SubType) {
+	const unsigned char ** s=PnP_SUB_TYPES;
+	while (*s && !((*s)[0]==BaseType 
+		       && (*s)[1]==SubType)) s++;
+	if (*s) return *s+2;
+	else return("Unknown !");
+};
+
+static const unsigned char *PnP_INTERFACE_STR(unsigned char BaseType, 
+					      unsigned char SubType,
+					      unsigned char Interface) {
+	const unsigned char ** s=PnP_INTERFACES;
+	while (*s && !((*s)[0]==BaseType 
+		       && (*s)[1]==SubType 
+		       && (*s)[2]==Interface)) s++;
+	if (*s) return *s+3;
+	else return NULL;
+};
+
+static void printsmallvendor(PnP_TAG_PACKET *pkt, int size) {
+	int i, c;
+	char decomp[4];
+#define p pkt->S14_Pack.S14_Data.S14_PPCPack
+	switch(p.Type) {
+	case 1:
+	  /* Decompress first 3 chars */
+	  c = *(unsigned short *)p.PPCData;
+	  decomp[0]='A'-1+((c>>10)&0x1F);
+	  decomp[1]='A'-1+((c>>5)&0x1F);
+	  decomp[2]='A'-1+(c&0x1F);
+	  decomp[3]=0;
+	  printk("    Chip identification: %s%4.4X\n",
+		 decomp, ld_le16((unsigned short *)(p.PPCData+2)));
+	  break;
+	default:
+	  printk("    Small vendor item type 0x%2.2x, data (hex): ",
+		 p.Type);
+	  for(i=0; i<size-2; i++) printk("%2.2x ", p.PPCData[i]);
+	  printk("\n");
+	  break;
+	}
+#undef p
+}
+
+static void printsmallpacket(PnP_TAG_PACKET * pkt, int size) {
+	static const unsigned char * intlevel[] = {"high", "low"};
+	static const unsigned char * intsense[] = {"edge", "level"};
+
+	switch (tag_small_item_name(pkt->S1_Pack.Tag)) {
+	case PnPVersion:
+	  printk("    PnPversion 0x%x.%x\n", 
+		 pkt->S1_Pack.Version[0], /* How to interpret version ? */
+		 pkt->S1_Pack.Version[1]);
+	  break;
+//	case Logicaldevice:
+	  break;
+//	case CompatibleDevice:
+	  break;
+	case IRQFormat:
+#define p pkt->S4_Pack
+	  printk("    IRQ Mask 0x%4.4x, %s %s sensitive\n", 
+		 ld_le16((unsigned short *)p.IRQMask),
+		 intlevel[(size>3) ? !(p.IRQInfo&0x05) : 0],
+		 intsense[(size>3) ? !(p.IRQInfo&0x03) : 0]);
+#undef p
+	  break;
+	case DMAFormat:
+#define p pkt->S5_Pack
+	  printk("    DMA channel mask 0x%2.2x, info 0x%2.2x\n",
+		 p.DMAMask, p.DMAInfo);
+#undef p
+	  break;
+	case StartDepFunc:
+	  printk("Start dependant function:\n");
+	  break;
+	case EndDepFunc:
+	  printk("End dependant function\n");
+	  break;
+	case IOPort:
+#define p pkt->S8_Pack
+	  printk("    Variable (%d decoded bits) I/O port\n"
+		 "      from 0x%4.4x to 0x%4.4x, alignment %d, %d ports\n",
+		 p.IOInfo&ISAAddr16bit?16:10,
+		 ld_le16((unsigned short *)p.RangeMin),
+ 		 ld_le16((unsigned short *)p.RangeMax),
+		 p.IOAlign, p.IONum); 
+#undef p
+	  break;
+	case FixedIOPort:
+#define p pkt->S9_Pack
+	  printk("    Fixed (10 decoded bits) I/O port from %3.3x to %3.3x\n",
+		 (p.Range[1]<<8)|p.Range[0],
+		 ((p.Range[1]<<8)|p.Range[0])+p.IONum-1);
+#undef p		 
+	  break;
+	case Res1:
+	case Res2:
+	case Res3:
+	  printk("    Undefined packet type %d!\n", 
+		 tag_small_item_name(pkt->S1_Pack.Tag));
+	  break;
+	case SmallVendorItem:
+	  printsmallvendor(pkt,size);
+	  break;
+	default:
+	  printk("    Type 0x2.2x%d, size=%d\n", 
+		 pkt->S1_Pack.Tag, size);    
+	  break;
+	}
+}
+
+static void printlargevendor(PnP_TAG_PACKET * pkt, int size) {
+	static const unsigned char * addrtype[] = {"I/O", "Memory", "System"};
+	static const unsigned char * inttype[] = {"8259", "MPIC", "RS6k BUID %d"};
+	static const unsigned char * convtype[] = {"Bus Memory", "Bus I/O", "DMA"};
+	static const unsigned char * transtype[] = {"direct", "mapped", "direct-store segment"};
+	static const unsigned char * L2type[] = {"WriteThru", "CopyBack"};
+	static const unsigned char * L2assoc[] = {"DirectMapped", "2-way set"};
+
+	int i;
+	char tmpstr[30], *t;
+#define p pkt->L4_Pack.L4_Data.L4_PPCPack
+	switch(p.Type) {
+	case 2:
+	  printk("    %d K %s %s L2 cache, %d/%d bytes line/sector size\n",
+		 ld_le32((unsigned int *)p.PPCData),
+		 L2type[p.PPCData[10]-1],
+		 L2assoc[p.PPCData[4]-1],
+		 ld_le16((unsigned short *)p.PPCData+3),
+		 ld_le16((unsigned short *)p.PPCData+4));
+	  break;
+	case 3:
+	  printk("    PCI Bridge parameters\n"
+		 "      ConfigBaseAddress %0x\n"
+		 "      ConfigBaseData %0x\n"
+		 "      Bus number %d\n",
+		 ld_le32((unsigned int *)p.PPCData),
+		 ld_le32((unsigned int *)(p.PPCData+8)),
+		 p.PPCData[16]);
+	  for(i=20; i<size-4; i+=12) {
+	  	int j, first;
+	  	if(p.PPCData[i]) printk("      PCI Slot %d", p.PPCData[i]);
+		else printk ("      Integrated PCI device");
+		for(j=0, first=1, t=tmpstr; j<4; j++) {
+			int line=ld_le16((unsigned short *)(p.PPCData+i+4)+j);
+			if(line!=0xffff){
+			        if(first) first=0; else *t++='/';
+				*t++='A'+j;
+			}
+		}
+		*t='\0';
+		printk(" DevFunc 0x%x interrupt line(s) %s routed to",
+		       p.PPCData[i+1],tmpstr);
+		sprintf(tmpstr,
+			inttype[p.PPCData[i+2]-1],
+			p.PPCData[i+3]);
+		printk(" %s line(s) ",
+		       tmpstr);
+		for(j=0, first=1, t=tmpstr; j<4; j++) {
+			int line=ld_le16((unsigned short *)(p.PPCData+i+4)+j);
+			if(line!=0xffff){
+				if(first) first=0; else *t++='/';
+				t+=sprintf(t,"%d(%c)",
+					   line&0x7fff,
+					   line&0x8000?'E':'L');
+			}
+		}
+		printk("%s\n",tmpstr);
+	  }
+	  break;
+	case 5:
+	  printk("    Bridge address translation, %s decoding:\n"
+		 "      Processor  Bus        Size       Conversion Translation\n"
+		 "      0x%8.8x 0x%8.8x 0x%8.8x %s %s\n",
+		 p.PPCData[0]&1 ? "positive" : "subtractive",
+		 ld_le32((unsigned int *)p.PPCData+1),
+		 ld_le32((unsigned int *)p.PPCData+3),
+		 ld_le32((unsigned int *)p.PPCData+5),
+		 convtype[p.PPCData[2]-1],
+		 transtype[p.PPCData[1]-1]);
+	  break;
+	case 6:
+	  printk("    Bus speed %d Hz, %d slot(s)\n",
+		 ld_le32((unsigned int *)p.PPCData),
+		 p.PPCData[4]);
+	  break;
+	case 7:
+	  printk("    SCSI buses: %d, id(s):", p.PPCData[0]);
+	  for(i=1; i<=p.PPCData[0]; i++) 
+	    printk(" %d%c", p.PPCData[i], i==p.PPCData[0] ? '\n' : ',');
+	  break;
+	case 9:
+	  printk("    %s address (%d bits), at 0x%x size 0x%x bytes\n",
+		 addrtype[p.PPCData[0]-1],
+		 p.PPCData[1],
+		 ld_le32((unsigned int *)(p.PPCData+4)),
+		 ld_le32((unsigned int *)(p.PPCData+12)));
+	  break;
+	case 10:
+	  sprintf(tmpstr,
+		  inttype[p.PPCData[0]-1],
+		  p.PPCData[1]);
+
+	  printk("    ISA interrupts routed to %s\n"
+		 "      lines", 
+		 tmpstr);
+	  for(i=0; i<16; i++) {
+	  	int line=ld_le16((unsigned short *)p.PPCData+i+1);
+		if (line!=0xffff) printk(" %d(IRQ%d)", line, i); 
+	  }
+	  printk("\n");
+	  break;
+	default:
+	  printk("    Large vendor item type 0x%2.2x\n      Data (hex):",
+		 p.Type);
+	  for(i=0; i<size-4; i++) printk(" %2.2x", p.PPCData[i]);
+	  printk("\n");
+#undef p
+	}
+}
+
+static void printlargepacket(PnP_TAG_PACKET * pkt, int size) {
+	int i;
+
+	switch (tag_large_item_name(pkt->S1_Pack.Tag)) {
+	case LargeVendorItem:
+	  printlargevendor(pkt, size);
+	  break;
+	default:
+	  printk("    Type 0x2.2x%d, size=%d\n", 
+		 pkt->S1_Pack.Tag, size);    
+	  break;
+	}
+}
+static void printpackets(PnP_TAG_PACKET * pkt, const char * cat) {
+	PnP_TAG_PACKET tmp;
+	if (pkt->S1_Pack.Tag== END_TAG) {
+		printk("  No packets describing %s resources.\n", cat);
+		return;
+	}
+	printk(  "  Packets describing %s resources:\n",cat);
+	do {
+		int size;
+		if (tag_type(pkt->S1_Pack.Tag)) {
+		  	size= 3 +
+			  pkt->L1_Pack.Count0 + 
+			  pkt->L1_Pack.Count1*256;
+			printlargepacket(pkt, size);
+		} else {
+			size=tag_small_count(pkt->S1_Pack.Tag)+1;
+			printsmallpacket(pkt, size);
+		}
+		(unsigned char *) pkt+=size;
+	} while (pkt->S1_Pack.Tag != END_TAG);
+}
+
+void print_residual_device_info(void)
+{
+	int i;
+	union _PnP_TAG_PACKET *pkt;
+	PPC_DEVICE *dev;
+#define did dev->DeviceId
+return;
+	
+	/* make sure we have residual data first */
+	if ( res.ResidualLength == 0 )
+		return;
+	
+	printk("Residual: %ld devices\n", res.ActualNumDevices);
+	for ( i = 0;
+	      i < res.ActualNumDevices ;
+	      i++)
+	{
+	  	char decomp[4], sn[20];
+		const char * s;
+		dev = &res.Devices[i];
+		s = PnP_INTERFACE_STR(did.BaseType, did.SubType, 
+				      did.Interface);
+		if(!s) {
+			sprintf(sn, "interface %d", did.Interface);
+			s=sn;
+		}
+		if ( did.BusId & PCIDEVICE ) 
+		  printk("PCI Device, Bus %d, DevFunc 0x%x:",
+			 dev->BusAccess.PCIAccess.BusNumber,
+			 dev->BusAccess.PCIAccess.DevFuncNumber);
+	       	if ( did.BusId & PNPISADEVICE ) printk("PNPISA Device:");
+		if ( did.BusId & ISADEVICE ) 
+		  printk("ISA Device, Slot %d, LogicalDev %d:",
+			 dev->BusAccess.ISAAccess.SlotNumber,
+			 dev->BusAccess.ISAAccess.LogicalDevNumber);
+		if ( did.BusId & EISADEVICE ) printk("EISA Device:");
+		if ( did.BusId & PROCESSORDEVICE ) 
+		  printk("ProcBus Device, Bus %d, BUID %d: ",
+			 dev->BusAccess.ProcBusAccess.BusNumber,
+			 dev->BusAccess.ProcBusAccess.BUID);
+		if ( did.BusId & PCMCIADEVICE ) printk("PCMCIA ");
+		if ( did.BusId & VMEDEVICE ) printk("VME ");
+		if ( did.BusId & MCADEVICE ) printk("MCA ");
+		if ( did.BusId & MXDEVICE ) printk("MX ");
+		/* Decompress first 3 chars */
+		decomp[0]='A'-1+((did.DevId>>26)&0x1F);
+		decomp[1]='A'-1+((did.DevId>>21)&0x1F);
+		decomp[2]='A'-1+((did.DevId>>16)&0x1F);
+		decomp[3]=0;
+		printk(" %s%4.4lX, %s, %s, %s\n", 
+		       decomp, did.DevId&0xffff,
+		       PnP_BASE_TYPES[did.BaseType],
+		       PnP_SUB_TYPE_STR(did.BaseType,did.SubType),
+		       s);
+		printpackets( (union _PnP_TAG_PACKET *)
+		      &res.DevicePnPHeap[dev->AllocatedOffset], "allocated");
+		printpackets( (union _PnP_TAG_PACKET *)
+		      &res.DevicePnPHeap[dev->PossibleOffset], "possible");
+		printpackets( (union _PnP_TAG_PACKET *)
+		      &res.DevicePnPHeap[dev->CompatibleOffset], "compatible");
+	}
+}
+
+
+
+static void printVPD(void) {
+#define vpd res.VitalProductData
+	int ps=vpd.PageSize, i, j;
+	static const char* Usage[]={
+	  "FirmwareStack",  "FirmwareHeap",  "FirmwareCode", "BootImage",
+	  "Free", "Unpopulated", "ISAAddr", "PCIConfig",
+	  "IOMemory", "SystemIO", "SystemRegs", "PCIAddr", 
+	  "UnPopSystemRom", "SystemROM", "ResumeBlock", "Other" 
+	};
+	static const unsigned char *FWMan[]={
+	  "IBM", "Motorola", "FirmWorks", "Bull"
+	};
+	static const unsigned char *FWFlags[]={
+	  "Conventional", "OpenFirmware", "Diagnostics", "LowDebug",
+	  "MultiBoot", "LowClient", "Hex41", "FAT", 
+	  "ISO9660", "SCSI_ID_Override", "Tape_Boot", "FW_Boot_Path"
+	};
+	static const unsigned char *ESM[]={
+	  "Port92", "PCIConfigA8", "FF001030", "????????"
+	};
+	static const unsigned char *SIOM[]={
+	  "Port850", "????????", "PCIConfigA8", "????????"
+	};
+
+	printk("Model: %s\n",vpd.PrintableModel);
+	printk("Serial: %s\n", vpd.Serial);
+	printk("FirmwareSupplier: %s\n", FWMan[vpd.FirmwareSupplier]);
+	printk("FirmwareFlags:");
+	for(j=0; j<12; j++) {
+	  	if (vpd.FirmwareSupports & (1<<j)) {
+			printk(" %s%c", FWFlags[j], 
+			       vpd.FirmwareSupports&(-2<<j) ? ',' : '\n');
+		}
+	}
+	printk("NVRamSize: %ld\n", vpd.NvramSize);
+	printk("SIMMslots: %ld\n", vpd.NumSIMMSlots);
+	printk("EndianSwitchMethod: %s\n", 
+	       ESM[vpd.EndianSwitchMethod>2 ? 2 : vpd.EndianSwitchMethod]);
+	printk("SpreadIOMethod: %s\n", 
+	       SIOM[vpd.SpreadIOMethod>3 ? 3 : vpd.SpreadIOMethod]);
+	printk("Processor/Bus frequencies (Hz): %ld/%ld\n",
+	       vpd.ProcessorHz, vpd.ProcessorBusHz);
+	printk("Time Base Divisor: %ld\n", vpd.TimeBaseDivisor);
+	printk("WordWidth, PageSize: %ld, %d\n", vpd.WordWidth, ps);
+	printk("Cache sector size, Lock granularity: %ld, %ld\n",
+	       vpd.CoherenceBlockSize, vpd.GranuleSize);
+	for (i=0; i<res.ActualNumMemSegs; i++) {
+		int mask=res.Segs[i].Usage, first, j;
+		printk("%8.8lx-%8.8lx ", 
+		       res.Segs[i].BasePage*ps,
+		       (res.Segs[i].PageCount+res.Segs[i].BasePage)*ps-1);
+		for(j=15, first=1; j>=0; j--) {
+			if (mask&(1<<j)) {
+				if (first) first=0;
+				else printk(", ");
+				printk("%s", Usage[j]);
+			}
+		}
+		printk("\n");
+	}
+}
+
 /*
  * Spit out some info about residual data
  */
+#if 0
 void print_residual_device_info(void)
 {
 	int i;
@@ -48,7 +625,6 @@
 	/* make sure we have residual data first */
 	if ( res.ResidualLength == 0 )
 		return;
-	
 	printk("Residual: %ld devices\n", res.ActualNumDevices);
 	for ( i = 0;
 	      i < res.ActualNumDevices ;
@@ -136,9 +712,6 @@
 		       did.BusId);
 	}
 }
+#endif	
 
-
-
-
-
-
+#endif /* 0 */
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c
--- v2.1.78/linux/arch/ppc/kernel/setup.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/setup.c	Mon Jan 12 15:18:13 1998
@@ -1,6 +1,6 @@
 /*
- * $Id: setup.c,v 1.16 1997/08/27 22:06:54 cort Exp $
- * Common prep/pmac boot and setup code.
+ * $Id: setup.c,v 1.48 1998/01/01 10:04:44 paulus Exp $
+ * Common prep/pmac/chrp boot and setup code.
  */
 
 #include <linux/config.h>
@@ -8,19 +8,31 @@
 #include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/reboot.h>
-#include <linux/openpic.h>
+#include <linux/delay.h>
+#include <linux/blk.h>
 
+#include <asm/adb.h>
 #include <asm/cuda.h>
 #include <asm/residual.h>
 #include <asm/io.h>
 #include <asm/ide.h>
+#include <asm/prom.h>
 
+extern char cmd_line[512];
 char saved_command_line[256];
 unsigned char aux_device_present;
 
+#if !defined(CONFIG_MACH_SPECIFIC)
+unsigned long ISA_DMA_THRESHOLD;
+unsigned long DMA_MODE_READ, DMA_MODE_WRITE;
+int _machine;
+#endif /* ! CONFIG_MACH_SPECIFIC */
+
 /* copy of the residual data */
 RESIDUAL res;
-int _machine;
+int _prep_type;
+/* if we have openfirmware */
+unsigned long have_of;
 
 /*
  * Perhaps we can put the pmac screen_info[] here
@@ -28,7 +40,7 @@
  * Until we get multiple-console support in here
  * that is.  -- Cort
  */ 
-#if defined(CONFIG_CHRP) || defined(CONFIG_PREP )
+#if !defined(CONFIG_PMAC_CONSOLE)
 struct screen_info screen_info = {
 	0, 25,			/* orig-x, orig-y */
 	{ 0, 0 },		/* unused */
@@ -48,94 +60,20 @@
 {
 	return 0;
 }
-int sd_find_target(void *a, int b)
-{
-	return 0;
-}
 void pmac_find_display(void)
 {
 }
-
 #endif
 
-/*
- * Find out what kind of machine we're on and save any data we need
- * from the early boot process (devtree is copied on pmac by prom_init() )
- */
-unsigned long identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
-			       unsigned long r6, unsigned long r7)
-{
-	extern unsigned long initrd_start, initrd_end;
-	extern char cmd_line[256];
-#ifdef CONFIG_PMAC /* cheat for now - perhaps a check for OF could tell us */
-	_machine = _MACH_Pmac;
-#endif /* CONFIG_PMAC */
-#ifdef CONFIG_PREP
-	/* make a copy of residual data */
-	if ( r3 )
-		memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(RESIDUAL) );
-	if (!strncmp(res.VitalProductData.PrintableModel,"IBM",3))
-		_machine = _MACH_IBM;
-	else
-		_machine = _MACH_Motorola;
-#endif /* CONFIG_PREP */
-#ifdef CONFIG_CHRP 
-	_machine = _MACH_chrp;
-#endif /* CONFIG_CHRP */
-
-	switch (_machine)
-	{
-	case _MACH_Pmac:
-		io_base = 0;
-		pci_dram_offset = 0;
-		break;
-	case _MACH_IBM:
-	case _MACH_Motorola:
-		io_base = 0x80000000;
-		pci_dram_offset = 0x80000000;
-#ifdef CONFIG_BLK_DEV_RAM
-		/* take care of initrd if we have one */
-		if ( r4 )
-		{
-			initrd_start = r4 + KERNELBASE;
-			initrd_end = r5 + KERNELBASE;
-		}
-#endif /* CONFIG_BLK_DEV_RAM */
-		/* take care of cmd line */
-		if ( r6 )
-		{
-			
-			*(char *)(r7+KERNELBASE) = 0;
-			strcpy(cmd_line, (char *)(r6+KERNELBASE));
-		}
-		break;
-	case _MACH_chrp:
-		/* LongTrail */
-		io_base = 0xf8000000;
-		pci_dram_offset = 0;
-		/* take care of initrd if we have one */
-		if ( r4 ) {
-			initrd_start = r4 + KERNELBASE;
-			initrd_end = r5 + KERNELBASE;
-		}
-		/* take care of cmd line */
-		if ( r6 ) {
-			*(char *)(r7+KERNELBASE) = 0;
-			strcpy(cmd_line, (char *)(r6+KERNELBASE));
-		}
-		break;
-	default:
-		printk("Unknown machine type in identify_machine!\n");
-	}
-	return 0;
-}
-
 /* cmd is ignored for now... */
 void machine_restart(char *cmd)
 {
-	struct cuda_request req;
+	struct adb_request req;
 	unsigned long flags;
 	unsigned long i = 10000;
+#if 0	
+	int err;
+#endif	
 
 	switch(_machine)
 	{
@@ -144,8 +82,14 @@
 		for (;;)
 			cuda_poll();
 		break;
-	case _MACH_IBM:
-	case _MACH_Motorola:
+	case _MACH_chrp:
+#if 0		/* RTAS doesn't seem to work on Longtrail.
+		   For now, do it the same way as the PReP. */
+		err = call_rtas("system-reboot", 0, 1, NULL);
+		printk("RTAS system-reboot returned %d\n", err);
+		for (;;);
+#endif
+	case _MACH_prep:
 		_disable_interrupts();
 		
 		/* set exception prefix high - to the prom */
@@ -160,25 +104,29 @@
 		while ( i != 0 ) i++;
 		panic("restart failed\n");
 		break;
-
-	case _MACH_chrp:
-		openpic_init_processor(1<<0);
-		break;
 	}
 }
 
 void machine_power_off(void)
 {
-	struct cuda_request req;
+	struct adb_request req;
+#if 0	
+	int err;
+#endif	
 
-	if ( _machine == _MACH_Pmac )
-	{
+	switch (_machine) {
+	case _MACH_Pmac:
 		cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN);
 		for (;;)
 			cuda_poll();
-	}
-	else /* prep or chrp */
-	{
+	case _MACH_chrp:
+#if 0		/* RTAS doesn't seem to work on Longtrail.
+		   For now, do it the same way as the PReP. */
+		err = call_rtas("power-off", 2, 1, NULL, 0, 0);
+		printk("RTAS system-reboot returned %d\n", err);
+		for (;;);
+#endif
+	case _MACH_prep:
 		machine_restart(NULL);
 	}
 }
@@ -202,38 +150,328 @@
 {
 	if ( _machine == _MACH_Pmac )
 		pmac_ide_init_hwif_ports(p,base,irq);
-	else /* prep */
+	else /* prep or chrp */
 		prep_ide_init_hwif_ports(p,base,irq);
 	
 }
 
-/*
- * Will merge more into here later  -- Cort
- */
 int get_cpuinfo(char *buffer)
 {
 	extern int pmac_get_cpuinfo(char *);
 	extern int chrp_get_cpuinfo(char *);	
 	extern int prep_get_cpuinfo(char *);
+	unsigned long len = 0;
+	unsigned long bogosum = 0;
+	unsigned long i;
+#ifdef __SMP__
+	extern unsigned long cpu_present_map;	
+	extern struct cpuinfo_PPC cpu_data[NR_CPUS];
+#define GET_PVR ((long int)(cpu_data[i].pvr))
+#define CD(x) (cpu_data[i].x)
+#else
+#define cpu_present_map 1L
+#define smp_num_cpus 1
+#define GET_PVR ((long int)_get_PVR())
+#define CD(x) (x)
+#endif	
+
+	for ( i = 0; i < smp_num_cpus ; i++ )
+	{
+		if ( ! ( cpu_present_map & (1<<i) ) )
+			continue;
+		if ( i )
+			len += sprintf(len+buffer,"\n");
+		len += sprintf(len+buffer,"processor\t: %lu\n",i);
+		len += sprintf(len+buffer,"cpu\t\t: ");
 	
+		switch (GET_PVR >> 16)
+		{
+		case 1:
+			len += sprintf(len+buffer, "601\n");
+			break;
+		case 3:
+			len += sprintf(len+buffer, "603\n");
+			break;
+		case 4:
+			len += sprintf(len+buffer, "604\n");
+			break;
+		case 6:
+			len += sprintf(len+buffer, "603e\n");
+			break;
+		case 7:
+			len += sprintf(len+buffer, "603ev\n");
+			break;
+		case 8:
+			len += sprintf(len+buffer, "750 (Arthur)\n");
+			break;
+		case 9:
+			len += sprintf(len+buffer, "604e\n");
+			break;
+		case 10:
+			len += sprintf(len+buffer, "604ev5 (MachV)\n");
+			break;
+		default:
+			len += sprintf(len+buffer, "unknown (%lu)\n",
+				       GET_PVR>>16);
+			break;
+		}
+
+		/*
+		 * Assume here that all clock rates are the same in a
+		 * smp system.  -- Cort
+		 */
+		if ( have_of )
+		{
+			struct device_node *cpu_node;
+			int *fp;
+			
+			cpu_node = find_type_devices("cpu");
+			if ( !cpu_node ) break;
+			fp = (int *) get_property(cpu_node, "clock-frequency", NULL);
+			if ( !fp ) break;
+			len += sprintf(len+buffer, "clock\t\t: %dMHz\n",
+				       *fp / 1000000);
+		}
+		
+		/* PREP's without residual data for some reason will give
+		   incorrect values here */
+		if ( is_prep )
+		{
+			len += sprintf(len+buffer, "clock\t\t: ");
+			if ( res.ResidualLength )
+				len += sprintf(len+buffer, "%ldMHz\n",
+				       (res.VitalProductData.ProcessorHz > 1024) ?
+				       res.VitalProductData.ProcessorHz>>20 :
+				       res.VitalProductData.ProcessorHz);
+			else
+				len += sprintf(len+buffer, "???\n");
+		}				
+		
+		len += sprintf(len+buffer, "revision\t: %ld.%ld\n",
+			       (GET_PVR & 0xff00) >> 8, GET_PVR & 0xff);
+
+		len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n",
+			       (CD(loops_per_sec)+2500)/500000,
+			       (CD(loops_per_sec)+2500)/5000 % 100);
+		bogosum += CD(loops_per_sec);
+	}
+
+#ifdef __SMP__
+	if ( i )
+		len += sprintf(buffer+len, "\n");
+	len += sprintf(buffer+len,"total bogomips\t: %lu.%02lu\n",
+	       (bogosum+2500)/500000,
+	       (bogosum+2500)/5000 % 100);
+#endif /* __SMP__ */
+
+	/*
+	 * Ooh's and aah's info about zero'd pages in idle task
+	 */ 
+	{
+		extern unsigned int zerocount, zerototal, zeropage_hits,zeropage_calls;
+		len += sprintf(buffer+len,"zero pages\t: total %u (%luKb) "
+			       "current: %u (%luKb) hits: %u/%u (%u%%)\n",
+			       zerototal, (zerototal*PAGE_SIZE)>>10,
+			       zerocount, (zerocount*PAGE_SIZE)>>10,
+			       zeropage_hits,zeropage_calls,
+			       /* : 1 below is so we don't div by zero */
+			       (zeropage_hits*100) /
+			            ((zeropage_calls)?zeropage_calls:1));
+	}
 
 	switch (_machine)
 	{
 	case _MACH_Pmac:
-		return pmac_get_cpuinfo(buffer);
+		len += pmac_get_cpuinfo(buffer+len);
 		break;
-	case _MACH_Motorola:
-	case _MACH_IBM:
-		return prep_get_cpuinfo(buffer);
+	case _MACH_prep:
+		len += prep_get_cpuinfo(buffer+len);
 		break;
 	case _MACH_chrp:
-		return chrp_get_cpuinfo(buffer);
+		len += chrp_get_cpuinfo(buffer+len);
 		break;
 	}
-	printk("Unknown machine %d in get_cpuinfo()\n",_machine);
-	return 0;
+	return len;
 }
 
+/*
+ * Find out what kind of machine we're on and save any data we need
+ * from the early boot process (devtree is copied on pmac by prom_init() )
+ */
+__initfunc(unsigned long
+identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
+		 unsigned long r6, unsigned long r7))
+{
+	extern unsigned long initrd_start, initrd_end;
+	extern setup_pci_ptrs(void);
+	unsigned long boot_sdr1;
+	ihandle prom_root;
+	unsigned char type[16], model[16];
+
+	asm("mfspr %0,25\n\t" :"=r" (boot_sdr1));
+
+	/* 
+	 * if we have a sdr1 then we have openfirmware 
+	 * and can ask it what machine we are (chrp/pmac/prep).
+	 * otherwise we're definitely prep. -- Cort
+	 */
+	if ( !boot_sdr1 )
+	{
+		/* we know for certain we're prep if no OF */
+		have_of = 0;
+		/* make a copy of residual data */
+		if ( r3 )
+			memcpy((void *)&res,(void *)(r3+KERNELBASE),
+			       sizeof(RESIDUAL));
+#ifndef CONFIG_MACH_SPECIFIC
+		_machine = _MACH_prep;
+#endif /* CONFIG_MACH_SPECIFIC */
+	}
+	else
+	{
+		/*
+		 * init prom here, then ask the openfirmware
+		 * what machine we are (prep/chrp/pmac).  We don't use
+		 * OF on prep just yet.  -- Cort
+		 */
+#ifndef CONFIG_PREP		/* don't use OF on prep yet */
+		have_of = 1;
+		/* prom_init has already been called from __start */
+		finish_device_tree();
+
+		/*
+		 * If we were booted via quik, r3 points to the physical
+		 * address of the command-line parameters.
+		 * If we were booted from an xcoff image (i.e. netbooted or
+		 * booted from floppy), we get the command line from the
+		 * bootargs property of the /chosen node.
+		 * If an initial ramdisk is present, r3 and r4
+		 * are used for initrd_start and initrd_size,
+		 * otherwise they contain 0xdeadbeef.  
+		 */
+		cmd_line[0] = 0;
+		if (r3 >= 0x4000 && r3 < 0x800000 && r4 == 0) {
+			strncpy(cmd_line, (char *)r3 + KERNELBASE,
+				sizeof(cmd_line));
+		} else {
+			struct device_node *chosen;
+			char *p;
+
+#ifdef CONFIG_BLK_DEV_INITRD
+			if (r3 - KERNELBASE < 0x800000
+			    && r4 != 0 && r4 != 0xdeadbeef) {
+				initrd_start = r3;
+				initrd_end = r3 + r4;
+				ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);
+			}
+#endif
+			chosen = find_path_device("/chosen");
+			if (chosen != NULL) {
+				p = get_property(chosen, "bootargs", NULL);
+				if (p != NULL)
+					strncpy(cmd_line, p, sizeof(cmd_line));
+			}
+		}
+		cmd_line[sizeof(cmd_line) - 1] = 0;
+#endif /* CONFIG_PREP */
+
+#ifndef CONFIG_MACH_SPECIFIC
+#if 0
+		prom_root = call_prom("finddevice", 1, 1, "/");		
+		call_prom("getprop", 4, 1, prom_root, "device_type", &type,
+			  (void *) sizeof(type));
+		call_prom("getprop", 4, 1, prom_root, "model", &type,
+			  (void *) sizeof(model));
+		if ( !strncmp("chrp", type,4) )
+		{
+			_machine = _MACH_chrp;
+		}
+		else 
+		{
+		        /*if ( !strncmp("Power Macintosh", type,15) )*/
+			_machine = _MACH_Pmac;
+                }
+#else
+
+#ifdef CONFIG_CHRP		
+		_machine = _MACH_chrp;
+#endif /* CONFIG_CHRP */
+#ifdef CONFIG_PMAC		
+		_machine = _MACH_Pmac;
+#endif /* CONFIG_PMAC */
+#ifdef CONFIG_PREP
+		_machine = _MACH_Prep;
+#endif /* CONFIG_PREP */
+#endif /* #if */		
+#endif /* CONFIG_MACH_SPECIFIC */
+	}
+
+	/* so that pmac/chrp can use pci to find its console -- Cort */
+	setup_pci_ptrs();
+	
+	switch (_machine)
+	{
+	case _MACH_Pmac:
+#if !defined(CONFIG_MACH_SPECIFIC)
+		isa_io_base = PMAC_ISA_IO_BASE;
+		isa_mem_base = PMAC_ISA_MEM_BASE;
+		pci_dram_offset = PMAC_PCI_DRAM_OFFSET;
+		ISA_DMA_THRESHOLD = ~0L;
+		DMA_MODE_READ = 1;
+		DMA_MODE_WRITE = 2;
+#endif /* ! CONFIG_MACH_SPECIFIC */
+		break;
+	case _MACH_prep:
+#if !defined(CONFIG_MACH_SPECIFIC)
+		isa_io_base = PREP_ISA_IO_BASE;
+		isa_mem_base = PREP_ISA_MEM_BASE;
+		pci_dram_offset = PREP_PCI_DRAM_OFFSET;
+		ISA_DMA_THRESHOLD = 0x00ffffff;
+		DMA_MODE_READ = 0x44;
+		DMA_MODE_WRITE = 0x48;
+#endif /* ! CONFIG_MACH_SPECIFIC */
+		/* figure out what kind of prep workstation we are */
+		if ( res.ResidualLength != 0 )
+		{
+			if ( !strncmp(res.VitalProductData.PrintableModel,"IBM",3) )
+				_prep_type = 0x00;
+			else
+				_prep_type = 0x01;
+		}
+		else /* assume motorola if no residual (netboot?) */
+			_prep_type = _PREP_Motorola;
+
+#ifdef CONFIG_BLK_DEV_RAM
+		/* take care of initrd if we have one */
+		if ( r4 )
+		{
+			initrd_start = r4 + KERNELBASE;
+			initrd_end = r5 + KERNELBASE;
+		}
+#endif /* CONFIG_BLK_DEV_RAM */
+		/* take care of cmd line */
+		if ( r6 )
+		{
+			*(char *)(r7+KERNELBASE) = 0;
+			strcpy(cmd_line, (char *)(r6+KERNELBASE));
+		}
+		break;
+	case _MACH_chrp:
+		/* LongTrail */
+#if !defined(CONFIG_MACH_SPECIFIC)
+		isa_io_base = CHRP_ISA_IO_BASE;
+		isa_mem_base = CHRP_ISA_MEM_BASE;
+		pci_dram_offset = CHRP_PCI_DRAM_OFFSET;
+		ISA_DMA_THRESHOLD = ~0L;
+		DMA_MODE_READ = 0x44;
+		DMA_MODE_WRITE = 0x48;
+#endif /* ! CONFIG_MACH_SPECIFIC */
+		break;
+	default:
+		printk("Unknown machine type in identify_machine!\n");
+	}
+	return 0;
+}
 
 __initfunc(unsigned long
 bios32_init(unsigned long memory_start, unsigned long memory_end))
@@ -244,25 +482,41 @@
 __initfunc(void setup_arch(char **cmdline_p,
 	unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
-	extern void pmac_setup_arch(char **, unsigned long *, unsigned long *);
-	extern void chrp_setup_arch(char **, unsigned long *, unsigned long *);
-	extern void prep_setup_arch(char **, unsigned long *, unsigned long *);
+	extern void pmac_setup_arch(unsigned long *, unsigned long *);
+	extern void chrp_setup_arch(unsigned long *, unsigned long *);
+	extern void prep_setup_arch(unsigned long *, unsigned long *);
+	extern int panic_timeout;
+	extern char _etext[], _edata[];
+	extern char *klimit;
+	extern unsigned long find_available_memory(void);
+	extern unsigned long *end_of_DRAM;
+
+	/* reboot on panic */	
+	panic_timeout = 180;
 	
-	switch (_machine)
-	{
+	init_task.mm->start_code = PAGE_OFFSET;
+	init_task.mm->end_code = (unsigned long) _etext;
+	init_task.mm->end_data = (unsigned long) _edata;
+	init_task.mm->brk = (unsigned long) klimit;	
+
+	/* Save unparsed command line copy for /proc/cmdline */
+	strcpy(saved_command_line, cmd_line);
+	*cmdline_p = cmd_line;
+
+	*memory_start_p = find_available_memory();
+	*memory_end_p = (unsigned long) end_of_DRAM;
+
+	switch (_machine) {
 	case _MACH_Pmac:
-		pmac_setup_arch(cmdline_p,memory_start_p,memory_end_p);
+		pmac_setup_arch(memory_start_p, memory_end_p);
 		break;
-	case _MACH_Motorola:
-	case _MACH_IBM:
-		prep_setup_arch(cmdline_p,memory_start_p,memory_end_p);
+	case _MACH_prep:
+		prep_setup_arch(memory_start_p, memory_end_p);
 		break;
 	case _MACH_chrp:
-		return chrp_setup_arch(cmdline_p,memory_start_p,memory_end_p);
+		chrp_setup_arch(memory_start_p, memory_end_p);
 		break;
+	default:
+		printk("Unknown machine %d in setup_arch()\n", _machine);
 	}
-	printk("Unknown machine %d in setup_arch()\n",_machine);
 }
-
-
-
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/signal.c linux/arch/ppc/kernel/signal.c
--- v2.1.78/linux/arch/ppc/kernel/signal.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/signal.c	Mon Jan 12 15:18:13 1998
@@ -6,12 +6,12 @@
  *
  *  Derived from "arch/i386/kernel/signal.c"
  *    Copyright (C) 1991, 1992 Linus Torvalds
+ *    1997-11-28  Modified for POSIX.1b signals by Richard Henderson
  *
  *  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>
@@ -24,45 +24,59 @@
 #include <linux/wait.h>
 #include <linux/ptrace.h>
 #include <linux/unistd.h>
+#include <linux/stddef.h>
 #include <linux/elf.h>
+#include <asm/ucontext.h>
 #include <asm/uaccess.h>
+#include <asm/pgtable.h>
 
-#define _S(nr) (1<<((nr)-1))
-
-#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
-
-#define DEBUG_SIGNALS
-#undef  DEBUG_SIGNALS
+#define DEBUG_SIG 0
 
-#define PAUSE_AFTER_SIGNAL
-#undef  PAUSE_AFTER_SIGNAL
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
 #ifndef MIN
 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
 #endif
 
-asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
+#define GP_REGS_SIZE	MIN(sizeof(elf_gregset_t), sizeof(struct pt_regs))
+
+/* 
+ * These are the flags in the MSR that the user is allowed to change
+ * by modifying the saved value of the MSR on the stack.  SE and BE
+ * should not be in this list since gdb may want to change these.  I.e,
+ * you should be able to step out of a signal handler to see what
+ * instruction executes next after the signal handler completes.
+ * Alternately, if you stepped into a signal handler, you should be
+ * able to continue 'til the next breakpoint from within the signal
+ * handler, even if the handler returns.
+ */
+#define MSR_USERCHANGE	(MSR_FE0 | MSR_FE1)
+
+int do_signal(sigset_t *oldset, struct pt_regs *regs);
+extern int sys_wait4(pid_t pid, unsigned long *stat_addr,
+		     int options, unsigned long *ru);
 
 /*
- * atomically swap in the new signal mask, and wait for a signal.
+ * Atomically swap in the new signal mask, and wait for a signal.
  */
-asmlinkage int sys_sigsuspend(unsigned long set, int p2, int p3, int p4, int p6, int p7, struct pt_regs *regs)
+int
+sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7,
+	       struct pt_regs *regs)
 {
-	unsigned long mask;
+	sigset_t saveset;
 
+	mask &= _BLOCKABLE;
 	spin_lock_irq(&current->sigmask_lock);
-	mask = current->blocked;
-	current->blocked = set & _BLOCKABLE;
+	saveset = current->blocked;
+	siginitset(&current->blocked, mask);
+	recalc_sigpending(current);
 	spin_unlock_irq(&current->sigmask_lock);
 
 	regs->gpr[3] = -EINTR;
-#ifdef DEBUG_SIGNALS
-printk("Task: %x[%d] - SIGSUSPEND at %x, Mask: %x\n", current, current->pid, regs->nip, set);	
-#endif
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(mask,regs)) {
+		if (do_signal(&saveset, regs))
 			/*
 			 * If a signal handler needs to be called,
 			 * do_signal() has set R3 to the signal number (the
@@ -72,64 +86,134 @@
 			 * R3, so it's still set to -EINTR (see above).
 			 */
 			return regs->gpr[3];
-		}
 	}
 }
 
-/* 
- * These are the flags in the MSR that the user is allowed to change
- * by modifying the saved value of the MSR on the stack.  SE and BE
- * should not be in this list since gdb may want to change these.  I.e,
- * you should be able to step out of a signal handler to see what
- * instruction executes next after the signal handler completes.
- * Alternately, if you stepped into a signal handler, you should be
- * able to continue 'til the next breakpoint from within the signal
- * handler, even if the handler returns.
+int
+sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, int p3, int p4, int p6,
+		  int p7, struct pt_regs *regs)
+{
+	sigset_t saveset, newset;
+
+	/* XXX: Don't preclude handling different sized sigset_t's.  */
+	if (sigsetsize != sizeof(sigset_t))
+		return -EINVAL;
+
+	if (copy_from_user(&newset, unewset, sizeof(newset)))
+		return -EFAULT;
+	sigdelsetmask(&newset, ~_BLOCKABLE);
+
+	spin_lock_irq(&current->sigmask_lock);
+	saveset = current->blocked;
+	current->blocked = newset;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+
+	regs->gpr[3] = -EINTR;
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		if (do_signal(&saveset, regs))
+			return regs->gpr[3];
+	}
+}
+
+int 
+sys_sigaction(int sig, const struct old_sigaction *act,
+	      struct old_sigaction *oact)
+{
+	struct k_sigaction new_ka, old_ka;
+	int ret;
+
+	if (act) {
+		old_sigset_t mask;
+		if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
+		    __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+		    __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+			return -EFAULT;
+		__get_user(new_ka.sa.sa_flags, &act->sa_flags);
+		__get_user(mask, &act->sa_mask);
+		siginitset(&new_ka.sa.sa_mask, mask);
+	}
+
+	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+	if (!ret && oact) {
+		if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
+		    __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+		    __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+			return -EFAULT;
+		__put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+		__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+	}
+
+	return ret;
+}
+
+/*
+ * When we have signals to deliver, we set up on the
+ * user stack, going down from the original stack pointer:
+ *	a sigregs struct
+ *	one or more sigcontext structs
+ *	a gap of __SIGNAL_FRAMESIZE bytes
+ *
+ * Each of these things must be a multiple of 16 bytes in size.
+ *
+ * XXX ultimately we will have to stack up a siginfo and ucontext
+ * for each rt signal.
  */
-#define MSR_USERCHANGE	(MSR_FE0 | MSR_FE1)
+struct sigregs {
+	elf_gregset_t	gp_regs;
+	double		fp_regs[ELF_NFPREG];
+	unsigned long	tramp[2];
+	/* Programs using the rs6000/xcoff abi can save up to 19 gp regs
+	   and 18 fp regs below sp before decrementing it. */
+	int		abigap[56];
+};
 
 /*
- * This sets regs->esp even though we don't actually use sigstacks yet..
+ * Do a signal return; undo the signal stack.
  */
-asmlinkage int sys_sigreturn(struct pt_regs *regs)
+int sys_sigreturn(struct pt_regs *regs)
 {
 	struct sigcontext_struct *sc, sigctx;
+	struct sigregs *sr;
 	int ret;
 	elf_gregset_t saved_regs;  /* an array of ELF_NGREG unsigned longs */
+	sigset_t set;
+	unsigned long prevsp;
 
 	sc = (struct sigcontext_struct *)(regs->gpr[1] + __SIGNAL_FRAMESIZE);
 	if (copy_from_user(&sigctx, sc, sizeof(sigctx)))
 		goto badframe;
-	current->blocked = sigctx.oldmask & _BLOCKABLE;
-	sc++;			/* Pop signal 'context' */
-#ifdef DEBUG_SIGNALS
-	printk("Sig return - Regs: %p, sc: %p, sig: %d\n", sigctx.regs, sc,
-	       sigctx.signal);
+
+	set.sig[0] = sigctx.oldmask;
+#if _NSIG_WORDS > 1
+	set.sig[1] = sigctx._unused[3];
 #endif
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+
+	sc++;			/* Look at next sigcontext */
 	if (sc == (struct sigcontext_struct *)(sigctx.regs)) {
 		/* Last stacked signal - restore registers */
+		sr = (struct sigregs *) sigctx.regs;
 		if (last_task_used_math == current)
 			giveup_fpu();
-		if (copy_from_user(saved_regs, sigctx.regs, sizeof(saved_regs)))
+		if (copy_from_user(saved_regs, &sr->gp_regs,
+				   sizeof(sr->gp_regs)))
 			goto badframe;
 		saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE)
 			| (saved_regs[PT_MSR] & MSR_USERCHANGE);
-		memcpy(regs, saved_regs, 
-		       MIN(sizeof(elf_gregset_t),sizeof(struct pt_regs)));
+		memcpy(regs, saved_regs, GP_REGS_SIZE);
 
-		if (copy_from_user(current->tss.fpr,
-				   (unsigned long *)sigctx.regs + ELF_NGREG,
-				   ELF_NFPREG * sizeof(double)))
+		if (copy_from_user(current->tss.fpr, &sr->fp_regs,
+				   sizeof(sr->fp_regs)))
 			goto badframe;
 
-		if (regs->trap == 0x0C00 /* System Call! */ &&
-		    ((int)regs->result == -ERESTARTNOHAND ||
-		     (int)regs->result == -ERESTARTSYS ||
-		     (int)regs->result == -ERESTARTNOINTR)) {
-			regs->gpr[3] = regs->orig_gpr3;
-			regs->nip -= 4; /* Back up & retry system call */
-			regs->result = 0;
-		}
 		ret = regs->result;
 
 	} else {
@@ -137,87 +221,193 @@
 		regs->gpr[1] = (unsigned long)sc - __SIGNAL_FRAMESIZE;
 		if (copy_from_user(&sigctx, sc, sizeof(sigctx)))
 			goto badframe;
+		sr = (struct sigregs *) sigctx.regs;
 		regs->gpr[3] = ret = sigctx.signal;
-		regs->gpr[4] = (unsigned long) sigctx.regs;
-		regs->link = regs->gpr[4] + ELF_NGREG * sizeof(unsigned long)
-			+ ELF_NFPREG * sizeof(double);
+		regs->gpr[4] = (unsigned long) sr;
+		regs->link = (unsigned long) &sr->tramp;
 		regs->nip = sigctx.handler;
+
+		if (get_user(prevsp, &sr->gp_regs[PT_R1])
+		    || put_user(prevsp, (unsigned long *) regs->gpr[1]))
+			goto badframe;
 	}
 	return ret;
 
 badframe:
 	lock_kernel();
 	do_exit(SIGSEGV);
-	unlock_kernel();
-	return -EFAULT;
+}	
+
+/*
+ * Set up a signal frame.
+ */
+static void
+setup_frame(struct pt_regs *regs, struct sigregs *frame,
+	    unsigned long newsp)
+{
+	struct sigcontext_struct *sc = (struct sigcontext_struct *) newsp;
+
+	if (verify_area(VERIFY_WRITE, frame, sizeof(*frame)))
+		goto badframe;
+	if (last_task_used_math == current)
+		giveup_fpu();
+	if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE)
+	    || __copy_to_user(&frame->fp_regs, current->tss.fpr,
+			      ELF_NFPREG * sizeof(double))
+	    || __put_user(0x38007777UL, &frame->tramp[0])    /* li r0,0x7777 */
+	    || __put_user(0x44000002UL, &frame->tramp[1]))   /* sc */
+		goto badframe;
+	flush_icache_range((unsigned long) &frame->tramp[0],
+			   (unsigned long) &frame->tramp[2]);
+
+	newsp -= __SIGNAL_FRAMESIZE;
+	if (put_user(regs->gpr[1], (unsigned long *)newsp)
+	    || get_user(regs->nip, &sc->handler)
+	    || get_user(regs->gpr[3], &sc->signal))
+		goto badframe;
+	regs->gpr[1] = newsp;
+	regs->gpr[4] = (unsigned long) frame;
+	regs->link = (unsigned long) frame->tramp;
+
+	return;
+
+badframe:
+#if DEBUG_SIG
+	printk("badframe in setup_frame, regs=%p frame=%p newsp=%lx\n",
+	       regs, frame, newsp);
+#endif
+	lock_kernel();
+	do_exit(SIGSEGV);
 }
 
+/*
+ * OK, we're invoking a handler
+ */
+static void
+handle_signal(unsigned long sig, struct k_sigaction *ka,
+	      siginfo_t *info, sigset_t *oldset, struct pt_regs * regs,
+	      unsigned long *newspp, unsigned long frame)
+{
+	struct sigcontext_struct *sc;
+
+	if (regs->trap == 0x0C00 /* System Call! */
+	    && ((int)regs->result == -ERESTARTNOHAND ||
+		((int)regs->result == -ERESTARTSYS &&
+		 !(ka->sa.sa_flags & SA_RESTART))))
+		regs->result = -EINTR;
+
+	/* Put another sigcontext on the stack */
+	*newspp -= sizeof(*sc);
+	sc = (struct sigcontext_struct *) *newspp;
+	if (verify_area(VERIFY_WRITE, sc, sizeof(*sc)))
+		goto badframe;
+
+	if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler)
+	    || __put_user(oldset->sig[0], &sc->oldmask)
+#if _NSIG_WORDS > 1
+	    || __put_user(oldset->sig[1], &sc->_unused[3])
+#endif
+	    || __put_user((struct pt_regs *)frame, &sc->regs)
+	    || __put_user(sig, &sc->signal))
+		goto badframe;
+
+	if (ka->sa.sa_flags & SA_ONESHOT)
+		ka->sa.sa_handler = SIG_DFL;
+
+	if (!(ka->sa.sa_flags & SA_NODEFER)) {
+		spin_lock_irq(&current->sigmask_lock);
+		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+		sigaddset(&current->blocked,sig);
+		recalc_sigpending(current);
+		spin_unlock_irq(&current->sigmask_lock);
+	}
+	return;
+
+badframe:
+#if DEBUG_SIG
+	printk("badframe in handle_signal, regs=%p frame=%lx newsp=%lx\n",
+	       regs, frame, *newspp);
+	printk("sc=%p sig=%d ka=%p info=%p oldset=%p\n", sc, sig, ka, info, oldset);
+#endif
+	lock_kernel();
+	do_exit(SIGSEGV);
+}
 
 /*
  * Note that 'init' is a special process: it doesn't get signals it doesn't
  * want to handle. Thus you cannot kill init even with a SIGKILL even by
  * mistake.
- *
- * Note that we go through the signals twice: once to check the signals that
- * the kernel can handle, and then we build all the user-level signal handling
- * stack-frames in one go after that.
  */
-asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
+int do_signal(sigset_t *oldset, struct pt_regs *regs)
 {
-	unsigned long mask;
-	unsigned long handler_signal = 0;
-	unsigned long *frame = NULL;
-	unsigned long *trampoline;
-	unsigned long *regs_ptr;
-	double *fpregs_ptr;
-	unsigned long nip = 0;
-	unsigned long signr;
-	struct sigcontext_struct *sc;
-	struct sigaction * sa;
-	int bitno;
+	siginfo_t info;
+	struct k_sigaction *ka;
+	unsigned long frame, newsp;
+
+	if (!oldset)
+		oldset = &current->blocked;
+
+	newsp = frame = regs->gpr[1] - sizeof(struct sigregs);
+
+	for (;;) {
+		unsigned long signr;
+
+		spin_lock_irq(&current->sigmask_lock);
+		signr = dequeue_signal(&current->blocked, &info);
+		spin_unlock_irq(&current->sigmask_lock);
+
+		if (!signr)
+			break;
 
-	mask = ~current->blocked;
-	while ((signr = current->signal & mask)) {
-#if 0
-		signr = ffz(~signr);  /* Compute bit # */
-#else
-		for (bitno = 0;  bitno < 32;  bitno++)
-			if (signr & (1<<bitno))
-				break;
-		signr = bitno;
-#endif
-		current->signal &= ~(1<<signr);  /* Clear bit */
-		sa = current->sig->action + signr;
-		signr++;
 		if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
+			/* Let the debugger run.  */
 			current->exit_code = signr;
 			current->state = TASK_STOPPED;
 			notify_parent(current, SIGCHLD);
 			schedule();
+
+			/* We're back.  Did the debugger cancel the sig?  */
 			if (!(signr = current->exit_code))
 				continue;
 			current->exit_code = 0;
+
+			/* The debugger continued.  Ignore SIGSTOP.  */
 			if (signr == SIGSTOP)
 				continue;
-			if (_S(signr) & current->blocked) {
-				current->signal |= _S(signr);
-				spin_lock_irq(&current->sigmask_lock);
-				spin_unlock_irq(&current->sigmask_lock);
+
+			/* Update the siginfo structure.  Is this good?  */
+			if (signr != info.si_signo) {
+				info.si_signo = signr;
+				info.si_errno = 0;
+				info.si_code = SI_USER;
+				info.si_pid = current->p_pptr->pid;
+				info.si_uid = current->p_pptr->uid;
+			}
+
+			/* If the (new) signal is now blocked, requeue it.  */
+			if (sigismember(&current->blocked, signr)) {
+				send_sig_info(signr, &info, current);
 				continue;
 			}
-			sa = current->sig->action + signr - 1;
 		}
-		if (sa->sa_handler == SIG_IGN) {
+
+		ka = &current->sig->action[signr-1];
+		if (ka->sa.sa_handler == SIG_IGN) {
 			if (signr != SIGCHLD)
 				continue;
-			/* check for SIGCHLD: it's special */
-			while (sys_waitpid(-1,NULL,WNOHANG) > 0)
+			/* Check for SIGCHLD: it's special.  */
+			while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)
 				/* nothing */;
 			continue;
 		}
-		if (sa->sa_handler == SIG_DFL) {
+
+		if (ka->sa.sa_handler == SIG_DFL) {
+			int exit_code = signr;
+
+			/* Init gets no signals it doesn't want.  */
 			if (current->pid == 1)
 				continue;
+
 			switch (signr) {
 			case SIGCONT: case SIGCHLD: case SIGWINCH:
 				continue;
@@ -225,49 +415,37 @@
 			case SIGTSTP: case SIGTTIN: case SIGTTOU:
 				if (is_orphaned_pgrp(current->pgrp))
 					continue;
+				/* FALLTHRU */
+
 			case SIGSTOP:
-				if (current->flags & PF_PTRACED)
-					continue;
 				current->state = TASK_STOPPED;
 				current->exit_code = signr;
-				if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags &
-						SA_NOCLDSTOP))
+				if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP))
 					notify_parent(current, SIGCHLD);
 				schedule();
 				continue;
 
 			case SIGQUIT: case SIGILL: case SIGTRAP:
-			case SIGIOT: case SIGFPE: case SIGSEGV:
+			case SIGABRT: case SIGFPE: case SIGSEGV:
 				lock_kernel();
-				if (current->binfmt && current->binfmt->core_dump) {
-					if (current->binfmt->core_dump(signr, regs))
-						signr |= 0x80;
-				}
+				if (current->binfmt
+				    && current->binfmt->core_dump
+				    && current->binfmt->core_dump(signr, regs))
+					exit_code |= 0x80;
 				unlock_kernel();
-				/* fall through */
-			default:
-				spin_lock_irq(&current->sigmask_lock);
-				current->signal |= _S(signr & 0x7f);
-				spin_unlock_irq(&current->sigmask_lock);
+				/* FALLTHRU */
 
+			default:
+				lock_kernel();
+				sigaddset(&current->signal, signr);
 				current->flags |= PF_SIGNALED;
-
-				lock_kernel(); /* 8-( */
-				do_exit(signr);
-				unlock_kernel();
+				do_exit(exit_code);
+				/* NOTREACHED */
 			}
 		}
-		/*
-		 * OK, we're invoking a handler
-		 */
-		if (regs->trap == 0x0C00 /* System Call! */) {
-			if ((int)regs->result == -ERESTARTNOHAND ||
-			    ((int)regs->result == -ERESTARTSYS &&
-			     !(sa->sa_flags & SA_RESTART)))
-				(int)regs->result = -EINTR;
-		}
-		handler_signal |= 1 << (signr-1);
-		mask &= ~sa->sa_mask;
+
+		/* Whee!  Actually deliver the signal.  */
+		handle_signal(signr, ka, &info, oldset, regs, &newsp, frame);
 	}
 
 	if (regs->trap == 0x0C00 /* System Call! */ &&
@@ -275,91 +453,13 @@
 	     (int)regs->result == -ERESTARTSYS ||
 	     (int)regs->result == -ERESTARTNOINTR)) {
 		regs->gpr[3] = regs->orig_gpr3;
-		regs->nip -= 4; /* Back up & retry system call */
+		regs->nip -= 4;		/* Back up & retry system call */
 		regs->result = 0;
 	}
 
-	if (!handler_signal)	/* no handler will be called - return 0 */
-		return 0;
-
-	nip = regs->nip;
-	frame = (unsigned long *) regs->gpr[1];
-
-	/*
-	 * Build trampoline code on stack, and save gp and fp regs.
-	 * The 56 word hole is because programs using the rs6000/xcoff
-	 * style calling sequence can save up to 19 gp regs and 18 fp regs
-	 * on the stack before decrementing sp.
-	 */
-	frame -= 2 + 56;
-	trampoline = frame;
-	frame -= ELF_NFPREG * sizeof(double) / sizeof(unsigned long);
-	fpregs_ptr = (double *) frame;
-	frame -= ELF_NGREG;
-	regs_ptr = frame;
-	/* verify stack is valid for writing to */
-	if (verify_area(VERIFY_WRITE, frame,
-			(ELF_NGREG + 2) * sizeof(long)
-			+ ELF_NFPREG * sizeof(double)))
-		goto badframe;
-	if (last_task_used_math == current)
-		giveup_fpu();
-	if (__copy_to_user(regs_ptr, regs, 
-	                   MIN(sizeof(elf_gregset_t),sizeof(struct pt_regs)))
-	    || __copy_to_user(fpregs_ptr, current->tss.fpr,
-			      ELF_NFPREG * sizeof(double))
-	    || __put_user(0x38007777UL, trampoline)	/* li r0,0x7777 */
-	    || __put_user(0x44000002UL, trampoline+1))	/* sc           */
-		goto badframe;
-
-	signr = 1;
-	sa = current->sig->action;
-
-	for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
-		if (mask > handler_signal)
-			break;
-		if (!(mask & handler_signal))
-			continue;
+	if (newsp == frame)
+		return 0;		/* no signals delivered */
 
-		frame -= sizeof(struct sigcontext_struct) / sizeof(long);
-		if (verify_area(VERIFY_WRITE, frame,
-				sizeof(struct sigcontext_struct)))
-			goto badframe;
-		sc = (struct sigcontext_struct *)frame;
-		nip = (unsigned long) sa->sa_handler;
-		if (sa->sa_flags & SA_ONESHOT)
-			sa->sa_handler = NULL;
-		if (__put_user(nip, &sc->handler)
-		    || __put_user(oldmask, &sc->oldmask)
-		    || __put_user(regs_ptr, &sc->regs)
-		    || __put_user(signr, &sc->signal))
-			goto badframe;
-		current->blocked |= sa->sa_mask;
-		regs->gpr[3] = signr;
-		regs->gpr[4] = (unsigned long) regs_ptr;
-	}
-
-	frame -= __SIGNAL_FRAMESIZE / sizeof(unsigned long);
-	if (put_user(regs->gpr[1], frame))
-		goto badframe;
-	regs->link = (unsigned long)trampoline;
-	regs->nip = nip;
-	regs->gpr[1] = (unsigned long) frame;
-
-	/* The DATA cache must be flushed here to insure coherency */
-	/* between the DATA & INSTRUCTION caches.  Since we just */
-	/* created an instruction stream using the DATA [cache] space */
-	/* and since the instruction cache will not look in the DATA */
-	/* cache for new data, we have to force the data to go on to */
-	/* memory and flush the instruction cache to force it to look */
-	/* there.  The following function performs this magic */
-	flush_icache_range((unsigned long) trampoline,
-			   (unsigned long) (trampoline + 2));
+	setup_frame(regs, (struct sigregs *) frame, newsp);
 	return 1;
-
-badframe:
-	lock_kernel();
-	do_exit(SIGSEGV);
-	unlock_kernel();
-	return 0;
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/smp.c linux/arch/ppc/kernel/smp.c
--- v2.1.78/linux/arch/ppc/kernel/smp.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/kernel/smp.c	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,181 @@
+/*
+ * $Id: smp.c,v 1.8 1998/01/06 06:44:57 cort Exp $
+ *
+ * Smp support for ppc.
+ *
+ * Written by Cort Dougan (cort@cs.nmt.edu) borrowing a great
+ * deal of code from the sparc and intel versions.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/tasks.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/spinlock.h>
+#include <asm/hardirq.h>
+#include <asm/softirq.h>
+#include <asm/init.h>
+#include <asm/io.h>
+
+int smp_threads_ready = 0;
+volatile int smp_commenced = 0;
+int smp_num_cpus = 1;
+unsigned long cpu_present_map = 0;
+volatile int cpu_number_map[NR_CPUS];
+volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};
+static unsigned char boot_cpu_id = 0;
+struct cpuinfo_PPC cpu_data[NR_CPUS];
+struct klock_info klock_info = { KLOCK_CLEAR, 0 };
+volatile unsigned char active_kernel_processor = NO_PROC_ID;	/* Processor holding kernel spinlock		*/
+
+volatile unsigned long hash_table_lock;
+
+int start_secondary(void *);
+
+extern void init_IRQ(void);
+extern int cpu_idle(void *unused);
+
+void smp_boot_cpus(void)
+{
+	extern unsigned long secondary_entry[];
+	extern struct task_struct *current_set[NR_CPUS];
+	int i, timeout;
+	struct task_struct *p;
+	
+        printk("Entering SMP Mode...\n");
+	cpu_present_map = 0;
+	for(i=0; i < NR_CPUS; i++)
+		cpu_number_map[i] = -1;
+
+        smp_store_cpu_info(boot_cpu_id);
+        active_kernel_processor = boot_cpu_id;
+	current->processor = boot_cpu_id;
+	
+        cpu_present_map |= 1;
+        /* assume a 2nd processor for now */	
+        cpu_present_map |= (1 << 1); 
+	smp_num_cpus = 2;
+        cpu_number_map[boot_cpu_id] = 0;
+	
+	/* create a process for second processor */
+        kernel_thread(start_secondary, NULL, CLONE_PID);
+        cpu_number_map[1] = 1;
+	p = task[1];
+	if ( !p )
+		panic("No idle task for secondary processor\n");
+	p->processor = 1;
+	current_set[1] = p;
+	/* setup entry point of secondary processor */
+	*(volatile unsigned long *)(0xf2800000)
+		= (unsigned long)secondary_entry-KERNELBASE;
+	/* interrupt secondary to begin executing code */
+	eieio();
+	*(volatile unsigned long *)(0xf80000c0) = 0;
+	eieio();
+	/* wait to see if the secondary made a callin (is actually up) */
+	for ( timeout = 0; timeout < 1500 ; timeout++ )
+		udelay(100);
+	if(cpu_callin_map[1]) {
+		cpu_number_map[1] = 1;
+		printk("Processor 1 found.\n");
+	} else {
+		smp_num_cpus--;
+		printk("Processor %d is stuck.\n", 1);
+	}
+{
+  extern unsigned long amhere;
+  printk("amhere: %x\n", amhere);
+}
+}
+
+void smp_commence(void)
+{
+	printk("SMP %d: smp_commence()\n",current->processor);
+	/*
+	 *	Lets the callin's below out of their loop.
+	 */
+	local_flush_tlb_all();
+	smp_commenced = 1;
+	local_flush_tlb_all();
+}
+
+/* intel needs this */
+void initialize_secondary(void)
+{
+}
+
+/* Activate a secondary processor. */
+int start_secondary(void *unused)
+{
+	printk("SMP %d: start_secondary()\n",current->processor);
+	smp_callin();
+	return cpu_idle(NULL);
+}
+
+void smp_callin(void)
+{
+	printk("SMP %d: smp_callin()\n",current->processor);
+	/*calibrate_delay();*/
+        smp_store_cpu_info(1);
+	
+	/* assume we're just the secondary processor for now */
+	cpu_callin_map[1] = 1;
+	dcbf(&cpu_callin_map[1]);
+	
+	current->mm->mmap->vm_page_prot = PAGE_SHARED;
+	current->mm->mmap->vm_start = PAGE_OFFSET;
+	current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
+
+	while(!smp_commenced)
+		barrier();
+	__sti();
+}
+
+void smp_setup(char *str, int *ints)
+{
+	printk("SMP %d: smp_setup()\n",current->processor);
+}
+
+void smp_message_pass(int target, int msg, unsigned long data, int wait)
+{
+	printk("SMP %d: sending smp message\n",current->processor);
+#if 0	
+	if ( smp_processor_id() == 0 )
+	{
+		/* interrupt secondary */
+		*(volatile unsigned long *)(0xf80000c0) = 0;
+	}
+	else
+	{
+		/* interrupt primary */
+		*(volatile unsigned long *)(0xf3019000);
+	}
+#endif	
+}
+
+int setup_profiling_timer(unsigned int multiplier)
+{
+	return 0;
+}
+
+__initfunc(void smp_store_cpu_info(int id))
+{
+        struct cpuinfo_PPC *c = &cpu_data[id];
+
+        c->loops_per_sec = loops_per_sec;
+        c->pvr = _get_PVR();
+}
+
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/syscalls.c linux/arch/ppc/kernel/syscalls.c
--- v2.1.78/linux/arch/ppc/kernel/syscalls.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/syscalls.c	Mon Jan 12 15:18:13 1998
@@ -32,6 +32,8 @@
 #include <linux/mman.h>
 #include <linux/sys.h>
 #include <linux/ipc.h>
+#include <linux/utsname.h>
+
 #include <asm/uaccess.h>
 #include <asm/ipc.h>
 
@@ -149,7 +151,7 @@
 			break;
 			}
 		case 1:	/* iBCS2 emulator entry point */
-			if (get_fs() != get_ds())
+			if (!segment_eq(get_fs(), get_ds()))
 				break;
 			ret = sys_shmat (first, (char *) ptr, second,
 					 (ulong *) third);
@@ -206,7 +208,8 @@
 		if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
 			goto out;
 	}
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+	
+	/*flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);*/
 	ret = do_mmap(file, addr, len, prot, flags, offset);
 out:
 	unlock_kernel();
@@ -236,4 +239,42 @@
 			return -EFAULT;
 	}
 	return sys_select(n, inp, outp, exp, tvp);
+}
+
+asmlinkage int sys_pause(void)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule();
+	return -ERESTARTNOHAND;
+}
+
+asmlinkage int sys_uname(struct old_utsname * name)
+{
+	if (name && !copy_to_user(name, &system_utsname, sizeof (*name)))
+		return 0;
+	return -EFAULT;
+}
+
+asmlinkage int sys_olduname(struct oldold_utsname * name)
+{
+	int error;
+
+	if (!name)
+		return -EFAULT;
+	if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname)))
+		return -EFAULT;
+  
+	error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN);
+	error -= __put_user(0,name->sysname+__OLD_UTS_LEN);
+	error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN);
+	error -= __put_user(0,name->nodename+__OLD_UTS_LEN);
+	error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN);
+	error -= __put_user(0,name->release+__OLD_UTS_LEN);
+	error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN);
+	error -= __put_user(0,name->version+__OLD_UTS_LEN);
+	error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN);
+	error = __put_user(0,name->machine+__OLD_UTS_LEN);
+	error = error ? -EFAULT : 0;
+
+	return error;
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c
--- v2.1.78/linux/arch/ppc/kernel/time.c	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/time.c	Mon Jan 12 15:18:13 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: time.c,v 1.10 1997/08/27 22:06:56 cort Exp $
+ * $Id: time.c,v 1.17 1997/12/28 22:47:21 paulus Exp $
  * Common time routines among all ppc machines.
  *
  * Written by Cort Dougan (cort@cs.nmt.edu) to merge
@@ -41,22 +41,6 @@
 unsigned count_period_num;	/* 1 decrementer count equals */
 unsigned count_period_den;	/* count_period_num / count_period_den us */
 
-/* Accessor functions for the decrementer register. */
-inline unsigned long
-get_dec(void)
-{
-	int ret;
-
-	asm volatile("mfspr %0,22" : "=r" (ret) :);
-	return ret;
-}
-
-inline void
-set_dec(int val)
-{
-	asm volatile("mtspr 22,%0" : : "r" (val));
-}
-
 /*
  * timer_interrupt - gets called when the decrementer overflows,
  * with interrupts disabled.
@@ -125,22 +109,6 @@
 void
 time_init(void)
 {
-	/* pmac hasn't yet called via_cuda_init() */
-	if ( _machine != _MACH_Pmac )
-	{
-
-		if ( _machine == _MACH_chrp )
-			xtime.tv_sec = chrp_get_rtc_time();
-		else /* assume prep */
-			xtime.tv_sec = prep_get_rtc_time();
-		xtime.tv_usec = 0;
-		/*
-		 * mark the rtc/on-chip timer as in sync
-		 * so we don't update right away
-		 */
-		last_rtc_update = xtime.tv_sec;
-	}
-
 	if ((_get_PVR() >> 16) == 1) {
 		/* 601 processor: dec counts down by 128 every 128ns */
 		decrementer_count = DECREMENTER_COUNT_601;
@@ -148,22 +116,35 @@
 		count_period_den = COUNT_PERIOD_DEN_601;
 	}
 
-	switch (_machine)
-	{
+	switch (_machine) {
 	case _MACH_Pmac:
-		pmac_calibrate_decr();
+		/* can't call pmac_get_rtc_time() yet,
+		   because via-cuda isn't initialized yet. */
+		if ((_get_PVR() >> 16) != 1)
+			pmac_calibrate_decr();
 		set_rtc_time = pmac_set_rtc_time;
 		break;
-	case _MACH_IBM:
-	case _MACH_Motorola:
-		prep_calibrate_decr();
-		set_rtc_time = prep_set_rtc_time;
-		break;
 	case _MACH_chrp:
-		chrp_calibrate_decr();
+		chrp_time_init();
+		xtime.tv_sec = chrp_get_rtc_time();
+		if ((_get_PVR() >> 16) != 1)
+			chrp_calibrate_decr();
 		set_rtc_time = chrp_set_rtc_time;
 		break;
+	case _MACH_prep:
+		xtime.tv_sec = prep_get_rtc_time();
+		prep_calibrate_decr();
+		set_rtc_time = prep_set_rtc_time;
+		break;
 	}
+	xtime.tv_usec = 0;
+
+	/*
+	 * mark the rtc/on-chip timer as in sync
+	 * so we don't update right away
+	 */
+	last_rtc_update = xtime.tv_sec;
+
 	set_dec(decrementer_count);
 }
 
@@ -180,7 +161,7 @@
 	unsigned long flags;
 	
 	save_flags(flags);
-	
+
 #define TIMER0_COUNT 0x40
 #define TIMER_CONTROL 0x43
 	/* set timer to periodic mode */
@@ -199,7 +180,7 @@
 
 void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs)
 {
-	int freq, divisor;
+	unsigned long freq, divisor;
 	static unsigned long t1 = 0, t2 = 0;
 
 	if ( !t1 )
@@ -209,9 +190,14 @@
 		t2 = get_dec();
 		t2 = t1-t2;  /* decr's in 1/HZ */
 		t2 = t2*HZ;  /* # decrs in 1s - thus in Hz */
+if ( (t2>>20) > 100 )
+{
+  printk("Decrementer frequency too high: %luMHz.  Using 15MHz.\n",t2>>20);
+  t2 = 998700000/60;
+}
 		freq = t2 * 60;	/* try to make freq/1e6 an integer */
 		divisor = 60;
-		printk("time_init: decrementer frequency = %d/%d (%luMHz)\n",
+		printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n",
 		       freq, divisor,t2>>20);
 		decrementer_count = freq / HZ / divisor;
 		count_period_num = divisor;
@@ -220,19 +206,6 @@
 	}
 }
 
-void chrp_calibrate_decr(void)
-{
-	int freq, fp, divisor;
-
-	fp = 16666000;		/* hardcoded for now */
-	freq = fp*60;	/* try to make freq/1e6 an integer */
-        divisor = 60;
-        printk("time_init: decrementer frequency = %d/%d\n", freq, divisor);
-        decrementer_count = freq / HZ / divisor;
-        count_period_num = divisor;
-        count_period_den = freq / 1000000;
-}
-
 
 /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
  * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
@@ -249,9 +222,9 @@
  * machines were long is 32-bit! (However, as time_t is signed, we
  * will already get problems at other places on 2038-01-19 03:14:08)
  */
-inline unsigned long mktime(unsigned int year, unsigned int mon,
-				   unsigned int day, unsigned int hour,
-				   unsigned int min, unsigned int sec)
+unsigned long mktime(unsigned int year, unsigned int mon,
+		     unsigned int day, unsigned int hour,
+		     unsigned int min, unsigned int sec)
 {
 	
 	if (0 >= (int) (mon -= 2)) {	/* 1..12 -> 11,12,1..10 */
@@ -266,3 +239,45 @@
 		)*60 + sec; /* finally seconds */
 }
 
+#define TICK_SIZE tick
+#define FEBRUARY	2
+#define	STARTOFTIME	1970
+#define SECDAY		86400L
+#define SECYR		(SECDAY * 365)
+#define	leapyear(year)		((year) % 4 == 0)
+#define	days_in_year(a) 	(leapyear(a) ? 366 : 365)
+#define	days_in_month(a) 	(month_days[(a) - 1])
+
+static int month_days[12] = {
+	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+void to_tm(int tim, struct rtc_time * tm)
+{
+	register int    i;
+	register long   hms, day;
+
+	day = tim / SECDAY;
+	hms = tim % SECDAY;
+
+	/* Hours, minutes, seconds are easy */
+	tm->tm_hour = hms / 3600;
+	tm->tm_min = (hms % 3600) / 60;
+	tm->tm_sec = (hms % 3600) % 60;
+
+	/* Number of years in days */
+	for (i = STARTOFTIME; day >= days_in_year(i); i++)
+		day -= days_in_year(i);
+	tm->tm_year = i;
+
+	/* Number of months in days left */
+	if (leapyear(tm->tm_year))
+		days_in_month(FEBRUARY) = 29;
+	for (i = 1; day >= days_in_month(i); i++)
+		day -= days_in_month(i);
+	days_in_month(FEBRUARY) = 28;
+	tm->tm_mon = i;
+
+	/* Days are what is left over (+1) from all that. */
+	tm->tm_mday = day + 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/time.h linux/arch/ppc/kernel/time.h
--- v2.1.78/linux/arch/ppc/kernel/time.h	Thu Sep  4 17:07:29 1997
+++ linux/arch/ppc/kernel/time.h	Mon Jan 12 15:18:13 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: time.h,v 1.5 1997/08/27 22:06:58 cort Exp $
+ * $Id: time.h,v 1.7 1997/12/28 22:47:24 paulus Exp $
  * Common time prototypes and such for all ppc machines.
  *
  * Written by Cort Dougan (cort@cs.nmt.edu) to merge
@@ -9,17 +9,16 @@
 #include <linux/mc146818rtc.h>
 
 /* time.c */
-__inline__ unsigned long get_dec(void);
-__inline__ void set_dec(int val);
 void prep_calibrate_decr_handler(int, void *,struct pt_regs *);
 void prep_calibrate_decr(void);
 void pmac_calibrate_decr(void);
-void chrp_calibrate_decr(void);
 extern unsigned decrementer_count;
 extern unsigned count_period_num;
 extern unsigned count_period_den;
 extern unsigned long mktime(unsigned int, unsigned int,unsigned int,
-				   unsigned int, unsigned int, unsigned int);
+			    unsigned int, unsigned int, unsigned int);
+extern void to_tm(int tim, struct rtc_time * tm);
+extern unsigned long last_rtc_update;
 
 /* pmac/prep/chrp_time.c */
 unsigned long prep_get_rtc_time(void);
@@ -29,46 +28,19 @@
 int pmac_set_rtc_time(unsigned long nowtime);
 int chrp_set_rtc_time(unsigned long nowtime);
 void pmac_read_rtc_time(void);
+void chrp_calibrate_decr(void);
+void chrp_time_init(void);
 
-#define TICK_SIZE tick
-#define FEBRUARY	2
-#define	STARTOFTIME	1970
-#define SECDAY		86400L
-#define SECYR		(SECDAY * 365)
-#define	leapyear(year)		((year) % 4 == 0)
-#define	days_in_year(a) 	(leapyear(a) ? 366 : 365)
-#define	days_in_month(a) 	(month_days[(a) - 1])
-
-static int      month_days[12] = {
-	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-};
-
-extern void inline to_tm(int tim, struct rtc_time * tm)
+/* Accessor functions for the decrementer register. */
+static __inline__ unsigned int get_dec(void)
 {
-	register int    i;
-	register long   hms, day;
+	unsigned int ret;
 
-	day = tim / SECDAY;
-	hms = tim % SECDAY;
+	asm volatile("mfspr %0,22" : "=r" (ret) :);
+	return ret;
+}
 
-	/* Hours, minutes, seconds are easy */
-	tm->tm_hour = hms / 3600;
-	tm->tm_min = (hms % 3600) / 60;
-	tm->tm_sec = (hms % 3600) % 60;
-
-	/* Number of years in days */
-	for (i = STARTOFTIME; day >= days_in_year(i); i++)
-		day -= days_in_year(i);
-	tm->tm_year = i;
-
-	/* Number of months in days left */
-	if (leapyear(tm->tm_year))
-		days_in_month(FEBRUARY) = 29;
-	for (i = 1; day >= days_in_month(i); i++)
-		day -= days_in_month(i);
-	days_in_month(FEBRUARY) = 28;
-	tm->tm_mon = i;
-
-	/* Days are what is left over (+1) from all that. */
-	tm->tm_mday = day + 1;
+static __inline__ void set_dec(unsigned int val)
+{
+	asm volatile("mtspr 22,%0" : : "r" (val));
 }
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/traps.c linux/arch/ppc/kernel/traps.c
--- v2.1.78/linux/arch/ppc/kernel/traps.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/traps.c	Mon Jan 12 15:18:13 1998
@@ -61,10 +61,10 @@
 	if (!user_mode(regs))
 	{
 		show_regs(regs);
-		print_backtrace((unsigned long *)regs->gpr[1]);
 #ifdef CONFIG_XMON
 		xmon(regs);
 #endif
+		print_backtrace((unsigned long *)regs->gpr[1]);
 		panic("Exception in kernel pc %lx signal %d",regs->nip,signr);
 	}
 	force_sig(signr, current);
@@ -103,10 +103,10 @@
 			printk("Unknown values in msr\n");
 		}
 		show_regs(regs);
-		print_backtrace((unsigned long *)regs->gpr[1]);
 #ifdef CONFIG_XMON
 		xmon(regs);
 #endif
+		print_backtrace((unsigned long *)regs->gpr[1]);
 		panic("machine check");
 	}
 	_exception(SIGSEGV, regs);	
@@ -186,15 +186,16 @@
 }
 
 void
-PromException(struct pt_regs *regs, int trap)
+StackOverflow(struct pt_regs *regs)
 {
-	regs->trap = trap;
+	printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n",
+	       current, regs->gpr[1]);
 #ifdef CONFIG_XMON
 	xmon(regs);
 #endif
-	printk("Exception %lx in prom at PC: %lx, SR: %lx\n",
-	       regs->trap, regs->nip, regs->msr);
-	/* probably should turn up the toes here */
+	show_regs(regs);
+	print_backtrace((unsigned long *)regs->gpr[1]);
+	panic("kernel stack overflow");
 }
 
 void
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/lib/Makefile linux/arch/ppc/lib/Makefile
--- v2.1.78/linux/arch/ppc/lib/Makefile	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/lib/Makefile	Mon Jan 12 15:18:13 1998
@@ -8,4 +8,9 @@
 O_TARGET = lib.o
 O_OBJS  = checksum.o string.o strcase.o
 
+ifdef SMP
+O_OBJS += locks.o
+endif
+
+
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/lib/checksum.S linux/arch/ppc/lib/checksum.S
--- v2.1.78/linux/arch/ppc/lib/checksum.S	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/lib/checksum.S	Mon Jan 12 15:18:13 1998
@@ -13,10 +13,11 @@
  */
 
 #include <linux/sys.h>
+#include <asm/processor.h>
 #include <asm/errno.h>
 #include "../kernel/ppc_asm.tmpl"
 
-_TEXT()
+	.text
 
 /*
  * ip_fast_csum(buf, len) -- Optimized for IP header
@@ -140,7 +141,8 @@
 5:	addze	r3,r0		/* add in final carry */
 	blr
 
-.section .fixup,"ax"
+/* These shouldn't go in the fixup section, since that would
+   cause the ex_table addresses to get out of order. */
 
 src_error_1:
 	li	r6,0
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/lib/locks.c linux/arch/ppc/lib/locks.c
--- v2.1.78/linux/arch/ppc/lib/locks.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/lib/locks.c	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,195 @@
+/*
+ * $Id: locks.c,v 1.7 1998/01/06 06:44:59 cort Exp $
+ *
+ * Locks for smp ppc 
+ * 
+ * Written by Cort Dougan (cort@cs.nmt.edu)
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/spinlock.h>
+
+#define DEBUG_LOCKS 1
+
+#undef INIT_STUCK
+#define INIT_STUCK 10000
+
+#undef STUCK
+#define STUCK \
+if(!--stuck) { printk("spin_lock(%p) CPU#%d nip %08lx\n", lock, cpu, nip); stuck = INIT_STUCK; }
+
+void _spin_lock(spinlock_t *lock)
+{
+	unsigned long val, nip = (unsigned long)__builtin_return_address(0);
+	int cpu = smp_processor_id();
+	int stuck = INIT_STUCK;
+	
+again:	
+	/* try expensive atomic load/store to get lock */
+	__asm__ __volatile__(
+		"10: \n\t"
+		"lwarx %0,0,%1 \n\t"
+		"stwcx. %2,0,%1 \n\t"
+		"bne- 10b \n\t"
+		: "=r" (val)
+		: "r" (&(lock->lock)), "r" ( (cpu&3)|(nip&~3L) ));
+	if(val) {
+		/* try cheap load until it's free */
+		while(lock->lock) {
+			STUCK;
+			barrier();
+		}
+		goto again;
+	}
+}
+
+void _spin_unlock(spinlock_t *lp)
+{
+	lp->lock = 0;
+}
+		
+#undef STUCK
+#define STUCK \
+if(!--stuck) { printk("_read_lock(%p) CPU#%d\n", rw, cpu); stuck = INIT_STUCK; }
+
+/*
+ * Just like x86, implement read-write locks as a 32-bit counter
+ * with the high bit (sign) being the "write" bit.
+ * -- Cort
+ */
+void _read_lock(rwlock_t *rw)
+{
+	unsigned long stuck = INIT_STUCK;
+	int cpu = smp_processor_id();
+
+again:	
+	/* get our read lock in there */
+	atomic_inc((atomic_t *) &(rw)->lock);
+	if ( (signed long)((rw)->lock) < 0) /* someone has a write lock */
+	{
+		/* turn off our read lock */
+		atomic_dec((atomic_t *) &(rw)->lock);
+		/* wait for the write lock to go away */
+		while ((signed long)((rw)->lock) < 0)
+		{
+			STUCK;
+		}
+		/* try to get the read lock again */
+		goto again;
+	}
+}
+
+void _read_unlock(rwlock_t *rw)
+{
+#ifdef DEBUG_LOCKS
+	if ( rw->lock == 0 )
+	{
+		if ( current)
+		printk("_read_unlock(): %s/%d (nip %08lX) lock %lx",
+		      current->comm,current->pid,current->tss.regs->nip,
+		      rw->lock);
+		else
+		  printk("no current\n");
+	}
+#endif /* DEBUG_LOCKS */
+	atomic_dec((atomic_t *) &(rw)->lock);
+}
+
+#undef STUCK
+#define STUCK \
+if(!--stuck) { printk("write_lock(%p) CPU#%d lock %lx)\n", rw, cpu,rw->lock); stuck = INIT_STUCK; }
+
+void _write_lock(rwlock_t *rw)
+{
+	unsigned long stuck = INIT_STUCK;
+	int cpu = smp_processor_id();
+
+again:	
+	if ( test_and_set_bit(31,&(rw)->lock) ) /* someone has a write lock */
+	{
+		while ( (rw)->lock & (1<<31) ) /* wait for write lock */
+		{
+			STUCK;
+		}
+		goto again;
+	}
+	
+	if ( (rw)->lock & ~(1<<31)) /* someone has a read lock */
+	{
+		/* clear our write lock and wait for reads to go away */
+		clear_bit(31,&(rw)->lock);
+		while ( (rw)->lock & ~(1<<31) )
+		{
+			STUCK;
+		}
+		goto again;
+	}
+}
+
+void _write_unlock(rwlock_t *rw)
+{
+#ifdef DEBUG_LOCKS
+	if ( !(rw->lock & (1<<31)) )
+	{
+		if ( current)
+		printk("_write_lock(): %s/%d (nip %08lX) lock %lx",
+		      current->comm,current->pid,current->tss.regs->nip,
+		      rw->lock);
+		else
+		  printk("no current\n");
+	}
+#endif /* DEBUG_LOCKS */
+	clear_bit(31,&(rw)->lock);
+}
+
+void __lock_kernel(struct task_struct *task)
+{
+#ifdef DEBUG_LOCKS
+	if ( (signed long)(task->lock_depth) < 0 )
+	{
+		printk("__lock_kernel(): %s/%d (nip %08lX) lock depth %x\n",
+		      task->comm,task->pid,task->tss.regs->nip,
+		      task->lock_depth);
+	}
+#endif /* DEBUG_LOCKS */
+	/* mine! */
+	if ( atomic_inc_return((atomic_t *) &task->lock_depth) == 1 )
+		klock_info.akp = smp_processor_id();
+	/* my kernel mode! mine!!! */
+}
+ 
+void __unlock_kernel(struct task_struct *task)
+{
+#ifdef DEBUG_LOCKS
+ 	if ( task->lock_depth == 0 )
+	{
+		printk("__unlock_kernel(): %s/%d (nip %08lX) lock depth %x\n",
+		      task->comm,task->pid,task->tss.regs->nip,
+		      task->lock_depth);
+		klock_info.akp = NO_PROC_ID;		
+		klock_info.kernel_flag = 0;
+		return;
+	}
+#endif /* DEBUG_LOCKS */
+	if ( atomic_dec_and_test((atomic_t *) &task->lock_depth) )
+	{
+		klock_info.akp = NO_PROC_ID;		
+		klock_info.kernel_flag = 0;
+	}
+}	
+
+void reacquire_kernel_lock(struct task_struct *task, int cpu,int depth)
+{
+	if (depth)
+	{
+		__cli();
+		__lock_kernel(task);
+		task->lock_depth = depth;
+		__sti();
+       }
+}
+
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/lib/string.S linux/arch/ppc/lib/string.S
--- v2.1.78/linux/arch/ppc/lib/string.S	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/lib/string.S	Mon Jan 12 15:18:13 1998
@@ -9,6 +9,7 @@
  * 2 of the License, or (at your option) any later version.
  */
 #include "../kernel/ppc_asm.tmpl"
+#include <asm/processor.h>
 #include <asm/errno.h>
 
 	.globl	strcpy
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/mkdiff linux/arch/ppc/mkdiff
--- v2.1.78/linux/arch/ppc/mkdiff	Mon Aug  4 16:25:36 1997
+++ linux/arch/ppc/mkdiff	Wed Dec 31 16:00:00 1969
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-N=`basename $PWD`
-date=`date +'%y%m%d'`
-cd ../
-mkdir -p dist
-echo Diff of: $N against $N.ORIG '->' $N-$date-ppc.patch 
-diff -uNr -X $N/arch/ppc/ignore $N.ORIG $N > dist/$N-$date-ppc.patch
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/mkdist linux/arch/ppc/mkdist
--- v2.1.78/linux/arch/ppc/mkdist	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/mkdist	Wed Dec 31 16:00:00 1969
@@ -1,13 +0,0 @@
-#!/bin/bash
-
-V=`egrep ^VERSION Makefile | awk '{print $3}'`
-P=`egrep ^PATCHLEVEL Makefile | awk '{print $3}'`
-S=`egrep ^SUBLEVEL Makefile | awk '{print $3}'`
-date=`date +'%y%m%d'`
-
-echo zImage-$V.$P.$S-$date
-echo System.map-$V.$P.$S-$date
-
-rcp zImage charon:ppc/dist/kernel-images/zImage-$V.$P.$S-$date
-rcp System.map charon:ppc/dist/kernel-images/System.map-$V.$P.$S-$date
-
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/mktar linux/arch/ppc/mktar
--- v2.1.78/linux/arch/ppc/mktar	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/mktar	Wed Dec 31 16:00:00 1969
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-V=`egrep ^VERSION Makefile | awk '{print $3}'`
-P=`egrep ^PATCHLEVEL Makefile | awk '{print $3}'`
-S=`egrep ^SUBLEVEL Makefile | awk '{print $3}'`
-date=`date +'%y%m%d'`
-cd ../
-mkdir -p dist
-tar -vzcf ppclinux-$V.$P.$S-$date.tar.gz -X linux/arch/ppc/ignore linux
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/mm/extable.c linux/arch/ppc/mm/extable.c
--- v2.1.78/linux/arch/ppc/mm/extable.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/ppc/mm/extable.c	Mon Jan 12 15:18:13 1998
@@ -16,17 +16,10 @@
 		 const struct exception_table_entry *last,
 		 unsigned long value)
 {
-	const struct exception_table_entry *mid;
-	for ( mid = first; mid < last; mid++)
-	{
-		if ( mid->insn == value )
-			return mid->fixup;
-	}
-	return 0;
-#if 0	
         while (first <= last) {
 		const struct exception_table_entry *mid;
 		long diff;
+
 		mid = (last - first) / 2 + first;
 		diff = mid->insn - value;
                 if (diff == 0)
@@ -36,7 +29,6 @@
                 else
                         last = mid-1;
         }
-#endif	
         return 0;
 }
 
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/mm/fault.c linux/arch/ppc/mm/fault.c
--- v2.1.78/linux/arch/ppc/mm/fault.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/ppc/mm/fault.c	Mon Jan 12 15:18:13 1998
@@ -37,11 +37,17 @@
 
 #ifdef CONFIG_XMON
 extern void xmon(struct pt_regs *);
-extern void (*xmon_fault_handler)(void);
+extern void (*xmon_fault_handler)(struct pt_regs *);
 extern int xmon_dabr_match(struct pt_regs *);
-int xmon_kernel_faults;
+int xmon_kernel_faults = 0;
 #endif
 
+unsigned long htab_reloads = 0; /* updated by head.S:hash_page() */
+unsigned long htab_evicts = 0;  /* updated by head.S:hash_page() */
+unsigned long pte_misses = 0; /* updated by do_page_fault() */
+unsigned long pte_errors = 0; /* updated by do_page_fault() */
+unsigned int probingmem = 0;
+
 extern void die_if_kernel(char *, struct pt_regs *, long);
 void bad_page_fault(struct pt_regs *, unsigned long);
 void do_page_fault(struct pt_regs *, unsigned long, unsigned long);
@@ -56,9 +62,19 @@
 	struct vm_area_struct * vma;
 	struct mm_struct *mm = current->mm;
 
+	/*printk("address: %08lx code: %08lx %s%s%s%s%s%s\n",
+	       address,error_code,
+	       (error_code&0x40000000)?"604 tlb&htab miss ":"",
+	       (error_code&0x20000000)?"603 tlbmiss ":"",
+	       (error_code&0x02000000)?"write ":"",
+	       (error_code&0x08000000)?"prot ":"",
+	       (error_code&0x95700000)?"I/O ":"",
+	       (regs->trap == 0x400)?"instr":"data"
+	       );*/
+	       
 #ifdef CONFIG_XMON
 	if (xmon_fault_handler && regs->trap == 0x300) {
-		xmon_fault_handler();
+		xmon_fault_handler(regs);
 		return;
 	}
 	if (error_code & 0x00400000) {
@@ -96,7 +112,7 @@
 		goto bad_area;
 
 good_area:
-	if (error_code & 0xb5700000)
+	if (error_code & 0x95700000)
 		/* an error such as lwarx to I/O controller space,
 		   address matching DABR, eciwx, etc. */
 		goto bad_area;
@@ -115,10 +131,18 @@
 	}
 	handle_mm_fault(current, vma, address, error_code & 0x02000000);
 	up(&mm->mmap_sem);
+	/*
+	 * keep track of tlb+htab misses that are good addrs but
+	 * just need pte's created via handle_mm_fault()
+	 * -- Cort
+	 */
+	pte_misses++;
 	return;
 
 bad_area:
+
 	up(&mm->mmap_sem);
+	pte_errors++;	
 	bad_page_fault(regs, address);
 }
 
@@ -126,7 +150,32 @@
 bad_page_fault(struct pt_regs *regs, unsigned long address)
 {
 	unsigned long fixup;
-
+#if 0	
+	extern unsigned long video_mem_base;
+	extern unsigned long video_mem_term;
+	
+	/*
+	 * Remap video IO areas for buggy X servers.
+	 * The S3 server wants direct access to video memory
+	 * at 0x8000 0000 and 0xc000 0000 on prep systems, but
+	 * we don't allow that AND we remap the io areas so it's not
+	 * even there!
+	 * So, for this task only give a virtual=physical mapping of the
+	 * video mem.
+	 * -- Cort
+	 */
+	if ( is_prep && user_mode(regs) )
+	{
+			printk("%s/%d: fault on %x\n",
+			       current->comm,current->pid,address);
+			printk("mapping: %x -> %x\n",
+			       address&PAGE_MASK, address&PAGE_MASK);
+			map_page(current,address&PAGE_MASK,address&PAGE_MASK,
+				 _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE|_PAGE_NO_CACHE|_PAGE_WRITETHRU);
+			return;
+	}
+#endif	
+	
 	if (user_mode(regs)) {
 		force_sig(SIGSEGV, current);
 		return;
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c
--- v2.1.78/linux/arch/ppc/mm/init.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/ppc/mm/init.c	Mon Jan 12 15:18:13 1998
@@ -53,21 +53,11 @@
 extern char etext[], _stext[];
 extern char __init_begin, __init_end;
 extern RESIDUAL res;
-
-/* Hardwired MMU segments */
-#if defined(CONFIG_PREP) || defined(CONFIG_PMAC)
-#define MMU_SEGMENT_1		0x80000000
-#define MMU_SEGMENT_2		0xc0000000
-#endif /* CONFIG_PREP || CONFIG_PMAC */
-#ifdef CONFIG_CHRP
-#define MMU_SEGMENT_1		0xf0000000	/* LongTrail */
-#define MMU_SEGMENT_2		0xc0000000
-#endif /* CONFIG_CHRP */
-
+char *klimit = _end;
+struct device_node *memory_node;
 
 void *find_mem_piece(unsigned, unsigned);
 static void mapin_ram(void);
-static void inherit_prom_translations(void);
 static void hash_init(void);
 static void *MMU_get_page(void);
 void map_page(struct task_struct *, unsigned long va,
@@ -76,22 +66,16 @@
 extern void show_net_buffers(void);
 extern unsigned long *find_end_of_memory(void);
 
-/*
- * this tells the prep system to map all of ram with the segregs
- * instead of the bats.  I'd like to get this to apply to the
- * pmac as well then have everything use the bats -- Cort
- */
-#undef MAP_RAM_WITH_SEGREGS 1 
+extern struct task_struct *current_set[NR_CPUS];
 
 /*
- * these are used to setup the initial page tables
- * They can waste up to an entire page since the
- * I'll fix this shortly -- Cort
+ * this tells the system to map all of ram with the segregs
+ * (i.e. page tables) instead of the bats.
  */
-#define MAX_MMU_PAGES	16
-unsigned int probingmem = 0;
-unsigned int mmu_pages_count = 0;
-char mmu_pages[(MAX_MMU_PAGES+1)*PAGE_SIZE];
+#undef MAP_RAM_WITH_SEGREGS 1
+
+/* optimization for 603 to load the tlb directly from the linux table */
+#define NO_RELOAD_HTAB 1 /* change in kernel/head.S too! */
 
 /*
  * BAD_PAGE is the page that is used for page faults when linux
@@ -122,8 +106,12 @@
 	return pte_mkdirty(mk_pte(empty_bad_page, PAGE_SHARED));
 }
 
+/*
+ * The following stuff defines a data structure for representing
+ * areas of memory as an array of (address, length) pairs, and
+ * procedures for manipulating them.
+ */
 #define MAX_MEM_REGIONS	32
-phandle memory_pkg;
 
 struct mem_pieces {
 	int n_regions;
@@ -134,34 +122,19 @@
 struct mem_pieces prom_mem;
 
 static void get_mem_prop(char *, struct mem_pieces *);
+static void sort_mem_pieces(struct mem_pieces *);
+static void coalesce_mem_pieces(struct mem_pieces *);
+static void append_mem_piece(struct mem_pieces *, unsigned, unsigned);
 static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int);
 static void print_mem_pieces(struct mem_pieces *);
 
-unsigned long avail_start;
-
-/*
- * Read in a property describing some pieces of memory.
- */
 static void
-get_mem_prop(char *name, struct mem_pieces *mp)
+sort_mem_pieces(struct mem_pieces *mp)
 {
-	int s, i;
-
-	s = (int) call_prom("getprop", 4, 1, memory_pkg, name,
-			    mp->regions, sizeof(mp->regions));
-	if (s < sizeof(mp->regions[0])) {
-		printk("getprop /memory %s returned %d\n", name, s);
-		abort();
-	}
-	mp->n_regions = s / sizeof(mp->regions[0]);
+	unsigned long a, s;
+	int i, j;
 
-	/*
-	 * Make sure the pieces are sorted.
-	 */
 	for (i = 1; i < mp->n_regions; ++i) {
-		unsigned long a, s;
-		int j;
-
 		a = mp->regions[i].address;
 		s = mp->regions[i].size;
 		for (j = i - 1; j >= 0; --j) {
@@ -174,6 +147,41 @@
 	}
 }
 
+static void
+coalesce_mem_pieces(struct mem_pieces *mp)
+{
+	unsigned long a, e;
+	int i, j, d;
+
+	d = 0;
+	for (i = 0; i < mp->n_regions; i = j) {
+		a = mp->regions[i].address;
+		e = a + mp->regions[i].size;
+		for (j = i + 1; j < mp->n_regions
+			     && mp->regions[j].address <= e; ++j)
+			e = mp->regions[j].address + mp->regions[j].size;
+		mp->regions[d].address = a;
+		mp->regions[d].size = e - a;
+		++d;
+	}
+	mp->n_regions = d;
+}
+
+/*
+ * Add some memory to an array of pieces
+ */
+static void
+append_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size)
+{
+	struct reg_property *rp;
+
+	if (mp->n_regions >= MAX_MEM_REGIONS)
+		return;
+	rp = &mp->regions[mp->n_regions++];
+	rp->address = start;
+	rp->size = size;
+}
+
 /*
  * Remove some memory from an array of pieces
  */
@@ -244,6 +252,9 @@
 	printk("\n");
 }
 
+/*
+ * Scan a region for a piece of a given size with the required alignment.
+ */
 void *
 find_mem_piece(unsigned size, unsigned align)
 {
@@ -266,8 +277,32 @@
 }
 
 /*
- * Collect information about RAM and which pieces are already in use.
- * At this point, we have the first 8MB mapped with a BAT.
+ * Read in a property describing some pieces of memory.
+ */
+static void
+get_mem_prop(char *name, struct mem_pieces *mp)
+{
+	struct reg_property *rp;
+	int s;
+
+	rp = (struct reg_property *) get_property(memory_node, name, &s);
+	if (rp == NULL) {
+		printk(KERN_ERR "error: couldn't get %s property on /memory\n",
+		       name);
+		abort();
+	}
+	mp->n_regions = s / sizeof(mp->regions[0]);
+	memcpy(mp->regions, rp, s);
+
+	/* Make sure the pieces are sorted. */
+	sort_mem_pieces(mp);
+	coalesce_mem_pieces(mp);
+}
+
+/*
+ * On systems with Open Firmware, collect information about
+ * physical RAM and which pieces are already in use.
+ * At this point, we have (at least) the first 8MB mapped with a BAT.
  * Our text, data, bss use something over 1MB, starting at 0.
  * Open Firmware may be using 1MB at the 4MB point.
  */
@@ -275,18 +310,24 @@
 {
 	unsigned long a, total;
 	unsigned long kstart, ksize;
-	extern char _stext[], _end[];
 	int i;
 
-	memory_pkg = call_prom("finddevice", 1, 1, "/memory");
-	if (memory_pkg == (void *) -1)
-		panic("can't find memory package");
+	memory_node = find_devices("memory");
+	if (memory_node == NULL) {
+		printk(KERN_ERR "can't find memory node\n");
+		abort();
+	}
 
 	/*
 	 * Find out where physical memory is, and check that it
 	 * starts at 0 and is contiguous.  It seems that RAM is
 	 * always physically contiguous on Power Macintoshes,
 	 * because MacOS can't cope if it isn't.
+	 *
+	 * Supporting discontiguous physical memory isn't hard,
+	 * it just makes the virtual <-> physical mapping functions
+	 * more complicated (or else you end up wasting space
+	 * in mem_map).
 	 */
 	get_mem_prop("reg", &phys_mem);
 	if (phys_mem.n_regions == 0)
@@ -295,15 +336,11 @@
 	if (a != 0)
 		panic("RAM doesn't start at physical address 0");
 	total = phys_mem.regions[0].size;
-	for (i = 1; i < phys_mem.n_regions; ++i) {
-		a = phys_mem.regions[i].address;
-		if (a != total) {
-			printk("RAM starting at 0x%lx is not contiguous\n", a);
-			printk("Using RAM from 0 to 0x%lx\n", total-1);
-			phys_mem.n_regions = i;
-			break;
-		}
-		total += phys_mem.regions[i].size;
+	if (phys_mem.n_regions > 1) {
+		printk("RAM starting at 0x%x is not contiguous\n",
+		       phys_mem.regions[1].address);
+		printk("Using RAM from 0 to 0x%lx\n", total-1);
+		phys_mem.n_regions = 1;
 	}
 
 	/* record which bits the prom is using */
@@ -320,9 +357,11 @@
 	 * Make sure the kernel text/data/bss is in neither.
 	 */
 	kstart = __pa(_stext);	/* should be 0 */
-	ksize = PAGE_ALIGN(_end - _stext);
+	ksize = PAGE_ALIGN(klimit - _stext);
 	remove_mem_piece(&phys_avail, kstart, ksize, 0);
 	remove_mem_piece(&prom_mem, kstart, ksize, 0);
+	remove_mem_piece(&phys_avail, 0, 0x4000, 0);
+	remove_mem_piece(&prom_mem, 0, 0x4000, 0);
 
 	return __va(total);
 }
@@ -333,14 +372,13 @@
  * that setup_arch returns, making sure that there are at
  * least 32 pages unused before this for MMU_get_page to use.
  */
+unsigned long avail_start;
+
 unsigned long find_available_memory(void)
 {
 	int i;
 	unsigned long a, free;
 	unsigned long start, end;
-	
-	if ( _machine != _MACH_Pmac )
-		return 0;
 
 	free = 0;
 	for (i = 0; i < phys_avail.n_regions - 1; ++i) {
@@ -382,8 +420,12 @@
 #ifdef CONFIG_NET
 	show_net_buffers();
 #endif
-	printk("%-8s %3s %3s %8s %8s %8s %9s %8s\n", "Process", "Pid", "Cnt",
+	printk("%-8s %3s %3s %8s %8s %8s %9s %8s", "Process", "Pid", "Cnt",
 	       "Ctx", "Ctx<<4", "Last Sys", "pc", "task");
+#ifdef __SMP__
+	printk(" %3s", "CPU");
+#endif /* __SMP__ */
+	printk("\n");
 	for_each_task(p)
 	{	
 		printk("%-8.8s %3d %3d %8ld %8ld %8ld %c%08lx %08lx ",
@@ -392,16 +434,28 @@
 		       p->mm->context<<4, p->tss.last_syscall,
 		       user_mode(p->tss.regs) ? 'u' : 'k', p->tss.regs->nip,
 		       (ulong)p);
-		if ( p == current )
-			printk("current");
-		if ( p == last_task_used_math )
 		{
+			int iscur = 0;
+#ifdef __SMP__
+			printk("%3d ", p->processor);
+			if ( (p->processor != NO_PROC_ID) &&
+			     (p == current_set[p->processor]) )
+			
+#else		
 			if ( p == current )
-				printk(",");
-			printk("last math");
+#endif /* __SMP__ */
+			{
+				iscur = 1;
+				printk("current");
+			}
+			if ( p == last_task_used_math )
+			{
+				if ( iscur )
+					printk(",");
+				printk("last math");
+			}			
+			printk("\n");
 		}
-
-		printk("\n");
 	}
 }
 
@@ -415,9 +469,9 @@
 	/*
 	 * Grab some memory for bad_page and bad_pagetable to use.
 	 */
-	empty_bad_page = start_mem;
-	empty_bad_page_table = start_mem + PAGE_SIZE;
-	start_mem += 2 * PAGE_SIZE;
+	empty_bad_page = PAGE_ALIGN(start_mem);
+	empty_bad_page_table = empty_bad_page + PAGE_SIZE;
+	start_mem = empty_bad_page + 2 * PAGE_SIZE;
 
 	/* note: free_area_init uses its second argument
 	   to size the mem_map array. */
@@ -442,53 +496,37 @@
 	/* mark usable pages in the mem_map[] */
 	start_mem = PAGE_ALIGN(start_mem);
 
-	if ( _machine == _MACH_Pmac )
-	{
-		remove_mem_piece(&phys_avail, __pa(avail_start),
-				 start_mem - avail_start, 1);
-		
-		for (addr = KERNELBASE ; addr < end_mem; addr += PAGE_SIZE)
-			set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags);
-		
-		for (i = 0; i < phys_avail.n_regions; ++i) {
-			a = (unsigned long) __va(phys_avail.regions[i].address);
-			lim = a + phys_avail.regions[i].size;
-			a = PAGE_ALIGN(a);
-			for (; a < lim; a += PAGE_SIZE)
-				clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
-		}
-		phys_avail.n_regions = 0;
-		
-		/* free the prom's memory */
-		for (i = 0; i < prom_mem.n_regions; ++i) {
-			a = (unsigned long) __va(prom_mem.regions[i].address);
-			lim = a + prom_mem.regions[i].size;
-			a = PAGE_ALIGN(a);
-			for (; a < lim; a += PAGE_SIZE)
-				clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
-		}
-		prom_trashed = 1;
-	}
-	else /* prep */
-	{
-		/* mark mem used by kernel as reserved, mark other unreserved */
-		for (addr = PAGE_OFFSET ; addr < end_mem; addr += PAGE_SIZE)
-		{
-			/* skip hash table gap */
-			if ( (addr > (ulong)_end) && (addr < (ulong)Hash))
-				continue;
-			if ( addr < (ulong) /*Hash_end*/ start_mem )
-				set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags);
-			else
-				clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags);
-		}
+	remove_mem_piece(&phys_avail, __pa(avail_start),
+			 start_mem - avail_start, 1);
+
+	for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE)
+		set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags);
+
+	for (i = 0; i < phys_avail.n_regions; ++i) {
+		a = (unsigned long) __va(phys_avail.regions[i].address);
+		lim = a + phys_avail.regions[i].size;
+		a = PAGE_ALIGN(a);
+		for (; a < lim; a += PAGE_SIZE)
+			clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
+	}
+	phys_avail.n_regions = 0;
+
+	/* free the prom's memory - no-op on prep */
+	for (i = 0; i < prom_mem.n_regions; ++i) {
+		a = (unsigned long) __va(prom_mem.regions[i].address);
+		lim = a + prom_mem.regions[i].size;
+		a = PAGE_ALIGN(a);
+		for (; a < lim; a += PAGE_SIZE)
+			clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
 	}
+	prom_trashed = 1;
 
 	for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
 		if (PageReserved(mem_map + MAP_NR(addr))) {
 			if (addr < (ulong) etext)
 				codepages++;
-			else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end))
+			else if (addr >= (unsigned long)&__init_begin
+				 && addr < (unsigned long)&__init_end)
                                 initpages++;
                         else if (addr < (ulong) start_mem)
 				datapages++;
@@ -497,7 +535,7 @@
 		atomic_set(&mem_map[MAP_NR(addr)].count, 1);
 #ifdef CONFIG_BLK_DEV_INITRD
 		if (!initrd_start ||
-		    (addr < initrd_start || addr >= initrd_end))
+		    addr < (initrd_start & PAGE_MASK) || addr >= initrd_end)
 #endif /* CONFIG_BLK_DEV_INITRD */
 			free_page(addr);
 	}
@@ -512,31 +550,22 @@
 }
 
 /*
- * this should reclaim gap between _end[] and hash table
- * as well as unused mmu_pages[] on prep systems.
- * When I get around to it, I'll put initialization functions
- * (called only at boot) in their own .section and free that -- Cort
+ * Unfortunately, we can't put initialization functions in their
+ * own section and free that at this point, because gas gets some
+ * relocations wrong if we do. :-(  But this code is here for when
+ * gas gets fixed.
  */
 void free_initmem(void)
 {
 	unsigned long a;
 	unsigned long num_freed_pages = 0;
 
-	/* free unused mmu_pages[] */
-	a = PAGE_ALIGN( (unsigned long) mmu_pages) + (mmu_pages_count*PAGE_SIZE);
-	for ( ; a < PAGE_ALIGN((unsigned long)mmu_pages)+(MAX_MMU_PAGES*PAGE_SIZE); a += PAGE_SIZE )
-	{
-		clear_bit( PG_reserved, &mem_map[MAP_NR(a)].flags );
-		atomic_set(&mem_map[MAP_NR(a)].count, 1);
-		free_page(a);
-		num_freed_pages++;
-	}
-
 	a = (unsigned long)(&__init_begin);
 	for (; a < (unsigned long)(&__init_end); a += PAGE_SIZE) {
-		mem_map[MAP_NR(a)].flags &= ~(1 << PG_reserved);
+		clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
 		atomic_set(&mem_map[MAP_NR(a)].count, 1);
 		free_page(a);
+		num_freed_pages++;
 	}
 
 	printk ("Freeing unused kernel memory: %ldk freed\n",
@@ -565,138 +594,72 @@
 	return;
 }
 
-BAT BAT0 =
-{
-  {
-    MMU_SEGMENT_1>>17, 	/* bepi */
-    BL_256M,		/* bl */
-    1,			/* vs -- supervisor mode valid */
-    1,			/* vp -- user mode valid */
-  },
-  {
-    MMU_SEGMENT_1>>17,		/* brpn */
-    1,			/* write-through */
-    1,			/* cache-inhibited */
-    0,			/* memory coherence */
-    1,			/* guarded */
-    BPP_RW		/* protection */
-  }
-};
-BAT BAT1 =
-{
-  {
-    MMU_SEGMENT_2>>17, 	/* bepi */
-    BL_256M,		/* bl */
-    1,			/* vs */
-    1,			/* vp */
-  },
-  {
-    MMU_SEGMENT_2>>17,		/* brpn */
-    1,			/* w */
-    1,			/* i (cache disabled) */
-    0,			/* m */
-    1,			/* g */
-    BPP_RW			/* pp */
-  }
-};
-BAT BAT2 =
-{
-  {
-    0x90000000>>17, 	/* bepi */
-    BL_256M, /* this gets set to amount of phys ram */
-    1,			/* vs */
-    0,			/* vp */
-  },
-  {
-    0x00000000>>17,		/* brpn */
-    0,			/* w */
-    0,			/* i */
-#ifdef __SMP__    
-    1,			/* m */
-#else    
-    0,			/* m */
-#endif    
-    0,			/* g */
-    BPP_RW			/* pp */
-  }
-};
-BAT BAT3 =
-{
-  {
-    0x00000000>>17, 	/* bepi */
-    BL_256M,		/* bl */
-    0,			/* vs */
-    0,			/* vp */
-  },
-  {
-    0x00000000>>17,		/* brpn */
-    0,			/* w */
-    0,			/* i (cache disabled) */
-    1,			/* m */
-    0,			/* g */
-    BPP_RW			/* pp */
-  }
-};
+union ubat {			/* BAT register values to be loaded */
+	BAT	bat;
+	P601_BAT bat_601;
+	u32	word[2];
+} BATS[4][2];			/* 4 pairs of IBAT, DBAT */
+
+struct batrange {		/* stores address ranges mapped by BATs */
+	unsigned long start;
+	unsigned long limit;
+	unsigned long phys;
+} bat_addrs[4];
 
-P601_BAT BAT0_601 =
-{
-  {
-    0x80000000>>17, 	/* bepi */
-    1,1,0, /* wim */
-    1, 0, /* vs, vp */
-    BPP_RW, /* pp */
-  },
-  {
-    0x80000000>>17,		/* brpn */
-    1, /* v */
-    BL_8M, /* bl */
-  }
-};
-
-P601_BAT BAT1_601 =
-{
-  {
-    MMU_SEGMENT_2>>17, 	/* bepi */
-    1,1,0, /* wim */
-    1, 0, /* vs, vp */
-    BPP_RW, /* pp */
-  },
-  {
-    MMU_SEGMENT_2>>17,		/* brpn */
-    1, /* v */
-    BL_8M, /* bl */
-  }
-};
-
-P601_BAT BAT2_601 =
-{
-  {
-    0x90000000>>17, 	/* bepi */
-    0,0,0, /* wim */
-    1, 0, /* vs, vp */
-    BPP_RW, /* pp */
-  },
-  {
-    0x00000000>>17,		/* brpn */
-    1, /* v */
-    BL_8M, /* bl */
-  }
-};
-
-P601_BAT BAT3_601 =
+/*
+ * Set up one of the I/D BAT (block address translation) register pairs.
+ * The parameters are not checked; in particular size must be a power
+ * of 2 between 128k and 256M.
+ */
+void
+setbat(int index, unsigned long virt, unsigned long phys,
+       unsigned int size, int flags)
 {
-  {
-    0x90800000>>17, 	/* bepi */
-    0,0,0, /* wim */
-    1, 0, /* vs, vp */
-    BPP_RW, /* pp */
-  },
-  {
-    0x00800000>>17,		/* brpn */
-    1, /* v */
-    BL_8M, /* bl */
-  }
-};
+	unsigned int bl;
+	int wimgxpp;
+	union ubat *bat = BATS[index];
+
+	bl = (size >> 17) - 1;
+	if ((_get_PVR() >> 16) != 1) {
+		/* 603, 604, etc. */
+		/* Do DBAT first */
+		wimgxpp = flags & (_PAGE_WRITETHRU | _PAGE_NO_CACHE
+				   | _PAGE_COHERENT | _PAGE_GUARDED);
+		wimgxpp |= (flags & _PAGE_RW)? BPP_RW: BPP_RX;
+		bat[1].word[0] = virt | (bl << 2) | 2; /* Vs=1, Vp=0 */
+		bat[1].word[1] = phys | wimgxpp;
+		if (flags & _PAGE_USER)
+			bat[1].bat.batu.vp = 1;
+		if (flags & _PAGE_GUARDED) {
+			/* G bit must be zero in IBATs */
+			bat[0].word[0] = bat[0].word[1] = 0;
+		} else {
+			/* make IBAT same as DBAT */
+			bat[0] = bat[1];
+		}
+	} else {
+		/* 601 cpu */
+		if (bl > BL_8M)
+			bl = BL_8M;
+		wimgxpp = flags & (_PAGE_WRITETHRU | _PAGE_NO_CACHE
+				   | _PAGE_COHERENT);
+		wimgxpp |= (flags & _PAGE_RW)?
+			((flags & _PAGE_USER)? PP_RWRW: PP_RWXX): PP_RXRX;
+		bat->word[0] = virt | wimgxpp | 4;	/* Ks=0, Ku=1 */
+		bat->word[1] = phys | bl | 0x40;	/* V=1 */
+	}
+
+	bat_addrs[index].start = virt;
+	bat_addrs[index].limit = virt + ((bl + 1) << 17) - 1;
+	bat_addrs[index].phys = phys;
+}
+
+#define IO_PAGE	(_PAGE_NO_CACHE | _PAGE_GUARDED | _PAGE_RW)
+#ifdef __SMP__
+#define RAM_PAGE (_PAGE_COHERENT | _PAGE_RW)
+#else
+#define RAM_PAGE (_PAGE_RW)
+#endif
 
 /*
  * This finds the amount of physical ram and does necessary
@@ -706,158 +669,76 @@
  */
 unsigned long *prep_find_end_of_memory(void)
 {
-	int i;
-	
-	if (res.TotalMemory == 0 )
+	unsigned long kstart, ksize;
+	unsigned long total;
+	total = res.TotalMemory;
+
+	if (total == 0 )
 	{
 		/*
 		 * I need a way to probe the amount of memory if the residual
 		 * data doesn't contain it. -- Cort
 		 */
 		printk("Ramsize from residual data was 0 -- Probing for value\n");
-		res.TotalMemory = 0x03000000;
-		printk("Ramsize default to be %ldM\n", res.TotalMemory>>20);
+		total = 0x02000000;
+		printk("Ramsize default to be %ldM\n", total>>20);
 	}
+	append_mem_piece(&phys_mem, 0, total);
+	phys_avail = phys_mem;
+	kstart = __pa(_stext);	/* should be 0 */
+	ksize = PAGE_ALIGN(klimit - _stext);
+	remove_mem_piece(&phys_avail, kstart, ksize, 0);
+	remove_mem_piece(&phys_avail, 0, 0x4000, 0);
 
-	/* NOTE: everything below here is moving to mapin_ram() */
-
-	
-	/*
-	 * if this is a 601, we can only map sizes of 8M with the BAT's
-	 * so we have to map what we can't map with the bats with the segregs
-	 * head.S will copy in the appropriate BAT's according to the processor
-	 * since the 601_BAT{2,3} structures are already setup to map
-	 * the first 16M correctly
-	 * -- Cort
-	 */
-#ifndef MAP_RAM_WITH_SEGREGS     /* don't need to do it twice */
-	if ( _get_PVR() == 1 )
-	{
-		/* map in rest of ram with seg regs */
-		if ( res.TotalMemory > 0x01000000 /* 16M */)
-		{
-			for (i = KERNELBASE+0x01000000;
-			     i < KERNELBASE+res.TotalMemory;  i += PAGE_SIZE)
-				map_page(&init_task, i, __pa(i),
-					 _PAGE_PRESENT| _PAGE_RW|_PAGE_DIRTY|_PAGE_ACCESSED);
-		}
-	}
-#endif /* MAP_RAM_WITH_SEGREGS */
-  
-#ifdef MAP_RAM_WITH_SEGREGS
-	/* turn off bat mapping kernel since being done with segregs */
-	memset(&BAT2, sizeof(BAT2), 0);
-	memset(&BAT2_601, sizeof(BAT2), 0); /* in case we're on a 601 */
-	memset(&BAT3_601, sizeof(BAT2), 0);
-	/* map all of ram for kernel with segregs */
-	for (i = KERNELBASE;  i < KERNELBASE+res.TotalMemory;  i += PAGE_SIZE)
-	{
-		if ( i < (unsigned long)etext )
-			map_page(&init_task, i, __pa(i),
-				 _PAGE_PRESENT/*| _PAGE_RW*/|_PAGE_DIRTY|_PAGE_ACCESSED);
-		else
-			map_page(&init_task, i, __pa(i),
-				 _PAGE_PRESENT| _PAGE_RW|_PAGE_DIRTY|_PAGE_ACCESSED);
-	}
-#endif /* MAP_RAM_WITH_SEGREGS */
-	
-	return (__va(res.TotalMemory));
+	return (__va(total));
 }
 
 
 /*
  * Map in all of physical memory starting at KERNELBASE.
  */
-extern int n_mem_regions;
-extern struct reg_property mem_regions[];
-
 #define PAGE_KERNEL_RO	__pgprot(_PAGE_PRESENT | _PAGE_ACCESSED)
 
 static void mapin_ram()
 {
-    int i;
-    unsigned long v, p, s, f;
+	int i;
+	unsigned long tot, bl, done;
+	unsigned long v, p, s, f;
 
-    if ( _machine == _MACH_Pmac )
-    {
-	    v = KERNELBASE;
-	    for (i = 0; i < phys_mem.n_regions; ++i) {
-		    p = phys_mem.regions[i].address;
-		    for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) {
-			    f = _PAGE_PRESENT | _PAGE_ACCESSED;
-			    if ((char *) v < _stext || (char *) v >= etext)
-				    f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE;
-			    else
-				    /* On the powerpc, no user access forces R/W kernel access */
-				    f |= _PAGE_USER;
-			    map_page(&init_task, v, p, f);
-			    v += PAGE_SIZE;
-			    p += PAGE_SIZE;
-		    }
-	    }
-    }
-    else /* prep */
-    {
-	    /* setup the bat2 mapping to cover physical ram */
-	    BAT2.batu.bl = 0x1; /* 256k mapping */
-	    for ( f = 256*1024 /* 256k */ ;
-		  (f <= res.TotalMemory) && (f <= 256*1024*1024);
-		  f *= 2 )
-		    BAT2.batu.bl = (BAT2.batu.bl << 1) | BAT2.batu.bl;
-	    /*
-	     * let ibm get to the device mem from user mode since
-	     * the X for them needs it right now -- Cort
-	     */
-	    if ( _machine == _MACH_IBM )
-		    BAT0.batu.vp = BAT1.batu.vp = 1;
-	    
-    }
-}
-
-#define MAX_PROM_TRANSLATIONS	64
-
-static struct translation_property prom_translations[MAX_PROM_TRANSLATIONS];
-int n_translations;
-phandle mmu_pkg;
-extern ihandle prom_chosen;
-
-static void inherit_prom_translations()
-{
-	int s, i, f;
-	unsigned long v, p, n;
-	struct translation_property *tp;
-	ihandle mmu_inst;
-
-	if ((int) call_prom("getprop", 4, 1, prom_chosen, "mmu",
-			    &mmu_inst, sizeof(mmu_inst)) != sizeof(mmu_inst))
-		panic("couldn't get /chosen mmu property");
-	mmu_pkg = call_prom("instance-to-package", 1, 1, mmu_inst);
-	if (mmu_pkg == (phandle) -1)
-		panic("couldn't get mmu package");
-	s = (int) call_prom("getprop", 4, 1, mmu_pkg, "translations",
-			    &prom_translations, sizeof(prom_translations));
-	if (s < sizeof(prom_translations[0]))
-		panic("couldn't get mmu translations property");
-	n_translations = s / sizeof(prom_translations[0]);
-
-	for (tp = prom_translations, i = 0; i < n_translations; ++i, ++tp) {
-		/* ignore stuff mapped down low */
-		if (tp->virt < 0x10000000 && tp->phys < 0x10000000)
-			continue;
-		/* map PPC mmu flags to linux mm flags */
-		f = (tp->flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU
-				  | _PAGE_COHERENT | _PAGE_GUARDED))
-			| pgprot_val(PAGE_KERNEL);
-		/* add these pages to the mappings */
-		v = tp->virt;
-		p = tp->phys;
-		n = tp->size;
-		for (; n != 0; n -= PAGE_SIZE) {
+#ifndef MAP_RAM_WITH_SEGREGS
+	/* Set up BAT2 and if necessary BAT3 to cover RAM. */
+	tot = (unsigned long)end_of_DRAM - KERNELBASE;
+	for (bl = 128<<10; bl < 256<<20; bl <<= 1)
+		if (bl * 2 > tot)
+			break;
+	setbat(2, KERNELBASE, 0, bl, RAM_PAGE);
+	done = __pa(bat_addrs[2].limit) + 1;
+	if (done < tot) {
+		/* use BAT3 to cover a bit more */
+		tot -= done;
+		for (bl = 128<<10; bl < 256<<20; bl <<= 1)
+			if (bl * 2 > tot)
+				break;
+		setbat(3, KERNELBASE+done, done, bl, RAM_PAGE);
+	}
+#endif
+
+	v = KERNELBASE;
+	for (i = 0; i < phys_mem.n_regions; ++i) {
+		p = phys_mem.regions[i].address;
+		for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) {
+			f = _PAGE_PRESENT | _PAGE_ACCESSED;
+			if ((char *) v < _stext || (char *) v >= etext)
+				f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE;
+			else
+				/* On the powerpc, no user access
+				   forces R/W kernel access */
+				f |= _PAGE_USER;
 			map_page(&init_task, v, p, f);
 			v += PAGE_SIZE;
 			p += PAGE_SIZE;
 		}
-	}
+	}	    
 }
 
 /*
@@ -866,56 +747,78 @@
 static void hash_init(void)
 {
 	int Hash_bits;
-	unsigned long h;
+	unsigned long h, ramsize;
 
 	extern unsigned int hash_page_patch_A[], hash_page_patch_B[],
-		hash_page_patch_C[];
+		hash_page_patch_C[], hash_page_patch_D[];
 
 	/*
 	 * Allow 64k of hash table for every 16MB of memory,
 	 * up to a maximum of 2MB.
 	 */
-	for (h = 64<<10; h < (ulong)__pa(end_of_DRAM) / 256 && h < 2<<20; h *= 2)
+	ramsize = (ulong)end_of_DRAM - KERNELBASE;
+	for (h = 64<<10; h < ramsize / 256 && h < 2<<20; h *= 2)
 		;
 	Hash_size = h;
 	Hash_mask = (h >> 6) - 1;
-
+	
+#ifdef NO_RELOAD_HTAB
+	/* shrink the htab since we don't use it on 603's -- Cort */
+	switch (_get_PVR()>>16) {
+	case 3: /* 603 */
+	case 6: /* 603e */
+	case 7: /* 603ev */
+		Hash_size = 0;
+		Hash_mask = 0;
+		break;
+	default:
+	        /* on 601/4 let things be */
+		break;
+ 	}
+#endif /* NO_RELOAD_HTAB */
+	
 	/* Find some memory for the hash table. */
-	if ( is_prep )
-		/* align htab on a Hash_size boundry above _end[] */
-		Hash = (PTE *)_ALIGN( (unsigned long)&_end, Hash_size);
-	else /* pmac */
+	if ( Hash_size )
 		Hash = find_mem_piece(Hash_size, Hash_size);
-	
+	else
+		Hash = 0;
+
 	printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n",
-	       __pa(end_of_DRAM) >> 20, Hash_size >> 10, Hash);
-	memset(Hash, 0, Hash_size);
-	Hash_end = (PTE *) ((unsigned long)Hash + Hash_size);
+	       ramsize >> 20, Hash_size >> 10, Hash);
+	if ( Hash_size )
+	{
+		memset(Hash, 0, Hash_size);
+		Hash_end = (PTE *) ((unsigned long)Hash + Hash_size);
 
-	/*
-	 * Patch up the instructions in head.S:hash_page
-	 */
-	Hash_bits = ffz(~Hash_size) - 6;
-	hash_page_patch_A[0] = (hash_page_patch_A[0] & ~0xffff)
-		| (__pa(Hash) >> 16);
-	hash_page_patch_A[1] = (hash_page_patch_A[1] & ~0x7c0)
-		| ((26 - Hash_bits) << 6);
-	if (Hash_bits > 16)
-		Hash_bits = 16;
-	hash_page_patch_A[2] = (hash_page_patch_A[2] & ~0x7c0)
-		| ((26 - Hash_bits) << 6);
-	hash_page_patch_B[0] = (hash_page_patch_B[0] & ~0xffff)
-		| (Hash_mask >> 10);
-	hash_page_patch_C[0] = (hash_page_patch_C[0] & ~0xffff)
-		| (Hash_mask >> 10);
+		/*
+		 * Patch up the instructions in head.S:hash_page
+		 */
+		Hash_bits = ffz(~Hash_size) - 6;
+		hash_page_patch_A[0] = (hash_page_patch_A[0] & ~0xffff)
+			| (__pa(Hash) >> 16);
+		hash_page_patch_A[1] = (hash_page_patch_A[1] & ~0x7c0)
+			| ((26 - Hash_bits) << 6);
+		if (Hash_bits > 16)
+			Hash_bits = 16;
+		hash_page_patch_A[2] = (hash_page_patch_A[2] & ~0x7c0)
+			| ((26 - Hash_bits) << 6);
+		hash_page_patch_B[0] = (hash_page_patch_B[0] & ~0xffff)
+			| (Hash_mask >> 10);
+		hash_page_patch_C[0] = (hash_page_patch_C[0] & ~0xffff)
+			| (Hash_mask >> 10);
+		hash_page_patch_D[0] = (hash_page_patch_D[0] & ~0xffff)
+			| (Hash_mask >> 10);
+		/*
+		 * Ensure that the locations we've patched have been written
+		 * out from the data cache and invalidated in the instruction
+		 * cache, on those machines with split caches.
+		 */
+		flush_icache_range((unsigned long) hash_page_patch_A,
+				   (unsigned long) (hash_page_patch_D + 1));
+	}
+	else
+		Hash_end = 0;
 
-	/*
-	 * Ensure that the locations we've patched have been written
-	 * out from the data cache and invalidated in the instruction
-	 * cache, on those machines with split caches.
-	 */
-	flush_icache_range((unsigned long) hash_page_patch_A,
-			   (unsigned long) (hash_page_patch_C + 1));
 }
 
 
@@ -929,19 +832,40 @@
 void
 MMU_init(void)
 {
-	if ( _machine == _MACH_Pmac )
+	if (have_of)
 		end_of_DRAM = pmac_find_end_of_memory();
-	else /* prep and chrp */
+	else /* prep */
 		end_of_DRAM = prep_find_end_of_memory();
-		
+
         hash_init();
         _SDR1 = __pa(Hash) | (Hash_mask >> 10);
 
 	/* Map in all of RAM starting at KERNELBASE */
 	mapin_ram();
-	if ( _machine == _MACH_Pmac )
-		/* Copy mappings from the prom */
-		inherit_prom_translations();
+
+	/*
+	 * Setup the bat mappings we're going to load that cover
+	 * the io areas.  RAM was mapped by mapin_ram().
+	 * -- Cort
+	 */
+	switch (_machine) {
+	case _MACH_prep:
+		setbat(0, 0x80000000, 0x80000000, 0x10000000,
+		       IO_PAGE + ((_prep_type == _PREP_IBM)? _PAGE_USER: 0));
+		setbat(1, 0xd0000000, 0xc0000000, 0x10000000,
+		       IO_PAGE + ((_prep_type == _PREP_IBM)? _PAGE_USER: 0));
+		break;
+	case _MACH_chrp:
+		setbat(0, 0xc0000000, 0xc0000000, 0x10000000, IO_PAGE);
+		setbat(1, 0xf8000000, 0xf8000000, 0x20000, IO_PAGE);
+		setbat(3, 0x80000000, 0x80000000, 0x10000000, IO_PAGE);
+		break;
+	case _MACH_Pmac:
+		setbat(0, 0xf3000000, 0xf3000000, 0x100000, IO_PAGE);
+		/* this is used to cover registers used by smp boards -- Cort */
+		setbat(3, 0xf8000000, 0xf8000000, 0x100000, IO_PAGE);
+		break;
+	}
 }
 
 static void *
@@ -954,18 +878,7 @@
 		if (p == 0)
 			panic("couldn't get a page in MMU_get_page");
 	} else {
-		if ( is_prep || (_machine == _MACH_chrp) )
-		{
-			mmu_pages_count++;
-			if ( mmu_pages_count > MAX_MMU_PAGES )
-				printk("out of mmu pages!\n");
-			p = (pte *)(PAGE_ALIGN((unsigned long)mmu_pages)+
-				    (mmu_pages_count+PAGE_SIZE));
-		}
-		else /* pmac */
-		{
-			p = find_mem_piece(PAGE_SIZE, PAGE_SIZE);
-		}
+		p = find_mem_piece(PAGE_SIZE, PAGE_SIZE);
 	}
 	memset(p, 0, PAGE_SIZE);
 	return p;
@@ -976,30 +889,15 @@
 {
 	unsigned long p, end = addr + size;
 
-	/*
-	 * BAT mappings on prep cover this already so don't waste
-	 * space with it. -- Cort
-	 */
-	if ( is_prep )
-		if ( ((addr >= 0xc0000000) && (end < (0xc0000000+(256<<20)))) ||
-		     ((addr >= 0x80000000) && (end < (0x80000000+(256<<20)))) )
-			return (void *)addr;
 	for (p = addr & PAGE_MASK; p < end; p += PAGE_SIZE)
-		map_page(&init_task, p, p, pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED);
+		map_page(&init_task, p, p,
+			 pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED);
 	return (void *) addr;
 }
 
-extern void iounmap(unsigned long *addr)
+void iounmap(unsigned long *addr)
 {
-	/*
-	 * BAT mappings on prep cover this already so don't waste
-	 * space with it. -- Cort
-	 */
-	if ( is_prep )
-		if ( (((unsigned long)addr >= 0xc0000000) && ((unsigned long)addr < (0xc0000000+(256<<20)))) ||
-		     (((unsigned long)addr >= 0x80000000) && ((unsigned long)addr < (0x80000000+(256<<20)))) )
-			return;
-	/* else unmap it */
+	/* XXX todo */
 }
 
 void
@@ -1008,7 +906,7 @@
 {
 	pmd_t *pd;
 	pte_t *pg;
-	
+	int b;
 
 	if (tsk->mm->pgd == NULL) {
 		/* Allocate upper level page map */
@@ -1017,7 +915,18 @@
 	/* Use upper 10 bits of VA to index the first level map */
 	pd = (pmd_t *) (tsk->mm->pgd + (va >> PGDIR_SHIFT));
 	if (pmd_none(*pd)) {
-		/* Need to allocate second-level table */
+		/*
+		 * Need to allocate second-level table, but first
+		 * check whether this address is already mapped by
+		 * the BATs; if so, don't bother allocating the page.
+		 */
+		for (b = 0; b < 4; ++b) {
+			if (va >= bat_addrs[b].start
+			    && va <= bat_addrs[b].limit) {
+				/* XXX should check the phys address matches */
+				return;
+			}
+		}
 		pg = (pte_t *) MMU_get_page();
 		pmd_val(*pd) = (unsigned long) pg;
 	}
@@ -1042,22 +951,14 @@
  */
 
 /*
- * Flush all tlb/hash table entries except for the kernel's.
- * We use the fact that only kernel mappings use VSIDs 0 - 15.
+ * Flush all tlb/hash table entries (except perhaps for those
+ * mapping RAM starting at PAGE_OFFSET, since they never change).
  */
 void
-flush_tlb_all(void)
+local_flush_tlb_all(void)
 {
-	struct task_struct *tsk;
-
-	read_lock(&tasklist_lock);
-	for_each_task(tsk) {
-		if (tsk->mm)
-			tsk->mm->context = NO_CONTEXT;
-	}
-	read_unlock(&tasklist_lock);
-	get_mmu_context(current);
-	set_context(current->mm->context);
+	memset(Hash, 0, Hash_size);
+	_tlbia();
 }
 
 
@@ -1067,7 +968,7 @@
  * that might be in the hash table.
  */
 void
-flush_tlb_mm(struct mm_struct *mm)
+local_flush_tlb_mm(struct mm_struct *mm)
 {
 	mm->context = NO_CONTEXT;
 	if (mm == current->mm) {
@@ -1077,6 +978,16 @@
 	}
 }
 
+void
+local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
+{
+	if (vmaddr < TASK_SIZE)
+		flush_hash_page(vma->vm_mm->context, vmaddr);
+	else
+		flush_hash_page(0, vmaddr);
+}
+
+
 /* for each page addr in the range, call MMU_invalidate_page()
    if the range is very large and the hash table is small it might be faster to
    do a search of the hash table and just invalidate pages that are in the range
@@ -1084,9 +995,16 @@
         -- Cort
    */
 void
-flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
 	start &= PAGE_MASK;
+
+	if (end - start > 20 * PAGE_SIZE)
+	{
+		flush_tlb_mm(mm);
+		return;
+	}
+
 	for (; start < end && start < TASK_SIZE; start += PAGE_SIZE)
 	{
 		flush_hash_page(mm->context, start);
@@ -1117,4 +1035,58 @@
 	current->mm->context = MUNGE_CONTEXT(++next_mmu_context);
 	set_context(current->mm->context);
 }
+
+#if 0
+/*
+ * Cache flush functions - these functions cause caches to be flushed
+ * on _all_ processors due to their use of dcbf.  local_flush_cache_all() is
+ * the only function that will not act on all processors in the system.
+ * -- Cort
+ */
+void local_flush_cache_all(void)
+{
+#if 0  
+	unsigned long hid0,tmp;
+	asm volatile(
+		"mfspr %0,1008 \n\t"
+		"mr    %1,%0 \n\t"
+		"or    %0,%2,%2 \n\t"
+		"mtspr 1008,%0 \n\t"
+		"sync \n\t"
+		"isync \n\t"
+		"andc  %0,%0,%2 \n\t"
+		"mtspr 1008,%0 \n\t"
+		: "=r" (tmp), "=r" (hid0)
+		: "r" (HID0_ICFI|HID0_DCI)
+		);
+#endif	
+}
+
+void local_flush_cache_mm(struct mm_struct *mm)
+{
+	struct vm_area_struct *vma = NULL;
+	vma = mm->mmap;
+	while(vma)
+	{
+		local_flush_cache_range(mm,vma->vm_start,vma->vm_end);
+		vma = vma->vm_next;
+	}
+}
+
+void local_flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr)
+{
+	unsigned long i;
+	vmaddr = PAGE_ALIGN(vmaddr);
+	for ( i = vmaddr ; i <= (vmaddr+PAGE_SIZE); i += 32 )
+		asm volatile("dcbf %0,%1\n\ticbi %0,%1\n\t" :: "r" (i), "r" (0));
+}
+
+void local_flush_cache_range(struct mm_struct *mm, unsigned long start,
+			    unsigned long end)
+{
+	unsigned long i;
+	for ( i = start ; i <= end; i += 32 )
+		asm volatile("dcbf %0,%1\n\ticbi %0,%1\n\t" :: "r" (i), "r" (0));
+}
+#endif
 
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/pmac_defconfig linux/arch/ppc/pmac_defconfig
--- v2.1.78/linux/arch/ppc/pmac_defconfig	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/pmac_defconfig	Mon Jan 12 15:18:13 1998
@@ -1,17 +1,17 @@
 #
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
 #
 
 #
 # Platform support
 #
+CONFIG_PPC=y
 CONFIG_NATIVE=y
+CONFIG_MACH_SPECIFIC=y
 CONFIG_PMAC=y
 # CONFIG_PREP is not set
-CONFIG_MCOMMON=y
-# CONFIG_M601 is not set
-# CONFIG_M603 is not set
-# CONFIG_M604 is not set
+# CONFIG_CHRP is not set
+CONFIG_COMMON=y
 
 #
 # General setup
@@ -24,6 +24,7 @@
 CONFIG_NET=y
 CONFIG_SYSCTL=y
 CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
 CONFIG_BINFMT_ELF=y
 CONFIG_KERNEL_ELF=y
 CONFIG_BINFMT_MISC=m
@@ -32,7 +33,7 @@
 CONFIG_MAC_KEYBOARD=y
 CONFIG_MAC_FLOPPY=y
 CONFIG_PROC_DEVICETREE=y
-CONFIG_XMON=y
+# CONFIG_XMON is not set
 CONFIG_ATY_VIDEO=y
 CONFIG_IMSTT_VIDEO=y
 
@@ -46,10 +47,6 @@
 #
 # CONFIG_BLK_DEV_FD is not set
 CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
 # CONFIG_BLK_DEV_HD_IDE is not set
 CONFIG_BLK_DEV_IDEDISK=y
 CONFIG_BLK_DEV_IDECD=y
@@ -60,10 +57,6 @@
 # CONFIG_BLK_DEV_RZ1000 is not set
 # CONFIG_BLK_DEV_TRITON is not set
 # CONFIG_IDE_CHIPSETS is not set
-
-#
-# Additional Block Devices
-#
 CONFIG_BLK_DEV_LOOP=m
 # CONFIG_BLK_DEV_MD is not set
 CONFIG_BLK_DEV_RAM=y
@@ -73,22 +66,54 @@
 # CONFIG_BLK_DEV_HD is not set
 
 #
-# SCSI support
+# NEW devices (io_request, all ALPHA and dangerous)
 #
-CONFIG_SCSI=y
+# CONFIG_IO_REQUEST is not set
 
 #
-# SCSI support type (disk, tape, CD-ROM)
+# Networking options
 #
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+CONFIG_NET_ALIAS=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_IP_ACCT is not set
+# CONFIG_IP_MASQUERADE is not set
+# CONFIG_IP_ROUTER is not set
+CONFIG_NET_IPIP=m
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+CONFIG_IP_ALIAS=y
+# CONFIG_SYN_COOKIES is not set
+CONFIG_INET_RARP=y
+CONFIG_IP_NOSR=y
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_AX25 is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_CPU_IS_SLOW is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
 CONFIG_BLK_DEV_SD=y
 CONFIG_CHR_DEV_ST=y
 CONFIG_BLK_DEV_SR=y
 CONFIG_BLK_DEV_SR_VENDOR=y
 CONFIG_CHR_DEV_SG=y
-
-#
-# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
-#
 # CONFIG_SCSI_MULTI_LUN is not set
 CONFIG_SCSI_CONSTANTS=y
 
@@ -125,58 +150,22 @@
 CONFIG_SCSI_MESH=y
 CONFIG_SCSI_MESH_SYNC_RATE=10
 CONFIG_SCSI_MAC53C94=y
-CONFIG_SCSI_QLOGIC_PMAC=m
 
 #
 # Network device support
 #
-
-#
-# Networking options
-#
-# CONFIG_NETLINK is not set
-# CONFIG_FIREWALL is not set
-CONFIG_NET_ALIAS=y
-CONFIG_INET=y
-CONFIG_IP_MULTICAST=y
-# CONFIG_IP_ACCT is not set
-# CONFIG_IP_ROUTER is not set
-CONFIG_NET_IPIP=m
-# CONFIG_IP_MROUTE is not set
-CONFIG_IP_ALIAS=y
-# CONFIG_SYN_COOKIES is not set
-
-#
-# (it is safe to leave these untouched)
-#
-# CONFIG_INET_PCTCP is not set
-CONFIG_INET_RARP=y
-CONFIG_PATH_MTU_DISCOVERY=y
-CONFIG_IP_NOSR=y
-CONFIG_SKB_LARGE=y
-# CONFIG_IPV6 is not set
-
-#
-#  
-#
-# CONFIG_IPX is not set
-# CONFIG_ATALK is not set
-# CONFIG_AX25 is not set
-# CONFIG_X25 is not set
-# CONFIG_LAPB is not set
-# CONFIG_BRIDGE is not set
-# CONFIG_LLC is not set
-# CONFIG_WAN_ROUTER is not set
 CONFIG_NETDEVICES=y
 # CONFIG_ARCNET is not set
 # CONFIG_DUMMY is not set
 # CONFIG_EQUALIZER is not set
+# CONFIG_ETHERTAP is not set
 CONFIG_NET_ETHERNET=y
 CONFIG_MACE=y
 CONFIG_DEC_ELCP=m
 # CONFIG_NET_VENDOR_3COM is not set
 # CONFIG_LANCE is not set
 # CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
 # CONFIG_NET_ISA is not set
 # CONFIG_NET_EISA is not set
 # CONFIG_NET_POCKET is not set
@@ -184,10 +173,6 @@
 # CONFIG_DLCI is not set
 # CONFIG_PLIP is not set
 CONFIG_PPP=m
-
-#
-# CCP compressors for PPP are only built as modules.
-#
 # CONFIG_NET_RADIO is not set
 # CONFIG_SLIP is not set
 # CONFIG_TR is not set
@@ -209,18 +194,14 @@
 # CONFIG_QUOTA is not set
 CONFIG_MINIX_FS=m
 CONFIG_EXT2_FS=y
-# CONFIG_FAT_FS is not set
-# CONFIG_MSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
-# CONFIG_UMSDOS_FS is not set
+CONFIG_ISO9660_FS=y
+# CONFIG_NLS is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
-# CONFIG_ROOT_NFS is not set
 # CONFIG_NFSD is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
 # CONFIG_SMB_FS is not set
-CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_AFFS_FS is not set
@@ -234,6 +215,7 @@
 #
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
+# CONFIG_SOFTCURSOR is not set
 CONFIG_SERIAL=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
@@ -245,30 +227,12 @@
 # CONFIG_APM is not set
 # CONFIG_WATCHDOG is not set
 # CONFIG_RTC is not set
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_VIDEO_BT848 is not set
 CONFIG_NVRAM=y
 # CONFIG_JOYSTICK is not set
 
 #
 # Sound
 #
-CONFIG_SOUND=m
-# CONFIG_PAS is not set
-# CONFIG_SB is not set
-# CONFIG_ADLIB is not set
-# CONFIG_GUS is not set
-# CONFIG_MPU401 is not set
-# CONFIG_PSS is not set
-# CONFIG_GUS16 is not set
-# CONFIG_GUSMAX is not set
-# CONFIG_MSS is not set
-# CONFIG_SSCAPE is not set
-# CONFIG_TRIX is not set
-# CONFIG_MAD16 is not set
-# CONFIG_CS4232 is not set
-# CONFIG_MAUI is not set
-# CONFIG_YM3812 is not set
-CONFIG_LOWLEVEL_SOUND=y
-# CONFIG_ACI_MIXER is not set
-# CONFIG_AWE32_SYNTH is not set
-# CONFIG_AEDSP16 is not set
-CONFIG_AWACS=y
+# CONFIG_SOUND is not set
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/prep_defconfig linux/arch/ppc/prep_defconfig
--- v2.1.78/linux/arch/ppc/prep_defconfig	Thu Sep  4 17:07:30 1997
+++ linux/arch/ppc/prep_defconfig	Mon Jan 12 15:18:13 1998
@@ -7,9 +7,11 @@
 #
 CONFIG_PPC=y
 CONFIG_NATIVE=y
+CONFIG_MACH_SPECIFIC=y
 # CONFIG_PMAC is not set
 CONFIG_PREP=y
-CONFIG_MCOMMON=y
+# CONFIG_CHRP is not set
+CONFIG_COMMON=y
 
 #
 # General setup
@@ -19,14 +21,20 @@
 CONFIG_MODVERSIONS=y
 CONFIG_KERNELD=y
 CONFIG_PCI=y
-CONFIG_PCI_OPTIMIZE=y
+# CONFIG_PCI_OPTIMIZE is not set
 CONFIG_NET=y
-CONFIG_SYSCTL=y
+# CONFIG_SYSCTL is not set
 CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
 CONFIG_BINFMT_ELF=y
 CONFIG_KERNEL_ELF=y
 # CONFIG_BINFMT_MISC is not set
 # CONFIG_BINFMT_JAVA is not set
+# CONFIG_PMAC_CONSOLE is not set
+# CONFIG_MAC_KEYBOARD is not set
+# CONFIG_MAC_FLOPPY is not set
+# CONFIG_PROC_DEVICETREE is not set
+# CONFIG_XMON is not set
 CONFIG_VGA_CONSOLE=y
 
 #
@@ -49,7 +57,7 @@
 # CONFIG_BLK_DEV_RZ1000 is not set
 # CONFIG_BLK_DEV_TRITON is not set
 # CONFIG_IDE_CHIPSETS is not set
-CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_LOOP is not set
 # CONFIG_BLK_DEV_MD is not set
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
@@ -58,6 +66,45 @@
 # CONFIG_BLK_DEV_HD is not set
 
 #
+# NEW devices (io_request, all ALPHA and dangerous)
+#
+# CONFIG_IO_REQUEST is not set
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+# CONFIG_NET_ALIAS is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_IP_ACCT is not set
+# CONFIG_IP_MASQUERADE is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_SYN_COOKIES is not set
+CONFIG_INET_RARP=y
+# CONFIG_IP_NOSR is not set
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_AX25 is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_CPU_IS_SLOW is not set
+# CONFIG_NET_SCHED is not set
+
+#
 # SCSI support
 #
 CONFIG_SCSI=y
@@ -67,7 +114,7 @@
 CONFIG_BLK_DEV_SR_VENDOR=y
 # CONFIG_CHR_DEV_SG is not set
 # CONFIG_SCSI_MULTI_LUN is not set
-CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_CONSTANTS is not set
 
 #
 # SCSI low-level drivers
@@ -111,46 +158,13 @@
 #
 # Network device support
 #
-
-#
-# Networking options
-#
-# CONFIG_NETLINK is not set
-# CONFIG_FIREWALL is not set
-# CONFIG_NET_ALIAS is not set
-CONFIG_INET=y
-# CONFIG_IP_MULTICAST is not set
-# CONFIG_IP_ACCT is not set
-# CONFIG_IP_ROUTER is not set
-# CONFIG_NET_IPIP is not set
-# CONFIG_SYN_COOKIES is not set
-# CONFIG_XTP is not set
-# CONFIG_INET_PCTCP is not set
-# CONFIG_INET_RARP is not set
-CONFIG_PATH_MTU_DISCOVERY=y
-# CONFIG_IP_NOSR is not set
-CONFIG_SKB_LARGE=y
-# CONFIG_IPV6 is not set
-# CONFIG_IPX is not set
-# CONFIG_ATALK is not set
-# CONFIG_AX25 is not set
-# CONFIG_X25 is not set
-# CONFIG_LAPB is not set
-# CONFIG_BRIDGE is not set
-# CONFIG_LLC is not set
-# CONFIG_WAN_ROUTER is not set
 CONFIG_NETDEVICES=y
 # CONFIG_ARCNET is not set
 # CONFIG_DUMMY is not set
 # CONFIG_EQUALIZER is not set
+# CONFIG_ETHERTAP is not set
 CONFIG_NET_ETHERNET=y
-CONFIG_NET_VENDOR_3COM=y
-# CONFIG_EL1 is not set
-# CONFIG_EL2 is not set
-# CONFIG_ELPLUS is not set
-# CONFIG_EL16 is not set
-CONFIG_EL3=y
-# CONFIG_VORTEX is not set
+# CONFIG_NET_VENDOR_3COM is not set
 CONFIG_LANCE=y
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_VENDOR_RACAL is not set
@@ -171,7 +185,7 @@
 # CONFIG_FDDI is not set
 # CONFIG_DLCI is not set
 # CONFIG_PLIP is not set
-CONFIG_PPP=y
+CONFIG_PPP=m
 # CONFIG_NET_RADIO is not set
 # CONFIG_SLIP is not set
 # CONFIG_TR is not set
@@ -193,25 +207,30 @@
 # CONFIG_QUOTA is not set
 # CONFIG_MINIX_FS is not set
 CONFIG_EXT2_FS=y
-CONFIG_FAT_FS=y
-CONFIG_MSDOS_FS=y
-# CONFIG_VFAT_FS is not set
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
 # CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
-# CONFIG_ROOT_NFS is not set
-CONFIG_NFSD=y
+# CONFIG_NFSD is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
 # CONFIG_SMB_FS is not set
-CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_ROMFS_FS is not set
 # CONFIG_AUTOFS_FS is not set
 # CONFIG_UFS_FS is not set
-CONFIG_MAC_PARTITION=y
+# CONFIG_MAC_PARTITION is not set
+
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
 
 #
 # Character devices
@@ -241,6 +260,8 @@
 # CONFIG_APM is not set
 # CONFIG_WATCHDOG is not set
 # CONFIG_RTC is not set
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_VIDEO_BT848 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_JOYSTICK is not set
 
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/ap1000/aplib.c linux/arch/sparc/ap1000/aplib.c
--- v2.1.78/linux/arch/sparc/ap1000/aplib.c	Wed Sep 24 20:05:46 1997
+++ linux/arch/sparc/ap1000/aplib.c	Mon Jan 12 15:15:43 1998
@@ -455,7 +455,7 @@
 
 	while (counter == aplib->rbuf_flag1 + aplib->rbuf_flag2) {
 		tnet_check_completion();
-		if (resched_needed())
+		if (need_resched)
 			break;
 		if (signal_pending(current)) break;
 	}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/ap1000/mpp.c linux/arch/sparc/ap1000/mpp.c
--- v2.1.78/linux/arch/sparc/ap1000/mpp.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/sparc/ap1000/mpp.c	Mon Jan 12 15:15:43 1998
@@ -28,7 +28,7 @@
 void mpp_schedule(struct cap_request *req)
 {
 	mpp_current_task = req->data[0];
-	resched_force();
+	need_resched = 1;
 	mark_bh(TQUEUE_BH);
 }
 
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/ap1000/msc.c linux/arch/sparc/ap1000/msc.c
--- v2.1.78/linux/arch/sparc/ap1000/msc.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/sparc/ap1000/msc.c	Mon Jan 12 15:15:43 1998
@@ -338,7 +338,7 @@
 #endif
 		MSC_OUT(MSC_INTR, AP_SET_INTR_MASK << MSC_INTR_QBMFUL_SH);
 		intr_mask |= (AP_INTR_REQ << MSC_INTR_QBMFUL_SH);
-		resched_force();
+		need_resched = 1;
 		block_parallel_tasks = 1;
 		mark_bh(TQUEUE_BH);
 	}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/ap1000/timer.c linux/arch/sparc/ap1000/timer.c
--- v2.1.78/linux/arch/sparc/ap1000/timer.c	Sun Jan 26 02:07:06 1997
+++ linux/arch/sparc/ap1000/timer.c	Mon Jan 12 15:15:43 1998
@@ -73,6 +73,7 @@
 	last_freerun = new_freerun;
 }
 
+#ifdef CONFIG_PROFILE
 
 static void profile_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 {
@@ -96,6 +97,8 @@
   }
 }
 
+#endif
+
 void ap_init_timers(void)
 {
 	extern void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs);
@@ -109,11 +112,13 @@
 		    timer_interrupt,
 		    (SA_INTERRUPT | SA_STATIC_ALLOC),
 		    "timer", NULL);
-	
+
+#ifdef CONFIG_PROFILE	
 	request_irq(APTIM0_IRQ,
 		    profile_interrupt,
 		    (SA_INTERRUPT | SA_STATIC_ALLOC),
 		    "profile", NULL);
+#endif
 	
 	ap_clear_clock_irq();
 	
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/ap1000/tnet.c linux/arch/sparc/ap1000/tnet.c
--- v2.1.78/linux/arch/sparc/ap1000/tnet.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/sparc/ap1000/tnet.c	Mon Jan 12 15:15:43 1998
@@ -613,7 +613,7 @@
 
 static void reschedule(void)
 {
-	resched_force();
+	need_resched = 1;
 	mark_bh(TQUEUE_BH);
 }
 
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.1.78/linux/arch/sparc/config.in	Tue Dec  2 16:45:17 1997
+++ linux/arch/sparc/config.in	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.38 1997/09/04 01:54:33 davem Exp $
+# $Id: config.in,v 1.51 1998/01/08 04:16:54 baccala Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -28,6 +28,7 @@
 
 if [ "$CONFIG_AP1000" = "y" ]; then
 	define_bool CONFIG_NO_KEYBOARD y
+	define_bool CONFIG_FDDI y
 	define_bool CONFIG_APFDDI y
 	define_bool CONFIG_APBLOCK y
 	define_bool CONFIG_APBIF y
@@ -39,6 +40,7 @@
 	define_bool CONFIG_SUN_MOUSE y
 	define_bool CONFIG_SERIAL y
 	define_bool CONFIG_SUN_SERIAL y
+	define_bool CONFIG_SERIAL_CONSOLE y
 	define_bool CONFIG_SUN_KEYBOARD y
 	define_bool CONFIG_SUN_CONSOLE y
 	define_bool CONFIG_SUN_AUXIO y
@@ -50,6 +52,7 @@
 tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
+bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
 bool 'Sysctl support' CONFIG_SYSCTL
 tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
@@ -78,6 +81,7 @@
 fi
 
 tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
+tristate 'Network block device support' CONFIG_BLK_DEV_NBD
 
 endmenu
 
@@ -86,6 +90,15 @@
 fi
 
 mainmenu_option next_comment
+comment 'ISDN subsystem'
+
+tristate 'ISDN support' CONFIG_ISDN
+if [ "$CONFIG_ISDN" != "n" ]; then
+  source drivers/isdn/Config.in
+fi
+endmenu
+
+mainmenu_option next_comment
 comment 'SCSI support'
 
 tristate 'SCSI support' CONFIG_SCSI
@@ -116,6 +129,8 @@
 fi
 endmenu
 
+source drivers/fc4/Config.in
+
 if [ "$CONFIG_NET" = "y" ]; then
 	mainmenu_option next_comment
 	comment 'Network device support'
@@ -162,7 +177,9 @@
 mainmenu_option next_comment
 comment 'Watchdog'
 
-bool 'Software watchdog' CONFIG_SOFT_WATCHDOG
+tristate 'Software watchdog' CONFIG_SOFT_WATCHDOG
+endmenu
+
 mainmenu_option next_comment
 comment 'Kernel hacking'
 
@@ -170,4 +187,5 @@
 if [ "$CONFIG_PROFILE" = "y" ]; then
 	int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
 fi
+bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
 endmenu
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
--- v2.1.78/linux/arch/sparc/defconfig	Mon Aug 18 18:19:44 1997
+++ linux/arch/sparc/defconfig	Mon Jan 12 15:15:43 1998
@@ -49,7 +49,6 @@
 #
 CONFIG_SUN_OPENPROMIO=m
 CONFIG_SUN_MOSTEK_RTC=y
-# CONFIG_SAB82532 is not set
 # CONFIG_SUN_BPP is not set
 # CONFIG_SUN_VIDEOPIX is not set
 
@@ -62,6 +61,7 @@
 CONFIG_SUN_OPENPROMFS=m
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
 CONFIG_SYSCTL=y
 CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
@@ -75,6 +75,8 @@
 CONFIG_BLK_DEV_MD=y
 CONFIG_MD_LINEAR=y
 CONFIG_MD_STRIPED=y
+CONFIG_MD_MIRRORING=m
+CONFIG_MD_RAID5=m
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_BLK_DEV_LOOP=m
@@ -82,41 +84,46 @@
 #
 # Networking options
 #
+CONFIG_PACKET=y
 CONFIG_NETLINK=y
 CONFIG_RTNETLINK=y
+# CONFIG_NETLINK_DEV is not set
 CONFIG_FIREWALL=y
 # CONFIG_NET_SECURITY is not set
 CONFIG_NET_ALIAS=y
+CONFIG_UNIX=y
 CONFIG_INET=y
 CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
 CONFIG_IP_FIREWALL=y
 # CONFIG_IP_FIREWALL_NETLINK is not set
 # CONFIG_IP_FIREWALL_VERBOSE is not set
+# CONFIG_IP_TRANSPARENT_PROXY is not set
+# CONFIG_IP_ALWAYS_DEFRAG is not set
+# CONFIG_IP_ACCT is not set
 CONFIG_IP_MASQUERADE=y
 
 #
 # Protocol-specific masquerading support will be built as modules.
 #
-# CONFIG_IP_TRANSPARENT_PROXY is not set
-# CONFIG_IP_ALWAYS_DEFRAG is not set
-# CONFIG_IP_ACCT is not set
 # CONFIG_IP_ROUTER is not set
 # CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
 CONFIG_IP_ALIAS=m
 # CONFIG_ARPD is not set
 # CONFIG_SYN_COOKIES is not set
-# CONFIG_XTP is not set
 
 #
 # (it is safe to leave these untouched)
 #
-# CONFIG_INET_PCTCP is not set
 CONFIG_INET_RARP=m
-# CONFIG_PATH_MTU_DISCOVERY is not set
 CONFIG_IP_NOSR=y
 CONFIG_SKB_LARGE=y
 CONFIG_IPV6=m
+# CONFIG_IPV6_EUI64 is not set
+# CONFIG_IPV6_NO_PB is not set
 
 #
 #  
@@ -125,13 +132,22 @@
 # CONFIG_IPX_INTERN is not set
 # CONFIG_IPX_PPROP_ROUTING is not set
 CONFIG_ATALK=m
-# CONFIG_IPDDP is not set
 # CONFIG_AX25 is not set
 CONFIG_X25=m
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_LLC is not set
 # CONFIG_WAN_ROUTER is not set
+# CONFIG_CPU_IS_SLOW is not set
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_CSZ=m
+CONFIG_NET_SCH_HFQ=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TBF=y
+CONFIG_NET_SCH_PFIFO=y
+CONFIG_NET_SCH_PRIO=y
 
 #
 # SCSI support
@@ -184,22 +200,20 @@
 CONFIG_QUOTA=y
 CONFIG_MINIX_FS=m
 CONFIG_EXT2_FS=y
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
 CONFIG_FAT_FS=m
 CONFIG_MSDOS_FS=m
-# CONFIG_VFAT_FS is not set
 # CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
-CONFIG_ROOT_NFS=y
-CONFIG_RNFS_BOOTP=y
-CONFIG_RNFS_RARP=y
 CONFIG_NFSD=m
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
 CONFIG_SMB_FS=m
 CONFIG_SMB_WIN95=y
 CONFIG_NCP_FS=m
-CONFIG_ISO9660_FS=y
 CONFIG_HPFS_FS=m
 CONFIG_SYSV_FS=m
 CONFIG_AFFS_FS=m
@@ -212,6 +226,43 @@
 # CONFIG_MAC_PARTITION is not set
 
 #
+# Native Language Support
+#
+CONFIG_NLS=y
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
+
+#
+# Watchdog
+#
+# CONFIG_SOFT_WATCHDOG is not set
+
+#
 # Kernel hacking
 #
 # CONFIG_PROFILE is not set
+# CONFIG_MAGIC_SYSRQ is not set
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/Makefile linux/arch/sparc/kernel/Makefile
--- v2.1.78/linux/arch/sparc/kernel/Makefile	Tue May 13 22:41:03 1997
+++ linux/arch/sparc/kernel/Makefile	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.40 1997/05/01 01:40:36 davem Exp $
+# $Id: Makefile,v 1.41 1997/11/19 15:11:59 jj Exp $
 # Makefile for the linux kernel.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -31,7 +31,7 @@
 all: kernel.o head.o init_task.o
 
 O_TARGET := kernel.o
-IRQ_OBJS := irq.o sun4m_irq.o sun4c_irq.o
+IRQ_OBJS := irq.o sun4m_irq.o sun4c_irq.o sun4d_irq.o
 O_OBJS   := entry.o wof.o wuf.o etrap.o rtrap.o traps.o ${IRQ_OBJS} \
 	    process.o signal.o ioport.o setup.o idprom.o \
 	    sys_sparc.o sunos_asm.o sparc-stub.o systbls.o sys_sunos.o  \
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/entry.S linux/arch/sparc/kernel/entry.S
--- v2.1.78/linux/arch/sparc/kernel/entry.S	Wed Apr 23 19:01:16 1997
+++ linux/arch/sparc/kernel/entry.S	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.138 1997/04/15 09:00:50 davem Exp $
+/* $Id: entry.S,v 1.142 1998/01/07 06:33:47 baccala Exp $
  * arch/sparc/kernel/entry.S:  Sparc trap low-level entry points.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -92,8 +92,8 @@
 	/* Load new kgdb register set. */
 	LOAD_KGDB_GLOBALS(sp)
 	LOAD_KGDB_INS(sp)
-	LOAD_KGDB_SREGS(sp, l0, l2)
-	wr	%l0, 0x0, %y
+	LOAD_KGDB_SREGS(sp, l4, l0, l3, l5, l1, l2)
+	wr      %l4, 0x0, %y
 
 	sethi	%hi(in_trap_handler), %l4
 	ld	[%lo(in_trap_handler) + %l4], %l5
@@ -108,7 +108,7 @@
 	STORE_PT_INS(sp)
 	STORE_PT_GLOBALS(sp)
 	STORE_PT_YREG(sp, g2)
-	STORE_PT_PRIV(sp, l1, l2, l3)
+	STORE_PT_PRIV(sp, l0, l1, l2)
 
 	RESTORE_ALL
 
@@ -283,7 +283,7 @@
  */
 
 	.align	4
-	.globl	real_irq_entry
+	.globl	real_irq_entry, patch_handler_irq
 real_irq_entry:
 	SAVE_ALL
 
@@ -299,6 +299,7 @@
 	wr	%g2, PSR_ET, %psr
 	WRITE_PAUSE
 	mov	%l7, %o0		! irq level
+patch_handler_irq:
 	call	C_LABEL(handler_irq)
 	 add	%sp, REGWIN_SZ, %o1	! pt_regs ptr
 	wr	%l0, PSR_ET, %psr
@@ -1109,7 +1110,7 @@
 	call	C_LABEL(do_ptrace)
 	 add	%sp, REGWIN_SZ, %o0
 
-	ld	[%curptr + 0x14], %l5
+	ld	[%curptr + AOFF_task_flags], %l5
 	andcc	%l5, 0x20, %g0
 	be	1f
 	 nop
@@ -1143,7 +1144,7 @@
 	call	C_LABEL(do_sigpause)
 	 add	%sp, REGWIN_SZ, %o1
 
-	ld	[%curptr + 0x14], %l5
+	ld	[%curptr + AOFF_task_flags], %l5
 	andcc	%l5, 0x20, %g0
 	be	1f
 	 nop
@@ -1161,7 +1162,26 @@
 	call	C_LABEL(do_sigsuspend)
 	 add	%sp, REGWIN_SZ, %o0
 
-	ld	[%curptr + 0x14], %l5
+	ld	[%curptr + AOFF_task_flags], %l5
+	andcc	%l5, 0x20, %g0
+	be	1f
+	 nop
+
+	call	C_LABEL(syscall_trace)
+	 nop
+
+1:
+	/* We are returning to a signal handler. */
+	RESTORE_ALL
+
+	.align	4
+	.globl	C_LABEL(sys_rt_sigsuspend)
+C_LABEL(sys_rt_sigsuspend):
+	/* Note: %o0, %o1 already have correct value... */
+	call	C_LABEL(do_rt_sigsuspend)
+	 add	%sp, REGWIN_SZ, %o2
+
+	ld	[%curptr + AOFF_task_flags], %l5
 	andcc	%l5, 0x20, %g0
 	be	1f
 	 nop
@@ -1179,7 +1199,7 @@
 	call	C_LABEL(do_sigreturn)
 	 add	%sp, REGWIN_SZ, %o0
 
-	ld	[%curptr + 0x14], %l5
+	ld	[%curptr + AOFF_task_flags], %l5
 	andcc	%l5, 0x20, %g0
 	be	1f
 	 nop
@@ -1193,6 +1213,24 @@
 	 */
 	RESTORE_ALL
 
+	.align	4
+	.globl	C_LABEL(sys_rt_sigreturn)
+C_LABEL(sys_rt_sigreturn):
+	call	C_LABEL(do_rt_sigreturn)
+	 add	%sp, REGWIN_SZ, %o0
+
+	ld	[%curptr + AOFF_task_flags], %l5
+	andcc	%l5, 0x20, %g0
+	be	1f
+	 nop
+
+	call	C_LABEL(syscall_trace)
+	 nop
+
+1:
+	/* We are returning to a signal handler. */
+	RESTORE_ALL
+
 	/* Now that we have a real sys_clone, sys_fork() is
 	 * implemented in terms of it.  Our _real_ implementation
 	 * of SunOS vfork() will use sys_clone() instead.
@@ -1301,7 +1339,7 @@
 	mov	%i1, %o1
 	mov	%i2, %o2
 
-	ld	[%curptr + 0x14], %l5
+	ld	[%curptr + AOFF_task_flags], %l5
 	mov	%i3, %o3
 	andcc	%l5, 0x20, %g0
 	mov	%i4, %o4
@@ -1315,7 +1353,7 @@
 
 	.globl	C_LABEL(ret_sys_call)
 C_LABEL(ret_sys_call):
-	ld	[%curptr + 0x14], %l6
+	ld	[%curptr + AOFF_task_flags], %l6
 	cmp	%o0, -ENOIOCTLCMD
 	ld	[%sp + REGWIN_SZ + PT_PSR], %g3
 	set	PSR_C, %g2
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/head.S linux/arch/sparc/kernel/head.S
--- v2.1.78/linux/arch/sparc/kernel/head.S	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc/kernel/head.S	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.83 1997/08/28 11:10:39 jj Exp $
+/* $Id: head.S,v 1.84 1997/11/19 15:12:01 jj Exp $
  * head.S: The initial boot code for the Sparc port of Linux.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -843,7 +843,7 @@
 		cmp	%l1, 'm'		! Test for sun4d, sun4e ?
 		be	sun4m_init
 		 cmp	%l1, 'd'		! Let us see how the beast will die
-		be	sun4m_init
+		be	sun4d_init
 		 nop
 
 		/* Jump into mmu context zero. */
@@ -852,6 +852,17 @@
 
 		b	sun4c_continue_boot
 		 nop
+
+sun4d_init:
+	/* Need to patch call to handler_irq */
+	set	C_LABEL(patch_handler_irq), %g4
+	set	C_LABEL(sun4d_handler_irq), %g5
+	sethi	%hi(0x40000000), %g3		! call
+	sub	%g5, %g4, %g5
+	srl	%g5, 2, %g5
+	or	%g5, %g3, %g5
+	st	%g5, [%g4]
+	/* Fall through to sun4m_init */
 
 sun4m_init:
 	/* XXX Fucking Cypress... */
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/irq.c linux/arch/sparc/kernel/irq.c
--- v2.1.78/linux/arch/sparc/kernel/irq.c	Thu May 15 16:48:01 1997
+++ linux/arch/sparc/kernel/irq.c	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/*  $Id: irq.c,v 1.75 1997/05/08 20:57:37 davem Exp $
+/*  $Id: irq.c,v 1.77 1997/11/19 15:33:05 jj Exp $
  *  arch/sparc/kernel/irq.c:  Interrupt request handling routines. On the
  *                            Sparc the IRQ's are basically 'cast in stone'
  *                            and you are supposed to probe the prom's device
@@ -101,10 +101,10 @@
  *
  */
 #define MAX_STATIC_ALLOC	4
-static struct irqaction static_irqaction[MAX_STATIC_ALLOC];
-static int static_irq_count = 0;
+struct irqaction static_irqaction[MAX_STATIC_ALLOC];
+int static_irq_count = 0;
 
-static struct irqaction *irq_action[NR_IRQS+1] = {
+struct irqaction *irq_action[NR_IRQS+1] = {
 	  NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL,
 	  NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL
 };
@@ -114,6 +114,11 @@
 	int i, len = 0;
 	struct irqaction * action;
 
+	if (sparc_cpu_model == sun4d) {
+		extern int sun4d_get_irq_list(char *);
+		
+		return sun4d_get_irq_list(buf);
+	}
 	for (i = 0 ; i < (NR_IRQS+1) ; i++) {
 	        action = *(i + irq_action);
 		if (!action) 
@@ -242,6 +247,11 @@
         unsigned long flags;
 	unsigned int cpu_irq;
 	
+	if (sparc_cpu_model == sun4d) {
+		extern void sun4d_free_irq(unsigned int, void *);
+		
+		return sun4d_free_irq(irq, dev_id);
+	}
 	cpu_irq = irq & NR_IRQS;
 	action = *(cpu_irq + irq_action);
         if (cpu_irq > 14) {  /* 14 irq levels on the sparc */
@@ -531,29 +541,28 @@
 void handler_irq(int irq, struct pt_regs * regs)
 {
 	struct irqaction * action;
-	unsigned int cpu_irq = irq & NR_IRQS;
 	int cpu = smp_processor_id();
 #ifdef __SMP__
 	extern void smp_irq_rotate(int cpu);
 #endif
 	
-	disable_pil_irq(cpu_irq);
+	disable_pil_irq(irq);
 #ifdef __SMP__
 	/* Only rotate on lower priority IRQ's (scsi, ethernet, etc.). */
 	if(irq < 10)
 		smp_irq_rotate(cpu);
 #endif
-	irq_enter(cpu, cpu_irq, regs);
-	action = *(cpu_irq + irq_action);
-	kstat.interrupts[cpu_irq]++;
+	irq_enter(cpu, irq, regs);
+	action = *(irq + irq_action);
+	kstat.interrupts[irq]++;
 	do {
 		if (!action || !action->handler)
 			unexpected_irq(irq, 0, regs);
 		action->handler(irq, action->dev_id, regs);
 		action = action->next;
 	} while (action);
-	irq_exit(cpu, cpu_irq);
-	enable_pil_irq(cpu_irq);
+	irq_exit(cpu, irq);
+	enable_pil_irq(irq);
 }
 
 #ifdef CONFIG_BLK_DEV_FD
@@ -669,12 +678,22 @@
 	unsigned long flags;
 	unsigned int cpu_irq;
 	
+	if (sparc_cpu_model == sun4d) {
+		extern int sun4d_request_irq(unsigned int, 
+					     void (*)(int, void *, struct pt_regs *),
+					     unsigned long, const char *, void *);
+		return sun4d_request_irq(irq, handler, irqflags, devname, dev_id);
+	}
 	cpu_irq = irq & NR_IRQS;
 	if(cpu_irq > 14)
 		return -EINVAL;
 
 	if (!handler)
 	    return -EINVAL;
+	    
+	if (irqflags & SA_DCOOKIE)
+		dev_id = ((struct devid_cookie *)dev_id)->real_dev_id;
+	
 	action = *(cpu_irq + irq_action);
 	if (action) {
 		if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) {
@@ -751,6 +770,7 @@
 {
 	extern void sun4c_init_IRQ( void );
 	extern void sun4m_init_IRQ( void );
+	extern void sun4d_init_IRQ( void );
     
 	switch(sparc_cpu_model) {
 	case sun4c:
@@ -759,6 +779,10 @@
 
 	case sun4m:
 		sun4m_init_IRQ();
+		break;
+		
+	case sun4d:
+		sun4d_init_IRQ();
 		break;
 
 	case ap1000:
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/muldiv.c linux/arch/sparc/kernel/muldiv.c
--- v2.1.78/linux/arch/sparc/kernel/muldiv.c	Mon Apr 14 16:28:07 1997
+++ linux/arch/sparc/kernel/muldiv.c	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/* $Id: muldiv.c,v 1.4 1997/04/11 00:42:08 davem Exp $
+/* $Id: muldiv.c,v 1.5 1997/12/15 20:07:20 ecd Exp $
  * muldiv.c: Hardware multiply/division illegal instruction trap
  *		for sun4c/sun4 (which do not have those instructions)
  *
@@ -69,18 +69,23 @@
 	ret;									\
 })
 
-static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
+static inline int
+store_reg(unsigned int result, unsigned int reg, struct pt_regs *regs)
 {
 	struct reg_window *win;
 
-	if(!reg)
-		return NULL;
-	else if(reg < 16)
-		return &regs->u_regs[reg];
-	win = (struct reg_window *) regs->u_regs[UREG_FP];
-	return &win->locals[reg - 16];
+	if (!reg)
+		return 0;
+	if (reg < 16) {
+		regs->u_regs[reg] = result;
+		return 0;
+	} else {
+		/* need to use put_user() in this case: */
+		win = (struct reg_window *)regs->u_regs[UREG_FP];
+		return (put_user(result, &win->locals[reg - 16]));
+	}
 }
-
+		
 extern void handle_hw_divzero (struct pt_regs *regs, unsigned long pc,
 			       unsigned long npc, unsigned long psr);
 
@@ -90,7 +95,6 @@
 	unsigned int insn;
 	int inst;
 	unsigned int rs1, rs2, rdv;
-	unsigned long *rd;
 
 	if (!pc) return -1; /* This happens to often, I think */
 	if (get_user (insn, (unsigned int *)pc)) return -1;
@@ -109,7 +113,6 @@
 		rs2 = fetch_reg(rs2, regs);
 	}
 	rs1 = fetch_reg(rs1, regs);
-	rd = fetch_reg_addr(rdv, regs);
 	switch (inst) {
 	case 10: /* umul */
 #ifdef DEBUG_MULDIV	
@@ -127,9 +130,8 @@
 #ifdef DEBUG_MULDIV
 		printk ("0x%x%08x\n", rs2, rs1);
 #endif
-		if (rd) {
-			if (put_user (rs1, rd)) return -1;
-		}
+		if (store_reg(rs1, rdv, regs))
+			return -1;
 		regs->y = rs2;
 		break;
 	case 11: /* smul */
@@ -148,9 +150,8 @@
 #ifdef DEBUG_MULDIV
 		printk ("0x%x%08x\n", rs2, rs1);
 #endif
-		if (rd) {
-			if (put_user (rs1, rd)) return -1;
-		}
+		if (store_reg(rs1, rdv, regs))
+			return -1;
 		regs->y = rs2;
 		break;
 	case 14: /* udiv */
@@ -179,8 +180,8 @@
 #ifdef DEBUG_MULDIV
 		printk ("0x%x\n", rs1);
 #endif
-		if (rd)
-                        if (put_user (rs1, rd)) return -1;
+		if (store_reg(rs1, rdv, regs))
+			return -1;
 		break;
 	case 15: /* sdiv */
 #ifdef DEBUG_MULDIV
@@ -208,8 +209,8 @@
 #ifdef DEBUG_MULDIV
 		printk ("0x%x\n", rs1);
 #endif
-		if (rd)
-                        if (put_user (rs1, rd)) return -1;
+		if (store_reg(rs1, rdv, regs))
+			return -1;
 		break;
 	}
 	if (is_foocc (insn)) {
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/process.c linux/arch/sparc/kernel/process.c
--- v2.1.78/linux/arch/sparc/kernel/process.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc/kernel/process.c	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.100 1997/08/10 04:49:23 davem Exp $
+/*  $Id: process.c,v 1.102 1997/12/01 03:36:31 davem Exp $
  *  linux/arch/sparc/kernel/process.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -120,7 +120,7 @@
 		}
 		/* endless idle loop with no priority at all */
 		current->counter = -100;
-		if(!smp_commenced || resched_needed())
+		if(!smp_commenced || need_resched)
 			schedule();
 	}
 }
@@ -197,7 +197,9 @@
 	       rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
 }
 
+#ifdef __SMP__
 static spinlock_t sparc_backtrace_lock = SPIN_LOCK_UNLOCKED;
+#endif
 
 void show_backtrace(void)
 {
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c
--- v2.1.78/linux/arch/sparc/kernel/ptrace.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/sparc/kernel/ptrace.c	Mon Jan 12 15:15:43 1998
@@ -799,7 +799,7 @@
 		addr = 1;
 
 	case PTRACE_CONT: { /* restart after signal. */
-		if ((unsigned long) data > NSIG) {
+		if ((unsigned long) data > _NSIG) {
 			pt_error_return(regs, EIO);
 			goto out;
 		}
@@ -851,7 +851,7 @@
 	}
 
 	case PTRACE_SUNDETACH: { /* detach a process that was attached. */
-		if ((unsigned long) data > NSIG) {
+		if ((unsigned long) data > _NSIG) {
 			pt_error_return(regs, EIO);
 			goto out;
 		}
@@ -898,9 +898,7 @@
 		current->pid, current->exit_code);
 #endif
 	if (current->exit_code) {
-		spin_lock_irq(&current->sigmask_lock);
-		current->signal |= (1 << (current->exit_code - 1));
-		spin_unlock_irq(&current->sigmask_lock);
+		send_sig (current->exit_code, current, 1);
+		current->exit_code = 0;
 	}
-	current->exit_code = 0;
 }
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/rtrap.S linux/arch/sparc/kernel/rtrap.S
--- v2.1.78/linux/arch/sparc/kernel/rtrap.S	Mon Aug 18 18:19:44 1997
+++ linux/arch/sparc/kernel/rtrap.S	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/* $Id: rtrap.S,v 1.47 1997/08/10 04:49:24 davem Exp $
+/* $Id: rtrap.S,v 1.49 1997/12/14 23:24:24 ecd Exp $
  * rtrap.S: Return from Sparc trap low-level code.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -65,27 +65,24 @@
 
 	wr	%t_psr, 0x0, %psr
 	b	ret_trap_kernel
-	 mov	1, %o0
+	 nop
 
 1:
-	ld	[%curptr + AOFF_task_processor], %o1
 	ld	[%twin_tmp1 + %lo(C_LABEL(need_resched))], %g2
-	sll	%o0, %o1, %o0
-
-	andcc	%g2, %o0, %g0
+	orcc	%g2, %g0, %g0
 	be	signal_p
-	 nop
+	 ld	[%curptr + AOFF_task_sigpending], %g2
 
 	call	C_LABEL(schedule)
 	 nop
 
+	ld	[%curptr + AOFF_task_sigpending], %g2
 signal_p:
-	ld	[%curptr + AOFF_task_signal], %g2
-	ld	[%curptr + AOFF_task_blocked], %o0
-	andncc	%g2, %o0, %g0
-	be,a	ret_trap_continue
+	cmp	%g2, 0
+	bz,a	ret_trap_continue
 	 ld	[%sp + REGWIN_SZ + PT_PSR], %t_psr
 
+	clr	%o0
 	mov	%l5, %o2
 	mov	%l6, %o3
 	call	C_LABEL(do_signal)
@@ -110,7 +107,8 @@
 	call	C_LABEL(try_to_clear_window_buffer)
 	 add	%sp, REGWIN_SZ, %o0
 
-	b,a	signal_p
+	b	signal_p
+	 ld	[%curptr + AOFF_task_sigpending], %g2
 
 ret_trap_nobufwins:
 	/* Load up the user's out registers so we can pull
@@ -179,7 +177,8 @@
 	call	C_LABEL(do_memaccess_unaligned)
 	 nop
 
-	b,a	signal_p
+	b	signal_p
+	 ld	[%curptr + AOFF_task_sigpending], %g2
 
 ret_trap_kernel:
 		/* Will the rett land us in the invalid window? */
@@ -228,7 +227,8 @@
 	call	C_LABEL(window_ret_fault)
 	 add	%sp, REGWIN_SZ, %o0
 
-	b,a	signal_p
+	b	signal_p
+	 ld	[%curptr + AOFF_task_sigpending], %g2
 
 	.globl	C_LABEL(sun4c_rett_stackchk)
 C_LABEL(sun4c_rett_stackchk):
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/setup.c linux/arch/sparc/kernel/setup.c
--- v2.1.78/linux/arch/sparc/kernel/setup.c	Thu May 29 21:53:04 1997
+++ linux/arch/sparc/kernel/setup.c	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.85 1997/05/27 06:45:54 davem Exp $
+/*  $Id: setup.c,v 1.87 1997/12/18 02:42:42 ecd Exp $
  *  linux/arch/sparc/kernel/setup.c
  *
  *  Copyright (C) 1995  David S. Miller (davem@caip.rutgers.edu)
@@ -25,6 +25,7 @@
 #include <linux/blk.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/console.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -118,7 +119,9 @@
 unsigned int boot_flags;
 #define BOOTME_DEBUG  0x1
 #define BOOTME_SINGLE 0x2
-#define BOOTME_KGDB   0x4
+#define BOOTME_KGDBA  0x4
+#define BOOTME_KGDBB  0x8
+#define BOOTME_KGDB   0xc
 
 #ifdef CONFIG_SUN_CONSOLE
 extern char *console_fb_path;
@@ -188,15 +191,14 @@
 				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);
+				boot_flags |= BOOTME_KGDBA;
 				prom_printf("KGDB: Using serial line /dev/ttya.\n");
 				break;
 			case 'b':
-				rs_kgdb_hook(1);
+				boot_flags |= BOOTME_KGDBB;
 				prom_printf("KGDB: Using serial line /dev/ttyb.\n");
 				break;
 #endif
@@ -207,7 +209,6 @@
 #endif
 			default:
 				printk("KGDB: Unknown tty line.\n");
-				boot_flags &= ~BOOTME_KGDB;
 				break;
 			}
 			commands += 9;
@@ -273,8 +274,6 @@
 
 extern int root_mountflags;
 
-extern void register_console(void (*proc)(const char *));
-
 char saved_command_line[256];
 char reboot_command[256];
 enum sparc_cpu sparc_cpu_model;
@@ -283,6 +282,16 @@
 
 static struct pt_regs fake_swapper_regs = { 0, 0, 0, 0, { 0, } };
 
+static void prom_cons_write(struct console *con, const char *str, unsigned count)
+{
+	while (count--)
+		prom_printf("%c", *str++);
+}
+
+static struct console prom_console = {
+	"PROM", prom_cons_write, 0, 0, 0, 0, 0, CON_PRINTBUFFER, 0, 0, 0
+};
+
 __initfunc(void setup_arch(char **cmdline_p,
 	unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
@@ -335,7 +344,7 @@
 		printk("SUN4U\n");
 		break;
 	case ap1000:
-		register_console((void (*) (const char *))prom_printf);
+		register_console(&prom_console);
 		printk("AP1000\n");
 		packed = 1;
 		break;
@@ -345,16 +354,6 @@
 	};
 
 	boot_flags_init(*cmdline_p);
-	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();
-	}
 
 	idprom_init();
 	load_mmu();
@@ -410,6 +409,56 @@
 	}
 not_relevant:
 		
+#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
+	}
+
+	if ((boot_flags & BOOTME_KGDBA)) {
+		rs_kgdb_hook(0);
+	}
+	if ((boot_flags & BOOTME_KGDBB)) {
+		rs_kgdb_hook(1);
+	}
+
+	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();
+	}
+
 	if (!root_flags)
 		root_mountflags &= ~MS_RDONLY;
 	ROOT_DEV = to_kdev_t(root_dev);
@@ -443,37 +492,6 @@
 	init_task.mm->context = (unsigned long) NO_CONTEXT;
 	init_task.tss.kregs = &fake_swapper_regs;
 
-#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)
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/signal.c linux/arch/sparc/kernel/signal.c
--- v2.1.78/linux/arch/sparc/kernel/signal.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/sparc/kernel/signal.c	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/*  $Id: signal.c,v 1.75 1997/08/05 19:19:26 davem Exp $
+/*  $Id: signal.c,v 1.77 1997/12/22 03:06:32 ecd Exp $
  *  linux/arch/sparc/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -24,9 +24,7 @@
 #include <asm/svr4.h>
 #include <asm/pgtable.h>
 
-#define _S(nr) (1<<((nr)-1))
-
-#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
 asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
 			 int options, unsigned long *ru);
@@ -35,7 +33,7 @@
 		   void *fpqueue, unsigned long *fpqdepth);
 extern void fpload(unsigned long *fpregs, unsigned long *fsr);
 
-asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs,
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs,
 			 unsigned long orig_o0, int ret_from_syscall);
 
 /* This turned off for production... */
@@ -56,12 +54,13 @@
  * ---------------------------------- <-- New %sp
  */
 struct signal_sframe {
-	struct reg_window sig_window;
-	int sig_num;
-	int sig_code;
-	struct sigcontext *sig_scptr;
-	int sig_address;
-	struct sigcontext sig_context;
+	struct reg_window	sig_window;
+	int			sig_num;
+	int			sig_code;
+	struct sigcontext	*sig_scptr;
+	int			sig_address;
+	struct sigcontext	sig_context;
+	unsigned int		extramask[_NSIG_WORDS - 1];
 };
 
 /* 
@@ -75,6 +74,7 @@
 	__siginfo_t		info;
 	__siginfo_fpu_t		*fpu_save;
 	unsigned long		insns [2] __attribute__ ((aligned (8)));
+	unsigned int		extramask[_NSIG_WORDS - 1];
 	__siginfo_fpu_t		fpu_state;
 };
 
@@ -86,13 +86,15 @@
  * atomically swap in the new signal mask, and wait for a signal.
  * This is really tricky on the Sparc, watch out...
  */
-asmlinkage void _sigpause_common(unsigned int set, struct pt_regs *regs)
+asmlinkage void _sigpause_common(old_sigset_t set, struct pt_regs *regs)
 {
-	unsigned long mask;
+	sigset_t saveset;
 
+	set &= _BLOCKABLE;
 	spin_lock_irq(&current->sigmask_lock);
-	mask = current->blocked;
-	current->blocked = set & _BLOCKABLE;
+	saveset = current->blocked;
+	siginitset(&current->blocked, set);
+	recalc_sigpending(current);
 	spin_unlock_irq(&current->sigmask_lock);
 
 	regs->pc = regs->npc;
@@ -112,7 +114,7 @@
 		 */
 		regs->psr |= PSR_C;
 		regs->u_regs[UREG_I0] = EINTR;
-		if (do_signal(mask, regs, 0, 0))
+		if (do_signal(&saveset, regs, 0, 0))
 			return;
 	}
 }
@@ -127,6 +129,52 @@
 	_sigpause_common(regs->u_regs[UREG_I0], regs);
 }
 
+asmlinkage void do_rt_sigsuspend(sigset_t *uset, size_t sigsetsize,
+				 struct pt_regs *regs)
+{
+	sigset_t oldset, set;
+
+	/* XXX: Don't preclude handling different sized sigset_t's.  */
+	if (sigsetsize != sizeof(sigset_t)) {
+		regs->psr |= PSR_C;
+		regs->u_regs[UREG_I0] = EINVAL;
+		return;
+	}
+
+	if (copy_from_user(&set, uset, sizeof(set))) {
+		regs->psr |= PSR_C;
+		regs->u_regs[UREG_I0] = EFAULT;
+		return;
+	}
+
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	oldset = current->blocked;
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+
+	regs->pc = regs->npc;
+	regs->npc += 4;
+
+	/* Condition codes and return value where set here for sigpause,
+	 * and so got used by setup_frame, which again causes sigreturn()
+	 * to return -EINTR.
+	 */
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		/*
+		 * Return -EINTR and set condition code here,
+		 * so the interrupted system call actually returns
+		 * these.
+		 */
+		regs->psr |= PSR_C;
+		regs->u_regs[UREG_I0] = EINTR;
+		if (do_signal(&oldset, regs, 0, 0))
+			return;
+	}
+}
 
 static inline void
 restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu)
@@ -154,11 +202,12 @@
 			       (sizeof(unsigned long *)))*16));
 }
 
-void do_new_sigreturn (struct pt_regs *regs)
+static inline void do_new_sigreturn (struct pt_regs *regs)
 {
 	struct new_signal_frame *sf;
-	unsigned long up_psr, pc, npc, mask;
-	
+	unsigned long up_psr, pc, npc;
+	sigset_t set;
+
 	sf = (struct new_signal_frame *) regs->u_regs [UREG_FP];
 
 	/* 1. Make sure we are not getting garbage from the user */
@@ -173,7 +222,7 @@
 
 	if ((pc | npc) & 3)
 		goto segv_and_exit;
-	
+
 	/* 2. Restore the state */
 	up_psr = regs->psr;
 	copy_from_user(regs, &sf->info.si_regs, sizeof (struct pt_regs));
@@ -181,28 +230,36 @@
 	/* User can only change condition codes and FPU enabling in %psr. */
 	regs->psr = (up_psr & ~(PSR_ICC | PSR_EF))
 		  | (regs->psr & (PSR_ICC | PSR_EF));
-	
+
 	if (sf->fpu_save)
 		restore_fpu_state(regs, sf->fpu_save);
 
 	/* This is pretty much atomic, no amount locking would prevent
 	 * the races which exist anyways.
 	 */
-	__get_user(mask, &sf->info.si_mask);
-	current->blocked = (mask & _BLOCKABLE);
+	if (__get_user(set.sig[0], &sf->info.si_mask) ||
+	    copy_from_user(&set.sig[1], &sf->extramask,
+			   (_NSIG_WORDS-1) * sizeof(unsigned int)))
+		goto segv_and_exit;
+
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
 	return;
 
 segv_and_exit:
 	/* Ugh, we need to grab master lock in these rare cases ;-( */
 	lock_kernel();
 	do_exit(SIGSEGV);
-	unlock_kernel();
 }
 
 asmlinkage void do_sigreturn(struct pt_regs *regs)
 {
 	struct sigcontext *scptr;
-	unsigned long pc, npc, psr, mask;
+	unsigned long pc, npc, psr;
+	sigset_t set;
 
 	synchronize_user_stack();
 
@@ -225,8 +282,17 @@
 	/* This is pretty much atomic, no amount locking would prevent
 	 * the races which exist anyways.
 	 */
-	__get_user(mask, &scptr->sigc_mask);
-	current->blocked = (mask & _BLOCKABLE);
+	if (__get_user(set.sig[0], &scptr->sigc_mask) ||
+	    /* Note that scptr + 1 points to extramask */
+	    copy_from_user(&set.sig[1], scptr + 1,
+			   (_NSIG_WORDS - 1) * sizeof(unsigned int)))
+		goto segv_and_exit;
+
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
 
 	__get_user(current->tss.sstk_info.cur_status, &scptr->sigc_onstack);
 	current->tss.sstk_info.cur_status &= 1;
@@ -248,11 +314,15 @@
 	/* Ugh, we need to grab master lock in these rare cases ;-( */
 	lock_kernel();
 	do_exit(SIGSEGV);
-	unlock_kernel();
+}
+
+asmlinkage void do_rt_sigreturn(struct pt_regs *regs)
+{
+	printk("XXX: FIXME: write do_rt_sigreturn\n");
 }
 
 /* Checks if the fp is valid */
-int invalid_frame_pointer (void *fp, int fplen)
+static inline int invalid_frame_pointer (void *fp, int fplen)
 {
 	if ((((unsigned long) fp) & 7) ||
 	    !__access_ok((unsigned long)fp, fplen) ||
@@ -263,8 +333,9 @@
 	return 0;
 }
 
-static void setup_frame(struct sigaction *sa, unsigned long pc, unsigned long npc,
-			struct pt_regs *regs, int signr, unsigned long oldmask)
+static inline void
+setup_frame(struct sigaction *sa, unsigned long pc, unsigned long npc,
+	    struct pt_regs *regs, int signr, sigset_t *oldset)
 {
 	struct signal_sframe *sframep;
 	struct sigcontext *sc;
@@ -291,7 +362,9 @@
 
 	/* We've already made sure frame pointer isn't in kernel space... */
 	__put_user(old_status, &sc->sigc_onstack);
-	__put_user(oldmask, &sc->sigc_mask);
+	__put_user(oldset->sig[0], &sc->sigc_mask);
+	__copy_to_user(sframep->extramask, &oldset->sig[1],
+		       (_NSIG_WORDS - 1) * sizeof(unsigned int));
 	__put_user(regs->u_regs[UREG_FP], &sc->sigc_sp);
 	__put_user(pc, &sc->sigc_pc);
 	__put_user(npc, &sc->sigc_npc);
@@ -334,7 +407,6 @@
 	/* Ugh, we need to grab master lock in these rare cases ;-( */
 	lock_kernel();
 	do_exit(SIGILL);
-	unlock_kernel();
 }
 
 
@@ -369,8 +441,9 @@
 	current->used_math = 0;
 }
 
-static void new_setup_frame(struct sigaction *sa, struct pt_regs *regs,
-			    int signo, unsigned long oldmask)
+static inline void
+new_setup_frame(struct k_sigaction *ka, struct pt_regs *regs,
+		int signo, sigset_t *oldset)
 {
 	struct new_signal_frame *sf;
 	int sigframe_size;
@@ -403,39 +476,51 @@
 		sf->fpu_save = NULL;
 	}
 
-	__put_user(oldmask, &sf->info.si_mask);
+	__put_user(oldset->sig[0], &sf->info.si_mask);
+	__copy_to_user(sf->extramask, &oldset->sig[1],
+		       (_NSIG_WORDS - 1) * sizeof(unsigned int));
 	copy_to_user(sf, (char *) regs->u_regs [UREG_FP],
 		     sizeof (struct reg_window));
 	
-	/* 3. return to kernel instructions */
-	__put_user(0x821020d8, &sf->insns [0]); /* mov __NR_sigreturn, %g1 */
-	__put_user(0x91d02010, &sf->insns [1]); /* t 0x10 */
-
-	/* 4. signal handler back-trampoline and parameters */
+	/* 3. signal handler back-trampoline and parameters */
 	regs->u_regs[UREG_FP] = (unsigned long) sf;
 	regs->u_regs[UREG_I0] = signo;
 	regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
-	regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
 
-	/* 5. signal handler */
-	regs->pc = (unsigned long) sa->sa_handler;
+	/* 4. signal handler */
+	regs->pc = (unsigned long) ka->sa.sa_handler;
 	regs->npc = (regs->pc + 4);
 
-	/* Flush instruction space. */
-	flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0]));
+	/* 5. return to kernel instructions */
+	if (ka->ka_restorer)
+		regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
+	else {
+		regs->u_regs[UREG_I7] = (unsigned long)(&(sf->insns[0]) - 2);
+
+		__put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */
+		__put_user(0x91d02010, &sf->insns[1]); /* t 0x10 */
+
+		/* Flush instruction space. */
+		flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0]));
+	}
 	return;
 
 sigill_and_return:
 	lock_kernel();
 	do_exit(SIGILL);
-	unlock_kernel();
 }
 
+static inline void
+new_setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs,
+		   int signo, sigset_t *oldset, siginfo_t *info)
+{
+	printk("XXX: FIXME: new_setup_rt_frame unimplemented\n");
+}
 
 /* Setup a Solaris stack frame */
 static inline void
 setup_svr4_frame(struct sigaction *sa, unsigned long pc, unsigned long npc,
-		 struct pt_regs *regs, int signr, unsigned long oldmask)
+		 struct pt_regs *regs, int signr, sigset_t *oldset)
 {
 	svr4_signal_frame_t *sfp;
 	svr4_gregset_t  *gr;
@@ -443,6 +528,7 @@
 	svr4_mcontext_t *mc;
 	svr4_gwindows_t *gw;
 	svr4_ucontext_t *uc;
+	svr4_sigset_t	setv;
 	int window = 0;
 
 	synchronize_user_stack();
@@ -470,7 +556,14 @@
 	 * sc->sigc_onstack = old_status;
 	 * anyways, it does not look like it is used for anything at all.
 	 */
-	__put_user(oldmask, &uc->sigmask.sigbits [0]);
+	setv.sigbits[0] = oldset->sig[0];
+	setv.sigbits[1] = oldset->sig[1];
+	if (_NSIG_WORDS >= 4) {
+		setv.sigbits[2] = oldset->sig[2];
+		setv.sigbits[3] = oldset->sig[3];
+		__copy_to_user(&uc->sigmask, &setv, sizeof(svr4_sigset_t));
+	} else
+		__copy_to_user(&uc->sigmask, &setv, 2 * sizeof(unsigned int));
 
 	/* Store registers */
 	__put_user(regs->pc, &((*gr) [SVR4_PC]));
@@ -547,13 +640,13 @@
 sigill_and_return:
 	lock_kernel();
 	do_exit(SIGILL);
-	unlock_kernel();
 }
 
 asmlinkage int svr4_getcontext (svr4_ucontext_t *uc, struct pt_regs *regs)
 {
 	svr4_gregset_t  *gr;
 	svr4_mcontext_t *mc;
+	svr4_sigset_t	setv;
 
 	synchronize_user_stack();
 
@@ -566,9 +659,15 @@
 	/* Setup convenience variables */
 	mc = &uc->mcontext;
 	gr = &mc->greg;
-	
-	/* We only have < 32 signals, fill the first slot only */
-	__put_user(current->blocked, &uc->sigmask.sigbits [0]);
+
+	setv.sigbits[0] = current->blocked.sig[0];
+	setv.sigbits[1] = current->blocked.sig[1];
+	if (_NSIG_WORDS >= 4) {
+		setv.sigbits[2] = current->blocked.sig[2];
+		setv.sigbits[3] = current->blocked.sig[3];
+		__copy_to_user(&uc->sigmask, &setv, sizeof(svr4_sigset_t));
+	} else
+		__copy_to_user(&uc->sigmask, &setv, 2 * sizeof(unsigned int));
 
 	/* Store registers */
 	__put_user(regs->pc, &uc->mcontext.greg [SVR4_PC]);
@@ -593,8 +692,6 @@
 sigsegv_and_return:
 	lock_kernel();
 	do_exit(SIGSEGV);
-	unlock_kernel();
-	return -EFAULT;
 }
 
 /* Set the context for a svr4 application, this is Solaris way to sigreturn */
@@ -602,7 +699,9 @@
 {
 	struct thread_struct *tp = &current->tss;
 	svr4_gregset_t  *gr;
-	unsigned long pc, npc, psr, mask;
+	unsigned long pc, npc, psr;
+	sigset_t set;
+	svr4_sigset_t setv;
 	
 	/* Fixme: restore windows, or is this already taken care of in
 	 * svr4_setup_frame when sync_user_windows is done?
@@ -633,8 +732,19 @@
 	/* This is pretty much atomic, no amount locking would prevent
 	 * the races which exist anyways.
 	 */
-	__get_user(mask, &c->sigmask.sigbits [0]);
-	current->blocked = (mask & _BLOCKABLE);
+	if (__copy_from_user(&setv, &c->sigmask, sizeof(svr4_sigset_t)))
+		goto sigsegv_and_return;
+	set.sig[0] = setv.sigbits[0];
+	set.sig[1] = setv.sigbits[1];
+	if (_NSIG_WORDS >= 4) {
+		set.sig[2] = setv.sigbits[2];
+		set.sig[3] = setv.sigbits[3];
+	}
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
 	regs->pc = pc;
 	regs->npc = npc | 1;
 	__get_user(regs->y, &((*gr) [SVR4_Y]));
@@ -650,27 +760,29 @@
 sigsegv_and_return:
 	lock_kernel();
 	do_exit(SIGSEGV);
-	unlock_kernel();
-	return -EFAULT;
 }
 
-static inline void handle_signal(unsigned long signr, struct sigaction *sa,
-				 unsigned long oldmask, struct pt_regs *regs,
-				 int svr4_signal)
+static inline void
+handle_signal(unsigned long signr, struct k_sigaction *ka,
+	      siginfo_t *info, sigset_t *oldset, struct pt_regs *regs,
+	      int svr4_signal)
 {
-	if(svr4_signal)
-		setup_svr4_frame(sa, regs->pc, regs->npc, regs, signr, oldmask);
+	if (svr4_signal)
+		setup_svr4_frame(&ka->sa, regs->pc, regs->npc, regs, signr, oldset);
 	else {
-		if (current->tss.new_signal)
-			new_setup_frame (sa, regs, signr, oldmask);
+		if (ka->sa.sa_flags & SA_SIGINFO)
+			new_setup_rt_frame(ka, regs, signr, oldset, info);
+		else if (current->tss.new_signal)
+			new_setup_frame (ka, regs, signr, oldset);
 		else
-			setup_frame(sa, regs->pc, regs->npc, regs, signr, oldmask);
+			setup_frame(&ka->sa, regs->pc, regs->npc, regs, signr, oldset);
 	}
-	if(sa->sa_flags & SA_ONESHOT)
-		sa->sa_handler = NULL;
-	if(!(sa->sa_flags & SA_NOMASK)) {
+	if(ka->sa.sa_flags & SA_ONESHOT)
+		ka->sa.sa_handler = SIG_DFL;
+	if(!(ka->sa.sa_flags & SA_NOMASK)) {
 		spin_lock_irq(&current->sigmask_lock);
-		current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE;
+		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+		sigaddset(&current->blocked, signr);
 		spin_unlock_irq(&current->sigmask_lock);
 	}
 }
@@ -699,22 +811,25 @@
  * want to handle. Thus you cannot kill init even with a SIGKILL even by
  * mistake.
  */
-asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs,
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs,
 			 unsigned long orig_i0, int restart_syscall)
 {
-	unsigned long signr, mask = ~current->blocked;
-	struct sigaction *sa;
+	unsigned long signr;
+	struct k_sigaction *ka;
+	siginfo_t info;
+
 	int svr4_signal = current->personality == PER_SVR4;
-	
-	while ((signr = current->signal & mask) != 0) {
-		signr = ffz(~signr);
 
+	if (!oldset)
+		oldset = &current->blocked;
+
+	for (;;) {
 		spin_lock_irq(&current->sigmask_lock);
-		current->signal &= ~(1 << signr);
+		signr = dequeue_signal(&current->blocked, &info);
 		spin_unlock_irq(&current->sigmask_lock);
 
-		sa = current->sig->action + signr;
-		signr++;
+		if (!signr) break;
+
 		if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
 			current->exit_code = signr;
 			current->state = TASK_STOPPED;
@@ -729,15 +844,26 @@
 			current->exit_code = 0;
 			if (signr == SIGSTOP)
 				continue;
-			if (_S(signr) & current->blocked) {
-				spin_lock_irq(&current->sigmask_lock);
-				current->signal |= _S(signr);
-				spin_unlock_irq(&current->sigmask_lock);
+
+			/* Update the siginfo structure.  Is this good?  */
+			if (signr != info.si_signo) {
+				info.si_signo = signr;
+				info.si_errno = 0;
+				info.si_code = SI_USER;
+				info.si_pid = current->p_pptr->pid;
+				info.si_uid = current->p_pptr->uid;
+			}
+
+			/* If the (new) signal is now blocked, requeue it.  */
+			if (sigismember(&current->blocked, signr)) {
+				send_sig_info(signr, &info, current);
 				continue;
 			}
-			sa = current->sig->action + signr - 1;
 		}
-		if(sa->sa_handler == SIG_IGN) {
+
+		ka = &current->sig->action[signr-1];
+
+		if(ka->sa.sa_handler == SIG_IGN) {
 			if(signr != SIGCHLD)
 				continue;
 
@@ -750,7 +876,9 @@
 				;
 			continue;
 		}
-		if(sa->sa_handler == SIG_DFL) {
+		if(ka->sa.sa_handler == SIG_DFL) {
+			unsigned long exit_code = signr;
+
 			if(current->pid == 1)
 				continue;
 			switch(signr) {
@@ -758,8 +886,9 @@
 				continue;
 
 			case SIGTSTP: case SIGTTIN: case SIGTTOU:
-				/* The operations performed by is_orphaned_pgrp()
-				 * are protected by the tasklist_lock.
+				/* The operations performed by
+				 * is_orphaned_pgrp() are protected by
+				 * the tasklist_lock.
 				 */
 				if (is_orphaned_pgrp(current->pgrp))
 					continue;
@@ -771,7 +900,7 @@
 				current->exit_code = signr;
 
 				/* notify_parent() is SMP safe */
-				if(!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags &
+				if(!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags &
 				     SA_NOCLDSTOP))
 					notify_parent(current, SIGCHLD);
 				schedule();
@@ -782,7 +911,7 @@
 				if(current->binfmt && current->binfmt->core_dump) {
 					lock_kernel();
 					if(current->binfmt->core_dump(signr, regs))
-						signr |= 0x80;
+						exit_code |= 0x80;
 					unlock_kernel();
 				}
 #ifdef DEBUG_SIGNALS
@@ -792,20 +921,16 @@
 #endif
 				/* fall through */
 			default:
-				spin_lock_irq(&current->sigmask_lock);
-				current->signal |= _S(signr & 0x7f);
-				spin_unlock_irq(&current->sigmask_lock);
-
+				lock_kernel();
+				sigaddset(&current->signal, signr);
 				current->flags |= PF_SIGNALED;
-
-				lock_kernel(); /* 8-( */
-				do_exit(signr);
-				unlock_kernel();
+				do_exit(exit_code);
+				/* NOT REACHED */
 			}
 		}
 		if(restart_syscall)
-			syscall_restart(orig_i0, regs, sa);
-		handle_signal(signr, sa, oldmask, regs, svr4_signal);
+			syscall_restart(orig_i0, regs, &ka->sa);
+		handle_signal(signr, ka, &info, oldset, regs, svr4_signal);
 		return 1;
 	}
 	if(restart_syscall &&
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/smp.c linux/arch/sparc/kernel/smp.c
--- v2.1.78/linux/arch/sparc/kernel/smp.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/sparc/kernel/smp.c	Mon Jan 12 15:15:43 1998
@@ -558,7 +558,7 @@
 /* Reschedule call back. */
 void smp_reschedule_irq(void)
 {
-	resched_force();
+	need_resched = 1;
 }
 
 /* Running cross calls. */
@@ -583,6 +583,8 @@
 /* Protects counters touched during level14 ticker */
 spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED;
 
+#ifdef CONFIG_PROFILE
+
 /* 32-bit Sparc specific profiling function. */
 static inline void sparc_do_profile(unsigned long pc)
 {
@@ -601,6 +603,8 @@
 	}
 }
 
+#endif
+
 volatile unsigned long smp_local_timer_ticks[1+NR_CPUS]={0,};
 
 unsigned int prof_multiplier[NR_CPUS];
@@ -614,8 +618,10 @@
 	int cpu = smp_processor_id();
 
 	clear_profile_irq(mid_xlate[cpu]);
+#ifdef CONFIG_PROFILE
 	if(!user_mode(regs))
 		sparc_do_profile(regs->pc);
+#endif
 	if(!--prof_counter[cpu]) {
 		int user = user_mode(regs);
 		if(current->pid) {
@@ -623,7 +629,7 @@
 
 			if(--current->counter < 0) {
 				current->counter = 0;
-				resched_force();
+				need_resched = 1;
 			}
 
 			spin_lock(&ticker_lock);
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/sparc-stub.c linux/arch/sparc/kernel/sparc-stub.c
--- v2.1.78/linux/arch/sparc/kernel/sparc-stub.c	Sun Jan 26 02:07:07 1997
+++ linux/arch/sparc/kernel/sparc-stub.c	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/* $Id: sparc-stub.c,v 1.20 1997/01/06 06:52:31 davem Exp $
+/* $Id: sparc-stub.c,v 1.22 1998/01/07 06:33:48 baccala Exp $
  * sparc-stub.c:  KGDB support for the Linux kernel.
  *
  * Modifications to run under Linux
@@ -323,7 +323,23 @@
 	unsigned char ch;
 
 	while (count-- > 0) {
-		ch = *mem++;
+		/* This assembler code is basically:  ch = *mem++;
+		 * except that we use the SPARC/Linux exception table
+		 * mechanism (see how "fixup" works in kernel_mna_trap_fault)
+		 * to arrange for a "return 0" upon a memory fault
+		 */
+		__asm__(
+			"1:	ldub [%0], %1
+				inc %0
+				.section .fixup,#alloc,#execinstr
+				.align 4
+			 2:	retl
+				 mov 0, %%o0
+				.section __ex_table, #alloc
+				.align 4
+				.word 1b, 2b
+				.text"
+					: "=r" (mem), "=r" (ch) : "0" (mem));
 		*buf++ = hexchars[ch >> 4];
 		*buf++ = hexchars[ch & 0xf];
 	}
@@ -345,7 +361,19 @@
 
 		ch = hex(*buf++) << 4;
 		ch |= hex(*buf++);
-		*mem++ = ch;
+		/* Assembler code is   *mem++ = ch;   with return 0 on fault */
+		__asm__(
+			"1:	stb %1, [%0]
+				inc %0
+				.section .fixup,#alloc,#execinstr
+				.align 4
+			 2:	retl
+				 mov 0, %%o0
+				.section __ex_table, #alloc
+				.align 4
+				.word 1b, 2b
+				.text"
+					: "=r" (mem) : "r" (ch) , "0" (mem));
 	}
 	return mem;
 }
@@ -387,13 +415,18 @@
 
 	/* In case GDB is started before us, ack any packets (presumably
 	 * "$?#xx") sitting there.
+	 *
+	 * I've found this code causes more problems than it solves,
+	 * so that's why it's commented out.  GDB seems to work fine
+	 * now starting either before or after the kernel   -bwb
 	 */
-
+#if 0
 	while((c = getDebugChar()) != '$');
 	while((c = getDebugChar()) != '#');
 	c = getDebugChar(); /* eat first csum byte */
 	c = getDebugChar(); /* eat second csum byte */
 	putDebugChar('+'); /* ack it */
+#endif
 
 	initialized = 1; /* connect! */
 	restore_flags(flags);
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/sparc_ksyms.c linux/arch/sparc/kernel/sparc_ksyms.c
--- v2.1.78/linux/arch/sparc/kernel/sparc_ksyms.c	Mon Jul  7 08:18:54 1997
+++ linux/arch/sparc/kernel/sparc_ksyms.c	Mon Jan 12 15:15:43 1998
@@ -1,10 +1,12 @@
-/* $Id: sparc_ksyms.c,v 1.60 1997/06/17 13:25:13 jj Exp $
+/* $Id: sparc_ksyms.c,v 1.61 1997/11/19 07:57:44 jj Exp $
  * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
  */
 
+/* Tell string.h we don't want memcpy etc. as cpp defines */
+#define EXPORT_SYMTAB_STROPS
 #define PROMLIB_INTERNAL
 
 #include <linux/config.h>
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/sun4d_irq.c linux/arch/sparc/kernel/sun4d_irq.c
--- v2.1.78/linux/arch/sparc/kernel/sun4d_irq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/kernel/sun4d_irq.c	Mon Jan 12 15:15:43 1998
@@ -0,0 +1,483 @@
+/*  $Id: sun4d_irq.c,v 1.3 1997/12/22 16:09:15 jj Exp $
+ *  arch/sparc/kernel/sun4d_irq.c: 
+ *			SS1000/SC2000 interrupt handling.
+ *
+ *  Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *  Heavily based on arch/sparc/kernel/irq.c.
+ */
+
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/psr.h>
+#include <asm/smp.h>
+#include <asm/vaddrs.h>
+#include <asm/timer.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/traps.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/spinlock.h>
+#include <asm/sbus.h>
+#include <asm/sbi.h>
+
+struct sun4d_timer_regs *sun4d_timers;
+#define TIMER_IRQ	10
+
+#define MAX_STATIC_ALLOC	4
+extern struct irqaction static_irqaction[MAX_STATIC_ALLOC];
+extern int static_irq_count;
+
+extern struct irqaction *irq_action[];
+
+struct sbus_action {
+	struct irqaction *action;
+	unsigned char	lock;
+	unsigned char	active;
+	unsigned char	disabled;
+} *sbus_actions;
+
+static int pil_to_sbus[] = {
+	0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0,
+};
+
+static int nsbi;
+
+int sun4d_get_irq_list(char *buf)
+{
+	int i, j = 0, k = 0, len = 0, sbusl;
+	struct irqaction * action;
+
+	for (i = 0 ; i < NR_IRQS ; i++) {
+		sbusl = pil_to_sbus[i];
+		if (!sbusl) {
+	 		action = *(i + irq_action);
+			if (!action) 
+		        	continue;
+		} else {
+			for (j = 0; j < nsbi; j++) {
+				for (k = 0; k < 4; k++)
+					if ((action = sbus_actions [(j << 5) + (sbusl << 2) + k].action))
+						goto found_it;
+			}
+			continue;
+		}
+found_it:	len += sprintf(buf+len, "%2d: %8d %c %s",
+			i, kstat.interrupts[i],
+			(action->flags & SA_INTERRUPT) ? '+' : ' ',
+			action->name);
+		action = action->next;
+		for (;;) {
+			for (; action; action = action->next) {
+				len += sprintf(buf+len, ",%s %s",
+					(action->flags & SA_INTERRUPT) ? " +" : "",
+					action->name);
+			}
+			if (!sbusl) break;
+			k++;
+			if (k < 4)
+				action = sbus_actions [(j << 5) + (sbusl << 2) + k].action;
+			else {
+				j++;
+				if (j == nsbi) break;
+				k = 0;
+				action = sbus_actions [(j << 5) + (sbusl << 2)].action;
+			}
+		}
+		len += sprintf(buf+len, "\n");
+	}
+	return len;
+}
+
+void sun4d_free_irq(unsigned int irq, void *dev_id)
+{
+	struct irqaction *action, **actionp;
+	struct irqaction *tmp = NULL;
+        unsigned long flags;
+	
+	if (irq < 15)
+		actionp = irq + irq_action;
+	else
+		actionp = &(sbus_actions[irq - (1 << 5)].action);
+	action = *actionp;
+	if (!action) {
+		printk("Trying to free free IRQ%d\n",irq);
+		return;
+	}
+	if (dev_id) {
+		for (; action; action = action->next) {
+			if (action->dev_id == dev_id)
+				break;
+			tmp = action;
+		}
+		if (!action) {
+			printk("Trying to free free shared IRQ%d\n",irq);
+			return;
+		}
+	} else if (action->flags & SA_SHIRQ) {
+		printk("Trying to free shared IRQ%d with NULL device ID\n", irq);
+		return;
+	}
+	if (action->flags & SA_STATIC_ALLOC)
+	{
+	    /* This interrupt is marked as specially allocated
+	     * so it is a bad idea to free it.
+	     */
+	    printk("Attempt to free statically allocated IRQ%d (%s)\n",
+		   irq, action->name);
+	    return;
+	}
+	
+        save_and_cli(flags);
+	if (action && tmp)
+		tmp->next = action->next;
+	else
+		*actionp = action->next;
+
+	kfree_s(action, sizeof(struct irqaction));
+
+	if (!(*actionp))
+		disable_irq(irq);
+
+	restore_flags(flags);
+}
+
+extern void unexpected_irq(int, void *, struct pt_regs *);
+
+void sun4d_handler_irq(int irq, struct pt_regs * regs)
+{
+	struct irqaction * action;
+	int cpu = smp_processor_id();
+	/* SBUS IRQ level (1 - 7) */
+	int sbusl = pil_to_sbus[irq];
+	
+	/* FIXME: Is this necessary?? */
+	cc_get_ipen();
+	
+	cc_set_iclr(1 << irq);
+	
+	irq_enter(cpu, irq, regs);
+	kstat.interrupts[irq]++;
+	if (!sbusl) {
+		action = *(irq + irq_action);
+		if (!action)
+			unexpected_irq(irq, 0, regs);
+		do {
+			action->handler(irq, action->dev_id, regs);
+			action = action->next;
+		} while (action);
+	} else {
+		int bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff;
+		int lock;
+		int sbino;
+		struct sbus_action *actionp;
+		unsigned mask, slot;
+		int sbil = (sbusl << 2);
+		
+		bw_clear_intr_mask(sbusl, bus_mask);
+		
+		/* Loop for each pending SBI */
+		for (sbino = 0; bus_mask; sbino++, bus_mask >>= 1)
+			if (bus_mask & 1) {
+				mask = acquire_sbi(SBI2DEVID(sbino), 0xf << sbil);
+				mask &= (0xf << sbil);
+				actionp = sbus_actions + (sbino << 5) + (sbil);
+				/* Loop for each pending SBI slot */
+				for (slot = (1 << sbil); mask; slot <<= 1, actionp++)
+					if (mask & slot) {
+						mask &= ~slot;
+						action = actionp->action;
+						__asm__ __volatile__ ("ldstub	[%1 + 4], %0"
+						: "=r" (lock) : "r" (actionp));
+						
+						if (!lock) {
+							if (!action)
+								unexpected_irq(irq, 0, regs);
+							do {
+								action->handler(irq, action->dev_id, regs);
+								action = action->next;
+							} while (action);
+							actionp->lock = 0;
+						} else
+							actionp->active = 1;
+						release_sbi(SBI2DEVID(sbino), slot);
+					}
+			}
+	}
+	irq_exit(cpu, irq);
+}
+
+int sun4d_request_irq(unsigned int irq,
+		void (*handler)(int, void *, struct pt_regs *),
+		unsigned long irqflags, const char * devname, void *dev_id)
+{
+	struct irqaction *action, *tmp = NULL, **actionp;
+	unsigned long flags;
+	int sbusl;
+	unsigned int *ret = NULL;
+	struct linux_sbus_device *sdev = NULL;
+	
+	if(irq > 14)
+		return -EINVAL;
+
+	if (!handler)
+	    return -EINVAL;
+	    
+	if (irqflags & SA_DCOOKIE) {
+		struct devid_cookie *d = (struct devid_cookie *)dev_id;
+		
+		dev_id = d->real_dev_id;
+		sdev = (struct linux_sbus_device *)d->bus_cookie;
+		ret = &d->ret_ino;
+	}
+	
+	sbusl = pil_to_sbus[irq];
+	if (sbusl && !sdev) {
+		printk ("Attempt to register SBUS IRQ %d without DCOOKIE\n", irq);
+		return -EINVAL;
+	}
+	if (sbusl) {
+		actionp = &(sbus_actions[(sdev->my_bus->board << 5) + 
+				(sbusl << 2) + sdev->slot].action);
+		*ret = ((sdev->my_bus->board + 1) << 5) +
+				(sbusl << 2) + sdev->slot;
+	} else
+		actionp = irq + irq_action;
+	action = *actionp;
+	
+	if (action) {
+		if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) {
+			for (tmp = action; tmp->next; tmp = tmp->next);
+		} else {
+			return -EBUSY;
+		}
+		if ((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) {
+			printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq);
+			return -EBUSY;
+		}   
+		action = NULL;		/* Or else! */
+	}
+
+	save_and_cli(flags);
+
+	/* If this is flagged as statically allocated then we use our
+	 * private struct which is never freed.
+	 */
+	if (irqflags & SA_STATIC_ALLOC)
+	    if (static_irq_count < MAX_STATIC_ALLOC)
+		action = &static_irqaction[static_irq_count++];
+	    else
+		printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n",irq, devname);
+	
+	if (action == NULL)
+	    action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
+						 GFP_KERNEL);
+	
+	if (!action) { 
+		restore_flags(flags);
+		return -ENOMEM;
+	}
+
+	action->handler = handler;
+	action->flags = irqflags;
+	action->mask = 0;
+	action->name = devname;
+	action->next = NULL;
+	action->dev_id = dev_id;
+
+	if (tmp)
+		tmp->next = action;
+	else
+		*actionp = action;
+		
+	if (ret) irq = *ret;
+
+	if (irq > NR_IRQS) {
+		struct sbus_action *s = sbus_actions + irq - (1 << 5);
+	
+		if (s->disabled) {
+			s->disabled = 0;
+			s->active = 0;
+			s->lock = 0;
+		}
+	}
+	
+	restore_flags(flags);
+	return 0;
+}
+
+static void sun4d_disable_irq(unsigned int irq)
+{
+	struct sbus_action *s;
+	
+	if (irq < NR_IRQS) {
+		/* FIXME */
+		printk ("Unable to disable IRQ %d\n", irq);
+		return;
+	}
+	s = sbus_actions + irq - (1 << 5);
+	
+	if (s->disabled) return;
+	s->disabled = 1;
+	__asm__ __volatile__ ("
+1:		ldstub	[%0 + 4], %%g1
+		orcc	%%g1, 0, %%g0
+		bne	1b"
+		: : "r" (s) : "g1", "cc");
+}
+
+static void sun4d_enable_irq(unsigned int irq)
+{
+	struct sbus_action *s;
+	struct irqaction *action;
+	
+	if (irq < NR_IRQS)
+		/* FIXME */
+		return;
+	s = sbus_actions + irq - (1 << 5);
+	
+	if (!s->disabled) return;
+	action = s->action;
+	s->disabled = 0;
+	while (s->active) {
+		s->active = 0;
+		while (action) {
+			/* FIXME: Hope no sbus intr handler uses regs */
+			action->handler(irq, action->dev_id, NULL);
+			action = action->next;
+		}
+	}
+	s->lock = 0;
+}
+
+#ifdef __SMP__
+
+/* +-------+-------------+-----------+------------------------------------+
+ * | bcast |  devid      |   sid     |              levels mask           |
+ * +-------+-------------+-----------+------------------------------------+
+ *  31      30         23 22       15 14                                 0
+ */
+#define IGEN_MESSAGE(bcast, devid, sid, levels) \
+	(((bcast) << 31) | ((devid) << 23) | ((sid) << 15) | (levels))
+
+static void sun4d_send_ipi(int cpu, int level)
+{
+	cc_set_igen(IGEN_MESSAGE(0, cpu << 3, 6 + ((level >> 1) & 7), 1 << (level - 1)));
+}
+
+static void sun4d_clear_ipi(int cpu, int level)
+{
+}
+
+static void sun4d_set_udt(int cpu)
+{
+}
+#endif
+ 
+static void sun4d_clear_clock_irq(void)
+{
+	volatile unsigned int clear_intr;
+	clear_intr = sun4d_timers->l10_timer_limit;
+}
+
+static void sun4d_clear_profile_irq(int cpu)
+{
+	bw_get_prof_limit(cpu);
+}
+
+static void sun4d_load_profile_irq(int cpu, unsigned int limit)
+{
+	bw_set_prof_limit(cpu, limit);
+}
+
+__initfunc(static void sun4d_init_timers(void (*counter_fn)(int, void *, struct pt_regs *)))
+{
+	int irq;
+	extern struct prom_cpuinfo linux_cpus[NCPUS];
+	int cpu;
+
+	/* Map the User Timer registers. */
+	sun4d_timers = sparc_alloc_io(BW_LOCAL_BASE+BW_TIMER_LIMIT, 0,
+				      PAGE_SIZE, "user timer", 0xf, 0x0);
+    
+	sun4d_timers->l10_timer_limit =  (((1000000/HZ) + 1) << 10);
+	master_l10_counter = &sun4d_timers->l10_cur_count;
+	master_l10_limit = &sun4d_timers->l10_timer_limit;
+
+	irq = request_irq(TIMER_IRQ,
+			  counter_fn,
+			  (SA_INTERRUPT | SA_STATIC_ALLOC),
+			  "timer", NULL);
+	if (irq) {
+		prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ);
+		prom_halt();
+	}
+	
+	/* Enable user timer free run for CPU 0 in BW */
+	/* bw_set_ctrl(0, bw_get_ctrl(0) | BW_CTRL_USER_TIMER); */
+    
+	for(cpu = 0; cpu < NCPUS; cpu++)
+		sun4d_load_profile_irq(linux_cpus[cpu].mid, 0);
+}
+
+__initfunc(unsigned long sun4d_init_sbi_irq(unsigned long memory_start))
+{
+	struct linux_sbus *sbus;
+	struct sbus_action *s;
+	int i;
+	unsigned mask;
+
+	nsbi = 0;
+	for_each_sbus(sbus)
+		nsbi++;
+	memory_start = ((memory_start + 7) & ~7);
+	sbus_actions = (struct sbus_action *)memory_start;
+	memory_start += (nsbi * 8 * 4 * sizeof(struct sbus_action));
+	memset (sbus_actions, 0, (nsbi * 8 * 4 * sizeof(struct sbus_action)));
+	for (i = 0, s = sbus_actions; i < nsbi * 8 * 4; i++, s++) {
+		s->lock = 0xff;
+		s->disabled = 1;
+	}
+	for_each_sbus(sbus) {
+		/* Get rid of pending irqs from PROM */
+		mask = acquire_sbi(sbus->devid, 0xffffffff);
+		if (mask) {
+			printk ("Clearing pending IRQs %08x on SBI %d\n", mask, sbus->board);
+			release_sbi(sbus->devid, mask);
+		}
+	}
+	return memory_start;
+}
+
+__initfunc(void sun4d_init_IRQ(void))
+{
+	__cli();
+
+	enable_irq = sun4d_enable_irq;
+	disable_irq = sun4d_disable_irq;
+	clear_clock_irq = sun4d_clear_clock_irq;
+	clear_profile_irq = sun4d_clear_profile_irq;
+	load_profile_irq = sun4d_load_profile_irq;
+	init_timers = sun4d_init_timers;
+#ifdef __SMP__
+	set_cpu_int = (void (*) (int, int))sun4d_send_ipi;
+	clear_cpu_int = (void (*) (int, int))sun4d_clear_ipi;
+	set_irq_udt = (void (*) (int))sun4d_set_udt;
+#endif
+	/* Cannot enable interrupts until OBP ticker is disabled. */
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/sun4m_irq.c linux/arch/sparc/kernel/sun4m_irq.c
--- v2.1.78/linux/arch/sparc/kernel/sun4m_irq.c	Wed Apr 23 19:01:16 1997
+++ linux/arch/sparc/kernel/sun4m_irq.c	Mon Jan 12 15:15:43 1998
@@ -163,7 +163,8 @@
 	sun4m_interrupts->clear = cpu_pil_to_imask[pil];
 }
 
-void sun4m_send_ipi(int cpu, int level)
+#ifdef __SMP__
+static void sun4m_send_ipi(int cpu, int level)
 {
 	unsigned long mask;
 
@@ -171,7 +172,7 @@
 	sun4m_interrupts->cpu_intregs[cpu].set = mask;
 }
 
-void sun4m_clear_ipi(int cpu, int level)
+static void sun4m_clear_ipi(int cpu, int level)
 {
 	unsigned long mask;
 
@@ -179,10 +180,11 @@
 	sun4m_interrupts->cpu_intregs[cpu].clear = mask;
 }
 
-void sun4m_set_udt(int cpu)
+static void sun4m_set_udt(int cpu)
 {
 	sun4m_interrupts->undirected_target = cpu;
 }
+#endif
 
 #define OBIO_INTR	0x20
 #define TIMER_IRQ  	(OBIO_INTR | 10)
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/sunos_ioctl.c linux/arch/sparc/kernel/sunos_ioctl.c
--- v2.1.78/linux/arch/sparc/kernel/sunos_ioctl.c	Mon Mar 17 14:54:21 1997
+++ linux/arch/sparc/kernel/sunos_ioctl.c	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/* $Id: sunos_ioctl.c,v 1.28 1997/02/15 01:17:05 davem Exp $
+/* $Id: sunos_ioctl.c,v 1.29 1997/09/18 10:37:31 rth Exp $
  * sunos_ioctl.c: The Linux Operating system: SunOS ioctl compatibility.
  * 
  * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -45,8 +45,8 @@
 
 	/* First handle an easy compat. case for tty ldisc. */
 	if(cmd == TIOCSETD) {
-		int *p, ntty = N_TTY;
-		int tmp, oldfs;
+		int *p, ntty = N_TTY, tmp;
+		mm_segment_t oldfs;
 
 		p = (int *) arg;
 		ret = -EFAULT;
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/sys_sparc.c linux/arch/sparc/kernel/sys_sparc.c
--- v2.1.78/linux/arch/sparc/kernel/sys_sparc.c	Wed Apr 23 19:01:16 1997
+++ linux/arch/sparc/kernel/sys_sparc.c	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.35 1997/04/16 05:56:09 davem Exp $
+/* $Id: sys_sparc.c,v 1.38 1998/01/09 16:42:48 jj Exp $
  * linux/arch/sparc/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -17,6 +17,7 @@
 #include <linux/shm.h>
 #include <linux/stat.h>
 #include <linux/mman.h>
+#include <linux/utsname.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 
@@ -248,52 +249,98 @@
 	unlock_kernel();
 }
 
-extern void check_pending(int signum);
-
 asmlinkage int
-sparc_sigaction (int signum, const struct sigaction *action, struct sigaction *oldaction)
+sparc_sigaction (int sig, const struct old_sigaction *act,
+		 struct old_sigaction *oact)
 {
-	struct sigaction new_sa, *p;
+	struct k_sigaction new_ka, old_ka;
+	int ret;
 
-	if(signum < 0) {
+	if (sig < 0) {
 		current->tss.new_signal = 1;
-		signum = -signum;
+		sig = -sig;
 	}
-	if(signum<1 || signum>32)
-		return -EINVAL;
 
-	p = signum - 1 + current->sig->action;
-	if(action) {
-		if(verify_area(VERIFY_READ,action,sizeof(struct sigaction)))
+	if (act) {
+		unsigned long mask;
+
+		if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
+		    __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+		    __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
 			return -EFAULT;
-		if (signum==SIGKILL || signum==SIGSTOP)
-			return -EINVAL;
-		if(copy_from_user(&new_sa, action, sizeof(struct sigaction)))
-			return -EFAULT;	
-		if (new_sa.sa_handler != SIG_DFL && new_sa.sa_handler != SIG_IGN) {
-			if(verify_area(VERIFY_READ, new_sa.sa_handler, 1))
-				return -EFAULT;
-		}
+		__get_user(new_ka.sa.sa_flags, &act->sa_flags);
+		__get_user(mask, &act->sa_mask);
+		siginitset(&new_ka.sa.sa_mask, mask);
+		new_ka.ka_restorer = NULL;
 	}
 
-	if (oldaction) {
+	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+	if (!ret && oact) {
 		/* In the clone() case we could copy half consistant
 		 * state to the user, however this could sleep and
 		 * deadlock us if we held the signal lock on SMP.  So for
 		 * now I take the easy way out and do no locking.
 		 */
-		if (copy_to_user(oldaction, p, sizeof(struct sigaction)))
-			return -EFAULT;	
+		if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
+		    __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+		    __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+			return -EFAULT;
+		__put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+		__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
 	}
 
-	if (action) {
-		spin_lock_irq(&current->sig->siglock);
-		*p = new_sa;
-		check_pending(signum);
-		spin_unlock_irq(&current->sig->siglock);
+	return ret;
+}
+
+asmlinkage int
+sys_rt_sigaction(int sig, const struct sigaction *act, struct sigaction *oact,
+		 void *restorer, size_t sigsetsize)
+{
+	struct k_sigaction new_ka, old_ka;
+	int ret;
+
+	/* XXX: Don't preclude handling different sized sigset_t's.  */
+	if (sigsetsize != sizeof(sigset_t))
+		return -EINVAL;
+
+	if (act) {
+		new_ka.ka_restorer = restorer;
+		if (copy_from_user(&new_ka.sa, act, sizeof(*act)))
+			return -EFAULT;
 	}
+
+	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+	if (!ret && oact) {
+		if (copy_to_user(oact, &old_ka.sa, sizeof(*oact)))
+			return -EFAULT;
+	}
+
+	return ret;
+}
+
+/* Just in case some old old binary calls this. */
+asmlinkage int sys_pause(void)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule();
+	return -ERESTARTNOHAND;
+}
+
+asmlinkage int sys_getdomainname(char *name, int len)
+{
+	int nlen = strlen(system_utsname.domainname);
+
+	if (nlen < len)
+		len = nlen;
+	if(len > __NEW_UTS_LEN)
+		return -EFAULT;
+	if(copy_to_user(name, system_utsname.domainname, len))
+		return -EFAULT;
 	return 0;
 }
+
 
 #ifndef CONFIG_AP1000
 /* only AP+ systems have sys_aplib */
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c
--- v2.1.78/linux/arch/sparc/kernel/sys_sunos.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc/kernel/sys_sunos.c	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.81 1997/07/20 05:59:31 davem Exp $
+/* $Id: sys_sunos.c,v 1.83 1997/12/14 23:24:28 ecd Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -348,35 +348,28 @@
 {
 	return SUNOS_NR_OPEN;
 }
-#define _S(nr) (1<<((nr)-1))
 
-#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
 asmlinkage unsigned long sunos_sigblock(unsigned long blk_mask)
 {
-	unsigned long flags;
 	unsigned long old;
 
-	lock_kernel();
-	save_and_cli(flags);
-	old = current->blocked;
-	current->blocked |= (blk_mask & _BLOCKABLE);
-	restore_flags(flags);
-	unlock_kernel();
+	spin_lock_irq(&current->sigmask_lock);
+	old = current->blocked.sig[0];
+	current->blocked.sig[0] |= (blk_mask & _BLOCKABLE);
+	spin_unlock_irq(&current->sigmask_lock);
 	return old;
 }
 
 asmlinkage unsigned long sunos_sigsetmask(unsigned long newmask)
 {
-	unsigned long flags;
 	unsigned long retval;
 
-	lock_kernel();
-	save_and_cli(flags);
-	retval = current->blocked;
-	current->blocked = newmask & _BLOCKABLE;
-	restore_flags(flags);
-	unlock_kernel();
+	spin_lock_irq(&current->sigmask_lock);
+	retval = current->blocked.sig[0];
+	current->blocked.sig[0] = (newmask & _BLOCKABLE);
+	spin_unlock_irq(&current->sigmask_lock);
 	return retval;
 }
 
@@ -430,8 +423,6 @@
 asmlinkage int sunos_getdents(unsigned int fd, void * dirent, int cnt)
 {
 	struct file * file;
-	struct dentry * dentry;
-	struct inode * inode;
 	struct sunos_dirent * lastdirent;
 	struct sunos_dirent_callback buf;
 	int error = -EBADF;
@@ -444,14 +435,6 @@
 	if(!file)
 		goto out;
 
-	dentry = file->f_dentry;
-	if(!dentry)
-		goto out;
-
-	inode = dentry->d_inode;
-	if(!inode)
-		goto out;
-
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
 		goto out;
@@ -464,7 +447,7 @@
 	buf.previous = NULL;
 	buf.count = cnt;
 	buf.error = 0;
-	error = file->f_op->readdir(inode, file, &buf, sunos_filldir);
+	error = file->f_op->readdir(file, &buf, sunos_filldir);
 	if (error < 0)
 		goto out;
 	lastdirent = buf.previous;
@@ -520,8 +503,6 @@
 asmlinkage int sunos_getdirentries(unsigned int fd, void * dirent, int cnt, unsigned int *basep)
 {
 	struct file * file;
-	struct dentry * dentry;
-	struct inode * inode;
 	struct sunos_direntry * lastdirent;
 	struct sunos_direntry_callback buf;
 	int error = -EBADF;
@@ -534,14 +515,6 @@
 	if(!file)
 		goto out;
 
-	dentry = file->f_dentry;
-	if(!dentry)
-		goto out;
-
-	inode = dentry->d_inode;
-	if(!inode)
-		goto out;
-
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
 		goto out;
@@ -554,7 +527,7 @@
 	buf.previous = NULL;
 	buf.count = cnt;
 	buf.error = 0;
-	error = file->f_op->readdir(inode, file, &buf, sunos_filldirentry);
+	error = file->f_op->readdir(file, &buf, sunos_filldirentry);
 	if (error < 0)
 		goto out;
 	lastdirent = buf.previous;
@@ -1254,58 +1227,47 @@
 
 #define SUNOS_SV_INTERRUPT 2
 
-extern void check_pending(int signum);
-
-asmlinkage int sunos_sigaction(int signum, const struct sigaction *action,
-	struct sigaction *oldaction)
+asmlinkage int
+sunos_sigaction(int sig, const struct old_sigaction *act,
+		struct old_sigaction *oact)
 {
-	struct sigaction new_sa, *p;
-	const int sigaction_size = sizeof (struct sigaction) - sizeof (void *);
+	struct k_sigaction new_ka, old_ka;
+	int ret;
 
 	current->personality |= PER_BSD;
-	if(signum < 1 || signum > 32)
-		return -EINVAL;
 
-	p = signum - 1 + current->sig->action;
+	if(act) {
+		old_sigset_t mask;
 
-	if(action) {
-		if(copy_from_user(&new_sa, action, sigaction_size))
-			return -EFAULT;
-		if (signum==SIGKILL || signum==SIGSTOP)
-			return -EINVAL;
-		memset(&new_sa, 0, sizeof(struct sigaction));
-		if(copy_from_user(&new_sa, action, sigaction_size))
+		if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
+		    __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+		    __get_user(new_ka.sa.sa_flags, &act->sa_flags))
 			return -EFAULT;
-		if (new_sa.sa_handler != SIG_DFL && new_sa.sa_handler != SIG_IGN) {
-			if(verify_area(VERIFY_READ, new_sa.sa_handler, 1))
-				return -EFAULT;
-		}
-		new_sa.sa_flags ^= SUNOS_SV_INTERRUPT;
+		__get_user(mask, &act->sa_mask);
+		new_ka.sa.sa_restorer = NULL;
+		new_ka.ka_restorer = NULL;
+		siginitset(&new_ka.sa.sa_mask, mask);
+		new_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT;
 	}
 
-	if (oldaction) {
+	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+	if (!ret && oact) {
 		/* In the clone() case we could copy half consistant
 		 * state to the user, however this could sleep and
 		 * deadlock us if we held the signal lock on SMP.  So for
 		 * now I take the easy way out and do no locking.
 		 * But then again we don't support SunOS lwp's anyways ;-)
 		 */
-		if (copy_to_user(oldaction, p, sigaction_size))
+		old_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT;
+		if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
+		    __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+		    __put_user(old_ka.sa.sa_flags, &oact->sa_flags))
 			 return -EFAULT;
-
-		if (oldaction->sa_flags & SA_RESTART)
-			oldaction->sa_flags &= ~SA_RESTART;
-		else
-			oldaction->sa_flags |= SUNOS_SV_INTERRUPT;
+		__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
 	}
 
-	if (action) {
-		spin_lock_irq(&current->sig->siglock);
-		*p = new_sa;
-		check_pending(signum);
-		spin_unlock_irq(&current->sig->siglock);
-	}
-	return 0;
+	return ret;
 }
 
 
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/systbls.S linux/arch/sparc/kernel/systbls.S
--- v2.1.78/linux/arch/sparc/kernel/systbls.S	Wed Sep 24 20:05:46 1997
+++ linux/arch/sparc/kernel/systbls.S	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.62 1997/04/23 23:01:08 ecd Exp $
+/* $Id: systbls.S,v 1.68 1997/12/24 17:26:38 ecd Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -30,11 +30,11 @@
 	.long C_LABEL(sys_setuid), C_LABEL(sys_getuid)
 /*25*/	.long C_LABEL(sys_time), C_LABEL(sys_ptrace), C_LABEL(sys_alarm)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_pause)
-/*30*/	.long C_LABEL(sys_utime), C_LABEL(sys_stty), C_LABEL(sys_gtty)
-	.long C_LABEL(sys_access), C_LABEL(sys_nice), C_LABEL(sys_ftime)
+/*30*/	.long C_LABEL(sys_utime), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_access), C_LABEL(sys_nice), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_sync), C_LABEL(sys_kill), C_LABEL(sys_newstat)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_newlstat), C_LABEL(sys_dup)
-	.long C_LABEL(sys_pipe), C_LABEL(sys_times), C_LABEL(sys_profil)
+	.long C_LABEL(sys_pipe), C_LABEL(sys_times), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_setgid), C_LABEL(sys_getgid)
 	.long C_LABEL(sys_signal), C_LABEL(sys_geteuid)
 /*50*/	.long C_LABEL(sys_getegid), C_LABEL(sys_acct), C_LABEL(sys_nis_syscall)
@@ -42,8 +42,8 @@
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_symlink), C_LABEL(sys_readlink)
 	.long C_LABEL(sys_execve), C_LABEL(sys_umask), C_LABEL(sys_chroot)
 	.long C_LABEL(sys_newfstat), C_LABEL(sys_nis_syscall), C_LABEL(sys_getpagesize)
-	.long C_LABEL(sys_msync), C_LABEL(sys_vfork), C_LABEL(sys_nis_syscall)
-	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_msync), C_LABEL(sys_vfork), C_LABEL(sys_pread)
+	.long C_LABEL(sys_pwrite), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_mmap), C_LABEL(sys_nis_syscall), C_LABEL(sys_munmap)
 	.long C_LABEL(sys_mprotect), C_LABEL(sys_nis_syscall), C_LABEL(sys_vhangup)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_getgroups)
@@ -54,10 +54,10 @@
 	.long C_LABEL(sys_fcntl), C_LABEL(sys_select), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-/*100*/	.long C_LABEL(sys_getpriority), C_LABEL(sys_nis_syscall)
-	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*100*/	.long C_LABEL(sys_getpriority), C_LABEL(sys_rt_sigreturn)
+	.long C_LABEL(sys_rt_sigaction), C_LABEL(sys_rt_sigprocmask)
+	.long C_LABEL(sys_rt_sigpending), C_LABEL(sys_rt_sigtimedwait)
+	.long C_LABEL(sys_rt_sigqueueinfo), C_LABEL(sys_rt_sigsuspend)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
@@ -73,13 +73,13 @@
 	.long C_LABEL(sys_mkdir), C_LABEL(sys_rmdir), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_getrlimit)
-	.long C_LABEL(sys_setrlimit), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_setrlimit), C_LABEL(sys_nis_syscall), C_LABEL(sys_prctl)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 /*150*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_poll), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_statfs), C_LABEL(sys_fstatfs)
 	.long C_LABEL(sys_umount), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
-	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_setdomainname)
+	.long C_LABEL(sys_getdomainname), C_LABEL(sys_setdomainname)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_quotactl), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_mount), C_LABEL(sys_ustat), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
@@ -89,12 +89,12 @@
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_sigpending), C_LABEL(sys_query_module)
 	.long C_LABEL(sys_setpgid), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_newuname), C_LABEL(sys_init_module)
-	.long C_LABEL(sys_personality), C_LABEL(sys_prof), C_LABEL(sys_break)
-	.long C_LABEL(sys_lock), C_LABEL(sys_mpx), C_LABEL(sys_ulimit)
+	.long C_LABEL(sys_personality), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_getppid), C_LABEL(sparc_sigaction), C_LABEL(sys_sgetmask)
 /*200*/	.long C_LABEL(sys_ssetmask), C_LABEL(sys_sigsuspend), C_LABEL(sys_newlstat)
 	.long C_LABEL(sys_uselib), C_LABEL(old_readdir), C_LABEL(sys_nis_syscall)
-	.long C_LABEL(sys_socketcall), C_LABEL(sys_syslog), C_LABEL(sys_olduname)
+	.long C_LABEL(sys_socketcall), C_LABEL(sys_syslog), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_idle), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_waitpid), C_LABEL(sys_swapoff), C_LABEL(sys_sysinfo)
 	.long C_LABEL(sys_ipc), C_LABEL(sys_sigreturn), C_LABEL(sys_clone)
@@ -102,7 +102,7 @@
 	.long C_LABEL(sys_create_module), C_LABEL(sys_delete_module)
 	.long C_LABEL(sys_get_kernel_syms), C_LABEL(sys_getpgid), C_LABEL(sys_bdflush)
 	.long C_LABEL(sys_sysfs), C_LABEL(sys_nis_syscall), C_LABEL(sys_setfsuid)
-	.long C_LABEL(sys_setfsgid), C_LABEL(sys_llseek), C_LABEL(sys_time)
+	.long C_LABEL(sys_setfsgid), C_LABEL(sys_select), C_LABEL(sys_time)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_stime), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_llseek)
 	/* "We are the Knights of the Forest of Ni!!" */
@@ -125,7 +125,6 @@
 	.long C_LABEL(sys_fdatasync)
 	.long C_LABEL(sys_nfsservctl)
 /*255*/	.long C_LABEL(sys_aplib)
-	.long C_LABEL(sys_prctl)
 	.long C_LABEL(sys_nis_syscall)
 
 	/* Now the SunOS syscall table. */
@@ -147,7 +146,7 @@
 	.long C_LABEL(sys_access), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
 	.long C_LABEL(sys_sync), C_LABEL(sys_kill), C_LABEL(sys_newstat)
 	.long C_LABEL(sunos_nosys), C_LABEL(sys_newlstat), C_LABEL(sys_dup)
-	.long C_LABEL(sys_pipe), C_LABEL(sunos_nosys), C_LABEL(sys_profil)
+	.long C_LABEL(sys_pipe), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
 	.long C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_getgid)
 	.long C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
 /*50*/	.long C_LABEL(sunos_nosys), C_LABEL(sys_acct), C_LABEL(sunos_nosys)
@@ -220,4 +219,3 @@
 	.long C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
 /*250*/	.long C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
 	.long C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sys_aplib)
-	.long C_LABEL(sunos_nosys)
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/lib/memcpy.S linux/arch/sparc/lib/memcpy.S
--- v2.1.78/linux/arch/sparc/lib/memcpy.S	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc/lib/memcpy.S	Mon Jan 12 15:15:43 1998
@@ -523,6 +523,15 @@
 
 #endif /* FASTER_REVERSE */
 
+/* NOTE: This code is executed just for the cases,
+         where %src (=%o1) & 3 is != 0.
+	 We need to align it to 4. So, for (%src & 3)
+	 1 we need to do ldub,lduh
+	 2 lduh
+	 3 just ldub
+         so even if it looks weird, the branches
+         are correct here. -jj
+ */
 78:	/* dword_align */
 
 	andcc		%o1, 1, %g0
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/mm/Makefile linux/arch/sparc/mm/Makefile
--- v2.1.78/linux/arch/sparc/mm/Makefile	Mon Jul  7 08:18:54 1997
+++ linux/arch/sparc/mm/Makefile	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.26 1997/06/24 15:48:06 jj Exp $
+# $Id: Makefile,v 1.27 1997/11/07 15:01:27 jj Exp $
 # Makefile for the linux Sparc-specific parts of the memory manager.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -10,7 +10,7 @@
 O_TARGET := mm.o
 O_OBJS   := fault.o init.o sun4c.o srmmu.o hypersparc.o viking.o \
 	    tsunami.o loadmmu.o generic.o asyncd.o extable.o \
-	    turbosparc.o
+	    turbosparc.o iommu.o io-unit.o
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/mm/asyncd.c linux/arch/sparc/mm/asyncd.c
--- v2.1.78/linux/arch/sparc/mm/asyncd.c	Thu May 15 16:48:02 1997
+++ linux/arch/sparc/mm/asyncd.c	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/*  $Id: asyncd.c,v 1.10 1997/05/15 21:14:24 davem Exp $
+/*  $Id: asyncd.c,v 1.11 1997/12/14 23:24:34 ecd Exp $
  *  The asyncd kernel daemon. This handles paging on behalf of 
  *  processes that receive page faults due to remote (async) memory
  *  accesses. 
@@ -239,7 +239,8 @@
 	current->session = 1;
 	current->pgrp = 1;
 	sprintf(current->comm, "asyncd");
-	current->blocked = ~0UL; /* block all signals */
+	sigfillset(&current->blocked); /* block all signals */
+	recalc_sigpending(current);
   
 	/* Give asyncd a realtime priority. */
 	current->policy = SCHED_FIFO;
@@ -259,7 +260,9 @@
 		save_flags(flags); cli();
 
 		while (!async_queue) {
-			current->signal = 0;
+			spin_lock_irq(&current->sigmask_lock);
+			flush_signals(current);
+			spin_unlock_irq(&current->sigmask_lock);
 			interruptible_sleep_on(&asyncd_wait);
 		}
 
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/mm/hypersparc.S linux/arch/sparc/mm/hypersparc.S
--- v2.1.78/linux/arch/sparc/mm/hypersparc.S	Thu May 29 21:53:04 1997
+++ linux/arch/sparc/mm/hypersparc.S	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/* $Id: hypersparc.S,v 1.10 1997/05/27 19:29:58 jj Exp $
+/* $Id: hypersparc.S,v 1.12 1997/11/27 15:42:30 jj Exp $
  * hypersparc.S: High speed Hypersparc mmu/cache operations.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -135,8 +135,7 @@
 	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
+	add	%o2, -PAGE_SIZE, %o0
 1:
 	or	%o0, 0x400, %g7
 	lda	[%g7] ASI_M_FLUSH_PROBE, %g7
@@ -157,10 +156,9 @@
 	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
+	 add	%o2, -PAGE_SIZE, %o0
 	mov	SRMMU_FAULT_STATUS, %g5
 	lda	[%g5] ASI_M_MMUREGS, %g0
 	mov	SRMMU_CTX_REG, %g7
@@ -191,10 +189,9 @@
 	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
+	sub	%o1, -PAGE_SIZE, %o1
 	add	%o4, %o5, %g1
 	add	%o4, %g1, %g2
 	add	%o4, %g2, %g3
@@ -242,9 +239,8 @@
 	orcc	%g5, 0, %g0
 	be	2f
 	 add	%o4, %g1, %g2
-	sethi	%hi(PAGE_SIZE), %g5
 	add	%o4, %g2, %g3
-	add	%o0, %g5, %o0
+	sub	%o0, -PAGE_SIZE, %o0
 	add	%o4, %g3, %g4
 	add	%o4, %g4, %g5
 	add	%o4, %g5, %g7
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/mm/init.c linux/arch/sparc/mm/init.c
--- v2.1.78/linux/arch/sparc/mm/init.c	Wed Apr 23 19:01:16 1997
+++ linux/arch/sparc/mm/init.c	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/*  $Id: init.c,v 1.49 1997/04/17 21:49:31 jj Exp $
+/*  $Id: init.c,v 1.50 1998/01/10 18:19:42 ecd Exp $
  *  linux/arch/sparc/mm/init.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -227,9 +227,10 @@
 	addr = KERNBASE;
 	while(addr < start_mem) {
 #ifdef CONFIG_BLK_DEV_INITRD
-		if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end)
+		if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end) {
 			mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
-		else
+			num_physpages--;
+		} else
 #endif	
 			mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
 		addr += PAGE_SIZE;
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/mm/io-unit.c linux/arch/sparc/mm/io-unit.c
--- v2.1.78/linux/arch/sparc/mm/io-unit.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/mm/io-unit.c	Mon Jan 12 15:15:43 1998
@@ -0,0 +1,158 @@
+/* $Id: io-unit.c,v 1.5 1997/12/22 16:09:26 jj Exp $
+ * io-unit.c:  IO-UNIT specific routines for memory management.
+ *
+ * Copyright (C) 1997 Jakub Jelinek    (jj@sunsite.mff.cuni.cz)
+ */
+ 
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <asm/pgtable.h>
+#include <asm/sbus.h>
+#include <asm/io.h>
+#include <asm/io-unit.h>
+#include <asm/mxcc.h>
+
+#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
+
+#define IOPERM        (IOUPTE_CACHE | IOUPTE_WRITE | IOUPTE_VALID)
+#define MKIOPTE(phys) ((((phys)>>4) & IOUPTE_PAGE) | IOPERM)
+
+unsigned long sun4d_dma_base;
+unsigned long sun4d_dma_vbase;
+unsigned long sun4d_dma_size;
+__initfunc(unsigned long
+iounit_init(int sbi_node, int io_node, unsigned long memory_start,
+	    unsigned long memory_end, struct linux_sbus *sbus))
+{
+	iopte_t *xpt, *xptend;
+	unsigned long paddr;
+	struct iounit_struct *iounit;
+	struct linux_prom_registers iommu_promregs[PROMREG_MAX];
+	
+	memory_start = LONG_ALIGN(memory_start);
+	iounit = (struct iounit_struct *)memory_start;
+	memory_start += sizeof(struct iounit_struct);
+
+	prom_getproperty(sbi_node, "reg", (void *) iommu_promregs,
+			 sizeof(iommu_promregs));
+	prom_apply_generic_ranges(io_node, 0, iommu_promregs, 3);
+	xpt = (iopte_t *)
+		sparc_alloc_io(iommu_promregs[2].phys_addr, 0, (PAGE_SIZE * 16),
+			       "XPT", iommu_promregs[2].which_io, 0x0);
+	if(!xpt) panic("Cannot map External Page Table.");
+	
+	sbus->iommu = (struct iommu_struct *)iounit;
+	iounit->page_table = xpt;
+	
+	/* Initialize new table. */
+	paddr = IOUNIT_DMA_BASE - sun4d_dma_base;
+	for (xptend = xpt + (sun4d_dma_size >> PAGE_SHIFT);
+	     xpt < xptend; paddr++)
+		*xpt++ = MKIOPTE(paddr);
+	for (xptend = iounit->page_table + (16 * PAGE_SIZE) / sizeof(iopte_t);
+	     xpt < xptend;)
+	     	*xpt++ = 0;
+	     	
+	return memory_start;
+}
+
+static __u32 iounit_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus)
+{
+	/* Viking MXCC is IO coherent, just need to translate the address to DMA handle */
+#ifdef IOUNIT_DEBUG
+	if ((((unsigned long) vaddr) & PAGE_MASK) < sun4d_dma_vaddr || 
+	    (((unsigned long) vaddr) & PAGE_MASK) + len > sun4d_dma_vbase + sun4d_dma_size)
+			panic("Using non-DMA memory for iounit_get_scsi_one");
+#endif	
+	return (__u32)(sun4d_dma_base + mmu_v2p((long)vaddr));
+}
+
+static void iounit_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
+{
+	/* Viking MXCC is IO coherent, just need to translate the address to DMA handle */
+	for (; sz >= 0; sz--) {
+#ifdef IOUNIT_DEBUG
+		unsigned long page = ((unsigned long) sg[sz].addr) & PAGE_MASK;
+		if (page < sun4d_dma_vbase || page + sg[sz].len > sun4d_dma_vbase + sun4d_dma_size)
+			panic("Using non-DMA memory for iounit_get_scsi_sgl");
+#endif	
+		sg[sz].dvma_addr = (__u32) (sun4d_dma_base + mmu_v2p((long)sg[sz].addr));;
+	}
+}
+
+static void iounit_release_scsi_one(__u32 vaddr, unsigned long len, struct linux_sbus *sbus)
+{
+}
+
+static void iounit_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
+{
+}
+
+#ifdef CONFIG_SBUS
+static void iounit_map_dma_area(unsigned long addr, int len)
+{
+	unsigned long page, end;
+	pgprot_t dvma_prot;
+	iopte_t *iopte;
+	struct linux_sbus *sbus;
+
+	dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV);
+	end = PAGE_ALIGN((addr + len));
+	while(addr < end) {
+		page = get_free_page(GFP_KERNEL);
+		if(!page) {
+			prom_printf("alloc_dvma: Cannot get a dvma page\n");
+			prom_halt();
+		} else {
+			pgd_t *pgdp;
+			pmd_t *pmdp;
+			pte_t *ptep;
+			long i;
+
+			pgdp = pgd_offset(init_task.mm, addr);
+			pmdp = pmd_offset(pgdp, addr);
+			ptep = pte_offset(pmdp, addr);
+
+			set_pte(ptep, pte_val(mk_pte(page, dvma_prot)));
+			
+			i = ((addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT);
+
+			for_each_sbus(sbus) {
+				struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+
+				iopte = (iopte_t *)(iounit->page_table + i);
+				*iopte = __iopte(MKIOPTE(mmu_v2p(page)));
+			}
+		}
+		addr += PAGE_SIZE;
+	}
+	flush_cache_all();
+	flush_tlb_all();
+}
+#endif
+
+static char *iounit_lockarea(char *vaddr, unsigned long len)
+{
+	return vaddr;
+}
+
+static void iounit_unlockarea(char *vaddr, unsigned long len)
+{
+}
+
+__initfunc(void ld_mmu_iounit(void))
+{
+	mmu_lockarea = iounit_lockarea;
+	mmu_unlockarea = iounit_unlockarea;
+
+	mmu_get_scsi_one = iounit_get_scsi_one;
+	mmu_get_scsi_sgl = iounit_get_scsi_sgl;
+	mmu_release_scsi_one = iounit_release_scsi_one;
+	mmu_release_scsi_sgl = iounit_release_scsi_sgl;
+
+#ifdef CONFIG_SBUS
+	mmu_map_dma_area = iounit_map_dma_area;
+#endif
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/mm/iommu.c linux/arch/sparc/mm/iommu.c
--- v2.1.78/linux/arch/sparc/mm/iommu.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/mm/iommu.c	Mon Jan 12 15:15:43 1998
@@ -0,0 +1,284 @@
+/* $Id: iommu.c,v 1.4 1997/11/21 17:31:31 jj Exp $
+ * iommu.c:  IOMMU specific routines for memory management.
+ *
+ * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
+ * Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru)
+ * Copyright (C) 1996 Eddie C. Dost    (ecd@skynet.be)
+ * Copyright (C) 1997 Jakub Jelinek    (jj@sunsite.mff.cuni.cz)
+ */
+ 
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <asm/pgtable.h>
+#include <asm/sbus.h>
+#include <asm/io.h>
+#include <asm/mxcc.h>
+
+/* srmmu.c */
+extern int viking_mxcc_present;
+extern void (*flush_page_for_dma)(unsigned long page);
+extern int flush_page_for_dma_global;
+/* viking.S */
+extern void viking_flush_page(unsigned long page);
+extern void viking_mxcc_flush_page(unsigned long page);
+
+#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
+
+#define IOPERM        (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID)
+#define MKIOPTE(phys) (((((phys)>>4) & IOPTE_PAGE) | IOPERM) & ~IOPTE_WAZ)
+
+static inline void iommu_map_dvma_pages_for_iommu(struct iommu_struct *iommu,
+						  unsigned long kern_end)
+{
+	unsigned long first = page_offset;
+	unsigned long last = kern_end;
+	iopte_t *iopte = iommu->page_table;
+
+	iopte += ((first - iommu->start) >> PAGE_SHIFT);
+	while(first <= last) {
+		*iopte++ = __iopte(MKIOPTE(mmu_v2p(first)));
+		first += PAGE_SIZE;
+	}
+}
+
+__initfunc(unsigned long
+iommu_init(int iommund, unsigned long memory_start,
+	   unsigned long memory_end, struct linux_sbus *sbus))
+{
+	unsigned int impl, vers, ptsize;
+	unsigned long tmp;
+	struct iommu_struct *iommu;
+	struct linux_prom_registers iommu_promregs[PROMREG_MAX];
+
+	memory_start = LONG_ALIGN(memory_start);
+	iommu = (struct iommu_struct *) memory_start;
+	memory_start += sizeof(struct iommu_struct);
+	prom_getproperty(iommund, "reg", (void *) iommu_promregs,
+			 sizeof(iommu_promregs));
+	iommu->regs = (struct iommu_regs *)
+		sparc_alloc_io(iommu_promregs[0].phys_addr, 0, (PAGE_SIZE * 3),
+			       "IOMMU registers", iommu_promregs[0].which_io, 0x0);
+	if(!iommu->regs)
+		panic("Cannot map IOMMU registers.");
+	impl = (iommu->regs->control & IOMMU_CTRL_IMPL) >> 28;
+	vers = (iommu->regs->control & IOMMU_CTRL_VERS) >> 24;
+	tmp = iommu->regs->control;
+	tmp &= ~(IOMMU_CTRL_RNGE);
+	switch(page_offset & 0xf0000000) {
+	case 0xf0000000:
+		tmp |= (IOMMU_RNGE_256MB | IOMMU_CTRL_ENAB);
+		iommu->plow = iommu->start = 0xf0000000;
+		break;
+	case 0xe0000000:
+		tmp |= (IOMMU_RNGE_512MB | IOMMU_CTRL_ENAB);
+		iommu->plow = iommu->start = 0xe0000000;
+		break;
+	case 0xd0000000:
+	case 0xc0000000:
+		tmp |= (IOMMU_RNGE_1GB | IOMMU_CTRL_ENAB);
+		iommu->plow = iommu->start = 0xc0000000;
+		break;
+	case 0xb0000000:
+	case 0xa0000000:
+	case 0x90000000:
+	case 0x80000000:
+		tmp |= (IOMMU_RNGE_2GB | IOMMU_CTRL_ENAB);
+		iommu->plow = iommu->start = 0x80000000;
+		break;
+	}
+	iommu->regs->control = tmp;
+	iommu_invalidate(iommu->regs);
+	iommu->end = 0xffffffff;
+
+	/* Allocate IOMMU page table */
+	ptsize = iommu->end - iommu->start + 1;
+	ptsize = (ptsize >> PAGE_SHIFT) * sizeof(iopte_t);
+
+	/* Stupid alignment constraints give me a headache. */
+	memory_start = PAGE_ALIGN(memory_start);
+	memory_start = (((memory_start) + (ptsize - 1)) & ~(ptsize - 1));
+	iommu->lowest = iommu->page_table = (iopte_t *) memory_start;
+	memory_start += ptsize;
+
+	/* Initialize new table. */
+	flush_cache_all();
+	memset(iommu->page_table, 0, ptsize);
+	iommu_map_dvma_pages_for_iommu(iommu, memory_end);
+	if(viking_mxcc_present) {
+		unsigned long start = (unsigned long) iommu->page_table;
+		unsigned long end = (start + ptsize);
+		while(start < end) {
+			viking_mxcc_flush_page(start);
+			start += PAGE_SIZE;
+		}
+	} else if(flush_page_for_dma == viking_flush_page) {
+		unsigned long start = (unsigned long) iommu->page_table;
+		unsigned long end = (start + ptsize);
+		while(start < end) {
+			viking_flush_page(start);
+			start += PAGE_SIZE;
+		}
+	}
+	flush_tlb_all();
+	iommu->regs->base = mmu_v2p((unsigned long) iommu->page_table) >> 4;
+	iommu_invalidate(iommu->regs);
+
+	sbus->iommu = iommu;
+	printk("IOMMU: impl %d vers %d page table at %p of size %d bytes\n",
+	       impl, vers, iommu->page_table, ptsize);
+	return memory_start;
+}
+
+static __u32 iommu_get_scsi_one_noflush(char *vaddr, unsigned long len, struct linux_sbus *sbus)
+{
+	return (__u32)vaddr;
+}
+
+static __u32 iommu_get_scsi_one_gflush(char *vaddr, unsigned long len, struct linux_sbus *sbus)
+{
+	flush_page_for_dma(0);
+	return (__u32)vaddr;
+}
+
+static __u32 iommu_get_scsi_one_pflush(char *vaddr, unsigned long len, struct linux_sbus *sbus)
+{
+	unsigned long page = ((unsigned long) vaddr) & PAGE_MASK;
+
+	while(page < ((unsigned long)(vaddr + len))) {
+		flush_page_for_dma(page);
+		page += PAGE_SIZE;
+	}
+	return (__u32)vaddr;
+}
+
+static void iommu_get_scsi_sgl_noflush(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
+{
+	for (; sz >= 0; sz--)
+		sg[sz].dvma_addr = (__u32) (sg[sz].addr);
+}
+
+static void iommu_get_scsi_sgl_gflush(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
+{
+	flush_page_for_dma(0);
+	for (; sz >= 0; sz--)
+		sg[sz].dvma_addr = (__u32) (sg[sz].addr);
+}
+
+static void iommu_get_scsi_sgl_pflush(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
+{
+	unsigned long page, oldpage = 0;
+
+	while(sz >= 0) {
+		page = ((unsigned long) sg[sz].addr) & PAGE_MASK;
+		if (oldpage == page)
+			page += PAGE_SIZE; /* We flushed that page already */
+		while(page < (unsigned long)(sg[sz].addr + sg[sz].len)) {
+			flush_page_for_dma(page);
+			page += PAGE_SIZE;
+		}
+		sg[sz].dvma_addr = (__u32) (sg[sz].addr);
+		sz--;
+		oldpage = page - PAGE_SIZE;
+	}
+}
+
+static void iommu_release_scsi_one(__u32 vaddr, unsigned long len, struct linux_sbus *sbus)
+{
+}
+
+static void iommu_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
+{
+}
+
+#ifdef CONFIG_SBUS
+static void iommu_map_dma_area(unsigned long addr, int len)
+{
+	unsigned long page, end;
+	pgprot_t dvma_prot;
+	struct iommu_struct *iommu = SBus_chain->iommu;
+	iopte_t *iopte = iommu->page_table;
+	iopte_t *iopte_first = iopte;
+
+	if(viking_mxcc_present)
+		dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV);
+	else
+		dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV);
+
+	iopte += ((addr - iommu->start) >> PAGE_SHIFT);
+	end = PAGE_ALIGN((addr + len));
+	while(addr < end) {
+		page = get_free_page(GFP_KERNEL);
+		if(!page) {
+			prom_printf("alloc_dvma: Cannot get a dvma page\n");
+			prom_halt();
+		} else {
+			pgd_t *pgdp;
+			pmd_t *pmdp;
+			pte_t *ptep;
+
+			pgdp = pgd_offset(init_task.mm, addr);
+			pmdp = pmd_offset(pgdp, addr);
+			ptep = pte_offset(pmdp, addr);
+
+			set_pte(ptep, pte_val(mk_pte(page, dvma_prot)));
+
+			iopte_val(*iopte++) = MKIOPTE(mmu_v2p(page));
+		}
+		addr += PAGE_SIZE;
+	}
+	flush_cache_all();
+	if(viking_mxcc_present) {
+		unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK;
+		unsigned long end = PAGE_ALIGN(((unsigned long) iopte));
+		while(start < end) {
+			viking_mxcc_flush_page(start);
+			start += PAGE_SIZE;
+		}
+	} else if(flush_page_for_dma == viking_flush_page) {
+		unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK;
+		unsigned long end = PAGE_ALIGN(((unsigned long) iopte));
+		while(start < end) {
+			viking_flush_page(start);
+			start += PAGE_SIZE;
+		}
+	}
+	flush_tlb_all();
+	iommu_invalidate(iommu->regs);
+}
+#endif
+
+static char *iommu_lockarea(char *vaddr, unsigned long len)
+{
+	return vaddr;
+}
+
+static void iommu_unlockarea(char *vaddr, unsigned long len)
+{
+}
+
+__initfunc(void ld_mmu_iommu(void))
+{
+	mmu_lockarea = iommu_lockarea;
+	mmu_unlockarea = iommu_unlockarea;
+
+	if (!flush_page_for_dma) {
+		/* IO coherent chip */
+		mmu_get_scsi_one = iommu_get_scsi_one_noflush;
+		mmu_get_scsi_sgl = iommu_get_scsi_sgl_noflush;
+	} else if (flush_page_for_dma_global) {
+		/* flush_page_for_dma flushes everything, no matter of what page is it */
+		mmu_get_scsi_one = iommu_get_scsi_one_gflush;
+		mmu_get_scsi_sgl = iommu_get_scsi_sgl_gflush;
+	} else {
+		mmu_get_scsi_one = iommu_get_scsi_one_pflush;
+		mmu_get_scsi_sgl = iommu_get_scsi_sgl_pflush;
+	}
+	mmu_release_scsi_one = iommu_release_scsi_one;
+	mmu_release_scsi_sgl = iommu_release_scsi_sgl;
+
+#ifdef CONFIG_SBUS
+	mmu_map_dma_area = iommu_map_dma_area;
+#endif
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
--- v2.1.78/linux/arch/sparc/mm/srmmu.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc/mm/srmmu.c	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/* $Id: srmmu.c,v 1.151 1997/08/28 11:10:54 jj Exp $
+/* $Id: srmmu.c,v 1.156 1997/11/28 14:23:42 jj Exp $
  * srmmu.c:  SRMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
@@ -25,11 +25,11 @@
 #include <asm/cache.h>
 #include <asm/oplib.h>
 #include <asm/sbus.h>
-#include <asm/iommu.h>
 #include <asm/asi.h>
 #include <asm/msi.h>
 #include <asm/a.out.h>
 #include <asm/mmu_context.h>
+#include <asm/io-unit.h>
 
 /* Now the cpu specific definitions. */
 #include <asm/viking.h>
@@ -47,6 +47,10 @@
 
 extern unsigned long sparc_iobase_vaddr;
 
+extern unsigned long sun4d_dma_base;
+extern unsigned long sun4d_dma_size;
+extern unsigned long sun4d_dma_vbase;
+
 #ifdef __SMP__
 #define FLUSH_BEGIN(mm)
 #define FLUSH_END
@@ -55,10 +59,14 @@
 #define FLUSH_END	}
 #endif
 
+static int phys_mem_contig;
+long page_contig_offset;
+
 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);
+void (*flush_page_for_dma)(unsigned long page);
+int flush_page_for_dma_global = 1;
 static void (*flush_chunk)(unsigned long chunk);
 #ifdef __SMP__
 static void (*local_flush_page_for_dma)(unsigned long page);
@@ -93,37 +101,13 @@
 
 #define srmmu_ahashfn(addr)	((addr) >> 24)
 
-static int viking_mxcc_present = 0;
-
-void srmmu_frob_mem_map(unsigned long start_mem)
-{
-	unsigned long bank_start, bank_end;
-	unsigned long addr;
-	int i;
-
-	/* First, mark all pages as invalid. */
-	for(addr = PAGE_OFFSET; MAP_NR(addr) < max_mapnr; addr += PAGE_SIZE)
-		mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
-
-	start_mem = PAGE_ALIGN(start_mem);
-	for(i = 0; srmmu_map[i].size; i++) {
-		bank_start = srmmu_map[i].vbase;
-		bank_end = bank_start + srmmu_map[i].size;
-		while(bank_start < bank_end) {
-			if((bank_start >= KERNBASE) &&
-			   (bank_start < start_mem)) {
-				bank_start += PAGE_SIZE;
-				continue;
-			}
-			mem_map[MAP_NR(bank_start)].flags &= ~(1<<PG_reserved);
-			bank_start += PAGE_SIZE;
-		}
-	}
-}
+int viking_mxcc_present = 0;
 
 /* Physical memory can be _very_ non-contiguous on the sun4m, especially
  * the SS10/20 class machines and with the latest openprom revisions.
  * So we have to do a quick lookup.
+ * We use the same for SS1000/SC2000 as a fall back, when phys memory is
+ * non-contiguous.
  */
 static inline unsigned long srmmu_v2p(unsigned long vaddr)
 {
@@ -145,6 +129,21 @@
 		return 0xffffffffUL;
 }
 
+/* Physical memory on most SS1000/SC2000 can be contiguous, so we handle that case
+ * as a special case to make things faster.
+ */
+static inline unsigned long srmmu_c_v2p(unsigned long vaddr)
+{
+	if (vaddr >= KERNBASE) return vaddr - KERNBASE;
+	return (vaddr - page_contig_offset);
+}
+
+static inline unsigned long srmmu_c_p2v(unsigned long paddr)
+{
+	if (paddr < (0xfd000000 - KERNBASE)) return paddr + KERNBASE;
+	return (paddr + page_contig_offset);
+}
+
 /* In general all page table modifications should use the V8 atomic
  * swap instruction.  This insures the mmu and the cpu are in sync
  * with respect to ref/mod bits in the page tables.
@@ -158,6 +157,37 @@
 /* Functions really use this, not srmmu_swap directly. */
 #define srmmu_set_entry(ptr, newentry) srmmu_swap((unsigned long *) (ptr), (newentry))
 
+__initfunc(void srmmu_frob_mem_map(unsigned long start_mem))
+{
+	unsigned long bank_start, bank_end;
+	unsigned long addr;
+	int i;
+
+	/* First, mark all pages as invalid. */
+	for(addr = PAGE_OFFSET; MAP_NR(addr) < max_mapnr; addr += PAGE_SIZE)
+		mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
+
+	start_mem = PAGE_ALIGN(start_mem);
+	for(i = 0; srmmu_map[i].size; i++) {
+		bank_start = srmmu_map[i].vbase;
+		bank_end = bank_start + srmmu_map[i].size;
+		while(bank_start < bank_end) {
+			if((bank_start >= KERNBASE) &&
+			   (bank_start < start_mem)) {
+				bank_start += PAGE_SIZE;
+				continue;
+			}
+			mem_map[MAP_NR(bank_start)].flags &= ~(1<<PG_reserved);
+			bank_start += PAGE_SIZE;
+		}
+	}
+	if (sparc_cpu_model == sun4d) {
+		for (addr = PAGE_OFFSET; MAP_NR(addr) < max_mapnr; addr += PAGE_SIZE)
+			if (addr < sun4d_dma_vbase || addr >= sun4d_dma_vbase + sun4d_dma_size)
+				clear_bit(PG_DMA, &mem_map[MAP_NR(addr)].flags);
+	}
+}
+
 /* The very generic SRMMU page table operations. */
 static unsigned int srmmu_pmd_align(unsigned int addr) { return SRMMU_PMD_ALIGN(addr); }
 static unsigned int srmmu_pgdir_align(unsigned int addr) { return SRMMU_PGDIR_ALIGN(addr); }
@@ -181,6 +211,15 @@
 static unsigned long srmmu_pte_page(pte_t pte)
 { return srmmu_device_memory(pte_val(pte))?~0:srmmu_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); }
 
+static unsigned long srmmu_c_pgd_page(pgd_t pgd)
+{ return srmmu_device_memory(pgd_val(pgd))?~0:srmmu_c_p2v((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4); }
+
+static unsigned long srmmu_c_pmd_page(pmd_t pmd)
+{ return srmmu_device_memory(pmd_val(pmd))?~0:srmmu_c_p2v((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4); }
+
+static unsigned long srmmu_c_pte_page(pte_t pte)
+{ return srmmu_device_memory(pte_val(pte))?~0:srmmu_c_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); }
+
 static int srmmu_pte_none(pte_t pte)          
 { return !(pte_val(pte) & 0xFFFFFFF); }
 static int srmmu_pte_present(pte_t pte)
@@ -227,6 +266,9 @@
 static pte_t srmmu_mk_pte(unsigned long page, pgprot_t pgprot)
 { return __pte(((srmmu_v2p(page)) >> 4) | pgprot_val(pgprot)); }
 
+static pte_t srmmu_c_mk_pte(unsigned long page, pgprot_t pgprot)
+{ return __pte(((srmmu_c_v2p(page)) >> 4) | pgprot_val(pgprot)); }
+
 static pte_t srmmu_mk_pte_phys(unsigned long page, pgprot_t pgprot)
 { return __pte(((page) >> 4) | pgprot_val(pgprot)); }
 
@@ -250,6 +292,21 @@
 	set_pte((pte_t *)pmdp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) ptep) >> 4)));
 }
 
+static void srmmu_c_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp)
+{ 
+	set_pte((pte_t *)ctxp, (SRMMU_ET_PTD | (srmmu_c_v2p((unsigned long) pgdp) >> 4)));
+}
+
+static void srmmu_c_pgd_set(pgd_t * pgdp, pmd_t * pmdp)
+{
+	set_pte((pte_t *)pgdp, (SRMMU_ET_PTD | (srmmu_c_v2p((unsigned long) pmdp) >> 4)));
+}
+
+static void srmmu_c_pmd_set(pmd_t * pmdp, pte_t * ptep)
+{
+	set_pte((pte_t *)pmdp, (SRMMU_ET_PTD | (srmmu_c_v2p((unsigned long) ptep) >> 4)));
+}
+
 static pte_t srmmu_pte_modify(pte_t pte, pgprot_t newprot)
 {
 	return __pte((pte_val(pte) & SRMMU_CHG_MASK) | pgprot_val(newprot));
@@ -273,6 +330,18 @@
 	return (pte_t *) srmmu_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
 }
 
+/* Find an entry in the second-level page table.. */
+static pmd_t *srmmu_c_pmd_offset(pgd_t * dir, unsigned long address)
+{
+	return (pmd_t *) srmmu_c_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
+}
+
+/* Find an entry in the third-level page table.. */ 
+static pte_t *srmmu_c_pte_offset(pmd_t * dir, unsigned long address)
+{
+	return (pte_t *) srmmu_c_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
+}
+
 /* This must update the context table entry for this process. */
 static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) 
 {
@@ -560,7 +629,7 @@
 		pmd_set(pmd, BAD_PAGETABLE);
 		return NULL;
 	}
-	return (pte_t *) srmmu_pmd_page(*pmd) + address;
+	return (pte_t *) pmd_page(*pmd) + address;
 }
 
 static void srmmu_pmd_free_kernel(pmd_t *pmd)
@@ -617,7 +686,7 @@
 		pmd_set(pmd, BAD_PAGETABLE);
 		return NULL;
 	}
-	return ((pte_t *) srmmu_pmd_page(*pmd)) + address;
+	return ((pte_t *) pmd_page(*pmd)) + address;
 }
 
 /* Real three-level page tables on SRMMU. */
@@ -646,7 +715,7 @@
 		pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
 		return NULL;
 	}
-	return (pmd_t *) srmmu_pgd_page(*pgd) + address;
+	return (pmd_t *) pgd_page(*pgd) + address;
 }
 
 static void srmmu_pgd_free(pgd_t *pgd)
@@ -789,8 +858,8 @@
 
 	physaddr &= PAGE_MASK;
 	pgdp = srmmu_pgd_offset(init_task.mm, virt_addr);
-	pmdp = srmmu_pmd_offset(pgdp, virt_addr);
-	ptep = srmmu_pte_offset(pmdp, virt_addr);
+	pmdp = pmd_offset(pgdp, virt_addr);
+	ptep = pte_offset(pmdp, virt_addr);
 	tmp = (physaddr >> 4) | SRMMU_ET_PTE;
 
 	/* I need to test whether this is consistent over all
@@ -814,23 +883,14 @@
 	pte_t *ptep;
 
 	pgdp = srmmu_pgd_offset(init_task.mm, virt_addr);
-	pmdp = srmmu_pmd_offset(pgdp, virt_addr);
-	ptep = srmmu_pte_offset(pmdp, virt_addr);
+	pmdp = pmd_offset(pgdp, virt_addr);
+	ptep = pte_offset(pmdp, virt_addr);
 
 	/* No need to flush uncacheable page. */
-	set_pte(ptep, srmmu_mk_pte((unsigned long) EMPTY_PGE, PAGE_SHARED));
+	set_pte(ptep, mk_pte((unsigned long) EMPTY_PGE, PAGE_SHARED));
 	flush_tlb_all();
 }
 
-static char *srmmu_lockarea(char *vaddr, unsigned long len)
-{
-	return vaddr;
-}
-
-static void srmmu_unlockarea(char *vaddr, unsigned long len)
-{
-}
-
 /* This is used in many routines below. */
 #define UWINMASK_OFFSET (const unsigned long)(&(((struct task_struct *)0)->tss.uwinmask))
 
@@ -1131,10 +1191,12 @@
 	cypress_flush_page_to_ram(chunk);
 }
 
+#if NOTUSED
 /* Cypress is also IO cache coherent. */
 static void cypress_flush_page_for_dma(unsigned long page)
 {
 }
+#endif
 
 /* Cypress has unified L2 VIPT, from which both instructions and data
  * are stored.  It does not have an onboard icache of any sort, therefore
@@ -1220,6 +1282,9 @@
 extern void viking_flush_page(unsigned long page);
 extern void viking_mxcc_flush_page(unsigned long page);
 extern void viking_flush_chunk(unsigned long chunk);
+extern void viking_c_flush_page(unsigned long page);
+extern void viking_c_mxcc_flush_page(unsigned long page);
+extern void viking_c_flush_chunk(unsigned long chunk);
 extern void viking_mxcc_flush_chunk(unsigned long chunk);
 extern void viking_flush_tlb_all(void);
 extern void viking_flush_tlb_mm(struct mm_struct *mm);
@@ -1345,184 +1410,6 @@
 		srmmu_set_context(mm->context);
 }
 
-/* IOMMU things go here. */
-
-#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
-
-#define IOPERM        (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID)
-#define MKIOPTE(phys) (((((phys)>>4) & IOPTE_PAGE) | IOPERM) & ~IOPTE_WAZ)
-
-static inline void srmmu_map_dvma_pages_for_iommu(struct iommu_struct *iommu,
-						  unsigned long kern_end)
-{
-	unsigned long first = page_offset;
-	unsigned long last = kern_end;
-	iopte_t *iopte = iommu->page_table;
-
-	iopte += ((first - iommu->start) >> PAGE_SHIFT);
-	while(first <= last) {
-		*iopte++ = __iopte(MKIOPTE(srmmu_v2p(first)));
-		first += PAGE_SIZE;
-	}
-}
-
-unsigned long iommu_init(int iommund, unsigned long memory_start,
-			 unsigned long memory_end, struct linux_sbus *sbus)
-{
-	unsigned int impl, vers, ptsize;
-	unsigned long tmp;
-	struct iommu_struct *iommu;
-	struct linux_prom_registers iommu_promregs[PROMREG_MAX];
-
-	memory_start = LONG_ALIGN(memory_start);
-	iommu = (struct iommu_struct *) memory_start;
-	memory_start += sizeof(struct iommu_struct);
-	prom_getproperty(iommund, "reg", (void *) iommu_promregs,
-			 sizeof(iommu_promregs));
-	iommu->regs = (struct iommu_regs *)
-		sparc_alloc_io(iommu_promregs[0].phys_addr, 0, (PAGE_SIZE * 3),
-			       "IOMMU registers", iommu_promregs[0].which_io, 0x0);
-	if(!iommu->regs)
-		panic("Cannot map IOMMU registers.");
-	impl = (iommu->regs->control & IOMMU_CTRL_IMPL) >> 28;
-	vers = (iommu->regs->control & IOMMU_CTRL_VERS) >> 24;
-	tmp = iommu->regs->control;
-	tmp &= ~(IOMMU_CTRL_RNGE);
-	switch(page_offset & 0xf0000000) {
-	case 0xf0000000:
-		tmp |= (IOMMU_RNGE_256MB | IOMMU_CTRL_ENAB);
-		iommu->plow = iommu->start = 0xf0000000;
-		break;
-	case 0xe0000000:
-		tmp |= (IOMMU_RNGE_512MB | IOMMU_CTRL_ENAB);
-		iommu->plow = iommu->start = 0xe0000000;
-		break;
-	case 0xd0000000:
-	case 0xc0000000:
-		tmp |= (IOMMU_RNGE_1GB | IOMMU_CTRL_ENAB);
-		iommu->plow = iommu->start = 0xc0000000;
-		break;
-	case 0xb0000000:
-	case 0xa0000000:
-	case 0x90000000:
-	case 0x80000000:
-		tmp |= (IOMMU_RNGE_2GB | IOMMU_CTRL_ENAB);
-		iommu->plow = iommu->start = 0x80000000;
-		break;
-	}
-	iommu->regs->control = tmp;
-	iommu_invalidate(iommu->regs);
-	iommu->end = 0xffffffff;
-
-	/* Allocate IOMMU page table */
-	ptsize = iommu->end - iommu->start + 1;
-	ptsize = (ptsize >> PAGE_SHIFT) * sizeof(iopte_t);
-
-	/* Stupid alignment constraints give me a headache. */
-	memory_start = PAGE_ALIGN(memory_start);
-	memory_start = (((memory_start) + (ptsize - 1)) & ~(ptsize - 1));
-	iommu->lowest = iommu->page_table = (iopte_t *) memory_start;
-	memory_start += ptsize;
-
-	/* Initialize new table. */
-	flush_cache_all();
-	memset(iommu->page_table, 0, ptsize);
-	srmmu_map_dvma_pages_for_iommu(iommu, memory_end);
-	if(viking_mxcc_present) {
-		unsigned long start = (unsigned long) iommu->page_table;
-		unsigned long end = (start + ptsize);
-		while(start < end) {
-			viking_mxcc_flush_page(start);
-			start += PAGE_SIZE;
-		}
-	} else if(flush_page_for_dma == viking_flush_page) {
-		unsigned long start = (unsigned long) iommu->page_table;
-		unsigned long end = (start + ptsize);
-		while(start < end) {
-			viking_flush_page(start);
-			start += PAGE_SIZE;
-		}
-	}
-	flush_tlb_all();
-	iommu->regs->base = srmmu_v2p((unsigned long) iommu->page_table) >> 4;
-	iommu_invalidate(iommu->regs);
-
-	sbus->iommu = iommu;
-	printk("IOMMU: impl %d vers %d page table at %p of size %d bytes\n",
-	       impl, vers, iommu->page_table, ptsize);
-	return memory_start;
-}
-
-void iommu_sun4d_init(int sbi_node, struct linux_sbus *sbus)
-{
-	u32 *iommu;
-	struct linux_prom_registers iommu_promregs[PROMREG_MAX];
-
-	prom_getproperty(sbi_node, "reg", (void *) iommu_promregs,
-			 sizeof(iommu_promregs));
-	iommu = (u32 *)
-		sparc_alloc_io(iommu_promregs[2].phys_addr, 0, (PAGE_SIZE * 16),
-			       "XPT", iommu_promregs[2].which_io, 0x0);
-	if(!iommu)
-		panic("Cannot map External Page Table.");
-
-	/* Initialize new table. */
-	flush_cache_all();
-	memset(iommu, 0, 16 * PAGE_SIZE);
-	if(viking_mxcc_present) {
-		unsigned long start = (unsigned long) iommu;
-		unsigned long end = (start + 16 * PAGE_SIZE);
-		while(start < end) {
-			viking_mxcc_flush_page(start);
-			start += PAGE_SIZE;
-		}
-	} else if(flush_page_for_dma == viking_flush_page) {
-		unsigned long start = (unsigned long) iommu;
-		unsigned long end = (start + 16 * PAGE_SIZE);
-		while(start < end) {
-			viking_flush_page(start);
-			start += PAGE_SIZE;
-		}
-	}
-	flush_tlb_all();
-
-	sbus->iommu = (struct iommu_struct *)iommu;
-}
-
-static __u32 srmmu_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus)
-{
-	unsigned long page = ((unsigned long) vaddr) & PAGE_MASK;
-
-	while(page < ((unsigned long)(vaddr + len))) {
-		flush_page_for_dma(page);
-		page += PAGE_SIZE;
-	}
-	return (__u32)vaddr;
-}
-
-static void srmmu_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
-{
-        unsigned long page;
-
-	while(sz >= 0) {
-		page = ((unsigned long) sg[sz].addr) & PAGE_MASK;
-		while(page < (unsigned long)(sg[sz].addr + sg[sz].len)) {
-			flush_page_for_dma(page);
-			page += PAGE_SIZE;
-		}
-		sg[sz].dvma_addr = (__u32) (sg[sz].addr);
-		sz--;
-	}
-}
-
-static void srmmu_release_scsi_one(__u32 vaddr, unsigned long len, struct linux_sbus *sbus)
-{
-}
-
-static void srmmu_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
-{
-}
-
 static unsigned long mempool;
 
 /* NOTE: All of this startup code assumes the low 16mb (approx.) of
@@ -1652,63 +1539,6 @@
 	}
 }
 
-#ifdef CONFIG_SBUS
-static void srmmu_map_dma_area(unsigned long addr, int len)
-{
-	unsigned long page, end;
-	pgprot_t dvma_prot;
-	struct iommu_struct *iommu = SBus_chain->iommu;
-	iopte_t *iopte = iommu->page_table;
-	iopte_t *iopte_first = iopte;
-
-	if(viking_mxcc_present)
-		dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV);
-	else
-		dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV);
-
-	iopte += ((addr - iommu->start) >> PAGE_SHIFT);
-	end = PAGE_ALIGN((addr + len));
-	while(addr < end) {
-		page = get_free_page(GFP_KERNEL);
-		if(!page) {
-			prom_printf("alloc_dvma: Cannot get a dvma page\n");
-			prom_halt();
-		} else {
-			pgd_t *pgdp;
-			pmd_t *pmdp;
-			pte_t *ptep;
-
-			pgdp = srmmu_pgd_offset(init_task.mm, addr);
-			pmdp = srmmu_pmd_offset(pgdp, addr);
-			ptep = srmmu_pte_offset(pmdp, addr);
-
-			set_pte(ptep, pte_val(srmmu_mk_pte(page, dvma_prot)));
-
-			iopte_val(*iopte++) = MKIOPTE(srmmu_v2p(page));
-		}
-		addr += PAGE_SIZE;
-	}
-	flush_cache_all();
-	if(viking_mxcc_present) {
-		unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK;
-		unsigned long end = PAGE_ALIGN(((unsigned long) iopte));
-		while(start < end) {
-			viking_mxcc_flush_page(start);
-			start += PAGE_SIZE;
-		}
-	} else if(flush_page_for_dma == viking_flush_page) {
-		unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK;
-		unsigned long end = PAGE_ALIGN(((unsigned long) iopte));
-		while(start < end) {
-			viking_flush_page(start);
-			start += PAGE_SIZE;
-		}
-	}
-	flush_tlb_all();
-	iommu_invalidate(iommu->regs);
-}
-#endif
-
 /* #define DEBUG_MAP_KERNEL */
 
 #ifdef DEBUG_MAP_KERNEL
@@ -2068,6 +1898,59 @@
 			srmmu_p2v_hash[srmmu_ahashfn(addr)] = &srmmu_map[entry];
 	}
 
+	page_contig_offset = page_offset - (0xfd000000 - KERNBASE);
+	phys_mem_contig = 1;
+	for(entry = 0; srmmu_map[entry].size; entry++)
+		if (srmmu_map[entry].pbase != srmmu_c_v2p (srmmu_map[entry].vbase)) {
+			phys_mem_contig = 0;
+			break;
+		}
+	if (phys_mem_contig) {
+		printk ("SRMMU: Physical memory is contiguous, bypassing VA<->PA hashes\n");
+		pte_page = srmmu_c_pte_page;
+		pmd_page = srmmu_c_pmd_page;
+		pgd_page = srmmu_c_pgd_page;
+		mk_pte = srmmu_c_mk_pte;
+		pte_offset = srmmu_c_pte_offset;
+		pmd_offset = srmmu_c_pmd_offset;
+		if (ctxd_set == srmmu_ctxd_set)
+			ctxd_set = srmmu_c_ctxd_set;
+		pgd_set = srmmu_c_pgd_set;
+		pmd_set = srmmu_c_pmd_set;
+		mmu_v2p = srmmu_c_v2p;
+		mmu_p2v = srmmu_c_p2v;
+		if (flush_chunk == viking_flush_chunk)
+			flush_chunk = viking_c_flush_chunk;
+	}
+	
+	if (sparc_cpu_model == sun4d) {
+		int i, j = -1;
+		unsigned long bank_start, bank_end;
+		
+		sun4d_dma_vbase = 0;
+		sun4d_dma_size = IOUNIT_DMA_SIZE - IOUNIT_DVMA_SIZE;
+		for (i = 0; srmmu_map[i].size; i++) {
+			bank_start = srmmu_map[i].vbase;
+			bank_end = bank_start + srmmu_map[i].size;
+			if (bank_start <= KERNBASE && bank_end > KERNBASE)
+				j = i;
+			else if (srmmu_map[i].size >= sun4d_dma_size) {
+				sun4d_dma_vbase = srmmu_map[i].vbase;
+				break;
+			}
+		}
+		if (!sun4d_dma_vbase && j != -1) {
+			if (srmmu_map[j].size >= sun4d_dma_size + 0x1000000)
+				sun4d_dma_vbase = srmmu_map[j].vbase + 0x1000000;
+			else {
+				sun4d_dma_vbase = srmmu_map[j].vbase;
+				if (srmmu_map[j].size < sun4d_dma_size)
+					sun4d_dma_size = srmmu_map[j].size;
+			}
+		}
+		sun4d_dma_base = IOUNIT_DMA_BASE - srmmu_v2p(sun4d_dma_vbase);
+	}
+
 	return; /* SUCCESS! */
 }
 
@@ -2104,7 +1987,7 @@
 	physmem_mapped_contig = 0;	    /* for init.c:taint_real_pages()   */
 
 	if (sparc_cpu_model == sun4d)
-		num_contexts = 65536; /* We now it is Viking */
+		num_contexts = 65536; /* We know it is Viking */
 	else {
 		/* Find the number of contexts on the srmmu. */
 		cpunode = prom_getchild(prom_root_node);
@@ -2381,11 +2264,6 @@
 	hyper_flush_whole_icache();
 	clear = srmmu_get_faddr();
 	clear = srmmu_get_fstatus();
-
-#ifdef __SMP__
-	/* Avoid unnecessary cross calls. */
-	flush_page_for_dma = local_flush_page_for_dma;
-#endif
 }
 
 __initfunc(static void init_hypersparc(void))
@@ -2407,7 +2285,7 @@
 
 	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_page_for_dma = NULL /* hypersparc_flush_page_for_dma */;
 
 	flush_chunk = hypersparc_flush_chunk; /* local flush _only_ */
 
@@ -2480,7 +2358,7 @@
 
 	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_page_for_dma = NULL /* cypress_flush_page_for_dma */;
 	sparc_update_rootmmu_dir = cypress_update_rootmmu_dir;
 
 	update_mmu_cache = srmmu_vac_update_mmu_cache;
@@ -2671,7 +2549,7 @@
 #endif
 
 	flush_sig_insns = turbosparc_flush_sig_insns;
-	flush_page_for_dma = hypersparc_flush_page_for_dma;
+	flush_page_for_dma = NULL /* turbosparc_flush_page_for_dma */;
 
 	poke_srmmu = poke_turbosparc;
 }
@@ -2761,6 +2639,9 @@
 #ifdef __SMP__
 	/* Avoid unnecessary cross calls. */
 	flush_cache_all = local_flush_cache_all;
+	flush_cache_mm = local_flush_cache_mm;
+	flush_cache_range = local_flush_cache_range;
+	flush_cache_page = local_flush_cache_page;
 	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;
@@ -2796,6 +2677,9 @@
 		 * which we use the IOMMU.
 		 */
 		flush_page_for_dma = viking_flush_page;
+		/* Also, this is so far the only chip which actually uses
+		   the page argument to flush_page_for_dma */
+		flush_page_for_dma_global = 0;
 	} else {
 		srmmu_name = "TI Viking/MXCC";
 		viking_mxcc_present = 1;
@@ -2803,7 +2687,7 @@
 		flush_chunk = viking_mxcc_flush_chunk; /* local flush _only_ */
 
 		/* MXCC vikings lack the DMA snooping bug. */
-		flush_page_for_dma = viking_flush_page_for_dma;
+		flush_page_for_dma = NULL /* viking_flush_page_for_dma */;
 	}
 
 	flush_cache_all = viking_flush_cache_all;
@@ -2951,9 +2835,12 @@
 
 #endif
 
-/* Load up routines and constants for sun4m mmu */
+/* Load up routines and constants for sun4m and sun4d mmu */
 __initfunc(void ld_mmu_srmmu(void))
 {
+	extern void ld_mmu_iommu(void);
+	extern void ld_mmu_iounit(void);
+	
 	/* First the constants */
 	pmd_shift = SRMMU_PMD_SHIFT;
 	pmd_size = SRMMU_PMD_SIZE;
@@ -3031,18 +2918,7 @@
 	pte_mkyoung = srmmu_pte_mkyoung;
 	update_mmu_cache = srmmu_update_mmu_cache;
 	destroy_context = srmmu_destroy_context;
-	mmu_lockarea = srmmu_lockarea;
-	mmu_unlockarea = srmmu_unlockarea;
-
-	mmu_get_scsi_one = srmmu_get_scsi_one;
-	mmu_get_scsi_sgl = srmmu_get_scsi_sgl;
-	mmu_release_scsi_one = srmmu_release_scsi_one;
-	mmu_release_scsi_sgl = srmmu_release_scsi_sgl;
-
-#ifdef CONFIG_SBUS
-	mmu_map_dma_area = srmmu_map_dma_area;
-#endif
-
+	
 	mmu_info = srmmu_mmu_info;
         mmu_v2p = srmmu_v2p;
         mmu_p2v = srmmu_p2v;
@@ -3085,6 +2961,11 @@
 	flush_tlb_page = smp_flush_tlb_page;
 	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;
+	if (flush_page_for_dma)
+		flush_page_for_dma = smp_flush_page_for_dma;
 #endif
+	if (sparc_cpu_model == sun4d)
+		ld_mmu_iounit();
+	else
+		ld_mmu_iommu();
 }
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/mm/turbosparc.S linux/arch/sparc/mm/turbosparc.S
--- v2.1.78/linux/arch/sparc/mm/turbosparc.S	Mon Jul  7 08:18:54 1997
+++ linux/arch/sparc/mm/turbosparc.S	Mon Jan 12 15:15:43 1998
@@ -1,5 +1,5 @@
-/* $Id: turbosparc.S,v 1.1 1997/06/24 15:48:05 jj Exp $
- * turbosparc.S: High speed Hypersparc mmu/cache operations.
+/* $Id: turbosparc.S,v 1.2 1997/11/26 13:27:59 jj Exp $
+ * turbosparc.S: High speed TurboSparc mmu/cache operations.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/mm/viking.S linux/arch/sparc/mm/viking.S
--- v2.1.78/linux/arch/sparc/mm/viking.S	Thu May 15 16:48:02 1997
+++ linux/arch/sparc/mm/viking.S	Mon Jan 12 15:15:43 1998
@@ -1,7 +1,8 @@
-/* $Id: viking.S,v 1.3 1997/05/04 10:02:14 ecd Exp $
+/* $Id: viking.S,v 1.6 1997/11/27 15:42:32 jj Exp $
  * viking.S: High speed Viking cache/mmu operations
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ * Copyright (C) 1997  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
  */
 
 #include <asm/ptrace.h>
@@ -36,6 +37,20 @@
 	.globl	viking_flush_tlb_all, viking_flush_tlb_mm
 	.globl	viking_flush_tlb_range, viking_flush_tlb_page
 
+	.globl	viking_c_mxcc_flush_page
+	.globl	viking_c_flush_page, viking_c_flush_chunk
+
+viking_c_flush_page:
+viking_c_flush_chunk:
+	sethi	%hi(KERNBASE), %g2
+	cmp	%o0, %g2
+	bgeu	2f
+	 sub	%o0, %g2, %g3
+	sethi	%hi(C_LABEL(page_contig_offset)), %g2
+	ld	[%g2 + %lo(C_LABEL(page_contig_offset))], %g2
+	ba	2f
+	 sub	%o0, %g2, %g3
+
 viking_flush_page:
 viking_flush_chunk:
 	sethi	%hi(C_LABEL(srmmu_v2p_hash)), %g2
@@ -56,13 +71,14 @@
 	sub	%o0, %o1, %g2
 	ld	[%g3 + 4], %o0
 	add	%g2, %o0, %g3
-	srl	%g3, 12, %g1		! ppage >> 12
+2:	srl	%g3, 12, %g1		! ppage >> 12
 
 	clr	%o1			! set counter, 0 - 127
 	sethi	%hi(KERNBASE + PAGE_SIZE - 0x80000000), %o3
 	sethi	%hi(0x80000000), %o4
 	sethi	%hi(VIKING_PTAG_VALID | VIKING_PTAG_DIRTY), %o5
-	sethi	%hi(PAGE_SIZE), %o0
+	sethi	%hi(2*PAGE_SIZE), %o0
+	sethi	%hi(PAGE_SIZE), %g7
 	clr	%o2			! block counter, 0 - 3
 5:
 	sll	%o1, 5, %g4
@@ -83,20 +99,16 @@
 
 	add	%g4, %o3, %g2		! (KERNBASE + PAGE_SIZE) | (set << 5)
 	ld	[%g2], %g3
+	ld	[%g2 + %g7], %g3
 	add	%g2, %o0, %g2
 	ld	[%g2], %g3
+	ld	[%g2 + %g7], %g3
 	add	%g2, %o0, %g2
 	ld	[%g2], %g3
+	ld	[%g2 + %g7], %g3
 	add	%g2, %o0, %g2
 	ld	[%g2], %g3
-	add	%g2, %o0, %g2
-	ld	[%g2], %g3
-	add	%g2, %o0, %g2
-	ld	[%g2], %g3
-	add	%g2, %o0, %g2
-	ld	[%g2], %g3
-	add	%g2, %o0, %g2
-	ld	[%g2], %g3
+	ld	[%g2 + %g7], %g3
 
 	b	8f
 	 inc	%o1
@@ -115,6 +127,15 @@
 	retl
 	 nop
 
+viking_c_mxcc_flush_page:
+	sethi	%hi(KERNBASE), %g2
+	cmp	%o0, %g2
+	bgeu	2f
+	 sub	%o0, %g2, %g3
+	sethi	%hi(C_LABEL(page_contig_offset)), %g2
+	ld	[%g2 + %lo(C_LABEL(page_contig_offset))], %g2
+	ba	2f
+	 sub	%o0, %g2, %g3
 
 viking_mxcc_flush_page:
 	sethi	%hi(C_LABEL(srmmu_v2p_hash)), %g2
@@ -134,17 +155,12 @@
 	ld	[%g3], %o1
 	sub	%o0, %o1, %g2
 	ld	[%g3 + 4], %o0
-	sethi   %hi(PAGE_SIZE), %g4
 	add	%g2, %o0, %g3
-	add	%g3, %g4, %g3			! ppage + PAGE_SIZE
-
+2:	sub	%g3, -PAGE_SIZE, %g3		! ppage + PAGE_SIZE
 	mov	0x10, %g2			! set cacheable bit
-	sethi	%hi(MXCC_SRCSTREAM), %o2
-	or	%o2, %lo(MXCC_SRCSTREAM), %o2
-	sethi	%hi(MXCC_DESSTREAM), %o3
+	sethi	%hi(MXCC_SRCSTREAM), %o3	! assume %hi(MXCC_SRCSTREAM) == %hi(MXCC_DESTSTREAM)
+	or	%o3, %lo(MXCC_SRCSTREAM), %o2
 	or	%o3, %lo(MXCC_DESSTREAM), %o3
-
-5:
 	sub	%g3, MXCC_STREAM_SIZE, %g3
 6:
 	stda	%g2, [%o2] ASI_M_MXCC
@@ -168,7 +184,6 @@
 	 nop
 
 viking_flush_tlb_all:
-	WINDOW_FLUSH(%g4, %g5)
 	mov	0x400, %g1
 	retl
 	 sta	%g0, [%g1] ASI_M_FLUSH_PROBE
@@ -179,16 +194,15 @@
 	lda	[%g1] ASI_M_MMUREGS, %g5
 #ifndef __SMP__
 	cmp	%o1, -1
-	be	viking_flush_tlb_mm_out
+	be	1f
 #endif
-	WINDOW_FLUSH(%g2, %g3)
-
 	mov	0x300, %g2
 	sta	%o1, [%g1] ASI_M_MMUREGS
 	sta	%g0, [%g2] ASI_M_FLUSH_PROBE
-viking_flush_tlb_mm_out:
 	retl
 	 sta	%g5, [%g1] ASI_M_MMUREGS
+1:	retl
+	 nop
 
 viking_flush_tlb_range:
 	mov	SRMMU_CTX_REG, %g1
@@ -196,42 +210,39 @@
 	lda	[%g1] ASI_M_MMUREGS, %g5
 #ifndef __SMP__
 	cmp	%o3, -1
-	be	viking_flush_tlb_range_out
+	be	2f
 #endif
-	WINDOW_FLUSH(%g2, %g3)
-
 	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
+1:	add	%o1, %o4, %o1
 	cmp	%o1, %o2
 	blu,a	1b
 	 sta	%g0, [%o1] ASI_M_FLUSH_PROBE
-viking_flush_tlb_range_out:
 	retl
 	 sta	%g5, [%g1] ASI_M_MMUREGS
+2:	retl
+	 nop
 
 viking_flush_tlb_page:
 	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
 	mov	SRMMU_CTX_REG, %g1
 	ld	[%o0 + AOFF_mm_context], %o3
-	and	%o1, PAGE_MASK, %o1
 	lda	[%g1] ASI_M_MMUREGS, %g5
 #ifndef __SMP__
 	cmp	%o3, -1
-	be	viking_flush_tlb_page_out
+	be	1f
 #endif
-	WINDOW_FLUSH(%g2, %g3)
-
+	and	%o1, PAGE_MASK, %o1
 	sta	%o3, [%g1] ASI_M_MMUREGS
 	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
-viking_flush_tlb_page_out:
 	retl
 	 sta	%g5, [%g1] ASI_M_MMUREGS
+1:	retl
+	 nop
 
 viking_flush_page_to_ram:
 viking_flush_page_for_dma:
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/prom/ranges.c linux/arch/sparc/prom/ranges.c
--- v2.1.78/linux/arch/sparc/prom/ranges.c	Mon Mar 17 14:54:22 1997
+++ linux/arch/sparc/prom/ranges.c	Mon Jan 12 15:15:43 1998
@@ -1,7 +1,8 @@
-/* $Id: ranges.c,v 1.8 1997/02/04 07:28:29 davem Exp $
+/* $Id: ranges.c,v 1.10 1997/12/19 12:37:18 jj Exp $
  * ranges.c: Handle ranges in newer proms for obio/sbus.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <linux/init.h>
@@ -22,7 +23,9 @@
 
 	for(regc=0; regc < nregs; regc++) {
 		for(rngc=0; rngc < nranges; rngc++)
-			if(regp[regc].which_io == rangep[rngc].ot_child_space)
+			if(regp[regc].which_io == rangep[rngc].ot_child_space &&
+			   regp[regc].phys_addr >= rangep[rngc].ot_child_base &&
+			   regp[regc].phys_addr + regp[regc].reg_size <= rangep[rngc].ot_child_base + rangep[rngc].or_size)
 				break; /* Fount it */
 		if(rngc==nranges) /* oops */
 			prom_printf("adjust_regs: Could not find range with matching bus type...\n");
@@ -39,10 +42,15 @@
 
 	for(rng1c=0; rng1c < nranges1; rng1c++) {
 		for(rng2c=0; rng2c < nranges2; rng2c++)
-			if(ranges1[rng1c].ot_child_space ==
-			   ranges2[rng2c].ot_child_space) break;
+			if(ranges1[rng1c].ot_parent_space == ranges2[rng2c].ot_child_space &&
+			   ranges1[rng1c].ot_parent_base >= ranges2[rng2c].ot_child_base &&
+			   ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size - ranges1[rng1c].ot_parent_base > 0U)
+			break;
 		if(rng2c == nranges2) /* oops */
 			prom_printf("adjust_ranges: Could not find matching bus type...\n");
+		else if (ranges1[rng1c].ot_parent_base + ranges1[rng1c].or_size > ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size)
+			ranges1[rng1c].or_size =
+				ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size - ranges1[rng1c].ot_parent_base;
 		ranges1[rng1c].ot_parent_space = ranges2[rng2c].ot_parent_space;
 		ranges1[rng1c].ot_parent_base += ranges2[rng2c].ot_parent_base;
 	}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/prom/tree.c linux/arch/sparc/prom/tree.c
--- v2.1.78/linux/arch/sparc/prom/tree.c	Mon Jul  7 08:18:54 1997
+++ linux/arch/sparc/prom/tree.c	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-/* $Id: tree.c,v 1.19 1997/06/27 14:52:54 jj Exp $
+/* $Id: tree.c,v 1.22 1997/09/25 02:19:22 davem Exp $
  * tree.c: Basic device tree traversal/scanning for the Linux
  *         prom library.
  *
@@ -229,36 +229,40 @@
 	return 0;
 }
 
-/* Return the first property type for node 'node'.
- */
-char * prom_firstprop(int node, char *buffer)
+/* Interal version of nextprop that does not alter return values. */
+char * __prom_nextprop(int node, char * oprop)
 {
 	unsigned long flags;
-	char *ret;
+	char *prop;
 
-	if(node == -1) return "";
-	save_flags(flags); cli();
-	ret = prom_nodeops->no_nextprop(node, (char *) 0x0);
+	save_and_cli(flags);
+	prop = prom_nodeops->no_nextprop(node, oprop);
 	restore_current();
 	restore_flags(flags);
-	return ret;
+
+	return prop;
+}
+
+/* Return the first property name for node 'node'. */
+/* buffer is unused argument, but as v9 uses it, we need to have the same interface */
+char * prom_firstprop(int node, char *bufer)
+{
+	if (node == 0 || node == -1)
+		return "";
+
+	return __prom_nextprop(node, "");
 }
 
 /* Return the property type string after property type 'oprop'
- * at node 'node' .  Returns NULL string if no more
+ * at node 'node' .  Returns empty string if no more
  * property types for this node.
  */
 char * prom_nextprop(int node, char *oprop, char *buffer)
 {
-	char *ret;
-	unsigned long flags;
+	if (node == 0 || node == -1)
+		return "";
 
-	if(node == -1) return "";
-	save_flags(flags); cli();
-	ret = prom_nodeops->no_nextprop(node, oprop);
-	restore_current();
-	restore_flags(flags);
-	return ret;
+	return __prom_nextprop(node, oprop);
 }
 
 int prom_finddevice(char *name)
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/Makefile linux/arch/sparc64/Makefile
--- v2.1.78/linux/arch/sparc64/Makefile	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/Makefile	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.22 1997/08/29 15:51:53 jj Exp $
+# $Id: Makefile,v 1.24 1997/10/02 16:31:16 jj Exp $
 # sparc64/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
@@ -38,10 +38,18 @@
   SUBDIRS += arch/sparc64/solaris
 endif
 
+ifneq ($(CONFIG_MATHEMU),n)
+  SUBDIRS += arch/sparc64/math-emu
+endif
+
 CORE_FILES := arch/sparc64/kernel/kernel.o arch/sparc64/mm/mm.o $(CORE_FILES)
 
 ifeq ($(CONFIG_SOLARIS_EMUL),y)
   CORE_FILES += arch/sparc64/solaris/solaris.o
+endif
+
+ifeq ($(CONFIG_MATHEMU),y)
+  CORE_FILES += arch/sparc64/math-emu/math-emu.o
 endif
 
 LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc64/prom/promlib.a \
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/boot/Makefile linux/arch/sparc64/boot/Makefile
--- v2.1.78/linux/arch/sparc64/boot/Makefile	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/boot/Makefile	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.3 1997/08/29 11:08:34 davem Exp $
+# $Id: Makefile,v 1.4 1997/12/15 20:08:56 ecd Exp $
 # Makefile for the Sparc64 boot stuff.
 #
 # Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -12,7 +12,7 @@
 boot:
 	@echo "Nothing special to be done for 'boot' on Linux/UltraSPARC."
 
-tftpboot.img: piggyback
+tftpboot.img: piggyback $(TOPDIR)/vmlinux $(ROOT_IMG)
 	$(ELFTOAOUT) $(TOPDIR)/vmlinux -o tftpboot.img
 	./piggyback tftpboot.img $(TOPDIR)/System.map $(ROOT_IMG)
 
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/config.in linux/arch/sparc64/config.in
--- v2.1.78/linux/arch/sparc64/config.in	Tue Dec  2 16:45:18 1997
+++ linux/arch/sparc64/config.in	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.17 1997/09/04 01:54:43 davem Exp $
+# $Id: config.in,v 1.36 1998/01/10 19:04:30 ecd Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -39,6 +39,7 @@
 	define_bool CONFIG_SUN_MOUSE y
 	define_bool CONFIG_SERIAL y
 	define_bool CONFIG_SUN_SERIAL y
+	define_bool CONFIG_SERIAL_CONSOLE y
 	define_bool CONFIG_SUN_KEYBOARD y
 	define_bool CONFIG_SUN_CONSOLE y
 	define_bool CONFIG_SUN_AUXIO y
@@ -48,8 +49,10 @@
 fi
 
 tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS
+bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
+bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
 bool 'Sysctl support' CONFIG_SYSCTL
 bool 'Kernel support for Linux/Sparc 32bit binary compatibility' CONFIG_SPARC32_COMPAT
 tristate 'Kernel support for 64-bit ELF binaries' CONFIG_BINFMT_ELF
@@ -64,8 +67,25 @@
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   tristate 'Solaris binary emulation' CONFIG_SOLARIS_EMUL
 fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Kernel Quad FPU (long double) and subnormal float/double emulation' CONFIG_MATHEMU
+fi
 endmenu
 
+if [ "$CONFIG_PCI" = "y" ]; then
+  tristate 'Parallel port support' CONFIG_PARPORT
+  if [ "$CONFIG_PARPORT" != "n" ]; then
+    dep_tristate '  Ultra/AX-style hardware' CONFIG_PARPORT_AX $CONFIG_PARPORT
+    if [ "$CONFIG_PARPORT_AX" != "n" ]; then
+      bool '  Support foreign hardware' CONFIG_PARPORT_OTHER
+    fi
+    dep_tristate '  Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT
+    if [ "$CONFIG_PRINTER" != "n" ]; then
+      bool '    Support IEEE1284 status readback' CONFIG_PRINTER_READBACK
+    fi
+  fi
+fi
+
 mainmenu_option next_comment
 comment 'Floppy, IDE, and other block devices'
 
@@ -75,7 +95,8 @@
 if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
   tristate '   Linear (append) mode' CONFIG_MD_LINEAR
   tristate '   RAID-0 (striping) mode' CONFIG_MD_STRIPED
-#  tristate '   RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
+  tristate '   RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
+  tristate '   RAID-4/RAID-5 mode' CONFIG_MD_RAID5
 fi
 
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
@@ -84,6 +105,7 @@
 fi
 
 tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
+tristate 'Network block device support' CONFIG_BLK_DEV_NBD
 
 if [ "$CONFIG_PCI" = "y" ]; then
 	tristate 'Ultra/PCI IDE disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE
@@ -94,7 +116,7 @@
 	  dep_tristate '   Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
 	  dep_tristate '   SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
 	  define_bool CONFIG_IDE_CHIPSETS y
-	  define_bool CONFIG_BLK_DEV_NS87415 y
+	  define_bool CONFIG_BLK_DEV_NS87415_AX y
 	fi
 fi
 
@@ -144,12 +166,27 @@
 	    bool '   Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
 	    int  '   Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15
 	  fi
+	  dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI
+	  if [ "$CONFIG_SCSI_NCR53C8XX" != "n" ]; then
+	    bool '  detect and read serial NVRAMs' CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
+	    bool '  enable tagged command queueing' CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
+	    int  '  maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 12
+	    int  '  synchronous transfers frequency in MHz' CONFIG_SCSI_NCR53C8XX_SYNC 10
+	    if [ "$CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE" != "y" ]; then
+	      bool '  not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
+	    fi
+	    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+	      bool '  assume boards are SYMBIOS compatible' CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+	    fi
+	  fi
 	fi
 
 	endmenu
 fi
 endmenu
 
+source drivers/fc4/Config.in
+
 if [ "$CONFIG_NET" = "y" ]; then
 	mainmenu_option next_comment
 	comment 'Network device support'
@@ -197,11 +234,18 @@
 source fs/nls/Config.in
 
 mainmenu_option next_comment
+comment 'Watchdog'
+
+tristate 'Software watchdog' CONFIG_SOFT_WATCHDOG
+endmenu
+
+mainmenu_option next_comment
 comment 'Kernel hacking'
 
 bool 'Kernel profiling support' CONFIG_PROFILE
 if [ "$CONFIG_PROFILE" = "y" ]; then
 	int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
 fi
+bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
 bool 'ECache flush trap support at ta 0x72' CONFIG_EC_FLUSH_TRAP
 endmenu
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- v2.1.78/linux/arch/sparc64/defconfig	Sun Sep  7 13:10:42 1997
+++ linux/arch/sparc64/defconfig	Mon Jan 12 15:15:43 1998
@@ -11,8 +11,8 @@
 # Loadable module support
 #
 CONFIG_MODULES=y
-# CONFIG_MODVERSIONS is not set
-# CONFIG_KERNELD is not set
+CONFIG_MODVERSIONS=y
+CONFIG_KERNELD=y
 
 #
 # General setup
@@ -30,6 +30,7 @@
 CONFIG_SUN_AUXIO=y
 CONFIG_SUN_IO=y
 CONFIG_PCI=y
+CONFIG_PCI_OLD_PROC=y
 
 #
 # SBUS Frame Buffer support
@@ -50,20 +51,27 @@
 #
 CONFIG_SUN_OPENPROMIO=m
 CONFIG_SUN_MOSTEK_RTC=y
-# CONFIG_SAB82532 is not set
+CONFIG_SAB82532=y
+# CONFIG_OBP_FLASH is not set
 # CONFIG_SUN_BPP is not set
 # CONFIG_SUN_VIDEOPIX is not set
 CONFIG_SUN_OPENPROMFS=m
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
 CONFIG_SYSCTL=y
 CONFIG_SPARC32_COMPAT=y
 CONFIG_BINFMT_ELF=y
 CONFIG_BINFMT_ELF32=y
 CONFIG_BINFMT_AOUT32=y
-CONFIG_BINFMT_JAVA=m
 CONFIG_BINFMT_MISC=m
+CONFIG_BINFMT_JAVA=m
 CONFIG_SOLARIS_EMUL=m
+CONFIG_MATHEMU=m
+CONFIG_PARPORT=y
+CONFIG_PARPORT_AX=y
+CONFIG_PRINTER=y
+CONFIG_PRINTER_READBACK=y
 
 #
 # Floppy, IDE, and other block devices
@@ -72,9 +80,12 @@
 CONFIG_BLK_DEV_MD=y
 CONFIG_MD_LINEAR=m
 CONFIG_MD_STRIPED=m
+CONFIG_MD_MIRRORING=m
+CONFIG_MD_RAID5=m
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_NBD is not set
 CONFIG_BLK_DEV_IDE=y
 CONFIG_BLK_DEV_IDEDISK=y
 CONFIG_BLK_DEV_IDECD=y
@@ -82,46 +93,60 @@
 CONFIG_BLK_DEV_IDEFLOPPY=m
 # CONFIG_BLK_DEV_IDESCSI is not set
 CONFIG_IDE_CHIPSETS=y
-CONFIG_BLK_DEV_NS87415=y
+CONFIG_BLK_DEV_NS87415_AX=y
 
 #
 # Networking options
 #
+CONFIG_PACKET=y
 # CONFIG_NETLINK is not set
 # CONFIG_FIREWALL is not set
 # CONFIG_NET_ALIAS is not set
+CONFIG_UNIX=y
 CONFIG_INET=y
 # CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
 # CONFIG_IP_ACCT is not set
+# CONFIG_IP_MASQUERADE is not set
 # CONFIG_IP_ROUTER is not set
 # CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
 # CONFIG_SYN_COOKIES is not set
-# CONFIG_XTP is not set
 
 #
 # (it is safe to leave these untouched)
 #
-# CONFIG_INET_PCTCP is not set
 CONFIG_INET_RARP=m
-CONFIG_PATH_MTU_DISCOVERY=y
 CONFIG_IP_NOSR=y
 CONFIG_SKB_LARGE=y
 CONFIG_IPV6=m
+# CONFIG_IPV6_EUI64 is not set
+# CONFIG_IPV6_NO_PB is not set
 
 #
 #  
 #
 CONFIG_IPX=m
 # CONFIG_IPX_INTERN is not set
-# CONFIG_IPX_PPROP_ROUTING is not set
 CONFIG_ATALK=m
-# CONFIG_IPDDP is not set
 # CONFIG_AX25 is not set
 # CONFIG_X25 is not set
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_LLC is not set
 # CONFIG_WAN_ROUTER is not set
+# CONFIG_CPU_IS_SLOW is not set
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=y
+CONFIG_NET_SCH_CSZ=y
+CONFIG_NET_SCH_HFQ=y
+CONFIG_NET_SCH_RED=y
+CONFIG_NET_SCH_SFQ=y
+CONFIG_NET_SCH_TBF=y
+CONFIG_NET_SCH_PFIFO=y
+CONFIG_NET_SCH_PRIO=y
 
 #
 # SCSI support
@@ -154,6 +179,12 @@
 # CONFIG_AIC7XXX_PAGE_ENABLE is not set
 # CONFIG_AIC7XXX_PROC_STATS is not set
 CONFIG_AIC7XXX_RESET_DELAY=5
+CONFIG_SCSI_NCR53C8XX=y
+CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT=y
+CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS=12
+CONFIG_SCSI_NCR53C8XX_SYNC=10
+# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
 
 #
 # Network device support
@@ -174,6 +205,7 @@
 CONFIG_SUNQE=m
 CONFIG_MYRI_SBUS=m
 CONFIG_DE4X5=y
+CONFIG_CDROM=y
 
 #
 # Filesystems
@@ -181,22 +213,21 @@
 # CONFIG_QUOTA is not set
 CONFIG_MINIX_FS=m
 CONFIG_EXT2_FS=y
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
 CONFIG_FAT_FS=m
 CONFIG_MSDOS_FS=m
-# CONFIG_VFAT_FS is not set
 # CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
-CONFIG_ROOT_NFS=y
-CONFIG_RNFS_BOOTP=y
-# CONFIG_RNFS_RARP is not set
 CONFIG_NFSD=m
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
 CONFIG_SMB_FS=m
 CONFIG_SMB_WIN95=y
 CONFIG_NCP_FS=m
-CONFIG_ISO9660_FS=y
 CONFIG_HPFS_FS=m
 CONFIG_SYSV_FS=m
 CONFIG_AFFS_FS=m
@@ -209,7 +240,44 @@
 # CONFIG_MAC_PARTITION is not set
 
 #
+# Native Language Support
+#
+CONFIG_NLS=y
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
+
+#
+# Watchdog
+#
+# CONFIG_SOFT_WATCHDOG is not set
+
+#
 # Kernel hacking
 #
 # CONFIG_PROFILE is not set
+# CONFIG_MAGIC_SYSRQ is not set
 # CONFIG_EC_FLUSH_TRAP is not set
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/Makefile linux/arch/sparc64/kernel/Makefile
--- v2.1.78/linux/arch/sparc64/kernel/Makefile	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/Makefile	Mon Jan 12 15:15:43 1998
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.34 1997/08/12 04:12:36 ecd Exp $
+# $Id: Makefile,v 1.35 1997/09/20 21:48:58 davem Exp $
 # Makefile for the linux kernel.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -71,8 +71,8 @@
 	@rm -f tmp.[ci]
 	#$(CC) -o check_asm check_asm.c
 	# <hack> Until we can do this natively, a hack has to take place
-	$(CC) -mmedlow -S -o check_asm.s check_asm.c
-	$(HOSTCC) -o check_asm check_asm.s
+	$(CC) -mmedlow -ffixed-g4 -S -o check_asm.s check_asm.c
+	$(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s
 	@rm -f check_asm.s
 	# </hack>
 	./check_asm > asm_offsets.h
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/binfmt_aout32.c linux/arch/sparc64/kernel/binfmt_aout32.c
--- v2.1.78/linux/arch/sparc64/kernel/binfmt_aout32.c	Sat Oct 25 02:44:15 1997
+++ linux/arch/sparc64/kernel/binfmt_aout32.c	Mon Jan 12 15:15:44 1998
@@ -57,11 +57,12 @@
  * macros to write out all the necessary info.
  */
 #define DUMP_WRITE(addr,nr) \
-while (file.f_op->write(inode,&file,(char *)(addr),(nr)) != (nr)) goto close_coredump
+while (file.f_op->write(&file,(char *)(addr),(nr),&file.f_pos) != (nr)) \
+	goto close_coredump
 
 #define DUMP_SEEK(offset) \
 if (file.f_op->llseek) { \
-	if (file.f_op->llseek(inode,&file,(offset),0) != (offset)) \
+	if (file.f_op->llseek(&file,(offset),0) != (offset)) \
  		goto close_coredump; \
 } else file.f_pos = (offset)
 
@@ -81,7 +82,7 @@
 	struct dentry * dentry = NULL;
 	struct inode * inode = NULL;
 	struct file file;
-	unsigned short fs;
+	mm_segment_t fs;
 	int has_dumped = 0;
 	char corefile[6+sizeof(current->comm)];
 	unsigned long dump_start, dump_size;
@@ -256,7 +257,7 @@
 	unsigned long p = bprm->p;
 	unsigned long fd_offset;
 	unsigned long rlim;
-	int retval;
+int retval;
 
 	ex = *((struct exec *) bprm->buf);		/* exec-header */
 	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
@@ -285,8 +286,6 @@
 		return retval;
 
 	/* OK, This is the point of no return */
-	memcpy(&current->tss.core_exec, &ex, sizeof(struct exec));
-
 	current->mm->end_code = ex.a_text +
 		(current->mm->start_code = N_TXTADDR(ex));
 	current->mm->end_data = ex.a_data +
@@ -420,13 +419,13 @@
 
 	/* Seek into the file */
 	if (file->f_op->llseek) {
-		if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0)
+		if ((error = file->f_op->llseek(file, 0, 0)) != 0)
 			return -ENOEXEC;
 	} else
 		file->f_pos = 0;
 
 	set_fs(KERNEL_DS);
-	error = file->f_op->read(inode, file, (char *) &ex, sizeof(ex));
+	error = file->f_op->read(file, (char *) &ex, sizeof(ex), &file->f_pos);
 	set_fs(USER_DS);
 	if (error != sizeof(ex))
 		return -ENOEXEC;
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/dtlb_miss.S linux/arch/sparc64/kernel/dtlb_miss.S
--- v2.1.78/linux/arch/sparc64/kernel/dtlb_miss.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/dtlb_miss.S	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: dtlb_miss.S,v 1.13 1997/08/14 19:27:15 davem Exp $
+/* $Id: dtlb_miss.S,v 1.14 1997/10/14 01:48:28 davem Exp $
  * dtlb_miss.S:	Data TLB miss code, this is included directly
  *              into the trap table.
  *
@@ -53,10 +53,10 @@
   /*0x24*/	srlx		%g1, 1, %g1				! PTE offset
 2:/*0x28*/	ldxa		[%g5 + %g4] ASI_PHYS_USE_EC, %g3	! Load PMD
   /*0x2c*/	ldxa		[%g3 + %g1] ASI_PHYS_USE_EC, %g5	! Load PTE
-  /*0x30*/	brlz,a,pt	%g5, 1f					! Valid set?
-  /*0x34*/	 stxa		%g5, [%g0] ASI_DTLB_DATA_IN		! TLB load
-  /*0x38*/	ba,a,pt		%xcc, sparc64_dtlb_refbit_catch		! Nope...
-1:/*0x3c*/	retry							! Trap return
+  /*0x30*/	brgez,pn	%g5, sparc64_dtlb_refbit_catch		! Valid set?
+  /*0x34*/	 nop							! delay
+  /*0x38*/	stxa		%g5, [%g0] ASI_DTLB_DATA_IN		! TLB load
+  /*0x3c*/	retry							! Trap return
 
 3:				/* ICACHE line 3 */
   /*0x40*/	sllx		%g1, 22, %g5			! This is now physical page + PAGE_OFFSET
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/ebus.c linux/arch/sparc64/kernel/ebus.c
--- v2.1.78/linux/arch/sparc64/kernel/ebus.c	Sun Sep  7 13:10:42 1997
+++ linux/arch/sparc64/kernel/ebus.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: ebus.c,v 1.8 1997/09/05 22:59:39 ecd Exp $
+/* $Id: ebus.c,v 1.17 1998/01/10 18:26:13 ecd Exp $
  * ebus.c: PCI to EBus bridge device.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -17,8 +17,15 @@
 #include <asm/oplib.h>
 #include <asm/bpp.h>
 
+#undef PROM_DEBUG
 #undef DEBUG_FILL_EBUS_DEV
 
+#ifdef PROM_DEBUG
+#define dprintf	prom_printf
+#else
+#define dprintf	printk
+#endif
+
 struct linux_ebus *ebus_chain = 0;
 
 extern void prom_ebus_ranges_init(struct linux_ebus *);
@@ -36,8 +43,12 @@
 #ifdef CONFIG_SUN_AUXIO
 extern void auxio_probe(void);
 #endif
+#ifdef CONFIG_OBP_FLASH
+extern int flash_init(void);
+#endif
 
-extern unsigned int psycho_irq_build(unsigned int full_ino);
+extern unsigned int psycho_irq_build(struct linux_pbm_info *pbm,
+				     unsigned int full_ino);
 
 static inline unsigned long
 ebus_alloc(unsigned long *memory_start, size_t size)
@@ -47,6 +58,7 @@
 	*memory_start = (*memory_start + 7) & ~(7);
 	mem = *memory_start;
 	*memory_start += size;
+	memset((void *)mem, 0, size);
 	return mem;
 }
 
@@ -79,19 +91,19 @@
 	} else {
 		dev->num_irqs = len / sizeof(irqs[0]);
 		for (i = 0; i < dev->num_irqs; i++)
-			dev->irqs[i] = psycho_irq_build(irqs[i]);
+			dev->irqs[i] = psycho_irq_build(dev->bus->parent, irqs[i]);
 	}
 
 #ifdef DEBUG_FILL_EBUS_DEV
-	printk("child '%s': address%s\n", dev->prom_name,
+	dprintf("child '%s': address%s\n", dev->prom_name,
 	       dev->num_addrs > 1 ? "es" : "");
 	for (i = 0; i < dev->num_addrs; i++)
-		printk("        %016lx\n", dev->base_address[i]);
+		dprintf("        %016lx\n", dev->base_address[i]);
 	if (dev->num_irqs) {
-		printk("        IRQ%s", dev->num_irqs > 1 ? "s" : "");
+		dprintf("        IRQ%s", dev->num_irqs > 1 ? "s" : "");
 		for (i = 0; i < dev->num_irqs; i++)
-			printk(" %08x", dev->irqs[i]);
-		printk("\n");
+			dprintf(" %08x", dev->irqs[i]);
+		dprintf("\n");
 	}
 #endif
 }
@@ -121,7 +133,7 @@
 	for (i = 0; i < dev->num_addrs; i++) {
 		n = (regs[i].which_io - 0x10) >> 2;
 
-		dev->base_address[i] = dev->parent->self->base_address[n];
+		dev->base_address[i] = dev->bus->self->base_address[n];
 		dev->base_address[i] += (unsigned long)regs[i].phys_addr;
 	}
 
@@ -131,19 +143,19 @@
 	} else {
 		dev->num_irqs = len / sizeof(irqs[0]);
 		for (i = 0; i < dev->num_irqs; i++)
-			dev->irqs[i] = psycho_irq_build(irqs[i]);
+			dev->irqs[i] = psycho_irq_build(dev->bus->parent, irqs[i]);
 	}
 
 #ifdef DEBUG_FILL_EBUS_DEV
-	printk("'%s': address%s\n", dev->prom_name,
+	dprintf("'%s': address%s\n", dev->prom_name,
 	       dev->num_addrs > 1 ? "es" : "");
 	for (i = 0; i < dev->num_addrs; i++)
-		printk("  %016lx\n", dev->base_address[i]);
+		dprintf("  %016lx\n", dev->base_address[i]);
 	if (dev->num_irqs) {
-		printk("  IRQ%s", dev->num_irqs > 1 ? "s" : "");
+		dprintf("  IRQ%s", dev->num_irqs > 1 ? "s" : "");
 		for (i = 0; i < dev->num_irqs; i++)
-			printk(" %08x", dev->irqs[i]);
-		printk("\n");
+			dprintf(" %08x", dev->irqs[i]);
+		dprintf("\n");
 	}
 #endif
 	if ((node = prom_getchild(node))) {
@@ -153,6 +165,7 @@
 		child = dev->children;
 		child->next = 0;
 		child->parent = dev;
+		child->bus = dev->bus;
 		fill_ebus_child(node, child);
 
 		while ((node = prom_getsibling(node))) {
@@ -162,6 +175,7 @@
 			child = child->next;
 			child->next = 0;
 			child->parent = dev;
+			child->bus = dev->bus;
 			fill_ebus_child(node, child);
 		}
 	}
@@ -195,6 +209,9 @@
 	}
 	if (!pdev) {
 		printk("ebus: No EBus's found.\n");
+#ifdef PROM_DEBUG
+		dprintf("ebus: No EBus's found.\n");
+#endif
 		return memory_start;
 	}
 
@@ -206,7 +223,10 @@
 	ebus->next = 0;
 
 	while (ebusnd) {
-		printk("ebus%d:\n", num_ebus);
+		printk("ebus%d:", num_ebus);
+#ifdef PROM_DEBUG
+		dprintf("ebus%d:", num_ebus);
+#endif
 
 		prom_getstring(ebusnd, "name", lbuf, sizeof(lbuf));
 		ebus->prom_node = ebusnd;
@@ -250,20 +270,34 @@
 				addr += (u64)rp->parent_phys_hi << 32UL;
 				*base++ = (unsigned long)__va(addr);
 
+				printk(" %lx[%x]", (unsigned long)__va(addr),
+				       regs[reg].size_lo);
+#ifdef PROM_DEBUG
+				dprintf(" %lx[%x]", (unsigned long)__va(addr),
+				        regs[reg].size_lo);
+#endif
 				break;
 			}
 		}
+		printk("\n");
+#ifdef PROM_DEBUG
+		dprintf("\n");
+#endif
 
 		prom_ebus_ranges_init(ebus);
 
 		nd = prom_getchild(ebusnd);
+		if (!nd)
+			goto next_ebus;
+
 		ebus->devices = (struct linux_ebus_device *)
-			ebus_alloc(&memory_start, sizeof(struct linux_ebus_device));
+				ebus_alloc(&memory_start,
+					   sizeof(struct linux_ebus_device));
 
 		dev = ebus->devices;
 		dev->next = 0;
 		dev->children = 0;
-		dev->parent = ebus;
+		dev->bus = ebus;
 		memory_start = fill_ebus_device(nd, dev, memory_start);
 
 		while ((nd = prom_getsibling(nd))) {
@@ -273,10 +307,11 @@
 			dev = dev->next;
 			dev->next = 0;
 			dev->children = 0;
-			dev->parent = ebus;
+			dev->bus = ebus;
 			memory_start = fill_ebus_device(nd, dev, memory_start);
 		}
 
+	next_ebus:
 		for (pdev = pdev->next; pdev; pdev = pdev->next) {
 			if ((pdev->vendor == PCI_VENDOR_ID_SUN) &&
 			    (pdev->device == PCI_DEVICE_ID_SUN_EBUS))
@@ -312,6 +347,9 @@
 #ifdef CONFIG_SUN_AUXIO
 	if (sparc_cpu_model == sun4u)
 		auxio_probe();
+#endif
+#ifdef CONFIG_OBP_FLASH
+	flash_init();
 #endif
 #ifdef __sparc_v9__
 	if (sparc_cpu_model == sun4u) {
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/entry.S linux/arch/sparc64/kernel/entry.S
--- v2.1.78/linux/arch/sparc64/kernel/entry.S	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/entry.S	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.65 1997/08/29 15:51:29 jj Exp $
+/* $Id: entry.S,v 1.76 1998/01/05 17:00:13 jj Exp $
  * arch/sparc64/kernel/entry.S:  Sparc64 trap low-level entry points.
  *
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -121,7 +121,7 @@
 	.align		32
 	.globl		do_fpdis
 do_fpdis:
-	ldx		[%g6 + AOFF_task_tss + AOFF_thread_flags], %g5		! Load	Group
+	lduh		[%g6 + AOFF_task_tss + AOFF_thread_flags], %g5		! Load	Group
 	sethi		%hi(TSTATE_PEF), %g4					! IEU0
 	sethi		%hi(FPDIS_OFF), %g3					! IEU1
 	wr		%g0, FPRS_FEF, %fprs					! LSU	Group+4bubbles
@@ -337,9 +337,28 @@
 	retl
 	 stx		%o1, [%o0 + PT_V9_TSTATE]
 
+	.globl		utrap, utrap_ill
+utrap:	brz,pn		%g1, etrap
+	 nop
+	save		%sp, -128, %sp
+	rdpr		%tstate, %l6
+	rdpr		%cwp, %l7
+	andn		%l6, TSTATE_CWP, %l6
+	wrpr		%l6, %l7, %tstate
+	rdpr		%tpc, %l6
+	rdpr		%tnpc, %l7
+	wrpr		%g1, 0, %tnpc
+	done
+utrap_ill:
+        call		bad_trap
+	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+	ba,pt		%xcc, rtrap
+	 clr		%l6
+
 #ifdef CONFIG_BLK_DEV_FD
 	.globl		floppy_hardint
 floppy_hardint:
+	wr		%g0, (1 << 11), %clear_softint
 	sethi		%hi(doing_pdma), %g1
 	ld		[%g1 + %lo(doing_pdma)], %g2
 	brz,pn		%g2, floppy_dosoftint
@@ -404,8 +423,9 @@
 	sethi		%hi(irq_action), %g1
 	or		%g1, %lo(irq_action), %g1
 	ldx		[%g1 + (11 << 3)], %g3		! irqaction[floppy_irq]
-	ldx		[%g3 + 0x10], %g4		! action->mask
-	st		%g0, [%g4]			! SYSIO_ICLR_IDLE
+	ldx		[%g3 + 0x10], %g4		! action->mask == ino_bucket ptr
+	ldx		[%g4 + 0x18], %g4		! bucket->iclr
+	stw		%g0, [%g4]			! SYSIO_ICLR_IDLE
 	membar		#Sync				! probably not needed...
 	retry
 
@@ -446,14 +466,52 @@
 	cmp		%g3, 1
 	bgu,a,pn	%icc, winfix_mna
 	 rdpr		%tpc, %g3
+	mov		DMMU_SFAR, %g4
+	mov		TLB_SFSR, %g5
 	sethi		%hi(109f), %g7
+	ldxa		[%g4] ASI_DMMU, %g4
+	ldxa		[%g5] ASI_DMMU, %g5
 	ba,pt		%xcc, etrap
 109:	 or		%g7, %lo(109b), %g7
+	mov		%l4, %o1
+	mov		%l5, %o2
 	call		mem_address_unaligned
 	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
 	ba,pt		%xcc, rtrap
 	 clr		%l6
 
+	.globl		do_lddfmna
+do_lddfmna:
+	mov		DMMU_SFAR, %g4
+	mov		TLB_SFSR, %g5
+	sethi		%hi(109f), %g7
+	ldxa		[%g4] ASI_DMMU, %g4
+	ldxa		[%g5] ASI_DMMU, %g5
+	ba,pt		%xcc, etrap
+109:	 or		%g7, %lo(109b), %g7
+	mov		%l4, %o1
+	mov		%l5, %o2
+	call		handle_lddfmna
+	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+	ba,pt		%xcc, rtrap
+	 clr		%l6
+
+	.globl		do_stdfmna
+do_stdfmna:
+	mov		DMMU_SFAR, %g4
+	mov		TLB_SFSR, %g5
+	sethi		%hi(109f), %g7
+	ldxa		[%g4] ASI_DMMU, %g4
+	ldxa		[%g5] ASI_DMMU, %g5
+	ba,pt		%xcc, etrap
+109:	 or		%g7, %lo(109b), %g7
+	mov		%l4, %o1
+	mov		%l5, %o2
+	call		handle_stdfmna
+	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+	ba,pt		%xcc, rtrap
+	 clr		%l6
+
 	.globl	breakpoint_trap
 breakpoint_trap:
 	call		sparc_breakpoint
@@ -471,13 +529,13 @@
 	mov		%o7, %l4
 	cmp		%o0, NR_SYSCALLS
 	blu,a,pt	%icc, 1f
-	 sll		%o0, 0x3, %o0
+	 sll		%o0, 0x2, %o0
 	sethi		%hi(sunos_nosys), %l6
 	b,pt		%xcc, 2f
 	 or		%l6, %lo(sunos_nosys), %l6
 1:	sethi		%hi(sunos_sys_table), %l7
 	or		%l7, %lo(sunos_sys_table), %l7
-	ldx		[%l7 + %o0], %l6
+	lduw		[%l7 + %o0], %l6
 2:	mov		%o1, %o0
 	mov		%o2, %o1
 	mov		%o3, %o2
@@ -526,7 +584,9 @@
 	 add	%sp, STACK_BIAS + REGWIN_SZ, %o0
 
 	.globl	sys_pipe, sys_execve, sys_sigpause, sys_nis_syscall
-	.globl	sys_sigsuspend, sys_sigreturn
+	.globl	sys_sigsuspend, sys_rt_sigsuspend, sys32_rt_sigsuspend
+	.globl	sys_sigreturn, sys_rt_sigreturn
+	.globl	sys32_sigreturn, sys32_rt_sigreturn
 	.globl	sys32_execve, sys_ptrace
 	.align	32
 sys_pipe:	sethi		%hi(sparc_pipe), %g1
@@ -546,42 +606,57 @@
 		add		%sp, STACK_BIAS + REGWIN_SZ, %o0
 		jmpl		%g1 + %lo(sparc32_execve), %g0
 		 nop
-
-		/* NOTE: %o0 has a correct value already */
-sys_sigpause:	call		do_sigpause
-		 add		%sp, STACK_BIAS + REGWIN_SZ, %o1
-		ldx		[%curptr + AOFF_task_flags], %l5
-		andcc		%l5, 0x20, %g0
-		be,pt		%icc, rtrap
-		 clr		%l6
-		call		syscall_trace
+sys_memory_ordering:
+		sethi		%hi(sparc_memory_ordering), %g1
+		add		%sp, STACK_BIAS + REGWIN_SZ, %o1
+		jmpl		%g1 + %lo(sparc_memory_ordering), %g0
 		 nop
 
-		ba,pt		%xcc, rtrap
-		 clr		%l6
-linux_sparc_ni_syscall:
-		sethi		%hi(sys_ni_syscall), %l7
-		b,pt		%xcc,syscall_is_too_hard
-		 or		%l7, %lo(sys_ni_syscall), %l7
-		nop
-
 		.align		32
-sys_sigsuspend:	call		do_sigsuspend
-		 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
-		ldx		[%curptr + AOFF_task_flags], %l5
-		andcc		%l5, 0x20, %g0
-		be,pt		%icc, rtrap
-		 clr		%l6
-		call		syscall_trace
-		 nop
-
-		ba,pt		%xcc, rtrap
-		 clr		%l6
-	
+sys_sigsuspend:	add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+		call		do_sigsuspend
+		 add		%o7, 1f-.-4, %o7
+		nop
+sys_rt_sigsuspend: /* NOTE: %o0,%o1 have a correct value already */
+		add		%sp, STACK_BIAS + REGWIN_SZ, %o2
+		call		do_rt_sigsuspend
+		 add		%o7, 1f-.-4, %o7
+		nop
+sys32_rt_sigsuspend: /* NOTE: %o0,%o1 have a correct value already */
+		srl		%o0, 0, %o0
+		add		%sp, STACK_BIAS + REGWIN_SZ, %o2
+		call		do_rt_sigsuspend32
+		 add		%o7, 1f-.-4, %o7
+		/* NOTE: %o0 has a correct value already */
+sys_sigpause:	add		%sp, STACK_BIAS + REGWIN_SZ, %o1
+		call		do_sigpause
+		 add		%o7, 1f-.-4, %o7
+		nop
+sys_sigreturn:	add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+		call		do_sigreturn
+		 add		%o7, 1f-.-4, %o7
+		nop
+sys32_sigreturn:
+		add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+		call		do_sigreturn32
+		 add		%o7, 1f-.-4, %o7
+		nop
+sys_rt_sigreturn:
+		add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+		call		do_rt_sigreturn
+		 add		%o7, 1f-.-4, %o7
+		nop
+sys32_rt_sigreturn:
+		add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+		call		do_rt_sigreturn32
+		 add		%o7, 1f-.-4, %o7
+		nop
+sys_ptrace:	add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+		call		do_ptrace
+		 add		%o7, 1f-.-4, %o7
+		nop
 		.align		32
-sys_sigreturn:	call		do_sigreturn
-		 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
-		ldx		[%curptr + AOFF_task_flags], %l5
+1:		ldx		[%curptr + AOFF_task_flags], %l5
 		andcc		%l5, 0x20, %g0
 		be,pt		%icc, rtrap
 		 clr		%l6
@@ -591,19 +666,6 @@
 		ba,pt		%xcc, rtrap
 		 clr		%l6
 
-		.align		32
-sys_ptrace:	call		do_ptrace
-		 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
-		ldx		[%curptr + AOFF_task_flags], %l5
-		andcc		%l5, 0x20, %g0
-		be,pt		%icc, rtrap
-		 clr		%l6
-		call		syscall_trace
-		 nop
-
-		ba,pt		%xcc, rtrap
-		 clr		%l6
-	
 	/* This is how fork() was meant to be done, 12 instruction entry.
 	 *
 	 * I questioned the following code briefly, let me clear things
@@ -648,42 +710,94 @@
 		b,pt		%xcc, ret_sys_call
 		 ldx		[%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0
 
+linux_sparc_ni_syscall:
+	sethi		%hi(sys_ni_syscall), %l7
+	b,pt		%xcc, 4f
+	 or		%l7, %lo(sys_ni_syscall), %l7
+
+linux_syscall_trace32:
+	call		syscall_trace
+	 nop
+	srl		%i0, 0, %o0
+	mov		%i4, %o4
+	srl		%i1, 0, %o1
+	srl		%i2, 0, %o2
+	b,pt		%xcc, 2f
+	 srl		%i3, 0, %o3
+
 linux_syscall_trace:
-		call		syscall_trace
-		 nop
-		mov		%i0, %o0
-		mov		%i1, %o1
-		mov		%i2, %o2
-		mov		%i3, %o3
-		b,pt		%xcc, 2f
-		 mov		%i4, %o4
+	call		syscall_trace
+	 nop
+	mov		%i0, %o0
+	mov		%i1, %o1
+	mov		%i2, %o2
+	mov		%i3, %o3
+	b,pt		%xcc, 2f
+	 mov		%i4, %o4
+
+
+	/* Linux 32-bit and SunOS system calls enter here... */
+	.align	32
+	.globl	linux_sparc_syscall32
+linux_sparc_syscall32:
+	/* Direct access to user regs, must faster. */
+	cmp		%g1, NR_SYSCALLS			! IEU1	Group
+	bgeu,pn		%xcc, linux_sparc_ni_syscall		! CTI
+	 srl		%i0, 0, %o0				! IEU0
+	sll		%g1, 2, %l4				! IEU0	Group
+#ifdef SYSCALL_TRACING
+	add		%sp, STACK_BIAS + REGWIN_SZ, %o1
+	call		syscall_trace_entry
+	 mov		%g1, %o0
+	srl		%i0, 0, %o0
+#endif
+	mov		%i4, %o4				! IEU1
+	lduw		[%l7 + %l4], %l7			! Load
+	srl		%i1, 0, %o1				! IEU0	Group
+	ldx		[%curptr + AOFF_task_flags], %l0	! Load
+
+	mov		%i5, %o5				! IEU1
+	srl		%i2, 0, %o2				! IEU0	Group
+	mov		%i0, %l5				! IEU1
+	andcc		%l0, 0x20, %g0				! IEU1	Group
+	bne,pn		%icc, linux_syscall_trace32		! CTI
+	 srl		%i3, 0, %o3				! IEU0
+	call		%l7					! CTI	Group brk forced
+	 add		%o7, 3f-.-4, %o7			! IEU0
 
 	/* Linux native and SunOS system calls enter here... */
 	.align	32
-	.globl	linux_sparc_syscall, syscall_is_too_hard, ret_sys_call
+	.globl	linux_sparc_syscall, ret_sys_call
 linux_sparc_syscall:
 	/* Direct access to user regs, must faster. */
 	cmp		%g1, NR_SYSCALLS			! IEU1	Group
 	bgeu,pn		%xcc, linux_sparc_ni_syscall		! CTI
 	 mov		%i0, %o0				! IEU0
 	sll		%g1, 2, %l4				! IEU0	Group
+#ifdef SYSCALL_TRACING
+	add		%sp, STACK_BIAS + REGWIN_SZ, %o1
+	call		syscall_trace_entry
+	 mov		%g1, %o0
+	mov		%i0, %o0
+#endif
 	mov		%i1, %o1				! IEU1
 	lduw		[%l7 + %l4], %l7			! Load
-syscall_is_too_hard:
-	mov		%i2, %o2				! IEU0	Group
-	ldx		[%curptr + AOFF_task_flags], %l5	! Load
+4:	mov		%i2, %o2				! IEU0	Group
+	ldx		[%curptr + AOFF_task_flags], %l0	! Load
 
-	st		%g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS]
 	mov		%i3, %o3				! IEU1
 	mov		%i4, %o4				! IEU0	Group
-	andcc		%l5, 0x20, %g0				! IEU1	2 bubbles
+	andcc		%l0, 0x20, %g0				! IEU1	Group+1 bubble
 	bne,pn		%icc, linux_syscall_trace		! CTI	Group
 	 mov		%i0, %l5				! IEU0
 2:	call		%l7					! CTI	Group brk forced
 	 mov		%i5, %o5				! IEU0
-	stx		%o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0]
-
+3:	stx		%o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0]
 ret_sys_call:
+#ifdef SYSCALL_TRACING
+	call		syscall_trace_exit
+	 add		%sp, STACK_BIAS + REGWIN_SZ, %o1
+#endif
 	ldx		[%curptr + AOFF_task_flags], %l6
 	sra		%o0, 0, %o0
 	mov		%ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/etrap.S linux/arch/sparc64/kernel/etrap.S
--- v2.1.78/linux/arch/sparc64/kernel/etrap.S	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/etrap.S	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: etrap.S,v 1.37 1997/08/21 09:13:18 davem Exp $
+/* $Id: etrap.S,v 1.39 1997/10/24 11:57:47 jj Exp $
  * etrap.S: Preparing for entry into the kernel on Sparc V9.
  *
  * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -79,11 +79,11 @@
 		andcc			%l0, FPRS_FEF, %g0					! IEU1		Group
 		be,pn			%icc, 6f						! CTI
 		 st			%l0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS]	! Store
-		ld			[%g6 + AOFF_task_tss + AOFF_thread_flags], %l4		! Load		Group
+		lduh			[%g6 + AOFF_task_tss + AOFF_thread_flags], %l4		! Load		Group
 		stx			%fsr, [%sp + FPU_OFF + 0x100] 				! Single	Group
 		or			%l4, %l0, %l4						! IEU0		Group
 		ba,pt			%xcc, 3f						! CTI
-		 st			%l4, [%g6 + AOFF_task_tss + AOFF_thread_flags]		! Store
+		 sth			%l4, [%g6 + AOFF_task_tss + AOFF_thread_flags]		! Store
 2:		rd			%fprs, %l0						! Single	Group+4bubbles
 		andcc			%l0, FPRS_FEF, %g0					! IEU1		Group
 		be,pn			%icc, 6f						! CTI
@@ -107,7 +107,7 @@
 5:		membar			#Sync							! Memory	
 6:		wr			%g0, 0x0, %fprs						! Single	Group+4bubbles
 		wrpr			%g0, 0x0, %tl						! Single	Group+4bubbles
-		mov			%g1, %l1						! IEU0		Group
+		andn			%g1, PSTATE_MM, %l1					! IEU0		Group
 		mov			%g4, %l4						! IEU1
 		mov			%g5, %l5						! IEU0		Group
 		mov			%g7, %l2						! IEU1
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c
--- v2.1.78/linux/arch/sparc64/kernel/ioctl32.c	Sun Sep  7 13:10:42 1997
+++ linux/arch/sparc64/kernel/ioctl32.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.18 1997/09/06 02:25:13 davem Exp $
+/* $Id: ioctl32.c,v 1.26 1997/12/15 15:11:02 jj Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -19,11 +19,19 @@
 #include <linux/md.h>
 #include <linux/kd.h>
 #include <linux/route.h>
+#include <linux/skbuff.h>
 #include <linux/netlink.h>
 #include <linux/vt.h>
 #include <linux/fs.h>
 #include <linux/fd.h>
 #include <linux/if_ppp.h>
+#include <linux/mtio.h>
+
+#include <scsi/scsi.h>
+/* Ugly hack. */
+#undef __KERNEL__
+#include <scsi/scsi_ioctl.h>
+#define __KERNEL__
 
 #include <asm/types.h>
 #include <asm/uaccess.h>
@@ -44,7 +52,7 @@
 
 static int w_long(unsigned int fd, unsigned int cmd, u32 arg)
 {
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	int err;
 	unsigned long val;
 	
@@ -97,16 +105,24 @@
 	struct ifconf ifc;
 	struct ifreq32 *ifr32;
 	struct ifreq *ifr;
-	unsigned long old_fs;
+	mm_segment_t old_fs;
 	unsigned int i, j;
 	int err;
 
 	if (copy_from_user(&ifc32, (struct ifconf32 *)A(arg), sizeof(struct ifconf32)))
 		return -EFAULT;
 
-	ifc.ifc_len = ((ifc32.ifc_len / sizeof (struct ifreq32)) + 1) * sizeof (struct ifreq);
-	ifc.ifc_buf = kmalloc (ifc.ifc_len, GFP_KERNEL);
-	if (!ifc.ifc_buf) return -ENOMEM;
+	if(ifc32.ifcbuf == 0) {
+		ifc32.ifc_len = 0;
+		ifc.ifc_len = 0;
+		ifc.ifc_buf = NULL;
+	} else {
+		ifc.ifc_len = ((ifc32.ifc_len / sizeof (struct ifreq32)) + 1) *
+			sizeof (struct ifreq);
+		ifc.ifc_buf = kmalloc (ifc.ifc_len, GFP_KERNEL);
+		if (!ifc.ifc_buf)
+			return -ENOMEM;
+	}
 	ifr = ifc.ifc_req;
 	ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf);
 	for (i = 0; i < ifc32.ifc_len; i += sizeof (struct ifreq32)) {
@@ -137,14 +153,15 @@
 				err = -EFAULT;
 		}
 	}
-	kfree (ifc.ifc_buf);
+	if(ifc.ifc_buf != NULL)
+		kfree (ifc.ifc_buf);
 	return err;
 }
 
 static inline int dev_ifsioc(unsigned int fd, unsigned int cmd, u32 arg)
 {
 	struct ifreq ifr;
-	unsigned long old_fs;
+	mm_segment_t old_fs;
 	int err;
 	
 	switch (cmd) {
@@ -183,7 +200,7 @@
 		case SIOCGIFMTU:
 		case SIOCGIFMEM:
 		case SIOCGIFHWADDR:
-		case SIOGIFINDEX:
+		case SIOCGIFINDEX:
 		case SIOCGIFADDR:
 		case SIOCGIFBRDADDR:
 		case SIOCGIFDSTADDR:
@@ -250,16 +267,10 @@
 	char devname[16];
 	u32 rtdev;
 	int ret;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	
-	if (get_user (r.rt_pad1, &(((struct rtentry32 *)A(arg))->rt_pad1)) ||
-	    copy_from_user (&r.rt_dst, &(((struct rtentry32 *)A(arg))->rt_dst), 3 * sizeof(struct sockaddr)) ||
+	if (copy_from_user (&r.rt_dst, &(((struct rtentry32 *)A(arg))->rt_dst), 3 * sizeof(struct sockaddr)) ||
 	    __get_user (r.rt_flags, &(((struct rtentry32 *)A(arg))->rt_flags)) ||
-	    __get_user (r.rt_pad2, &(((struct rtentry32 *)A(arg))->rt_pad2)) ||
-	    __get_user (r.rt_pad3, &(((struct rtentry32 *)A(arg))->rt_pad3)) ||
-	    __get_user (r.rt_tos, &(((struct rtentry32 *)A(arg))->rt_tos)) ||
-	    __get_user (r.rt_class, &(((struct rtentry32 *)A(arg))->rt_class)) ||
-	    __get_user (r.rt_pad4, &(((struct rtentry32 *)A(arg))->rt_pad4)) ||
 	    __get_user (r.rt_metric, &(((struct rtentry32 *)A(arg))->rt_metric)) ||
 	    __get_user (r.rt_mtu, &(((struct rtentry32 *)A(arg))->rt_mtu)) ||
 	    __get_user (r.rt_window, &(((struct rtentry32 *)A(arg))->rt_window)) ||
@@ -277,116 +288,6 @@
 	return ret;
 }
 
-struct nlmsghdr32 {
-        u32   		nlmsg_len;      /* Length of message including header */
-        u32   		nlmsg_type;     /* Message type */
-        u32   		nlmsg_seq;      /* Sequence number */
-        u32   		nlmsg_pid;      /* Sending process PID */
-        unsigned char   nlmsg_data[0];
-};
-
-struct in_rtmsg32 {
-        struct in_addr  rtmsg_prefix;
-        struct in_addr  rtmsg_gateway;
-        unsigned        rtmsg_flags;
-        u32   		rtmsg_mtu;
-        u32   		rtmsg_window;
-        unsigned short  rtmsg_rtt;
-        short           rtmsg_metric;
-        unsigned char   rtmsg_tos;
-        unsigned char   rtmsg_class;
-        unsigned char   rtmsg_prefixlen;
-        unsigned char   rtmsg_reserved;
-        int             rtmsg_ifindex;
-};
-
-struct in_ifmsg32 {
-        struct sockaddr ifmsg_lladdr;
-        struct in_addr  ifmsg_prefix;
-        struct in_addr  ifmsg_brd;
-        unsigned        ifmsg_flags;
-        u32   		ifmsg_mtu;
-        short           ifmsg_metric;
-        unsigned char   ifmsg_prefixlen;
-        unsigned char   ifmsg_reserved;
-        int             ifmsg_index;
-        char            ifmsg_name[16];
-};
-
-static inline int rtmsg_ioctl(unsigned int fd, u32 arg)
-{
-	struct {
-		struct nlmsghdr n;
-		union {
-			struct in_rtmsg rt;
-			struct in_ifmsg iff;
-			struct in_rtctlmsg ctl;
-			struct in_rtrulemsg rule;
-		} u;
-	} nn;
-	char *p;
-	int ret;
-	unsigned long old_fs = get_fs();
-	
-	if (get_user (nn.n.nlmsg_len, &(((struct nlmsghdr32 *)A(arg))->nlmsg_len)) ||
-	    __get_user (nn.n.nlmsg_type, &(((struct nlmsghdr32 *)A(arg))->nlmsg_type)) ||
-	    __get_user (nn.n.nlmsg_seq, &(((struct nlmsghdr32 *)A(arg))->nlmsg_seq)) ||
-	    __get_user (nn.n.nlmsg_pid, &(((struct nlmsghdr32 *)A(arg))->nlmsg_pid)) ||
-	    __get_user (nn.n.nlmsg_data[0], &(((struct nlmsghdr32 *)A(arg))->nlmsg_data[0])))
-		return -EFAULT;
-	p = ((char *)(&nn.n)) + sizeof(struct nlmsghdr);
-	arg += sizeof(struct nlmsghdr32);
-	switch (nn.n.nlmsg_type) {
-	case RTMSG_NEWRULE:
-	case RTMSG_DELRULE:
-		if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_rtrulemsg)
-		    - sizeof(struct in_rtmsg) + sizeof(struct in_rtmsg32))
-			return -EINVAL;
-		if (copy_from_user (p, (struct in_rtrulemsg *)A(arg), sizeof(struct in_rtrulemsg) - sizeof(struct in_rtmsg)))
-			return -EFAULT;
-		nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_rtrulemsg);
-		p += sizeof (struct in_rtrulemsg) - sizeof(struct in_rtmsg);
-		arg += sizeof (struct in_rtrulemsg) - sizeof(struct in_rtmsg);
-		goto newroute;
-	case RTMSG_NEWROUTE:
-	case RTMSG_DELROUTE:
-		if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_rtmsg))
-			return -EINVAL;
-		nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_rtmsg);
-newroute:
-		if (copy_from_user (p, (struct in_rtmsg32 *)A(arg), 2*sizeof(struct in_addr) + sizeof(unsigned)) ||
-	    	    __get_user (((struct in_rtmsg *)p)->rtmsg_mtu, &((struct in_rtmsg32 *)A(arg))->rtmsg_mtu) ||
-	    	    __get_user (((struct in_rtmsg *)p)->rtmsg_window, &((struct in_rtmsg32 *)A(arg))->rtmsg_window) ||
-		    copy_from_user (&(((struct in_rtmsg *)p)->rtmsg_rtt), &((struct in_rtmsg32 *)A(arg))->rtmsg_rtt, 
-			2 * sizeof(short) + 4 + sizeof(int)))
-			return -EFAULT;
-		break;
-	case RTMSG_NEWDEVICE:
-	case RTMSG_DELDEVICE:
-		if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_ifmsg))
-			return -EINVAL;
-		nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_ifmsg);
-		if (copy_from_user (p, (struct in_ifmsg32 *)A(arg), 
-			sizeof(struct sockaddr) + 2*sizeof(struct in_addr) + sizeof(unsigned)) ||
-	    	    __get_user (((struct in_ifmsg *)p)->ifmsg_mtu, &((struct in_ifmsg32 *)A(arg))->ifmsg_mtu) ||
-		    copy_from_user (&(((struct in_ifmsg *)p)->ifmsg_metric), &((struct in_ifmsg32 *)A(arg))->ifmsg_metric, 
-			sizeof(short) + 2 + sizeof(int) + 16))
-			return -EFAULT;
-		break;
-	case RTMSG_CONTROL:
-		if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_rtctlmsg))
-			return -EINVAL;
-		nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_rtctlmsg);
-		if (copy_from_user (p, (struct in_rtctlmsg *)A(arg), sizeof(struct in_rtctlmsg)))
-			return -EFAULT;
-		break;
-	}
-	set_fs (KERNEL_DS);
-	ret = sys_ioctl (fd, SIOCRTMSG, (long)&(nn.n));
-	set_fs (old_fs);
-	return ret;
-}
-
 struct hd_geometry32 {
 	unsigned char heads;
 	unsigned char sectors;
@@ -396,7 +297,7 @@
                         
 static inline int hdio_getgeo(unsigned int fd, u32 arg)
 {
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	struct hd_geometry geo;
 	int err;
 	
@@ -428,7 +329,7 @@
 	int ret;
 	char red[256], green[256], blue[256];
 	u32 r, g, b;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	
 	if (get_user(f.index, &(((struct fbcmap32 *)A(arg))->index)) ||
 	    __get_user(f.count, &(((struct fbcmap32 *)A(arg))->count)) ||
@@ -480,7 +381,7 @@
 	char image[128], mask[128];
 	u32 r, g, b;
 	u32 m, i;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	
 	if (copy_from_user (&f, (struct fbcursor32 *)A(arg), 2 * sizeof (short) + 2 * sizeof(struct fbcurpos)) ||
 	    __get_user(f.size.fbx, &(((struct fbcursor32 *)A(arg))->size.fbx)) ||
@@ -516,7 +417,7 @@
 	
 static int hdio_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg)
 {
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	unsigned long kval;
 	unsigned int *uvp;
 	int error;
@@ -533,6 +434,328 @@
 	return error;
 }
 
+struct floppy_struct32 {
+	unsigned int	size;
+	unsigned int	sect;
+	unsigned int	head;
+	unsigned int	track;
+	unsigned int	stretch;
+	unsigned char	gap;
+	unsigned char	rate;
+	unsigned char	spec1;
+	unsigned char	fmt_gap;
+	const __kernel_caddr_t32 name;
+};
+
+struct floppy_drive_params32 {
+	char		cmos;
+	u32		max_dtr;
+	u32		hlt;
+	u32		hut;
+	u32		srt;
+	u32		spinup;
+	u32		spindown;
+	unsigned char	spindown_offset;
+	unsigned char	select_delay;
+	unsigned char	rps;
+	unsigned char	tracks;
+	u32		timeout;
+	unsigned char	interleave_sect;
+	struct floppy_max_errors max_errors;
+	char		flags;
+	char		read_track;
+	short		autodetect[8];
+	int		checkfreq;
+	int		native_format;
+};
+
+struct floppy_drive_struct32 {
+	signed char	flags;
+	u32		spinup_date;
+	u32		select_date;
+	u32		first_read_date;
+	short		probed_format;
+	short		track;
+	short		maxblock;
+	short		maxtrack;
+	int		generation;
+	int		keep_data;
+	int		fd_ref;
+	int		fd_device;
+	int		last_checked;
+	__kernel_caddr_t32 dmabuf;
+	int		bufblocks;
+};
+
+struct floppy_fdc_state32 {
+	int		spec1;
+	int		spec2;
+	int		dtr;
+	unsigned char	version;
+	unsigned char	dor;
+	u32		address;
+	unsigned int	rawcmd:2;
+	unsigned int	reset:1;
+	unsigned int	need_configure:1;
+	unsigned int	perp_mode:2;
+	unsigned int	has_fifo:1;
+	unsigned int	driver_version;
+	unsigned char	track[4];
+};
+
+struct floppy_write_errors32 {
+	unsigned int	write_errors;
+	u32		first_error_sector;
+	int		first_error_generation;
+	u32		last_error_sector;
+	int		last_error_generation;
+	unsigned int	badness;
+};
+
+#define FDSETPRM32 _IOW(2, 0x42, struct floppy_struct32) 
+#define FDDEFPRM32 _IOW(2, 0x43, struct floppy_struct32) 
+#define FDGETPRM32 _IOR(2, 0x04, struct floppy_struct32)
+#define FDSETDRVPRM32 _IOW(2, 0x90, struct floppy_drive_params32)
+#define FDGETDRVPRM32 _IOR(2, 0x11, struct floppy_drive_params32)
+#define FDGETDRVSTAT32 _IOR(2, 0x12, struct floppy_drive_struct32)
+#define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct floppy_drive_struct32)
+#define FDGETFDCSTAT32 _IOR(2, 0x15, struct floppy_fdc_state32)
+#define FDWERRORGET32  _IOR(2, 0x17, struct floppy_write_errors32)
+
+static struct {
+	unsigned int	cmd32;
+	unsigned int	cmd;
+} fd_ioctl_trans_table[] = {
+	{ FDSETPRM32, FDSETPRM },
+	{ FDDEFPRM32, FDDEFPRM },
+	{ FDGETPRM32, FDGETPRM },
+	{ FDSETDRVPRM32, FDSETDRVPRM },
+	{ FDGETDRVPRM32, FDGETDRVPRM },
+	{ FDGETDRVSTAT32, FDGETDRVSTAT },
+	{ FDPOLLDRVSTAT32, FDPOLLDRVSTAT },
+	{ FDGETFDCSTAT32, FDGETFDCSTAT },
+	{ FDWERRORGET32, FDWERRORGET }
+};
+
+#define NR_FD_IOCTL_TRANS (sizeof(fd_ioctl_trans_table)/sizeof(fd_ioctl_trans_table[0]))
+
+static int fd_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	mm_segment_t old_fs = get_fs();
+	void *karg;
+	unsigned int kcmd = 0;
+	int i, err;
+
+	for (i = 0; i < NR_FD_IOCTL_TRANS; i++)
+		if (cmd == fd_ioctl_trans_table[i].cmd32) {
+			kcmd = fd_ioctl_trans_table[i].cmd;
+			break;
+		}
+	if (!kcmd)
+		return -EINVAL;
+
+	switch (cmd) {
+		case FDSETPRM32:
+		case FDDEFPRM32:
+		case FDGETPRM32:
+		{
+			struct floppy_struct *f;
+
+			f = karg = kmalloc(GFP_KERNEL, sizeof(struct floppy_struct));
+			if (!karg)
+				return -ENOMEM;
+			if (cmd == FDGETPRM32)
+				break;
+			if (__get_user(f->size, &((struct floppy_struct32 *)A(arg))->size) ||
+			    __get_user(f->sect, &((struct floppy_struct32 *)A(arg))->sect) ||
+			    __get_user(f->head, &((struct floppy_struct32 *)A(arg))->head) ||
+			    __get_user(f->track, &((struct floppy_struct32 *)A(arg))->track) ||
+			    __get_user(f->stretch, &((struct floppy_struct32 *)A(arg))->stretch) ||
+			    __get_user(f->gap, &((struct floppy_struct32 *)A(arg))->gap) ||
+			    __get_user(f->rate, &((struct floppy_struct32 *)A(arg))->rate) ||
+			    __get_user(f->spec1, &((struct floppy_struct32 *)A(arg))->spec1) ||
+			    __get_user(f->fmt_gap, &((struct floppy_struct32 *)A(arg))->fmt_gap) ||
+			    __get_user((u64)f->name, &((struct floppy_struct32 *)A(arg))->name)) {
+				kfree(karg);
+				return -EFAULT;
+			}
+			break;
+		}
+		case FDSETDRVPRM32:
+		case FDGETDRVPRM32:
+		{
+			struct floppy_drive_params *f;
+
+			f = karg = kmalloc(GFP_KERNEL, sizeof(struct floppy_drive_params));
+			if (!karg)
+				return -ENOMEM;
+			if (cmd == FDGETDRVPRM32)
+				break;
+			if (__get_user(f->cmos, &((struct floppy_drive_params32 *)A(arg))->cmos) ||
+			    __get_user(f->max_dtr, &((struct floppy_drive_params32 *)A(arg))->max_dtr) ||
+			    __get_user(f->hlt, &((struct floppy_drive_params32 *)A(arg))->hlt) ||
+			    __get_user(f->hut, &((struct floppy_drive_params32 *)A(arg))->hut) ||
+			    __get_user(f->srt, &((struct floppy_drive_params32 *)A(arg))->srt) ||
+			    __get_user(f->spinup, &((struct floppy_drive_params32 *)A(arg))->spinup) ||
+			    __get_user(f->spindown, &((struct floppy_drive_params32 *)A(arg))->spindown) ||
+			    __get_user(f->spindown_offset, &((struct floppy_drive_params32 *)A(arg))->spindown_offset) ||
+			    __get_user(f->select_delay, &((struct floppy_drive_params32 *)A(arg))->select_delay) ||
+			    __get_user(f->rps, &((struct floppy_drive_params32 *)A(arg))->rps) ||
+			    __get_user(f->tracks, &((struct floppy_drive_params32 *)A(arg))->tracks) ||
+			    __get_user(f->timeout, &((struct floppy_drive_params32 *)A(arg))->timeout) ||
+			    __get_user(f->interleave_sect, &((struct floppy_drive_params32 *)A(arg))->interleave_sect) ||
+			    __copy_from_user(&f->max_errors, &((struct floppy_drive_params32 *)A(arg))->max_errors, sizeof(f->max_errors)) ||
+			    __get_user(f->flags, &((struct floppy_drive_params32 *)A(arg))->flags) ||
+			    __get_user(f->read_track, &((struct floppy_drive_params32 *)A(arg))->read_track) ||
+			    __copy_from_user(f->autodetect, ((struct floppy_drive_params32 *)A(arg))->autodetect, sizeof(f->autodetect)) ||
+			    __get_user(f->checkfreq, &((struct floppy_drive_params32 *)A(arg))->checkfreq) ||
+			    __get_user(f->native_format, &((struct floppy_drive_params32 *)A(arg))->native_format)) {
+				kfree(karg);
+				return -EFAULT;
+			}
+			break;
+		}
+		case FDGETDRVSTAT32:
+		case FDPOLLDRVSTAT32:
+			karg = kmalloc(GFP_KERNEL, sizeof(struct floppy_drive_struct));
+			if (!karg)
+				return -ENOMEM;
+			break;
+		case FDGETFDCSTAT32:
+			karg = kmalloc(GFP_KERNEL, sizeof(struct floppy_fdc_state));
+			if (!karg)
+				return -ENOMEM;
+			break;
+		case FDWERRORGET32:
+			karg = kmalloc(GFP_KERNEL, sizeof(struct floppy_write_errors));
+			if (!karg)
+				return -ENOMEM;
+			break;
+		default:
+			return -EINVAL;
+	}
+	set_fs (KERNEL_DS);
+	err = sys_ioctl (fd, kcmd, (unsigned long)karg);
+	set_fs (old_fs);
+	if (err) {
+		kfree(karg);
+		return err;
+	}
+	switch (cmd) {
+		case FDGETPRM32:
+		{
+			struct floppy_struct *f = karg;
+
+			if (__put_user(f->size, &((struct floppy_struct32 *)A(arg))->size) ||
+			    __put_user(f->sect, &((struct floppy_struct32 *)A(arg))->sect) ||
+			    __put_user(f->head, &((struct floppy_struct32 *)A(arg))->head) ||
+			    __put_user(f->track, &((struct floppy_struct32 *)A(arg))->track) ||
+			    __put_user(f->stretch, &((struct floppy_struct32 *)A(arg))->stretch) ||
+			    __put_user(f->gap, &((struct floppy_struct32 *)A(arg))->gap) ||
+			    __put_user(f->rate, &((struct floppy_struct32 *)A(arg))->rate) ||
+			    __put_user(f->spec1, &((struct floppy_struct32 *)A(arg))->spec1) ||
+			    __put_user(f->fmt_gap, &((struct floppy_struct32 *)A(arg))->fmt_gap) ||
+			    __put_user((u64)f->name, &((struct floppy_struct32 *)A(arg))->name)) {
+				kfree(karg);
+				return -EFAULT;
+			}
+			break;
+		}
+		case FDGETDRVPRM32:
+		{
+			struct floppy_drive_params *f = karg;
+
+			if (__put_user(f->cmos, &((struct floppy_drive_params32 *)A(arg))->cmos) ||
+			    __put_user(f->max_dtr, &((struct floppy_drive_params32 *)A(arg))->max_dtr) ||
+			    __put_user(f->hlt, &((struct floppy_drive_params32 *)A(arg))->hlt) ||
+			    __put_user(f->hut, &((struct floppy_drive_params32 *)A(arg))->hut) ||
+			    __put_user(f->srt, &((struct floppy_drive_params32 *)A(arg))->srt) ||
+			    __put_user(f->spinup, &((struct floppy_drive_params32 *)A(arg))->spinup) ||
+			    __put_user(f->spindown, &((struct floppy_drive_params32 *)A(arg))->spindown) ||
+			    __put_user(f->spindown_offset, &((struct floppy_drive_params32 *)A(arg))->spindown_offset) ||
+			    __put_user(f->select_delay, &((struct floppy_drive_params32 *)A(arg))->select_delay) ||
+			    __put_user(f->rps, &((struct floppy_drive_params32 *)A(arg))->rps) ||
+			    __put_user(f->tracks, &((struct floppy_drive_params32 *)A(arg))->tracks) ||
+			    __put_user(f->timeout, &((struct floppy_drive_params32 *)A(arg))->timeout) ||
+			    __put_user(f->interleave_sect, &((struct floppy_drive_params32 *)A(arg))->interleave_sect) ||
+			    __copy_to_user(&((struct floppy_drive_params32 *)A(arg))->max_errors, &f->max_errors, sizeof(f->max_errors)) ||
+			    __put_user(f->flags, &((struct floppy_drive_params32 *)A(arg))->flags) ||
+			    __put_user(f->read_track, &((struct floppy_drive_params32 *)A(arg))->read_track) ||
+			    __copy_to_user(((struct floppy_drive_params32 *)A(arg))->autodetect, f->autodetect, sizeof(f->autodetect)) ||
+			    __put_user(f->checkfreq, &((struct floppy_drive_params32 *)A(arg))->checkfreq) ||
+			    __put_user(f->native_format, &((struct floppy_drive_params32 *)A(arg))->native_format)) {
+				kfree(karg);
+				return -EFAULT;
+			}
+			break;
+		}
+		case FDGETDRVSTAT32:
+		case FDPOLLDRVSTAT32:
+		{
+			struct floppy_drive_struct *f = karg;
+
+			if (__put_user(f->flags, &((struct floppy_drive_struct32 *)A(arg))->flags) ||
+			    __put_user(f->spinup_date, &((struct floppy_drive_struct32 *)A(arg))->spinup_date) ||
+			    __put_user(f->select_date, &((struct floppy_drive_struct32 *)A(arg))->select_date) ||
+			    __put_user(f->first_read_date, &((struct floppy_drive_struct32 *)A(arg))->first_read_date) ||
+			    __put_user(f->probed_format, &((struct floppy_drive_struct32 *)A(arg))->probed_format) ||
+			    __put_user(f->track, &((struct floppy_drive_struct32 *)A(arg))->track) ||
+			    __put_user(f->maxblock, &((struct floppy_drive_struct32 *)A(arg))->maxblock) ||
+			    __put_user(f->maxtrack, &((struct floppy_drive_struct32 *)A(arg))->maxtrack) ||
+			    __put_user(f->generation, &((struct floppy_drive_struct32 *)A(arg))->generation) ||
+			    __put_user(f->keep_data, &((struct floppy_drive_struct32 *)A(arg))->keep_data) ||
+			    __put_user(f->fd_ref, &((struct floppy_drive_struct32 *)A(arg))->fd_ref) ||
+			    __put_user(f->fd_device, &((struct floppy_drive_struct32 *)A(arg))->fd_device) ||
+			    __put_user(f->last_checked, &((struct floppy_drive_struct32 *)A(arg))->last_checked) ||
+			    __put_user((u64)f->dmabuf, &((struct floppy_drive_struct32 *)A(arg))->dmabuf) ||
+			    __put_user((u64)f->bufblocks, &((struct floppy_drive_struct32 *)A(arg))->bufblocks)) {
+				kfree(karg);
+				return -EFAULT;
+			}
+			break;
+		}
+		case FDGETFDCSTAT32:
+		{
+			struct floppy_fdc_state *f = karg;
+
+			if (__put_user(f->spec1, &((struct floppy_fdc_state32 *)A(arg))->spec1) ||
+			    __put_user(f->spec2, &((struct floppy_fdc_state32 *)A(arg))->spec2) ||
+			    __put_user(f->dtr, &((struct floppy_fdc_state32 *)A(arg))->dtr) ||
+			    __put_user(f->version, &((struct floppy_fdc_state32 *)A(arg))->version) ||
+			    __put_user(f->dor, &((struct floppy_fdc_state32 *)A(arg))->dor) ||
+			    __put_user(f->address, &((struct floppy_fdc_state32 *)A(arg))->address) ||
+			    __copy_to_user((char *)&((struct floppy_fdc_state32 *)A(arg))->address
+			    		   + sizeof(((struct floppy_fdc_state32 *)A(arg))->address),
+					   (char *)&f->address + sizeof(f->address), sizeof(int)) ||
+			    __put_user(f->driver_version, &((struct floppy_fdc_state32 *)A(arg))->driver_version) ||
+			    __copy_to_user(((struct floppy_fdc_state32 *)A(arg))->track, f->track, sizeof(f->track))) {
+				kfree(karg);
+				return -EFAULT;
+			}
+			break;
+		}
+		case FDWERRORGET32:
+		{
+			struct floppy_write_errors *f = karg;
+
+			if (__put_user(f->write_errors, &((struct floppy_write_errors32 *)A(arg))->write_errors) ||
+			    __put_user(f->first_error_sector, &((struct floppy_write_errors32 *)A(arg))->first_error_sector) ||
+			    __put_user(f->first_error_generation, &((struct floppy_write_errors32 *)A(arg))->first_error_generation) ||
+			    __put_user(f->last_error_sector, &((struct floppy_write_errors32 *)A(arg))->last_error_sector) ||
+			    __put_user(f->last_error_generation, &((struct floppy_write_errors32 *)A(arg))->last_error_generation) ||
+			    __put_user(f->badness, &((struct floppy_write_errors32 *)A(arg))->badness)) {
+				kfree(karg);
+				return -EFAULT;
+			}
+			break;
+		}
+		default:
+			break;
+	}
+	kfree(karg);
+	return 0;
+}
+
 struct ppp_option_data32 {
 	__kernel_caddr_t32	ptr;
 	__u32			length;
@@ -540,14 +763,28 @@
 };
 #define PPPIOCSCOMPRESS32	_IOW('t', 77, struct ppp_option_data32)
 
-static int ppp_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
+struct ppp_idle32 {
+	__kernel_time_t32 xmit_idle;
+	__kernel_time_t32 recv_idle;
+};
+#define PPPIOCGIDLE32		_IOR('t', 63, struct ppp_idle32)
+
+static int ppp_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg)
 {
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	struct ppp_option_data32 data32;
 	struct ppp_option_data data;
-	int err;
+	struct ppp_idle32 idle32;
+	struct ppp_idle idle;
+	unsigned int kcmd;
+	void *karg;
+	int err = 0;
 
 	switch (cmd) {
+	case PPPIOCGIDLE32:
+		kcmd = PPPIOCGIDLE;
+		karg = &idle;
+		break;
 	case PPPIOCSCOMPRESS32:
 		if (copy_from_user(&data32, (struct ppp_option_data32 *)A(arg), sizeof(struct ppp_option_data32)))
 			return -EFAULT;
@@ -555,33 +792,163 @@
 		if (!data.ptr)
 			return -ENOMEM;
 		if (copy_from_user(data.ptr, (__u8 *)A(data32.ptr), data32.length)) {
-			err = -EFAULT;
-			goto out;
+			kfree(data.ptr);
+			return -EFAULT;
 		}
 		data.length = data32.length;
 		data.transmit = data32.transmit;
+		kcmd = PPPIOCSCOMPRESS;
+		karg = &data;
 		break;
 	default:
 		printk("ppp_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n",
 		       (int)fd, (unsigned int)cmd, (unsigned int)arg);
 		return -EINVAL;
 	}
-	old_fs = get_fs();
 	set_fs (KERNEL_DS);
-	err = sys_ioctl (fd, cmd, (unsigned long)&data);
+	err = sys_ioctl (fd, kcmd, (unsigned long)karg);
 	set_fs (old_fs);
-	if (err)
-		goto out;
 	switch (cmd) {
+	case PPPIOCGIDLE32:
+		if (err)
+			return err;
+		idle32.xmit_idle = idle.xmit_idle;
+		idle32.recv_idle = idle.recv_idle;
+		if (copy_to_user((struct ppp_idle32 *)A(arg), &idle32, sizeof(struct ppp_idle32)))
+			return -EFAULT;
+		break;
 	case PPPIOCSCOMPRESS32:
+		kfree(data.ptr);
+		break;
 	default:
 		break;
 	}
-out:
-	kfree(data.ptr);
 	return err;
 }
 
+
+struct mtget32 {
+	__u32	mt_type;
+	__u32	mt_resid;
+	__u32	mt_dsreg;
+	__u32	mt_gstat;
+	__u32	mt_erreg;
+	__kernel_daddr_t32	mt_fileno;
+	__kernel_daddr_t32	mt_blkno;
+};
+#define MTIOCGET32	_IOR('m', 2, struct mtget32)
+
+struct mtpos32 {
+	__u32	mt_blkno;
+};
+#define MTIOCPOS32	_IOR('m', 3, struct mtpos32)
+
+struct mtconfiginfo32 {
+	__u32	mt_type;
+	__u32	ifc_type;
+	__u16	irqnr;
+	__u16	dmanr;
+	__u16	port;
+	__u32	debug;
+	__u32	have_dens:1;
+	__u32	have_bsf:1;
+	__u32	have_fsr:1;
+	__u32	have_bsr:1;
+	__u32	have_eod:1;
+	__u32	have_seek:1;
+	__u32	have_tell:1;
+	__u32	have_ras1:1;
+	__u32	have_ras2:1;
+	__u32	have_ras3:1;
+	__u32	have_qfa:1;
+	__u32	pad1:5;
+	char	reserved[10];
+};
+#define	MTIOCGETCONFIG32	_IOR('m', 4, struct mtconfiginfo32)
+#define	MTIOCSETCONFIG32	_IOW('m', 5, struct mtconfiginfo32)
+
+static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	mm_segment_t old_fs = get_fs();
+	struct mtconfiginfo info;
+	struct mtget get;
+	struct mtpos pos;
+	unsigned long kcmd;
+	void *karg;
+	int err = 0;
+
+	switch(cmd) {
+	case MTIOCPOS32:
+		kcmd = MTIOCPOS;
+		karg = &pos;
+		break;
+	case MTIOCGET32:
+		kcmd = MTIOCGET;
+		karg = &get;
+		break;
+	case MTIOCGETCONFIG32:
+		kcmd = MTIOCGETCONFIG;
+		karg = &info;
+		break;
+	case MTIOCSETCONFIG32:
+		kcmd = MTIOCSETCONFIG;
+		karg = &info;
+		if (__get_user(info.mt_type, &((struct mtconfiginfo32 *)A(arg))->mt_type) ||
+		    __get_user(info.ifc_type, &((struct mtconfiginfo32 *)A(arg))->ifc_type) ||
+		    __get_user(info.irqnr, &((struct mtconfiginfo32 *)A(arg))->irqnr) ||
+		    __get_user(info.dmanr, &((struct mtconfiginfo32 *)A(arg))->dmanr) ||
+		    __get_user(info.port, &((struct mtconfiginfo32 *)A(arg))->port) ||
+		    __get_user(info.debug, &((struct mtconfiginfo32 *)A(arg))->debug) ||
+		    __copy_from_user((char *)&info.debug + sizeof(info.debug),
+				     (char *)&((struct mtconfiginfo32 *)A(arg))->debug
+			    		     + sizeof(((struct mtconfiginfo32 *)A(arg))->debug),
+				     sizeof(__u32)))
+			return -EFAULT;
+		break;
+	default:
+		printk("mt_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n",
+		       (int)fd, (unsigned int)cmd, (unsigned int)arg);
+		return -EINVAL;
+	}
+	set_fs (KERNEL_DS);
+	err = sys_ioctl (fd, kcmd, (unsigned long)karg);
+	set_fs (old_fs);
+	if (err)
+		return err;
+	switch (cmd) {
+	case MTIOCPOS32:
+		if (__put_user(pos.mt_blkno, &((struct mtpos32 *)A(arg))->mt_blkno))
+			return -EFAULT;
+		break;
+	case MTIOCGET32:
+		if (__put_user(get.mt_type, &((struct mtget32 *)A(arg))->mt_type) ||
+		    __put_user(get.mt_resid, &((struct mtget32 *)A(arg))->mt_resid) ||
+		    __put_user(get.mt_dsreg, &((struct mtget32 *)A(arg))->mt_dsreg) ||
+		    __put_user(get.mt_gstat, &((struct mtget32 *)A(arg))->mt_gstat) ||
+		    __put_user(get.mt_erreg, &((struct mtget32 *)A(arg))->mt_erreg) ||
+		    __put_user(get.mt_fileno, &((struct mtget32 *)A(arg))->mt_fileno) ||
+		    __put_user(get.mt_blkno, &((struct mtget32 *)A(arg))->mt_blkno))
+			return -EFAULT;
+		break;
+	case MTIOCGETCONFIG32:
+		if (__put_user(info.mt_type, &((struct mtconfiginfo32 *)A(arg))->mt_type) ||
+		    __put_user(info.ifc_type, &((struct mtconfiginfo32 *)A(arg))->ifc_type) ||
+		    __put_user(info.irqnr, &((struct mtconfiginfo32 *)A(arg))->irqnr) ||
+		    __put_user(info.dmanr, &((struct mtconfiginfo32 *)A(arg))->dmanr) ||
+		    __put_user(info.port, &((struct mtconfiginfo32 *)A(arg))->port) ||
+		    __put_user(info.debug, &((struct mtconfiginfo32 *)A(arg))->debug) ||
+		    __copy_to_user((char *)&((struct mtconfiginfo32 *)A(arg))->debug
+			    		   + sizeof(((struct mtconfiginfo32 *)A(arg))->debug),
+					   (char *)&info.debug + sizeof(info.debug), sizeof(__u32)))
+			return -EFAULT;
+		break;
+	case MTIOCSETCONFIG32:
+		break;
+	}
+	return 0;
+}
+
+
 asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
 {
 	struct file * filp;
@@ -617,7 +984,7 @@
 	case SIOCSIFHWADDR:
 	case SIOCADDMULTI:
 	case SIOCDELMULTI:
-	case SIOGIFINDEX:
+	case SIOCGIFINDEX:
 	case SIOCGIFMAP:
 	case SIOCSIFMAP:
 	case SIOCGIFADDR:
@@ -628,6 +995,8 @@
 	case SIOCSIFDSTADDR:
 	case SIOCGIFNETMASK:
 	case SIOCSIFNETMASK:
+	case SIOCSIFPFLAGS:
+	case SIOCGIFPFLAGS:
 	case SIOCGPPPSTATS:
 	case SIOCGPPPCSTATS:
 	case SIOCGPPPVER:
@@ -639,10 +1008,12 @@
 		error = routing_ioctl(fd, cmd, arg);
 		goto out;
 
-	case SIOCRTMSG:
-		error = rtmsg_ioctl(fd, arg);
+	case SIOCRTMSG: /* Note SIOCRTMSG is no longer, so this is safe and
+			 * the user would have seen just an -EINVAL anyways.
+			 */
+		error = -EINVAL;
 		goto out;
-		
+
 	case HDIO_GETGEO:
 		error = hdio_getgeo(fd, arg);
 		goto out;
@@ -671,6 +1042,30 @@
 		error = hdio_ioctl_trans(fd, cmd, arg);
 		goto out;
 
+	case FDSETPRM32:
+	case FDDEFPRM32:
+	case FDGETPRM32:
+	case FDSETDRVPRM32:
+	case FDGETDRVPRM32:
+	case FDGETDRVSTAT32:
+	case FDPOLLDRVSTAT32:
+	case FDGETFDCSTAT32:
+	case FDWERRORGET32:
+		error = fd_ioctl_trans(fd, cmd, (unsigned long)arg);
+		goto out;
+
+	case PPPIOCGIDLE32:
+	case PPPIOCSCOMPRESS32:
+		error = ppp_ioctl_trans(fd, cmd, arg);
+		goto out;
+
+	case MTIOCGET32:
+	case MTIOCPOS32:
+	case MTIOCGETCONFIG32:
+	case MTIOCSETCONFIG32:
+		error = mt_ioctl_trans(fd, cmd, arg);
+		goto out;
+
 	/* List here exlicitly which ioctl's are known to have
 	 * compatable types passed or none at all...
 	 */
@@ -751,13 +1146,22 @@
 	case BLKROGET:
 
 	/* 0x02 -- Floppy ioctls */
+	case FDMSGON:
+	case FDMSGOFF:
 	case FDSETEMSGTRESH:
 	case FDFLUSH:
+	case FDWERRORCLR:
 	case FDSETMAXERRS:
 	case FDGETMAXERRS:
 	case FDGETDRVTYP:
 	case FDEJECT:
-	/* XXX The rest need struct floppy_* translations. */
+	case FDCLRPRM:
+	case FDFMTBEG:
+	case FDFMTEND:
+	case FDRESET:
+	case FDTWADDLE:
+	case FDFMTTRK:
+	case FDRAWCMD:
 
 	/* 0x12 */
 	case BLKRRPART:
@@ -807,6 +1211,15 @@
 	case KIOCSRATE:
 	case KIOCGRATE:
 	
+	/* Big S */
+	case SCSI_IOCTL_GET_IDLUN:
+	case SCSI_IOCTL_DOORLOCK:
+	case SCSI_IOCTL_DOORUNLOCK:
+	case SCSI_IOCTL_TEST_UNIT_READY:
+	case SCSI_IOCTL_TAGGED_ENABLE:
+	case SCSI_IOCTL_TAGGED_DISABLE:
+	case SCSI_IOCTL_GET_BUS_NUMBER:
+	
 	/* Big V */
 	case VT_SETMODE:
 	case VT_GETMODE:
@@ -829,6 +1242,9 @@
 	case RTCGET:
 	case RTCSET:
 
+	/* Little m */
+	case MTIOCTOP:
+
 	/* OPENPROMIO, SunOS/Solaris only, the NetBSD one's have
 	 * embedded pointers in the arg which we'd need to clean up...
 	 */
@@ -860,9 +1276,11 @@
 	case SIOCSARP:
 	case SIOCGARP:
 	case SIOCDARP:
+#if 0 /* XXX No longer exist in new routing code. XXX */
 	case OLD_SIOCSARP:
 	case OLD_SIOCGARP:
 	case OLD_SIOCDARP:
+#endif
 	case SIOCSRARP:
 	case SIOCGRARP:
 	case SIOCDRARP:
@@ -887,12 +1305,7 @@
 	case PPPIOCSNPMODE:
 	case PPPIOCGDEBUG:
 	case PPPIOCSDEBUG:
-	case PPPIOCGIDLE:
 		error = sys_ioctl (fd, cmd, (unsigned long)arg);
-		goto out;
-
-	case PPPIOCSCOMPRESS32:
-		error = ppp_ioctl (fd, cmd, (unsigned long)arg);
 		goto out;
 
 	default:
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/irq.c linux/arch/sparc64/kernel/irq.c
--- v2.1.78/linux/arch/sparc64/kernel/irq.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/irq.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: irq.c,v 1.39 1997/08/31 03:11:18 davem Exp $
+/* $Id: irq.c,v 1.47 1998/01/10 18:26:17 ecd Exp $
  * irq.c: UltraSparc IRQ handling/init/registry.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -57,6 +57,7 @@
 	unsigned int ino;
 	unsigned int *imap;
 	unsigned int *iclr;
+	unsigned char *imap_refcnt;
 };
 
 #define INO_HASHSZ	(NUM_HARD_IVECS >> 2)
@@ -114,9 +115,15 @@
 	}
 #if 0
 #ifdef CONFIG_PCI
-	len += sprintf(buf + len, "ISTAT: PCI[%016lx] OBIO[%016lx]\n",
-		       psycho_root->psycho_regs->pci_istate,
-		       psycho_root->psycho_regs->obio_istate);
+	{
+		struct linux_psycho *p;
+		for (p = psycho_root; p; p = p->next)
+			len += sprintf(buf + len,
+				       "ISTAT[%d]: PCI[%016lx] OBIO[%016lx]\n",
+				       p->index,
+				       p->psycho_regs->pci_istate,
+				       p->psycho_regs->obio_istate);
+	}
 #endif
 #endif
 	return len;
@@ -229,10 +236,10 @@
 	13, /* Audio Record */
 	14, /* Audio Playback */
 	15, /* PowerFail */
-	12, /* Keyboard/Mouse/Serial */
+	9,  /* Keyboard/Mouse/Serial */
 	11, /* Floppy */
 	2,  /* Spare Hardware */
-	12, /* Keyboard */
+	9,  /* Keyboard */
 	4,  /* Mouse */
 	12, /* Serial */
 	10, /* Timer 0 */
@@ -411,7 +418,7 @@
 		offset += ((unsigned long)pregs);
 		*imap = ((unsigned int *)offset) + 1;
 		*iclr = (unsigned int *)
-			(((unsigned long)pregs) + psycho_imap_offset(irq));
+			(((unsigned long)pregs) + psycho_iclr_offset(irq));
 		return;
 	}
 #endif
@@ -449,10 +456,17 @@
 			       unsigned int **imap, unsigned int **iclr,
 			       unsigned int irq)
 {
-	struct linux_psycho *psycho = psycho_root;
-	struct psycho_regs *pregs = psycho->psycho_regs;
+	struct linux_psycho *psycho;
+	struct psycho_regs *pregs;
 	unsigned long addr, imoff;
 
+	psycho = psycho_by_index((irq & PCI_IRQ_BUSNO) >> PCI_IRQ_BUSNO_SHFT);
+	if (!psycho) {
+		printk("get_irq_translations: BAD PSYCHO BUSNO[%x]\n", irq);
+		panic("Bad PSYCHO IRQ frobnication...");
+	}
+	pregs = psycho->psycho_regs;
+
 	addr = (unsigned long) &pregs->imap_a_slot0;
 	imoff = (irq & PCI_IRQ_IMAP_OFF) >> PCI_IRQ_IMAP_OFF_SHFT;
 	addr = addr + imoff;
@@ -515,7 +529,7 @@
 	unsigned long flags;
 	unsigned int *imap, *iclr;
 	void *bus_id = NULL;
-	int ivindex, ivindex_fixup, cpu_irq = -1;
+	int ivindex, ivindex_fixup, cpu_irq = -1, pending;
 	
 	if(!handler)
 	    return -EINVAL;
@@ -605,7 +619,10 @@
 		return -ENOMEM;
 	}
 
+	pending = ((ivector_to_mask[ivindex] & 0x80000000) != 0);
 	ivector_to_mask[ivindex] = (1 << cpu_irq);
+	if(pending)
+		ivector_to_mask[ivindex] |= 0x80000000;
 
 	if(dcookie) {
 		dcookie->ret_ino = ivindex;
@@ -625,6 +642,11 @@
 		*(cpu_irq + irq_action) = action;
 
 	enable_irq(ivindex);
+
+	/* We ate the IVEC already, this makes sure it does not get lost. */
+	if(pending)
+		set_softint(1 << cpu_irq);
+
 	restore_flags(flags);
 #ifdef __SMP__
 	if(irqs_have_been_distributed)
@@ -638,6 +660,7 @@
 	struct irqaction *action;
 	struct irqaction *tmp = NULL;
 	unsigned long flags;
+	unsigned int *imap = NULL;
 	unsigned int cpu_irq;
 	int ivindex = -1;
 
@@ -685,8 +708,8 @@
 
 	if(action->flags & SA_IMAP_MASKED) {
 		struct ino_bucket *bucket = (struct ino_bucket *)action->mask;
-		unsigned int *imap = bucket->imap;
 
+		imap = bucket->imap;
 		if(imap != NULL) {
 			ivindex = bucket->ino;
 			ivector_to_mask[ivindex] = 0;
@@ -696,8 +719,27 @@
 	}
 
 	kfree(action);
-	if(ivindex != -1)
-		disable_irq(ivindex);
+
+	if(ivindex != -1) {
+		struct ino_bucket *bp;
+		int i, count = 0;
+
+		/* The trick is that we can't turn the thing off when there
+		 * are potentially other sub-irq level references.
+		 */
+		if(imap != NULL) {
+			for(i = 0; i < INO_HASHSZ; i++) {
+				bp = ino_hash[i];
+				while(bp) {
+					if(bp->imap == imap)
+						count++;
+					bp = bp->next;
+				}
+			}
+		}
+		if(count < 2)
+			disable_irq(ivindex);
+	}
 
 	restore_flags(flags);
 }
@@ -863,12 +905,21 @@
 void report_spurious_ivec(struct pt_regs *regs)
 {
 	extern unsigned long ivec_spurious_cookie;
-	static int times = 0;
 
+#if 0
 	printk("IVEC: Spurious interrupt vector (%016lx) received at (%016lx)\n",
 	       ivec_spurious_cookie, regs->tpc);
-	if(times++ > 1)
-		prom_halt();
+#endif
+
+	/* We can actually see this on Ultra/PCI PCI cards, which are bridges
+	 * to other devices.  Here a single IMAP enabled potentially multiple
+	 * unique interrupt sources (which each do have a unique ICLR register.
+	 *
+	 * So what we do is just register that the IVEC arrived, when registered
+	 * for real the request_irq() code will check the high bit and signal
+	 * a local CPU interrupt for it.
+	 */
+	ivector_to_mask[ivec_spurious_cookie] |= (0x80000000);
 }
 
 void unexpected_irq(int irq, void *dev_cookie, struct pt_regs *regs)
@@ -899,6 +950,7 @@
 
 void handler_irq(int irq, struct pt_regs *regs)
 {
+	struct ino_bucket *bucket = NULL;
 	struct irqaction *action;
 	int cpu = smp_processor_id();
 
@@ -911,20 +963,19 @@
 		unexpected_irq(irq, 0, regs);
 	} else {
 		do {
-			struct ino_bucket *bucket = NULL;
-			unsigned int ino = 0;
+			unsigned long *swmask = NULL;
 
 			if(action->flags & SA_IMAP_MASKED) {
 				bucket = (struct ino_bucket *)action->mask;
 
-				ino = bucket->ino;
-				if(!(ivector_to_mask[ino] & 0x80000000))
+				swmask = &ivector_to_mask[bucket->ino];
+				if(!(*swmask & 0x80000000))
 					continue;
 			}
 
 			action->handler(irq, action->dev_id, regs);
-			if(bucket) {
-				ivector_to_mask[ino] &= ~(0x80000000);
+			if(swmask) {
+				*swmask &= ~(0x80000000);
 				*(bucket->iclr) = SYSIO_ICLR_IDLE;
 			}
 		} while((action = action->next) != NULL);
@@ -938,12 +989,14 @@
 void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs)
 {
 	struct irqaction *action = *(irq + irq_action);
+	struct ino_bucket *bucket;
 	int cpu = smp_processor_id();
 
 	irq_enter(cpu, irq);
+	bucket = (struct ino_bucket *)action->mask;
 	floppy_interrupt(irq, dev_cookie, regs);
-	if(action->flags & SA_IMAP_MASKED)
-		*((unsigned int *)action->mask) = SYSIO_ICLR_IDLE;
+	ivector_to_mask[bucket->ino] &= ~(0x80000000);
+	*(bucket->iclr) = SYSIO_ICLR_IDLE;
 	irq_exit(cpu, irq);
 }
 #endif
@@ -975,28 +1028,60 @@
 
 int request_fast_irq(unsigned int irq,
 		     void (*handler)(int, void *, struct pt_regs *),
-		     unsigned long irqflags, const char *name)
+		     unsigned long irqflags, const char *name, void *dev_id)
 {
 	struct irqaction *action;
+	struct devid_cookie *dcookie = NULL;
+	struct ino_bucket *bucket = NULL;
 	unsigned long flags;
-	unsigned int cpu_irq, *imap, *iclr;
-	int ivindex = -1;
-
-	/* XXX This really is not the way to do it, the "right way"
-	 * XXX is to have drivers set SA_SBUS or something like that
-	 * XXX in irqflags and we base our decision here on whether
-	 * XXX that flag bit is set or not.
-	 *
-	 * In this case nobody can have a fast interrupt at the level
-	 * where TICK interrupts live.
-	 */
-	if(irq == 14)
-		return -EINVAL;
-	cpu_irq = sysio_ino_to_pil[irq];
+	unsigned int *imap, *iclr;
+	void *bus_id = NULL;
+	int ivindex, ivindex_fixup, cpu_irq = -1;
 
 	if(!handler)
 		return -EINVAL;
-	imap = sysio_irq_to_imap(irq);
+
+	imap = iclr = NULL;
+	ivindex_fixup = 0;
+#ifdef CONFIG_PCI
+	if(PCI_IRQ_P(irq)) {
+		pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq);
+	} else
+#endif
+	if(irqflags & SA_DCOOKIE) {
+		if(!dev_id) {
+			printk("request_fast_irq: SA_DCOOKIE but dev_id is NULL!\n");
+			panic("Bogus irq registry.");
+		}
+		dcookie		= dev_id;
+		dev_id		= dcookie->real_dev_id;
+		cpu_irq		= dcookie->pil;
+		imap		= dcookie->imap;
+		iclr		= dcookie->iclr;
+		bus_id		= dcookie->bus_cookie;
+		get_irq_translations(&cpu_irq, &ivindex_fixup, &imap,
+				     &iclr, bus_id, irqflags, irq);
+	} else {
+		/* XXX NOTE: This code is maintained for compatability until I can
+		 * XXX       verify that all drivers sparc64 will use are updated
+		 * XXX       to use the new IRQ registry dcookie interface.  -DaveM
+		 */
+		if(irq == 14)
+			cpu_irq = irq;
+		else
+			cpu_irq = sysio_ino_to_pil[irq];
+		imap = sysio_irq_to_imap(irq);
+		if(!imap) {
+			printk("request_irq: BAD, null imap for old style "
+			       "irq registry IRQ[%x].\n", irq);
+			panic("Bad IRQ registery...");
+		}
+		iclr = sysio_imap_to_iclr(imap);
+	}
+
+	ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO));
+	ivindex += ivindex_fixup;
+
 	action = *(cpu_irq + irq_action);
 	if(action) {
 		if(action->flags & SA_SHIRQ)
@@ -1023,28 +1108,35 @@
 	}
 	install_fast_irq(cpu_irq, handler);
 
-	if(imap) {
-		ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO));
-		ivector_to_mask[ivindex] = (1 << cpu_irq);
-		iclr = sysio_imap_to_iclr(imap);
-		action->mask = (unsigned long) iclr;
-		irqflags |= SA_IMAP_MASKED;
-		add_ino_hash(ivindex, imap, iclr, irqflags);
-	} else
-		action->mask = 0;
+	bucket = add_ino_hash(ivindex, imap, iclr, irqflags);
+	if(!bucket) {
+		kfree(action);
+		restore_flags(flags);
+		return -ENOMEM;
+	}
+
+	ivector_to_mask[ivindex] = (1 << cpu_irq);
 
+	if(dcookie) {
+		dcookie->ret_ino = ivindex;
+		dcookie->ret_pil = cpu_irq;
+	}
+
+	action->mask = (unsigned long) bucket;
 	action->handler = handler;
-	action->flags = irqflags;
+	action->flags = irqflags | SA_IMAP_MASKED;
 	action->dev_id = NULL;
 	action->name = name;
 	action->next = NULL;
 
 	*(cpu_irq + irq_action) = action;
-
-	if(ivindex != -1)
-		enable_irq(ivindex);
+	enable_irq(ivindex);
 
 	restore_flags(flags);
+#ifdef __SMP__
+	if(irqs_have_been_distributed)
+		distribute_irqs();
+#endif
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/itlb_miss.S linux/arch/sparc64/kernel/itlb_miss.S
--- v2.1.78/linux/arch/sparc64/kernel/itlb_miss.S	Mon Apr 14 16:28:09 1997
+++ linux/arch/sparc64/kernel/itlb_miss.S	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: itlb_miss.S,v 1.10 1997/03/26 12:24:18 davem Exp $
+/* $Id: itlb_miss.S,v 1.11 1997/10/14 01:48:25 davem Exp $
  * itlb_miss.S:	Instruction TLB miss code, this is included directly
  *              into the trap table.
  *
@@ -23,10 +23,10 @@
   /*0x24*/	srlx		%g1, 1, %g1				! PTE offset
   /*0x28*/	ldxa		[%g5 + %g4] ASI_PHYS_USE_EC, %g3	! Load PMD
 2:/*0x2c*/	ldxa		[%g3 + %g1] ASI_PHYS_USE_EC, %g5	! Load PTE
-  /*0x30*/	brlz,a,pt	%g5, 1f					! Valid set?
-  /*0x34*/	 stxa		%g5, [%g0] ASI_ITLB_DATA_IN		! TLB load
-  /*0x38*/	ba,a,pt		%xcc, sparc64_itlb_refbit_catch		! Nope...
-1:/*0x3c*/	retry							! Trap return
+  /*0x30*/	brgez,pn	%g5, sparc64_itlb_refbit_catch		! Valid set?
+  /*0x34*/	 nop							! delay
+  /*0x38*/	stxa		%g5, [%g0] ASI_ITLB_DATA_IN		! TLB load
+  /*0x3c*/	retry							! Trap return
 
 3:				/* ICACHE line 3 */
   /*0x40*/	ldxa		[%g6 + %g3] ASI_PHYS_USE_EC, %g5	! Load kern PGD
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/process.c linux/arch/sparc64/kernel/process.c
--- v2.1.78/linux/arch/sparc64/kernel/process.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/process.c	Mon Jan 12 15:15:44 1998
@@ -1,9 +1,9 @@
-/*  $Id: process.c,v 1.42 1997/08/19 14:17:55 jj Exp $
+/*  $Id: process.c,v 1.50 1998/01/09 16:39:33 jj Exp $
  *  arch/sparc64/kernel/process.c
  *
  *  Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu)
  *  Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
- *  Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *  Copyright (C) 1997, 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 /*
@@ -39,6 +39,8 @@
 #include <asm/elf.h>
 #include <asm/fpumacro.h>
 
+/* #define VERBOSE_SHOWREGS */
+
 #define PGTCACHE_HIGH_WATER		50
 #define PGTCACHE_LOW_WATER		25
 
@@ -98,7 +100,7 @@
 		}
 		barrier();
 		current->counter = -100;
-		if(resched_needed())
+		if(need_resched)
 			schedule();
 		barrier();
 	}
@@ -166,7 +168,7 @@
 {
 	struct reg_window32 *rw;
 	struct reg_window32 r_w;
-	unsigned long old_fs;
+	mm_segment_t old_fs;
 	
 	__asm__ __volatile__ ("flushw");
 	rw = (struct reg_window32 *)((long)(unsigned)regs->u_regs[14]);
@@ -192,7 +194,7 @@
 {
 	struct reg_window *rw;
 	struct reg_window r_w;
-	unsigned long old_fs;
+	mm_segment_t old_fs;
 
 	if ((regs->tstate & TSTATE_PRIV) || !(current->tss.flags & SPARC_FLAG_32BIT)) {
 		__asm__ __volatile__ ("flushw");
@@ -311,8 +313,30 @@
 #endif
 }
 
+#ifdef VERBOSE_SHOWREGS
+static void idump_from_user (unsigned int *pc)
+{
+        int i;
+        int code;
+        
+        if((((unsigned long) pc) & 3))
+        	return;
+        
+        pc -= 3;
+        for(i = -3; i < 6; i++) {
+        	get_user(code, pc);
+                printk("%c%08x%c",i?' ':'<',code,i?' ':'>');
+                pc++;
+        }
+        printk("\n");
+}
+#endif
+                                                                		
 void show_regs(struct pt_regs *regs)
 {
+#ifdef VERBOSE_SHOWREGS
+	extern long etrap, etraptl1;
+#endif
 	__show_regs(regs);
 #ifdef __SMP__
 	{
@@ -321,6 +345,17 @@
 		smp_report_regs();
 	}
 #endif
+
+#ifdef VERBOSE_SHOWREGS	
+	if (regs->tpc >= &etrap && regs->tpc < &etraptl1 &&
+	    regs->u_regs[14] >= (long)current - PAGE_SIZE &&
+	    regs->u_regs[14] < (long)current + 6 * PAGE_SIZE) {
+		printk ("*********parent**********\n");
+		__show_regs((struct pt_regs *)(regs->u_regs[14] + STACK_BIAS + REGWIN_SZ));
+		idump_from_user(((struct pt_regs *)(regs->u_regs[14] + STACK_BIAS + REGWIN_SZ))->tpc);
+		printk ("*********endpar**********\n");
+	}
+#endif
 }
 
 void show_regs32(struct pt_regs32 *regs)
@@ -352,29 +387,35 @@
 	printk("sig_address:       0x%016lx\n", tss->sig_address);
 	printk("sig_desc:          0x%016lx\n", tss->sig_desc);
 	printk("ksp:               0x%016lx\n", tss->ksp);
-	printk("kpc:               0x%016lx\n", tss->kpc);
+	printk("kpc:               0x%08x\n", tss->kpc);
 
-	for (i = 0; i < NSWINS; i++) {
-		if (!tss->rwbuf_stkptrs[i])
-			continue;
-		printk("reg_window[%d]:\n", i);
-		printk("stack ptr:         0x%016lx\n", tss->rwbuf_stkptrs[i]);
+	if (tss->w_saved) {
+		for (i = 0; i < NSWINS; i++) {
+			if (!tss->rwbuf_stkptrs[i])
+				continue;
+			printk("reg_window[%d]:\n", i);
+			printk("stack ptr:         0x%016lx\n", tss->rwbuf_stkptrs[i]);
+		}
+		printk("w_saved:           0x%04x\n", tss->w_saved);
 	}
-	printk("w_saved:           0x%08lx\n", tss->w_saved);
 
 	printk("sstk_info.stack:   0x%016lx\n",
 	        (unsigned long)tss->sstk_info.the_stack);
 	printk("sstk_info.status:  0x%016lx\n",
 	        (unsigned long)tss->sstk_info.cur_status);
 	printk("flags:             0x%08x\n", tss->flags);
-	printk("current_ds:        0x%016lx\n", tss->current_ds);
-
-	/* XXX missing: core_exec */
+	printk("current_ds:        0x%016lx\n", tss->current_ds.seg);
 }
 
 /* Free current thread data structures etc.. */
 void exit_thread(void)
 {
+	if (current->tss.utraps) {
+		if (current->tss.utraps[0] < 2)
+			kfree (current->tss.utraps);
+		else
+			current->tss.utraps[0]--;
+	}
 }
 
 void flush_thread(void)
@@ -540,9 +581,9 @@
 	memcpy(child_trap_frame, (((struct reg_window *)regs)-1), tframe_size);
 	p->tss.ksp = ((unsigned long) child_trap_frame) - STACK_BIAS;
 #ifdef __SMP__
-	p->tss.kpc = ((unsigned long) ret_from_smpfork) - 0x8;
+	p->tss.kpc = ((unsigned int) ((unsigned long) ret_from_smpfork)) - 0x8;
 #else
-	p->tss.kpc = ((unsigned long) ret_from_syscall) - 0x8;
+	p->tss.kpc = ((unsigned int) ((unsigned long) ret_from_syscall)) - 0x8;
 #endif
 	p->tss.kregs = (struct pt_regs *)(child_trap_frame+sizeof(struct reg_window));
 	p->tss.cwp = (regs->tstate + 1) & TSTATE_CWP;
@@ -569,6 +610,8 @@
 				return -EFAULT;
 			p->tss.kregs->u_regs[UREG_FP] = csp;
 		}
+		if (p->tss.utraps)
+			p->tss.utraps[0]++;
 	}
 
 	/* Set the return value for the child. */
@@ -595,7 +638,6 @@
 	dump->regs.y = regs->y;
 	/* fuck me plenty */
 	memcpy(&dump->regs.regs[0], &regs->u_regs[1], (sizeof(unsigned long) * 15));
-	dump->uexec = current->tss.core_exec;
 	dump->u_tsize = (((unsigned long) current->mm->end_code) -
 		((unsigned long) current->mm->start_code)) & ~(PAGE_SIZE - 1);
 	dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1)));
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/psycho.c linux/arch/sparc64/kernel/psycho.c
--- v2.1.78/linux/arch/sparc64/kernel/psycho.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/psycho.c	Mon Jan 12 15:15:44 1998
@@ -1,7 +1,8 @@
-/* $Id: psycho.c,v 1.22 1997/08/31 03:51:40 davem Exp $
+/* $Id: psycho.c,v 1.31 1998/01/10 18:26:15 ecd Exp $
  * psycho.c: Ultra/AX U2P PCI controller support.
  *
  * Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu)
+ * Copyright (C) 1998 Eddie C. Dost   (ecd@skynet.be)
  */
 
 #include <linux/config.h>
@@ -11,6 +12,16 @@
 #include <asm/ebus.h>
 #include <asm/sbus.h> /* for sanity check... */
 
+#undef PROM_DEBUG
+#undef FIXUP_REGS_DEBUG
+#undef FIXUP_IRQ_DEBUG
+
+#ifdef PROM_DEBUG
+#define dprintf	prom_printf
+#else
+#define dprintf printk
+#endif
+
 #ifndef CONFIG_PCI
 
 int pcibios_present(void)
@@ -49,6 +60,28 @@
 #include <asm/uaccess.h>
 
 struct linux_psycho *psycho_root = NULL;
+struct linux_psycho **psycho_index_map;
+int linux_num_psycho = 0;
+static struct linux_pbm_info *bus2pbm[256];
+
+static int pbm_read_config_byte(struct linux_pbm_info *pbm,
+				unsigned char bus, unsigned char devfn,
+				unsigned char where, unsigned char *value);
+static int pbm_read_config_word(struct linux_pbm_info *pbm,
+				unsigned char bus, unsigned char devfn,
+				unsigned char where, unsigned short *value);
+static int pbm_read_config_dword(struct linux_pbm_info *pbm,
+				 unsigned char bus, unsigned char devfn,
+				 unsigned char where, unsigned int *value);
+static int pbm_write_config_byte(struct linux_pbm_info *pbm,
+				 unsigned char bus, unsigned char devfn,
+				 unsigned char where, unsigned char value);
+static int pbm_write_config_word(struct linux_pbm_info *pbm,
+				 unsigned char bus, unsigned char devfn,
+				 unsigned char where, unsigned short value);
+static int pbm_write_config_dword(struct linux_pbm_info *pbm,
+				  unsigned char bus, unsigned char devfn,
+				  unsigned char where, unsigned int value);
 
 /* This is used to make the scan_bus in the generic PCI code be
  * a nop, as we need to control the actual bus probing sequence.
@@ -69,10 +102,22 @@
 	unsigned long control, i;
 	unsigned long *iopte;
 
+	/*
+	 * Invalidate TLB Entries.
+	 */
+	control = psycho->psycho_regs->iommu_control;
+	control |= IOMMU_CTRL_DENAB;
+	psycho->psycho_regs->iommu_control = control;
+	for(i = 0; i < 16; i++) {
+		psycho->psycho_regs->iommu_data[i] = 0;
+	}
+	control &= ~(IOMMU_CTRL_DENAB);
+	psycho->psycho_regs->iommu_control = control;
+
 	memory_start = (tsbbase + ((32 * 1024) * 8));
 	iopte = (unsigned long *)tsbbase;
 
-	for(i = 0; i < (65536 / 2); i++) {
+	for(i = 0; i < (32 * 1024); i++) {
 		*iopte = (IOPTE_VALID | IOPTE_64K |
 			  IOPTE_CACHE | IOPTE_WRITE);
 		*iopte |= (i << 16);
@@ -94,21 +139,26 @@
 unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
 {
 	struct linux_prom64_registers pr_regs[3];
+	struct linux_psycho *psycho;
 	char namebuf[128];
 	u32 portid;
 	int node;
 
 	printk("PSYCHO: Probing for controllers.\n");
+#ifdef PROM_DEBUG
+	dprintf("PSYCHO: Probing for controllers.\n");
+#endif
 
 	memory_start = long_align(memory_start);
 	node = prom_getchild(prom_root_node);
 	while((node = prom_searchsiblings(node, "pci")) != 0) {
-		struct linux_psycho *psycho = (struct linux_psycho *)memory_start;
 		struct linux_psycho *search;
 		struct linux_pbm_info *pbm = NULL;
 		u32 busrange[2];
 		int err, is_pbm_a;
 
+		psycho = (struct linux_psycho *)memory_start;
+
 		portid = prom_getintdefault(node, "upa-portid", 0xff);
 		for(search = psycho_root; search; search = search->next) {
 			if(search->upa_portid == portid) {
@@ -123,7 +173,8 @@
 			}
 		}
 
-		memory_start = long_align(memory_start + sizeof(struct linux_psycho));
+		memory_start = long_align(memory_start +
+					  sizeof(struct linux_psycho));
 
 		memset(psycho, 0, sizeof(*psycho));
 
@@ -131,8 +182,12 @@
 		psycho_root = psycho;
 
 		psycho->upa_portid = portid;
+		psycho->index = linux_num_psycho++;
 
-		/* Map in PSYCHO register set and report the presence of this PSYCHO. */
+		/*
+		 * Map in PSYCHO register set and report the presence
+		 * of this PSYCHO.
+		 */
 		err = prom_getproperty(node, "reg",
 				       (char *)&pr_regs[0], sizeof(pr_regs));
 		if(err == 0 || err == -1) {
@@ -141,7 +196,10 @@
 			prom_halt();
 		}
 
-		/* Third REG in property is base of entire PSYCHO register space. */
+		/*
+		 * Third REG in property is base of entire PSYCHO
+		 * register space.
+		 */
 		psycho->psycho_regs = sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff),
 						     NULL, sizeof(struct psycho_regs),
 						     "PSYCHO Registers",
@@ -154,12 +212,19 @@
 
 		printk("PSYCHO: Found controller, main regs at %p\n",
 		       psycho->psycho_regs);
-#if 0
-		printk("PSYCHO: Interrupt retry [%016lx]\n",
-		       psycho->psycho_regs->irq_retry);
+#ifdef PROM_DEBUG
+		dprintf("PSYCHO: Found controller, main regs at %p\n",
+			psycho->psycho_regs);
 #endif
+
 		psycho->psycho_regs->irq_retry = 0xff;
 
+#if 0
+		psycho->psycho_regs->ecc_control |= 1;
+		psycho->psycho_regs->sbuf_a_control = 0;
+		psycho->psycho_regs->sbuf_b_control = 0;
+#endif
+
 		/* Now map in PCI config space for entire PSYCHO. */
 		psycho->pci_config_space =
 			sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff)+0x01000000),
@@ -172,7 +237,12 @@
 		}
 
 		/* Report some more info. */
-		printk("PSYCHO: PCI config space at %p\n", psycho->pci_config_space);
+		printk("PSYCHO: PCI config space at %p\n",
+		       psycho->pci_config_space);
+#ifdef PROM_DEBUG
+		dprintf("PSYCHO: PCI config space at %p\n",
+			psycho->pci_config_space);
+#endif
 
 		memory_start = psycho_iommu_init(psycho, memory_start);
 
@@ -223,6 +293,13 @@
 		prom_halt();
 	}
 
+	psycho_index_map = (struct linux_psycho **)long_align(memory_start);
+	memory_start = long_align(memory_start + linux_num_psycho
+					       * sizeof(struct linux_psycho *));
+
+	for (psycho = psycho_root; psycho; psycho = psycho->next)
+		psycho_index_map[psycho->index] = psycho;
+
 	return memory_start;
 }
 
@@ -361,18 +438,118 @@
 	return pci_init_alloc(sizeof(struct pcidev_cookie));
 }
 
+
+static void
+pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus)
+{
+	unsigned int devfn, l, class;
+	unsigned char hdr_type = 0;
+
+	for (devfn = 0; devfn < 0xff; ++devfn) {
+		if (PCI_FUNC(devfn) == 0) {
+			pbm_read_config_byte(pbm, bus, devfn,
+					     PCI_HEADER_TYPE, &hdr_type);
+		} else if (!(hdr_type & 0x80)) {
+			/* not a multi-function device */
+			continue;
+		}
+
+		/* Check if there is anything here. */
+		pbm_read_config_dword(pbm, bus, devfn, PCI_VENDOR_ID, &l);
+		if (l == 0xffffffff || l == 0x00000000) {
+			hdr_type = 0;
+			continue;
+		}
+
+		/* See if this is a bridge device. */
+		pbm_read_config_dword(pbm, bus, devfn,
+				      PCI_CLASS_REVISION, &class);
+
+		if ((class >> 16) == PCI_CLASS_BRIDGE_PCI) {
+			unsigned int buses;
+
+			pbm_read_config_dword(pbm, bus, devfn,
+					      PCI_PRIMARY_BUS, &buses);
+
+			/*
+			 * First reconfigure everything underneath the bridge.
+			 */
+			pbm_reconfigure_bridges(pbm, (buses >> 8) & 0xff);
+
+			/*
+			 * Unconfigure this bridges bus numbers,
+			 * pci_scan_bus() will fix this up properly.
+			 */
+			buses &= 0xff000000;
+			pbm_write_config_dword(pbm, bus, devfn,
+					       PCI_PRIMARY_BUS, buses);
+		}
+	}
+}
+
+static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus)
+{
+	unsigned int nbus;
+
+	/*
+	 * First, reconfigure all bridge devices underneath this pbm.
+	 */
+	pbm_reconfigure_bridges(pbm, pbm->pci_first_busno);
+
+	/*
+	 * Now reconfigure the pbm to it's new bus number and set up
+	 * our bus2pbm mapping for this pbm.
+	 */
+	nbus = pbm->pci_last_busno - pbm->pci_first_busno;
+
+	pbm_write_config_byte(pbm, pbm->pci_first_busno, 0, 0x40, bus);
+
+	pbm->pci_first_busno = bus;
+	pbm_write_config_byte(pbm, bus, 0, 0x41, 0xff);
+
+	do {
+		bus2pbm[bus++] = pbm;
+	} while (nbus--);
+}
+
+
 static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart)
 {
+	static struct pci_bus *pchain = NULL;
 	struct pci_bus *pbus = &pbm->pci_bus;
+	static unsigned char busno = 0;
 
 	/* PSYCHO PBM's include child PCI bridges in bus-range property,
 	 * but we don't scan each of those ourselves, Linux generic PCI
 	 * probing code will find child bridges and link them into this
 	 * pbm's root PCI device hierarchy.
 	 */
-	pbus->number = pbm->pci_first_busno;
+
+	pbus->number = pbus->secondary = busno;
 	pbus->sysdata = pbm;
+
+	pbm_fixup_busno(pbm, busno);
+
 	pbus->subordinate = pci_scan_bus(pbus, mstart);
+
+	/*
+	 * Set the maximum subordinate bus of this pbm.
+	 */
+	pbm->pci_last_busno = pbus->subordinate;
+	pbm_write_config_byte(pbm, busno, 0, 0x41, pbm->pci_last_busno);
+
+	busno = pbus->subordinate + 1;
+
+	/*
+	 * Fixup the chain of primary PCI busses.
+	 */
+	if (pchain) {
+		pchain->next = &pbm->pci_bus;
+		pchain = pchain->next;
+	} else {
+		pchain = &pci_root;
+		memcpy(pchain, &pbm->pci_bus, sizeof(pci_root));
+	}
 }
 
 static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
@@ -433,8 +610,6 @@
 			pdev_cookie_fillin(pbm, pdev);
 }
 
-/* #define RECORD_ASSIGNMENTS_DEBUG */
-
 /* Walk PROM device tree under PBM, looking for 'assigned-address'
  * properties, and recording them in pci_vma's linked in via
  * PBM->assignments.
@@ -534,8 +709,6 @@
 	assignment_walk_siblings(pbm, prom_getchild(pbm->prom_node));
 }
 
-/* #define FIXUP_REGS_DEBUG */
-
 static void fixup_regs(struct pci_dev *pdev,
 		       struct linux_pbm_info *pbm,
 		       struct linux_prom_pci_registers *pregs,
@@ -556,12 +729,14 @@
 		if(bustype == 0) {
 			/* Config space cookie, nothing to do. */
 			if(preg != 0)
-				prom_printf("fixup_doit: strange, config space not 0\n");
+				printk("%s: strange, config space not 0\n",
+				       __FUNCTION__);
 			continue;
 		} else if(bustype == 3) {
 			/* XXX add support for this... */
-			prom_printf("fixup_doit: Warning, ignoring 64-bit PCI "
-				    "memory space, tell DaveM.\n");
+			printk("%s: Warning, ignoring 64-bit PCI memory space, "
+			       "tell Eddie C. Dost (ecd@skynet.be).\n",
+			       __FUNCTION__);
 			continue;
 		}
 		bsreg = (pregs[preg].phys_hi & 0xff);
@@ -574,8 +749,13 @@
 		if((bsreg < PCI_BASE_ADDRESS_0) ||
 		   (bsreg > (PCI_BASE_ADDRESS_5 + 4)) ||
 		   (bsreg & 3)) {
-			prom_printf("fixup_doit: Warning, ignoring bogus basereg [%x]\n",
-				    bsreg);
+			printk("%s: [%04x:%04x]: "
+			       "Warning, ignoring bogus basereg [%x]\n",
+			       __FUNCTION__, pdev->vendor, pdev->device, bsreg);
+			printk("  PROM reg: %08x.%08x.%08x %08x.%08x\n",
+			       pregs[preg].phys_hi, pregs[preg].phys_mid,
+			       pregs[preg].phys_lo, pregs[preg].size_hi,
+			       pregs[preg].size_lo);
 			continue;
 		}
 
@@ -740,22 +920,22 @@
 					  pdev->devfn,
 					  PCI_COMMAND, &l);
 #ifdef FIXUP_REGS_DEBUG
-		prom_printf("[");
+		dprintf("[");
 #endif
 		if(IO_seen) {
 #ifdef FIXUP_REGS_DEBUG
-			prom_printf("IO ");
+			dprintf("IO ");
 #endif
 			l |= PCI_COMMAND_IO;
 		}
 		if(MEM_seen) {
 #ifdef FIXUP_REGS_DEBUG
-			prom_printf("MEM");
+			dprintf("MEM");
 #endif
 			l |= PCI_COMMAND_MEMORY;
 		}
 #ifdef FIXUP_REGS_DEBUG
-		prom_printf("]");
+		dprintf("]");
 #endif
 		pcibios_write_config_dword(pdev->bus->number,
 					   pdev->devfn,
@@ -763,7 +943,7 @@
 	}
 
 #ifdef FIXUP_REGS_DEBUG
-	prom_printf("REG_FIXUP[%s]: ", pci_strdev(pdev->vendor, pdev->device));
+	dprintf("REG_FIXUP[%04x,%04x]: ", pdev->vendor, pdev->device);
 	for(preg = 0; preg < 6; preg++) {
 		if(pdev->base_address[preg] != 0)
 			prom_printf("%d[%016lx] ", preg, pdev->base_address[preg]);
@@ -814,7 +994,7 @@
 }
 
 /* Exported for EBUS probing layer. */
-unsigned int psycho_irq_build(unsigned int full_ino)
+unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino)
 {
 	unsigned long imap_off, ign, ino;
 
@@ -906,11 +1086,9 @@
 	}
 	imap_off -= imap_offset(imap_a_slot0);
 
-	return pci_irq_encode(imap_off, 0 /* XXX */, ign, ino);
+	return pci_irq_encode(imap_off, pbm->parent->index, ign, ino);
 }
 
-/* #define FIXUP_IRQ_DEBUG */
-
 static void fixup_irq(struct pci_dev *pdev,
 		      struct linux_pbm_info *pbm,
 		      int node)
@@ -920,24 +1098,30 @@
 	int err;
 
 #ifdef FIXUP_IRQ_DEBUG
-	printk("fixup_irq[%s:%s]: ",
-	       pci_strvendor(pdev->vendor),
-	       pci_strdev(pdev->vendor, pdev->device));
+	dprintf("fixup_irq[%04x:%04x]: ", pdev->vendor, pdev->device);
 #endif
 	err = prom_getproperty(node, "interrupts", (void *)&prom_irq, sizeof(prom_irq));
 	if(err == 0 || err == -1) {
-		prom_printf("fixup_irq: No interrupts property for dev[%s:%s]\n",
-			    pci_strvendor(pdev->vendor),
-			    pci_strdev(pdev->vendor, pdev->device));
+		prom_printf("fixup_irq: No interrupts property for dev[%04x:%04x]\n",
+			    pdev->vendor, pdev->device);
 		prom_halt();
 	}
 
 	/* See if fully specified already (ie. for onboard devices like hme) */
 	if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) {
-		pdev->irq = psycho_irq_build(prom_irq);
+		pdev->irq = psycho_irq_build(pbm, prom_irq);
+#ifdef FIXUP_IRQ_DEBUG
+		dprintf("fully specified prom_irq[%x] pdev->irq[%x]",
+		        prom_irq, pdev->irq);
+#endif
+	/* See if onboard device interrupt (i.e. bit 5 set) */
+	} else if((prom_irq & PSYCHO_IMAP_INO) & 0x20) {
+		pdev->irq = psycho_irq_build(pbm,
+					     (pbm->parent->upa_portid << 6)
+					     | prom_irq);
 #ifdef FIXUP_IRQ_DEBUG
-		printk("fully specified prom_irq[%x] pdev->irq[%x]",
-		       prom_irq, pdev->irq);
+		dprintf("partially specified prom_irq[%x] pdev->irq[%x]",
+		        prom_irq, pdev->irq);
 #endif
 	} else {
 		unsigned int bus, slot, line;
@@ -952,20 +1136,25 @@
 			if(pbm == &pbm->parent->pbm_A)
 				slot = (pdev->devfn >> 3) - 1;
 			else
-				slot = ((pdev->devfn >> 3) >> 1) - 1;
+				slot = (pdev->devfn >> 3) - 2;
 		} else {
 			/* Underneath a bridge, use slot number of parent
 			 * bridge.
 			 */
-			slot = (pdev->bus->self->devfn >> 3) - 1;
+			if(pbm == &pbm->parent->pbm_A)
+				slot = (pdev->bus->self->devfn >> 3) - 1;
+			else
+				slot = (pdev->bus->self->devfn >> 3) - 2;
 
 			/* Use low slot number bits of child as IRQ line. */
-			line = ((pdev->devfn >> 3) & 3);
+			line = (line + ((pdev->devfn >> 3) - 4)) % 4;
 		}
 		slot = (slot << 2);
 
-		pdev->irq = psycho_irq_build((((portid << 6) & PSYCHO_IMAP_IGN) |
-					     (bus | slot | line)));
+		pdev->irq = psycho_irq_build(pbm,
+					     (((portid << 6) & PSYCHO_IMAP_IGN)
+					     | (bus | slot | line)));
+
 #ifdef FIXUP_IRQ_DEBUG
 		do {
 			unsigned char iline, ipin;
@@ -978,15 +1167,24 @@
 						       pdev->devfn,
 						       PCI_INTERRUPT_LINE,
 						       &iline);
-			printk("FIXED portid[%x] bus[%x] slot[%x] line[%x] irq[%x] "
-			       "iline[%x] ipin[%x] prom_irq[%x]",
-			       portid, bus>>4, slot>>2, line, pdev->irq,
-			       iline, ipin, prom_irq);
+			dprintf("FIXED portid[%x] bus[%x] slot[%x] line[%x] irq[%x] "
+			        "iline[%x] ipin[%x] prom_irq[%x]",
+			        portid, bus>>4, slot>>2, line, pdev->irq,
+			        iline, ipin, prom_irq);
 		} while(0);
 #endif
 	}
+
+	/*
+	 * Write the INO to config space PCI_INTERRUPT_LINE.
+	 */
+	(void)pcibios_write_config_byte(pdev->bus->number,
+					pdev->devfn,
+					PCI_INTERRUPT_LINE,
+					pdev->irq & PCI_IRQ_INO);
+
 #ifdef FIXUP_IRQ_DEBUG
-	printk("\n");
+	dprintf("\n");
 #endif
 }
 
@@ -1093,15 +1291,11 @@
 	/* Second, fixup base address registers and IRQ lines... */
 	fixup_addr_irq(&psycho->pbm_A);
 	fixup_addr_irq(&psycho->pbm_B);
-
-#if 0
-	prom_halt();
-#endif
 }
 
 unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end)
 {
-	struct linux_psycho *psycho = psycho_root;
+	struct linux_psycho *psycho;
 
 	pci_probe_enable = 1;
 
@@ -1117,11 +1311,13 @@
 	 * XXX apps that need to get at PCI configuration space.
 	 */
 
-	/* Probe busses under PBM A. */
-	pbm_probe(&psycho->pbm_A, &memory_start);
+	for (psycho = psycho_root; psycho; psycho = psycho->next) {
+		/* Probe busses under PBM B. */
+		pbm_probe(&psycho->pbm_B, &memory_start);
 
-	/* Probe busses under PBM B. */
-	pbm_probe(&psycho->pbm_B, &memory_start);
+		/* Probe busses under PBM A. */
+		pbm_probe(&psycho->pbm_A, &memory_start);
+	}
 
 	pci_init_alloc_init(&memory_start);
 
@@ -1130,15 +1326,17 @@
 	 * sysdata with a pointer to the PBM (for pci_bus's) or
 	 * a pci_dev cookie (PBM+PROM_NODE, for pci_dev's).
 	 */
-	fill_in_pbm_cookies(&psycho->pbm_A);
-	fill_in_pbm_cookies(&psycho->pbm_B);
+	for (psycho = psycho_root; psycho; psycho = psycho->next) {
+		fill_in_pbm_cookies(&psycho->pbm_A);
+		fill_in_pbm_cookies(&psycho->pbm_B);
+
+		/* See what OBP has taken care of already. */
+		record_assignments(&psycho->pbm_A);
+		record_assignments(&psycho->pbm_B);
 
-	/* See what OBP has taken care of already. */
-	record_assignments(&psycho->pbm_A);
-	record_assignments(&psycho->pbm_B);
-
-	/* Now, fix it all up. */
-	psycho_final_fixup(psycho);
+		/* Now, fix it all up. */
+		psycho_final_fixup(psycho);
+	}
 
 	pci_init_alloc_fini();
 
@@ -1153,113 +1351,124 @@
  * XXX space exists, on Ultra we can have many of them, especially with
  * XXX 'dual-pci' boards on Sunfire/Starfire/Wildfire.
  */
-static char *pci_mkaddr(unsigned char bus, unsigned char device_fn,
-			unsigned char where)
+static void *
+pci_mkaddr(struct linux_pbm_info *pbm, unsigned char bus,
+	   unsigned char devfn, unsigned char where)
 {
-	unsigned long ret = (unsigned long) psycho_root->pci_config_space;
+	unsigned long ret;
+
+	if (!pbm)
+		return NULL;
+
+	ret = (unsigned long) pbm->parent->pci_config_space;
 
 	ret |= (1 << 24);
-	ret |= ((bus & 0xff) << 16);
-	ret |= ((device_fn & 0xff) << 8);
-	ret |= (where & 0xfc);
-	return (unsigned char *)ret;
+	ret |= (bus << 16);
+	ret |= (devfn << 8);
+	ret |= where;
+
+	return (void *)ret;
 }
 
-static inline int out_of_range(unsigned char bus, unsigned char device_fn)
+static inline int
+out_of_range(struct linux_pbm_info *pbm, unsigned char bus, unsigned char devfn)
 {
-	return ((bus == 0 && PCI_SLOT(device_fn) > 4) ||
-		(bus == 1 && PCI_SLOT(device_fn) > 6) ||
+	return (((pbm == &pbm->parent->pbm_B) && PCI_SLOT(devfn) > 4) ||
+		((pbm == &pbm->parent->pbm_A) && PCI_SLOT(devfn) > 6) ||
 		(pci_probe_enable == 0));
 }
 
-int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn,
-			      unsigned char where, unsigned char *value)
-{
-	unsigned char *addr = pci_mkaddr(bus, device_fn, where);
-	unsigned int word, trapped;
+static int
+pbm_read_config_byte(struct linux_pbm_info *pbm,
+		     unsigned char bus, unsigned char devfn,
+		     unsigned char where, unsigned char *value)
+{
+	unsigned char *addr = pci_mkaddr(pbm, bus, devfn, where);
+	unsigned int trapped;
+	unsigned char byte;
 
 	*value = 0xff;
 
-	if(out_of_range(bus, device_fn))
+	if (!addr)
+		return PCIBIOS_SUCCESSFUL;
+
+	if (out_of_range(pbm, bus, devfn))
 		return PCIBIOS_SUCCESSFUL;
 
 	pci_poke_in_progress = 1;
 	pci_poke_faulted = 0;
 	__asm__ __volatile__("membar #Sync\n\t"
-			     "lduwa [%1] %2, %0\n\t"
+			     "lduba [%1] %2, %0\n\t"
 			     "membar #Sync"
-			     : "=r" (word)
+			     : "=r" (byte)
 			     : "r" (addr), "i" (ASI_PL));
 	pci_poke_in_progress = 0;
 	trapped = pci_poke_faulted;
 	pci_poke_faulted = 0;
-	if(!trapped) {
-		switch(where & 3) {
-		case 0:
-			*value = word & 0xff;
-			break;
-		case 1:
-			*value = (word >> 8) & 0xff;
-			break;
-		case 2:
-			*value = (word >> 16) & 0xff;
-			break;
-		case 3:
-			*value = (word >> 24) & 0xff;
-			break;
-		};
-	}
+	if(!trapped)
+		*value = byte;
 	return PCIBIOS_SUCCESSFUL;
 }
 
-int pcibios_read_config_word (unsigned char bus, unsigned char device_fn,
-			      unsigned char where, unsigned short *value)
-{
-	unsigned short *addr = (unsigned short *)pci_mkaddr(bus, device_fn, where);
-	unsigned int word, trapped;
+static int
+pbm_read_config_word(struct linux_pbm_info *pbm,
+		     unsigned char bus, unsigned char devfn,
+		     unsigned char where, unsigned short *value)
+{
+	unsigned short *addr = pci_mkaddr(pbm, bus, devfn, where);
+	unsigned int trapped;
+	unsigned short word;
 
 	*value = 0xffff;
 
-	if(out_of_range(bus, device_fn))
+	if (!addr)
 		return PCIBIOS_SUCCESSFUL;
 
+	if (out_of_range(pbm, bus, devfn))
+		return PCIBIOS_SUCCESSFUL;
+
+	if (where & 0x01) {
+		printk("pcibios_read_config_word: misaligned reg [%x]\n",
+		       where);
+		return PCIBIOS_SUCCESSFUL;
+	}
+
 	pci_poke_in_progress = 1;
 	pci_poke_faulted = 0;
 	__asm__ __volatile__("membar #Sync\n\t"
-			     "lduwa [%1] %2, %0\n\t"
+			     "lduha [%1] %2, %0\n\t"
 			     "membar #Sync"
 			     : "=r" (word)
 			     : "r" (addr), "i" (ASI_PL));
 	pci_poke_in_progress = 0;
 	trapped = pci_poke_faulted;
 	pci_poke_faulted = 0;
-	if(!trapped) {
-		switch(where & 3) {
-		case 0:
-			*value = word & 0xffff;
-			break;
-		case 2:
-			*value = (word >> 16) & 0xffff;
-			break;
-		default:
-			printk("pcibios_read_config_word: misaligned "
-			       "reg [%x]\n", where);
-			break;
-		};
-	}
+	if(!trapped)
+		*value = word;
 	return PCIBIOS_SUCCESSFUL;
 }
 
-int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn,
-			       unsigned char where, unsigned int *value)
+static int
+pbm_read_config_dword(struct linux_pbm_info *pbm,
+		      unsigned char bus, unsigned char devfn,
+		      unsigned char where, unsigned int *value)
 {
-	unsigned int *addr = (unsigned int *)pci_mkaddr(bus, device_fn, where);
+	unsigned int *addr = pci_mkaddr(pbm, bus, devfn, where);
 	unsigned int word, trapped;
 
 	*value = 0xffffffff;
 
-	if(out_of_range(bus, device_fn))
+	if (!addr)
+		return PCIBIOS_SUCCESSFUL;
+
+	if (out_of_range(pbm, bus, devfn))
+		return PCIBIOS_SUCCESSFUL;
+
+	if (where & 0x03) {
+		printk("pcibios_read_config_dword: misaligned reg [%x]\n",
+		       where);
 		return PCIBIOS_SUCCESSFUL;
+	}
 
 	pci_poke_in_progress = 1;
 	pci_poke_faulted = 0;
@@ -1276,12 +1485,17 @@
 	return PCIBIOS_SUCCESSFUL;
 }
 
-int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn,
-			       unsigned char where, unsigned char value)
+static int
+pbm_write_config_byte(struct linux_pbm_info *pbm,
+		      unsigned char bus, unsigned char devfn,
+		      unsigned char where, unsigned char value)
 {
-	unsigned char *addr = pci_mkaddr(bus, device_fn, where);
+	unsigned char *addr = pci_mkaddr(pbm, bus, devfn, where);
 
-	if(out_of_range(bus, device_fn))
+	if (!addr)
+		return PCIBIOS_SUCCESSFUL;
+
+	if (out_of_range(pbm, bus, devfn))
 		return PCIBIOS_SUCCESSFUL;
 
 	pci_poke_in_progress = 1;
@@ -1299,14 +1513,25 @@
 	return PCIBIOS_SUCCESSFUL;
 }
 
-int pcibios_write_config_word (unsigned char bus, unsigned char device_fn,
-			       unsigned char where, unsigned short value)
+static int
+pbm_write_config_word(struct linux_pbm_info *pbm,
+		      unsigned char bus, unsigned char devfn,
+		      unsigned char where, unsigned short value)
 {
-	unsigned short *addr = (unsigned short *)pci_mkaddr(bus, device_fn, where);
+	unsigned short *addr = pci_mkaddr(pbm, bus, devfn, where);
 
-	if(out_of_range(bus, device_fn))
+	if (!addr)
 		return PCIBIOS_SUCCESSFUL;
 
+	if (out_of_range(pbm, bus, devfn))
+		return PCIBIOS_SUCCESSFUL;
+
+	if (where & 0x01) {
+		printk("pcibios_write_config_word: misaligned reg [%x]\n",
+		       where);
+		return PCIBIOS_SUCCESSFUL;
+	}
+
 	pci_poke_in_progress = 1;
 	__asm__ __volatile__("membar #Sync\n\t"
 			     "stha %0, [%1] %2\n\t"
@@ -1317,14 +1542,25 @@
 	return PCIBIOS_SUCCESSFUL;
 }
 
-int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
-				unsigned char where, unsigned int value)
+static int
+pbm_write_config_dword(struct linux_pbm_info *pbm,
+		       unsigned char bus, unsigned char devfn,
+		       unsigned char where, unsigned int value)
 {
-	unsigned int *addr = (unsigned int *)pci_mkaddr(bus, device_fn, where);
+	unsigned int *addr = pci_mkaddr(pbm, bus, devfn, where);
 
-	if(out_of_range(bus, device_fn))
+	if (!addr)
 		return PCIBIOS_SUCCESSFUL;
 
+	if (out_of_range(pbm, bus, devfn))
+		return PCIBIOS_SUCCESSFUL;
+
+	if (where & 0x03) {
+		printk("pcibios_write_config_dword: misaligned reg [%x]\n",
+		       where);
+		return PCIBIOS_SUCCESSFUL;
+	}
+
 	pci_poke_in_progress = 1;
 	__asm__ __volatile__("membar #Sync\n\t"
 			     "stwa %0, [%1] %2\n\t"
@@ -1335,6 +1571,42 @@
 	return PCIBIOS_SUCCESSFUL;
 }
 
+int pcibios_read_config_byte (unsigned char bus, unsigned char devfn,
+			      unsigned char where, unsigned char *value)
+{
+	return pbm_read_config_byte(bus2pbm[bus], bus, devfn, where, value);
+}
+
+int pcibios_read_config_word (unsigned char bus, unsigned char devfn,
+			      unsigned char where, unsigned short *value)
+{
+	return pbm_read_config_word(bus2pbm[bus], bus, devfn, where, value);
+}
+
+int pcibios_read_config_dword (unsigned char bus, unsigned char devfn,
+			       unsigned char where, unsigned int *value)
+{
+	return pbm_read_config_dword(bus2pbm[bus], bus, devfn, where, value);
+}
+
+int pcibios_write_config_byte (unsigned char bus, unsigned char devfn,
+			       unsigned char where, unsigned char value)
+{
+	return pbm_write_config_byte(bus2pbm[bus], bus, devfn, where, value);
+}
+
+int pcibios_write_config_word (unsigned char bus, unsigned char devfn,
+			       unsigned char where, unsigned short value)
+{
+	return pbm_write_config_word(bus2pbm[bus], bus, devfn, where, value);
+}
+
+int pcibios_write_config_dword (unsigned char bus, unsigned char devfn,
+			        unsigned char where, unsigned int value)
+{
+	return pbm_write_config_dword(bus2pbm[bus], bus, devfn, where, value);
+}
+
 asmlinkage int sys_pciconfig_read(unsigned long bus,
 				  unsigned long dfn,
 				  unsigned long off,
@@ -1346,19 +1618,22 @@
 	unsigned int uint;
 	int err = 0;
 
+	if(!suser())
+		return -EPERM;
+
 	lock_kernel();
 	switch(len) {
 	case 1:
 		pcibios_read_config_byte(bus, dfn, off, &ubyte);
-		put_user(ubyte, buf);
+		put_user(ubyte, (unsigned char *)buf);
 		break;
 	case 2:
 		pcibios_read_config_word(bus, dfn, off, &ushort);
-		put_user(ushort, buf);
+		put_user(ushort, (unsigned short *)buf);
 		break;
 	case 4:
 		pcibios_read_config_dword(bus, dfn, off, &uint);
-		put_user(uint, buf);
+		put_user(uint, (unsigned int *)buf);
 		break;
 
 	default:
@@ -1380,6 +1655,9 @@
 	unsigned short ushort;
 	unsigned int uint;
 	int err = 0;
+
+	if(!suser())
+		return -EPERM;
 
 	lock_kernel();
 	switch(len) {
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/ptrace.c linux/arch/sparc64/kernel/ptrace.c
--- v2.1.78/linux/arch/sparc64/kernel/ptrace.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/ptrace.c	Mon Jan 12 15:15:44 1998
@@ -309,12 +309,14 @@
 	}
 	if(offset >= 16 && offset < 784) {
 		offset -= 16; offset >>= 2;
-		pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr);
+		if (t->w_saved)
+			pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr);
 		return;
 	}
 	if(offset >= 784 && offset < 832) {
 		offset -= 784; offset >>= 2;
-		pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr);
+		if (t->w_saved)
+			pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr);
 		return;
 	}
 	switch(offset) {
@@ -399,12 +401,14 @@
 		goto failure;
 	if(offset >= 16 && offset < 784) {
 		offset -= 16; offset >>= 2;
-		*(((unsigned long *)(&t->reg_window[0]))+offset) = value;
+		if (t->w_saved)
+			*(((unsigned long *)(&t->reg_window[0]))+offset) = value;
 		goto success;
 	}
 	if(offset >= 784 && offset < 832) {
 		offset -= 784; offset >>= 2;
-		*(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value;
+		if (t->w_saved)
+			*(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value;
 		goto success;
 	}
 	switch(offset) {
@@ -964,7 +968,7 @@
 		addr = 1;
 
 	case PTRACE_CONT: { /* restart after signal. */
-		if (data > NSIG) {
+		if (data > _NSIG) {
 			pt_error_return(regs, EIO);
 			goto out;
 		}
@@ -1016,7 +1020,7 @@
 	}
 
 	case PTRACE_SUNDETACH: { /* detach a process that was attached. */
-		if ((unsigned long) data > NSIG) {
+		if ((unsigned long) data > _NSIG) {
 			pt_error_return(regs, EIO);
 			goto out;
 		}
@@ -1063,10 +1067,7 @@
 		current->pid, current->exit_code);
 #endif
 	if (current->exit_code) {
-		spin_lock_irq(&current->sigmask_lock);
-		current->signal |= (1 << (current->exit_code - 1));
-		spin_unlock_irq(&current->sigmask_lock);
+		send_sig (current->exit_code, current, 1);
+		current->exit_code = 0;
 	}
-
-	current->exit_code = 0;
 }
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/rtrap.S linux/arch/sparc64/kernel/rtrap.S
--- v2.1.78/linux/arch/sparc64/kernel/rtrap.S	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/rtrap.S	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: rtrap.S,v 1.33 1997/08/21 09:13:22 davem Exp $
+/* $Id: rtrap.S,v 1.37 1997/12/11 15:14:54 jj Exp $
  * rtrap.S: Preparing for return from trap on Sparc V9.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -55,11 +55,11 @@
 		ldda			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x0c0] %asi, %f48
 1:		membar			#Sync
 		ldx			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x100], %fsr
-rt_continue:	ld			[%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0
+rt_continue:	lduh			[%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0
 		ldx			[%sp + PTREGS_OFF + PT_V9_G1], %g1
 		ldx			[%sp + PTREGS_OFF + PT_V9_G2], %g2
 		ldx			[%sp + PTREGS_OFF + PT_V9_G3], %g3
-		mov			%g6, %l6
+		mov			%g6, %o5
 		ldx			[%sp + PTREGS_OFF + PT_V9_G4], %g4
 		ldx			[%sp + PTREGS_OFF + PT_V9_G5], %g5
 		ldx			[%sp + PTREGS_OFF + PT_V9_G6], %g6
@@ -88,10 +88,10 @@
 		wrpr			%o2, %g0, %tnpc
 		mov			PRIMARY_CONTEXT, %l7
 		brnz,pn			%l3, kern_rtt
-		 mov			SECONDARY_CONTEXT, %l5
+		 mov			SECONDARY_CONTEXT, %o4
 		stxa			%l0, [%l7] ASI_DMMU
-		stxa			%l0, [%l5] ASI_DMMU
-		flush			%l6
+		stxa			%l0, [%o4] ASI_DMMU
+		flush			%o5
 		rdpr			%wstate, %l1
 
 		rdpr			%otherwin, %l2
@@ -106,30 +106,25 @@
 		retry
 kern_rtt:	restore
 		retry
-to_user:	lduw			[%g6 + AOFF_task_processor], %o0
-		mov			1, %o1
-		sethi			%hi(need_resched), %l0
+to_user:	sethi			%hi(need_resched), %l0
 		ldx			[%l0 + %lo(need_resched)], %l0
-		sllx			%o1, %o0, %o1
 		wrpr			%l7, PSTATE_IE, %pstate
-		andcc			%o1, %l0, %g0
-		be,pt			%xcc, check_signal
-		 ldx			[%g6 + AOFF_task_signal], %l0
+		orcc			%g0, %l0, %g0
+		be,a,pt			%xcc, check_signal
+		 lduw			[%g6 + AOFF_task_sigpending], %l0
 
 		call			schedule
 		 nop
-		ldx			[%g6 + AOFF_task_signal], %l0
-		nop
-check_signal:	ldx			[%g6 + AOFF_task_blocked], %o0
-		ld			[%sp + PTREGS_OFF + PT_V9_FPRS], %l2
-		andncc			%l0, %o0, %g0
-		be,pt			%xcc, check_user_wins
-		 ldx			[%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2
+		lduw			[%g6 + AOFF_task_sigpending], %l0
+check_signal:	ld			[%sp + PTREGS_OFF + PT_V9_FPRS], %l2
+		brz,a,pt		%l0, check_user_wins
+		 lduh			[%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2
+		clr			%o0
 		mov			%l5, %o2  
 		mov			%l6, %o3
 		call			do_signal
 		 add			%sp, STACK_BIAS + REGWIN_SZ, %o1
-		ldx			[%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2
+		lduh			[%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2
 		clr			%l6
 check_user_wins:brz,pt			%o2, 1f
 		 sethi			%hi(TSTATE_PEF), %o3
@@ -142,7 +137,7 @@
 		be,a,pt			%icc, rt_continue
 		 andn			%l1, %o3, %l1		! If fprs.FEF is not set, disable tstate.PEF
 		ldx			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x108], %o3
-		ld			[%g6 + AOFF_task_tss + AOFF_thread_flags], %l2
+		lduh			[%g6 + AOFF_task_tss + AOFF_thread_flags], %l2
 		wr			%g0, FPRS_FEF, %fprs
 		wr			%o3, 0, %gsr
 		andcc			%l2, SPARC_FLAG_USEDFPUL, %g0
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/setup.c linux/arch/sparc64/kernel/setup.c
--- v2.1.78/linux/arch/sparc64/kernel/setup.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/setup.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.12 1997/08/28 02:23:19 ecd Exp $
+/*  $Id: setup.c,v 1.18 1997/12/18 02:43:00 ecd Exp $
  *  linux/arch/sparc64/kernel/setup.c
  *
  *  Copyright (C) 1995,1996  David S. Miller (davem@caip.rutgers.edu)
@@ -37,6 +37,10 @@
 #include <asm/idprom.h>
 #include <asm/head.h>
 
+#ifdef CONFIG_IP_PNP
+#include <net/ipconfig.h>
+#endif
+
 struct screen_info screen_info = {
 	0, 0,			/* orig-x, orig-y */
 	{ 0, 0, },		/* unused */
@@ -236,20 +240,35 @@
 char saved_command_line[256];
 char reboot_command[256];
 
-#ifdef CONFIG_ROOT_NFS
-extern char nfs_root_addrs[];
-#endif
-
 unsigned long phys_base;
 
 static struct pt_regs fake_swapper_regs = { { 0, }, 0, 0, 0, 0 };
 
+#if 0
+#include <linux/console.h>
+
+static void prom_cons_write(struct console *con, const char *str, unsigned count)
+{
+        while (count--)
+                prom_printf("%c", *str++);
+}
+
+static struct console prom_console = {
+        "PROM", prom_cons_write, 0, 0, 0, 0, 0, CON_PRINTBUFFER, 0, 0, 0
+};
+#endif
+
 __initfunc(void setup_arch(char **cmdline_p,
 	unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
+	extern int serial_console;  /* in console.c, of course */
 	unsigned long lowest_paddr;
 	int total, i;
 
+#if 0
+	register_console(&prom_console);
+#endif
+
 	/* Initialize PROM console and command line. */
 	*cmdline_p = prom_getbootargs();
 	strcpy(saved_command_line, *cmdline_p);
@@ -330,62 +349,58 @@
 	init_task.mm->context = (unsigned long) NO_CONTEXT;
 	init_task.tss.kregs = &fake_swapper_regs;
 
-#ifdef CONFIG_ROOT_NFS	
-	if (!*nfs_root_addrs) {
+#ifdef CONFIG_IP_PNP
+	if (!ic_set_manually) {
 		int chosen = prom_finddevice ("/chosen");
 		u32 cl, sv, gw;
-		char *p = nfs_root_addrs;
 		
 		cl = prom_getintdefault (chosen, "client-ip", 0);
 		sv = prom_getintdefault (chosen, "server-ip", 0);
 		gw = prom_getintdefault (chosen, "gateway-ip", 0);
 		if (cl && sv) {
-			strcpy (p, in_ntoa (cl));
-			p += strlen (p);
-			*p++ = ':';
-			strcpy (p, in_ntoa (sv));
-			p += strlen (p);
-			*p++ = ':';
-			if (gw) {
-				strcpy (p, in_ntoa (gw));
-				p += strlen (p);
-			}
-			strcpy (p, "::::none");
+			ic_myaddr = cl;
+			ic_servaddr = sv;
+			if (gw)
+				ic_gateway = gw;
+			ic_bootp_flag = ic_rarp_flag = 0;
 		}
 	}
 #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's 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();
-				}
+	switch (console_fb) {
+	case 0: /* Let's 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: "
+					    "input %d, output %d\n",
+					    idev, odev);
+				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
+		break;
+	case 1: /* Force one of the framebuffers as console */
+		serial_console = 0;
+		break;
+	case 2: /* Force ttya as console */
+		serial_console = 1;
+		break;
+	case 3: /* Force ttyb as console */
+		serial_console = 2;
+		break;
 	}
+	*memory_start_p = sun_serial_setup(*memory_start_p); /* set this up ASAP */
+#else
+	serial_console = 0;
+#endif
 }
 
 asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on)
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/signal.c linux/arch/sparc64/kernel/signal.c
--- v2.1.78/linux/arch/sparc64/kernel/signal.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/signal.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/*  $Id: signal.c,v 1.24 1997/09/02 20:53:03 davem Exp $
+/*  $Id: signal.c,v 1.27 1997/12/15 15:04:44 jj Exp $
  *  arch/sparc64/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -26,15 +26,14 @@
 #include <asm/fpumacro.h>
 #include <asm/uctx.h>
 #include <asm/smp_lock.h>
+#include <asm/siginfo.h>
 
-#define _S(nr) (1<<((nr)-1))
-
-#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
 asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
 			 int options, unsigned long *ru);
 
-asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs,
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs,
 			 unsigned long orig_o0, int ret_from_syscall);
 
 /* This turned off for production... */
@@ -61,8 +60,20 @@
 	if((pc | npc) & 3)
 		goto do_sigsegv;
 	if(regs->u_regs[UREG_I1]) {
-		__get_user(current->blocked, &ucp->uc_sigmask);
-		current->blocked &= _BLOCKABLE;
+		sigset_t set;
+
+		if (_NSIG_WORDS == 1) {
+			if (__get_user(set.sig[0], &ucp->uc_sigmask.sig[0]))
+				goto do_sigsegv;
+		} else {
+			if (__copy_from_user(&set, &ucp->uc_sigmask, sizeof(sigset_t)))
+				goto do_sigsegv;
+		}
+		sigdelsetmask(&set, ~_BLOCKABLE);
+		spin_lock_irq(&current->sigmask_lock);
+		current->blocked = set;
+		recalc_sigpending(current);
+		spin_unlock_irq(&current->sigmask_lock);
 	}
 	regs->tpc = pc;
 	regs->tnpc = npc;
@@ -132,7 +143,11 @@
 	regs->tpc   = regs->tnpc;
 	regs->tnpc += 4;
 
-	__put_user(current->blocked, &ucp->uc_sigmask);
+	if (_NSIG_WORDS == 1)
+		__put_user(current->blocked.sig[0], (unsigned long *)&ucp->uc_sigmask);
+	else
+		__copy_to_user(&ucp->uc_sigmask, &current->blocked, sizeof(sigset_t));
+
 	__put_user(regs->tstate, &((*grp)[MC_TSTATE]));
 	__put_user(regs->tpc, &((*grp)[MC_PC]));
 	__put_user(regs->tnpc, &((*grp)[MC_NPC]));
@@ -198,31 +213,45 @@
 	__siginfo_t		info;
 	__siginfo_fpu_t *	fpu_save;
 	unsigned int		insns [2];
+	unsigned long		extramask[_NSIG_WORDS-1];
+	__siginfo_fpu_t		fpu_state;
+};
+
+struct rt_signal_frame {
+	struct sparc_stackf	ss;
+	siginfo_t		info;
+	struct pt_regs		regs;
+	sigset_t		mask;
+	__siginfo_fpu_t *	fpu_save;
+	unsigned int		insns [2];
 	__siginfo_fpu_t		fpu_state;
 };
 
 /* Align macros */
 #define NF_ALIGNEDSZ  (((sizeof(struct new_signal_frame) + 7) & (~7)))
+#define RT_ALIGNEDSZ  (((sizeof(struct rt_signal_frame) + 7) & (~7)))
 
 /*
  * atomically swap in the new signal mask, and wait for a signal.
  * This is really tricky on the Sparc, watch out...
  */
-asmlinkage void _sigpause_common(unsigned int set, struct pt_regs *regs)
+asmlinkage void _sigpause_common(old_sigset_t set, struct pt_regs *regs)
 {
-	unsigned long mask;
+	sigset_t saveset;
 
 #ifdef CONFIG_SPARC32_COMPAT
 	if (current->tss.flags & SPARC_FLAG_32BIT) {
-		extern asmlinkage void _sigpause32_common(unsigned int,
+		extern asmlinkage void _sigpause32_common(old_sigset_t32,
 							  struct pt_regs *);
 		_sigpause32_common(set, regs);
 		return;
 	}
 #endif
+	set &= _BLOCKABLE;
 	spin_lock_irq(&current->sigmask_lock);
-	mask = current->blocked;
-	current->blocked = set & _BLOCKABLE;
+	saveset = current->blocked;
+	siginitset(&current->blocked, set);
+	recalc_sigpending(current);
 	spin_unlock_irq(&current->sigmask_lock);
 	
 	regs->tpc = regs->tnpc;
@@ -242,7 +271,7 @@
 		 */
 		regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY);
 		regs->u_regs[UREG_I0] = EINTR;
-		if (do_signal(mask, regs, 0, 0))
+		if (do_signal(&saveset, regs, 0, 0))
 			return;
 	}
 }
@@ -257,6 +286,50 @@
 	_sigpause_common(regs->u_regs[UREG_I0], regs);
 }
 
+asmlinkage void do_rt_sigsuspend(sigset_t *uset, size_t sigsetsize, struct pt_regs *regs)
+{
+	sigset_t oldset, set;
+        
+	/* XXX: Don't preclude handling different sized sigset_t's.  */
+	if (sigsetsize != sizeof(sigset_t)) {
+		regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY);
+		regs->u_regs[UREG_I0] = EINVAL;
+		return;
+	}
+	if (copy_from_user(&set, uset, sizeof(set))) {
+		regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY);
+		regs->u_regs[UREG_I0] = EFAULT;
+		return;
+	}
+                                                                
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	oldset = current->blocked;
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+	
+	regs->tpc = regs->tnpc;
+	regs->tnpc += 4;
+
+	/* Condition codes and return value where set here for sigpause,
+	 * and so got used by setup_frame, which again causes sigreturn()
+	 * to return -EINTR.
+	 */
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		/*
+		 * Return -EINTR and set condition code here,
+		 * so the interrupted system call actually returns
+		 * these.
+		 */
+		regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY);
+		regs->u_regs[UREG_I0] = EINTR;
+		if (do_signal(&oldset, regs, 0, 0))
+			return;
+	}
+}
 
 static inline void
 restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu)
@@ -282,47 +355,100 @@
 	struct new_signal_frame *sf;
 	unsigned long tpc, tnpc, tstate;
 	__siginfo_fpu_t *fpu_save;
-	unsigned long mask;
+	sigset_t set;
 
-#ifdef CONFIG_SPARC32_COMPAT
-	if (current->tss.flags & SPARC_FLAG_32BIT) {
-		extern asmlinkage void do_sigreturn32(struct pt_regs *);
-		return do_sigreturn32(regs);
-	}
-#endif	
 	synchronize_user_stack ();
 	sf = (struct new_signal_frame *)
 		(regs->u_regs [UREG_FP] + STACK_BIAS);
 
 	/* 1. Make sure we are not getting garbage from the user */
-	if (verify_area (VERIFY_READ, sf, sizeof (*sf)))
+	if (((unsigned long) sf) & 3)
+		goto segv;
+
+	if (get_user(tpc, &sf->info.si_regs.tpc) ||
+	    __get_user(tnpc, &sf->info.si_regs.tnpc) ||
+	    ((tpc | tnpc) & 3))
+		goto segv;
+
+	regs->tpc = tpc;
+	regs->tnpc = tnpc;
+
+	/* 2. Restore the state */
+	if (__get_user(regs->y, &sf->info.si_regs.y) ||
+	    __get_user(tstate, &sf->info.si_regs.tstate) ||
+	    copy_from_user(regs->u_regs, sf->info.si_regs.u_regs, sizeof(regs->u_regs)))
+		goto segv;
+
+	/* User can only change condition codes in %tstate. */
+	regs->tstate &= ~(TSTATE_ICC);
+	regs->tstate |= (tstate & TSTATE_ICC);
+
+	if (__get_user(fpu_save, &sf->fpu_save))
+		goto segv;
+	if (fpu_save)
+		restore_fpu_state(regs, &sf->fpu_state);
+	if (__get_user(set.sig[0], &sf->info.si_mask) ||
+	    (_NSIG_WORDS > 1 &&
+	     __copy_from_user(&set.sig[1], &sf->extramask,
+	      sizeof(sf->extramask))))
 		goto segv;
 
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+	return;
+segv:
+	lock_kernel();
+	do_exit(SIGSEGV);
+}
+
+void do_rt_sigreturn(struct pt_regs *regs)
+{
+	struct rt_signal_frame *sf;
+	unsigned long tpc, tnpc, tstate;
+	__siginfo_fpu_t *fpu_save;
+	sigset_t set;
+
+	synchronize_user_stack ();
+	sf = (struct rt_signal_frame *)
+		(regs->u_regs [UREG_FP] + STACK_BIAS);
+
+	/* 1. Make sure we are not getting garbage from the user */
 	if (((unsigned long) sf) & 3)
 		goto segv;
 
-	get_user(tpc, &sf->info.si_regs.tpc);
-	__get_user(tnpc, &sf->info.si_regs.tnpc);
-	if ((tpc | tnpc) & 3)
+	if (get_user(tpc, &sf->regs.tpc) ||
+	    __get_user(tnpc, &sf->regs.tnpc) ||
+	    ((tpc | tnpc) & 3))
 		goto segv;
 
 	regs->tpc = tpc;
 	regs->tnpc = tnpc;
 
 	/* 2. Restore the state */
-	__get_user(regs->y, &sf->info.si_regs.y);
-	__get_user(tstate, &sf->info.si_regs.tstate);
-	copy_from_user(regs->u_regs, sf->info.si_regs.u_regs, sizeof(regs->u_regs));
+	if (__get_user(regs->y, &sf->regs.y) ||
+	    __get_user(tstate, &sf->regs.tstate) ||
+	    copy_from_user(regs->u_regs, sf->regs.u_regs, sizeof(regs->u_regs)))
+		goto segv;
 
 	/* User can only change condition codes in %tstate. */
 	regs->tstate &= ~(TSTATE_ICC);
 	regs->tstate |= (tstate & TSTATE_ICC);
 
-	__get_user(fpu_save, &sf->fpu_save);
+	if (__get_user(fpu_save, &sf->fpu_save))
+		goto segv;
 	if (fpu_save)
 		restore_fpu_state(regs, &sf->fpu_state);
-	__get_user(mask, &sf->info.si_mask);
-	current->blocked = mask & _BLOCKABLE;
+
+	if (__copy_from_user(&set, &sf->mask, sizeof(sigset_t)))
+		goto segv;
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
 	return;
 segv:
 	lock_kernel();
@@ -364,8 +490,8 @@
 }
 
 static inline void
-new_setup_frame(struct sigaction *sa, struct pt_regs *regs,
-		  int signo, unsigned long oldmask)
+new_setup_frame(struct k_sigaction *ka, struct pt_regs *regs,
+		  int signo, sigset_t *oldset)
 {
 	struct new_signal_frame *sf;
 	int sigframe_size;
@@ -398,33 +524,122 @@
 		__put_user(0, &sf->fpu_save);
 	}
 
-	__put_user(oldmask, &sf->info.si_mask);
+	__put_user(oldset->sig[0], &sf->info.si_mask);
+	if (_NSIG_WORDS > 1)
+		__copy_to_user(sf->extramask, &oldset->sig[1], sizeof(sf->extramask));
 
 	copy_in_user((u64 *)sf,
 		     (u64 *)(regs->u_regs[UREG_FP]+STACK_BIAS),
 		     sizeof(struct reg_window));
 	
-	/* 3. return to kernel instructions */
-	__put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */
-	__put_user(0x91d02011, &sf->insns[1]); /* t 0x11 */
+	/* 3. signal handler back-trampoline and parameters */
+	regs->u_regs[UREG_FP] = ((unsigned long) sf) - STACK_BIAS;
+	regs->u_regs[UREG_I0] = signo;
+	regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
+
+	/* 5. signal handler */
+	regs->tpc = (unsigned long) ka->sa.sa_handler;
+	regs->tnpc = (regs->tpc + 4);
+
+	/* 4. return to kernel instructions */
+	if (ka->ka_restorer)
+		regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
+	else {
+		/* Flush instruction space. */
+		unsigned long address = ((unsigned long)&(sf->insns[0]));
+		pgd_t *pgdp = pgd_offset(current->mm, address);
+		pmd_t *pmdp = pmd_offset(pgdp, address);
+		pte_t *ptep = pte_offset(pmdp, address);
+
+		regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
+		
+		__put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */
+		__put_user(0x91d0206d, &sf->insns[1]); /* t 0x6d */
+
+		if(pte_present(*ptep)) {
+			unsigned long page = pte_page(*ptep);
+
+			__asm__ __volatile__("
+			membar	#StoreStore
+			flush	%0 + %1"
+			: : "r" (page), "r" (address & (PAGE_SIZE - 1))
+			: "memory");
+		}
+	}
+	return;
+
+sigill:
+	lock_kernel();
+	do_exit(SIGILL);
+}
+
+static inline void
+setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs,
+	       int signo, sigset_t *oldset, siginfo_t *info)
+{
+	struct rt_signal_frame *sf;
+	int sigframe_size;
+
+	/* 1. Make sure everything is clean */
+	synchronize_user_stack();
+	sigframe_size = RT_ALIGNEDSZ;
+	if (!(current->tss.flags & SPARC_FLAG_USEDFPU))
+		sigframe_size -= sizeof(__siginfo_fpu_t);
 
-	/* 4. signal handler back-trampoline and parameters */
+	sf = (struct rt_signal_frame *)
+		(regs->u_regs[UREG_FP] + STACK_BIAS - sigframe_size);
+	
+	if (invalid_frame_pointer (sf, sigframe_size))
+		goto sigill;
+
+	if (current->tss.w_saved != 0) {
+		printk ("%s[%d]: Invalid user stack frame for "
+			"signal delivery.\n", current->comm, current->pid);
+		goto sigill;
+	}
+
+	/* 2. Save the current process state */
+	copy_to_user(&sf->regs, regs, sizeof (*regs));
+
+	if (current->tss.flags & SPARC_FLAG_USEDFPU) {
+		save_fpu_state(regs, &sf->fpu_state);
+		__put_user((u64)&sf->fpu_state, &sf->fpu_save);
+	} else {
+		__put_user(0, &sf->fpu_save);
+	}
+
+	copy_to_user(&sf->mask, oldset, sizeof(sigset_t));
+
+	copy_in_user((u64 *)sf,
+		     (u64 *)(regs->u_regs[UREG_FP]+STACK_BIAS),
+		     sizeof(struct reg_window));
+
+	copy_to_user(&sf->info, info, sizeof(siginfo_t));
+	
+	/* 3. signal handler back-trampoline and parameters */
 	regs->u_regs[UREG_FP] = ((unsigned long) sf) - STACK_BIAS;
 	regs->u_regs[UREG_I0] = signo;
 	regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
-	regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
 
 	/* 5. signal handler */
-	regs->tpc = (unsigned long) sa->sa_handler;
+	regs->tpc = (unsigned long) ka->sa.sa_handler;
 	regs->tnpc = (regs->tpc + 4);
 
-	/* Flush instruction space. */
-	{
+	/* 4. return to kernel instructions */
+	if (ka->ka_restorer)
+		regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
+	else {
+		/* Flush instruction space. */
 		unsigned long address = ((unsigned long)&(sf->insns[0]));
 		pgd_t *pgdp = pgd_offset(current->mm, address);
 		pmd_t *pmdp = pmd_offset(pgdp, address);
 		pte_t *ptep = pte_offset(pmdp, address);
 
+		regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
+		
+		__put_user(0x82102065, &sf->insns[0]); /* mov __NR_rt_sigreturn, %g1 */
+		__put_user(0x91d0206d, &sf->insns[1]); /* t 0x6d */
+
 		if(pte_present(*ptep)) {
 			unsigned long page = pte_page(*ptep);
 
@@ -442,15 +657,21 @@
 	do_exit(SIGILL);
 }
 
-static inline void handle_signal(unsigned long signr, struct sigaction *sa,
-				 unsigned long oldmask, struct pt_regs *regs)
+static inline void handle_signal(unsigned long signr, struct k_sigaction *ka,
+				 siginfo_t *info,
+				 sigset_t *oldset, struct pt_regs *regs)
 {
-	new_setup_frame(sa, regs, signr, oldmask);
-	if(sa->sa_flags & SA_ONESHOT)
-		sa->sa_handler = NULL;
-	if(!(sa->sa_flags & SA_NOMASK)) {
+	if(ka->sa.sa_flags & SA_SIGINFO)
+		setup_rt_frame(ka, regs, signr, oldset, info);
+	else
+		new_setup_frame(ka, regs, signr, oldset);
+	if(ka->sa.sa_flags & SA_ONESHOT)
+		ka->sa.sa_handler = SIG_DFL;
+	if(!(ka->sa.sa_flags & SA_NOMASK)) {
 		spin_lock_irq(&current->sigmask_lock);
-		current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE;
+		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+		sigaddset(&current->blocked,signr);
+		recalc_sigpending(current);
 		spin_unlock_irq(&current->sigmask_lock);
 	}
 }
@@ -479,28 +700,30 @@
  * want to handle. Thus you cannot kill init even with a SIGKILL even by
  * mistake.
  */
-asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs,
-			   unsigned long orig_i0, int restart_syscall)
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs,
+			 unsigned long orig_i0, int restart_syscall)
 {
-	unsigned long signr, mask = ~current->blocked;
-	struct sigaction *sa;
+	unsigned long signr;
+	siginfo_t info;
+	struct k_sigaction *ka;
+	
+	if (!oldset)
+		oldset = &current->blocked;
 
 #ifdef CONFIG_SPARC32_COMPAT
 	if (current->tss.flags & SPARC_FLAG_32BIT) {
-		extern asmlinkage int do_signal32(unsigned long, struct pt_regs *,
+		extern asmlinkage int do_signal32(sigset_t *, struct pt_regs *,
 						  unsigned long, int);
-		return do_signal32(oldmask, regs, orig_i0, restart_syscall);
+		return do_signal32(oldset, regs, orig_i0, restart_syscall);
 	}
 #endif	
-	while ((signr = current->signal & mask) != 0) {
-		signr = ffz(~signr);
-
+	for (;;) {
 		spin_lock_irq(&current->sigmask_lock);
-		current->signal &= ~(1 << signr);
+		signr = dequeue_signal(&current->blocked, &info);
 		spin_unlock_irq(&current->sigmask_lock);
+		
+		if (!signr) break;
 
-		sa = current->sig->action + signr;
-		signr++;
 		if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
 			current->exit_code = signr;
 			current->state = TASK_STOPPED;
@@ -511,15 +734,26 @@
 			current->exit_code = 0;
 			if (signr == SIGSTOP)
 				continue;
-			if (_S(signr) & current->blocked) {
-				spin_lock_irq(&current->sigmask_lock);
-				current->signal |= _S(signr);
-				spin_unlock_irq(&current->sigmask_lock);
+
+			/* Update the siginfo structure.  Is this good?  */
+			if (signr != info.si_signo) {
+				info.si_signo = signr;
+				info.si_errno = 0;
+				info.si_code = SI_USER;
+				info.si_pid = current->p_pptr->pid;
+				info.si_uid = current->p_pptr->uid;
+			}
+
+			/* If the (new) signal is now blocked, requeue it.  */
+			if (sigismember(&current->blocked, signr)) {
+				send_sig_info(signr, &info, current);
 				continue;
 			}
-			sa = current->sig->action + signr - 1;
 		}
-		if(sa->sa_handler == SIG_IGN) {
+		
+		ka = &current->sig->action[signr-1];
+		
+		if(ka->sa.sa_handler == SIG_IGN) {
 			if(signr != SIGCHLD)
 				continue;
 
@@ -532,7 +766,9 @@
                                 ;
 			continue;
 		}
-		if(sa->sa_handler == SIG_DFL) {
+		if(ka->sa.sa_handler == SIG_DFL) {
+			unsigned long exit_code = signr;
+			
 			if(current->pid == 1)
 				continue;
 			switch(signr) {
@@ -548,7 +784,7 @@
 					continue;
 				current->state = TASK_STOPPED;
 				current->exit_code = signr;
-				if(!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags &
+				if(!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags &
 				     SA_NOCLDSTOP))
 					notify_parent(current, SIGCHLD);
 				schedule();
@@ -559,7 +795,7 @@
 				if(current->binfmt && current->binfmt->core_dump) {
 					lock_kernel();
 					if(current->binfmt->core_dump(signr, regs))
-						signr |= 0x80;
+						exit_code |= 0x80;
 					unlock_kernel();
 				}
 #ifdef DEBUG_SIGNALS
@@ -569,20 +805,16 @@
 #endif
 				/* fall through */
 			default:
-				spin_lock_irq(&current->sigmask_lock);
-				current->signal |= _S(signr & 0x7f);
-				spin_unlock_irq(&current->sigmask_lock);
-
-				current->flags |= PF_SIGNALED;
-
 				lock_kernel();
-				do_exit(signr);
-				unlock_kernel();
+				sigaddset(&current->signal, signr);
+				current->flags |= PF_SIGNALED;
+				do_exit(exit_code);
+				/* NOT REACHED */
 			}
 		}
 		if(restart_syscall)
-			syscall_restart(orig_i0, regs, sa);
-		handle_signal(signr, sa, oldmask, regs);
+			syscall_restart(orig_i0, regs, &ka->sa);
+		handle_signal(signr, ka, &info, oldset, regs);
 		return 1;
 	}
 	if(restart_syscall &&
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/signal32.c linux/arch/sparc64/kernel/signal32.c
--- v2.1.78/linux/arch/sparc64/kernel/signal32.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/signal32.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/*  $Id: signal32.c,v 1.30 1997/08/29 15:51:33 jj Exp $
+/*  $Id: signal32.c,v 1.34 1997/12/15 15:04:49 jj Exp $
  *  arch/sparc64/kernel/signal32.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -26,14 +26,12 @@
 #include <asm/fpumacro.h>
 #include <asm/smp_lock.h>
 
-#define _S(nr) (1<<((nr)-1))
-
-#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
 asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
 			 int options, unsigned long *ru);
 
-asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs,
+asmlinkage int do_signal32(sigset_t *oldset, struct pt_regs *regs,
 			 unsigned long orig_o0, int ret_from_syscall);
 
 /* This turned off for production... */
@@ -60,6 +58,7 @@
 	/* struct sigcontext32 * */ u32 sig_scptr;
 	int sig_address;
 	struct sigcontext32 sig_context;
+	unsigned extramask[_NSIG_WORDS32 - 1];
 };
 
 /* 
@@ -72,24 +71,38 @@
 	__siginfo32_t		info;
 	/* __siginfo_fpu32_t * */ u32 fpu_save;
 	unsigned int		insns [2];
+	unsigned		extramask[_NSIG_WORDS32 - 1];
+	__siginfo_fpu_t		fpu_state;
+};
+
+struct rt_signal_frame32 {
+	struct sparc_stackf32	ss;
+	siginfo_t32		info;
+	struct pt_regs32	regs;
+	sigset_t32		mask;
+	/* __siginfo_fpu32_t * */ u32 fpu_save;
+	unsigned int		insns [2];
 	__siginfo_fpu_t		fpu_state;
 };
 
 /* Align macros */
 #define SF_ALIGNEDSZ  (((sizeof(struct signal_sframe32) + 7) & (~7)))
 #define NF_ALIGNEDSZ  (((sizeof(struct new_signal_frame32) + 7) & (~7)))
+#define RT_ALIGNEDSZ  (((sizeof(struct rt_signal_frame32) + 7) & (~7)))
 
 /*
  * atomically swap in the new signal mask, and wait for a signal.
  * This is really tricky on the Sparc, watch out...
  */
-asmlinkage void _sigpause32_common(unsigned int set, struct pt_regs *regs)
+asmlinkage void _sigpause32_common(old_sigset_t32 set, struct pt_regs *regs)
 {
-	unsigned int mask;
+	sigset_t saveset;
 
+	set &= _BLOCKABLE;
 	spin_lock_irq(&current->sigmask_lock);
-	mask = current->blocked;
-	current->blocked = set & _BLOCKABLE;
+	saveset = current->blocked;
+	siginitset(&current->blocked, set);
+	recalc_sigpending(current);
 	spin_unlock_irq(&current->sigmask_lock);
 	
 	regs->tpc = regs->tnpc;
@@ -109,7 +122,58 @@
 		 */
 		regs->tstate |= TSTATE_ICARRY;
 		regs->u_regs[UREG_I0] = EINTR;
-		if (do_signal32(mask, regs, 0, 0))
+		if (do_signal32(&saveset, regs, 0, 0))
+			return;
+	}
+}
+
+asmlinkage void do_rt_sigsuspend32(u32 uset, size_t sigsetsize, struct pt_regs *regs)
+{
+	sigset_t oldset, set;
+	sigset_t32 set32;
+        
+	/* XXX: Don't preclude handling different sized sigset_t's.  */
+	if (((__kernel_size_t32)sigsetsize) != sizeof(sigset_t)) {
+		regs->tstate |= TSTATE_ICARRY;
+		regs->u_regs[UREG_I0] = EINVAL;
+		return;
+	}
+	if (copy_from_user(&set32, (void *)(long)uset, sizeof(set32))) {
+		regs->tstate |= TSTATE_ICARRY;
+		regs->u_regs[UREG_I0] = EFAULT;
+		return;
+	}
+	switch (_NSIG_WORDS) {
+	case 4: set.sig[3] = set32.sig[6] + (((long)set32.sig[7]) << 32);
+	case 3: set.sig[2] = set32.sig[4] + (((long)set32.sig[5]) << 32);
+	case 2: set.sig[1] = set32.sig[2] + (((long)set32.sig[3]) << 32);
+	case 1: set.sig[0] = set32.sig[0] + (((long)set32.sig[1]) << 32);
+	}
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	oldset = current->blocked;
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+	
+	regs->tpc = regs->tnpc;
+	regs->tnpc += 4;
+
+	/* Condition codes and return value where set here for sigpause,
+	 * and so got used by setup_frame, which again causes sigreturn()
+	 * to return -EINTR.
+	 */
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		/*
+		 * Return -EINTR and set condition code here,
+		 * so the interrupted system call actually returns
+		 * these.
+		 */
+		regs->tstate |= TSTATE_ICARRY;
+		regs->u_regs[UREG_I0] = EINTR;
+		if (do_signal32(&oldset, regs, 0, 0))
 			return;
 	}
 }
@@ -134,7 +198,9 @@
 {
 	struct new_signal_frame32 *sf;
 	unsigned int psr;
-	unsigned pc, npc, fpu_save, mask;
+	unsigned pc, npc, fpu_save;
+	sigset_t set;
+	unsigned seta[_NSIG_WORDS32];
 	
 	regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
 	sf = (struct new_signal_frame32 *) regs->u_regs [UREG_FP];
@@ -177,16 +243,23 @@
 	regs->tstate &= ~(TSTATE_ICC);
 	regs->tstate |= psr_to_tstate_icc(psr);
 
-#if 0
-	if (psr & PSR_EF)
-		regs->tstate |= TSTATE_PEF;
-#endif		
-
 	__get_user(fpu_save, &sf->fpu_save);
 	if (fpu_save)
 		restore_fpu_state32(regs, &sf->fpu_state);
-	__get_user(mask, &sf->info.si_mask);
-	current->blocked = mask & _BLOCKABLE;
+	if (__get_user(seta[0], &sf->info.si_mask) ||
+	    copy_from_user(seta+1, &sf->extramask, (_NSIG_WORDS32 - 1) * sizeof(unsigned)))
+	    	goto segv;
+	switch (_NSIG_WORDS) {
+		case 4: set.sig[3] = seta[6] + (((long)seta[7]) << 32);
+		case 3: set.sig[2] = seta[4] + (((long)seta[5]) << 32);
+		case 2: set.sig[1] = seta[2] + (((long)seta[3]) << 32);
+		case 1: set.sig[0] = seta[0] + (((long)seta[1]) << 32);
+	}
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
 	return;
 segv:
 	lock_kernel();
@@ -197,7 +270,8 @@
 {
 	struct sigcontext32 *scptr;
 	unsigned pc, npc, psr;
-	unsigned long mask;
+	sigset_t set;
+	unsigned seta[_NSIG_WORDS32];
 
 	synchronize_user_stack();
 	if (current->tss.new_signal)
@@ -216,8 +290,22 @@
 	if((pc | npc) & 3)
 		goto segv; /* Nice try. */
 
-	__get_user(mask, &scptr->sigc_mask);
-	current->blocked = (mask & _BLOCKABLE);
+	if (__get_user(seta[0], &scptr->sigc_mask) ||
+	    /* Note that scptr + 1 points to extramask */
+	    copy_from_user(seta+1, scptr + 1, (_NSIG_WORDS32 - 1) * sizeof(unsigned)))
+	    	goto segv;
+	switch (_NSIG_WORDS) {
+		case 4: set.sig[3] = seta[6] + (((long)seta[7]) << 32);
+		case 3: set.sig[2] = seta[4] + (((long)seta[5]) << 32);
+		case 2: set.sig[1] = seta[2] + (((long)seta[3]) << 32);
+		case 1: set.sig[0] = seta[0] + (((long)seta[1]) << 32);
+	}
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+	
 	__get_user(current->tss.sstk_info.cur_status, &scptr->sigc_onstack);
 	current->tss.sstk_info.cur_status &= 1;
 	regs->tpc = pc;
@@ -236,6 +324,78 @@
 	do_exit (SIGSEGV);
 }
 
+asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
+{
+	struct rt_signal_frame32 *sf;
+	unsigned int psr;
+	unsigned pc, npc, fpu_save;
+	sigset_t set;
+	sigset_t32 seta;
+	
+	synchronize_user_stack();
+	regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
+	sf = (struct rt_signal_frame32 *) regs->u_regs [UREG_FP];
+
+	/* 1. Make sure we are not getting garbage from the user */
+	if (verify_area (VERIFY_READ, sf, sizeof (*sf))	||
+	    (((unsigned long) sf) & 3))
+		goto segv;
+
+	get_user(pc, &sf->regs.pc);
+	__get_user(npc, &sf->regs.npc);
+
+	if ((pc | npc) & 3)
+		goto segv;
+
+	regs->tpc = pc;
+	regs->tnpc = npc;
+
+	/* 2. Restore the state */
+	__get_user(regs->y, &sf->regs.y);
+	__get_user(psr, &sf->regs.psr);
+
+	__get_user(regs->u_regs[UREG_G1], &sf->regs.u_regs[UREG_G1]);
+	__get_user(regs->u_regs[UREG_G2], &sf->regs.u_regs[UREG_G2]);
+	__get_user(regs->u_regs[UREG_G3], &sf->regs.u_regs[UREG_G3]);
+	__get_user(regs->u_regs[UREG_G4], &sf->regs.u_regs[UREG_G4]);
+	__get_user(regs->u_regs[UREG_G5], &sf->regs.u_regs[UREG_G5]);
+	__get_user(regs->u_regs[UREG_G6], &sf->regs.u_regs[UREG_G6]);
+	__get_user(regs->u_regs[UREG_G7], &sf->regs.u_regs[UREG_G7]);
+	__get_user(regs->u_regs[UREG_I0], &sf->regs.u_regs[UREG_I0]);
+	__get_user(regs->u_regs[UREG_I1], &sf->regs.u_regs[UREG_I1]);
+	__get_user(regs->u_regs[UREG_I2], &sf->regs.u_regs[UREG_I2]);
+	__get_user(regs->u_regs[UREG_I3], &sf->regs.u_regs[UREG_I3]);
+	__get_user(regs->u_regs[UREG_I4], &sf->regs.u_regs[UREG_I4]);
+	__get_user(regs->u_regs[UREG_I5], &sf->regs.u_regs[UREG_I5]);
+	__get_user(regs->u_regs[UREG_I6], &sf->regs.u_regs[UREG_I6]);
+	__get_user(regs->u_regs[UREG_I7], &sf->regs.u_regs[UREG_I7]);
+
+	/* User can only change condition codes in %tstate. */
+	regs->tstate &= ~(TSTATE_ICC);
+	regs->tstate |= psr_to_tstate_icc(psr);
+
+	__get_user(fpu_save, &sf->fpu_save);
+	if (fpu_save)
+		restore_fpu_state32(regs, &sf->fpu_state);
+	if (copy_from_user(&seta, &sf->mask, sizeof(sigset_t32)))
+	    	goto segv;
+	switch (_NSIG_WORDS) {
+		case 4: set.sig[3] = seta.sig[6] + (((long)seta.sig[7]) << 32);
+		case 3: set.sig[2] = seta.sig[4] + (((long)seta.sig[5]) << 32);
+		case 2: set.sig[1] = seta.sig[2] + (((long)seta.sig[3]) << 32);
+		case 1: set.sig[0] = seta.sig[0] + (((long)seta.sig[1]) << 32);
+	}
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+	return;
+segv:
+	lock_kernel();
+	do_exit(SIGSEGV);
+}
+
 /* Checks if the fp is valid */
 static int invalid_frame_pointer(void *fp, int fplen)
 {
@@ -246,10 +406,12 @@
 
 static void
 setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc,
-	      struct pt_regs *regs, int signr, unsigned long oldmask)
+	      struct pt_regs *regs, int signr, sigset_t *oldset)
 {
 	struct signal_sframe32 *sframep;
 	struct sigcontext32 *sc;
+	unsigned seta[_NSIG_WORDS32];
+	
 #if 0	
 	int window = 0;
 #endif	
@@ -278,7 +440,19 @@
 
 	/* We've already made sure frame pointer isn't in kernel space... */
 	__put_user(old_status, &sc->sigc_onstack);
-	__put_user(oldmask, &sc->sigc_mask);
+	
+	switch (_NSIG_WORDS) {
+	case 4: seta[7] = (oldset->sig[3] >> 32);
+	        seta[6] = oldset->sig[3];
+	case 3: seta[5] = (oldset->sig[2] >> 32);
+	        seta[4] = oldset->sig[2];
+	case 2: seta[3] = (oldset->sig[1] >> 32);
+	        seta[2] = oldset->sig[1];
+	case 1: seta[1] = (oldset->sig[0] >> 32);
+	        seta[0] = oldset->sig[0];
+	}
+	__put_user(seta[0], &sc->sigc_mask);
+	__copy_to_user(sframep->extramask, seta + 1, (_NSIG_WORDS32 - 1) * sizeof(unsigned));
 	__put_user(regs->u_regs[UREG_FP], &sc->sigc_sp);
 	__put_user(pc, &sc->sigc_pc);
 	__put_user(npc, &sc->sigc_npc);
@@ -346,13 +520,14 @@
 	regs->tstate &= ~TSTATE_PEF;
 }
 
-static inline void new_setup_frame32(struct sigaction *sa, struct pt_regs *regs,
-				     int signo, unsigned long oldmask)
+static inline void new_setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,
+				     int signo, sigset_t *oldset)
 {
 	struct new_signal_frame32 *sf;
 	int sigframe_size;
 	u32 psr;
 	int i;
+	unsigned seta[_NSIG_WORDS32];
 
 	/* 1. Make sure everything is clean */
 	synchronize_user_stack();
@@ -397,33 +572,47 @@
 		__put_user(0, &sf->fpu_save);
 	}
 
-	__put_user(oldmask, &sf->info.si_mask);
+	switch (_NSIG_WORDS) {
+	case 4: seta[7] = (oldset->sig[3] >> 32);
+	        seta[6] = oldset->sig[3];
+	case 3: seta[5] = (oldset->sig[2] >> 32);
+	        seta[4] = oldset->sig[2];
+	case 2: seta[3] = (oldset->sig[1] >> 32);
+	        seta[2] = oldset->sig[1];
+	case 1: seta[1] = (oldset->sig[0] >> 32);
+	        seta[0] = oldset->sig[0];
+	}
+	__put_user(seta[0], &sf->info.si_mask);
+	__copy_to_user(sf->extramask, seta + 1, (_NSIG_WORDS32 - 1) * sizeof(unsigned));
 
 	copy_in_user((u32 *)sf,
 		     (u32 *)(regs->u_regs[UREG_FP]),
 		     sizeof(struct reg_window32));
 	
-	/* 3. return to kernel instructions */
-	__put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */
-	__put_user(0x91d02010, &sf->insns[1]); /* t 0x10 */
-
-	/* 4. signal handler back-trampoline and parameters */
+	/* 3. signal handler back-trampoline and parameters */
 	regs->u_regs[UREG_FP] = (unsigned long) sf;
 	regs->u_regs[UREG_I0] = signo;
 	regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
-	regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
 
-	/* 5. signal handler */
-	regs->tpc = (unsigned long) sa->sa_handler;
+	/* 4. signal handler */
+	regs->tpc = (unsigned long) ka->sa.sa_handler;
 	regs->tnpc = (regs->tpc + 4);
 
-	/* Flush instruction space. */
-	{
+	/* 5. return to kernel instructions */
+	if (ka->ka_restorer)
+		regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
+	else {
+		/* Flush instruction space. */
 		unsigned long address = ((unsigned long)&(sf->insns[0]));
 		pgd_t *pgdp = pgd_offset(current->mm, address);
 		pmd_t *pmdp = pmd_offset(pgdp, address);
 		pte_t *ptep = pte_offset(pmdp, address);
 
+		regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
+	
+		__put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */
+		__put_user(0x91d02010, &sf->insns[1]); /* t 0x10 */
+
 		if(pte_present(*ptep)) {
 			unsigned long page = pte_page(*ptep);
 
@@ -444,7 +633,7 @@
 /* Setup a Solaris stack frame */
 static inline void
 setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc,
-		   struct pt_regs *regs, int signr, unsigned long oldmask)
+		   struct pt_regs *regs, int signr, sigset_t *oldset)
 {
 	svr4_signal_frame_t *sfp;
 	svr4_gregset_t  *gr;
@@ -452,6 +641,7 @@
 	svr4_mcontext_t *mc;
 	svr4_gwindows_t *gw;
 	svr4_ucontext_t *uc;
+	svr4_sigset_t setv;
 #if 0	
 	int window = 0;
 #endif	
@@ -485,8 +675,15 @@
 	 * sc->sigc_onstack = old_status;
 	 * anyways, it does not look like it is used for anything at all.
 	 */
-	__put_user(oldmask, &uc->sigmask.sigbits [0]);
-
+	setv.sigbits[0] = oldset->sig[0];
+	setv.sigbits[1] = (oldset->sig[0] >> 32);
+	if (_NSIG_WORDS >= 2) {
+		setv.sigbits[2] = oldset->sig[1];
+		setv.sigbits[3] = (oldset->sig[1] >> 32);
+		__copy_to_user(&uc->sigmask, &setv, sizeof(svr4_sigset_t));
+	} else
+		__copy_to_user(&uc->sigmask, &setv, 2 * sizeof(unsigned));
+	
 	/* Store registers */
 	__put_user(regs->tpc, &((*gr) [SVR4_PC]));
 	__put_user(regs->tnpc, &((*gr) [SVR4_NPC]));
@@ -572,11 +769,12 @@
 {
 	svr4_gregset_t  *gr;
 	svr4_mcontext_t *mc;
+	svr4_sigset_t setv;
 	int i;
 
 	synchronize_user_stack();
 	if (current->tss.w_saved){
-		printk ("Uh oh, w_saved is not zero (%ld)\n", current->tss.w_saved);
+		printk ("Uh oh, w_saved is not zero (%d)\n", (int) current->tss.w_saved);
 		lock_kernel();
 		do_exit (SIGSEGV);
 	}
@@ -586,9 +784,15 @@
 	/* Setup convenience variables */
 	mc = &uc->mcontext;
 	gr = &mc->greg;
-	
-	/* We only have < 32 signals, fill the first slot only */
-	__put_user(current->blocked, &uc->sigmask.sigbits [0]);
+
+	setv.sigbits[0] = current->blocked.sig[0];
+	setv.sigbits[1] = (current->blocked.sig[0] >> 32);
+	if (_NSIG_WORDS >= 2) {
+		setv.sigbits[2] = current->blocked.sig[1];
+		setv.sigbits[3] = (current->blocked.sig[1] >> 32);
+		__copy_to_user(&uc->sigmask, &setv, sizeof(svr4_sigset_t));
+	} else
+		__copy_to_user(&uc->sigmask, &setv, 2 * sizeof(unsigned));
 
 	/* Store registers */
 	__put_user(regs->tpc, &uc->mcontext.greg [SVR4_PC]);
@@ -622,6 +826,8 @@
 	struct thread_struct *tp = &current->tss;
 	svr4_gregset_t  *gr;
 	u32 pc, npc, psr;
+	sigset_t set;
+	svr4_sigset_t setv;
 	int i;
 	
 	/* Fixme: restore windows, or is this already taken care of in
@@ -630,7 +836,7 @@
 	flush_user_windows();
 	
 	if (tp->w_saved){
-		printk ("Uh oh, w_saved is: 0x%lx\n", tp->w_saved);
+		printk ("Uh oh, w_saved is: 0x%x\n", tp->w_saved);
 		goto sigsegv;
 	}
 	if (((unsigned long) c) & 3){
@@ -654,8 +860,16 @@
 	/* Retrieve information from passed ucontext */
 	    /* note that nPC is ored a 1, this is used to inform entry.S */
 	    /* that we don't want it to mess with our PC and nPC */
-	__get_user(current->blocked, &c->sigmask.sigbits [0]);
-	current->blocked &= _BLOCKABLE;
+	if (copy_from_user (&setv, &c->sigmask, sizeof(svr4_sigset_t)))
+		goto sigsegv;
+	set.sig[0] = setv.sigbits[0] | (((long)setv.sigbits[1]) << 32);
+	if (_NSIG_WORDS >= 2)
+		set.sig[1] = setv.sigbits[2] | (((long)setv.sigbits[3]) << 32);
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
 	regs->tpc = pc;
 	regs->tnpc = npc | 1;
 	__get_user(regs->y, &((*gr) [SVR4_Y]));
@@ -678,23 +892,137 @@
 	do_exit(SIGSEGV);
 }
 
-static inline void handle_signal32(unsigned long signr, struct sigaction *sa,
-				   unsigned long oldmask, struct pt_regs *regs,
+static inline void setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs,
+				        unsigned long signr, sigset_t *oldset,
+				        siginfo_t *info)
+{
+	struct rt_signal_frame32 *sf;
+	int sigframe_size;
+	u32 psr;
+	int i;
+	sigset_t32 seta;
+
+	/* 1. Make sure everything is clean */
+	synchronize_user_stack();
+	sigframe_size = RT_ALIGNEDSZ;
+	if (!(current->tss.flags & SPARC_FLAG_USEDFPU))
+		sigframe_size -= sizeof(__siginfo_fpu_t);
+
+	regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
+	sf = (struct rt_signal_frame32 *)(regs->u_regs[UREG_FP] - sigframe_size);
+	
+	if (invalid_frame_pointer (sf, sigframe_size)) {
+#ifdef DEBUG_SIGNALS
+		printk("rt_setup_frame32(%s:%d): invalid_frame_pointer(%p, %d)\n",
+		       current->comm, current->pid, sf, sigframe_size);
+#endif
+		goto sigill;
+	}
+
+	if (current->tss.w_saved != 0) {
+#ifdef DEBUG_SIGNALS
+		printk ("%s[%d]: Invalid user stack frame for "
+			"signal delivery.\n", current->comm, current->pid);
+#endif
+		goto sigill;
+	}
+
+	/* 2. Save the current process state */
+	put_user(regs->tpc, &sf->regs.pc);
+	__put_user(regs->tnpc, &sf->regs.npc);
+	__put_user(regs->y, &sf->regs.y);
+	psr = tstate_to_psr (regs->tstate);
+	if(current->tss.flags & SPARC_FLAG_USEDFPU)
+		psr |= PSR_EF;
+	__put_user(psr, &sf->regs.psr);
+	for (i = 0; i < 16; i++)
+		__put_user(regs->u_regs[i], &sf->regs.u_regs[i]);
+
+	if (psr & PSR_EF) {
+		save_fpu_state32(regs, &sf->fpu_state);
+		__put_user((u64)&sf->fpu_state, &sf->fpu_save);
+	} else {
+		__put_user(0, &sf->fpu_save);
+	}
+
+	switch (_NSIG_WORDS) {
+	case 4: seta.sig[7] = (oldset->sig[3] >> 32);
+	        seta.sig[6] = oldset->sig[3];
+	case 3: seta.sig[5] = (oldset->sig[2] >> 32);
+	        seta.sig[4] = oldset->sig[2];
+	case 2: seta.sig[3] = (oldset->sig[1] >> 32);
+	        seta.sig[2] = oldset->sig[1];
+	case 1: seta.sig[1] = (oldset->sig[0] >> 32);
+	        seta.sig[0] = oldset->sig[0];
+	}
+	__copy_to_user(&sf->mask, &seta, sizeof(sigset_t));
+
+	copy_in_user((u32 *)sf,
+		     (u32 *)(regs->u_regs[UREG_FP]),
+		     sizeof(struct reg_window32));
+	
+	/* 3. signal handler back-trampoline and parameters */
+	regs->u_regs[UREG_FP] = (unsigned long) sf;
+	regs->u_regs[UREG_I0] = signr;
+	regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
+
+	/* 4. signal handler */
+	regs->tpc = (unsigned long) ka->sa.sa_handler;
+	regs->tnpc = (regs->tpc + 4);
+
+	/* 5. return to kernel instructions */
+	if (ka->ka_restorer)
+		regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
+	else {
+		/* Flush instruction space. */
+		unsigned long address = ((unsigned long)&(sf->insns[0]));
+		pgd_t *pgdp = pgd_offset(current->mm, address);
+		pmd_t *pmdp = pmd_offset(pgdp, address);
+		pte_t *ptep = pte_offset(pmdp, address);
+
+		regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
+	
+		__put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */
+		__put_user(0x91d02010, &sf->insns[1]); /* t 0x10 */
+
+		if(pte_present(*ptep)) {
+			unsigned long page = pte_page(*ptep);
+
+			__asm__ __volatile__("
+			membar	#StoreStore
+			flush	%0 + %1"
+			: : "r" (page), "r" (address & (PAGE_SIZE - 1))
+			: "memory");
+		}
+	}
+	return;
+
+sigill:
+	lock_kernel();
+	do_exit(SIGILL);
+}
+
+static inline void handle_signal32(unsigned long signr, struct k_sigaction *ka,
+				   siginfo_t *info,
+				   sigset_t *oldset, struct pt_regs *regs,
 				   int svr4_signal)
 {
 	if(svr4_signal)
-		setup_svr4_frame32(sa, regs->tpc, regs->tnpc, regs, signr, oldmask);
+		setup_svr4_frame32(&ka->sa, regs->tpc, regs->tnpc, regs, signr, oldset);
 	else {
-		if (current->tss.new_signal)
-			new_setup_frame32(sa, regs, signr, oldmask);
+		if (ka->sa.sa_flags & SA_SIGINFO)
+			setup_rt_frame32(ka, regs, signr, oldset, info);
+		else if (current->tss.new_signal)
+			new_setup_frame32(ka, regs, signr, oldset);
 		else
-			setup_frame32(sa, regs->tpc, regs->tnpc, regs, signr, oldmask);
+			setup_frame32(&ka->sa, regs->tpc, regs->tnpc, regs, signr, oldset);
 	}
-	if(sa->sa_flags & SA_ONESHOT)
-		sa->sa_handler = NULL;
-	if(!(sa->sa_flags & SA_NOMASK)) {
+	if(ka->sa.sa_flags & SA_ONESHOT)
+		ka->sa.sa_handler = SIG_DFL;
+	if(!(ka->sa.sa_flags & SA_NOMASK)) {
 		spin_lock_irq(&current->sigmask_lock);
-		current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE;
+		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+		sigaddset(&current->blocked,signr);
 		spin_unlock_irq(&current->sigmask_lock);
 	}
 }
@@ -723,22 +1051,22 @@
  * want to handle. Thus you cannot kill init even with a SIGKILL even by
  * mistake.
  */
-asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs,
+asmlinkage int do_signal32(sigset_t *oldset, struct pt_regs * regs,
 			   unsigned long orig_i0, int restart_syscall)
 {
-	unsigned long signr, mask = ~current->blocked;
-	struct sigaction *sa;
+	unsigned long signr;
+	struct k_sigaction *ka;
+	siginfo_t info;
+	
 	int svr4_signal = current->personality == PER_SVR4;
 	
-	while ((signr = current->signal & mask) != 0) {
-		signr = ffz(~signr);
-
+	for (;;) {
 		spin_lock_irq(&current->sigmask_lock);
-		current->signal &= ~(1 << signr);
+		signr = dequeue_signal(&current->blocked, &info);
 		spin_unlock_irq(&current->sigmask_lock);
+		
+		if (!signr) break;
 
-		sa = current->sig->action + signr;
-		signr++;
 		if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
 			current->exit_code = signr;
 			current->state = TASK_STOPPED;
@@ -749,15 +1077,26 @@
 			current->exit_code = 0;
 			if (signr == SIGSTOP)
 				continue;
-			if (_S(signr) & current->blocked) {
-				spin_lock_irq(&current->sigmask_lock);
-				current->signal |= _S(signr);
-				spin_unlock_irq(&current->sigmask_lock);
+
+			/* Update the siginfo structure.  Is this good?  */
+			if (signr != info.si_signo) {
+				info.si_signo = signr;
+				info.si_errno = 0;
+				info.si_code = SI_USER;
+				info.si_pid = current->p_pptr->pid;
+				info.si_uid = current->p_pptr->uid;
+			}
+
+			/* If the (new) signal is now blocked, requeue it.  */
+			if (sigismember(&current->blocked, signr)) {
+				send_sig_info(signr, &info, current);
 				continue;
 			}
-			sa = current->sig->action + signr - 1;
 		}
-		if(sa->sa_handler == SIG_IGN) {
+		
+		ka = &current->sig->action[signr-1];
+		
+		if(ka->sa.sa_handler == SIG_IGN) {
 			if(signr != SIGCHLD)
 				continue;
 
@@ -770,7 +1109,9 @@
                                 ;
 			continue;
 		}
-		if(sa->sa_handler == SIG_DFL) {
+		if(ka->sa.sa_handler == SIG_DFL) {
+			unsigned long exit_code = signr;
+			
 			if(current->pid == 1)
 				continue;
 			switch(signr) {
@@ -786,7 +1127,7 @@
 					continue;
 				current->state = TASK_STOPPED;
 				current->exit_code = signr;
-				if(!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags &
+				if(!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags &
 				     SA_NOCLDSTOP))
 					notify_parent(current, SIGCHLD);
 				schedule();
@@ -797,7 +1138,7 @@
 				if(current->binfmt && current->binfmt->core_dump) {
 					lock_kernel();
 					if(current->binfmt->core_dump(signr, regs))
-						signr |= 0x80;
+						exit_code |= 0x80;
 					unlock_kernel();
 				}
 #ifdef DEBUG_SIGNALS
@@ -807,20 +1148,16 @@
 #endif
 				/* fall through */
 			default:
-				spin_lock_irq(&current->sigmask_lock);
-				current->signal |= _S(signr & 0x7f);
-				spin_unlock_irq(&current->sigmask_lock);
-
-				current->flags |= PF_SIGNALED;
-
 				lock_kernel();
-				do_exit(signr);
-				unlock_kernel();
+				sigaddset(&current->signal, signr);
+				current->flags |= PF_SIGNALED;
+				do_exit(exit_code);
+				/* NOT REACHED */
 			}
 		}
 		if(restart_syscall)
-			syscall_restart32(orig_i0, regs, sa);
-		handle_signal32(signr, sa, oldmask, regs, svr4_signal);
+			syscall_restart32(orig_i0, regs, &ka->sa);
+		handle_signal32(signr, ka, &info, oldset, regs, svr4_signal);
 		return 1;
 	}
 	if(restart_syscall &&
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c
--- v2.1.78/linux/arch/sparc64/kernel/smp.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/smp.c	Mon Jan 12 15:15:44 1998
@@ -3,6 +3,7 @@
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
  */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/tasks.h>
@@ -23,6 +24,7 @@
 #include <asm/spinlock.h>
 #include <asm/hardirq.h>
 #include <asm/softirq.h>
+#include <asm/uaccess.h>
 
 #define __KERNEL_SYSCALLS__
 #include <linux/unistd.h>
@@ -346,7 +348,7 @@
 	spin_lock(&scheduler_lock);
 	get_new_mmu_context(mm, &tlb_context_cache);
 	mm->cpu_vm_mask = (1UL << smp_processor_id());
-	if(current->tss.current_ds) {
+	if(segment_eq(current->tss.current_ds,USER_DS)) {
 		u32 ctx = mm->context & 0x1fff;
 
 		current->tss.ctx = ctx;
@@ -473,6 +475,7 @@
 
 static inline void sparc64_do_profile(unsigned long pc)
 {
+#ifdef CONFIG_PROFILE
 	if(prof_buffer && current->pid) {
 		extern int _stext;
 
@@ -483,6 +486,7 @@
 			pc = prof_len - 1;
 		atomic_inc((atomic_t *)&prof_buffer[pc]);
 	}
+#endif
 }
 
 static unsigned long real_tick_offset, current_tick_offset;
@@ -510,7 +514,7 @@
 				update_one_process(current, 1, user, !user);
 				if(--current->counter < 0) {
 					current->counter = 0;
-					resched_force();
+					need_resched = 1;
 				}
 
 				if(user) {
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/sparc64_ksyms.c linux/arch/sparc64/kernel/sparc64_ksyms.c
--- v2.1.78/linux/arch/sparc64/kernel/sparc64_ksyms.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/sparc64_ksyms.c	Mon Jan 12 15:15:44 1998
@@ -1,10 +1,12 @@
-/* $Id: sparc64_ksyms.c,v 1.21 1997/09/03 12:29:07 jj Exp $
+/* $Id: sparc64_ksyms.c,v 1.27 1997/11/19 07:57:46 jj Exp $
  * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
  */
 
+/* Tell string.h we don't want memcpy etc. as cpp defines */
+#define EXPORT_SYMTAB_STROPS
 #define PROMLIB_INTERNAL
 
 #include <linux/config.h>
@@ -12,6 +14,7 @@
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/in6.h>
+#include <linux/pci.h>
 
 #include <asm/oplib.h>
 #include <asm/delay.h>
@@ -30,10 +33,14 @@
 #include <asm/user.h>
 #include <asm/uaccess.h>
 #include <asm/checksum.h>
+#include <asm/fpumacro.h>
 #ifdef CONFIG_SBUS
 #include <asm/sbus.h>
 #include <asm/dma.h>
 #endif
+#ifdef CONFIG_PCI
+#include <asm/ebus.h>
+#endif
 #include <asm/a.out.h>
 #include <asm/svr4.h>
 
@@ -43,6 +50,7 @@
 	short revents;
 };
 
+extern void die_if_kernel(char *str, struct pt_regs *regs);
 extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long,
 				unsigned long, unsigned long, unsigned long);
 void _sigpause_common (unsigned int set, struct pt_regs *);
@@ -68,6 +76,7 @@
 extern int svr4_setcontext(svr4_ucontext_t *uc, struct pt_regs *regs);
 extern int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);
 extern int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg);
+extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *);
                 
 extern void bcopy (const char *, char *, int);
 extern int __ashrdi3(int, int);
@@ -123,6 +132,10 @@
 EXPORT_SYMBOL(SBus_chain);
 EXPORT_SYMBOL(dma_chain);
 #endif
+#if CONFIG_PCI
+EXPORT_SYMBOL(ebus_chain);
+EXPORT_SYMBOL(pci_devices);
+#endif
 
 /* Solaris/SunOS binary compatibility */
 EXPORT_SYMBOL(_sigpause_common);
@@ -131,6 +144,9 @@
 /* Should really be in linux/kernel/ksyms.c */
 EXPORT_SYMBOL(dump_thread);
 
+/* math-emu wants this */
+EXPORT_SYMBOL(die_if_kernel);
+
 /* prom symbols */
 EXPORT_SYMBOL(idprom);
 EXPORT_SYMBOL(prom_root_node);
@@ -188,6 +204,10 @@
 EXPORT_SYMBOL(linux_cpus);
 EXPORT_SYMBOL(sys_ioctl);
 EXPORT_SYMBOL(sys32_ioctl);
+#endif
+
+#ifdef CONFIG_MATHEMU_MODULE
+EXPORT_SYMBOL(handle_mathemu);
 #endif
 
 /* Special internal versions of library functions. */
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/sunos_ioctl32.c linux/arch/sparc64/kernel/sunos_ioctl32.c
--- v2.1.78/linux/arch/sparc64/kernel/sunos_ioctl32.c	Thu Jul 17 10:06:04 1997
+++ linux/arch/sparc64/kernel/sunos_ioctl32.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: sunos_ioctl32.c,v 1.4 1997/07/17 02:20:43 davem Exp $
+/* $Id: sunos_ioctl32.c,v 1.5 1997/09/18 10:37:57 rth Exp $
  * sunos_ioctl32.c: SunOS ioctl compatability on sparc64.
  *
  * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -100,7 +100,7 @@
 		goto out;
 
 	if(cmd == TIOCSETD) {
-		unsigned long old_fs = get_fs();
+		mm_segment_t old_fs = get_fs();
 		int *p, ntty = N_TTY;
 		int tmp;
 
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/sys32.S linux/arch/sparc64/kernel/sys32.S
--- v2.1.78/linux/arch/sparc64/kernel/sys32.S	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/sys32.S	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: sys32.S,v 1.3 1997/08/22 20:11:47 davem Exp $
+/* $Id: sys32.S,v 1.4 1997/09/09 17:13:29 jj Exp $
  * sys32.S: I-cache tricks for 32-bit compatability layer simple
  *          conversions.
  *
@@ -8,8 +8,7 @@
 	.text
 
 	.align		32
-	.globl		sys32_mmap, sys32_mprotect, sys32_munmap, sys32_msync
-	.globl		sys32_mlock, sys32_munlock, sys32_mremap, sparc32_brk
+	.globl		sys32_mmap
 sys32_mmap:
 	srl		%o0, 0, %o0			! IEU0	Group
 	sethi		%hi(0xffffffff), %g2		! IEU1
@@ -22,153 +21,15 @@
 	and		%o5, %g2, %o5			! IEU0	Group
 	call		sys_mmap			! CTI	Group brk forced
 	 mov		%g1, %o7			! IEU0	Group (regdep)
-sys32_mprotect:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	srl		%o2, 0, %o2
-	call		sys_mprotect
-	 mov		%g1, %o7
-sys32_munmap:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	call		sys_munmap
-	 mov		%g1, %o7
-sparc32_brk:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_brk
-	 mov		%g1, %o7
-sys32_msync:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	call		sys_msync
-	 mov		%g1, %o7
-sys32_mlock:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	call		sys_mlock
-	 mov		%g1, %o7
-sys32_munlock:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	call		sys_munlock
-	 mov		%g1, %o7
-sys32_mremap:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	srl		%o2, 0, %o2
-	srl		%o3, 0, %o3
-	call		sys_mremap
-	 mov		%g1, %o7
 
 	.align		32
-	.globl		sys32_read, sys32_write, sys32_open, sys32_access
-	.globl		sys32_chdir, sys32_lseek, sys32_llseek, sys32_poll
-	.globl		sys32_readlink, sys32_unlink, sys32_rmdir, sys32_symlink
-	.globl		sys32_link, sys32_rename, sys32_truncate, sys32_ftruncate
-	.globl		sys32_chroot, sys32_chmod, sys32_chown, sys32_creat
-	.globl		sys32_mkdir, sys32_mknod, sys32_ustat
-sys32_read:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	srl		%o2, 0, %o2
-	call		sys_read
-	 mov		%g1, %o7
-sys32_write:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	srl		%o2, 0, %o2
-	call		sys_write
-	 mov		%g1, %o7
-sys32_open:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_open
-	 mov		%g1, %o7
-sys32_access:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_access
-	 mov		%g1, %o7
-sys32_chdir:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_chdir
-	 mov		%g1, %o7
+	.globl		sys32_lseek
+	.globl		sys32_chmod, sys32_chown, sys32_mknod
 sys32_lseek:
 	sra		%o1, 0, %o1
 	mov		%o7, %g1
 	call		sys_lseek
 	 mov		%g1, %o7
-sys32_llseek:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	srl		%o2, 0, %o2
-	srl		%o3, 0, %o3
-	call		sys_llseek
-	 mov		%g1, %o7
-sys32_poll:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_poll
-	 mov		%g1, %o7
-sys32_readlink:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	call		sys_readlink
-	 mov		%g1, %o7
-sys32_unlink:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_unlink
-	 mov		%g1, %o7
-sys32_rmdir:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_rmdir
-	 mov		%g1, %o7
-sys32_symlink:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	call		sys_symlink
-	 mov		%g1, %o7
-sys32_link:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	call		sys_link
-	 mov		%g1, %o7
-sys32_rename:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	call		sys_rename
-	 mov		%g1, %o7
-	nop
-sys32_truncate:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	call		sys_truncate
-	 mov		%g1, %o7
-sys32_ftruncate:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	call		sys_ftruncate
-	 mov		%g1, %o7
-sys32_chroot:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_chroot
-	 mov		%g1, %o7
 sys32_chmod:
 	sll		%o1, 16, %o1
 	mov		%o7, %g1
@@ -185,16 +46,6 @@
 	srl		%o2, 16, %o2
 	call		sys_chown
 	 mov		%g1, %o7
-sys32_creat:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_creat
-	 mov		%g1, %o7
-sys32_mkdir:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_mkdir
-	 mov		%g1, %o7
 sys32_mknod:
 	sll		%o2, 16, %o2
 	mov		%o7, %g1
@@ -202,50 +53,9 @@
 	srl		%o2, 16, %o2
 	call		sys_mknod
 	 mov		%g1, %o7
-sys32_ustat:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	call		sys_ustat
-	  mov		%g1, %o7
 
 	.align		32
-	.globl		sys32_bind, sys32_accept, sys32_connect, sys32_getsockname
-	.globl		sys32_getpeername, sys32_send, sys32_sendto, sys32_recv
-	.globl		sys32_recvfrom, sys32_setsockopt, sys32_getsockopt
-sys32_bind:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	call		sys_bind
-	 mov		%g1, %o7
-sys32_accept:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	srl		%o2, 0, %o2
-	call		sys_accept
-	 mov		%g1, %o7
-sys32_connect:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	call		sys_connect
-	 mov		%g1, %o7
-sys32_getsockname:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	srl		%o2, 0, %o2
-	call		sys_getsockname
-	 mov		%g1, %o7
-sys32_getpeername:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	srl		%o2, 0, %o2
-	call		sys_getpeername
-	 mov		%g1, %o7
-sys32_send:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	srl		%o2, 0, %o2
-	call		sys_send
-	 mov		%g1, %o7
+	.globl		sys32_sendto, sys32_recvfrom, sys32_getsockopt
 sys32_sendto:
 	srl		%o1, 0, %o1
 	mov		%o7, %g1
@@ -253,12 +63,6 @@
 	srl		%o4, 0, %o4
 	call		sys_sendto
 	 mov		%g1, %o7
-sys32_recv:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	srl		%o2, 0, %o2
-	call		sys_recv
-	 mov		%g1, %o7
 sys32_recvfrom:
 	srl		%o1, 0, %o1
 	mov		%o7, %g1
@@ -267,11 +71,6 @@
 	srl		%o5, 0, %o5
 	call		sys_recvfrom
 	 mov		%g1, %o7
-sys32_setsockopt:
-	srl		%o3, 0, %o3
-	mov		%o7, %g1
-	call		sys_setsockopt
-	 mov		%g1, %o7
 sys32_getsockopt:
 	srl		%o3, 0, %o3
 	mov		%o7, %g1
@@ -279,111 +78,9 @@
 	call		sys_setsockopt
 	 mov		%g1, %o7
 
-	.globl		sys32_bdflush, sys32_uselib, sys32_umount, sys32_syslog
-	.globl		sys32_personality, sys32_waitpid
-	.globl		sys32_sched_setscheduler
-	.globl		sys32_sched_setparam, sys32_sched_getparam, sys32_signal
-	.globl		sys32_reboot, sys32_acct, sys32_newuname, sys32_olduname
-	.globl		sys32_sethostname, sys32_gethostname, sys32_setdomainname
-	.globl		sys32_time, sys32_swapoff, sys32_swapon
-	.globl		sys32_create_module, sys32_init_module, sys32_delete_module
+	.globl		sys32_bdflush
 sys32_bdflush:
 	sra		%o1, 0, %o1
 	mov		%o7, %g1
 	call		sys_bdflush
-	 mov		%g1, %o7
-sys32_uselib:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_uselib
-	 mov		%g1, %o7
-sys32_umount:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_umount
-	 mov		%g1, %o7
-sys32_syslog:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	call		sys_syslog
-	 mov		%g1, %o7
-sys32_personality:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_personality
-	 mov		%g1, %o7
-sys32_waitpid:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	call		sys_waitpid
-	 mov		%g1, %o7
-sys32_sched_setscheduler:
-	srl		%o2, 0, %o2
-	mov		%o7, %g1
-	call		sys_sched_setscheduler
-	 mov		%g1, %o7
-sys32_sched_setparam:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	call		sys_sched_setparam
-	 mov		%g1, %o7
-sys32_sched_getparam:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	call		sys_sched_getparam
-	 mov		%g1, %o7
-sys32_signal:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	call		sys_signal
-	 mov		%g1, %o7
-sys32_reboot:
-	srl		%o3, 0, %o3
-	mov		%o7, %g1
-	call		sys_reboot
-	 mov		%g1, %o7
-sys32_acct:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_acct
-	 mov		%g1, %o7
-sys32_newuname:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_newuname
-	 mov		%g1, %o7
-sys32_olduname:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_olduname
-	 mov		%g1, %o7
-sys32_sethostname:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_sethostname
-	 mov		%g1, %o7
-sys32_gethostname:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_gethostname
-	 mov		%g1, %o7
-sys32_setdomainname:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_setdomainname
-	 mov		%g1, %o7
-sys32_time:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_time
-	 mov		%g1, %o7
-sys32_swapoff:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_swapoff
-	 mov		%g1, %o7
-sys32_swapon:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	call		sys_swapon
 	 mov		%g1, %o7
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/sys_sparc.c linux/arch/sparc64/kernel/sys_sparc.c
--- v2.1.78/linux/arch/sparc64/kernel/sys_sparc.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/sys_sparc.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.5 1997/09/03 12:29:05 jj Exp $
+/* $Id: sys_sparc.c,v 1.9 1997/12/11 15:15:44 jj Exp $
  * linux/arch/sparc64/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -16,11 +16,14 @@
 #include <linux/shm.h>
 #include <linux/stat.h>
 #include <linux/mman.h>
+#include <linux/utsname.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/malloc.h>
 
 #include <asm/uaccess.h>
 #include <asm/ipc.h>
+#include <asm/utrap.h>
 
 /* XXX Make this per-binary type, this way we can detect the type of
  * XXX a binary.  Every Sparc executable calls this very early on.
@@ -219,39 +222,16 @@
 
 extern void check_pending(int signum);
 
-asmlinkage int
-sparc_sigaction (int signum, const struct sigaction *action, struct sigaction *oldaction)
+asmlinkage int sys_getdomainname(char *name, int len)
 {
-	struct sigaction new_sa, *p;
+        int nlen = strlen(system_utsname.domainname);
 
-	if(signum < 0) {
-		current->tss.new_signal = 1;
-		signum = -signum;
-	}
-	if (signum<1 || signum>32)
-		return -EINVAL;
-	p = signum - 1 + current->sig->action;
-	if (action) {
-		if (signum==SIGKILL || signum==SIGSTOP)
-			return -EINVAL;
-		if(copy_from_user(&new_sa, action, sizeof(struct sigaction)))
-			return -EFAULT;
-		if (new_sa.sa_handler != SIG_DFL && new_sa.sa_handler != SIG_IGN) {
-			int err = verify_area(VERIFY_READ, new_sa.sa_handler, 1);
-			if (err)
-				return err;
-		}
-	}
-	if (oldaction) {
-		if (copy_to_user(oldaction, p, sizeof(struct sigaction)))
-			return -EFAULT;
-	}
-	if (action) {
-		spin_lock_irq(&current->sig->siglock);
-		*p = new_sa;
-		check_pending(signum);
-		spin_unlock_irq(&current->sig->siglock);
-	}
+        if (nlen < len)
+                len = nlen;
+	if(len > __NEW_UTS_LEN)
+		return -EFAULT;
+	if(copy_to_user(name, system_utsname.domainname, len))
+		return -EFAULT;
 	return 0;
 }
 
@@ -271,4 +251,117 @@
 	send_sig(SIGSEGV, current, 1);
 	unlock_kernel();
 	return 0;
+}
+
+asmlinkage int sys_utrap_install(utrap_entry_t type, utrap_handler_t new_p, utrap_handler_t new_d,
+				 utrap_handler_t *old_p, utrap_handler_t *old_d)
+{
+	if (type < UT_INSTRUCTION_EXCEPTION || type > UT_TRAP_INSTRUCTION_31)
+		return -EINVAL;
+	if (new_p == (utrap_handler_t)(long)UTH_NOCHANGE) {
+		if (old_p) {
+			if (!current->tss.utraps)
+				put_user_ret(NULL, old_p, -EFAULT);
+			else
+				put_user_ret((utrap_handler_t)(current->tss.utraps[type]), old_p, -EFAULT);
+		}
+		if (old_d)
+			put_user_ret(NULL, old_d, -EFAULT);
+		return 0;
+	}
+	lock_kernel();
+	if (!current->tss.utraps) {
+		current->tss.utraps = kmalloc((UT_TRAP_INSTRUCTION_31+1)*sizeof(long), GFP_KERNEL);
+		if (!current->tss.utraps) return -ENOMEM;
+		current->tss.utraps[0] = 1;
+		memset(current->tss.utraps+1, 0, UT_TRAP_INSTRUCTION_31*sizeof(long));
+	} else {
+		if ((utrap_handler_t)current->tss.utraps[type] != new_p && current->tss.utraps[0] > 1) {
+			long *p = current->tss.utraps;
+			
+			current->tss.utraps = kmalloc((UT_TRAP_INSTRUCTION_31+1)*sizeof(long), GFP_KERNEL);
+			if (!current->tss.utraps) {
+				current->tss.utraps = p;
+				return -ENOMEM;
+			}
+			p[0]--;
+			current->tss.utraps[0] = 1;
+			memcpy(current->tss.utraps+1, p+1, UT_TRAP_INSTRUCTION_31*sizeof(long));
+		}
+	}
+	if (old_p)
+		put_user_ret((utrap_handler_t)(current->tss.utraps[type]), old_p, -EFAULT);
+	if (old_d)
+		put_user_ret(NULL, old_d, -EFAULT);
+	current->tss.utraps[type] = (long)new_p;
+	unlock_kernel();
+	return 0;
+}
+
+long sparc_memory_ordering(unsigned long model, struct pt_regs *regs)
+{
+	if (model >= 3)
+		return -EINVAL;
+	regs->tstate = (regs->tstate & ~TSTATE_MM) | (model << 14);
+	return 0;
+}
+
+asmlinkage int
+sys_sigaction(int sig, const struct old_sigaction *act,
+	      struct old_sigaction *oact)
+{
+	struct k_sigaction new_ka, old_ka;
+	int ret;
+
+	if (act) {
+		old_sigset_t mask;
+		if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
+		    __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+		    __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+			return -EFAULT;
+		__get_user(new_ka.sa.sa_flags, &act->sa_flags);
+		__get_user(mask, &act->sa_mask);
+		siginitset(&new_ka.sa.sa_mask, mask);
+		new_ka.ka_restorer = NULL;
+	}
+
+	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+	if (!ret && oact) {
+		if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
+		    __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+		    __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+			return -EFAULT;
+		__put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+		__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+	}
+
+	return ret;
+}
+
+asmlinkage int
+sys_rt_sigaction(int sig, const struct sigaction *act, struct sigaction *oact,
+		 void *restorer, size_t sigsetsize)
+{
+	struct k_sigaction new_ka, old_ka;
+	int ret;
+
+	/* XXX: Don't preclude handling different sized sigset_t's.  */
+	if (sigsetsize != sizeof(sigset_t))
+		return -EINVAL;
+
+	if (act) {
+		new_ka.ka_restorer = restorer;
+		if (copy_from_user(&new_ka.sa, act, sizeof(*act)))
+			return -EFAULT;
+	}
+
+	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+	if (!ret && oact) {
+		if (copy_to_user(oact, &old_ka.sa, sizeof(*oact)))
+			return -EFAULT;
+	}
+
+	return ret;
 }
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
--- v2.1.78/linux/arch/sparc64/kernel/sys_sparc32.c	Wed Dec 10 11:12:43 1997
+++ linux/arch/sparc64/kernel/sys_sparc32.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.55 1997/09/04 01:54:51 davem Exp $
+/* $Id: sys_sparc32.c,v 1.71 1997/12/11 15:15:11 jj Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -28,6 +28,7 @@
 #include <linux/uio.h>
 #include <linux/nfs_fs.h>
 #include <linux/smb_fs.h>
+#include <linux/smb_mount.h>
 #include <linux/ncp_fs.h>
 #include <linux/quota.h>
 #include <linux/file.h>
@@ -39,6 +40,7 @@
 #include <linux/nfsd/syscall.h>
 #include <linux/module.h>
 #include <linux/poll.h>
+#include <linux/personality.h>
 
 #include <asm/types.h>
 #include <asm/ipc.h>
@@ -253,7 +255,7 @@
 		case SEMCTL: {
 			union semun fourth;
 			void *pad;
-			unsigned long old_fs;
+			mm_segment_t old_fs;
 			struct semid_ds s;
 			
 			err = -EINVAL;
@@ -336,7 +338,7 @@
 							err = -EFAULT;
 					}
 					if (!err) {
-						unsigned long old_fs = get_fs();
+						mm_segment_t old_fs = get_fs();
 						set_fs (KERNEL_DS);
 						err = sys_msgsnd (first, p, second, third);
 						set_fs (old_fs);
@@ -348,7 +350,7 @@
 		case MSGRCV:
 			{
 				struct msgbuf *p;
-				unsigned long old_fs;
+				mm_segment_t old_fs;
 				long msgtyp = fifth;
 				
 				if (!version) {
@@ -398,7 +400,7 @@
 		case MSGCTL:
 			{
 				struct msqid_ds m;
-				unsigned long old_fs;
+				mm_segment_t old_fs;
 				
 				switch (second) {
 					case IPC_INFO:
@@ -482,7 +484,7 @@
 		case SHMCTL:
 			{
 				struct shmid_ds s;
-				unsigned long old_fs;
+				mm_segment_t old_fs;
 				
 				switch (second) {
 					case IPC_INFO:
@@ -590,7 +592,7 @@
 	case F_SETLKW:
 		{
 			struct flock f;
-			unsigned long old_fs;
+			mm_segment_t old_fs;
 			long ret;
 			
 			if(get_flock(&f, (struct flock32 *)A(arg)))
@@ -625,7 +627,7 @@
 	int cmds = cmd >> SUBCMDSHIFT;
 	int err;
 	struct dqblk d;
-	unsigned long old_fs;
+	mm_segment_t old_fs;
 	char *spec;
 	
 	switch (cmds) {
@@ -685,7 +687,7 @@
 {
 	int ret;
 	struct statfs s;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	char *pth;
 	
 	pth = getname32 (path);
@@ -707,7 +709,7 @@
 {
 	int ret;
 	struct statfs s;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	
 	set_fs (KERNEL_DS);
 	ret = sys_fstatfs(fd, &s);
@@ -723,7 +725,7 @@
 {
 	struct utimbuf32 { __kernel_time_t32 actime, modtime; };
 	struct utimbuf t;
-	unsigned long old_fs;
+	mm_segment_t old_fs;
 	int ret;
 	char *filenam;
 	
@@ -746,14 +748,15 @@
 
 struct iovec32 { u32 iov_base; __kernel_size_t32 iov_len; };
 
-typedef long (*IO_fn_t)(struct inode *, struct file *, char *, unsigned long);
+typedef ssize_t (*IO_fn_t)(struct file *, char *, size_t, loff_t *);
 
-static long do_readv_writev32(int type, struct inode *inode, struct file *file,
+static long do_readv_writev32(int type, struct file *file,
 			      const struct iovec32 *vector, u32 count)
 {
 	unsigned long tot_len;
 	struct iovec iovstack[UIO_FASTIOV];
 	struct iovec *iov=iovstack, *ivp;
+	struct inode *inode;
 	long retval, i;
 	IO_fn_t fn;
 
@@ -789,6 +792,7 @@
 		i--;
 	}
 
+	inode = file->f_dentry->d_inode;
 	retval = locks_verify_area((type == VERIFY_READ) ?
 				   FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
 				   inode, file, file->f_pos, tot_len);
@@ -828,7 +832,7 @@
 		len = ivp->iov_len;
 		ivp++;
 		count--;
-		nr = fn(inode, file, base, len);
+		nr = fn(file, base, len, &file->f_pos);
 		if (nr < 0) {
 			if (retval)
 				break;
@@ -847,69 +851,53 @@
 asmlinkage long sys32_readv(int fd, u32 vector, u32 count)
 {
 	struct file *file;
-	struct dentry *dentry;
-	struct inode *inode;
-	long err = -EBADF;
+	long ret = -EBADF;
 
 	lock_kernel();
-	if(fd >= NR_OPEN)
-		goto out;
-
-	file = current->files->fd[fd];
+	file = fget(fd);
 	if(!file)
-		goto out;
+		goto bad_file;
 
 	if(!(file->f_mode & 1))
 		goto out;
 
-	dentry = file->f_dentry;
-	if(!dentry)
-		goto out;
-
-	inode = dentry->d_inode;
-	if(!inode)
-		goto out;
-
-	err = do_readv_writev32(VERIFY_WRITE, inode, file,
+	ret = do_readv_writev32(VERIFY_WRITE, file,
 				(struct iovec32 *)A(vector), count);
+	if (ret > 0)
+		current->io_usage += ret;
+
 out:
+	fput(file);
+bad_file:
 	unlock_kernel();
-	return err;
+	return ret;
 }
 
 asmlinkage long sys32_writev(int fd, u32 vector, u32 count)
 {
-	int error = -EBADF;
 	struct file *file;
-	struct dentry *dentry;
-	struct inode *inode;
+	int ret = -EBADF;
 
 	lock_kernel();
-	if(fd >= NR_OPEN)
-		goto out;
-
-	file = current->files->fd[fd];
+	file = fget(fd);
 	if(!file)
-		goto out;
+		goto bad_file;
 
 	if(!(file->f_mode & 2))
 		goto out;
 
-	dentry = file->f_dentry;
-	if(!dentry)
-		goto out;
-
-	inode = dentry->d_inode;
-	if(!inode)
-		goto out;
-
-	down(&inode->i_sem);
-	error = do_readv_writev32(VERIFY_READ, inode, file,
+	down(&file->f_dentry->d_inode->i_sem);
+	ret = do_readv_writev32(VERIFY_READ, file,
 				(struct iovec32 *)A(vector), count);
-	up(&inode->i_sem);
+	up(&file->f_dentry->d_inode->i_sem);
+	if (ret > 0)
+		current->io_usage += ret;
+
 out:
+	fput(file);
+bad_file:
 	unlock_kernel();
-	return error;
+	return ret;
 }
 
 /* readdir & getdents */
@@ -951,8 +939,6 @@
 {
 	int error = -EBADF;
 	struct file * file;
-	struct dentry * dentry;
-	struct inode * inode;
 	struct readdir_callback32 buf;
 
 	lock_kernel();
@@ -963,14 +949,6 @@
 	if(!file)
 		goto out;
 
-	dentry = file->f_dentry;
-	if(!dentry)
-		goto out;
-
-	inode = dentry->d_inode;
-	if(!inode)
-		goto out;
-
 	buf.count = 0;
 	buf.dirent = (struct old_linux_dirent32 *)A(dirent);
 
@@ -978,7 +956,7 @@
 	if (!file->f_op || !file->f_op->readdir)
 		goto out;
 
-	error = file->f_op->readdir(inode, file, &buf, fillonedir);
+	error = file->f_op->readdir(file, &buf, fillonedir);
 	if (error < 0)
 		goto out;
 	error = buf.count;
@@ -1028,8 +1006,6 @@
 asmlinkage int sys32_getdents(unsigned int fd, u32 dirent, unsigned int count)
 {
 	struct file * file;
-	struct dentry * dentry;
-	struct inode *inode;
 	struct linux_dirent32 * lastdirent;
 	struct getdents_callback32 buf;
 	int error = -EBADF;
@@ -1042,14 +1018,6 @@
 	if(!file)
 		goto out;
 
-	dentry = file->f_dentry;
-	if(!dentry)
-		goto out;
-
-	inode = dentry->d_inode;
-	if(!inode)
-		goto out;
-
 	buf.current_dir = (struct linux_dirent32 *) A(dirent);
 	buf.previous = NULL;
 	buf.count = count;
@@ -1059,7 +1027,7 @@
 	if (!file->f_op || !file->f_op->readdir)
 		goto out;
 
-	error = file->f_op->readdir(inode, file, &buf, filldir);
+	error = file->f_op->readdir(file, &buf, filldir);
 	if (error < 0)
 		goto out;
 	lastdirent = buf.previous;
@@ -1088,7 +1056,7 @@
 	if (ufdset) {
 		unsigned long odd;
 
-		if (verify_area(VERIFY_WRITE, ufdset, nn*sizeof(u32)))
+		if (verify_area(VERIFY_WRITE, ufdset, n*sizeof(u32)))
 			return -EFAULT;
 
 		odd = n & 1UL;
@@ -1104,12 +1072,12 @@
 		if (odd)
 			__get_user(*fdset, ufdset);
 	} else {
-		/* Tricky, must clear full unsigned long in the kernel
-		   fdset at the end, this makes sure that actually
-		   happens.  */
+		/* Tricky, must clear full unsigned long in the
+		 * kernel fdset at the end, this makes sure that
+		 * actually happens.
+		 */
 		memset(fdset, 0, ((n + 1) & ~1)*sizeof(u32));
 	}
-
 	return 0;
 }
 
@@ -1168,7 +1136,7 @@
 		goto out;
 	if (n > KFDS_NR)
 		n = KFDS_NR;
- 
+
 	nn = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32));
 	if ((ret = get_fd_set32(nn, fds->in, inp)) ||
 	    (ret = get_fd_set32(nn, fds->out, outp)) ||
@@ -1196,7 +1164,7 @@
 		goto out;
 	if (!ret) {
 		ret = -ERESTARTNOHAND;
-		if (current->signal & ~current->blocked)
+		if (signal_pending(current))
 			goto out;
 		ret = 0;
 	}
@@ -1237,7 +1205,7 @@
 	int ret;
 	struct stat s;
 	char *filenam;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	
 	filenam = getname32 (filename);
 	ret = PTR_ERR(filenam);
@@ -1259,7 +1227,7 @@
 	int ret;
 	struct stat s;
 	char *filenam;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	
 	filenam = getname32 (filename);
 	ret = PTR_ERR(filenam);
@@ -1280,7 +1248,7 @@
 {
 	int ret;
 	struct stat s;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	
 	set_fs (KERNEL_DS);
 	ret = sys_newfstat(fd, &s);
@@ -1290,29 +1258,11 @@
 	return ret;
 }
 
-extern asmlinkage int sys_sysfs(int option, ...);
+extern asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2);
 
-asmlinkage int sys32_sysfs(int option, ...)
+asmlinkage int sys32_sysfs(int option, u32 arg1, u32 arg2)
 {
-        va_list args;
-	unsigned int x;
-	int ret = -EINVAL;
-
-        va_start(args, option);
-        switch (option) {
-                case 1:
-			ret = sys_sysfs(option, (const char *)A(va_arg(args, u32)));
-			break;
-                case 2:
-			x = va_arg(args, unsigned int);
-			ret = sys_sysfs(option, x, (char *)A(va_arg(args, u32)));
-			break;
-                case 3:
-			ret = sys_sysfs(option);
-			break;
-	}
-        va_end(args);
-	return ret;
+	return sys_sysfs(option, arg1, arg2);
 }
 
 struct ncp_mount_data32 {
@@ -1347,17 +1297,7 @@
 
 struct smb_mount_data32 {
         int version;
-        unsigned int fd;
         __kernel_uid_t32 mounted_uid;
-        struct sockaddr_in addr;
-        char server_name[17];
-        char client_name[17];
-        char service[64];
-        char root_path[64];
-        char username[64];
-        char password[64];
-        char domain[64];
-        unsigned short max_xmit;
         __kernel_uid_t32 uid;
         __kernel_gid_t32 gid;
         __kernel_mode_t32 file_mode;
@@ -1434,7 +1374,7 @@
 				 (void *)A(data));
 	} else {
 		unsigned long dev_page, dir_page, data_page;
-		int old_fs;
+		mm_segment_t old_fs;
 
 		err = copy_mount_stuff_to_kernel((const void *)A(dev_name), &dev_page);
 		if(err)
@@ -1527,7 +1467,7 @@
 		struct rusage r;
 		int ret;
 		unsigned int status;
-		unsigned long old_fs = get_fs();
+		mm_segment_t old_fs = get_fs();
 		
 		set_fs (KERNEL_DS);
 		ret = sys_wait4(pid, stat_addr ? &status : NULL, options, &r);
@@ -1558,7 +1498,7 @@
 {
 	struct sysinfo s;
 	int ret;
-	unsigned long old_fs = get_fs ();
+	mm_segment_t old_fs = get_fs ();
 	
 	set_fs (KERNEL_DS);
 	ret = sys_sysinfo(&s);
@@ -1589,7 +1529,7 @@
 {
 	struct timespec t;
 	int ret;
-	unsigned long old_fs = get_fs ();
+	mm_segment_t old_fs = get_fs ();
 	
 	set_fs (KERNEL_DS);
 	ret = sys_sched_rr_get_interval(pid, &t);
@@ -1606,7 +1546,7 @@
 {
 	struct timespec t;
 	int ret;
-	unsigned long old_fs = get_fs ();
+	mm_segment_t old_fs = get_fs ();
 	
 	if (get_user (t.tv_sec, &(((struct timespec32 *)A(rqtp))->tv_sec)) ||
 	    __get_user (t.tv_nsec, &(((struct timespec32 *)A(rqtp))->tv_nsec)))
@@ -1622,34 +1562,235 @@
 	return ret;
 }
 
-extern asmlinkage int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset);
+extern asmlinkage int sys_sigprocmask(int how, old_sigset_t *set, old_sigset_t *oset);
 
 asmlinkage int sys32_sigprocmask(int how, u32 set, u32 oset)
 {
-	sigset_t s;
+	old_sigset_t s;
 	int ret;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	
-	if (set && get_user (s, (sigset_t32 *)A(set))) return -EFAULT;
+	if (set && get_user (s, (old_sigset_t32 *)A(set))) return -EFAULT;
 	set_fs (KERNEL_DS);
 	ret = sys_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL);
 	set_fs (old_fs);
-	if (oset && put_user (s, (sigset_t32 *)A(oset))) return -EFAULT;
-	return ret;
+	if (ret) return ret;
+	if (oset && put_user (s, (old_sigset_t32 *)A(oset))) return -EFAULT;
+	return 0;
 }
 
-extern asmlinkage int sys_sigpending(sigset_t *set);
+extern asmlinkage int sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, size_t sigsetsize);
 
-asmlinkage int sys32_sigpending(u32 set)
+asmlinkage int sys32_rt_sigprocmask(int how, u32 set, u32 oset, __kernel_size_t32 sigsetsize)
 {
 	sigset_t s;
+	sigset_t32 s32;
 	int ret;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
+	
+	if (set) {
+		if (copy_from_user (&s32, (sigset_t32 *)A(set), sizeof(sigset_t32)))
+			return -EFAULT;
+		switch (_NSIG_WORDS) {
+		case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32);
+		case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32);
+		case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32);
+		case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32);
+		}
+	}
+	set_fs (KERNEL_DS);
+	ret = sys_rt_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL, sigsetsize);
+	set_fs (old_fs);
+	if (ret) return ret;
+	if (oset) {
+		switch (_NSIG_WORDS) {
+		case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3];
+		case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2];
+		case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1];
+		case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0];
+		}
+		if (copy_to_user ((sigset_t32 *)A(set), &s32, sizeof(sigset_t32)))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+extern asmlinkage int sys_sigpending(old_sigset_t *set);
+
+asmlinkage int sys32_sigpending(u32 set)
+{
+	old_sigset_t s;
+	int ret;
+	mm_segment_t old_fs = get_fs();
 		
 	set_fs (KERNEL_DS);
 	ret = sys_sigpending(&s);
 	set_fs (old_fs);
-	if (put_user (s, (sigset_t32 *)A(set))) return -EFAULT;
+	if (put_user (s, (old_sigset_t32 *)A(set))) return -EFAULT;
+	return ret;
+}
+
+extern asmlinkage int sys_rt_sigpending(sigset_t *set, size_t sigsetsize);
+
+asmlinkage int sys32_rt_sigpending(u32 set, __kernel_size_t32 sigsetsize)
+{
+	sigset_t s;
+	sigset_t32 s32;
+	int ret;
+	mm_segment_t old_fs = get_fs();
+		
+	set_fs (KERNEL_DS);
+	ret = sys_rt_sigpending(&s, sigsetsize);
+	set_fs (old_fs);
+	if (!ret) {
+		switch (_NSIG_WORDS) {
+		case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3];
+		case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2];
+		case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1];
+		case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0];
+		}
+		if (copy_to_user ((sigset_t32 *)A(set), &s32, sizeof(sigset_t32)))
+			return -EFAULT;
+	}
+	return ret;
+}
+
+siginfo_t32 *
+siginfo64to32(siginfo_t32 *d, siginfo_t *s)
+{
+	memset (&d, 0, sizeof(siginfo_t32));
+	d->si_signo = s->si_signo;
+	d->si_errno = s->si_errno;
+	d->si_code = s->si_code;
+	if (s->si_signo >= SIGRTMIN) {
+		d->si_pid = s->si_pid;
+		d->si_uid = s->si_uid;
+		/* XXX: Ouch, how to find this out??? */
+		d->si_int = s->si_int;
+	} else switch (s->si_signo) {
+	/* XXX: What about POSIX1.b timers */
+	case SIGCHLD:
+		d->si_pid = s->si_pid;
+		d->si_status = s->si_status;
+		d->si_utime = s->si_utime;
+		d->si_stime = s->si_stime;
+		break;
+	case SIGSEGV:
+	case SIGBUS:
+	case SIGFPE:
+	case SIGILL:
+		d->si_addr = (long)(s->si_addr);
+		/* XXX: Do we need to translate this from sparc64 to sparc32 traps? */
+		d->si_trapno = s->si_trapno;
+		break;
+	case SIGPOLL:
+		d->si_band = s->si_band;
+		d->si_fd = s->si_fd;
+		break;
+	default:
+		d->si_pid = s->si_pid;
+		d->si_uid = s->si_uid;
+		break;
+	}
+	return d;
+}
+
+siginfo_t *
+siginfo32to64(siginfo_t *d, siginfo_t32 *s)
+{
+	d->si_signo = s->si_signo;
+	d->si_errno = s->si_errno;
+	d->si_code = s->si_code;
+	if (s->si_signo >= SIGRTMIN) {
+		d->si_pid = s->si_pid;
+		d->si_uid = s->si_uid;
+		/* XXX: Ouch, how to find this out??? */
+		d->si_int = s->si_int;
+	} else switch (s->si_signo) {
+	/* XXX: What about POSIX1.b timers */
+	case SIGCHLD:
+		d->si_pid = s->si_pid;
+		d->si_status = s->si_status;
+		d->si_utime = s->si_utime;
+		d->si_stime = s->si_stime;
+		break;
+	case SIGSEGV:
+	case SIGBUS:
+	case SIGFPE:
+	case SIGILL:
+		d->si_addr = (void *)A(s->si_addr);
+		/* XXX: Do we need to translate this from sparc32 to sparc64 traps? */
+		d->si_trapno = s->si_trapno;
+		break;
+	case SIGPOLL:
+		d->si_band = s->si_band;
+		d->si_fd = s->si_fd;
+		break;
+	default:
+		d->si_pid = s->si_pid;
+		d->si_uid = s->si_uid;
+		break;
+	}
+	return d;
+}
+
+extern asmlinkage int
+sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo,
+		    const struct timespec *uts, size_t sigsetsize);
+
+asmlinkage int
+sys32_rt_sigtimedwait(u32 uthese, u32 uinfo,
+		      u32 uts, __kernel_size_t32 sigsetsize)
+{
+	sigset_t s;
+	sigset_t32 s32;
+	struct timespec t;
+	int ret;
+	mm_segment_t old_fs = get_fs();
+	siginfo_t info;
+	siginfo_t32 info32;
+		
+	if (copy_from_user (&s32, (sigset_t32 *)A(uthese), sizeof(sigset_t32)))
+		return -EFAULT;
+	switch (_NSIG_WORDS) {
+	case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32);
+	case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32);
+	case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32);
+	case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32);
+	}
+	if (uts) {
+		if (get_user (t.tv_sec, &(((struct timespec32 *)A(uts))->tv_sec)) ||
+		    __get_user (t.tv_nsec, &(((struct timespec32 *)A(uts))->tv_nsec)))
+			return -EFAULT;
+	}
+	set_fs (KERNEL_DS);
+	ret = sys_rt_sigtimedwait(&s, &info, &t, sigsetsize);
+	set_fs (old_fs);
+	if (ret >= 0 && uinfo) {
+		if (copy_to_user ((siginfo_t32 *)A(uinfo), siginfo64to32(&info32, &info), sizeof(siginfo_t32)))
+			return -EFAULT;
+	}
+	return ret;
+}
+
+extern asmlinkage int
+sys_rt_sigqueueinfo(int pid, int sig, siginfo_t *uinfo);
+
+asmlinkage int
+sys32_rt_sigqueueinfo(int pid, int sig, u32 uinfo)
+{
+	siginfo_t info;
+	siginfo_t32 info32;
+	int ret;
+	mm_segment_t old_fs = get_fs();
+	
+	if (copy_from_user (&info32, (siginfo_t32 *)A(uinfo), sizeof(siginfo_t32)))
+		return -EFAULT;
+	/* XXX: Is this correct? */
+	siginfo32to64(&info, &info32);
+	set_fs (KERNEL_DS);
+	ret = sys_rt_sigqueueinfo(pid, sig, &info);
+	set_fs (old_fs);
 	return ret;
 }
 
@@ -1684,7 +1825,7 @@
 {
 	uid_t a, b, c;
 	int ret;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 		
 	set_fs (KERNEL_DS);
 	ret = sys_getresuid(&a, &b, &c);
@@ -1696,6 +1837,49 @@
 	return ret;
 }
 
+extern asmlinkage int sys_setregid(gid_t rgid, gid_t egid);
+
+asmlinkage int sys32_setregid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid)
+{
+	gid_t srgid, segid;
+
+	srgid = (rgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)rgid);
+	segid = (egid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)egid);
+	return sys_setregid(srgid, segid);
+}
+
+extern asmlinkage int sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid);
+
+asmlinkage int sys32_setresgid(__kernel_gid_t32 rgid,
+			       __kernel_gid_t32 egid,
+			       __kernel_gid_t32 sgid)
+{
+	gid_t srgid, segid, ssgid;
+
+	srgid = (rgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)rgid);
+	segid = (egid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)egid);
+	ssgid = (sgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)sgid);
+	return sys_setresgid(srgid, segid, ssgid);
+}
+
+extern asmlinkage int sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
+
+asmlinkage int sys32_getresgid(u32 rgid, u32 egid, u32 sgid)
+{
+	gid_t a, b, c;
+	int ret;
+	mm_segment_t old_fs = get_fs();
+		
+	set_fs (KERNEL_DS);
+	ret = sys_getresgid(&a, &b, &c);
+	set_fs (old_fs);
+	if (put_user (a, (__kernel_gid_t32 *)A(rgid)) ||
+	    put_user (b, (__kernel_gid_t32 *)A(egid)) ||
+	    put_user (c, (__kernel_gid_t32 *)A(sgid)))
+		return -EFAULT;
+	return ret;
+}
+
 struct tms32 {
 	__kernel_clock_t32 tms_utime;
 	__kernel_clock_t32 tms_stime;
@@ -1709,7 +1893,7 @@
 {
 	struct tms t;
 	long ret;
-	unsigned long old_fs = get_fs ();
+	mm_segment_t old_fs = get_fs ();
 	
 	set_fs (KERNEL_DS);
 	ret = sys_times(tbuf ? &t : NULL);
@@ -1729,7 +1913,7 @@
 {
 	gid_t gl[NGROUPS];
 	int ret, i;
-	unsigned long old_fs = get_fs ();
+	mm_segment_t old_fs = get_fs ();
 	
 	set_fs (KERNEL_DS);
 	ret = sys_getgroups(gidsetsize, gl);
@@ -1747,7 +1931,7 @@
 {
 	gid_t gl[NGROUPS];
 	int ret, i;
-	unsigned long old_fs = get_fs ();
+	mm_segment_t old_fs = get_fs ();
 	
 	if ((unsigned) gidsetsize > NGROUPS)
 		return -EINVAL;
@@ -1774,7 +1958,7 @@
 {
 	struct rlimit r;
 	int ret;
-	unsigned long old_fs = get_fs ();
+	mm_segment_t old_fs = get_fs ();
 	
 	set_fs (KERNEL_DS);
 	ret = sys_getrlimit(resource, &r);
@@ -1792,7 +1976,7 @@
 {
 	struct rlimit r;
 	int ret;
-	unsigned long old_fs = get_fs ();
+	mm_segment_t old_fs = get_fs ();
 
 	if (resource >= RLIM_NLIMITS) return -EINVAL;	
 	if (get_user (r.rlim_cur, &(((struct rlimit32 *)A(rlim))->rlim_cur)) ||
@@ -1814,7 +1998,7 @@
 {
 	struct rusage r;
 	int ret;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 		
 	set_fs (KERNEL_DS);
 	ret = sys_getrusage(who, &r);
@@ -1854,7 +2038,7 @@
 {
 	struct timex t;
 	int ret;
-	unsigned long old_fs = get_fs ();
+	mm_segment_t old_fs = get_fs ();
 
 	if (get_user (t.modes, &(((struct timex32 *)A(txc_p))->modes)) ||
 	    __get_user (t.offset, &(((struct timex32 *)A(txc_p))->offset)) ||
@@ -2158,21 +2342,19 @@
                                 AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)};
 #undef AL
 
-extern asmlinkage int sys32_bind(int fd, u32 umyaddr, int addrlen);
-extern asmlinkage int sys32_connect(int fd, u32 uservaddr, int addrlen);
-extern asmlinkage int sys32_accept(int fd, u32 upeer_sockaddr, u32 upeer_addrlen);
-extern asmlinkage int sys32_getsockname(int fd, u32 usockaddr, u32 usockaddr_len);
-extern asmlinkage int sys32_getpeername(int fd, u32 usockaddr, u32 usockaddr_len);
-extern asmlinkage int sys32_send(int fd, u32 buff, __kernel_size_t32 len,
-				 unsigned flags);
+extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen);
+extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen);
+extern asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen);
+extern asmlinkage int sys_getsockname(int fd, struct sockaddr *usockaddr, int *usockaddr_len);
+extern asmlinkage int sys_getpeername(int fd, struct sockaddr *usockaddr, int *usockaddr_len);
+extern asmlinkage int sys_send(int fd, void *buff, size_t len, unsigned flags);
 extern asmlinkage int sys32_sendto(int fd, u32 buff, __kernel_size_t32 len,
 				   unsigned flags, u32 addr, int addr_len);
-extern asmlinkage int sys32_recv(int fd, u32 ubuf, __kernel_size_t32 size,
-				 unsigned flags);
+extern asmlinkage int sys_recv(int fd, void *ubuf, size_t size, unsigned flags);
 extern asmlinkage int sys32_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size,
 				     unsigned flags, u32 addr, u32 addr_len);
-extern asmlinkage int sys32_setsockopt(int fd, int level, int optname,
-				       u32 optval, int optlen);
+extern asmlinkage int sys_setsockopt(int fd, int level, int optname,
+				     char *optval, int optlen);
 extern asmlinkage int sys32_getsockopt(int fd, int level, int optname,
 				       u32 optval, u32 optlen);
 
@@ -2199,31 +2381,31 @@
 		case SYS_SOCKET:
 			return sys_socket(a0, a1, a[2]);
 		case SYS_BIND:
-			return sys32_bind(a0, a1, a[2]);
+			return sys_bind(a0, (struct sockaddr *)A(a1), a[2]);
 		case SYS_CONNECT:
-			return sys32_connect(a0, a1, a[2]);
+			return sys_connect(a0, (struct sockaddr *)A(a1), a[2]);
 		case SYS_LISTEN:
 			return sys_listen(a0, a1);
 		case SYS_ACCEPT:
-			return sys32_accept(a0, a1, a[2]);
+			return sys_accept(a0, (struct sockaddr *)A(a1), (int *)A(a[2]));
 		case SYS_GETSOCKNAME:
-			return sys32_getsockname(a0, a1, a[2]);
+			return sys_getsockname(a0, (struct sockaddr *)A(a1), (int *)A(a[2]));
 		case SYS_GETPEERNAME:
-			return sys32_getpeername(a0, a1, a[2]);
+			return sys_getpeername(a0, (struct sockaddr *)A(a1), (int *)A(a[2]));
 		case SYS_SOCKETPAIR:
 			return sys_socketpair(a0, a1, a[2], (int *)A(a[3]));
 		case SYS_SEND:
-			return sys32_send(a0, a1, a[2], a[3]);
+			return sys_send(a0, (void *)A(a1), a[2], a[3]);
 		case SYS_SENDTO:
 			return sys32_sendto(a0, a1, a[2], a[3], a[4], a[5]);
 		case SYS_RECV:
-			return sys32_recv(a0, a1, a[2], a[3]);
+			return sys_recv(a0, (void *)A(a1), a[2], a[3]);
 		case SYS_RECVFROM:
 			return sys32_recvfrom(a0, a1, a[2], a[3], a[4], a[5]);
 		case SYS_SHUTDOWN:
 			return sys_shutdown(a0,a1);
 		case SYS_SETSOCKOPT:
-			return sys32_setsockopt(a0, a1, a[2], a[3], a[4]);
+			return sys_setsockopt(a0, a1, a[2], (char *)A(a[3]), a[4]);
 		case SYS_GETSOCKOPT:
 			return sys32_getsockopt(a0, a1, a[2], a[3], a[4]);
 		case SYS_SENDMSG:
@@ -2236,51 +2418,88 @@
 
 extern void check_pending(int signum);
 
-asmlinkage int sparc32_sigaction (int signum, u32 action, u32 oldaction)
+asmlinkage int sys32_sigaction (int sig, u32 act, u32 oact)
 {
-	struct sigaction32 new_sa, old_sa;
-	struct sigaction *p;
+        struct k_sigaction new_ka, old_ka;
+        int ret;
 
-	if(signum < 0) {
+	if(sig < 0) {
 		current->tss.new_signal = 1;
-		signum = -signum;
+		sig = -sig;
 	}
-	if (signum<1 || signum>32)
-		return -EINVAL;
-	p = signum - 1 + current->sig->action;
-	if (action) {
-		if (signum==SIGKILL || signum==SIGSTOP)
-			return -EINVAL;
-		if(copy_from_user(&new_sa, A(action), sizeof(struct sigaction32)))
-			return -EFAULT;
-		if (((__sighandler_t)A(new_sa.sa_handler)) != SIG_DFL && 
-		    ((__sighandler_t)A(new_sa.sa_handler)) != SIG_IGN) {
-			int err = verify_area(VERIFY_READ,
-					      (__sighandler_t)A(new_sa.sa_handler), 1);
-			if (err)
-				return err;
-		}
-	}
-	if (oldaction) {
-		old_sa.sa_handler = (unsigned)(u64)(p->sa_handler);
-		old_sa.sa_mask = (sigset_t32)(p->sa_mask);
-		old_sa.sa_flags = (unsigned)(p->sa_flags);
-		old_sa.sa_restorer = (unsigned)(u64)(p->sa_restorer);
-		if (copy_to_user(A(oldaction), &old_sa, sizeof(struct sigaction32)))
-			return -EFAULT;
-	}
-	if (action) {
-		spin_lock_irq(&current->sig->siglock);
-		p->sa_handler = (__sighandler_t)A(new_sa.sa_handler);
-		p->sa_mask = (sigset_t)(new_sa.sa_mask);
-		p->sa_flags = new_sa.sa_flags;
-		p->sa_restorer = (void (*)(void))A(new_sa.sa_restorer);
-		check_pending(signum);
-		spin_unlock_irq(&current->sig->siglock);
-	}
-	return 0;
+
+        if (act) {
+		old_sigset_t32 mask;
+		
+		if (get_user((long)new_ka.sa.sa_handler, &((struct old_sigaction32 *)A(act))->sa_handler) ||
+		    __get_user((long)new_ka.sa.sa_restorer, &((struct old_sigaction32 *)A(act))->sa_restorer))
+			return -EFAULT;
+		__get_user(new_ka.sa.sa_flags, &((struct old_sigaction32 *)A(act))->sa_flags);
+		__get_user(mask, &((struct old_sigaction32 *)A(act))->sa_mask);
+                new_ka.ka_restorer = NULL;
+                siginitset(&new_ka.sa.sa_mask, mask);
+        }
+
+        ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+        if (!ret && oact) {
+        	if (put_user((long)old_ka.sa.sa_handler, &((struct old_sigaction32 *)A(oact))->sa_handler) ||
+		    __put_user((long)old_ka.sa.sa_restorer, &((struct old_sigaction32 *)A(oact))->sa_restorer))
+                        return -EFAULT;
+		__put_user(old_ka.sa.sa_flags, &((struct old_sigaction32 *)A(oact))->sa_flags);
+		__put_user(old_ka.sa.sa_mask.sig[0], &((struct old_sigaction32 *)A(oact))->sa_mask);
+        }
+
+	return ret;
 }
 
+asmlinkage int
+sys32_rt_sigaction(int sig, u32 act, u32 oact,
+                 u32 restorer, __kernel_size_t32 sigsetsize)
+{
+        struct k_sigaction new_ka, old_ka;
+        int ret;
+	sigset_t32 set32;
+
+        /* XXX: Don't preclude handling different sized sigset_t's.  */
+        if (sigsetsize != sizeof(sigset_t32))
+                return -EINVAL;
+
+        if (act) {
+		if (get_user((long)new_ka.sa.sa_handler, &((struct sigaction32 *)A(act))->sa_handler) ||
+		    __copy_from_user(&set32, &((struct sigaction32 *)A(act))->sa_mask, sizeof(sigset_t32)))
+                        return -EFAULT;
+		switch (_NSIG_WORDS) {
+		case 4: new_ka.sa.sa_mask.sig[3] = set32.sig[6] | (((long)set32.sig[7]) << 32);
+		case 3: new_ka.sa.sa_mask.sig[2] = set32.sig[4] | (((long)set32.sig[5]) << 32);
+		case 2: new_ka.sa.sa_mask.sig[1] = set32.sig[2] | (((long)set32.sig[3]) << 32);
+		case 1: new_ka.sa.sa_mask.sig[0] = set32.sig[0] | (((long)set32.sig[1]) << 32);
+		}
+		__get_user(new_ka.sa.sa_flags, &((struct sigaction32 *)A(act))->sa_flags);
+		__get_user((long)new_ka.sa.sa_restorer, &((struct sigaction32 *)A(act))->sa_restorer);
+                new_ka.ka_restorer = (void *)(long)restorer;
+        }
+
+        ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+        if (!ret && oact) {
+		switch (_NSIG_WORDS) {
+		case 4: set32.sig[7] = (old_ka.sa.sa_mask.sig[3] >> 32); set32.sig[6] = old_ka.sa.sa_mask.sig[3];
+		case 3: set32.sig[5] = (old_ka.sa.sa_mask.sig[2] >> 32); set32.sig[4] = old_ka.sa.sa_mask.sig[2];
+		case 2: set32.sig[3] = (old_ka.sa.sa_mask.sig[1] >> 32); set32.sig[2] = old_ka.sa.sa_mask.sig[1];
+		case 1: set32.sig[1] = (old_ka.sa.sa_mask.sig[0] >> 32); set32.sig[0] = old_ka.sa.sa_mask.sig[0];
+		}
+        	if (put_user((long)old_ka.sa.sa_handler, &((struct sigaction32 *)A(oact))->sa_handler) ||
+		    __copy_to_user(&((struct sigaction32 *)A(oact))->sa_mask, &set32, sizeof(sigset_t32)))
+                        return -EFAULT;
+		__put_user(old_ka.sa.sa_flags, &((struct sigaction32 *)A(oact))->sa_flags);
+		__put_user((long)old_ka.sa.sa_restorer, &((struct sigaction32 *)A(oact))->sa_restorer);
+        }
+
+        return ret;
+}
+
+
 /*
  * count32() counts the number of arguments/envelopes
  */
@@ -2476,7 +2695,7 @@
 asmlinkage int sys32_query_module(u32 name_user, int which, u32 buf, __kernel_size_t32 bufsize, u32 retv)
 {
 	char *buff;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	size_t val;
 	int ret, i, j;
 	unsigned long *p;
@@ -2544,7 +2763,7 @@
 			if (!ret && val) {
 				char *strings = buff + ((unsigned long *)buff)[1];
 				j = *(p - 1) - ((unsigned long *)buff)[1];
-				j = j + strlen (buff + j) + 1;
+				j = j + strlen (strings + j) + 1;
 				if (bufsize < j) {
 					bufsiz = 0;
 					goto qmsym_toshort;
@@ -2597,7 +2816,7 @@
 {
 	int len, i;
 	struct kernel_sym *tbl;
-	unsigned long old_fs;
+	mm_segment_t old_fs;
 	
 	len = sys_get_kernel_syms(NULL);
 	if (!table) return len;
@@ -2858,7 +3077,7 @@
 	union nfsctl_res32 *res32 = (union nfsctl_res32 *)A(u_resp);
 	struct nfsctl_arg *karg = NULL;
 	union nfsctl_res *kres = NULL;
-	unsigned long oldfs;
+	mm_segment_t oldfs;
 	int err;
 
 	karg = kmalloc(sizeof(*karg), GFP_USER);
@@ -3003,7 +3222,7 @@
 {
 	char *kfilename;
 	struct timeval ktvs[2];
-	unsigned long old_fs;
+	mm_segment_t old_fs;
 	int ret;
 
 	kfilename = getname32(filename);
@@ -3023,4 +3242,88 @@
 		putname32(kfilename);
 	}
 	return ret;
+}
+
+/* These are here just in case some old sparc32 binary calls it. */
+asmlinkage int sys32_pause(void)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule();
+	return -ERESTARTNOHAND;
+}
+
+/* PCI config space poking. */
+extern asmlinkage int sys_pciconfig_read(unsigned long bus,
+					 unsigned long dfn,
+					 unsigned long off,
+					 unsigned long len,
+					 unsigned char *buf);
+
+extern asmlinkage int sys_pciconfig_write(unsigned long bus,
+					  unsigned long dfn,
+					  unsigned long off,
+					  unsigned long len,
+					  unsigned char *buf);
+
+asmlinkage int sys32_pciconfig_read(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf)
+{
+	return sys_pciconfig_read((unsigned long) bus,
+				  (unsigned long) dfn,
+				  (unsigned long) off,
+				  (unsigned long) len,
+				  (unsigned char *)A(ubuf));
+}
+
+asmlinkage int sys32_pciconfig_write(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf)
+{
+	return sys_pciconfig_write((unsigned long) bus,
+				   (unsigned long) dfn,
+				   (unsigned long) off,
+				   (unsigned long) len,
+				   (unsigned char *)A(ubuf));
+}
+
+extern asmlinkage int sys_prctl(int option, unsigned long arg2, unsigned long arg3,
+				unsigned long arg4, unsigned long arg5);
+
+asmlinkage int sys32_prctl(int option, u32 arg2, u32 arg3, u32 arg4, u32 arg5)
+{
+	return sys_prctl(option,
+			 (unsigned long) arg2,
+			 (unsigned long) arg3,
+			 (unsigned long) arg4,
+			 (unsigned long) arg5);
+}
+
+
+extern asmlinkage int sys_newuname(struct new_utsname * name);
+
+asmlinkage int sys32_newuname(struct new_utsname * name)
+{
+	int ret = sys_newuname(name);
+	
+	if (current->personality == PER_LINUX32 && !ret) {
+		ret = copy_to_user(name->machine, "sparc\0\0", 8);
+	}
+	return ret;
+}
+
+extern asmlinkage ssize_t sys_pread(unsigned int fd, char * buf,
+				    size_t count, loff_t pos);
+
+extern asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf,
+				     size_t count, loff_t pos);
+
+typedef __kernel_ssize_t32 ssize_t32;
+
+asmlinkage ssize_t32 sys32_pread(unsigned int fd, u32 ubuf,
+				 __kernel_size_t32 count, u32 pos)
+{
+	return sys_pread(fd, (char *) A(ubuf), count, pos);
+}
+
+asmlinkage ssize_t32 sys32_pwrite(unsigned int fd, u32 ubuf,
+				  __kernel_size_t32 count, u32 pos)
+{
+	return sys_pwrite(fd, (char *) A(ubuf), count, pos);
 }
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/sys_sunos32.c linux/arch/sparc64/kernel/sys_sunos32.c
--- v2.1.78/linux/arch/sparc64/kernel/sys_sunos32.c	Thu Jul 17 10:06:04 1997
+++ linux/arch/sparc64/kernel/sys_sunos32.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos32.c,v 1.3 1997/07/17 02:20:48 davem Exp $
+/* $Id: sys_sunos32.c,v 1.7 1997/12/11 15:15:19 jj Exp $
  * sys_sunos32.c: SunOS binary compatability layer on sparc64.
  *
  * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -297,35 +297,28 @@
 	return SUNOS_NR_OPEN;
 }
 
-#define _S(nr) (1<<((nr)-1))
 
-#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
 asmlinkage u32 sunos_sigblock(u32 blk_mask)
 {
-	unsigned long flags;
 	u32 old;
 
-	lock_kernel();
-	save_and_cli(flags);
-	old = (u32) current->blocked;
-	current->blocked |= (blk_mask & _BLOCKABLE);
-	restore_flags(flags);
-	unlock_kernel();
+	spin_lock_irq(&current->sigmask_lock);
+	old = (u32) current->blocked.sig[0];
+	current->blocked.sig[0] |= (blk_mask & _BLOCKABLE);
+	spin_unlock_irq(&current->sigmask_lock);
 	return old;
 }
 
 asmlinkage u32 sunos_sigsetmask(u32 newmask)
 {
-	unsigned long flags;
 	u32 retval;
 
-	lock_kernel();
-	save_and_cli(flags);
-	retval = (u32) current->blocked;
-	current->blocked = (newmask & _BLOCKABLE);
-	restore_flags(flags);
-	unlock_kernel();
+	spin_lock_irq(&current->sigmask_lock);
+	retval = (u32) current->blocked.sig[0];
+	current->blocked.sig[0] = (newmask & _BLOCKABLE);
+	spin_unlock_irq(&current->sigmask_lock);
 	return retval;
 }
 
@@ -379,8 +372,6 @@
 asmlinkage int sunos_getdents(unsigned int fd, u32 u_dirent, int cnt)
 {
 	struct file * file;
-	struct dentry * dentry;
-	struct inode * inode;
 	struct sunos_dirent * lastdirent;
 	struct sunos_dirent_callback buf;
 	int error = -EBADF;
@@ -394,14 +385,6 @@
 	if(!file)
 		goto out;
 
-	dentry = file->f_dentry;
-	if(!dentry)
-		goto out;
-
-	inode = dentry->d_inode;
-	if(!inode)
-		goto out;
-
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
 		goto out;
@@ -415,7 +398,7 @@
 	buf.count = cnt;
 	buf.error = 0;
 
-	error = file->f_op->readdir(inode, file, &buf, sunos_filldir);
+	error = file->f_op->readdir(file, &buf, sunos_filldir);
 	if (error < 0)
 		goto out;
 	lastdirent = buf.previous;
@@ -472,8 +455,6 @@
 				   int cnt, u32 u_basep)
 {
 	struct file * file;
-	struct dentry * dentry;
-	struct inode * inode;
 	struct sunos_direntry * lastdirent;
 	struct sunos_direntry_callback buf;
 	int error = -EBADF;
@@ -488,14 +469,6 @@
 	if(!file)
 		goto out;
 
-	dentry = file->f_dentry;
-	if(!dentry)
-		goto out;
-
-	inode = dentry->d_inode;
-	if(!inode)
-		goto out;
-
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
 		goto out;
@@ -509,7 +482,7 @@
 	buf.count = cnt;
 	buf.error = 0;
 
-	error = file->f_op->readdir(inode, file, &buf, sunos_filldirentry);
+	error = file->f_op->readdir(file, &buf, sunos_filldirentry);
 	if (error < 0)
 		goto out;
 	lastdirent = buf.previous;
@@ -1140,7 +1113,7 @@
 	struct sparc_stackf32 *sp;
 	struct msqid_ds kds;
 	struct msgbuf *kmbuf;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	u32 arg5;
 	int rval;
 
@@ -1264,7 +1237,8 @@
 asmlinkage int sunos_shmsys(int op, u32 arg1, u32 arg2, u32 arg3)
 {
 	struct shmid_ds ksds;
-	unsigned long raddr, old_fs = get_fs();
+	unsigned long raddr;
+	mm_segment_t old_fs = get_fs();
 	int rval;
 
 	lock_kernel();
@@ -1328,20 +1302,20 @@
 	return ret;
 }
 
-extern asmlinkage int sys32_read(unsigned int fd, u32 buf, int count);
-extern asmlinkage int sys32_write(unsigned int fd, u32 buf,int count);
-extern asmlinkage int sys32_recv(int fd, u32 ubuf, int size, unsigned flags);
-extern asmlinkage int sys32_send(int fd, u32 buff, int len, unsigned flags);
-extern asmlinkage int sys32_accept(int fd, u32 sa, u32 addrlen);
+extern asmlinkage int sys_read(unsigned int fd, char *buf, unsigned long count);
+extern asmlinkage int sys_write(unsigned int fd, char *buf, unsigned long count);
+extern asmlinkage int sys_recv(int fd, void *ubuf, size_t size, unsigned flags);
+extern asmlinkage int sys_send(int fd, void *buff, size_t len, unsigned flags);
+extern asmlinkage int sys_accept(int fd, struct sockaddr *sa, int *addrlen);
 extern asmlinkage int sys32_readv(u32 fd, u32 vector, s32 count);
 extern asmlinkage int sys32_writev(u32 fd, u32 vector, s32 count);
 
-asmlinkage int sunos_read(unsigned int fd, u32 buf, int count)
+asmlinkage int sunos_read(unsigned int fd, u32 buf, u32 count)
 {
 	int ret;
 
 	lock_kernel();
-	ret = check_nonblock(sys32_read(fd, buf, count), fd);
+	ret = check_nonblock(sys_read(fd, (char *)A(buf), count), fd);
 	unlock_kernel();
 	return ret;
 }
@@ -1356,12 +1330,12 @@
 	return ret;
 }
 
-asmlinkage int sunos_write(unsigned int fd, u32 buf, int count)
+asmlinkage int sunos_write(unsigned int fd, u32 buf, u32 count)
 {
 	int ret;
 
 	lock_kernel();
-	ret = check_nonblock(sys32_write(fd, buf, count), fd);
+	ret = check_nonblock(sys_write(fd, (char *)A(buf), count), fd);
 	unlock_kernel();
 	return ret;
 }
@@ -1381,7 +1355,7 @@
 	int ret;
 
 	lock_kernel();
-	ret = check_nonblock(sys32_recv(fd, ubuf, size, flags), fd);
+	ret = check_nonblock(sys_recv(fd, (void *)A(ubuf), size, flags), fd);
 	unlock_kernel();
 	return ret;
 }
@@ -1391,7 +1365,7 @@
 	int ret;
 
 	lock_kernel();
-	ret = check_nonblock(sys32_send(fd, buff, len, flags), fd);
+	ret = check_nonblock(sys_send(fd, (void *)A(buff), len, flags), fd);
 	unlock_kernel();
 	return ret;
 }
@@ -1401,78 +1375,48 @@
 	int ret;
 
 	lock_kernel();
-	ret = check_nonblock(sys32_accept(fd, sa, addrlen), fd);
+	ret = check_nonblock(sys_accept(fd, (struct sockaddr *)A(sa), (int *)A(addrlen)), fd);
 	unlock_kernel();
 	return ret;
 }
 
 #define SUNOS_SV_INTERRUPT 2
 
-extern void check_pending(int signum);
-
-asmlinkage int sunos_sigaction(int signum, u32 action, u32 oldaction)
+asmlinkage int sunos_sigaction (int sig, u32 act, u32 oact)
 {
-	struct sigaction32 new_sa, old_sa;
-	struct sigaction *p;
-	const int sigaction_size = sizeof (struct sigaction32) - sizeof (u32);
+	struct k_sigaction new_ka, old_ka;
+	int ret;
 
 	current->personality |= PER_BSD;
-	if(signum < 1 || signum > 32)
-		return -EINVAL;
 
-	p = signum - 1 + current->sig->action;
+	if (act) {
+		old_sigset_t32 mask;
 
-	if(action) {
-		if (signum==SIGKILL || signum==SIGSTOP)
-			return -EINVAL;
-		memset(&new_sa, 0, sizeof(struct sigaction32));
-		if(copy_from_user(&new_sa, (struct sigaction32 *)A(action),
-				  sigaction_size))
+		if (get_user((long)new_ka.sa.sa_handler, &((struct old_sigaction32 *)A(act))->sa_handler) ||
+		    __get_user(new_ka.sa.sa_flags, &((struct old_sigaction32 *)A(act))->sa_flags))
 			return -EFAULT;
-		if (((__sighandler_t)A(new_sa.sa_handler) != SIG_DFL) &&
-		     (__sighandler_t)A(new_sa.sa_handler) != SIG_IGN) {
-			if(verify_area(VERIFY_READ,
-				       (__sighandler_t)A(new_sa.sa_handler), 1))
-				return -EFAULT;
-		}
-		new_sa.sa_flags ^= SUNOS_SV_INTERRUPT;
+		__get_user(mask, &((struct old_sigaction32 *)A(act))->sa_mask);
+		new_ka.sa.sa_restorer = NULL;
+		new_ka.ka_restorer = NULL;
+		siginitset(&new_ka.sa.sa_mask, mask);
+		new_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT;
 	}
 
-	if (oldaction) {
-		/* In the clone() case we could copy half consistant
-		 * state to the user, however this could sleep and
-		 * deadlock us if we held the signal lock on SMP.  So for
-		 * now I take the easy way out and do no locking.
-		 * But then again we don't support SunOS lwp's anyways ;-)
-		 */
-		old_sa.sa_handler = (unsigned)(u64)(p->sa_handler);
-		old_sa.sa_mask = (sigset_t32)(p->sa_mask);
-		old_sa.sa_flags = (unsigned)(p->sa_flags);
-
-		if (old_sa.sa_flags & SA_RESTART)
-			old_sa.sa_flags &= ~SA_RESTART;
-		else
-			old_sa.sa_flags |= SUNOS_SV_INTERRUPT;
-		if (copy_to_user((struct sigaction32 *)A(oldaction),
-				 &old_sa, sigaction_size))
-			 return -EFAULT;
-	}
+	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
 
-	if (action) {
-		spin_lock_irq(&current->sig->siglock);
-		p->sa_handler = (__sighandler_t)A(new_sa.sa_handler);
-		p->sa_mask = (sigset_t)(new_sa.sa_mask);
-		p->sa_flags = new_sa.sa_flags;
-		p->sa_restorer = (void (*)(void))0;
-		check_pending(signum);
-		spin_unlock_irq(&current->sig->siglock);
+	if (!ret && oact) {
+		old_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT;
+		if (put_user((long)old_ka.sa.sa_handler, &((struct old_sigaction32 *)A(oact))->sa_handler) ||
+		    __put_user(old_ka.sa.sa_flags, &((struct old_sigaction32 *)A(oact))->sa_flags))
+			return -EFAULT;
+		__put_user(old_ka.sa.sa_mask.sig[0], &((struct old_sigaction32 *)A(oact))->sa_mask);
 	}
-	return 0;
-}
 
+	return ret;
+}
 
-extern asmlinkage int sys32_setsockopt(int fd, int level, int optname,
-				     u32 optval, int optlen);
+extern asmlinkage int sys_setsockopt(int fd, int level, int optname,
+				     char *optval, int optlen);
 extern asmlinkage int sys32_getsockopt(int fd, int level, int optname,
 				     u32 optval, u32 optlen);
 
@@ -1488,7 +1432,7 @@
 		if (tr_opt >=2 && tr_opt <= 6)
 			tr_opt += 30;
 	}
-	ret = sys32_setsockopt(fd, level, tr_opt, optval, optlen);
+	ret = sys_setsockopt(fd, level, tr_opt, (char *)A(optval), optlen);
 	unlock_kernel();
 	return ret;
 }
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/systbls.S linux/arch/sparc64/kernel/systbls.S
--- v2.1.78/linux/arch/sparc64/kernel/systbls.S	Wed Sep 24 20:05:46 1997
+++ linux/arch/sparc64/kernel/systbls.S	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.24 1997/08/22 20:12:06 davem Exp $
+/* $Id: systbls.S,v 1.37 1997/12/24 17:27:31 ecd Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -17,58 +17,58 @@
 
 	.globl sys_call_table32
 sys_call_table32:
-/*0*/	.word sys_setup, sys_exit, sys_fork, sys32_read, sys32_write
-/*5*/	.word sys32_open, sys_close, sys32_wait4, sys32_creat, sys32_link
-/*10*/  .word sys32_unlink, sunos_execv, sys32_chdir, sys_nis_syscall, sys32_mknod
-/*15*/	.word sys32_chmod, sys32_chown, sparc32_brk, sys_nis_syscall, sys32_lseek
+/*0*/	.word sys_setup, sys_exit, sys_fork, sys_read, sys_write
+/*5*/	.word sys_open, sys_close, sys32_wait4, sys_creat, sys_link
+/*10*/  .word sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys32_mknod
+/*15*/	.word sys32_chmod, sys32_chown, sparc_brk, sys_nis_syscall, sys32_lseek
 /*20*/	.word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid
-/*25*/	.word sys32_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_pause
-/*30*/	.word sys32_utime, sys_stty, sys_gtty, sys32_access, sys_nice
-	.word sys_ftime, sys_sync, sys_kill, sys32_newstat, sys_nis_syscall
-/*40*/	.word sys32_newlstat, sys_dup, sys_pipe, sys32_times, sys_profil
-	.word sys_nis_syscall, sys_setgid, sys_getgid, sys32_signal, sys_geteuid
-/*50*/	.word sys_getegid, sys32_acct, sys_nis_syscall, sys_nis_syscall, sys32_ioctl
-	.word sys32_reboot, sys_nis_syscall, sys32_symlink, sys32_readlink, sys32_execve
-/*60*/	.word sys_umask, sys32_chroot, sys32_newfstat, sys_nis_syscall, sys_getpagesize
-	.word sys32_msync, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-/*70*/	.word sys_nis_syscall, sys32_mmap, sys_nis_syscall, sys32_munmap, sys32_mprotect
+/*25*/	.word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys32_pause
+/*30*/	.word sys32_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice
+	.word sys_nis_syscall, sys_sync, sys_kill, sys32_newstat, sys_nis_syscall
+/*40*/	.word sys32_newlstat, sys_dup, sys_pipe, sys32_times, sys_nis_syscall
+	.word sys_nis_syscall, sys_setgid, sys_getgid, sys_signal, sys_geteuid
+/*50*/	.word sys_getegid, sys_acct, sys_nis_syscall, sys_nis_syscall, sys32_ioctl
+	.word sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys32_execve
+/*60*/	.word sys_umask, sys_chroot, sys32_newfstat, sys_nis_syscall, sys_getpagesize
+	.word sys_msync, sys_vfork, sys32_pread, sys32_pwrite, sys_nis_syscall
+/*70*/	.word sys_nis_syscall, sys32_mmap, sys_nis_syscall, sys_munmap, sys_mprotect
 	.word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys32_getgroups
 /*80*/	.word sys32_setgroups, sys_getpgrp, sys_nis_syscall, sys32_setitimer, sys_nis_syscall
-	.word sys32_swapon, sys32_getitimer, sys_nis_syscall, sys32_sethostname, sys_nis_syscall
+	.word sys_swapon, sys32_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall
 /*90*/	.word sys_dup2, sys_nis_syscall, sys32_fcntl, sys32_select, sys_nis_syscall
 	.word sys_fsync, sys_setpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-/*100*/	.word sys_getpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*100*/ .word sys_getpriority, sys32_rt_sigreturn, sys32_rt_sigaction, sys32_rt_sigprocmask, sys32_rt_sigpending
+	.word sys32_rt_sigtimedwait, sys32_rt_sigqueueinfo, sys32_rt_sigsuspend, sys_nis_syscall, sys_nis_syscall
 /*110*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
 	.word sys_nis_syscall, sys32_gettimeofday, sys32_getrusage, sys_nis_syscall, sys_nis_syscall
 /*120*/	.word sys32_readv, sys32_writev, sys32_settimeofday, sys_fchown, sys_fchmod
-	.word sys_nis_syscall, sys32_setreuid, sys_setregid, sys32_rename, sys32_truncate
-/*130*/	.word sys32_ftruncate, sys_flock, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-	.word sys_nis_syscall, sys32_mkdir, sys32_rmdir, sys_nis_syscall, sys_nis_syscall
+	.word sys_nis_syscall, sys32_setreuid, sys32_setregid, sys_rename, sys_truncate
+/*130*/	.word sys_ftruncate, sys_flock, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+	.word sys_nis_syscall, sys_mkdir, sys_rmdir, sys_nis_syscall, sys_nis_syscall
 /*140*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getrlimit
-	.word sys32_setrlimit, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-/*150*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-	.word sys_nis_syscall, sys_nis_syscall, sys32_statfs, sys32_fstatfs, sys32_umount
-/*160*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_setdomainname, sys_nis_syscall
-	.word sys32_quotactl, sys_nis_syscall, sys32_mount, sys32_ustat, sys_nis_syscall
+	.word sys32_setrlimit, sys_nis_syscall, sys32_prctl, sys32_pciconfig_read, sys32_pciconfig_write
+/*150*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_poll, sys_nis_syscall
+	.word sys_nis_syscall, sys_nis_syscall, sys32_statfs, sys32_fstatfs, sys_umount
+/*160*/	.word sys_nis_syscall, sys_nis_syscall, sys_getdomainname, sys_setdomainname, sys_nis_syscall
+	.word sys32_quotactl, sys_nis_syscall, sys32_mount, sys_ustat, sys_nis_syscall
 /*170*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getdents
 	.word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
 /*180*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_query_module
 	.word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_newuname
-/*190*/	.word sys32_init_module, sys32_personality, sys_prof, sys_break, sys_lock
-	.word sys_mpx, sys_ulimit, sys_getppid, sparc32_sigaction, sys_sgetmask
-/*200*/	.word sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys32_uselib, old32_readdir
-	.word sys_nis_syscall, sys32_socketcall, sys32_syslog, sys32_olduname, sys_nis_syscall
-/*210*/	.word sys_idle, sys_nis_syscall, sys32_waitpid, sys32_swapoff, sys32_sysinfo
-	.word sys32_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys32_adjtimex
+/*190*/	.word sys32_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+	.word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys32_sigaction, sys_sgetmask
+/*200*/	.word sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys_uselib, old32_readdir
+	.word sys_nis_syscall, sys32_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall
+/*210*/	.word sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys32_sysinfo
+	.word sys32_ipc, sys32_sigreturn, sys_clone, sys_nis_syscall, sys32_adjtimex
 /*220*/	.word sys32_sigprocmask, sys32_create_module, sys32_delete_module, sys32_get_kernel_syms, sys_getpgid
 	.word sys32_bdflush, sys32_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid
-/*230*/	.word sys32_llseek, sys32_time, sys_nis_syscall, sys_stime, sys_nis_syscall
-	.word sys_nis_syscall, sys32_llseek, sys32_mlock, sys32_munlock, sys_mlockall
-/*240*/	.word sys_munlockall, sys32_sched_setparam, sys32_sched_getparam, sys_nis_syscall, sys_nis_syscall
+/*230*/	.word sys32_select, sys_time, sys_nis_syscall, sys_stime, sys_nis_syscall
+	.word sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall
+/*240*/	.word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_nis_syscall, sys_nis_syscall
 	.word sys_nis_syscall, sys_sched_get_priority_max, sys_sched_get_priority_min, sys32_sched_rr_get_interval, sys32_nanosleep
-/*250*/	.word sys32_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys32_nfsservctl
-	.word sys_aplib, sys_prctl
+/*250*/	.word sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys32_nfsservctl
+	.word sys_aplib
 
 	/* Now the 64-bit native Linux syscall table. */
 
@@ -81,23 +81,23 @@
 /*10*/  .word sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys_mknod
 /*15*/	.word sys_chmod, sys_chown, sparc_brk, sys_nis_syscall, sys_lseek
 /*20*/	.word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid
-/*25*/	.word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_pause
-/*30*/	.word sys_utime, sys_stty, sys_gtty, sys_access, sys_nice
-	.word sys_ftime, sys_sync, sys_kill, sys_newstat, sys_nis_syscall
-/*40*/	.word sys_newlstat, sys_dup, sys_pipe, sys_times, sys_profil
+/*25*/	.word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_nis_syscall
+/*30*/	.word sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice
+	.word sys_nis_syscall, sys_sync, sys_kill, sys_newstat, sys_nis_syscall
+/*40*/	.word sys_newlstat, sys_dup, sys_pipe, sys_times, sys_nis_syscall
 	.word sys_nis_syscall, sys_setgid, sys_getgid, sys_signal, sys_geteuid
-/*50*/	.word sys_getegid, sys_acct, sys_nis_syscall, sys_nis_syscall, sys_ioctl
+/*50*/	.word sys_getegid, sys_acct, sys_memory_ordering, sys_nis_syscall, sys_ioctl
 	.word sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys_execve
 /*60*/	.word sys_umask, sys_chroot, sys_newfstat, sys_nis_syscall, sys_getpagesize
-	.word sys_nis_syscall, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+	.word sys_msync, sys_vfork, sys_pread, sys_pwrite, sys_nis_syscall
 /*70*/	.word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys_munmap, sys_mprotect
 	.word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys_getgroups
 /*80*/	.word sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall
 	.word sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall
 /*90*/	.word sys_dup2, sys_nis_syscall, sys_fcntl, sys_select, sys_nis_syscall
 	.word sys_fsync, sys_setpriority, sys_socket, sys_connect, sys_accept
-/*100*/	.word sys_getpriority, sys_send, sys_recv, sys_nis_syscall, sys_bind
-	.word sys_setsockopt, sys_listen, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*100*/	.word sys_getpriority, sys_rt_sigreturn, sys_rt_sigaction, sys_rt_sigprocmask, sys_rt_sigpending
+	.word sys_rt_sigtimedwait, sys_rt_sigqueueinfo, sys_rt_sigsuspend, sys_nis_syscall, sys_nis_syscall
 /*110*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_recvmsg, sys_sendmsg
 	.word sys_nis_syscall, sys_gettimeofday, sys_getrusage, sys_getsockopt, sys_nis_syscall
 /*120*/	.word sys_readv, sys_writev, sys_settimeofday, sys_fchown, sys_fchmod
@@ -105,29 +105,29 @@
 /*130*/	.word sys_ftruncate, sys_flock, sys_nis_syscall, sys_sendto, sys_shutdown
 	.word sys_socketpair, sys_mkdir, sys_rmdir, sys_nis_syscall, sys_nis_syscall
 /*140*/	.word sys_nis_syscall, sys_getpeername, sys_nis_syscall, sys_nis_syscall, sys_getrlimit
-	.word sys_setrlimit, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-/*150*/	.word sys_getsockname, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+	.word sys_setrlimit, sys_nis_syscall, sys_prctl, sys_pciconfig_read, sys_pciconfig_write
+/*150*/	.word sys_getsockname, sys_nis_syscall, sys_nis_syscall, sys_poll, sys_nis_syscall
 	.word sys_nis_syscall, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_umount
-/*160*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_setdomainname, sys_nis_syscall
+/*160*/	.word sys_nis_syscall, sys_nis_syscall, sys_getdomainname, sys_setdomainname, sys_utrap_install
 	.word sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_nis_syscall
 /*170*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getdents
 	.word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
 /*180*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_query_module
 	.word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_newuname
-/*190*/	.word sys_init_module, sys_personality, sys_prof, sys_break, sys_lock
-	.word sys_mpx, sys_ulimit, sys_getppid, sparc_sigaction, sys_sgetmask
+/*190*/	.word sys_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+	.word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys_sigaction, sys_sgetmask
 /*200*/	.word sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, sys_nis_syscall
 	.word sys_nis_syscall, sys_nis_syscall, sys_syslog, sys_nis_syscall, sys_nis_syscall
 /*210*/	.word sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo
 	.word sys_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys_adjtimex
 /*220*/	.word sys_sigprocmask, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid
 	.word sys_bdflush, sys_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid
-/*230*/	.word sys_llseek, sys_time, sys_nis_syscall, sys_stime, sys_nis_syscall
+/*230*/	.word sys_select, sys_time, sys_nis_syscall, sys_stime, sys_nis_syscall
 	.word sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall
 /*240*/	.word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_nis_syscall, sys_nis_syscall
 	.word sys_nis_syscall, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep
 /*250*/	.word sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
-	.word sys_aplib, sys_prctl
+	.word sys_aplib
 
 	/* Now the 32-bit SunOS syscall table. */
 
@@ -136,40 +136,40 @@
 sunos_sys_table:
 /*0*/	.word sunos_indir, sys_exit, sys_fork
 	.word sunos_read, sunos_write, sunos_open
-	.word sys_close, sunos_wait4, sys32_creat
-	.word sys32_link, sys32_unlink, sunos_execv
-	.word sys32_chdir, sunos_nosys, sys32_mknod
+	.word sys_close, sunos_wait4, sys_creat
+	.word sys_link, sys_unlink, sunos_execv
+	.word sys_chdir, sunos_nosys, sys32_mknod
 	.word sys32_chmod, sys32_chown, sunos_brk
 	.word sunos_nosys, sys32_lseek, sunos_getpid
 	.word sunos_nosys, sunos_nosys, sunos_nosys
 	.word sunos_getuid, sunos_nosys, sys_ptrace
 	.word sunos_nosys, sunos_nosys, sunos_nosys
 	.word sunos_nosys, sunos_nosys, sunos_nosys
-	.word sys32_access, sunos_nosys, sunos_nosys
+	.word sys_access, sunos_nosys, sunos_nosys
 	.word sys_sync, sys_kill, sys32_newstat
 	.word sunos_nosys, sys32_newlstat, sys_dup
-	.word sys_pipe, sunos_nosys, sys_profil
+	.word sys_pipe, sunos_nosys, sunos_nosys
 	.word sunos_nosys, sunos_nosys, sunos_getgid
 	.word sunos_nosys, sunos_nosys
-/*50*/	.word sunos_nosys, sys32_acct, sunos_nosys
-	.word sunos_mctl, sunos_ioctl, sys32_reboot
-	.word sunos_nosys, sys32_symlink, sys32_readlink
-	.word sys32_execve, sys_umask, sys32_chroot
+/*50*/	.word sunos_nosys, sys_acct, sunos_nosys
+	.word sunos_mctl, sunos_ioctl, sys_reboot
+	.word sunos_nosys, sys_symlink, sys_readlink
+	.word sys32_execve, sys_umask, sys_chroot
 	.word sys32_newfstat, sunos_nosys, sys_getpagesize
-	.word sys32_msync, sys_vfork, sunos_nosys
+	.word sys_msync, sys_vfork, sunos_nosys
 	.word sunos_nosys, sunos_sbrk, sunos_sstk
-	.word sunos_mmap, sunos_vadvise, sys32_munmap
-	.word sys32_mprotect, sunos_madvise, sys_vhangup
+	.word sunos_mmap, sunos_vadvise, sys_munmap
+	.word sys_mprotect, sunos_madvise, sys_vhangup
 	.word sunos_nosys, sunos_mincore, sys32_getgroups
 	.word sys32_setgroups, sys_getpgrp, sunos_setpgrp
-	.word sys32_setitimer, sunos_nosys, sys32_swapon
-	.word sys32_getitimer, sys32_gethostname, sys32_sethostname
+	.word sys32_setitimer, sunos_nosys, sys_swapon
+	.word sys32_getitimer, sys_gethostname, sys_sethostname
 	.word sunos_getdtablesize, sys_dup2, sunos_nop
 	.word sys32_fcntl, sunos_select, sunos_nop
 	.word sys_fsync, sys_setpriority, sys_socket
-	.word sys32_connect, sunos_accept
+	.word sys_connect, sunos_accept
 /*100*/	.word sys_getpriority, sunos_send, sunos_recv
-	.word sunos_nosys, sys32_bind, sunos_setsockopt
+	.word sunos_nosys, sys_bind, sunos_setsockopt
 	.word sys_listen, sunos_nosys, sunos_sigaction
 	.word sunos_sigblock, sunos_sigsetmask, sys_sigpause
 	.word sys32_sigstack, sys32_recvmsg, sys32_sendmsg
@@ -177,21 +177,21 @@
 	.word sunos_getsockopt, sunos_nosys, sunos_readv
 	.word sunos_writev, sys32_settimeofday, sys_fchown
 	.word sys_fchmod, sys32_recvfrom, sys32_setreuid
-	.word sys_setregid, sys32_rename, sys32_truncate
-	.word sys32_ftruncate, sys_flock, sunos_nosys
+	.word sys_setregid, sys_rename, sys_truncate
+	.word sys_ftruncate, sys_flock, sunos_nosys
 	.word sys32_sendto, sys_shutdown, sys_socketpair
-	.word sys32_mkdir, sys32_rmdir, sys32_utimes
-	.word sys_sigreturn, sunos_nosys, sys32_getpeername
+	.word sys_mkdir, sys_rmdir, sys32_utimes
+	.word sys32_sigreturn, sunos_nosys, sys_getpeername
 	.word sunos_gethostid, sunos_nosys, sys32_getrlimit
 	.word sys32_setrlimit, sunos_killpg, sunos_nosys
 	.word sunos_nosys, sunos_nosys
-/*150*/	.word sys32_getsockname, sunos_nosys, sunos_nosys
-	.word sys32_poll, sunos_nosys, sunos_nosys
+/*150*/	.word sys_getsockname, sunos_nosys, sunos_nosys
+	.word sys_poll, sunos_nosys, sunos_nosys
 	.word sunos_getdirentries, sys32_statfs, sys32_fstatfs
-	.word sys32_umount, sunos_nosys, sunos_nosys
-	.word sunos_getdomainname, sys32_setdomainname
+	.word sys_umount, sunos_nosys, sunos_nosys
+	.word sunos_getdomainname, sys_setdomainname
 	.word sunos_nosys, sys32_quotactl, sunos_nosys
-	.word sunos_mount, sys32_ustat, sunos_semsys
+	.word sunos_mount, sys_ustat, sunos_semsys
 	.word sunos_nosys, sunos_shmsys, sunos_audit
 	.word sunos_nosys, sunos_getdents, sys_setsid
 	.word sys_fchdir, sunos_nosys, sunos_nosys
@@ -221,4 +221,3 @@
 	.word sunos_nosys, sunos_nosys
 /*250*/	.word sunos_nosys, sunos_nosys, sunos_nosys
 	.word sunos_nosys, sunos_nosys, sys_aplib
-	.word sunos_nosys
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/traps.c linux/arch/sparc64/kernel/traps.c
--- v2.1.78/linux/arch/sparc64/kernel/traps.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/traps.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.31 1997/08/11 14:35:33 davem Exp $
+/* $Id: traps.c,v 1.44 1998/01/09 16:39:35 jj Exp $
  * arch/sparc64/kernel/traps.c
  *
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -26,9 +26,13 @@
 #include <asm/uaccess.h>
 #include <asm/fpumacro.h>
 #include <asm/lsu.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
 
 /* #define SYSCALL_TRACING */
 /* #define VERBOSE_SYSCALL_TRACING */
+/* #define DEBUG_FPU */
 
 #ifdef SYSCALL_TRACING
 #ifdef VERBOSE_SYSCALL_TRACING
@@ -125,8 +129,9 @@
 	int i;
 #endif
 
-	if(strcmp(current->comm, "bash.sunos"))
-		return;
+#if 0
+	if (!current->pid) return;
+#endif	
 	printk("SYS[%s:%d]: PC(%016lx) <%3d> ",
 	       current->comm, current->pid, regs->tpc, (int)g1);
 #ifdef VERBOSE_SYSCALL_TRACING
@@ -141,12 +146,20 @@
 		for(i = 0; i < sdp->num_args; i++) {
 			if(i)
 				printk(",");
-			if(!sdp->arg_is_string[i])
-				printk("%08x", (unsigned int)regs->u_regs[UREG_I0 + i]);
-			else {
-				strncpy_from_user(scall_strbuf,
-						  (char *)regs->u_regs[UREG_I0 + i],
-						  512);
+			if(!sdp->arg_is_string[i]) {
+				if (current->tss.flags & SPARC_FLAG_32BIT)
+					printk("%08x", (unsigned int)regs->u_regs[UREG_I0 + i]);
+				else
+					printk("%016lx", regs->u_regs[UREG_I0 + i]);
+			} else {
+				if (current->tss.flags & SPARC_FLAG_32BIT)
+					strncpy_from_user(scall_strbuf,
+							  (char *)(regs->u_regs[UREG_I0 + i] & 0xffffffff),
+							  512);
+				else
+					strncpy_from_user(scall_strbuf,
+							  (char *)regs->u_regs[UREG_I0 + i],
+							  512);
 				printk("%s", scall_strbuf);
 			}
 		}
@@ -157,7 +170,9 @@
 
 unsigned long syscall_trace_exit(unsigned long retval, struct pt_regs *regs)
 {
-	if(!strcmp(current->comm, "bash.sunos"))
+#if 0
+	if (current->pid)
+#endif
 		printk("ret[%016lx]\n", retval);
 	return retval;
 }
@@ -192,6 +207,24 @@
 
 void data_access_exception (struct pt_regs *regs)
 {
+	if (regs->tstate & TSTATE_PRIV) {
+		/* Test if this comes from uaccess places. */
+		unsigned long fixup, g2;
+
+		g2 = regs->u_regs[UREG_G2];
+		if ((fixup = search_exception_table (regs->tpc, &g2))) {
+			/* Ouch, somebody is trying ugly VM hole tricks on us... */
+#ifdef DEBUG_EXCEPTIONS
+			printk("Exception: PC<%016lx> faddr<UNKNOWN>\n", regs->tpc);
+			printk("EX_TABLE: insn<%016lx> fixup<%016lx> "
+			       "g2<%016lx>\n", regs->tpc, fixup, g2);
+#endif
+			regs->tpc = fixup;
+			regs->tnpc = regs->tpc + 4;
+			regs->u_regs[UREG_G2] = g2;
+			return;
+		}
+	}
 	send_sig(SIGSEGV, current, 1);
 }
 
@@ -271,11 +304,46 @@
 
 void do_fpieee(struct pt_regs *regs)
 {
+#ifdef DEBUG_FPU	
+	struct fpustate *f = FPUSTATE;
+
+	printk("fpieee %016lx\n", f->fsr);
+#endif
 	do_fpe_common(regs);
 }
 
+#ifdef CONFIG_MATHEMU_MODULE
+volatile int (*handle_mathemu)(struct pt_regs *, struct fpustate *) = NULL;
+#else
+extern int do_mathemu(struct pt_regs *, struct fpustate *);
+#endif
+
 void do_fpother(struct pt_regs *regs)
 {
+	struct fpustate *f = FPUSTATE;
+	int ret = 0;
+
+	switch ((f->fsr & 0x1c000)) {
+	case (2 << 14): /* unfinished_FPop */
+	case (3 << 14): /* unimplemented_FPop */
+#ifdef CONFIG_MATHEMU_MODULE
+#ifdef CONFIG_KERNELD
+		if (!handle_mathemu)
+			request_module("math-emu");
+#endif
+		if (handle_mathemu)
+			ret = handle_mathemu(regs, f);
+#else
+#ifdef CONFIG_MATHEMU
+		ret = do_mathemu(regs, f);
+#endif
+#endif
+		break;
+	}
+	if (ret) return;
+#ifdef DEBUG_FPU	
+	printk("fpother %016lx\n", f->fsr);
+#endif
 	do_fpe_common(regs);
 }
 
@@ -298,7 +366,7 @@
 	int i;
 	
 	if((((unsigned long) pc) & 3))
-                return;
+		return;
 
 	for(i = -3; i < 6; i++)
 		printk("%c%08x%c",i?' ':'<',pc[i],i?' ':'>');
@@ -332,33 +400,51 @@
 				(rw->ins[6] + STACK_BIAS);
 		}
 	}
-	printk("Instruction DUMP:");
-	instruction_dump ((unsigned int *) regs->tpc);
+	if(regs->tstate & TSTATE_PRIV) {
+		printk("Instruction DUMP:");
+		instruction_dump ((unsigned int *) regs->tpc);
+	}
 	lock_kernel(); /* Or else! */
 	if(regs->tstate & TSTATE_PRIV)
 		do_exit(SIGKILL);
 	do_exit(SIGSEGV);
 }
 
+extern int handle_popc(u32 insn, struct pt_regs *regs);
+extern int handle_ldq_stq(u32 insn, struct pt_regs *regs);
+
 void do_illegal_instruction(struct pt_regs *regs)
 {
 	unsigned long pc = regs->tpc;
 	unsigned long tstate = regs->tstate;
+	u32 insn;
 
 	if(tstate & TSTATE_PRIV)
 		die_if_kernel("Kernel illegal instruction", regs);
+	if(current->tss.flags & SPARC_FLAG_32BIT)
+		pc = (u32)pc;
+	if (get_user(insn, (u32 *)pc) != -EFAULT) {
+		if ((insn & 0xc1ffc000) == 0x81700000) /* POPC */ {
+			if (handle_popc(insn, regs))
+				return;
+		} else if ((insn & 0xc1580000) == 0xc1100000) /* LDQ/STQ */ {
+			if (handle_ldq_stq(insn, regs))
+				return;
+		}
+	}
 	current->tss.sig_address = pc;
 	current->tss.sig_desc = SUBSIG_ILLINST;
 	send_sig(SIGILL, current, 1);
 }
 
-void mem_address_unaligned(struct pt_regs *regs)
+void mem_address_unaligned(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr)
 {
 	if(regs->tstate & TSTATE_PRIV) {
 		extern void kernel_unaligned_trap(struct pt_regs *regs,
-						  unsigned int insn);
+						  unsigned int insn, 
+						  unsigned long sfar, unsigned long sfsr);
 
-		return kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc));
+		return kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc), sfar, sfsr);
 	} else {
 		current->tss.sig_address = regs->tpc;
 		current->tss.sig_desc = SUBSIG_PRIVINST;
@@ -390,22 +476,6 @@
 	send_sig(SIGILL, current, 1);
 }
 
-/* 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)
-{
-	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;
-	send_sig(SIGBUS, current, 1);
-}
-
 void handle_hw_divzero(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 		       unsigned long psr)
 {
@@ -463,19 +533,9 @@
 	die_if_kernel("TL1: IRQ Exception", regs);
 }
 
-void do_lddfmna(struct pt_regs *regs)
-{
-	die_if_kernel("TL0: LDDF Exception", regs);
-}
-
 void do_lddfmna_tl1(struct pt_regs *regs)
 {
 	die_if_kernel("TL1: LDDF Exception", regs);
-}
-
-void do_stdfmna(struct pt_regs *regs)
-{
-	die_if_kernel("TL0: STDF Exception", regs);
 }
 
 void do_stdfmna_tl1(struct pt_regs *regs)
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/ttable.S linux/arch/sparc64/kernel/ttable.S
--- v2.1.78/linux/arch/sparc64/kernel/ttable.S	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/ttable.S	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: ttable.S,v 1.20 1997/08/29 15:51:39 jj Exp $
+/* $Id: ttable.S,v 1.22 1997/10/16 07:07:46 jj Exp $
  * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -33,8 +33,8 @@
 tl0_dae:	TRAP(do_dae)
 tl0_resv033:	BTRAP(0x33)
 tl0_mna:	TRAP_NOSAVE(do_mna)
-tl0_lddfmna:	TRAP(do_lddfmna)
-tl0_stdfmna:	TRAP(do_stdfmna)
+tl0_lddfmna:	TRAP_NOSAVE(do_lddfmna)
+tl0_stdfmna:	TRAP_NOSAVE(do_stdfmna)
 tl0_privact:	TRAP(do_privact)
 tl0_resv038:	BTRAP(0x38) BTRAP(0x39) BTRAP(0x3a) BTRAP(0x3b) BTRAP(0x3c) BTRAP(0x3d)
 tl0_resv03e:	BTRAP(0x3e) BTRAP(0x3f) BTRAP(0x40)
@@ -106,10 +106,14 @@
 tl0_resv10a:	BTRAP(0x10a) BTRAP(0x10b) BTRAP(0x10c) BTRAP(0x10d) BTRAP(0x10e)
 tl0_resv10f:	BTRAP(0x10f)
 tl0_linux32:	LINUX_32BIT_SYSCALL_TRAP
-tl0_linux64:	LINUX_64BIT_SYSCALL_TRAP
-tl0_resv112:	BTRAP(0x112) BTRAP(0x113) BTRAP(0x114) BTRAP(0x115) BTRAP(0x116)
-tl0_resv117:	BTRAP(0x117) BTRAP(0x118) BTRAP(0x119) BTRAP(0x11a) BTRAP(0x11b)
-tl0_resv11c:	BTRAP(0x11c) BTRAP(0x11d) BTRAP(0x11e) BTRAP(0x11f)
+tl0_oldlinux64:	LINUX_64BIT_SYSCALL_TRAP
+tl0_resv112:	TRAP_UTRAP(UT_TRAP_INSTRUCTION_18,0x112) TRAP_UTRAP(UT_TRAP_INSTRUCTION_19,0x113)
+tl0_resv114:	TRAP_UTRAP(UT_TRAP_INSTRUCTION_20,0x114) TRAP_UTRAP(UT_TRAP_INSTRUCTION_21,0x115)
+tl0_resv116:	TRAP_UTRAP(UT_TRAP_INSTRUCTION_22,0x116) TRAP_UTRAP(UT_TRAP_INSTRUCTION_23,0x117)
+tl0_resv118:	TRAP_UTRAP(UT_TRAP_INSTRUCTION_24,0x118) TRAP_UTRAP(UT_TRAP_INSTRUCTION_25,0x119)
+tl0_resv11a:	TRAP_UTRAP(UT_TRAP_INSTRUCTION_26,0x11a) TRAP_UTRAP(UT_TRAP_INSTRUCTION_27,0x11b)
+tl0_resv11c:	TRAP_UTRAP(UT_TRAP_INSTRUCTION_28,0x11c) TRAP_UTRAP(UT_TRAP_INSTRUCTION_29,0x11d)
+tl0_resv11e:	TRAP_UTRAP(UT_TRAP_INSTRUCTION_30,0x11e) TRAP_UTRAP(UT_TRAP_INSTRUCTION_31,0x11f)
 tl0_getcc:	GETCC_TRAP
 tl0_setcc:	SETCC_TRAP
 tl0_resv122:	BTRAP(0x122) BTRAP(0x123) BTRAP(0x124) BTRAP(0x125) BTRAP(0x126)
@@ -127,7 +131,8 @@
 tl0_resv15a:	BTRAP(0x15a) BTRAP(0x15b) BTRAP(0x15c) BTRAP(0x15d) BTRAP(0x15e)
 tl0_resv15f:	BTRAP(0x15f) BTRAP(0x160) BTRAP(0x161) BTRAP(0x162) BTRAP(0x163)
 tl0_resv164:	BTRAP(0x164) BTRAP(0x165) BTRAP(0x166) BTRAP(0x167) BTRAP(0x168)
-tl0_resv169:	BTRAP(0x169) BTRAP(0x16a) BTRAP(0x16b) BTRAP(0x16c) BTRAP(0x16d)
+tl0_resv169:	BTRAP(0x169) BTRAP(0x16a) BTRAP(0x16b) BTRAP(0x16c)
+tl0_linux64:	LINUX_64BIT_SYSCALL_TRAP
 tl0_gsctx:	TRAP(sparc64_get_context) TRAP(sparc64_set_context)
 tl0_resv170:	BTRAP(0x170) BTRAP(0x171) 
 #ifdef CONFIG_EC_FLUSH_TRAP
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/unaligned.c linux/arch/sparc64/kernel/unaligned.c
--- v2.1.78/linux/arch/sparc64/kernel/unaligned.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/kernel/unaligned.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: unaligned.c,v 1.4 1997/08/19 15:25:11 jj Exp $
+/* $Id: unaligned.c,v 1.8 1997/10/14 16:21:24 jj Exp $
  * unaligned.c: Unaligned load/store trap handling with special
  *              cases for the kernel to do them more quickly.
  *
@@ -17,6 +17,8 @@
 #include <asm/uaccess.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <asm/fpumacro.h>
+#include <asm/bitops.h>
 
 /* #define DEBUG_MNA */
 
@@ -24,8 +26,8 @@
 	load,    /* ld, ldd, ldh, ldsh */
 	store,   /* st, std, sth, stsh */
 	both,    /* Swap, ldstub, cas, ... */
-	fpload,
-	fpstore,
+	fpld,
+	fpst,
 	invalid,
 };
 
@@ -101,34 +103,52 @@
 	return imm << 51 >> 51;
 }
 
-static inline unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
+static unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
 {
-	struct reg_window *win;
-
+	unsigned long value;
+	
 	if(reg < 16)
 		return (!reg ? 0 : regs->u_regs[reg]);
-
-	/* Ho hum, the slightly complicated case. */
-	win = (struct reg_window *) regs->u_regs[UREG_FP];
-	return win->locals[reg - 16]; /* yes, I know what this does... */
+	if (regs->tstate & TSTATE_PRIV) {
+		struct reg_window *win;
+		win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+		value = win->locals[reg - 16];
+	} else if (current->tss.flags & SPARC_FLAG_32BIT) {
+		struct reg_window32 *win32;
+		win32 = (struct reg_window32 *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
+		get_user(value, &win32->locals[reg - 16]);
+	} else {
+		struct reg_window *win;
+		win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+		get_user(value, &win->locals[reg - 16]);
+	}
+	return value;
 }
 
-static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
+static unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
 {
-	struct reg_window *win;
-
 	if(reg < 16)
 		return &regs->u_regs[reg];
-	win = (struct reg_window *) regs->u_regs[UREG_FP];
-	return &win->locals[reg - 16];
+	if (regs->tstate & TSTATE_PRIV) {
+		struct reg_window *win;
+		win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+		return &win->locals[reg - 16];
+	} else if (current->tss.flags & SPARC_FLAG_32BIT) {
+		struct reg_window32 *win32;
+		win32 = (struct reg_window32 *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
+		return (unsigned long *)&win32->locals[reg - 16];
+	} else {
+		struct reg_window *win;
+		win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+		return &win->locals[reg - 16];
+	}
 }
 
 static inline unsigned long compute_effective_address(struct pt_regs *regs,
-						      unsigned int insn)
+						      unsigned int insn, unsigned int rd)
 {
 	unsigned int rs1 = (insn >> 14) & 0x1f;
 	unsigned int rs2 = insn & 0x1f;
-	unsigned int rd = (insn >> 25) & 0x1f;
 
 	if(insn & 0x2000) {
 		maybe_flush_windows(rs1, 0, rd);
@@ -326,7 +346,7 @@
 	unsigned long fixup = search_exception_table (regs->tpc, &g2);
 
 	if (!fixup) {
-		unsigned long address = compute_effective_address(regs, insn);
+		unsigned long address = compute_effective_address(regs, insn, ((insn >> 25) & 0x1f));
         	if(address < PAGE_SIZE) {
                 	printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference in mna handler");
         	} else
@@ -344,7 +364,7 @@
 	regs->u_regs [UREG_G2] = g2;
 }
 
-asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn)
+asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, unsigned long sfar, unsigned long sfsr)
 {
 	enum direction dir = decode_direction(insn);
 	int size = decode_access_size(insn);
@@ -365,7 +385,7 @@
 		: "o0", "o1", "o2", "o3", "o4", "o5", "o7",
 		  "g1", "g2", "g3", "g4", "g5", "g7", "cc");
 	} else {
-		unsigned long addr = compute_effective_address(regs, insn);
+		unsigned long addr = compute_effective_address(regs, insn, ((insn >> 25) & 0x1f));
 
 #ifdef DEBUG_MNA
 		printk("KMNA: pc=%016lx [dir=%s addr=%016lx size=%d] retpc[%016lx]\n",
@@ -401,117 +421,243 @@
 	unlock_kernel();
 }
 
-#if 0 /* XXX: Implement user mna some day */
-static inline int ok_for_user(struct pt_regs *regs, unsigned int insn,
-			      enum direction dir)
-{
-	unsigned int reg;
-	int retval, check = (dir == load) ? VERIFY_READ : VERIFY_WRITE;
-	int size = ((insn >> 19) & 3) == 3 ? 8 : 4;
-
-	if((regs->pc | regs->npc) & 3)
-		return 0;
+static char popc_helper[] = {
+0, 1, 1, 2, 1, 2, 2, 3,
+1, 2, 2, 3, 2, 3, 3, 4, 
+};
 
-	/* Must verify_area() in all the necessary places. */
-#define WINREG_ADDR(regnum) ((void *)(((unsigned long *)regs->u_regs[UREG_FP])+(regnum)))
-	retval = 0;
-	reg = (insn >> 25) & 0x1f;
-	if(reg >= 16) {
-		retval = verify_area(check, WINREG_ADDR(reg - 16), size);
-		if(retval)
-			return retval;
+int handle_popc(u32 insn, struct pt_regs *regs)
+{
+	u64 value;
+	int ret, i, rd = ((insn >> 25) & 0x1f);
+	                        
+	if (insn & 0x2000) {
+		maybe_flush_windows(0, 0, rd);
+		value = sign_extend_imm13(insn);
+	} else {
+		maybe_flush_windows(0, insn & 0x1f, rd);
+		value = fetch_reg(insn & 0x1f, regs);
 	}
-	reg = (insn >> 14) & 0x1f;
-	if(reg >= 16) {
-		retval = verify_area(check, WINREG_ADDR(reg - 16), size);
-		if(retval)
-			return retval;
+	for (ret = 0, i = 0; i < 16; i++) {
+		ret += popc_helper[value & 0xf];
+		value >>= 4;
 	}
-	if(!(insn & 0x2000)) {
-		reg = (insn & 0x1f);
-		if(reg >= 16) {
-			retval = verify_area(check, WINREG_ADDR(reg - 16), size);
-			if(retval)
-				return retval;
+	if(rd < 16) {
+		if (rd)
+			regs->u_regs[rd] = ret;
+	} else {
+		if (current->tss.flags & SPARC_FLAG_32BIT) {
+			struct reg_window32 *win32;
+			win32 = (struct reg_window32 *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
+			put_user(ret, &win32->locals[rd - 16]);
+		} else {
+			struct reg_window *win;
+			win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+			get_user(ret, &win->locals[rd - 16]);
 		}
 	}
-	return retval;
-#undef WINREG_ADDR
+	advance(regs);
+	return 1;
 }
 
-void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("user_mna_trap_fault");
+extern void do_fpother(struct pt_regs *regs);
+extern void do_privact(struct pt_regs *regs);
+extern void data_access_exception(struct pt_regs *regs);
+
+int handle_ldq_stq(u32 insn, struct pt_regs *regs)
+{
+	unsigned long addr = compute_effective_address(regs, insn, 0);
+	int freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
+	struct fpustate *f = FPUSTATE;
+	int asi = decode_asi(insn, regs);
+	int flag = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU;
+
+	f->fsr &= ~0x1c000;
+	if (freg & 3) {
+		f->fsr |= (6 << 14) /* invalid_fp_register */;
+		do_fpother(regs);
+		return 0;
+	}
+	if (insn & 0x200000) {
+		/* STQ */
+		u64 first = 0, second = 0;
+		
+		if (current->tss.flags & flag) {
+			first = *(u64 *)&f->regs[freg];
+			second = *(u64 *)&f->regs[freg+2];
+		}
+		if (asi < 0x80) {
+			do_privact(regs);
+			return 1;
+		}
+		switch (asi) {
+		case ASI_P:
+		case ASI_S: break;
+		case ASI_PL:
+		case ASI_SL: 
+			{
+				/* Need to convert endians */
+				u64 tmp = __swab64p(&first);
+				
+				first = __swab64p(&second);
+				second = tmp;
+				break;
+			}
+		default:
+			data_access_exception(regs);
+			return 1;
+		}
+		if (put_user (first >> 32, (u32 *)addr) ||
+		    __put_user ((u32)first, (u32 *)(addr + 4)) ||
+		    __put_user (second >> 32, (u32 *)(addr + 8)) ||
+		    __put_user ((u32)second, (u32 *)(addr + 12))) {
+		    	data_access_exception(regs);
+		    	return 1;
+		}
+	} else {
+		/* LDQ */
+		u32 first, second, third, fourth;
 
-void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
-{
-	current->tss.sig_address = regs->pc;
-	current->tss.sig_desc = SUBSIG_PRIVINST;
-	send_sig(SIGBUS, current, 1);
+		if (asi < 0x80) {
+			do_privact(regs);
+			return 1;
+		} else if (asi > ASI_SNFL) {
+			data_access_exception(regs);
+			return 1;
+		}
+		if (get_user (first, (u32 *)addr) ||
+		    __get_user (second, (u32 *)(addr + 4)) ||
+		    __get_user (third, (u32 *)(addr + 8)) ||
+		    __get_user (fourth, (u32 *)(addr + 12))) {
+			if (asi & 0x2) /* NF */ {
+				first = 0; second = 0; third = 0; fourth = 0;
+			} else {
+		    		data_access_exception(regs);
+		    		return 1;
+		    	}
+		}
+		if (asi & 0x8) /* Little */ {
+			u32 tmp = le32_to_cpup(&first);
+			
+			first = le32_to_cpup(&fourth);
+			fourth = tmp;
+			tmp = le32_to_cpup(&second);
+			second = le32_to_cpup(&third);
+			third = tmp;
+		}
+		regs->fprs |= FPRS_FEF;
+		if (!(current->tss.flags & SPARC_FLAG_USEDFPU)) {
+			current->tss.flags |= SPARC_FLAG_USEDFPU;
+			f->fsr = 0;
+			f->gsr = 0;
+		}
+		if (!(current->tss.flags & flag)) {
+			if (freg < 32)
+				memset(f->regs, 0, 32*sizeof(u32));
+			else
+				memset(f->regs+32, 0, 32*sizeof(u32));
+		}
+		f->regs[freg] = first;
+		f->regs[freg+1] = second;
+		f->regs[freg+2] = third;
+		f->regs[freg+3] = fourth;
+		current->tss.flags |= flag;
+	}
+	advance(regs);
+	return 1;
 }
 
-asmlinkage void user_unaligned_trap(struct pt_regs *regs, unsigned int insn)
+void handle_lddfmna(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr)
 {
-	enum direction dir;
-
-	lock_kernel();
-	if(!(current->tss.flags & SPARC_FLAG_UNALIGNED) ||
-	   (((insn >> 30) & 3) != 3))
-		goto kill_user;
-	dir = decode_direction(insn);
-	if(!ok_for_user(regs, insn, dir)) {
-		goto kill_user;
-	} else {
-		int size = decode_access_size(insn);
-		unsigned long addr;
-
-		if(floating_point_load_or_store_p(insn)) {
-			printk("User FPU load/store unaligned unsupported.\n");
-			goto kill_user;
+	unsigned long pc = regs->tpc;
+	unsigned long tstate = regs->tstate;
+	u32 insn;
+	u32 first, second;
+	u64 value;
+	u8 asi, freg;
+	int flag;
+	struct fpustate *f = FPUSTATE;
+
+	if(tstate & TSTATE_PRIV)
+		die_if_kernel("lddfmna from kernel", regs);
+	if(current->tss.flags & SPARC_FLAG_32BIT)
+		pc = (u32)pc;
+	if (get_user(insn, (u32 *)pc) != -EFAULT) {
+		asi = sfsr >> 16;
+		if (asi > ASI_SNFL)
+			goto daex;
+		if (get_user(first, (u32 *)sfar) ||
+		     get_user(second, (u32 *)(sfar + 4))) {
+			if (asi & 0x2) /* NF */ {
+				first = 0; second = 0;
+			} else
+				goto daex;
 		}
-
-		addr = compute_effective_address(regs, insn);
-		switch(dir) {
-		case load:
-			do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs),
-					size, (unsigned long *) addr,
-					decode_signedness(insn),
-					user_unaligned_trap_fault);
-			break;
-
-		case store:
-			do_integer_store(((insn>>25)&0x1f), size,
-					 (unsigned long *) addr, regs,
-					 user_unaligned_trap_fault);
-			break;
-
-		case both:
-			do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs),
-				  (unsigned long *) addr,
-				  user_unaligned_trap_fault);
-			break;
-
-		default:
-			unaligned_panic("Impossible user unaligned trap.");
-
-			__asm__ __volatile__ ("\n"
-"user_unaligned_trap_fault:\n\t"
-			"mov	%0, %%o0\n\t"
-			"call	user_mna_trap_fault\n\t"
-			" mov	%1, %%o1\n\t"
-			:
-			: "r" (regs), "r" (insn)
-			: "o0", "o1", "o2", "o3", "o4", "o5", "o7",
-			  "g1", "g2", "g3", "g4", "g5", "g7", "cc");
-			goto out;
+		freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
+		value = (((u64)first) << 32) | second;
+		if (asi & 0x8) /* Little */
+			value = __swab64p(&value);
+		flag = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU;
+		regs->fprs |= FPRS_FEF;
+		if (!(current->tss.flags & SPARC_FLAG_USEDFPU)) {
+			current->tss.flags |= SPARC_FLAG_USEDFPU;
+			f->fsr = 0;
+			f->gsr = 0;
 		}
-		advance(regs);
-		goto out;
+		if (!(current->tss.flags & flag)) {
+			if (freg < 32)
+				memset(f->regs, 0, 32*sizeof(u32));
+			else
+				memset(f->regs+32, 0, 32*sizeof(u32));
+		}
+		*(u64 *)(f->regs + freg) = value;
+		current->tss.flags |= flag;
+	} else {
+daex:		data_access_exception(regs);
+		return;
 	}
+	advance(regs);
+	return;
+}
 
-kill_user:
-	current->tss.sig_address = regs->pc;
-	current->tss.sig_desc = SUBSIG_PRIVINST;
-	send_sig(SIGBUS, current, 1);
-out:
-	unlock_kernel();
+void handle_stdfmna(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr)
+{
+	unsigned long pc = regs->tpc;
+	unsigned long tstate = regs->tstate;
+	u32 insn;
+	u64 value;
+	u8 asi, freg;
+	int flag;
+	struct fpustate *f = FPUSTATE;
+
+	if(tstate & TSTATE_PRIV)
+		die_if_kernel("stdfmna from kernel", regs);
+	if(current->tss.flags & SPARC_FLAG_32BIT)
+		pc = (u32)pc;
+	if (get_user(insn, (u32 *)pc) != -EFAULT) {
+		freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
+		asi = sfsr >> 16;
+		value = 0;
+		flag = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU;
+		if (asi > ASI_SNFL)
+			goto daex;
+		if (current->tss.flags & flag)
+			value = *(u64 *)&f->regs[freg];
+		switch (asi) {
+		case ASI_P:
+		case ASI_S: break;
+		case ASI_PL:
+		case ASI_SL: 
+			value = __swab64p(&value); break;
+		default: goto daex;
+		}
+		if (put_user (value >> 32, (u32 *)sfar) ||
+		    __put_user ((u32)value, (u32 *)(sfar + 4)))
+			goto daex;
+	} else {
+daex:		data_access_exception(regs);
+		return;
+	}
+	advance(regs);
+	return;
 }
-#endif
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/winfixup.S linux/arch/sparc64/kernel/winfixup.S
--- v2.1.78/linux/arch/sparc64/kernel/winfixup.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/winfixup.S	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: winfixup.S,v 1.19 1997/08/08 08:33:37 jj Exp $
+/* $Id: winfixup.S,v 1.22 1997/10/24 11:57:48 jj Exp $
  *
  * winfixup.S: Handle cases where user stack pointer is found to be bogus.
  *
@@ -48,6 +48,10 @@
 	 * most things are where they need to be, we also have the address
 	 * which triggered the fault handy as well.
 	 *
+	 * Also note that we must preserve %l5 and %l6.  If the user was
+	 * returning from a system call, we must make it look this way
+	 * after we process the fill fault on the users stack.
+	 *
 	 * First, get into the window where the original restore was executed.
 	 */
 
@@ -65,15 +69,23 @@
 	rdpr		%pstate, %l1			! Prepare to change globals.
 
 	mov		%g6, %o7			! Get current.
-	mov		%g5, %l5			! Fault address
-	clr		%l4				! It was a load, not a store
+	andn		%l1, PSTATE_MM, %l1		! We want to be in RMO
+	srlx		%g5, PAGE_SHIFT, %o1		! Fault address
 	wrpr		%g0, 0x0, %tl			! Out of trap levels.
 	wrpr		%l1, (PSTATE_IE | PSTATE_AG | PSTATE_RMO), %pstate
 	sethi		%uhi(PAGE_OFFSET), %g4		! Prepare page_offset global reg
 	mov		%o7, %g6
-	b,pt		%xcc, window_scheisse_merge	! And merge.
+	sllx		%g4, 32, %g4			! and finish it...
+	clr		%o2
 
-	 sllx		%g4, 32, %g4			! and finish it...
+	/* This is the same as below, except we handle this a bit special
+	 * since we must preserve %l5 and %l6, see comment above.
+	 */
+	sllx		%o1, PAGE_SHIFT, %o1
+	call		do_sparc64_fault
+	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+	b,pt		%xcc, rtrap
+	 nop						! yes, nop is correct
 
 	/* Be very careful about usage of the alternate globals here.
 	 * You cannot touch %g4/%g5 as that has the fault information
@@ -84,59 +96,59 @@
 	 * do not touch %g7 or %g2 so we handle the two cases fine.
 	 */
 spill_fixup:
-	ld		[%g6 + AOFF_task_tss + AOFF_thread_flags], %g1
+	lduh		[%g6 + AOFF_task_tss + AOFF_thread_flags], %g1
 	andcc		%g1, SPARC_FLAG_32BIT, %g0
-	ldx		[%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1
+	lduh		[%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1
+
 	sll		%g1, 3, %g3
 	add		%g6, %g3, %g3
 	stx		%sp, [%g3 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs]
 	sll		%g1, 7, %g3
-
 	bne,pt		%xcc, 1f
 	 add		%g6, %g3, %g3
 	stx		%l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00]
 	stx		%l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08]
+
 	stx		%l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10]
 	stx		%l3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18]
 	stx		%l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20]
 	stx		%l5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28]
-
 	stx		%l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30]
 	stx		%l7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38]
 	stx		%i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x40]
 	stx		%i1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x48]
+
 	stx		%i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x50]
 	stx		%i3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x58]
 	stx		%i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x60]
 	stx		%i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x68]
-
 	stx		%i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x70]
 	b,pt		%xcc, 2f
 	 stx		%i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78]
 1:	stw		%l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00]
+
 	stw		%l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x04]
 	stw		%l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08]
 	stw		%l3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x0c]
 	stw		%l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10]
-
 	stw		%l5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x14]
 	stw		%l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18]
 	stw		%l7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x1c]
 	stw		%i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20]
+
 	stw		%i1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x24]
 	stw		%i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28]
 	stw		%i3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x2c]
 	stw		%i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30]
-
 	stw		%i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x34]
 	stw		%i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38]
 	stw		%i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x3c]
 2:	add		%g1, 1, %g1
-	stx		%g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved]
+
+	sth		%g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved]
 	rdpr		%tstate, %g1
 	andcc		%g1, TSTATE_PRIV, %g0
 	saved
-
 	and		%g1, TSTATE_CWP, %g1
 	be,a,pn		%xcc, window_scheisse_from_user_common
 	 or		%g4, 0x4, %g4			! we know it was a write
@@ -146,7 +158,6 @@
 	sethi		%hi(109f), %g7
 	ba,pt		%xcc, etrap
 109:	 or		%g7, %lo(109b), %g7
-window_scheisse_merge:
 	srlx		%l5, PAGE_SHIFT, %o1
 
 	and		%l4, 0x4, %o2
@@ -173,10 +184,15 @@
 	andcc		%g1, TSTATE_PRIV, %g0
 	be,pt		%xcc, window_mna_from_user_common
 	 and		%g1, TSTATE_CWP, %g1
+
+	/* Please, see fill_fixup commentary about why we must preserve
+	 * %l5 and %l6 to preserve absolute correct semantics.
+	 */
 	rdpr		%wstate, %g2			! Grab user mode wstate.
 	wrpr		%g1, %cwp			! Get into the right window.
 	sll		%g2, 3, %g2			! NORMAL-->OTHER
 	wrpr		%g0, 0x0, %canrestore		! Standard etrap stuff.
+
 	wrpr		%g2, 0x0, %wstate		! This must be consistant.
 	wrpr		%g0, 0x0, %otherwin		! We know this.
 	mov		PRIMARY_CONTEXT, %g1		! Change contexts...
@@ -185,23 +201,28 @@
 	rdpr		%pstate, %l1			! Prepare to change globals.
 	mov		%g4, %o5			! Setup args for
 	mov		%g5, %o4			! final call to do_sparc64_fault.
+	andn		%l1, PSTATE_MM, %l1		! We want to be in RMO
+
 	mov		%g6, %o7			! Stash away current.
 	wrpr		%g0, 0x0, %tl			! Out of trap levels.
 	wrpr		%l1, (PSTATE_IE | PSTATE_AG | PSTATE_RMO), %pstate
 	sethi		%uhi(PAGE_OFFSET), %g4		! Set page_offset global reg.
 	mov		%o7, %g6			! Get current back.
-	b,pt		%xcc, window_mna_merge		! And merge.
-	 sllx		%g4, 32, %g4			! Finish it.
+	sllx		%g4, 32, %g4			! Finish it.
+	call		mem_address_unaligned
+	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
 
+	b,pt		%xcc, rtrap
+	 nop						! yes, the nop is correct
 spill_fixup_mna:
-	ld		[%g6 + AOFF_task_tss + AOFF_thread_flags], %g1
+	lduh		[%g6 + AOFF_task_tss + AOFF_thread_flags], %g1
 	andcc		%g1, SPARC_FLAG_32BIT, %g0
-	ldx		[%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1
+	lduh		[%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1
 	sll		%g1, 3, %g3
 	add		%g6, %g3, %g3
 	stx		%sp, [%g3 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs]
-	sll		%g1, 7, %g3
 
+	sll		%g1, 7, %g3
 	bne,pt		%xcc, 1f
 	 add		%g6, %g3, %g3
 	stx		%l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00]
@@ -209,8 +230,8 @@
 	stx		%l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10]
 	stx		%l3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18]
 	stx		%l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20]
-	stx		%l5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28]
 
+	stx		%l5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28]
 	stx		%l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30]
 	stx		%l7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38]
 	stx		%i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x40]
@@ -218,8 +239,8 @@
 	stx		%i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x50]
 	stx		%i3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x58]
 	stx		%i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x60]
-	stx		%i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x68]
 
+	stx		%i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x68]
 	stx		%i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x70]
 	stx		%i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78]
 	b,pt		%xcc, 2f
@@ -227,16 +248,15 @@
 1:	std		%l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00]
 	std		%l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08]
 	std		%l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10]
-	std		%l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18]
 
+	std		%l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18]
 	std		%i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20]
 	std		%i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28]
 	std		%i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30]
 	std		%i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38]
 	add		%g1, 1, %g1
-2:	stx		%g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved]
+2:	sth		%g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved]
 	rdpr		%tstate, %g1
-	nop
 
 	andcc		%g1, TSTATE_PRIV, %g0
 	saved
@@ -248,7 +268,6 @@
 	sethi		%hi(109f), %g7
 	ba,pt		%xcc, etrap
 109:	 or		%g7, %lo(109b), %g7
-window_mna_merge:
 	call		mem_address_unaligned
 	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
 	ba,pt		%xcc, rtrap
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/lib/checksum.S linux/arch/sparc64/lib/checksum.S
--- v2.1.78/linux/arch/sparc64/lib/checksum.S	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/lib/checksum.S	Mon Jan 12 15:15:44 1998
@@ -35,204 +35,6 @@
 	/* I think I have an erection...  Once _AGAIN_ the SunSoft
 	 * engineers are caught asleep at the keyboard, tsk tsk...
 	 */
-#define CSUMCOPY_ECACHE_LOAD(off, t0, t1, t2, t3, t4, t5, t6, t7)			\
-	ldxa		[%src + off + 0x00] %asi, t0;					\
-	ldxa		[%src + off + 0x08] %asi, t1;					\
-	ldxa		[%src + off + 0x10] %asi, t2;					\
-	ldxa		[%src + off + 0x18] %asi, t3;					\
-	ldxa		[%src + off + 0x20] %asi, t4;					\
-	ldxa		[%src + off + 0x28] %asi, t5;					\
-	ldxa		[%src + off + 0x30] %asi, t6;					\
-	ldxa		[%src + off + 0x38] %asi, t7;					\
-	nop; nop; /* DO NOT TOUCH THIS!!!!! */
-
-#define CSUMCOPY_EC_STALIGNED_LDNXT(off, t0, t1, t2, t3, t4, t5, t6, t7)		\
-	stx		t0, [%dst + off - 0x40];					\
-	addcc		%sum, t0, %sum;							\
-	bcc,pt		%xcc, 11f;							\
-	 ldxa		[%src + off + 0x00] %asi, t0;					\
-	add		%sum, 1, %sum;							\
-11:	stx		t1, [%dst + off - 0x38];					\
-	addcc		%sum, t1, %sum;							\
-	bcc,pt		%xcc, 12f;							\
-	 ldxa		[%src + off + 0x08] %asi, t1;					\
-	add		%sum, 1, %sum;							\
-12:	stx		t2, [%dst + off - 0x30];					\
-	addcc		%sum, t2, %sum;							\
-	bcc,pt		%xcc, 13f;							\
-	 ldxa		[%src + off + 0x10] %asi, t2;					\
-	add		%sum, 1, %sum;							\
-13:	stx		t3, [%dst + off - 0x28];					\
-	addcc		%sum, t3, %sum;							\
-	bcc,pt		%xcc, 14f;							\
-	 ldxa		[%src + off + 0x18] %asi, t3;					\
-	add		%sum, 1, %sum;							\
-14:	stx		t4, [%dst + off - 0x20];					\
-	addcc		%sum, t4, %sum;							\
-	bcc,pt		%xcc, 15f;							\
-	 ldxa		[%src + off + 0x20] %asi, t4;					\
-	add		%sum, 1, %sum;							\
-15:	stx		t5, [%dst + off - 0x18];					\
-	addcc		%sum, t5, %sum;							\
-	bcc,pt		%xcc, 16f;							\
-	 ldxa		[%src + off + 0x28] %asi, t5;					\
-	add		%sum, 1, %sum;							\
-16:	stx		t6, [%dst + off - 0x10];					\
-	addcc		%sum, t6, %sum;							\
-	bcc,pt		%xcc, 17f;							\
-	 ldxa		[%src + off + 0x30] %asi, t6;					\
-	add		%sum, 1, %sum;							\
-17:	stx		t7, [%dst + off - 0x08];					\
-	addcc		%sum, t7, %sum;							\
-	bcc,pt		%xcc, 18f;							\
-	 ldxa		[%src + off + 0x38] %asi, t7;					\
-	add		%sum, 1, %sum;							\
-18:
-
-#define CSUMCOPY_EC_STUNALIGN_LDNXT(off, t0, t1, t2, t3, t4, t5, t6, t7)		\
-	stw		t0, [%dst + off - 0x3c];					\
-	addcc		%sum, t0, %sum;							\
-	srlx		t0, 32, t0;							\
-	stw		t0, [%dst + off - 0x40];					\
-	bcc,pt		%xcc, 21f;							\
-	 ldxa		[%src + off + 0x00] %asi, t0;					\
-	add		%sum, 1, %sum;							\
-21:	stw		t1, [%dst + off - 0x34];					\
-	addcc		%sum, t1, %sum;							\
-	srlx		t1, 32, t1;							\
-	stw		t1, [%dst + off - 0x38];					\
-	bcc,pt		%xcc, 22f;							\
-	 ldxa		[%src + off + 0x08] %asi, t1;					\
-	add		%sum, 1, %sum;							\
-22:	stw		t2, [%dst + off - 0x2c];					\
-	addcc		%sum, t2, %sum;							\
-	srlx		t2, 32, t2;							\
-	stw		t2, [%dst + off - 0x30];					\
-	bcc,pt		%xcc, 23f;							\
-	 ldxa		[%src + off + 0x10] %asi, t2;					\
-	add		%sum, 1, %sum;							\
-23:	stw		t3, [%dst + off - 0x24];					\
-	addcc		%sum, t3, %sum;							\
-	srlx		t3, 32, t3;							\
-	stw		t3, [%dst + off - 0x28];					\
-	bcc,pt		%xcc, 24f;							\
-	 ldxa		[%src + off + 0x18] %asi, t3;					\
-	add		%sum, 1, %sum;							\
-24:	stw		t4, [%dst + off - 0x1c];					\
-	addcc		%sum, t4, %sum;							\
-	srlx		t4, 32, t4;							\
-	stw		t4, [%dst + off - 0x20];					\
-	bcc,pt		%xcc, 25f;							\
-	 ldxa		[%src + off + 0x20] %asi, t4;					\
-	add		%sum, 1, %sum;							\
-25:	stw		t5, [%dst + off - 0x14];					\
-	addcc		%sum, t5, %sum;							\
-	srlx		t5, 32, t5;							\
-	stw		t5, [%dst + off - 0x18];					\
-	bcc,pt		%xcc, 26f;							\
-	 ldxa		[%src + off + 0x28] %asi, t5;					\
-	add		%sum, 1, %sum;							\
-26:	stw		t6, [%dst + off - 0x0c];					\
-	addcc		%sum, t6, %sum;							\
-	srlx		t6, 32, t6;							\
-	stw		t6, [%dst + off - 0x10];					\
-	bcc,pt		%xcc, 27f;							\
-	 ldxa		[%src + off + 0x30] %asi, t6;					\
-	add		%sum, 1, %sum;							\
-27:	stw		t7, [%dst + off - 0x04];					\
-	addcc		%sum, t7, %sum;							\
-	srlx		t7, 32, t7;							\
-	stw		t7, [%dst + off - 0x08];					\
-	bcc,pt		%xcc, 28f;							\
-	 ldxa		[%src + off + 0x38] %asi, t7;					\
-	add		%sum, 1, %sum;							\
-28:
-
-#define CSUMCOPY_EC_STALIGNED(off, t0, t1, t2, t3, t4, t5, t6, t7)			\
-	addcc		%sum, t0, %sum;							\
-	bcc,pt		%xcc, 31f;							\
-	 stx		t0, [%dst + off + 0x00];					\
-	add		%sum, 1, %sum;							\
-31:	addcc		%sum, t1, %sum;							\
-	bcc,pt		%xcc, 32f;							\
-	 stx		t1, [%dst + off + 0x08];					\
-	add		%sum, 1, %sum;							\
-32:	addcc		%sum, t2, %sum;							\
-	bcc,pt		%xcc, 33f;							\
-	 stx		t2, [%dst + off + 0x10];					\
-	add		%sum, 1, %sum;							\
-33:	addcc		%sum, t3, %sum;							\
-	bcc,pt		%xcc, 34f;							\
-	 stx		t3, [%dst + off + 0x18];					\
-	add		%sum, 1, %sum;							\
-34:	addcc		%sum, t4, %sum;							\
-	bcc,pt		%xcc, 35f;							\
-	 stx		t4, [%dst + off + 0x20];					\
-	add		%sum, 1, %sum;							\
-35:	addcc		%sum, t5, %sum;							\
-	bcc,pt		%xcc, 36f;							\
-	 stx		t5, [%dst + off + 0x28];					\
-	add		%sum, 1, %sum;							\
-36:	addcc		%sum, t6, %sum;							\
-	bcc,pt		%xcc, 37f;							\
-	 stx		t6, [%dst + off + 0x30];					\
-	add		%sum, 1, %sum;							\
-37:	addcc		%sum, t7, %sum;							\
-	bcc,pt		%xcc, 38f;							\
-	 stx		t7, [%dst + off + 0x38];					\
-	add		%sum, 1, %sum;							\
-38:
-
-#define CSUMCOPY_EC_STUNALIGN(off, t0, t1, t2, t3, t4, t5, t6, t7)			\
-	stw		t0, [%dst + off + 0x04];					\
-	addcc		%sum, t0, %sum;							\
-	srlx		t0, 32, t0;							\
-	bcc,pt		%xcc, 41f;							\
-	 stw		t0, [%dst + off + 0x00];					\
-	add		%sum, 1, %sum;							\
-41:	stw		t1, [%dst + off + 0x0c];					\
-	addcc		%sum, t1, %sum;							\
-	srlx		t1, 32, t1;							\
-	bcc,pt		%xcc, 42f;							\
-	 stw		t1, [%dst + off + 0x08];					\
-	add		%sum, 1, %sum;							\
-42:	stw		t2, [%dst + off + 0x14];					\
-	addcc		%sum, t2, %sum;							\
-	srlx		t2, 32, t2;							\
-	bcc,pt		%xcc, 43f;							\
-	 stw		t2, [%dst + off + 0x10];					\
-	add		%sum, 1, %sum;							\
-43:	stw		t3, [%dst + off + 0x1c];					\
-	addcc		%sum, t3, %sum;							\
-	srlx		t3, 32, t3;							\
-	bcc,pt		%xcc, 44f;							\
-	 stw		t3, [%dst + off + 0x18];					\
-	add		%sum, 1, %sum;							\
-44:	stw		t4, [%dst + off + 0x24];					\
-	addcc		%sum, t4, %sum;							\
-	srlx		t4, 32, t4;							\
-	bcc,pt		%xcc, 45f;							\
-	 stw		t4, [%dst + off + 0x20];					\
-	add		%sum, 1, %sum;							\
-45:	stw		t5, [%dst + off + 0x2c];					\
-	addcc		%sum, t5, %sum;							\
-	srlx		t5, 32, t5;							\
-	bcc,pt		%xcc, 46f;							\
-	 stw		t5, [%dst + off + 0x28];					\
-	add		%sum, 1, %sum;							\
-46:	stw		t6, [%dst + off + 0x34];					\
-	addcc		%sum, t6, %sum;							\
-	srlx		t6, 32, t6;							\
-	bcc,pt		%xcc, 47f;							\
-	 stw		t6, [%dst + off + 0x30];					\
-	add		%sum, 1, %sum;							\
-47:	stw		t7, [%dst + off + 0x3c];					\
-	addcc		%sum, t7, %sum;							\
-	srlx		t7, 32, t7;							\
-	bcc,pt		%xcc, 48f;							\
-	 stw		t7, [%dst + off + 0x38];					\
-	add		%sum, 1, %sum;							\
-48:
 
 #define CSUMCOPY_LASTCHUNK(off, t0, t1)							\
 	ldxa		[%src - off - 0x08] %asi, t0;					\
@@ -296,6 +98,7 @@
 	 add		%sum, 1, %sum		! IEU1
 
 cc_fixit:
+	cmp		%len, 6			! IEU1	Group
 	bl,a,pn		%icc, ccte		! CTI
 	 andcc		%len, 0xf, %g7		! IEU1	Group
 	andcc		%src, 2, %g0		! IEU1	Group
@@ -316,17 +119,17 @@
 	sll		%g3, 16, %g3		! IEU0	Group
 	srl		%sum, 16, %sum		! IEU0	Group
 	or		%g3, %sum, %sum		! IEU0	Group (regdep)
-1:	be,pt		%icc, cc_dword_aligned	! CTI
-	 andn		%len, 0xff, %g2		! IEU1
+1:	be,pt		%icc, ccmerge		! CTI
+	 andcc		%len, 0xf0, %g1		! IEU1
 	lduwa		[%src + 0x00] %asi, %g4	! Load	Group
 	sub		%len, 4, %len		! IEU0
 	add		%src, 4, %src		! IEU1
 	add		%dst, 4, %dst		! IEU0	Group
 	addcc		%g4, %sum, %sum		! IEU1	Group + 1 bubble
 	stw		%g4, [%dst - 0x4]	! Store
-	bcc,pt		%xcc, cc_dword_aligned	! CTI
-	 andn		%len, 0xff, %g2		! IEU0	Group
-	b,pt		%xcc, cc_dword_aligned	! CTI	4 clocks (mispredict)
+	bcc,pt		%xcc, ccmerge		! CTI
+	 andcc		%len, 0xf0, %g1		! IEU1	Group
+	b,pt		%xcc, ccmerge		! CTI	4 clocks (mispredict)
 	 add		%sum, 1, %sum		! IEU0
 
 	.align		32
@@ -342,26 +145,8 @@
 	 cmp		%len, 256		! IEU1	Group
 	bgeu,pt		%icc, csum_partial_copy_vis ! CTI
 	 andcc		%src, 7, %g0		! IEU1	Group
-	be,pt		%icc, cc_dword_aligned	! CTI
-	 andn		%len, 0xff, %g2		! IEU0
-	b,pt		%xcc, cc_fixit		! CTI	Group
-	 cmp		%len, 6			! IEU1
-cc_dword_aligned:
-	brz,pn		%g2, 3f			! CTI	Group
-	 andcc		%dst, 4, %g0		! IEU1	Group (brz uses IEU1)
-	be,pn		%icc, ccdbl + 4		! CTI
-5:	CSUMCOPY_ECACHE_LOAD(       0x00,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7)
-	CSUMCOPY_EC_STUNALIGN_LDNXT(0x40,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7)
-	CSUMCOPY_EC_STUNALIGN_LDNXT(0x80,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7)
-	CSUMCOPY_EC_STUNALIGN_LDNXT(0xc0,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7)
-	CSUMCOPY_EC_STUNALIGN(      0xc0,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7)
-10:
-	sub		%len, 256, %len		! IEU0	Group
-	add		%src, 256, %src		! IEU1
-	andncc		%len, 0xff, %g0		! IEU1	Group
-	bne,pt		%icc, 5b		! CTI
-	 add		%dst, 256, %dst		! IEU0
-3:	andcc		%len, 0xf0, %g1		! IEU1	Group
+	bne,pn		%icc, cc_fixit		! CTI
+	 andcc		%len, 0xf0, %g1		! IEU1	Group
 ccmerge:be,pn		%icc, ccte		! CTI
 	 andcc		%len, 0xf, %g7		! IEU1	Group
 	sll		%g1, 2, %o4		! IEU0
@@ -396,19 +181,6 @@
 	 add		%o0, 1, %o0		! IEU1	4 clocks (mispredict)
 1:	retl					! CTI	Group brk forced
 	 sllx		%g4, 32,%g4		! IEU0	Group
-ccdbl:	CSUMCOPY_ECACHE_LOAD(       0x00,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7)
-	CSUMCOPY_EC_STALIGNED_LDNXT(0x40,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7)
-	CSUMCOPY_EC_STALIGNED_LDNXT(0x80,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7)
-	CSUMCOPY_EC_STALIGNED_LDNXT(0xc0,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7)
-	CSUMCOPY_EC_STALIGNED(      0xc0,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7)
-11:
-	sub		%len, 256, %len		! IEU0	Group
-	add		%src, 256, %src		! IEU1
-	andncc		%len, 0xff, %g0		! IEU1	Group	
-	bne,pt		%icc, ccdbl		! CTI
-	 add		%dst, 256, %dst		! IEU0
-	b,pt		%xcc, ccmerge		! CTI	Group
-	 andcc		%len, 0xf0, %g1		! IEU1
 
 ccslow:	mov	0, %g5
 	brlez,pn %len, 4f
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/lib/strncpy_from_user.S linux/arch/sparc64/lib/strncpy_from_user.S
--- v2.1.78/linux/arch/sparc64/lib/strncpy_from_user.S	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/lib/strncpy_from_user.S	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,5 @@
-/* strncpy_from_user.S: Sparc64 strncpy from userspace.
+/* $Id: strncpy_from_user.S,v 1.5 1997/09/08 11:29:23 jj Exp $
+ * strncpy_from_user.S: Sparc64 strncpy from userspace.
  *
  *  Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
@@ -6,6 +7,10 @@
 #include <asm/asi.h>
 #include <asm/errno.h>
 
+	.data
+	.align	8
+0:	.xword	0x0101010101010101
+
 	.text
 	.align	4
 
@@ -14,41 +19,117 @@
 	 * -EFAULT		for an exception
 	 * count		if we hit the buffer limit
 	 * bytes copied		if we hit a null byte
+	 * (without the null byte)
+	 *
+	 * This implementation assumes:
+	 * %o1 is 8 aligned => !(%o2 & 7)
+	 * %o0 is 8 aligned (if not, it will be slooooow, but will work)
+	 *
+	 * This is optimized for the common case:
+	 * in my stats, 90% of src are 8 aligned (even on sparc32)
+	 * and average length is 18 or so.
 	 */
 
 	.globl	__strncpy_from_user
 __strncpy_from_user:
 	/* %o0=dest, %o1=src, %o2=count */
-	brlez,pn %o2, 3f
+	sethi	%hi(0b), %o5		! IEU0	Group
+	andcc	%o1, 7, %g0		! IEU1
+	bne,pn	%icc, 30f		! CTI
+	 ldx	[%o5 + %lo(0b)], %o4	! Load	Group
+60:	ldxa	[%o1] ASI_S, %g1	! Load	Group
+	add	%o1, %o2, %o1		! IEU0
+	subcc	%g0, %o2, %o3		! IEU1
+	bgeu,pn	%xcc, 10f		! CTI
+	 sllx	%o4, 7, %o5		! IEU0	Group
+	add	%o0, %o2, %o0		! IEU1
+1:	sub	%g1, %o4, %g2		! IEU0	Group
+	stx	%g1, [%o0 + %o3]	! Store
+	andcc	%g2, %o5, %g0		! IEU1	Group
+	bne,pn	%xcc, 5f		! CTI
+	 add	%o3, 8, %o3		! IEU0
+	brlz,a,pt %o3, 1b		! CTI(IEU1) Group
+61:	 ldxa	[%o1 + %o3] ASI_S, %g1	! Load
+10:	retl				! CTI	Group
+	 mov	%o2, %o0		! IEU0
+5:	srlx	%g2, 32, %g7		! IEU0	Group
+	sethi	%hi(0xff00), %g5	! IEU1
+	andcc	%g7, %o5, %g0		! IEU1	Group
+	be,pn	%icc, 2f		! CTI
+	 or	%g5, %lo(0xff00), %g5	! IEU0
+	srlx	%g1, 48, %g7		! IEU0	Group
+	andcc	%g7, %g5, %g0		! IEU1	Group
+	be,pn	%icc, 50f		! CTI
+	 andcc	%g7, 0xff, %g0		! IEU1	Group
+	be,pn	%icc, 51f		! CTI
+	 srlx	%g1, 32, %g7		! IEU0
+	andcc	%g7, %g5, %g0		! IEU1	Group
+	be,pn	%icc, 52f		! CTI
+	 andcc	%g7, 0xff, %g0		! IEU1	Group
+	be,pn	%icc, 53f		! CTI
+2:	 andcc	%g2, %o5, %g0		! IEU1	Group
+	be,pn	%icc, 2f		! CTI
+	 srl	%g1, 16, %g7		! IEU0
+	andcc	%g7, %g5, %g0		! IEU1	Group
+	be,pn	%icc, 54f		! CTI
+	 andcc	%g7, 0xff, %g0		! IEU1	Group
+	be,pn	%icc, 55f		! CTI
+	 andcc	%g1, %g5, %g0		! IEU1	Group
+	be,pn	%icc, 56f		! CTI
+	 andcc	%g1, 0xff, %g0		! IEU1	Group
+	be,a,pn	%icc, 57f		! CTI
+	 add	%o2, %o3, %o0		! IEU0
+2:	brlz,a,pt %o3, 1b		! CTI(IEU1) Group
+62:	 ldxa	[%o1 + %o3] ASI_S, %g1	! Load
+	retl				! CTI	Group
+	 mov	%o2, %o0		! IEU0
+50:	add	%o2, %o3, %o0
+	retl
+	 sub	%o0, 8, %o0
+51:	add	%o2, %o3, %o0
+	retl
+	 sub	%o0, 7, %o0
+52:	add	%o2, %o3, %o0
+	retl
+	 sub	%o0, 6, %o0
+53:	add	%o2, %o3, %o0
+	retl
+	 sub	%o0, 5, %o0
+54:	add	%o2, %o3, %o0
+	retl
+	 sub	%o0, 4, %o0
+55:	add	%o2, %o3, %o0
+	retl
+	 sub	%o0, 3, %o0
+56:	add	%o2, %o3, %o0
+	retl
+	 sub	%o0, 2, %o0
+57:	retl
+	 sub	%o0, 1, %o0
+30:	brlez,pn %o2, 3f
 	 add	%o1, %o2, %o1
 	sub	%g0, %o2, %o3
 	add	%o0, %o2, %o0
-10:
-	lduba	[%o1 + %o3] ASI_S, %o4
-1:
-	brz,pn	%o4, 2f
+63:	lduba	[%o1 + %o3] ASI_S, %o4
+1:	brz,pn	%o4, 2f
 	 stb	%o4, [%o0 + %o3]
 	addcc	%o3, 1, %o3
 	bne,pt	%xcc, 1b
-11:
-	 lduba	[%o1 + %o3] ASI_S, %o4
-	retl
+64:	 lduba	[%o1 + %o3] ASI_S, %o4
+3:	retl
 	 mov	%o2, %o0
-2:
-	add	%o3, 1, %o3
-	retl
+2:	retl
 	 add	%o2, %o3, %o0
-3:
-	retl
-	 clr	%o0
 
 	.section .fixup,#alloc,#execinstr
 	.align	4
-4:
-	retl
+4:	retl
 	 mov	-EFAULT, %o0
 
 	.section __ex_table,#alloc
 	.align	4
-	.word	10b, 4b
-	.word	11b, 4b
+	.word	60b, 4b
+	.word	61b, 4b
+	.word	62b, 4b
+	.word	63b, 4b
+	.word	64b, 4b
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/Makefile linux/arch/sparc64/math-emu/Makefile
--- v2.1.78/linux/arch/sparc64/math-emu/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/Makefile	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,33 @@
+#
+# Makefile for the FPU Quad (long double) instruction emulation.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := math-emu.o
+O_OBJS   := math.o fabsq.o faddq.o fdivq.o fdmulq.o fitoq.o 		\
+		fmovq.o fmulq.o fnegq.o fqtoi.o fqtox.o fsubq.o		\
+		fxtoq.o fdtoq.o fstoq.o fqtos.o fqtod.o fsqrtq.o	\
+		fcmpq.o fcmpeq.o udivmodti4.o				\
+		fsqrts.o fsqrtd.o fadds.o faddd.o fsubs.o fsubd.o	\
+		fmuls.o fmuld.o fdivs.o fdivd.o fsmuld.o		\
+		fstoi.o fdtoi.o fstox.o fdtox.o fstod.o fdtos.o
+
+ifeq ($(CONFIG_MATHEMU),m)
+M_OBJS   := $(O_TARGET)
+endif
+
+.S.s:
+	$(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s
+
+.S.o:
+	$(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
+
+ifneq ($(CONFIG_MATHEMU),y)
+do_it_all:
+endif
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/double.h linux/arch/sparc64/math-emu/double.h
--- v2.1.78/linux/arch/sparc64/math-emu/double.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/double.h	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,129 @@
+/*
+ * Definitions for IEEE Double Precision
+ */
+
+#if _FP_W_TYPE_SIZE < 32
+#error "Here's a nickle kid.  Go buy yourself a real computer."
+#endif
+
+#if _FP_W_TYPE_SIZE < 64
+#define _FP_FRACTBITS_D		(2 * _FP_W_TYPE_SIZE)
+#else
+#define _FP_FRACTBITS_D		_FP_W_TYPE_SIZE
+#endif
+
+#define _FP_FRACBITS_D		53
+#define _FP_FRACXBITS_D		(_FP_FRACTBITS_D - _FP_FRACBITS_D)
+#define _FP_WFRACBITS_D		(_FP_WORKBITS + _FP_FRACBITS_D)
+#define _FP_WFRACXBITS_D	(_FP_FRACTBITS_D - _FP_WFRACBITS_D)
+#define _FP_EXPBITS_D		11
+#define _FP_EXPBIAS_D		1023
+#define _FP_EXPMAX_D		2047
+
+#define _FP_QNANBIT_D		\
+	((_FP_W_TYPE)1 << (_FP_FRACBITS_D-2) % _FP_W_TYPE_SIZE)
+#define _FP_IMPLBIT_D		\
+	((_FP_W_TYPE)1 << (_FP_FRACBITS_D-1) % _FP_W_TYPE_SIZE)
+#define _FP_OVERFLOW_D		\
+	((_FP_W_TYPE)1 << _FP_WFRACBITS_D % _FP_W_TYPE_SIZE)
+
+#if _FP_W_TYPE_SIZE < 64
+
+union _FP_UNION_D
+{
+  double flt;
+  struct {
+#if __BYTE_ORDER == __BIG_ENDIAN
+    unsigned sign  : 1;
+    unsigned exp   : _FP_EXPBITS_D;
+    unsigned frac1 : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0) - _FP_W_TYPE_SIZE;
+    unsigned frac0 : _FP_W_TYPE_SIZE;
+#else
+    unsigned frac0 : _FP_W_TYPE_SIZE;
+    unsigned frac1 : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0) - _FP_W_TYPE_SIZE;
+    unsigned exp   : _FP_EXPBITS_D;
+    unsigned sign  : 1;
+#endif
+  } bits __attribute__((packed));
+};
+
+#define FP_DECL_D(X)		_FP_DECL(2,X)
+#define FP_UNPACK_RAW_D(X,val)	_FP_UNPACK_RAW_2(D,X,val)
+#define FP_PACK_RAW_D(val,X)	_FP_PACK_RAW_2(D,val,X)
+
+#define FP_UNPACK_D(X,val)		\
+  do {					\
+    _FP_UNPACK_RAW_2(D,X,val);		\
+    _FP_UNPACK_CANONICAL(D,2,X);	\
+  } while (0)
+
+#define FP_PACK_D(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(D,2,X);		\
+    _FP_PACK_RAW_2(D,val,X);		\
+  } while (0)
+
+#define FP_NEG_D(R,X)		_FP_NEG(D,2,R,X)
+#define FP_ADD_D(R,X,Y)		_FP_ADD(D,2,R,X,Y)
+#define FP_SUB_D(R,X,Y)		_FP_SUB(D,2,R,X,Y)
+#define FP_MUL_D(R,X,Y)		_FP_MUL(D,2,R,X,Y)
+#define FP_DIV_D(R,X,Y)		_FP_DIV(D,2,R,X,Y)
+#define FP_SQRT_D(R,X)		_FP_SQRT(D,2,R,X)
+
+#define FP_CMP_D(r,X,Y,un)	_FP_CMP(D,2,r,X,Y,un)
+#define FP_CMP_EQ_D(r,X,Y)	_FP_CMP_EQ(D,2,r,X,Y)
+
+#define FP_TO_INT_D(r,X,rsz,rsg)  _FP_TO_INT(D,2,r,X,rsz,rsg)
+#define FP_FROM_INT_D(X,r,rs,rt)  _FP_FROM_INT(D,2,X,r,rs,rt)
+
+#else
+
+union _FP_UNION_D
+{
+  double flt;
+  struct {
+#if __BYTE_ORDER == __BIG_ENDIAN
+    unsigned sign : 1;
+    unsigned exp  : _FP_EXPBITS_D;
+    unsigned long frac : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0);
+#else
+    unsigned long frac : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0);
+    unsigned exp  : _FP_EXPBITS_D;
+    unsigned sign : 1;
+#endif
+  } bits __attribute__((packed));
+};
+
+#define FP_DECL_D(X)		_FP_DECL(1,X)
+#define FP_UNPACK_RAW_D(X,val)	_FP_UNPACK_RAW_1(D,X,val)
+#define FP_PACK_RAW_D(val,X)	_FP_PACK_RAW_1(D,val,X)
+
+#define FP_UNPACK_D(X,val)		\
+  do {					\
+    _FP_UNPACK_RAW_1(D,X,val);		\
+    _FP_UNPACK_CANONICAL(D,1,X);	\
+  } while (0)
+
+#define FP_PACK_D(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(D,1,X);		\
+    _FP_PACK_RAW_1(D,val,X);		\
+  } while (0)
+
+#define FP_NEG_D(R,X)		_FP_NEG(D,1,R,X)
+#define FP_ADD_D(R,X,Y)		_FP_ADD(D,1,R,X,Y)
+#define FP_SUB_D(R,X,Y)		_FP_SUB(D,1,R,X,Y)
+#define FP_MUL_D(R,X,Y)		_FP_MUL(D,1,R,X,Y)
+#define FP_DIV_D(R,X,Y)		_FP_DIV(D,1,R,X,Y)
+#define FP_SQRT_D(R,X)		_FP_SQRT(D,1,R,X)
+
+/* The implementation of _FP_MUL_D and _FP_DIV_D should be chosen by
+   the target machine.  */
+
+#define FP_CMP_D(r,X,Y,un)	_FP_CMP(D,1,r,X,Y,un)
+#define FP_CMP_EQ_D(r,X,Y)	_FP_CMP_EQ(D,1,r,X,Y)
+
+#define FP_TO_INT_D(r,X,rsz,rsg)  _FP_TO_INT(D,1,r,X,rsz,rsg)
+#define FP_FROM_INT_D(X,r,rs,rt)  _FP_FROM_INT(D,1,X,r,rs,rt)
+
+#endif /* W_TYPE_SIZE < 64 */
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fabsq.c linux/arch/sparc64/math-emu/fabsq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fabsq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fabsq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,19 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FABSQ(unsigned long *rd, unsigned long *rs2)
+{
+/*
+	FP_DECL_Q(A); FP_DECL_Q(R);
+
+	__FP_UNPACK_Q(A, rs2);
+	_FP_FRAC_COPY_2(R, A);
+	R_c = A_c;
+	R_e = A_e;
+	R_s = 0;
+	__FP_PACK_Q(rd, R);
+ */
+	rd[0] = rs2[0] & 0x7fffffffffffffffUL;
+	rd[1] = rs2[1];
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/faddd.c linux/arch/sparc64/math-emu/faddd.c
--- v2.1.78/linux/arch/sparc64/math-emu/faddd.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/faddd.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "double.h"
+
+int FADDD(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
+
+	__FP_UNPACK_D(A, rs1);
+	__FP_UNPACK_D(B, rs2);
+	FP_ADD_D(R, A, B);
+	__FP_PACK_D(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/faddq.c linux/arch/sparc64/math-emu/faddq.c
--- v2.1.78/linux/arch/sparc64/math-emu/faddq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/faddq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FADDQ(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
+
+	__FP_UNPACK_Q(A, rs1);
+	__FP_UNPACK_Q(B, rs2);
+	FP_ADD_Q(R, A, B);
+	__FP_PACK_Q(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fadds.c linux/arch/sparc64/math-emu/fadds.c
--- v2.1.78/linux/arch/sparc64/math-emu/fadds.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fadds.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "single.h"
+
+int FADDS(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R);
+
+	__FP_UNPACK_S(A, rs1);
+	__FP_UNPACK_S(B, rs2);
+	FP_ADD_S(R, A, B);
+	__FP_PACK_S(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fcmpeq.c linux/arch/sparc64/math-emu/fcmpeq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fcmpeq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fcmpeq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,28 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FCMPEQ(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_Q(A); FP_DECL_Q(B);
+	long ret;
+	int fccno = ((long)rd) & 3;
+	unsigned long fsr;
+	
+	rd = (void *)(((long)rd)&~3);
+	__FP_UNPACK_Q(A, rs1);
+	__FP_UNPACK_Q(B, rs2);
+	FP_CMP_Q(ret, A, B, 3);
+	switch (ret) {
+	case 1: ret = 2; break;
+	case -1: ret = 1; break;
+	}
+	fsr = *(unsigned long *)rd;
+	switch (fccno) {
+	case 0: fsr &= ~0xc00; fsr |= (ret << 10); break;
+	case 1: fsr &= ~0x300000000UL; fsr |= (ret << 32); break;
+	case 2: fsr &= ~0xc00000000UL; fsr |= (ret << 34); break;
+	case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break;
+	}
+	*(unsigned long *)rd = fsr;
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fcmpq.c linux/arch/sparc64/math-emu/fcmpq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fcmpq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fcmpq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,28 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FCMPQ(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_Q(A); FP_DECL_Q(B);
+	long ret;
+	int fccno = ((long)rd) & 3;
+	unsigned long fsr;
+	
+	rd = (void *)(((long)rd)&~3);
+	__FP_UNPACK_Q(A, rs1);
+	__FP_UNPACK_Q(B, rs2);
+	FP_CMP_Q(ret, A, B, 3);
+	switch (ret) {
+	case 1: ret = 2; break;
+	case -1: ret = 1; break;
+	}
+	fsr = *(unsigned long *)rd;
+	switch (fccno) {
+	case 0: fsr &= ~0xc00; fsr |= (ret << 10); break;
+	case 1: fsr &= ~0x300000000UL; fsr |= (ret << 32); break;
+	case 2: fsr &= ~0xc00000000UL; fsr |= (ret << 34); break;
+	case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break;
+	}
+	*(unsigned long *)rd = fsr;
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fdivd.c linux/arch/sparc64/math-emu/fdivd.c
--- v2.1.78/linux/arch/sparc64/math-emu/fdivd.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fdivd.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "double.h"
+
+int FDIVD(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
+
+	__FP_UNPACK_D(A, rs1);
+	__FP_UNPACK_D(B, rs2);
+	FP_DIV_D(R, A, B);
+	__FP_PACK_D(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fdivq.c linux/arch/sparc64/math-emu/fdivq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fdivq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fdivq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FDIVQ(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
+
+	__FP_UNPACK_Q(A, rs1);
+	__FP_UNPACK_Q(B, rs2);
+	FP_DIV_Q(R, A, B);
+	__FP_PACK_Q(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fdivs.c linux/arch/sparc64/math-emu/fdivs.c
--- v2.1.78/linux/arch/sparc64/math-emu/fdivs.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fdivs.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "single.h"
+
+int FDIVS(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R);
+
+	__FP_UNPACK_S(A, rs1);
+	__FP_UNPACK_S(B, rs2);
+	FP_DIV_S(R, A, B);
+	__FP_PACK_S(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fdmulq.c linux/arch/sparc64/math-emu/fdmulq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fdmulq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fdmulq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,16 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "double.h"
+
+int FDMULQ(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_D(IN); FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
+
+	__FP_UNPACK_D(IN, rs1);
+	FP_CONV(Q,D,2,1,A,IN);
+	__FP_UNPACK_D(IN, rs2);
+	FP_CONV(Q,D,2,1,B,IN);
+	FP_MUL_Q(R, A, B);
+	__FP_PACK_Q(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fdtoi.c linux/arch/sparc64/math-emu/fdtoi.c
--- v2.1.78/linux/arch/sparc64/math-emu/fdtoi.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fdtoi.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "double.h"
+
+int FDTOI(unsigned *rd, void *rs2)
+{
+	FP_DECL_D(A);
+	unsigned r;
+
+	__FP_UNPACK_D(A, rs2);
+	FP_TO_INT_D(r, A, 32, 1);
+	*rd = r;
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fdtoq.c linux/arch/sparc64/math-emu/fdtoq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fdtoq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fdtoq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "double.h"
+
+int FDTOQ(void *rd, void *rs2)
+{
+	FP_DECL_D(A); FP_DECL_Q(R);
+
+	__FP_UNPACK_D(A, rs2);
+	FP_CONV(Q,D,2,1,R,A);
+	__FP_PACK_Q(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fdtos.c linux/arch/sparc64/math-emu/fdtos.c
--- v2.1.78/linux/arch/sparc64/math-emu/fdtos.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fdtos.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "double.h"
+#include "single.h"
+
+int FDTOS(void *rd, void *rs2)
+{
+	FP_DECL_D(A); FP_DECL_S(R);
+
+	__FP_UNPACK_D(A, rs2);
+	FP_CONV(S,D,1,1,R,A);
+	__FP_PACK_S(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fdtox.c linux/arch/sparc64/math-emu/fdtox.c
--- v2.1.78/linux/arch/sparc64/math-emu/fdtox.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fdtox.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "double.h"
+
+int FDTOX(unsigned long *rd, void *rs2)
+{
+	FP_DECL_D(A);
+	unsigned long r;
+
+	__FP_UNPACK_D(A, rs2);
+	FP_TO_INT_D(r, A, 64, 1);
+	*rd = r;
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fitoq.c linux/arch/sparc64/math-emu/fitoq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fitoq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fitoq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,12 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FITOQ(void *rd, void *rs2)
+{
+	FP_DECL_Q(R);
+	int a = *(int *)rs2;
+
+	FP_FROM_INT_Q(R, a, 32, int);
+	__FP_PACK_Q(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fmovq.c linux/arch/sparc64/math-emu/fmovq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fmovq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fmovq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,6 @@
+int FMOVQ(unsigned long *rd, unsigned long *rs2)
+{
+	rd[0] = rs2[0];
+	rd[1] = rs2[1];
+	return 0;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fmuld.c linux/arch/sparc64/math-emu/fmuld.c
--- v2.1.78/linux/arch/sparc64/math-emu/fmuld.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fmuld.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "double.h"
+
+int FMULD(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
+
+	__FP_UNPACK_D(A, rs1);
+	__FP_UNPACK_D(B, rs2);
+	FP_MUL_D(R, A, B);
+	__FP_PACK_D(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fmulq.c linux/arch/sparc64/math-emu/fmulq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fmulq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fmulq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FMULQ(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
+
+	__FP_UNPACK_Q(A, rs1);
+	__FP_UNPACK_Q(B, rs2);
+	FP_MUL_Q(R, A, B);
+	__FP_PACK_Q(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fmuls.c linux/arch/sparc64/math-emu/fmuls.c
--- v2.1.78/linux/arch/sparc64/math-emu/fmuls.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fmuls.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "single.h"
+
+int FMULS(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R);
+
+	__FP_UNPACK_S(A, rs1);
+	__FP_UNPACK_S(B, rs2);
+	FP_MUL_S(R, A, B);
+	__FP_PACK_S(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fnegq.c linux/arch/sparc64/math-emu/fnegq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fnegq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fnegq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,18 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FNEGQ(unsigned long *rd, unsigned long *rs2)
+{
+/*
+	FP_DECL_Q(A); FP_DECL_Q(R);
+
+	__FP_UNPACK_Q(A, rs2);
+	FP_NEG_Q(R, A);
+	__FP_PACK_Q(rd, R);
+ */
+	rd[0] = rs2[0] ^ 0x8000000000000000UL;
+	rd[1] = rs2[1];
+	return 1;
+}
+
+                
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fqtod.c linux/arch/sparc64/math-emu/fqtod.c
--- v2.1.78/linux/arch/sparc64/math-emu/fqtod.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fqtod.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "double.h"
+
+int FQTOD(void *rd, void *rs2)
+{
+	FP_DECL_Q(A); FP_DECL_D(R);
+
+	__FP_UNPACK_Q(A, rs2);
+	FP_CONV(D,Q,1,2,R,A);
+	__FP_PACK_D(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fqtoi.c linux/arch/sparc64/math-emu/fqtoi.c
--- v2.1.78/linux/arch/sparc64/math-emu/fqtoi.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fqtoi.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FQTOI(unsigned *rd, void *rs2)
+{
+	FP_DECL_Q(A);
+	unsigned r;
+
+	__FP_UNPACK_Q(A, rs2);
+	FP_TO_INT_Q(r, A, 32, 1);
+	*rd = r;
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fqtos.c linux/arch/sparc64/math-emu/fqtos.c
--- v2.1.78/linux/arch/sparc64/math-emu/fqtos.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fqtos.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "single.h"
+
+int FQTOS(void *rd, void *rs2)
+{
+	FP_DECL_Q(A); FP_DECL_S(R);
+
+	__FP_UNPACK_Q(A, rs2);
+	FP_CONV(S,Q,1,2,R,A);
+	__FP_PACK_S(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fqtox.c linux/arch/sparc64/math-emu/fqtox.c
--- v2.1.78/linux/arch/sparc64/math-emu/fqtox.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fqtox.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FQTOX(unsigned long *rd, void *rs2)
+{
+	FP_DECL_Q(A);
+	unsigned long r;
+
+	__FP_UNPACK_Q(A, rs2);
+	FP_TO_INT_Q(r, A, 64, 1);
+	*rd = r;
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fsmuld.c linux/arch/sparc64/math-emu/fsmuld.c
--- v2.1.78/linux/arch/sparc64/math-emu/fsmuld.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fsmuld.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,16 @@
+#include "soft-fp.h"
+#include "double.h"
+#include "single.h"
+
+int FSMULD(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_S(IN); FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
+
+	__FP_UNPACK_S(IN, rs1);
+	FP_CONV(D,S,1,1,A,IN);
+	__FP_UNPACK_S(IN, rs2);
+	FP_CONV(D,S,1,1,B,IN);
+	FP_MUL_D(R, A, B);
+	__FP_PACK_D(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fsqrtd.c linux/arch/sparc64/math-emu/fsqrtd.c
--- v2.1.78/linux/arch/sparc64/math-emu/fsqrtd.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fsqrtd.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,12 @@
+#include "soft-fp.h"
+#include "double.h"
+
+int FSQRTD(void *rd, void *rs2)
+{
+	FP_DECL_D(A); FP_DECL_D(R);
+        
+	__FP_UNPACK_D(A, rs2);
+	FP_SQRT_D(R, A);
+	__FP_PACK_D(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fsqrtq.c linux/arch/sparc64/math-emu/fsqrtq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fsqrtq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fsqrtq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,12 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FSQRTQ(void *rd, void *rs2)
+{
+	FP_DECL_Q(A); FP_DECL_Q(R);
+        
+	__FP_UNPACK_Q(A, rs2);
+	FP_SQRT_Q(R, A);
+	__FP_PACK_Q(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fsqrts.c linux/arch/sparc64/math-emu/fsqrts.c
--- v2.1.78/linux/arch/sparc64/math-emu/fsqrts.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fsqrts.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,12 @@
+#include "soft-fp.h"
+#include "single.h"
+
+int FSQRTS(void *rd, void *rs2)
+{
+	FP_DECL_S(A); FP_DECL_S(R);
+        
+	__FP_UNPACK_S(A, rs2);
+	FP_SQRT_S(R, A);
+	__FP_PACK_S(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fstod.c linux/arch/sparc64/math-emu/fstod.c
--- v2.1.78/linux/arch/sparc64/math-emu/fstod.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fstod.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "double.h"
+#include "single.h"
+
+int FSTOD(void *rd, void *rs2)
+{
+	FP_DECL_S(A); FP_DECL_D(R);
+
+	__FP_UNPACK_S(A, rs2);
+	FP_CONV(D,S,1,1,R,A);
+	__FP_PACK_D(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fstoi.c linux/arch/sparc64/math-emu/fstoi.c
--- v2.1.78/linux/arch/sparc64/math-emu/fstoi.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fstoi.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "single.h"
+
+int FSTOI(unsigned *rd, void *rs2)
+{
+	FP_DECL_S(A);
+	unsigned r;
+
+	__FP_UNPACK_S(A, rs2);
+	FP_TO_INT_S(r, A, 32, 1);
+	*rd = r;
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fstoq.c linux/arch/sparc64/math-emu/fstoq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fstoq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fstoq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "single.h"
+
+int FSTOQ(void *rd, void *rs2)
+{
+	FP_DECL_S(A); FP_DECL_Q(R);
+
+	__FP_UNPACK_S(A, rs2);
+	FP_CONV(Q,S,2,1,R,A);
+	__FP_PACK_Q(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fstox.c linux/arch/sparc64/math-emu/fstox.c
--- v2.1.78/linux/arch/sparc64/math-emu/fstox.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fstox.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "single.h"
+
+int FSTOX(unsigned long *rd, void *rs2)
+{
+	FP_DECL_S(A);
+	unsigned long r;
+
+	__FP_UNPACK_S(A, rs2);
+	FP_TO_INT_S(r, A, 64, 1);
+	*rd = r;
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fsubd.c linux/arch/sparc64/math-emu/fsubd.c
--- v2.1.78/linux/arch/sparc64/math-emu/fsubd.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fsubd.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,15 @@
+#include "soft-fp.h"
+#include "double.h"
+
+int FSUBD(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
+
+	__FP_UNPACK_D(A, rs1);
+	__FP_UNPACK_D(B, rs2);
+	if (B_c != FP_CLS_NAN)
+		B_s ^= 1;
+	FP_ADD_D(R, A, B);
+	__FP_PACK_D(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fsubq.c linux/arch/sparc64/math-emu/fsubq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fsubq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fsubq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,15 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FSUBQ(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
+
+	__FP_UNPACK_Q(A, rs1);
+	__FP_UNPACK_Q(B, rs2);
+	if (B_c != FP_CLS_NAN)
+		B_s ^= 1;
+	FP_ADD_Q(R, A, B);
+	__FP_PACK_Q(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fsubs.c linux/arch/sparc64/math-emu/fsubs.c
--- v2.1.78/linux/arch/sparc64/math-emu/fsubs.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fsubs.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,15 @@
+#include "soft-fp.h"
+#include "single.h"
+
+int FSUBS(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R);
+
+	__FP_UNPACK_S(A, rs1);
+	__FP_UNPACK_S(B, rs2);
+	if (B_c != FP_CLS_NAN)
+		B_s ^= 1;
+	FP_ADD_S(R, A, B);
+	__FP_PACK_S(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/fxtoq.c linux/arch/sparc64/math-emu/fxtoq.c
--- v2.1.78/linux/arch/sparc64/math-emu/fxtoq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/fxtoq.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,12 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FXTOQ(void *rd, void *rs2)
+{
+	FP_DECL_Q(R);
+	long a = *(long *)rs2;
+
+	FP_FROM_INT_Q(R, a, 64, long);
+	__FP_PACK_Q(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/math.c linux/arch/sparc64/math-emu/math.c
--- v2.1.78/linux/arch/sparc64/math-emu/math.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/math.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,209 @@
+/* $Id: math.c,v 1.3 1997/10/15 07:28:55 jj Exp $
+ * arch/sparc64/math-emu/math.c
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ * Emulation routines originate from soft-fp package, which is part
+ * of glibc and has appropriate copyrights in it.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+
+#include <asm/fpumacro.h>
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+
+#define FLOATFUNC(x) extern int x(void *,void *,void *);
+
+FLOATFUNC(FMOVQ)
+FLOATFUNC(FNEGQ)
+FLOATFUNC(FABSQ)
+FLOATFUNC(FSQRTQ)
+FLOATFUNC(FADDQ)
+FLOATFUNC(FSUBQ)
+FLOATFUNC(FMULQ)
+FLOATFUNC(FDIVQ)
+FLOATFUNC(FDMULQ)
+FLOATFUNC(FQTOX)
+FLOATFUNC(FXTOQ)
+FLOATFUNC(FQTOS)
+FLOATFUNC(FQTOD)
+FLOATFUNC(FITOQ)
+FLOATFUNC(FSTOQ)
+FLOATFUNC(FDTOQ)
+FLOATFUNC(FQTOI)
+FLOATFUNC(FCMPQ)
+FLOATFUNC(FCMPEQ)
+
+FLOATFUNC(FSQRTS)
+FLOATFUNC(FSQRTD)
+FLOATFUNC(FADDS)
+FLOATFUNC(FADDD)
+FLOATFUNC(FSUBS)
+FLOATFUNC(FSUBD)
+FLOATFUNC(FMULS)
+FLOATFUNC(FMULD)
+FLOATFUNC(FDIVS)
+FLOATFUNC(FDIVD)
+FLOATFUNC(FSMULD)
+FLOATFUNC(FSTOX)
+FLOATFUNC(FDTOX)
+FLOATFUNC(FDTOS)
+FLOATFUNC(FSTOD)
+FLOATFUNC(FSTOI)
+FLOATFUNC(FDTOI)
+
+int do_mathemu(struct pt_regs *regs, struct fpustate *f)
+{
+	unsigned long pc = regs->tpc;
+	unsigned long tstate = regs->tstate;
+	u32 insn = 0;
+	int type = 0; /* 01 is single, 10 is double, 11 is quad, 
+			 000011 is rs1, 001100 is rs2, 110000 is rd (00 in rd is fcc)
+			 111100000000 tells which ftt may that happen in */
+	int freg;
+	static u64 zero[2] = { 0L, 0L };
+	int flags;
+	int (*func)(void *,void *,void *) = NULL;
+
+	if(tstate & TSTATE_PRIV)
+		die_if_kernel("FPQuad from kernel", regs);
+	MOD_INC_USE_COUNT;
+	if(current->tss.flags & SPARC_FLAG_32BIT)
+		pc = (u32)pc;
+	if (get_user(insn, (u32 *)pc) != -EFAULT) {
+		if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
+			switch ((insn >> 5) & 0x1ff) {
+			/* QUAD - ftt == 3 */
+			case 0x003: type = 0x33c; func = FMOVQ; break;
+			case 0x007: type = 0x33c; func = FNEGQ; break;
+			case 0x00b: type = 0x33c; func = FABSQ; break;
+			case 0x02b: type = 0x33c; func = FSQRTQ; break;
+			case 0x043: type = 0x33f; func = FADDQ; break;
+			case 0x047: type = 0x33f; func = FSUBQ; break;
+			case 0x04b: type = 0x33f; func = FMULQ; break;
+			case 0x04f: type = 0x33f; func = FDIVQ; break;
+			case 0x06e: type = 0x33a; func = FDMULQ; break;
+			case 0x083: type = 0x32c; func = FQTOX; break;
+			case 0x08c: type = 0x338; func = FXTOQ; break;
+			case 0x0c7: type = 0x31c; func = FQTOS; break;
+			case 0x0cb: type = 0x32c; func = FQTOD; break;
+			case 0x0cc: type = 0x334; func = FITOQ; break;
+			case 0x0cd: type = 0x334; func = FSTOQ; break;
+			case 0x0ce: type = 0x338; func = FDTOQ; break;
+			case 0x0d3: type = 0x31c; func = FQTOI; break;
+			/* SUBNORMAL - ftt == 2 */
+			case 0x029: type = 0x214; func = FSQRTS; break;
+			case 0x02a: type = 0x228; func = FSQRTD; break;
+			case 0x041: type = 0x215; func = FADDS; break;
+			case 0x042: type = 0x22a; func = FADDD; break;
+			case 0x045: type = 0x215; func = FSUBS; break;
+			case 0x046: type = 0x22a; func = FSUBD; break;
+			case 0x049: type = 0x215; func = FMULS; break;
+			case 0x04a: type = 0x22a; func = FMULD; break;
+			case 0x04d: type = 0x215; func = FDIVS; break;
+			case 0x04e: type = 0x22a; func = FDIVD; break;
+			case 0x069: type = 0x225; func = FSMULD; break;
+			case 0x081: type = 0x224; func = FSTOX; break;
+			case 0x082: type = 0x228; func = FDTOX; break;
+			case 0x0c6: type = 0x218; func = FDTOS; break;
+			case 0x0c9: type = 0x224; func = FSTOD; break;
+			case 0x0d1: type = 0x214; func = FSTOI; break;
+			case 0x0d2: type = 0x218; func = FDTOI; break;
+			}
+		}
+		else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
+			switch ((insn >> 5) & 0x1ff) {
+			case 0x053: type = 0x30f; func = FCMPQ; break;
+			case 0x057: type = 0x30f; func = FCMPEQ; break;
+			}
+		}
+	}
+	if (type) {
+		void *rs1 = NULL, *rs2 = NULL, *rd = NULL;
+		
+		freg = (f->fsr >> 14) & 0xf;
+		if (freg != (type >> 8))
+			goto err;
+		f->fsr &= ~0x1c000;
+		freg = ((insn >> 14) & 0x1f);
+		switch (type & 0x3) {
+		case 3: if (freg & 2) {
+				f->fsr |= (6 << 14) /* invalid_fp_register */;
+				goto err;
+			}
+		case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
+		case 1: rs1 = (void *)&f->regs[freg]; 
+			flags = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU; 
+			if (!(current->tss.flags & flags))
+			rs1 = (void *)&zero;
+			break;
+		}
+		freg = (insn & 0x1f);
+		switch ((type >> 2) & 0x3) {
+		case 3: if (freg & 2) {
+				f->fsr |= (6 << 14) /* invalid_fp_register */;
+				goto err;
+			}
+		case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
+		case 1: rs2 = (void *)&f->regs[freg];
+			flags = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU; 
+			if (!(current->tss.flags & flags))
+				rs2 = (void *)&zero;
+			break;
+		}
+		freg = ((insn >> 25) & 0x1f);
+		switch ((type >> 4) & 0x3) {
+		case 0: rd = (void *)(((long)&f->fsr) | (freg & 3)); break;
+		case 3: if (freg & 2) {
+				f->fsr |= (6 << 14) /* invalid_fp_register */;
+				goto err;
+			}
+		case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
+		case 1: rd = (void *)&f->regs[freg];
+			flags = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU; 
+			regs->fprs |= FPRS_FEF;
+			if (!(current->tss.flags & SPARC_FLAG_USEDFPU)) {
+				current->tss.flags |= SPARC_FLAG_USEDFPU;
+				f->fsr = 0;
+				f->gsr = 0;
+			}
+			if (!(current->tss.flags & flags)) {
+				if (freg < 32)
+					memset(f->regs, 0, 32*sizeof(u32));
+				else
+					memset(f->regs+32, 0, 32*sizeof(u32));
+			}
+			current->tss.flags |= flags;
+			break;
+		}
+		func(rd, rs2, rs1);
+		regs->tpc = regs->tnpc;
+		regs->tnpc += 4;
+		MOD_DEC_USE_COUNT;
+		return 1;
+	}
+err:	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Jakub Jelinek (jj@sunsite.mff.cuni.cz), Richard Henderson (rth@cygnus.com)");
+MODULE_DESCRIPTION("FPU emulation module");
+
+extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *);
+
+int init_module(void)
+{
+	handle_mathemu = do_mathemu;
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	handle_mathemu = NULL;
+}
+#endif
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/op-1.h linux/arch/sparc64/math-emu/op-1.h
--- v2.1.78/linux/arch/sparc64/math-emu/op-1.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/op-1.h	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,245 @@
+/*
+ * Basic one-word fraction declaration and manipulation.
+ */
+
+#define _FP_FRAC_DECL_1(X)	_FP_W_TYPE X##_f
+#define _FP_FRAC_COPY_1(D,S)	(D##_f = S##_f)
+#define _FP_FRAC_SET_1(X,I)	(X##_f = I)
+#define _FP_FRAC_HIGH_1(X)	(X##_f)
+#define _FP_FRAC_LOW_1(X)	(X##_f)
+#define _FP_FRAC_WORD_1(X,w)	(X##_f)
+
+#define _FP_FRAC_ADDI_1(X,I)	(X##_f += I)
+#define _FP_FRAC_SLL_1(X,N)			\
+  do {						\
+    if (__builtin_constant_p(N) && (N) == 1)	\
+      X##_f += X##_f;				\
+    else					\
+      X##_f <<= (N);				\
+  } while (0)
+#define _FP_FRAC_SRL_1(X,N)	(X##_f >>= N)
+
+/* Right shift with sticky-lsb.  */
+#define _FP_FRAC_SRS_1(X,N,sz)	__FP_FRAC_SRS_1(X##_f, N, sz)
+
+#define __FP_FRAC_SRS_1(X,N,sz)						\
+   (X = (X >> (N) | (__builtin_constant_p(N) && (N) == 1		\
+		     ? X & 1 : (X << (_FP_W_TYPE_SIZE - (N))) != 0)))
+
+#define _FP_FRAC_ADD_1(R,X,Y)	(R##_f = X##_f + Y##_f)
+#define _FP_FRAC_SUB_1(R,X,Y)	(R##_f = X##_f - Y##_f)
+#define _FP_FRAC_CLZ_1(z, X)	__FP_CLZ(z, X##_f)
+
+/* Predicates */
+#define _FP_FRAC_NEGP_1(X)	((_FP_WS_TYPE)X##_f < 0)
+#define _FP_FRAC_ZEROP_1(X)	(X##_f == 0)
+#define _FP_FRAC_OVERP_1(fs,X)	(X##_f & _FP_OVERFLOW_##fs)
+#define _FP_FRAC_EQ_1(X, Y)	(X##_f == Y##_f)
+#define _FP_FRAC_GE_1(X, Y)	(X##_f >= Y##_f)
+#define _FP_FRAC_GT_1(X, Y)	(X##_f > Y##_f)
+
+#define _FP_ZEROFRAC_1		0
+#define _FP_MINFRAC_1		1
+
+/*
+ * Unpack the raw bits of a native fp value.  Do not classify or
+ * normalize the data.
+ */
+
+#define _FP_UNPACK_RAW_1(fs, X, val)				\
+  do {								\
+    union _FP_UNION_##fs _flo; _flo.flt = (val);		\
+								\
+    X##_f = _flo.bits.frac;					\
+    X##_e = _flo.bits.exp;					\
+    X##_s = _flo.bits.sign;					\
+  } while (0)
+
+
+/*
+ * Repack the raw bits of a native fp value.
+ */
+
+#define _FP_PACK_RAW_1(fs, val, X)				\
+  do {								\
+    union _FP_UNION_##fs _flo;					\
+								\
+    _flo.bits.frac = X##_f;					\
+    _flo.bits.exp  = X##_e;					\
+    _flo.bits.sign = X##_s;					\
+								\
+    (val) = _flo.flt;						\
+  } while (0)
+
+
+/*
+ * Multiplication algorithms:
+ */
+
+/* Basic.  Assuming the host word size is >= 2*FRACBITS, we can do the
+   multiplication immediately.  */
+
+#define _FP_MUL_MEAT_1_imm(fs, R, X, Y)					\
+  do {									\
+    R##_f = X##_f * Y##_f;						\
+    /* Normalize since we know where the msb of the multiplicands	\
+       were (bit B), we know that the msb of the of the product is	\
+       at either 2B or 2B-1.  */					\
+    _FP_FRAC_SRS_1(R, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs);	\
+  } while (0)
+
+/* Given a 1W * 1W => 2W primitive, do the extended multiplication.  */
+
+#define _FP_MUL_MEAT_1_wide(fs, R, X, Y, doit)				\
+  do {									\
+    _FP_W_TYPE _Z_f0, _Z_f1;						\
+    doit(_Z_f1, _Z_f0, X##_f, Y##_f);					\
+    /* Normalize since we know where the msb of the multiplicands	\
+       were (bit B), we know that the msb of the of the product is	\
+       at either 2B or 2B-1.  */					\
+    _FP_FRAC_SRS_2(_Z, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs);	\
+    R##_f = _Z_f0;							\
+  } while (0)
+
+/* Finally, a simple widening multiply algorithm.  What fun!  */
+
+#define _FP_MUL_MEAT_1_hard(fs, R, X, Y)				\
+  do {									\
+    _FP_W_TYPE _xh, _xl, _yh, _yl, _z_f0, _z_f1, _a_f0, _a_f1;		\
+									\
+    /* split the words in half */					\
+    _xh = X##_f >> (_FP_W_TYPE_SIZE/2);					\
+    _xl = X##_f & (((_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE/2)) - 1);		\
+    _yh = Y##_f >> (_FP_W_TYPE_SIZE/2);					\
+    _yl = Y##_f & (((_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE/2)) - 1);		\
+									\
+    /* multiply the pieces */						\
+    _z_f0 = _xl * _yl;							\
+    _a_f0 = _xh * _yl;							\
+    _a_f1 = _xl * _yh;							\
+    _z_f1 = _xh * _yh;							\
+									\
+    /* reassemble into two full words */				\
+    if ((_a_f0 += _a_f1) < _a_f1)					\
+      _z_f1 += (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE/2);			\
+    _a_f1 = _a_f0 >> (_FP_W_TYPE_SIZE/2);				\
+    _a_f0 = _a_f0 << (_FP_W_TYPE_SIZE/2);				\
+    _FP_FRAC_ADD_2(_z, _z, _a);						\
+									\
+    /* normalize */							\
+    _FP_FRAC_SRS_2(_z, _FP_WFRACBITS_##fs - 1, 2*_FP_WFRACBITS_##fs);	\
+    R##_f = _z_f0;							\
+  } while (0)
+
+
+/*
+ * Division algorithms:
+ */
+
+/* Basic.  Assuming the host word size is >= 2*FRACBITS, we can do the
+   division immediately.  Give this macro either _FP_DIV_HELP_imm for
+   C primitives or _FP_DIV_HELP_ldiv for the ISO function.  Which you
+   choose will depend on what the compiler does with divrem4.  */
+
+#define _FP_DIV_MEAT_1_imm(fs, R, X, Y, doit)		\
+  do {							\
+    _FP_W_TYPE _q, _r;					\
+    X##_f <<= (X##_f < Y##_f				\
+	       ? R##_e--, _FP_WFRACBITS_##fs		\
+	       : _FP_WFRACBITS_##fs - 1);		\
+    doit(_q, _r, X##_f, Y##_f);				\
+    R##_f = _q | (_r != 0);				\
+  } while (0)
+
+/* GCC's longlong.h defines a 2W / 1W => (1W,1W) primitive udiv_qrnnd
+   that may be useful in this situation.  This first is for a primitive
+   that requires normalization, the second for one that does not.  Look
+   for UDIV_NEEDS_NORMALIZATION to tell which your machine needs.  */
+
+#define _FP_DIV_MEAT_1_udiv_norm(fs, R, X, Y)				\
+  do {									\
+    _FP_W_TYPE _nh, _nl, _q, _r;					\
+									\
+    /* Normalize Y -- i.e. make the most significant bit set.  */	\
+    Y##_f <<= _FP_WFRACXBITS_##fs - 1;					\
+									\
+    /* Shift X op correspondingly high, that is, up one full word.  */	\
+    if (X##_f <= Y##_f)							\
+      {									\
+	_nl = 0;							\
+	_nh = X##_f;							\
+      }									\
+    else								\
+      {									\
+	R##_e++;							\
+	_nl = X##_f << (_FP_W_TYPE_SIZE-1);				\
+	_nh = X##_f >> 1;						\
+      }									\
+    									\
+    udiv_qrnnd(_q, _r, _nh, _nl, Y##_f);				\
+    R##_f = _q | (_r != 0);						\
+  } while (0)
+
+#define _FP_DIV_MEAT_1_udiv(fs, R, X, Y)		\
+  do {							\
+    _FP_W_TYPE _nh, _nl, _q, _r;			\
+    if (X##_f < Y##_f)					\
+      {							\
+	R##_e--;					\
+	_nl = X##_f << _FP_WFRACBITS_##fs;		\
+	_nh = X##_f >> _FP_WFRACXBITS_##fs;		\
+      }							\
+    else						\
+      {							\
+	_nl = X##_f << (_FP_WFRACBITS_##fs - 1);	\
+	_nh = X##_f >> (_FP_WFRACXBITS_##fs + 1);	\
+      }							\
+    udiv_qrnnd(_q, _r, _nh, _nl, Y##_f);		\
+    R##_f = _q | (_r != 0);				\
+  } while (0)
+  
+  
+/*
+ * Square root algorithms:
+ * We have just one right now, maybe Newton approximation
+ * should be added for those machines where division is fast.
+ */
+ 
+#define _FP_SQRT_MEAT_1(R, S, T, X, q)			\
+  do {							\
+    while (q)						\
+      {							\
+        T##_f = S##_f + q;				\
+        if (T##_f <= X##_f)				\
+          {						\
+            S##_f = T##_f + q;				\
+            X##_f -= T##_f;				\
+            R##_f += q;					\
+          }						\
+        _FP_FRAC_SLL_1(X, 1);				\
+        q >>= 1;					\
+      }							\
+  } while (0)
+
+/*
+ * Assembly/disassembly for converting to/from integral types.  
+ * No shifting or overflow handled here.
+ */
+
+#define _FP_FRAC_ASSEMBLE_1(r, X, rsize)	(r = X##_f)
+#define _FP_FRAC_DISASSEMBLE_1(X, r, rsize)	(X##_f = r)
+
+
+/*
+ * Convert FP values between word sizes
+ */
+
+#define _FP_FRAC_CONV_1_1(dfs, sfs, D, S)				\
+  do {									\
+    D##_f = S##_f;							\
+    if (_FP_WFRACBITS_##sfs > _FP_WFRACBITS_##dfs)			\
+      _FP_FRAC_SRS_1(D, (_FP_WFRACBITS_##sfs-_FP_WFRACBITS_##dfs),	\
+		     _FP_WFRACBITS_##sfs);				\
+    else								\
+      D##_f <<= _FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs;		\
+  } while (0)
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/op-2.h linux/arch/sparc64/math-emu/op-2.h
--- v2.1.78/linux/arch/sparc64/math-emu/op-2.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/op-2.h	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,408 @@
+/*
+ * Basic two-word fraction declaration and manipulation.
+ */
+
+#define _FP_FRAC_DECL_2(X)	_FP_W_TYPE X##_f0, X##_f1
+#define _FP_FRAC_COPY_2(D,S)	(D##_f0 = S##_f0, D##_f1 = S##_f1)
+#define _FP_FRAC_SET_2(X,I)	__FP_FRAC_SET_2(X, I)
+#define _FP_FRAC_HIGH_2(X)	(X##_f1)
+#define _FP_FRAC_LOW_2(X)	(X##_f0)
+#define _FP_FRAC_WORD_2(X,w)	(X##_f##w)
+
+#define _FP_FRAC_SLL_2(X,N)						\
+  do {									\
+    if ((N) < _FP_W_TYPE_SIZE)						\
+      {									\
+        if (__builtin_constant_p(N) && (N) == 1) 			\
+          {								\
+            X##_f1 = X##_f1 + X##_f1 + (((_FP_WS_TYPE)(X##_f0)) < 0);	\
+            X##_f0 += X##_f0;						\
+          }								\
+        else								\
+          {								\
+	    X##_f1 = X##_f1 << (N) | X##_f0 >> (_FP_W_TYPE_SIZE - (N));	\
+	    X##_f0 <<= (N);						\
+	  }								\
+      }									\
+    else								\
+      {									\
+	X##_f1 = X##_f0 << ((N) - _FP_W_TYPE_SIZE);			\
+	X##_f0 = 0;							\
+      }									\
+  } while (0)
+
+#define _FP_FRAC_SRL_2(X,N)						\
+  do {									\
+    if ((N) < _FP_W_TYPE_SIZE)						\
+      {									\
+	X##_f0 = X##_f0 >> (N) | X##_f1 << (_FP_W_TYPE_SIZE - (N));	\
+	X##_f1 >>= (N);							\
+      }									\
+    else								\
+      {									\
+	X##_f0 = X##_f1 >> ((N) - _FP_W_TYPE_SIZE);			\
+	X##_f1 = 0;							\
+      }									\
+  } while (0)
+
+/* Right shift with sticky-lsb.  */
+#define _FP_FRAC_SRS_2(X,N,sz)						\
+  do {									\
+    if ((N) < _FP_W_TYPE_SIZE)						\
+      {									\
+	X##_f0 = (X##_f1 << (_FP_W_TYPE_SIZE - (N)) | X##_f0 >> (N) |	\
+		  (__builtin_constant_p(N) && (N) == 1			\
+		   ? X##_f0 & 1						\
+		   : (X##_f0 << (_FP_W_TYPE_SIZE - (N))) != 0));	\
+	X##_f1 >>= (N);							\
+      }									\
+    else								\
+      {									\
+	X##_f0 = (X##_f1 >> ((N) - _FP_W_TYPE_SIZE) |			\
+	          (((X##_f1 << (sz - (N))) | X##_f0) != 0));		\
+	X##_f1 = 0;							\
+      }									\
+  } while (0)
+
+#define _FP_FRAC_ADDI_2(X,I) \
+  __FP_FRAC_ADDI_2(X##_f1, X##_f0, I)
+
+#define _FP_FRAC_ADD_2(R,X,Y) \
+  __FP_FRAC_ADD_2(R##_f1, R##_f0, X##_f1, X##_f0, Y##_f1, Y##_f0)
+
+#define _FP_FRAC_SUB_2(R,X,Y) \
+  __FP_FRAC_SUB_2(R##_f1, R##_f0, X##_f1, X##_f0, Y##_f1, Y##_f0)
+
+#define _FP_FRAC_CLZ_2(R,X)	\
+  do {				\
+    if (X##_f1)			\
+      __FP_CLZ(R,X##_f1);	\
+    else 			\
+    {				\
+      __FP_CLZ(R,X##_f0);	\
+      R += _FP_W_TYPE_SIZE;	\
+    }				\
+  } while(0)
+
+/* Predicates */
+#define _FP_FRAC_NEGP_2(X)	((_FP_WS_TYPE)X##_f1 < 0)
+#define _FP_FRAC_ZEROP_2(X)	((X##_f1 | X##_f0) == 0)
+#define _FP_FRAC_OVERP_2(fs,X)	(X##_f1 & _FP_OVERFLOW_##fs)
+#define _FP_FRAC_EQ_2(X, Y)	(X##_f1 == Y##_f1 && X##_f0 == Y##_f0)
+#define _FP_FRAC_GT_2(X, Y)	\
+  (X##_f1 > Y##_f1 || X##_f1 == Y##_f1 && X##_f0 > Y##_f0)
+#define _FP_FRAC_GE_2(X, Y)	\
+  (X##_f1 > Y##_f1 || X##_f1 == Y##_f1 && X##_f0 >= Y##_f0)
+
+#define _FP_ZEROFRAC_2		0, 0
+#define _FP_MINFRAC_2		0, 1
+
+/*
+ * Internals 
+ */
+
+#define __FP_FRAC_SET_2(X,I1,I0)	(X##_f0 = I0, X##_f1 = I1)
+
+#define __FP_CLZ_2(R, xh, xl)	\
+  do {				\
+    if (xh)			\
+      __FP_CLZ(R,xl);		\
+    else 			\
+    {				\
+      __FP_CLZ(R,xl);		\
+      R += _FP_W_TYPE_SIZE;	\
+    }				\
+  } while(0)
+
+#if 0
+
+#ifndef __FP_FRAC_ADDI_2
+#define __FP_FRAC_ADDI_2(xh, xl, i) \
+  (xh += ((xl += i) < i))
+#endif
+#ifndef __FP_FRAC_ADD_2
+#define __FP_FRAC_ADD_2(rh, rl, xh, xl, yh, yl) \
+  (rh = xh + yh + ((rl = xl + yl) < xl))
+#endif
+#ifndef __FP_FRAC_SUB_2
+#define __FP_FRAC_SUB_2(rh, rl, xh, xl, yh, yl) \
+  (rh = xh - yh - ((rl = xl - yl) > xl))
+#endif
+
+#else
+
+#undef __FP_FRAC_ADDI_2
+#define __FP_FRAC_ADDI_2(xh, xl, i)	add_ssaaaa(xh, xl, xh, xl, 0, i)
+#undef __FP_FRAC_ADD_2
+#define __FP_FRAC_ADD_2			add_ssaaaa
+#undef __FP_FRAC_SUB_2
+#define __FP_FRAC_SUB_2			sub_ddmmss
+
+#endif
+
+/*
+ * Unpack the raw bits of a native fp value.  Do not classify or
+ * normalize the data.
+ */
+
+#define _FP_UNPACK_RAW_2(fs, X, val)			\
+  do {							\
+    union _FP_UNION_##fs _flo; _flo.flt = (val);	\
+							\
+    X##_f0 = _flo.bits.frac0;				\
+    X##_f1 = _flo.bits.frac1;				\
+    X##_e  = _flo.bits.exp;				\
+    X##_s  = _flo.bits.sign;				\
+  } while (0)
+
+
+/*
+ * Repack the raw bits of a native fp value.
+ */
+
+#define _FP_PACK_RAW_2(fs, val, X)			\
+  do {							\
+    union _FP_UNION_##fs _flo;				\
+							\
+    _flo.bits.frac0 = X##_f0;				\
+    _flo.bits.frac1 = X##_f1;				\
+    _flo.bits.exp   = X##_e;				\
+    _flo.bits.sign  = X##_s;				\
+							\
+    (val) = _flo.flt;					\
+  } while (0)
+
+
+/*
+ * Multiplication algorithms:
+ */
+
+/* Given a 1W * 1W => 2W primitive, do the extended multiplication.  */
+
+#define _FP_MUL_MEAT_2_wide(fs, R, X, Y, doit)				\
+  do {									\
+    _FP_FRAC_DECL_4(_z); _FP_FRAC_DECL_2(_b); _FP_FRAC_DECL_2(_c);	\
+									\
+    doit(_FP_FRAC_WORD_4(_z,1), _FP_FRAC_WORD_4(_z,0), X##_f0, Y##_f0); \
+    doit(_b_f1, _b_f0, X##_f0, Y##_f1);					\
+    doit(_c_f1, _c_f0, X##_f1, Y##_f0);					\
+    doit(_FP_FRAC_WORD_4(_z,3), _FP_FRAC_WORD_4(_z,2), X##_f1, Y##_f1); \
+									\
+    __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
+		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0),	\
+		    _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
+		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0),	\
+		    0, _b_f1, _b_f0, 0);				\
+    __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
+		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0),	\
+		    _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
+		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0),	\
+		    0, _c_f1, _c_f0, 0);				\
+									\
+    /* Normalize since we know where the msb of the multiplicands	\
+       were (bit B), we know that the msb of the of the product is	\
+       at either 2B or 2B-1.  */					\
+    _FP_FRAC_SRS_4(_z, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs);	\
+    R##_f0 = _FP_FRAC_WORD_4(_z,0);					\
+    R##_f1 = _FP_FRAC_WORD_4(_z,1);					\
+  } while (0)
+
+#define _FP_MUL_MEAT_2_gmp(fs, R, X, Y)					\
+  do {									\
+    _FP_W_TYPE _x[2], _y[2], _z[4];					\
+    _x[0] = X##_f0; _x[1] = X##_f1;					\
+    _y[0] = Y##_f0; _y[1] = Y##_f1;					\
+									\
+    mpn_mul_n(_z, _x, _y, 2);						\
+									\
+    /* Normalize since we know where the msb of the multiplicands	\
+       were (bit B), we know that the msb of the of the product is	\
+       at either 2B or 2B-1.  */					\
+    _FP_FRAC_SRS_4(_z, _FP_WFRACBITS##_fs-1, 2*_FP_WFRACBITS_##fs);	\
+    R##_f0 = _z[0];							\
+    R##_f1 = _z[1];							\
+  } while (0)
+
+
+/*
+ * Division algorithms:
+ */
+
+#define _FP_DIV_MEAT_2_udiv_64(fs, R, X, Y)				\
+  do {									\
+    extern void _fp_udivmodti4(_FP_W_TYPE q[2], _FP_W_TYPE r[2],	\
+			       _FP_W_TYPE n1, _FP_W_TYPE n0,		\
+			       _FP_W_TYPE d1, _FP_W_TYPE d0);		\
+    _FP_W_TYPE _n_f3, _n_f2, _n_f1, _n_f0, _r_f1, _r_f0;		\
+    _FP_W_TYPE _q_f1, _q_f0, _m_f1, _m_f0;				\
+    _FP_W_TYPE _rmem[2], _qmem[2];					\
+									\
+    if (_FP_FRAC_GT_2(X, Y))						\
+      {									\
+	R##_e++;							\
+	_n_f3 = X##_f1 >> 1;						\
+	_n_f2 = X##_f1 << (_FP_W_TYPE_SIZE - 1) | X##_f0 >> 1;		\
+	_n_f1 = X##_f0 << (_FP_W_TYPE_SIZE - 1);			\
+	_n_f0 = 0;							\
+      }									\
+    else								\
+      {									\
+	_n_f3 = X##_f1;							\
+	_n_f2 = X##_f0;							\
+	_n_f1 = _n_f0 = 0;						\
+      }									\
+									\
+    /* Normalize, i.e. make the most significant bit of the 		\
+       denominator set.  */						\
+    _FP_FRAC_SLL_2(Y, _FP_WFRACXBITS_##fs - 1);				\
+									\
+    /* Do the 256/128 bit division given the 128-bit _fp_udivmodtf4 	\
+       primitive snagged from libgcc2.c.  */				\
+									\
+    _fp_udivmodti4(_qmem, _rmem, _n_f3, _n_f2, 0, Y##_f1);		\
+    _q_f1 = _qmem[0];							\
+    umul_ppmm(_m_f1, _m_f0, _q_f1, Y##_f0);				\
+    _r_f1 = _rmem[0];							\
+    _r_f0 = _n_f1;							\
+    if (_FP_FRAC_GT_2(_m, _r))						\
+      {									\
+	_q_f1--;							\
+	_FP_FRAC_ADD_2(_r, _r, Y);					\
+	if (_FP_FRAC_GE_2(_r, Y) && _FP_FRAC_GT_2(_m, _r))		\
+	  {								\
+	    _q_f1--;							\
+	    _FP_FRAC_ADD_2(_r, _r, Y);					\
+	  }								\
+      }									\
+    _FP_FRAC_SUB_2(_r, _r, _m);						\
+									\
+    _fp_udivmodti4(_qmem, _rmem, _r_f1, _r_f0, 0, Y##_f1);		\
+    _q_f0 = _qmem[0];							\
+    umul_ppmm(_m_f1, _m_f0, _q_f0, Y##_f0);				\
+    _r_f1 = _rmem[0];							\
+    _r_f0 = _n_f0;							\
+    if (_FP_FRAC_GT_2(_m, _r))						\
+      {									\
+	_q_f0--;							\
+	_FP_FRAC_ADD_2(_r, _r, Y);					\
+	if (_FP_FRAC_GE_2(_r, Y) && _FP_FRAC_GT_2(_m, _r))		\
+	  {								\
+	    _q_f0--;							\
+	    _FP_FRAC_ADD_2(_r, _r, Y);					\
+	  }								\
+      }									\
+    _FP_FRAC_SUB_2(_r, _r, _m);						\
+									\
+    R##_f1 = _q_f1;							\
+    R##_f0 = _q_f0 | ((_r_f1 | _r_f0) != 0);				\
+  } while (0)
+
+
+#define _FP_DIV_MEAT_2_gmp(fs, R, X, Y)					\
+  do {									\
+    _FP_W_TYPE _x[4], _y[2], _z[4];					\
+    _y[0] = Y##_f0; _y[1] = Y##_f1;					\
+    _x[0] = _x[3] = 0;							\
+    if (_FP_FRAC_GT_2(X, Y))						\
+      {									\
+	R##_e++;							\
+	_x[1] = (X##_f0 << (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE) |	\
+		 X##_f1 >> (_FP_W_TYPE_SIZE -				\
+			    (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE)));	\
+	_x[2] = X##_f1 << (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE);		\
+      }									\
+    else								\
+      {									\
+	_x[1] = (X##_f0 << (_FP_WFRACBITS - _FP_W_TYPE_SIZE) |		\
+		 X##_f1 >> (_FP_W_TYPE_SIZE -				\
+			    (_FP_WFRACBITS - _FP_W_TYPE_SIZE)));	\
+	_x[2] = X##_f1 << (_FP_WFRACBITS - _FP_W_TYPE_SIZE);		\
+      }									\
+									\
+    (void) mpn_divrem (_z, 0, _x, 4, _y, 2);				\
+    R##_f1 = _z[1];							\
+    R##_f0 = _z[0] | ((_x[0] | _x[1]) != 0);				\
+  } while (0)
+
+
+/*
+ * Square root algorithms:
+ * We have just one right now, maybe Newton approximation
+ * should be added for those machines where division is fast.
+ */
+ 
+#define _FP_SQRT_MEAT_2(R, S, T, X, q)			\
+  do {							\
+    while (q)						\
+      {							\
+        T##_f1 = S##_f1 + q;				\
+        if (T##_f1 <= X##_f1)				\
+          {						\
+            S##_f1 = T##_f1 + q;			\
+            X##_f1 -= T##_f1;				\
+            R##_f1 += q;				\
+          }						\
+        _FP_FRAC_SLL_2(X, 1);				\
+        q >>= 1;					\
+      }							\
+    q = (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE - 1);		\
+    while (q)						\
+      {							\
+        T##_f0 = S##_f0 + q;				\
+        T##_f1 = S##_f1;				\
+        if (T##_f1 < X##_f1 || 				\
+            (T##_f1 == X##_f1 && T##_f0 < X##_f0))	\
+          {						\
+            S##_f0 = T##_f0 + q;			\
+            if (((_FP_WS_TYPE)T##_f0) < 0 &&		\
+                ((_FP_WS_TYPE)S##_f0) >= 0)		\
+              S##_f1++;					\
+            _FP_FRAC_SUB_2(X, X, T);			\
+            R##_f0 += q;				\
+          }						\
+        _FP_FRAC_SLL_2(X, 1);				\
+        q >>= 1;					\
+      }							\
+  } while (0)
+
+
+/*
+ * Assembly/disassembly for converting to/from integral types.  
+ * No shifting or overflow handled here.
+ */
+
+#define _FP_FRAC_ASSEMBLE_2(r, X, rsize)	\
+  do {						\
+    if (rsize <= _FP_W_TYPE_SIZE)		\
+      r = X##_f0;				\
+    else					\
+      {						\
+	r = X##_f1;				\
+	r <<= _FP_W_TYPE_SIZE;			\
+	r += X##_f0;				\
+      }						\
+  } while (0)
+
+#define _FP_FRAC_DISASSEMBLE_2(X, r, rsize)				\
+  do {									\
+    X##_f0 = r;								\
+    X##_f1 = (rsize <= _FP_W_TYPE_SIZE ? 0 : r >> _FP_W_TYPE_SIZE);	\
+  } while (0)
+
+/*
+ * Convert FP values between word sizes
+ */
+
+#define _FP_FRAC_CONV_1_2(dfs, sfs, D, S)				\
+  do {									\
+    _FP_FRAC_SRS_2(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs),	\
+		   _FP_WFRACBITS_##sfs);				\
+    D##_f = S##_f0;							\
+  } while (0)
+
+#define _FP_FRAC_CONV_2_1(dfs, sfs, D, S)				\
+  do {									\
+    D##_f0 = S##_f;							\
+    D##_f1 = 0;								\
+    _FP_FRAC_SLL_2(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs));	\
+  } while (0)
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/op-4.h linux/arch/sparc64/math-emu/op-4.h
--- v2.1.78/linux/arch/sparc64/math-emu/op-4.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/op-4.h	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,78 @@
+/*
+ * Basic four-word fraction declaration and manipulation.
+ */
+
+#define _FP_FRAC_DECL_4(X)	_FP_W_TYPE X##_f[4]
+#define _FP_FRAC_COPY_4(D,S)			\
+  (D##_f[0] = S##_f[0], D##_f[1] = S##_f[1],	\
+   D##_f[2] = S##_f[2], D##_f[3] = S##_f[3])
+#define _FP_FRAC_SET_4(X,I)	__FP_FRAC_SET_4(X, I)
+#define _FP_FRAC_HIGH_4(X)	(X##_f[3])
+#define _FP_FRAC_LOW_4(X)	(X##_f[0])
+#define _FP_FRAC_WORD_4(X,w)	(X##_f[w])
+
+#define _FP_FRAC_SLL_4(X,N)						\
+  do {									\
+    _FP_I_TYPE _up, _down, _skip, _i;					\
+    _skip = (N) / _FP_W_TYPE_SIZE;					\
+    _up = (N) % _FP_W_TYPE_SIZE;					\
+    _down = _FP_W_TYPE_SIZE - _up;					\
+    for (_i = 3; _i > _skip; --_i)					\
+      X##_f[_i] = X##_f[_i-_skip] << _up | X##_f[_i-_skip-1] >> _down;	\
+    X##_f[_i] <<= _up;							\
+    for (--_i; _i >= 0; --_i)						\
+      X##_f[_i] = 0;							\
+  } while (0)
+
+#define _FP_FRAC_SRL_4(X,N)						\
+  do {									\
+    _FP_I_TYPE _up, _down, _skip, _i;					\
+    _skip = (N) / _FP_W_TYPE_SIZE;					\
+    _down = (N) % _FP_W_TYPE_SIZE;					\
+    _up = _FP_W_TYPE_SIZE - _down;					\
+    for (_i = 0; _i < 4-_skip; ++_i)					\
+      X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up;	\
+    X##_f[_i] >>= _down;						\
+    for (++_i; _i < 4; ++_i)						\
+      X##_f[_i] = 0;							\
+  } while (0)
+
+
+/* Right shift with sticky-lsb.  */
+#define _FP_FRAC_SRS_4(X,N,size)					\
+  do {									\
+    _FP_I_TYPE _up, _down, _skip, _i;					\
+    _FP_W_TYPE _s;							\
+    _skip = (N) / _FP_W_TYPE_SIZE;					\
+    _down = (N) % _FP_W_TYPE_SIZE;					\
+    _up = _FP_W_TYPE_SIZE - _down;					\
+    for (_s = _i = 0; _i < _skip; ++_i)					\
+      _s |= X##_f[_i];							\
+    _s = X##_f[_i] << _up;						\
+    X##_f[0] = X##_f[_skip] >> _down | X##_f[_skip+1] << _up | (_s != 0); \
+    for (_i = 1; _i < 4-_skip; ++_i)					\
+      X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up;	\
+    X##_f[_i] >>= _down;						\
+    for (++_i; _i < 4; ++_i)						\
+      X##_f[_i] = 0;							\
+  } while (0)
+
+#define _FP_FRAC_ADD_4(R,X,Y)						\
+  __FP_FRAC_ADD_4(R##_f[3], R##_f[2], R##_f[1], R##_f[0],		\
+		  X##_f[3], X##_f[2], X##_f[1], X##_f[0],		\
+		  Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0])
+
+/*
+ * Internals 
+ */
+
+#define __FP_FRAC_SET_4(X,I3,I2,I1,I0)					\
+  (X##_f[3] = I3, X##_f[2] = I2, X##_f[1] = I1, X##_f[0] = I0)
+
+#ifndef __FP_FRAC_ADD_4
+#define __FP_FRAC_ADD_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0)		\
+  (r0 = x0 + y0,							\
+   r1 = x1 + y1 + (r0 < x0),						\
+   r2 = x2 + y2 + (r1 < x1),						\
+   r3 = x3 + y3 + (r2 < x2))
+#endif
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/op-common.h linux/arch/sparc64/math-emu/op-common.h
--- v2.1.78/linux/arch/sparc64/math-emu/op-common.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/op-common.h	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,628 @@
+#define _FP_DECL(wc, X)			\
+  _FP_I_TYPE X##_c, X##_s, X##_e;	\
+  _FP_FRAC_DECL_##wc(X)
+
+/*
+ * Finish truely unpacking a native fp value by classifying the kind
+ * of fp value and normalizing both the exponent and the fraction.
+ */
+
+#define _FP_UNPACK_CANONICAL(fs, wc, X)					\
+do {									\
+  switch (X##_e)							\
+  {									\
+  default:								\
+    _FP_FRAC_HIGH_##wc(X) |= _FP_IMPLBIT_##fs;				\
+    _FP_FRAC_SLL_##wc(X, _FP_WORKBITS);					\
+    X##_e -= _FP_EXPBIAS_##fs;						\
+    X##_c = FP_CLS_NORMAL;						\
+    break;								\
+									\
+  case 0:								\
+    if (_FP_FRAC_ZEROP_##wc(X))						\
+      X##_c = FP_CLS_ZERO;						\
+    else								\
+      {									\
+	/* a denormalized number */					\
+	_FP_I_TYPE _shift;						\
+	_FP_FRAC_CLZ_##wc(_shift, X);					\
+	_shift -= _FP_FRACXBITS_##fs;					\
+	_FP_FRAC_SLL_##wc(X, (_shift+_FP_WORKBITS));			\
+	X##_e -= _FP_EXPBIAS_##fs - 1 + _shift;				\
+	X##_c = FP_CLS_NORMAL;						\
+      }									\
+    break;								\
+									\
+  case _FP_EXPMAX_##fs:							\
+    if (_FP_FRAC_ZEROP_##wc(X))						\
+      X##_c = FP_CLS_INF;						\
+    else								\
+      /* we don't differentiate between signaling and quiet nans */	\
+      X##_c = FP_CLS_NAN;						\
+    break;								\
+  }									\
+} while (0)
+
+
+/*
+ * Before packing the bits back into the native fp result, take care
+ * of such mundane things as rounding and overflow.  Also, for some
+ * kinds of fp values, the original parts may not have been fully
+ * extracted -- but that is ok, we can regenerate them now.
+ */
+
+#define _FP_PACK_CANONICAL(fs, wc, X)				\
+do {								\
+  switch (X##_c)						\
+  {								\
+  case FP_CLS_NORMAL:						\
+    X##_e += _FP_EXPBIAS_##fs;					\
+    if (X##_e > 0)						\
+      {								\
+	_FP_ROUND(wc, X);					\
+	if (_FP_FRAC_OVERP_##wc(fs, X))				\
+	  {							\
+	    _FP_FRAC_SRL_##wc(X, (_FP_WORKBITS+1));		\
+	    X##_e++;						\
+	  }							\
+	else							\
+	  _FP_FRAC_SRL_##wc(X, _FP_WORKBITS);			\
+	if (X##_e >= _FP_EXPMAX_##fs)				\
+	  {							\
+	    /* overflow to infinity */				\
+	    X##_e = _FP_EXPMAX_##fs;				\
+	    _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);		\
+	  }							\
+      }								\
+    else							\
+      {								\
+	/* we've got a denormalized number */			\
+	X##_e = -X##_e + 1;					\
+	if (X##_e <= _FP_WFRACBITS_##fs)			\
+	  {							\
+	    _FP_FRAC_SRS_##wc(X, X##_e, _FP_WFRACBITS_##fs);	\
+	    _FP_ROUND(wc, X);					\
+	    X##_e = _FP_FRAC_OVERP_##wc(fs, X);			\
+	    _FP_FRAC_SRL_##wc(X, _FP_WORKBITS);			\
+	  }							\
+	else							\
+	  {							\
+	    /* underflow to zero */				\
+	    X##_e = 0;						\
+	    _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);		\
+	  }							\
+      }								\
+    break;							\
+								\
+  case FP_CLS_ZERO:						\
+    X##_e = 0;							\
+    _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);			\
+    break;							\
+								\
+  case FP_CLS_INF:						\
+    X##_e = _FP_EXPMAX_##fs;					\
+    _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);			\
+    break;							\
+								\
+  case FP_CLS_NAN:						\
+    X##_e = _FP_EXPMAX_##fs;					\
+    if (!_FP_KEEPNANFRACP)					\
+      {								\
+	_FP_FRAC_SET_##wc(X, _FP_NANFRAC_##fs);			\
+	X##_s = 0;						\
+      }								\
+    else							\
+      _FP_FRAC_HIGH_##wc(X) |= _FP_QNANBIT_##fs;		\
+    break;							\
+  }								\
+} while (0)
+
+
+/*
+ * Main addition routine.  The input values should be cooked.
+ */
+
+#define _FP_ADD(fs, wc, R, X, Y)					     \
+do {									     \
+  switch (_FP_CLS_COMBINE(X##_c, Y##_c))				     \
+  {									     \
+  case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL):			     \
+    {									     \
+      /* shift the smaller number so that its exponent matches the larger */ \
+      _FP_I_TYPE diff = X##_e - Y##_e;					     \
+									     \
+      if (diff < 0)							     \
+	{								     \
+	  diff = -diff;							     \
+	  if (diff <= _FP_WFRACBITS_##fs)				     \
+	    _FP_FRAC_SRS_##wc(X, diff, _FP_WFRACBITS_##fs);		     \
+	  else if (!_FP_FRAC_ZEROP_##wc(X))				     \
+	    _FP_FRAC_SET_##wc(X, _FP_MINFRAC_##wc);			     \
+	  else								     \
+	    _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);			     \
+	  R##_e = Y##_e;						     \
+	}								     \
+      else								     \
+	{								     \
+	  if (diff > 0)							     \
+	    {								     \
+	      if (diff <= _FP_WFRACBITS_##fs)				     \
+	        _FP_FRAC_SRS_##wc(Y, diff, _FP_WFRACBITS_##fs);		     \
+	      else if (!_FP_FRAC_ZEROP_##wc(Y))				     \
+	        _FP_FRAC_SET_##wc(Y, _FP_MINFRAC_##wc);			     \
+	      else							     \
+	        _FP_FRAC_SET_##wc(Y, _FP_ZEROFRAC_##wc);		     \
+	    }								     \
+	  R##_e = X##_e;						     \
+	}								     \
+									     \
+      R##_c = FP_CLS_NORMAL;						     \
+									     \
+      if (X##_s == Y##_s)						     \
+	{								     \
+	  R##_s = X##_s;						     \
+	  _FP_FRAC_ADD_##wc(R, X, Y);					     \
+	  if (_FP_FRAC_OVERP_##wc(fs, R))				     \
+	    {								     \
+	      _FP_FRAC_SRS_##wc(R, 1, _FP_WFRACBITS_##fs);		     \
+	      R##_e++;							     \
+	    }								     \
+	}								     \
+      else								     \
+	{								     \
+	  R##_s = X##_s;						     \
+	  _FP_FRAC_SUB_##wc(R, X, Y);					     \
+	  if (_FP_FRAC_ZEROP_##wc(R))					     \
+	    {								     \
+	      /* return an exact zero */				     \
+	      if (FP_ROUNDMODE == FP_RND_MINF)				     \
+		R##_s |= Y##_s;						     \
+	      else							     \
+		R##_s &= Y##_s;						     \
+	      R##_c = FP_CLS_ZERO;					     \
+	    }								     \
+	  else								     \
+	    {								     \
+	      if (_FP_FRAC_NEGP_##wc(R))				     \
+		{							     \
+		  _FP_FRAC_SUB_##wc(R, Y, X);				     \
+		  R##_s = Y##_s;					     \
+		}							     \
+									     \
+	      /* renormalize after subtraction */			     \
+	      _FP_FRAC_CLZ_##wc(diff, R);				     \
+	      diff -= _FP_WFRACXBITS_##fs;				     \
+	      if (diff)							     \
+		{							     \
+		  R##_e -= diff;					     \
+		  _FP_FRAC_SLL_##wc(R, diff);				     \
+		}							     \
+	    }								     \
+	}								     \
+      break;								     \
+    }									     \
+									     \
+  case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN):				     \
+    _FP_CHOOSENAN(fs, wc, R, X, Y);					     \
+    break;								     \
+									     \
+  case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO):			     \
+    R##_e = X##_e;							     \
+  case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL):			     \
+  case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF):				     \
+  case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO):				     \
+    _FP_FRAC_COPY_##wc(R, X);						     \
+    R##_s = X##_s;							     \
+    R##_c = X##_c;							     \
+    break;								     \
+									     \
+  case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL):			     \
+    R##_e = Y##_e;							     \
+  case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN):			     \
+  case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN):				     \
+  case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN):				     \
+    _FP_FRAC_COPY_##wc(R, Y);						     \
+    R##_s = Y##_s;							     \
+    R##_c = Y##_c;							     \
+    break;								     \
+									     \
+  case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF):				     \
+    if (X##_s != Y##_s)							     \
+      {									     \
+	/* +INF + -INF => NAN */					     \
+	_FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs);				     \
+	R##_s = X##_s ^ Y##_s;						     \
+	R##_c = FP_CLS_NAN;						     \
+	break;								     \
+      }									     \
+    /* FALLTHRU */							     \
+									     \
+  case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL):			     \
+  case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO):				     \
+    R##_s = X##_s;							     \
+    R##_c = FP_CLS_INF;							     \
+    break;								     \
+									     \
+  case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF):			     \
+  case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF):				     \
+    R##_s = Y##_s;							     \
+    R##_c = FP_CLS_INF;							     \
+    break;								     \
+									     \
+  case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO):			     \
+    /* make sure the sign is correct */					     \
+    if (FP_ROUNDMODE == FP_RND_MINF)					     \
+      R##_s = X##_s | Y##_s;						     \
+    else								     \
+      R##_s = X##_s & Y##_s;						     \
+    R##_c = FP_CLS_ZERO;						     \
+    break;								     \
+									     \
+  default:								     \
+    abort();								     \
+  }									     \
+} while (0)
+
+
+/*
+ * Main negation routine.  FIXME -- when we care about setting exception
+ * bits reliably, this will not do.  We should examine all of the fp classes.
+ */
+
+#define _FP_NEG(fs, wc, R, X)		\
+  do {					\
+    _FP_FRAC_COPY_##wc(R, X);		\
+    R##_c = X##_c;			\
+    R##_e = X##_e;			\
+    R##_s = 1 ^ X##_s;			\
+  } while (0)
+
+
+/*
+ * Main multiplication routine.  The input values should be cooked.
+ */
+
+#define _FP_MUL(fs, wc, R, X, Y)			\
+do {							\
+  R##_s = X##_s ^ Y##_s;				\
+  switch (_FP_CLS_COMBINE(X##_c, Y##_c))		\
+  {							\
+  case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL):	\
+    R##_c = FP_CLS_NORMAL;				\
+    R##_e = X##_e + Y##_e + 1;				\
+							\
+    _FP_MUL_MEAT_##fs(R,X,Y);				\
+							\
+    if (_FP_FRAC_OVERP_##wc(fs, R))			\
+      _FP_FRAC_SRS_##wc(R, 1, _FP_WFRACBITS_##fs);	\
+    else						\
+      R##_e--;						\
+    break;						\
+							\
+  case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN):		\
+    _FP_CHOOSENAN(fs, wc, R, X, Y);			\
+    break;						\
+							\
+  case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL):	\
+  case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF):		\
+  case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO):		\
+    R##_s = X##_s;					\
+							\
+  case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF):		\
+  case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL):	\
+  case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL):	\
+  case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO):	\
+    _FP_FRAC_COPY_##wc(R, X);				\
+    R##_c = X##_c;					\
+    break;						\
+							\
+  case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN):	\
+  case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN):		\
+  case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN):		\
+    R##_s = Y##_s;					\
+							\
+  case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF):	\
+  case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO):	\
+    _FP_FRAC_COPY_##wc(R, Y);				\
+    R##_c = Y##_c;					\
+    break;						\
+							\
+  case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO):		\
+  case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF):		\
+    R##_c = FP_CLS_NAN;					\
+    _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs);		\
+    break;						\
+							\
+  default:						\
+    abort();						\
+  }							\
+} while (0)
+
+
+/*
+ * Main division routine.  The input values should be cooked.
+ */
+
+#define _FP_DIV(fs, wc, R, X, Y)			\
+do {							\
+  R##_s = X##_s ^ Y##_s;				\
+  switch (_FP_CLS_COMBINE(X##_c, Y##_c))		\
+  {							\
+  case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL):	\
+    R##_c = FP_CLS_NORMAL;				\
+    R##_e = X##_e - Y##_e;				\
+							\
+    _FP_DIV_MEAT_##fs(R,X,Y);				\
+    break;						\
+							\
+  case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN):		\
+    _FP_CHOOSENAN(fs, wc, R, X, Y);			\
+    break;						\
+							\
+  case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL):	\
+  case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF):		\
+  case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO):		\
+    R##_s = X##_s;					\
+    _FP_FRAC_COPY_##wc(R, X);				\
+    R##_c = X##_c;					\
+    break;						\
+							\
+  case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN):	\
+  case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN):		\
+  case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN):		\
+    R##_s = Y##_s;					\
+    _FP_FRAC_COPY_##wc(R, Y);				\
+    R##_c = Y##_c;					\
+    break;						\
+							\
+  case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF):	\
+  case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF):		\
+  case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL):	\
+    R##_c = FP_CLS_ZERO;				\
+    break;						\
+							\
+  case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO):	\
+  case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO):		\
+  case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL):	\
+    R##_c = FP_CLS_INF;					\
+    break;						\
+							\
+  case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF):		\
+  case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO):	\
+    R##_c = FP_CLS_NAN;					\
+    _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs);		\
+    break;						\
+							\
+  default:						\
+    abort();						\
+  }							\
+} while (0)
+
+
+/*
+ * Main differential comparison routine.  The inputs should be raw not
+ * cooked.  The return is -1,0,1 for normal values, 2 otherwise.
+ */
+
+#define _FP_CMP(fs, wc, ret, X, Y, un)					\
+  do {									\
+    /* NANs are unordered */						\
+    if ((X##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(X))		\
+	|| (Y##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(Y)))	\
+      {									\
+	ret = un;							\
+      }									\
+    else								\
+      {									\
+	/* Force -0 -> +0 */						\
+	if (!X##_e && _FP_FRAC_ZEROP_##wc(X)) X##_s = 0;		\
+	if (!Y##_e && _FP_FRAC_ZEROP_##wc(Y)) X##_s = 0;		\
+									\
+	if (X##_s != Y##_s)						\
+	  ret = X##_s ? -1 : 1;						\
+	else if (X##_e > Y##_e)						\
+	  ret = X##_s ? -1 : 1;						\
+	else if (X##_e < Y##_e)						\
+	  ret = X##_s ? 1 : -1;						\
+	else if (_FP_FRAC_GT_##wc(X, Y))				\
+	  ret = X##_s ? -1 : 1;						\
+	else if (_FP_FRAC_GT_##wc(Y, X))				\
+	  ret = X##_s ? 1 : -1;						\
+	else								\
+	  ret = 0;							\
+      }									\
+  } while (0)
+
+
+/* Simplification for strict equality.  */
+
+#define _FP_CMP_EQ(fs, wc, ret, X, Y)					  \
+  do {									  \
+    /* NANs are unordered */						  \
+    if ((X##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(X))		  \
+	|| (Y##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(Y)))	  \
+      {									  \
+	ret = 1;							  \
+      }									  \
+    else								  \
+      {									  \
+	ret = !(X##_e == Y##_e						  \
+		&& _FP_FRAC_EQ_##wc(X, Y)				  \
+		&& (X##_s == Y##_s || !X##_e && _FP_FRAC_ZEROP_##wc(X))); \
+      }									  \
+  } while (0)
+
+/*
+ * Main square root routine.  The input value should be cooked.
+ */
+
+#define _FP_SQRT(fs, wc, R, X)						\
+do {									\
+    _FP_FRAC_DECL_##wc(T); _FP_FRAC_DECL_##wc(S);			\
+    _FP_W_TYPE q;							\
+    switch (X##_c)							\
+    {									\
+    case FP_CLS_NAN:							\
+    	R##_s = 0;							\
+    	R##_c = FP_CLS_NAN;						\
+    	_FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);			\
+    	break;								\
+    case FP_CLS_INF:							\
+    	if (X##_s)							\
+    	  {								\
+    	    R##_s = 0;							\
+	    R##_c = FP_CLS_NAN; /* sNAN */				\
+    	  }								\
+    	else								\
+    	  {								\
+    	    R##_s = 0;							\
+    	    R##_c = FP_CLS_INF; /* sqrt(+inf) = +inf */			\
+    	  }								\
+    	break;								\
+    case FP_CLS_ZERO:							\
+	R##_s = X##_s;							\
+    	R##_c = FP_CLS_ZERO; /* sqrt(+-0) = +-0 */			\
+	break;								\
+    case FP_CLS_NORMAL:							\
+    	R##_s = 0;							\
+        if (X##_s)							\
+          {								\
+	    R##_c = FP_CLS_NAN; /* sNAN */				\
+	    break;							\
+          }								\
+    	R##_c = FP_CLS_NORMAL;						\
+        if (X##_e & 1)							\
+          _FP_FRAC_SLL_##wc(X, 1);					\
+        R##_e = X##_e >> 1;						\
+        _FP_FRAC_SET_##wc(S, _FP_ZEROFRAC_##wc);			\
+        _FP_FRAC_SET_##wc(R, _FP_ZEROFRAC_##wc);			\
+        q = _FP_OVERFLOW_##fs;						\
+        _FP_FRAC_SLL_##wc(X, 1);					\
+        _FP_SQRT_MEAT_##wc(R, S, T, X, q);				\
+        _FP_FRAC_SRL_##wc(R, 1);					\
+    }									\
+  } while (0)
+
+/*
+ * Convert from FP to integer
+ */
+
+#define _FP_TO_INT(fs, wc, r, X, rsize, rsigned)				\
+  do {										\
+    switch (X##_c)								\
+      {										\
+      case FP_CLS_NORMAL:							\
+	if (X##_e < 0)								\
+	  {									\
+	  case FP_CLS_NAN:							\
+	  case FP_CLS_ZERO:							\
+	    r = 0;								\
+	  }									\
+	else if (X##_e >= rsize - (rsigned != 0))				\
+	  {									\
+	  case FP_CLS_INF:							\
+	    if (rsigned)							\
+	      {									\
+		r = 1;								\
+		r <<= rsize - 1;						\
+		r -= 1 - X##_s;							\
+	      }									\
+	    else								\
+	      {									\
+		r = 0;								\
+		if (!X##_s)							\
+		  r = ~r;							\
+	      }									\
+	  }									\
+	else									\
+	  {									\
+	    if (_FP_W_TYPE_SIZE*wc < rsize)					\
+	      {									\
+		_FP_FRAC_ASSEMBLE_##wc(r, X, rsize);				\
+		r <<= X##_e - _FP_WFRACBITS_##fs;				\
+	      }									\
+	    else								\
+	      {									\
+		if (X##_e >= _FP_WFRACBITS_##fs)				\
+		  _FP_FRAC_SLL_##wc(X, (X##_e - _FP_WFRACBITS_##fs + 1));	\
+		else								\
+		  _FP_FRAC_SRL_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 1));	\
+		_FP_FRAC_ASSEMBLE_##wc(r, X, rsize);				\
+	      }									\
+	    if (rsigned && X##_s)						\
+	      r = -r;								\
+	  }									\
+	break;									\
+      }										\
+  } while (0)
+
+#define _FP_FROM_INT(fs, wc, X, r, rsize, rtype)			\
+  do {									\
+    if (r)								\
+      {									\
+	X##_c = FP_CLS_NORMAL;						\
+									\
+	if ((X##_s = (r < 0)))						\
+	  r = -r;							\
+	/* Note that `r' is now considered unsigned, so we don't have	\
+	   to worry about the single signed overflow case.  */		\
+									\
+	if (rsize <= _FP_W_TYPE_SIZE)					\
+	  __FP_CLZ(X##_e, r);						\
+	else								\
+	  __FP_CLZ_2(X##_e, (_FP_W_TYPE)(r >> _FP_W_TYPE_SIZE), 	\
+		     (_FP_W_TYPE)r);					\
+	if (rsize < _FP_W_TYPE_SIZE)					\
+		X##_e -= (_FP_W_TYPE_SIZE - rsize);			\
+	X##_e = rsize - X##_e - 1;					\
+									\
+	if (_FP_FRACBITS_##fs < rsize && _FP_WFRACBITS_##fs < X##_e)	\
+	  __FP_FRAC_SRS_1(r, (X##_e - _FP_WFRACBITS_##fs), rsize);	\
+	r &= ~((_FP_W_TYPE)1 << X##_e);					\
+	_FP_FRAC_DISASSEMBLE_##wc(X, ((unsigned rtype)r), rsize);	\
+	_FP_FRAC_SLL_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 1));		\
+      }									\
+    else								\
+      {									\
+	X##_c = FP_CLS_ZERO, X##_s = 0;					\
+      }									\
+  } while (0)
+
+
+#define FP_CONV(dfs,sfs,dwc,swc,D,S)			\
+  do {							\
+    _FP_FRAC_CONV_##dwc##_##swc(dfs, sfs, D, S);	\
+    D##_e = S##_e;					\
+    D##_c = S##_c;					\
+    D##_s = S##_s;					\
+  } while (0)
+
+/*
+ * Helper primitives.
+ */
+
+/* Count leading zeros in a word.  */
+
+#ifndef __FP_CLZ
+#define __FP_CLZ(r, x)				\
+  do {						\
+    _FP_W_TYPE _t = (x);			\
+    r = _FP_W_TYPE_SIZE - 1;			\
+    if (_t > 0xffffffff) r -= 32;		\
+    if (_t > 0xffffffff) _t >>= 32;		\
+    if (_t > 0xffff) r -= 16;			\
+    if (_t > 0xffff) _t >>= 16;			\
+    if (_t > 0xff) r -= 8;			\
+    if (_t > 0xff) _t >>= 8;			\
+    if (_t & 0xf0) r -= 4;			\
+    if (_t & 0xf0) _t >>= 4;			\
+    if (_t & 0xc) r -= 2;			\
+    if (_t & 0xc) _t >>= 2;			\
+    if (_t & 0x2) r -= 1;			\
+  } while (0)
+#endif
+
+#define _FP_DIV_HELP_imm(q, r, n, d)		\
+  do {						\
+    q = n / d, r = n % d;			\
+  } while (0)
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/quad.h linux/arch/sparc64/math-emu/quad.h
--- v2.1.78/linux/arch/sparc64/math-emu/quad.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/quad.h	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,71 @@
+/*
+ * Definitions for IEEE Quad Precision
+ */
+
+#if _FP_W_TYPE_SIZE < 64
+#error "Only stud muffins allowed, schmuck."
+#endif
+
+#define _FP_FRACTBITS_Q		(2*_FP_W_TYPE_SIZE)
+
+#define _FP_FRACBITS_Q		113
+#define _FP_FRACXBITS_Q		(_FP_FRACTBITS_Q - _FP_FRACBITS_Q)
+#define _FP_WFRACBITS_Q		(_FP_WORKBITS + _FP_FRACBITS_Q)
+#define _FP_WFRACXBITS_Q	(_FP_FRACTBITS_Q - _FP_WFRACBITS_Q)
+#define _FP_EXPBITS_Q		15
+#define _FP_EXPBIAS_Q		16383
+#define _FP_EXPMAX_Q		32767
+
+#define _FP_QNANBIT_Q		\
+	((_FP_W_TYPE)1 << (_FP_FRACBITS_Q-2) % _FP_W_TYPE_SIZE)
+#define _FP_IMPLBIT_Q		\
+	((_FP_W_TYPE)1 << (_FP_FRACBITS_Q-1) % _FP_W_TYPE_SIZE)
+#define _FP_OVERFLOW_Q		\
+	((_FP_W_TYPE)1 << (_FP_WFRACBITS_Q % _FP_W_TYPE_SIZE))
+
+union _FP_UNION_Q
+{
+  long double flt /* __attribute__((mode(TF))) */ ;
+  struct {
+#if __BYTE_ORDER == __BIG_ENDIAN
+    unsigned sign  : 1;
+    unsigned exp   : _FP_EXPBITS_Q;
+    unsigned long frac1 : _FP_FRACBITS_Q-(_FP_IMPLBIT_Q != 0)-_FP_W_TYPE_SIZE;
+    unsigned long frac0 : _FP_W_TYPE_SIZE;
+#else
+    unsigned long frac0 : _FP_W_TYPE_SIZE;
+    unsigned long frac1 : _FP_FRACBITS_Q-(_FP_IMPLBIT_Q != 0)-_FP_W_TYPE_SIZE;
+    unsigned exp   : _FP_EXPBITS_Q;
+    unsigned sign  : 1;
+#endif
+  } bits;
+};
+
+#define FP_DECL_Q(X)		_FP_DECL(2,X)
+#define FP_UNPACK_RAW_Q(X,val)	_FP_UNPACK_RAW_2(Q,X,val)
+#define FP_PACK_RAW_Q(val,X)	_FP_PACK_RAW_2(Q,val,X)
+
+#define FP_UNPACK_Q(X,val)		\
+  do {					\
+    _FP_UNPACK_RAW_2(Q,X,val);		\
+    _FP_UNPACK_CANONICAL(Q,2,X);	\
+  } while (0)
+
+#define FP_PACK_Q(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(Q,2,X);		\
+    _FP_PACK_RAW_2(Q,val,X);		\
+  } while (0)
+
+#define FP_NEG_Q(R,X)		_FP_NEG(Q,2,R,X)
+#define FP_ADD_Q(R,X,Y)		_FP_ADD(Q,2,R,X,Y)
+#define FP_SUB_Q(R,X,Y)		_FP_SUB(Q,2,R,X,Y)
+#define FP_MUL_Q(R,X,Y)		_FP_MUL(Q,2,R,X,Y)
+#define FP_DIV_Q(R,X,Y)		_FP_DIV(Q,2,R,X,Y)
+#define FP_SQRT_Q(R,X)		_FP_SQRT(Q,2,R,X)
+
+#define FP_CMP_Q(r,X,Y,un)	_FP_CMP(Q,2,r,X,Y,un)
+#define FP_CMP_EQ_Q(r,X,Y)	_FP_CMP_EQ(Q,2,r,X,Y)
+
+#define FP_TO_INT_Q(r,X,rsz,rsg)  _FP_TO_INT(Q,2,r,X,rsz,rsg)
+#define FP_FROM_INT_Q(X,r,rs,rt)  _FP_FROM_INT(Q,2,X,r,rs,rt)
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/sfp-machine.h linux/arch/sparc64/math-emu/sfp-machine.h
--- v2.1.78/linux/arch/sparc64/math-emu/sfp-machine.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/sfp-machine.h	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,225 @@
+/* Machine-dependent software floating-point definitions.  Sparc64 version.
+   Copyright (C) 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#define _FP_W_TYPE_SIZE		64
+#define _FP_W_TYPE		unsigned long
+#define _FP_WS_TYPE		signed long
+#define _FP_I_TYPE		long
+
+#define _FP_MUL_MEAT_S(R,X,Y)	_FP_MUL_MEAT_1_imm(S,R,X,Y)
+#define _FP_MUL_MEAT_D(R,X,Y)	_FP_MUL_MEAT_1_wide(D,R,X,Y,umul_ppmm)
+#define _FP_MUL_MEAT_Q(R,X,Y)	_FP_MUL_MEAT_2_wide(Q,R,X,Y,umul_ppmm)
+
+#define _FP_DIV_MEAT_S(R,X,Y)	_FP_DIV_MEAT_1_imm(S,R,X,Y,_FP_DIV_HELP_imm)
+#define _FP_DIV_MEAT_D(R,X,Y)	_FP_DIV_MEAT_1_udiv(D,R,X,Y)
+#define _FP_DIV_MEAT_Q(R,X,Y)	_FP_DIV_MEAT_2_udiv_64(Q,R,X,Y)
+
+#define _FP_NANFRAC_S		_FP_QNANBIT_S
+#define _FP_NANFRAC_D		_FP_QNANBIT_D
+#define _FP_NANFRAC_Q		_FP_QNANBIT_Q, 0
+
+#define _FP_KEEPNANFRACP 1
+#define _FP_CHOOSENAN(fs, wc, R, X, Y)				\
+  do {								\
+    R##_s = Y##_s;						\
+    _FP_FRAC_COPY_##wc(R,Y);					\
+    R##_c = FP_CLS_NAN;						\
+  } while (0)
+
+#define __FP_UNPACK_RAW_1(fs, X, val)				\
+  do {								\
+    union _FP_UNION_##fs *_flo =				\
+    	(union _FP_UNION_##fs *)val;				\
+								\
+    X##_f = _flo->bits.frac;					\
+    X##_e = _flo->bits.exp;					\
+    X##_s = _flo->bits.sign;					\
+  } while (0)
+
+#define __FP_PACK_RAW_1(fs, val, X)				\
+  do {								\
+    union _FP_UNION_##fs *_flo =				\
+    	(union _FP_UNION_##fs *)val;				\
+								\
+    _flo->bits.frac = X##_f;					\
+    _flo->bits.exp  = X##_e;					\
+    _flo->bits.sign = X##_s;					\
+  } while (0)
+  
+#define __FP_UNPACK_RAW_2(fs, X, val)			\
+  do {							\
+    union _FP_UNION_##fs *_flo =			\
+    	(union _FP_UNION_##fs *)val;			\
+							\
+    X##_f0 = _flo->bits.frac0;				\
+    X##_f1 = _flo->bits.frac1;				\
+    X##_e  = _flo->bits.exp;				\
+    X##_s  = _flo->bits.sign;				\
+  } while (0)
+
+#define __FP_PACK_RAW_2(fs, val, X)			\
+  do {							\
+    union _FP_UNION_##fs *_flo =			\
+    	(union _FP_UNION_##fs *)val;			\
+							\
+    _flo->bits.frac0 = X##_f0;				\
+    _flo->bits.frac1 = X##_f1;				\
+    _flo->bits.exp   = X##_e;				\
+    _flo->bits.sign  = X##_s;				\
+  } while (0)
+
+#define __FP_UNPACK_S(X,val)		\
+  do {					\
+    __FP_UNPACK_RAW_1(S,X,val);		\
+    _FP_UNPACK_CANONICAL(S,1,X);	\
+  } while (0)
+
+#define __FP_PACK_S(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(S,1,X);		\
+    __FP_PACK_RAW_1(S,val,X);		\
+  } while (0)
+
+#define __FP_UNPACK_D(X,val)		\
+  do {					\
+    __FP_UNPACK_RAW_1(D,X,val);		\
+    _FP_UNPACK_CANONICAL(D,1,X);	\
+  } while (0)
+
+#define __FP_PACK_D(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(D,1,X);		\
+    __FP_PACK_RAW_1(D,val,X);		\
+  } while (0)
+
+#define __FP_UNPACK_Q(X,val)		\
+  do {					\
+    __FP_UNPACK_RAW_2(Q,X,val);		\
+    _FP_UNPACK_CANONICAL(Q,2,X);	\
+  } while (0)
+
+#define __FP_PACK_Q(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(Q,2,X);		\
+    __FP_PACK_RAW_2(Q,val,X);		\
+  } while (0)
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) 						\
+  __asm__ ("addcc %4,%5,%1
+  	    add %2,%3,%0
+  	    bcs,a,pn %%xcc, 1f
+  	    add %0, 1, %0
+  	    1:"										\
+	   : "=r" ((UDItype)(sh)),				      			\
+	     "=&r" ((UDItype)(sl))				      			\
+	   : "r" ((UDItype)(ah)),				     			\
+	     "r" ((UDItype)(bh)),				      			\
+	     "r" ((UDItype)(al)),				     			\
+	     "r" ((UDItype)(bl))				       			\
+	   : "cc")
+	   
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) 						\
+  __asm__ ("subcc %4,%5,%1
+  	    sub %2,%3,%0
+  	    bcs,a,pn %%xcc, 1f
+  	    sub %0, 1, %0
+  	    1:"										\
+	   : "=r" ((UDItype)(sh)),				      			\
+	     "=&r" ((UDItype)(sl))				      			\
+	   : "r" ((UDItype)(ah)),				     			\
+	     "r" ((UDItype)(bh)),				      			\
+	     "r" ((UDItype)(al)),				     			\
+	     "r" ((UDItype)(bl))				       			\
+	   : "cc")
+	   
+#define umul_ppmm(wh, wl, u, v) 							\
+  do {											\
+  	long tmp1 = 0, tmp2 = 0, tmp3 = 0;						\
+	  __asm__ ("mulx %2,%3,%1
+  		    srlx %2,32,%4
+	  	    srl %3,0,%5
+  		    mulx %4,%5,%6
+  		    srlx %3,32,%4
+  		    srl %2,0,%5
+  		    mulx %4,%5,%5
+  		    srlx %2,32,%4
+  		    add %5,%6,%6
+  		    srlx %3,32,%5
+  		    mulx %4,%5,%4
+  		    srlx %6,32,%5
+  		    add %4,%5,%0"							\
+	   : "=r" ((UDItype)(wh)),				      			\
+	     "=&r" ((UDItype)(wl))				      			\
+	   : "r" ((UDItype)(u)),				     			\
+	     "r" ((UDItype)(v)),				      			\
+	     "r" ((UDItype)(tmp1)),				      			\
+	     "r" ((UDItype)(tmp2)),				      			\
+	     "r" ((UDItype)(tmp3))				      			\
+	   : "cc");									\
+  } while (0)
+  
+#define udiv_qrnnd(q, r, n1, n0, d) 							\
+  do {                                                                  		\
+    UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m;                     		\
+    __d1 = (d >> 32);                                           			\
+    __d0 = (USItype)d;                                            			\
+                                                                        		\
+    __r1 = (n1) % __d1;                                                 		\
+    __q1 = (n1) / __d1;                                                 		\
+    __m = (UWtype) __q1 * __d0;                                         		\
+    __r1 = (__r1 << 32) | (n0 >> 32);                          				\
+    if (__r1 < __m)                                                     		\
+      {                                                                 		\
+        __q1--, __r1 += (d);                                            		\
+        if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */		\
+          if (__r1 < __m)                                               		\
+            __q1--, __r1 += (d);                                        		\
+      }                                                                 		\
+    __r1 -= __m;                                                        		\
+                                                                        		\
+    __r0 = __r1 % __d1;                                                 		\
+    __q0 = __r1 / __d1;                                                 		\
+    __m = (UWtype) __q0 * __d0;                                         		\
+    __r0 = (__r0 << 32) | ((USItype)n0);                           			\
+    if (__r0 < __m)                                                     		\
+      {                                                                 		\
+        __q0--, __r0 += (d);                                            		\
+        if (__r0 >= (d))                                                		\
+          if (__r0 < __m)                                               		\
+            __q0--, __r0 += (d);                                        		\
+      }                                                                 		\
+    __r0 -= __m;                                                        		\
+                                                                        		\
+    (q) = (UWtype) (__q1 << 32)  | __q0;                                		\
+    (r) = __r0;                                                         		\
+  } while (0)
+
+#define UDIV_NEEDS_NORMALIZATION 1  
+
+#define abort()										\
+	return 0
+
+#ifdef __BIG_ENDIAN
+#define __BYTE_ORDER __BIG_ENDIAN
+#else
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/single.h linux/arch/sparc64/math-emu/single.h
--- v2.1.78/linux/arch/sparc64/math-emu/single.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/single.h	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,66 @@
+/*
+ * Definitions for IEEE Single Precision
+ */
+
+#if _FP_W_TYPE_SIZE < 32
+#error "Here's a nickle kid.  Go buy yourself a real computer."
+#endif
+
+#define _FP_FRACBITS_S		24
+#define _FP_FRACXBITS_S		(_FP_W_TYPE_SIZE - _FP_FRACBITS_S)
+#define _FP_WFRACBITS_S		(_FP_WORKBITS + _FP_FRACBITS_S)
+#define _FP_WFRACXBITS_S	(_FP_W_TYPE_SIZE - _FP_WFRACBITS_S)
+#define _FP_EXPBITS_S		8
+#define _FP_EXPBIAS_S		127
+#define _FP_EXPMAX_S		255
+#define _FP_QNANBIT_S		((_FP_W_TYPE)1 << (_FP_FRACBITS_S-2))
+#define _FP_IMPLBIT_S		((_FP_W_TYPE)1 << (_FP_FRACBITS_S-1))
+#define _FP_OVERFLOW_S		((_FP_W_TYPE)1 << (_FP_WFRACBITS_S))
+
+/* The implementation of _FP_MUL_MEAT_S and _FP_DIV_MEAT_S should be
+   chosen by the target machine.  */
+
+union _FP_UNION_S
+{
+  float flt;
+  struct {
+#if __BYTE_ORDER == __BIG_ENDIAN
+    unsigned sign : 1;
+    unsigned exp  : _FP_EXPBITS_S;
+    unsigned frac : _FP_FRACBITS_S - (_FP_IMPLBIT_S != 0);
+#else
+    unsigned frac : _FP_FRACBITS_S - (_FP_IMPLBIT_S != 0);
+    unsigned exp  : _FP_EXPBITS_S;
+    unsigned sign : 1;
+#endif
+  } bits __attribute__((packed));
+};
+
+#define FP_DECL_S(X)		_FP_DECL(1,X)
+#define FP_UNPACK_RAW_S(X,val)	_FP_UNPACK_RAW_1(S,X,val)
+#define FP_PACK_RAW_S(val,X)	_FP_PACK_RAW_1(S,val,X)
+
+#define FP_UNPACK_S(X,val)		\
+  do {					\
+    _FP_UNPACK_RAW_1(S,X,val);		\
+    _FP_UNPACK_CANONICAL(S,1,X);	\
+  } while (0)
+
+#define FP_PACK_S(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(S,1,X);		\
+    _FP_PACK_RAW_1(S,val,X);		\
+  } while (0)
+
+#define FP_NEG_S(R,X)		_FP_NEG(S,1,R,X)
+#define FP_ADD_S(R,X,Y)		_FP_ADD(S,1,R,X,Y)
+#define FP_SUB_S(R,X,Y)		_FP_SUB(S,1,R,X,Y)
+#define FP_MUL_S(R,X,Y)		_FP_MUL(S,1,R,X,Y)
+#define FP_DIV_S(R,X,Y)		_FP_DIV(S,1,R,X,Y)
+#define FP_SQRT_S(R,X)		_FP_SQRT(S,1,R,X)
+
+#define FP_CMP_S(r,X,Y,un)	_FP_CMP(S,1,r,X,Y,un)
+#define FP_CMP_EQ_S(r,X,Y)	_FP_CMP_EQ(S,1,r,X,Y)
+
+#define FP_TO_INT_S(r,X,rsz,rsg)  _FP_TO_INT(S,1,r,X,rsz,rsg)
+#define FP_FROM_INT_S(X,r,rs,rt)  _FP_FROM_INT(S,1,X,r,rs,rt)
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/soft-fp.h linux/arch/sparc64/math-emu/soft-fp.h
--- v2.1.78/linux/arch/sparc64/math-emu/soft-fp.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/soft-fp.h	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,83 @@
+#ifndef SOFT_FP_H
+#define SOFT_FP_H
+
+#include "sfp-machine.h"
+
+#define _FP_WORKBITS		3
+#define _FP_WORK_LSB		((_FP_W_TYPE)1 << 3)
+#define _FP_WORK_ROUND		((_FP_W_TYPE)1 << 2)
+#define _FP_WORK_GUARD		((_FP_W_TYPE)1 << 1)
+#define _FP_WORK_STICKY		((_FP_W_TYPE)1 << 0)
+
+#ifndef FP_RND_NEAREST
+# define FP_RND_NEAREST		0
+# define FP_RND_ZERO		1
+# define FP_RND_PINF		2
+# define FP_RND_MINF		3
+# define FP_ROUNDMODE		FP_RND_NEAREST
+#endif
+
+#define _FP_ROUND_NEAREST(wc, X)			\
+  do { 							\
+    if ((_FP_FRAC_LOW_##wc(X) & 15) != _FP_WORK_ROUND)	\
+      _FP_FRAC_ADDI_##wc(X, _FP_WORK_ROUND);		\
+  } while(0)
+
+#define _FP_ROUND_ZERO(wc, X)
+
+#define _FP_ROUND_PINF(wc, X)				\
+  do {							\
+    if (!X##_s && (_FP_FRAC_LOW_##wc(X) & 7))		\
+      _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB);		\
+  } while (0)
+
+#define _FP_ROUND_MINF(wc, X)				\
+  do {							\
+    if (X##_s && (_FP_FRAC_LOW_##wc(X) & 7))		\
+      _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB);		\
+  } while (0)
+
+#define _FP_ROUND(wc, X)			\
+	switch (FP_ROUNDMODE)			\
+	{					\
+	  case FP_RND_NEAREST:			\
+	    _FP_ROUND_NEAREST(wc,X);		\
+	    break;				\
+	  case FP_RND_ZERO:			\
+	    _FP_ROUND_ZERO(wc,X);		\
+	    break;				\
+	  case FP_RND_PINF:			\
+	    _FP_ROUND_PINF(wc,X);		\
+	    break;				\
+	  case FP_RND_MINF:			\
+	    _FP_ROUND_MINF(wc,X);		\
+	    break;				\
+	}
+
+#define FP_CLS_NORMAL		0
+#define FP_CLS_ZERO		1
+#define FP_CLS_INF		2
+#define FP_CLS_NAN		3
+
+#define _FP_CLS_COMBINE(x,y)	(((x) << 2) | (y))
+
+#include "op-1.h"
+#include "op-2.h"
+#include "op-4.h"
+#include "op-common.h"
+
+/* Sigh.  Silly things longlong.h needs.  */
+#define UWtype		_FP_W_TYPE
+#define W_TYPE_SIZE	_FP_W_TYPE_SIZE
+
+typedef int SItype __attribute__((mode(SI)));
+typedef int DItype __attribute__((mode(DI)));
+typedef unsigned int USItype __attribute__((mode(SI)));
+typedef unsigned int UDItype __attribute__((mode(DI)));
+#if _FP_W_TYPE_SIZE == 32
+typedef unsigned int UHWtype __attribute__((mode(HI)));
+#elif _FP_W_TYPE_SIZE == 64
+typedef USItype UHWtype;
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/math-emu/udivmodti4.c linux/arch/sparc64/math-emu/udivmodti4.c
--- v2.1.78/linux/arch/sparc64/math-emu/udivmodti4.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/udivmodti4.c	Mon Jan 12 15:15:44 1998
@@ -0,0 +1,191 @@
+/* This has so very few changes over libgcc2's __udivmoddi4 it isn't funny.  */
+
+#include "soft-fp.h"
+
+#undef count_leading_zeros
+#define count_leading_zeros  __FP_CLZ
+
+void
+_fp_udivmodti4(_FP_W_TYPE q[2], _FP_W_TYPE r[2],
+	       _FP_W_TYPE n1, _FP_W_TYPE n0,
+	       _FP_W_TYPE d1, _FP_W_TYPE d0)
+{
+  _FP_W_TYPE q0, q1, r0, r1;
+  _FP_I_TYPE b, bm;
+
+  if (d1 == 0)
+    {
+#if !UDIV_NEEDS_NORMALIZATION
+      if (d0 > n1)
+	{
+	  /* 0q = nn / 0D */
+
+	  udiv_qrnnd (q0, n0, n1, n0, d0);
+	  q1 = 0;
+
+	  /* Remainder in n0.  */
+	}
+      else
+	{
+	  /* qq = NN / 0d */
+
+	  if (d0 == 0)
+	    d0 = 1 / d0;	/* Divide intentionally by zero.  */
+
+	  udiv_qrnnd (q1, n1, 0, n1, d0);
+	  udiv_qrnnd (q0, n0, n1, n0, d0);
+
+	  /* Remainder in n0.  */
+	}
+
+      r0 = n0;
+      r1 = 0;
+
+#else /* UDIV_NEEDS_NORMALIZATION */
+
+      if (d0 > n1)
+	{
+	  /* 0q = nn / 0D */
+
+	  count_leading_zeros (bm, d0);
+
+	  if (bm != 0)
+	    {
+	      /* Normalize, i.e. make the most significant bit of the
+		 denominator set.  */
+
+	      d0 = d0 << bm;
+	      n1 = (n1 << bm) | (n0 >> (_FP_W_TYPE_SIZE - bm));
+	      n0 = n0 << bm;
+	    }
+
+	  udiv_qrnnd (q0, n0, n1, n0, d0);
+	  q1 = 0;
+
+	  /* Remainder in n0 >> bm.  */
+	}
+      else
+	{
+	  /* qq = NN / 0d */
+
+	  if (d0 == 0)
+	    d0 = 1 / d0;	/* Divide intentionally by zero.  */
+
+	  count_leading_zeros (bm, d0);
+
+	  if (bm == 0)
+	    {
+	      /* From (n1 >= d0) /\ (the most significant bit of d0 is set),
+		 conclude (the most significant bit of n1 is set) /\ (the
+		 leading quotient digit q1 = 1).
+
+		 This special case is necessary, not an optimization.
+		 (Shifts counts of SI_TYPE_SIZE are undefined.)  */
+
+	      n1 -= d0;
+	      q1 = 1;
+	    }
+	  else
+	    {
+	      _FP_W_TYPE n2;
+
+	      /* Normalize.  */
+
+	      b = _FP_W_TYPE_SIZE - bm;
+
+	      d0 = d0 << bm;
+	      n2 = n1 >> b;
+	      n1 = (n1 << bm) | (n0 >> b);
+	      n0 = n0 << bm;
+
+	      udiv_qrnnd (q1, n1, n2, n1, d0);
+	    }
+
+	  /* n1 != d0...  */
+
+	  udiv_qrnnd (q0, n0, n1, n0, d0);
+
+	  /* Remainder in n0 >> bm.  */
+	}
+
+      r0 = n0 >> bm;
+      r1 = 0;
+#endif /* UDIV_NEEDS_NORMALIZATION */
+    }
+  else
+    {
+      if (d1 > n1)
+	{
+	  /* 00 = nn / DD */
+
+	  q0 = 0;
+	  q1 = 0;
+
+	  /* Remainder in n1n0.  */
+	  r0 = n0;
+	  r1 = n1;
+	}
+      else
+	{
+	  /* 0q = NN / dd */
+
+	  count_leading_zeros (bm, d1);
+	  if (bm == 0)
+	    {
+	      /* From (n1 >= d1) /\ (the most significant bit of d1 is set),
+		 conclude (the most significant bit of n1 is set) /\ (the
+		 quotient digit q0 = 0 or 1).
+
+		 This special case is necessary, not an optimization.  */
+
+	      /* The condition on the next line takes advantage of that
+		 n1 >= d1 (true due to program flow).  */
+	      if (n1 > d1 || n0 >= d0)
+		{
+		  q0 = 1;
+		  sub_ddmmss (n1, n0, n1, n0, d1, d0);
+		}
+	      else
+		q0 = 0;
+
+	      q1 = 0;
+
+	      r0 = n0;
+	      r1 = n1;
+	    }
+	  else
+	    {
+	      _FP_W_TYPE m1, m0, n2;
+
+	      /* Normalize.  */
+
+	      b = _FP_W_TYPE_SIZE - bm;
+
+	      d1 = (d1 << bm) | (d0 >> b);
+	      d0 = d0 << bm;
+	      n2 = n1 >> b;
+	      n1 = (n1 << bm) | (n0 >> b);
+	      n0 = n0 << bm;
+
+	      udiv_qrnnd (q0, n1, n2, n1, d1);
+	      umul_ppmm (m1, m0, q0, d0);
+
+	      if (m1 > n1 || (m1 == n1 && m0 > n0))
+		{
+		  q0--;
+		  sub_ddmmss (m1, m0, m1, m0, d1, d0);
+		}
+
+	      q1 = 0;
+
+	      /* Remainder in (n1n0 - m1m0) >> bm.  */
+	      sub_ddmmss (n1, n0, n1, n0, m1, m0);
+	      r0 = (n1 << b) | (n0 >> bm);
+	      r1 = n1 >> bm;
+	    }
+	}
+    }
+
+  q[0] = q0; q[1] = q1;
+  r[0] = r0, r[1] = r1;
+}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/mm/asyncd.c linux/arch/sparc64/mm/asyncd.c
--- v2.1.78/linux/arch/sparc64/mm/asyncd.c	Thu May 15 16:48:02 1997
+++ linux/arch/sparc64/mm/asyncd.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/*  $Id: asyncd.c,v 1.2 1997/05/15 21:14:32 davem Exp $
+/*  $Id: asyncd.c,v 1.3 1997/12/11 15:15:58 jj Exp $
  *  The asyncd kernel daemon. This handles paging on behalf of 
  *  processes that receive page faults due to remote (async) memory
  *  accesses. 
@@ -239,7 +239,9 @@
 	current->session = 1;
 	current->pgrp = 1;
 	sprintf(current->comm, "asyncd");
-	current->blocked = ~0UL; /* block all signals */
+	
+	sigfillset(&current->blocked); /* block all signals */
+	recalc_sigpending(current);
   
 	/* Give asyncd a realtime priority. */
 	current->policy = SCHED_FIFO;
@@ -259,7 +261,9 @@
 		save_flags(flags); cli();
 
 		while (!async_queue) {
-			current->signal = 0;
+			spin_lock_irq(&current->sigmask_lock);
+			flush_signals(current);
+			spin_unlock_irq(&current->sigmask_lock);
 			interruptible_sleep_on(&asyncd_wait);
 		}
 
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/mm/init.c linux/arch/sparc64/mm/init.c
--- v2.1.78/linux/arch/sparc64/mm/init.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/mm/init.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/*  $Id: init.c,v 1.55 1997/08/24 01:22:29 davem Exp $
+/*  $Id: init.c,v 1.60 1998/01/10 18:19:51 ecd Exp $
  *  arch/sparc64/mm/init.c
  *
  *  Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -116,9 +116,10 @@
 /* IOMMU support, the ideas are right, the code should be cleaned a bit still... */
 
 /* This keeps track of pages used in sparc_alloc_dvma() invocations. */
-static unsigned long dvma_map_pages[0x10000000 >> 16] = { 0, };
-static unsigned long dvma_pages_current_offset = 0;
-static int dvma_pages_current_index = 0;
+/* NOTE: All of these are inited to 0 in bss, don't need to make data segment bigger */
+static unsigned long dvma_map_pages[0x10000000 >> 16];
+static unsigned long dvma_pages_current_offset;
+static int dvma_pages_current_index;
 
 /* #define E3000_DEBUG */
 
@@ -664,8 +665,9 @@
 {
 	pte_t *pte;
 
-	pte = (pte_t *) get_free_page(GFP_KERNEL);
+	pte = (pte_t *) __get_free_page(GFP_KERNEL);
 	if(pte) {
+		clear_page((unsigned long)pte);
 		pmd_set(pmd, pte);
 		return pte + offset;
 	}
@@ -834,7 +836,7 @@
 	allocate_ptable_skeleton(IOBASE_VADDR, IOBASE_VADDR + 0x4000000);
 	allocate_ptable_skeleton(DVMA_VADDR, DVMA_VADDR + 0x4000000);
 	inherit_prom_mappings();
-	
+
 	/* Ok, we can use our TLB miss and window trap handlers safely. */
 	setup_tba((unsigned long)init_mm.pgd);
 
@@ -854,9 +856,9 @@
 	membar("#Sync");
 
 	inherit_locked_prom_mappings(1);
-	
+
 	flush_tlb_all();
-	
+
 	start_mem = free_area_init(PAGE_ALIGN(mempool), end_mem);
 
 	return device_scan (PAGE_ALIGN (start_mem));
@@ -881,6 +883,8 @@
 			if((phys_addr >= base) && (phys_addr < limit) &&
 			   ((phys_addr + PAGE_SIZE) < limit))
 				mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
+			if (phys_addr >= 0xf0000000)
+				mem_map[MAP_NR(addr)].flags &= ~(1<<PG_DMA);
 		}
 	}
 }
@@ -903,9 +907,10 @@
 	addr = PAGE_OFFSET;
 	while(addr < start_mem) {
 #ifdef CONFIG_BLK_DEV_INITRD
-		if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end)
+		if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end) {
 			mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
-		else
+			num_physpages--;
+		} else
 #endif	
 			mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
 		addr += PAGE_SIZE;
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/mm/ultra.S linux/arch/sparc64/mm/ultra.S
--- v2.1.78/linux/arch/sparc64/mm/ultra.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/mm/ultra.S	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: ultra.S,v 1.18 1997/08/08 08:34:23 jj Exp $
+/* $Id: ultra.S,v 1.20 1997/10/03 20:42:46 davem Exp $
  * ultra.S: Don't expand these all over the place...
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -46,26 +46,29 @@
 	cmp		%o4, 96
 	bgu,pn		%icc, 9b
 	 mov		SECONDARY_CONTEXT, %g7
-	rdpr		%pstate, %g1
-	wrpr		%g1, PSTATE_IE, %pstate
 	ldxa		[%g7] ASI_DMMU, %g2
-	cmp		%g2, %o0
 
+	nop
+	nop
+	cmp		%g2, %o0
 	be,pt		%icc, 1f
 	 sub		%o3, %g5, %o3
+	rdpr		%pstate, %g1
+	wrpr		%g1, PSTATE_IE, %pstate
 	stxa		%o0, [%g7] ASI_DMMU
+
 1:	stxa		%g0, [%o1 + %o3] ASI_DMMU_DEMAP
 	stxa		%g0, [%o1 + %o3] ASI_IMMU_DEMAP
 	brnz,pt		%o3, 1b
 	 sub		%o3, %g5, %o3
 	flush		%g6
-
-	be,a,pt		%icc, 1f
+	be,pt		%icc, 1f
 	 nop
 	stxa		%g2, [%g7] ASI_DMMU
-1:	flush		%g6
+
+	flush		%g6
 	wrpr		%g1, 0, %pstate
-	retl
+1:	retl
 	 nop
 
 	.align		32
@@ -73,7 +76,7 @@
 	mov		SECONDARY_CONTEXT, %g7
 	ldxa		[%g7] ASI_DMMU, %g2
 	cmp		%g2, %o0
-	be,pt		%icc, 1f
+	bne,pt		%icc, 1f
 	 or		%o1, 0x10, %g3
 	stxa		%g0, [%g3] ASI_DMMU_DEMAP
 	stxa		%g0, [%g3] ASI_IMMU_DEMAP
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/prom/console.c linux/arch/sparc64/prom/console.c
--- v2.1.78/linux/arch/sparc64/prom/console.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/prom/console.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: console.c,v 1.8 1997/08/16 08:00:16 davem Exp $
+/* $Id: console.c,v 1.9 1997/10/29 07:41:43 ecd Exp $
  * console.c: Routines that deal with sending and receiving IO
  *            to/from the current console device using the PROM.
  *
@@ -91,6 +91,14 @@
 	memset(propb, 0, sizeof(propb));
 	st_p = prom_finddevice ("/options");
 	prom_getproperty(st_p, "input-device", propb, sizeof(propb));
+
+	/*
+	 * If we get here with propb == 'keyboard', we are on ttya, as
+	 * the PROM defaulted to this due to 'no input device'.
+	 */
+	if (!strncmp(propb, "keyboard", 8))
+		return PROMDEV_ITTYA;
+
 	if (strncmp (propb, "tty", 3) || !propb[3])
 		return PROMDEV_I_UNK;
 	switch (propb[3]) {
@@ -120,6 +128,14 @@
 	memset(propb, 0, sizeof(propb));
 	st_p = prom_finddevice ("/options");
 	prom_getproperty(st_p, "output-device", propb, sizeof(propb));
+
+	/*
+	 * If we get here with propb == 'screen', we are on ttya, as
+	 * the PROM defaulted to this due to 'no input device'.
+	 */
+	if (!strncmp(propb, "screen", 6))
+		return PROMDEV_OTTYA;
+
 	if (strncmp (propb, "tty", 3) || !propb[3])
 		return PROMDEV_O_UNK;
 	switch (propb[3]) {
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/prom/devops.c linux/arch/sparc64/prom/devops.c
--- v2.1.78/linux/arch/sparc64/prom/devops.c	Mon Mar 17 14:54:23 1997
+++ linux/arch/sparc64/prom/devops.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: devops.c,v 1.2 1997/02/25 12:40:20 jj Exp $
+/* $Id: devops.c,v 1.3 1997/10/29 07:43:28 ecd Exp $
  * devops.c:  Device operations using the PROM.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -13,7 +13,7 @@
 
 /* Open the device described by the string 'dstr'.  Returns the handle
  * to that device used for subsequent operations on that device.
- * Returns -1 on failure.
+ * Returns 0 on failure.
  */
 int
 prom_devopen(char *dstr)
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/prom/tree.c linux/arch/sparc64/prom/tree.c
--- v2.1.78/linux/arch/sparc64/prom/tree.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/prom/tree.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: tree.c,v 1.6 1997/08/12 16:32:48 davem Exp $
+/* $Id: tree.c,v 1.10 1998/01/10 22:39:00 ecd Exp $
  * tree.c: Basic device tree traversal/scanning for the Linux
  *         prom library.
  *
@@ -195,6 +195,7 @@
 prom_getname (int node, char *buffer, int len)
 {
 	int i, sbus = 0;
+	int pci = 0, ebus = 0, ide = 0;
 	struct linux_prom_registers *reg;
 	struct linux_prom64_registers reg64[PROMREG_MAX];
 	
@@ -203,9 +204,37 @@
 		if (i > 0) {
 			buffer [i] = 0;
 			if (!strcmp (buffer, "sbus"))
-				break;
+				goto getit;
 		}
 	}
+	if ((pci = prom_getparent (node))) {
+		i = prom_getproperty (pci, "name", buffer, len);
+		if (i > 0) {
+			buffer [i] = 0;
+			if (!strcmp (buffer, "pci"))
+				goto getit;
+		}
+		pci = 0;
+	}
+	if ((ebus = prom_getparent (node))) {
+		i = prom_getproperty (ebus, "name", buffer, len);
+		if (i > 0) {
+			buffer[i] = 0;
+			if (!strcmp (buffer, "ebus"))
+				goto getit;
+		}
+		ebus = 0;
+	}
+	if ((ide = prom_getparent (node))) {
+		i = prom_getproperty (ide, "name", buffer, len);
+		if (i > 0) {
+			buffer [i] = 0;
+			if (!strcmp (buffer, "ide"))
+				goto getit;
+		}
+		ide = 0;
+	}
+getit:
 	i = prom_getproperty (node, "name", buffer, len);
 	if (i <= 0) {
 		buffer [0] = 0;
@@ -220,8 +249,28 @@
 	if (sbus) {
 		reg = (struct linux_prom_registers *)reg64;
 		sprintf (buffer, "@%x,%x", reg[0].which_io, (uint)reg[0].phys_addr);
+	} else if (pci) {
+		int dev, fn;
+		reg = (struct linux_prom_registers *)reg64;
+		fn = (reg[0].which_io >> 8) & 0x07;
+		dev = (reg[0].which_io >> 11) & 0x1f;
+		if (fn)
+			sprintf (buffer, "@%x,%x", dev, fn);
+		else
+			sprintf (buffer, "@%x", dev);
+	} else if (ebus) {
+		reg = (struct linux_prom_registers *)reg64;
+		sprintf (buffer, "@%x,%x", reg[0].which_io, reg[0].phys_addr);
+	} else if (ide) {
+		reg = (struct linux_prom_registers *)reg64;
+		sprintf (buffer, "@%x,%x", reg[0].which_io, reg[0].phys_addr);
+	} else if (i == 4) {	/* Happens on 8042's children on Ultra/PCI. */
+		reg = (struct linux_prom_registers *)reg64;
+		sprintf (buffer, "@%x", reg[0].which_io);
 	} else {
-		sprintf (buffer, "@%x,%x", (unsigned int)(reg64[0].phys_addr >> 36), (unsigned int)(reg64[0].phys_addr));
+		sprintf (buffer, "@%x,%x",
+			 (unsigned int)(reg64[0].phys_addr >> 36),
+			 (unsigned int)(reg64[0].phys_addr));
 	}
 	return 0;
 }
@@ -318,9 +367,9 @@
 prom_pathtoinode(char *path)
 {
 	int node, inst;
-	
+
 	inst = prom_devopen (path);
-	if (inst == -1) return 0;
+	if (inst == 0) return 0;
 	node = prom_inst2pkg (inst);
 	prom_devclose (inst);
 	if (node == -1) return 0;
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/solaris/Makefile linux/arch/sparc64/solaris/Makefile
--- v2.1.78/linux/arch/sparc64/solaris/Makefile	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/solaris/Makefile	Mon Jan 12 15:15:44 1998
@@ -11,13 +11,14 @@
 O_OBJS   := entry64.o fs.o misc.o signal.o systbl.o ioctl.o ipc.o socksys.o
 ifeq ($(CONFIG_SOLARIS_EMUL),m)
 M_OBJS   := $(O_TARGET)
+CPPFLAGS = $(MODFLAGS)
 endif
 
 .S.s:
-	$(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s
+	$(CPP) -D__ASSEMBLY__ $(CPPFLAGS) -ansi $< -o $*.s
 
 .S.o:
-	$(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
+	$(CC) -D__ASSEMBLY__ $(CPPFLAGS) -ansi -c $< -o $*.o
 
 ifneq ($(CONFIG_SOLARIS_EMUL),y)
 do_it_all:
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/solaris/entry64.S linux/arch/sparc64/solaris/entry64.S
--- v2.1.78/linux/arch/sparc64/solaris/entry64.S	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/solaris/entry64.S	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: entry64.S,v 1.3 1997/09/03 12:29:12 jj Exp $
+/* $Id: entry64.S,v 1.4 1997/09/09 17:13:50 jj Exp $
  * entry64.S:  Solaris syscall emulation entry point.
  *
  * Copyright (C) 1996,1997 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
@@ -25,12 +25,12 @@
 solaris_syscall_trace:
 	call		syscall_trace
 	 nop
-	mov		%i0, %o0
-	mov		%i1, %o1
-	mov		%i2, %o2
-	mov		%i3, %o3
+	srl		%i0, 0, %o0
+	mov		%i4, %o4
+	srl		%i1, 0, %o1
+	mov		%i5, %o5
 	b,pt		%xcc, 2f
-	 mov		%i4, %o4
+	 srl		%i2, 0, %o2
 
 solaris_sucks:
 /* Solaris is a big system which needs to be able to do all the things
@@ -41,6 +41,7 @@
 	mov		%i2, %i1
 	srl		%o0, 0, %o0
 	mov		%i3, %i2
+	movrz		%g1, 256, %g1 /* Ensure we don't loop forever */
 	mov		%i4, %i3
 	mov		%i5, %i4
 	ba,pt		%xcc, solaris_sparc_syscall
@@ -55,7 +56,7 @@
 	call		solaris_register
 	 nop
 	ba,pt		%xcc, 1f
-	 mov		%i0, %o0
+	 mov		%i4, %o4
 
 linux_syscall_for_solaris:
 	sll		%l7, 2, %l4
@@ -66,46 +67,35 @@
 	.align	32
 	.globl	solaris_sparc_syscall
 solaris_sparc_syscall:
-#ifdef DEBUG_SOLARIS
-	mov		%g1, %o0
-	call		entry_printk
-	 add		%sp, STACK_BIAS + REGWIN_SZ, %o1
-#endif
 	ldub		[%g6 + AOFF_task_personality + ASIZ_task_personality - 1], %l0
 	cmp		%g1, 255
 	bg,pn		%icc, solaris_unimplemented
 	 srl		%g1, 0, %g1
 	sethi		%hi(solaris_sys_table), %l7
 	brz,pn		%g1, solaris_sucks
-	 mov		%i0, %o0
+	 mov		%i4, %o4
 	sll		%g1, 2, %l4
 	cmp		%l0, 1
 	bne,pn		%icc, solaris_reg
-1:	 mov		%i1, %o1				
+1:	 srl		%i0, 0, %o0
 	lduw		[%l7 + %l4], %l7
-	mov		%i2, %o2				
+	srl		%i1, 0, %o1
 	ldx		[%g6 + AOFF_task_flags], %l5
-!	st		%g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS]
 	cmp		%l7, NR_SYSCALLS
 	bleu,a,pn	%xcc, linux_syscall_for_solaris
 	 sethi		%hi(sys_call_table32), %l6
 	andcc		%l7, 1, %g0
 	bne,a,pn	%icc, 10f
 	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
-10:	mov		%i3, %o3
-	mov		%i4, %o4
+10:	srl		%i2, 0, %o2
+	mov		%i5, %o5
 	andn		%l7, 3, %l7
 	andcc		%l5, 0x20, %g0				
 	bne,pn		%icc, solaris_syscall_trace		
 	 mov		%i0, %l5
 2:	call		%l7
-	 mov		%i5, %o5
+	 srl		%i3, 0, %o3
 ret_from_solaris:
-#ifdef DEBUG_SOLARIS
-	call		exit_printk
-	 mov		%o0, %l0
-	mov		%l0, %o0
-#endif
 	stx		%o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0]
 	ldx		[%g6 + AOFF_task_flags], %l6
 	sra		%o0, 0, %o0
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/solaris/fs.c linux/arch/sparc64/solaris/fs.c
--- v2.1.78/linux/arch/sparc64/solaris/fs.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/solaris/fs.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: fs.c,v 1.4 1997/09/04 15:46:26 jj Exp $
+/* $Id: fs.c,v 1.6 1997/10/13 03:54:05 davem Exp $
  * fs.c: fs related syscall emulation for Solaris
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -10,6 +10,7 @@
 #include <linux/smp_lock.h>
 #include <linux/limits.h>
 #include <linux/resource.h>
+#include <linux/quotaops.h>
 
 #include <asm/uaccess.h>
 #include <asm/string.h>
@@ -82,7 +83,7 @@
 	int ret;
 	struct stat s;
 	char *filenam;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	int (*sys_newstat)(char *,struct stat *) = 
 		(int (*)(char *,struct stat *))SYS(stat);
 	
@@ -110,7 +111,7 @@
 	int ret;
 	struct stat s;
 	char *filenam;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	int (*sys_newlstat)(char *,struct stat *) = 
 		(int (*)(char *,struct stat *))SYS(lstat);
 	
@@ -136,7 +137,7 @@
 {
 	int ret;
 	struct stat s;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	int (*sys_newfstat)(unsigned,struct stat *) = 
 		(int (*)(unsigned,struct stat *))SYS(fstat);
 	
@@ -187,7 +188,7 @@
 {
 	int ret;
 	struct statfs s;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	int (*sys_statfs)(const char *,struct statfs *) = 
 		(int (*)(const char *,struct statfs *))SYS(statfs);
 	struct sol_statfs *ss = (struct sol_statfs *)A(buf);
@@ -229,7 +230,7 @@
 {
 	int ret;
 	struct statfs s;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	int (*sys_fstatfs)(unsigned,struct statfs *) = 
 		(int (*)(unsigned,struct statfs *))SYS(fstatfs);
 	struct sol_statfs *ss = (struct sol_statfs *)A(buf);
@@ -276,7 +277,7 @@
 static int report_statvfs(struct inode *inode, u32 buf)
 {
 	struct statfs s;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	int error;
 	struct sol_statvfs *ss = (struct sol_statvfs *)A(buf);
 			
@@ -418,7 +419,7 @@
 	case SOL_F_SETLKW:
 		{
 			struct flock f;
-			unsigned long old_fs = get_fs();
+			mm_segment_t old_fs = get_fs();
 
 			switch (cmd) {
 			case SOL_F_GETLK: cmd = F_GETLK; break;
@@ -515,16 +516,7 @@
 		newattrs.ia_mode &= ~S_ISGID;
 		newattrs.ia_valid |= ATTR_MODE;
 	}
-	if (inode->i_sb && inode->i_sb->dq_op) {
-		inode->i_sb->dq_op->initialize(inode, -1);
-		error = -EDQUOT;
-		if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0))
-			goto out;
-		error = notify_change(inode, &newattrs);
-		if (error)
-			inode->i_sb->dq_op->transfer(inode, &newattrs, 1);
-	} else
-		error = notify_change(inode, &newattrs);
+	DQUOT_TRANSFER(inode, newattrs);
 out:
 	return error;
 }
@@ -690,7 +682,7 @@
 	int (*sys_llseek)(unsigned int, unsigned long, unsigned long, loff_t *, unsigned int) =
 		(int (*)(unsigned int, unsigned long, unsigned long, loff_t *, unsigned int))SYS(_llseek);
 	int ret;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	loff_t retval;
 	
 	set_fs(KERNEL_DS);
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/solaris/ioctl.c linux/arch/sparc64/solaris/ioctl.c
--- v2.1.78/linux/arch/sparc64/solaris/ioctl.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/solaris/ioctl.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: ioctl.c,v 1.2 1997/09/04 00:59:22 davem Exp $
+/* $Id: ioctl.c,v 1.4 1997/09/18 10:38:24 rth Exp $
  * ioctl.c: Solaris ioctl emulation.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -15,6 +15,7 @@
 #include <linux/smp_lock.h>
 #include <linux/ioctl.h>
 #include <linux/fs.h>
+#include <linux/netdevice.h>
 
 #include <asm/uaccess.h>
 #include <asm/termios.h>
@@ -92,7 +93,9 @@
 		case B460800: baud = 6; break;
 		case B614400: baud = 7; break;
 		case B921600: baud = 8; break;
-		/* case B1843200: baud = 9; break; */
+#if 0		
+		case B1843200: baud = 9; break;
+#endif
 		}
 		cflag |= 0x200000 | baud;
 	}
@@ -120,7 +123,7 @@
 {
 	int ret;
 	struct solaris_termio s;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	
 	if (copy_from_user (&s, (struct solaris_termio *)A(arg), sizeof(struct solaris_termio)))
 		return -EFAULT;
@@ -135,7 +138,7 @@
 {
 	int ret;
 	struct solaris_termios s;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 
 	set_fs(KERNEL_DS);	
 	ret = sys_ioctl(fd, cmd, (unsigned long)&s);
@@ -156,7 +159,7 @@
 {
 	int ret;
 	struct solaris_termios s;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 
 	set_fs(KERNEL_DS);
 	ret = sys_ioctl(fd, TCGETS, (unsigned long)&s);
@@ -232,7 +235,7 @@
 {
 	char *p;
 	int ret;
-	unsigned long old_fs;
+	mm_segment_t old_fs;
 	struct strioctl si;
 	
 	switch (cmd & 0xff) {
@@ -275,6 +278,117 @@
 	}
 	return -ENOSYS;
 }
+
+static inline int solaris_s(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	switch (cmd & 0xff) {
+	case 0: /* SIOCSHIWAT */
+	case 2: /* SIOCSLOWAT */
+		return 0; /* We don't support them */
+	case 1: /* SIOCGHIWAT */
+	case 3: /* SIOCGLOWAT */
+		put_user_ret (0, (u32 *)A(arg), -EFAULT);
+		return 0; /* Lie */
+	case 7: /* SIOCATMARK */
+		return sys_ioctl(fd, SIOCATMARK, arg);
+	case 8: /* SIOCSPGRP */
+		return sys_ioctl(fd, SIOCSPGRP, arg);
+	case 9: /* SIOCGPGRP */
+		return sys_ioctl(fd, SIOCGPGRP, arg);
+	}
+	return -ENOSYS;
+}
+
+static inline int solaris_r(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	switch (cmd & 0xff) {
+	case 10: /* SIOCADDRT */
+		return sys32_ioctl(fd, SIOCADDRT, arg);
+	case 11: /* SIOCDELRT */
+		return sys32_ioctl(fd, SIOCDELRT, arg);
+	}
+	return -ENOSYS;
+}
+
+static inline int solaris_i(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	switch (cmd & 0xff) {
+	case 12: /* SIOCSIFADDR */
+		return sys32_ioctl(fd, SIOCSIFADDR, arg);
+	case 13: /* SIOCGIFADDR */
+		return sys32_ioctl(fd, SIOCGIFADDR, arg);
+	case 14: /* SIOCSIFDSTADDR */
+		return sys32_ioctl(fd, SIOCSIFDSTADDR, arg);
+	case 15: /* SIOCGIFDSTADDR */
+		return sys32_ioctl(fd, SIOCGIFDSTADDR, arg);
+	case 16: /* SIOCSIFFLAGS */
+		return sys32_ioctl(fd, SIOCSIFFLAGS, arg);
+	case 17: /* SIOCGIFFLAGS */
+		return sys32_ioctl(fd, SIOCGIFFLAGS, arg);
+	case 18: /* SIOCSIFMEM */
+		return sys32_ioctl(fd, SIOCSIFMEM, arg);
+	case 19: /* SIOCGIFMEM */
+		return sys32_ioctl(fd, SIOCGIFMEM, arg);
+	case 20: /* SIOCGIFCONF */
+		return sys32_ioctl(fd, SIOCGIFCONF, arg);
+	case 21: /* SIOCSIFMTU */
+		return sys32_ioctl(fd, SIOCSIFMTU, arg);
+	case 22: /* SIOCGIFMTU */
+		return sys32_ioctl(fd, SIOCGIFMTU, arg);
+	case 23: /* SIOCGIFBRDADDR */
+		return sys32_ioctl(fd, SIOCGIFBRDADDR, arg);
+	case 24: /* SIOCSIFBRDADDR */
+		return sys32_ioctl(fd, SIOCSIFBRDADDR, arg);
+	case 25: /* SIOCGIFNETMASK */
+		return sys32_ioctl(fd, SIOCGIFNETMASK, arg);
+	case 26: /* SIOCSIFNETMASK */
+		return sys32_ioctl(fd, SIOCSIFNETMASK, arg);
+	case 27: /* SIOCGIFMETRIC */
+		return sys32_ioctl(fd, SIOCGIFMETRIC, arg);
+	case 28: /* SIOCSIFMETRIC */
+		return sys32_ioctl(fd, SIOCSIFMETRIC, arg);
+	case 30: /* SIOCSARP */
+		return sys32_ioctl(fd, SIOCSARP, arg);
+	case 31: /* SIOCGARP */
+		return sys32_ioctl(fd, SIOCGARP, arg);
+	case 32: /* SIOCDARP */
+		return sys32_ioctl(fd, SIOCDARP, arg);
+	case 52: /* SIOCGETNAME */
+	case 53: /* SIOCGETPEER */
+		{
+			struct sockaddr uaddr;
+			int uaddr_len = sizeof(struct sockaddr), ret;
+			long args[3];
+			mm_segment_t old_fs = get_fs();
+			int (*sys_socketcall)(int, unsigned long *) =
+				(int (*)(int, unsigned long *))SYS(socketcall);
+			
+			args[0] = fd; args[1] = (long)&uaddr; args[2] = (long)&uaddr_len;
+			set_fs(KERNEL_DS);
+			ret = sys_socketcall(((cmd & 0xff) == 52) ? SYS_GETSOCKNAME : SYS_GETPEERNAME,
+					args);
+			set_fs(old_fs);
+			if (ret >= 0)
+				copy_to_user_ret((char *)A(arg), &uaddr, uaddr_len, -EFAULT);
+			return ret;
+		}
+#if 0		
+	case 86: /* SIOCSOCKSYS */
+		return socksys_syscall(fd, arg);
+#endif		
+	case 87: /* SIOCGIFNUM */
+		{
+			struct device *d;
+			int i = 0;
+			
+			for (d = dev_base; d; d = d->next) i++;
+			put_user_ret (i, (int *)A(arg), -EFAULT);
+			return 0;
+		}
+	}
+	return -ENOSYS;
+}
+
 /* }}} */
 
 asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
@@ -288,15 +402,13 @@
 	filp = current->files->fd[fd];
 	if(!filp) goto out;
 
-	if (!filp->f_op || !filp->f_op->ioctl) {
-		error = sys_ioctl (fd, cmd, (unsigned long)arg);
-		goto out;
-	}
-	
 	error = -EFAULT;
 	switch ((cmd >> 8) & 0xff) {
 	case 'S': error = solaris_S(fd, cmd, arg); break;
 	case 'T': error = solaris_T(fd, cmd, arg); break;
+	case 'i': error = solaris_i(fd, cmd, arg); break;
+	case 'r': error = solaris_r(fd, cmd, arg); break;
+	case 's': error = solaris_s(fd, cmd, arg); break;
 	case 't': error = solaris_t(fd, cmd, arg); break;
 	default:
 		error = -ENOSYS;
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/solaris/ipc.c linux/arch/sparc64/solaris/ipc.c
--- v2.1.78/linux/arch/sparc64/solaris/ipc.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/solaris/ipc.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: ipc.c,v 1.1 1997/09/03 12:29:29 jj Exp $
+/* $Id: ipc.c,v 1.2 1997/09/18 10:38:27 rth Exp $
  * ipc.c: Solaris IPC emulation
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -53,7 +53,7 @@
 {
 	int (*sys_ipc)(unsigned,int,int,unsigned long,void *,long) = 
 		(int (*)(unsigned,int,int,unsigned long,void *,long))SYS(ipc);
-	unsigned long old_fs;
+	mm_segment_t old_fs;
 	unsigned long raddr;
 	int ret;
 		
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/solaris/misc.c linux/arch/sparc64/solaris/misc.c
--- v2.1.78/linux/arch/sparc64/solaris/misc.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/solaris/misc.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: misc.c,v 1.4 1997/09/04 14:57:31 jj Exp $
+/* $Id: misc.c,v 1.6 1997/12/14 23:40:15 ecd Exp $
  * misc.c: Miscelaneous syscall emulation for Solaris
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -283,7 +283,7 @@
 			linux_cpus[smp_processor_id()].prom_node,
 			"clock-frequency", 167000000);
 #ifdef __SMP__	
-	case SOLARIS_CONFIG_NPROC_CONF:	return NCPUS;
+	case SOLARIS_CONFIG_NPROC_CONF:	return NR_CPUS;
 	case SOLARIS_CONFIG_NPROC_ONLN:	return smp_num_cpus;
 #else
 	case SOLARIS_CONFIG_NPROC_CONF:	return 1;
@@ -446,18 +446,3 @@
 }
 #endif
 
-#ifdef DEBUG_SOLARIS
-void entry_printk(int sysno, struct pt_regs *regs)
-{
-	printk ("Entering %d\n", sysno);
-	printk ("%08x %08x %08x %08x\n", (int)regs->u_regs[UREG_I0],
-					 (int)regs->u_regs[UREG_I1],
-					 (int)regs->u_regs[UREG_I2],
-					 (int)regs->u_regs[UREG_I3]);
-}
-
-void exit_printk(unsigned long ret)
-{
-	printk ("Returning %016lx\n", ret);
-}
-#endif
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/solaris/signal.c linux/arch/sparc64/solaris/signal.c
--- v2.1.78/linux/arch/sparc64/solaris/signal.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/solaris/signal.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: signal.c,v 1.2 1997/09/03 12:29:19 jj Exp $
+/* $Id: signal.c,v 1.5 1997/12/15 15:04:59 jj Exp $
  * signal.c: Signal emulation for Solaris
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -74,11 +74,11 @@
 {
 	struct sigaction sa, old;
 	int ret;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	int (*sys_sigaction)(int,struct sigaction *,struct sigaction *) = 
 		(int (*)(int,struct sigaction *,struct sigaction *))SYS(sigaction);
 	
-	sa.sa_mask = 0L;
+	sigemptyset(&sa.sa_mask);
 	sa.sa_restorer = NULL;
 	sa.sa_handler = (__sighandler_t)A(arg);
 	sa.sa_flags = 0;
@@ -99,13 +99,14 @@
 {
 	if (arg != 2) /* HOLD */ {
 		spin_lock_irq(&current->sigmask_lock);
-		current->blocked &= ~_S(sig);
+		sigdelsetmask(&current->blocked, _S(sig));
+		recalc_sigpending(current);
 		spin_unlock_irq(&current->sigmask_lock);
 		return sig_handler (sig, arg, 0);
 	} else {
-		sigset_t n = _S(sig) & _BLOCKABLE;
 		spin_lock_irq(&current->sigmask_lock);
-		current->blocked |= n;
+		sigaddsetmask(&current->blocked, (_S(sig) & ~_BLOCKABLE));
+		recalc_sigpending(current);
 		spin_unlock_irq(&current->sigmask_lock);
 		return 0;
 	}
@@ -119,7 +120,8 @@
 static inline long solaris_sigrelse(int sig)
 {
 	spin_lock_irq(&current->sigmask_lock);
-	current->blocked &= ~_S(sig);
+	sigdelsetmask(&current->blocked, _S(sig));
+	recalc_sigpending(current);
 	spin_unlock_irq(&current->sigmask_lock);
 	return 0;
 }
@@ -162,14 +164,14 @@
 	u32 x;
 	int sig;
 	
-	*q = 0L;
+	sigemptyset(q);
 	x = p[0];
 	for (i = 1; i <= SOLARIS_NSIGNALS; i++) {
 		if (x & 1) {
 			sig = solaris_to_linux_signals[i];
 			if (sig == -1)
 				return -EINVAL;
-			*q |= 1L << (sig - 1);
+			sigaddsetmask(q, (1L << (sig - 1)));
 		}
 		x >>= 1;
 		if (i == 32)
@@ -181,14 +183,12 @@
 static inline int mapout(sigset_t *q, u32 *p)
 {
 	int i;
-	sigset_t x;
 	int sig;
 	
 	p[0] = 0;
 	p[1] = 0;
-	x = *q;
-	for (i = 1; i <= 32; i++, x >>= 1) {
-		if (x & 1) {
+	for (i = 1; i <= 32; i++) {
+		if (sigismember(q, sigmask(i))) {
 			sig = linux_to_solaris_signals[i];
 			if (sig == -1)
 				return -EINVAL;
@@ -199,13 +199,12 @@
 		}
 	}
 	return 0;
-	
 }
 
 asmlinkage int solaris_sigprocmask(int how, u32 in, u32 out)
 {
 	sigset_t in_s, *ins, out_s, *outs;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	int ret;
 	int (*sys_sigprocmask)(int,sigset_t *,sigset_t *) = 
 		(int (*)(int,sigset_t *,sigset_t *))SYS(sigprocmask);
@@ -243,7 +242,7 @@
 	if (copy_from_user (tmp, (sol_sigset_t *)A(mask), 2*sizeof(u32)))
 		return -EFAULT;
 	if (mapin (tmp, &s)) return -EINVAL;
-	return (long)s;
+	return (long)s.sig[0];
 }
 
 struct sol_sigaction {
@@ -258,7 +257,7 @@
 	u32 tmp, tmp2[4];
 	struct sigaction s, s2;
 	int ret;
-	unsigned long old_fs = get_fs();
+	mm_segment_t old_fs = get_fs();
 	int (*sys_sigaction)(int,struct sigaction *,struct sigaction *) = 
 		(int (*)(int,struct sigaction *,struct sigaction *))SYS(sigaction);
 	
@@ -311,12 +310,13 @@
 	u32 tmp[4];
 	switch (which) {
 	case 1: /* sigpending */
-		lock_kernel();
-		s = current->blocked & current->signal;
-		unlock_kernel();
+		spin_lock_irq(&current->sigmask_lock);
+		sigandsets(&s, &current->blocked, &current->signal);
+		recalc_sigpending(current);
+		spin_unlock_irq(&current->sigmask_lock);
 		break;
 	case 2: /* sigfillset - I just set signals which have linux equivalents */
-		s = 0x7fffffff;
+		sigfillset(&s);
 		break;
 	default: return -EINVAL;
 	}
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/solaris/socksys.c linux/arch/sparc64/solaris/socksys.c
--- v2.1.78/linux/arch/sparc64/solaris/socksys.c	Thu Sep  4 17:07:30 1997
+++ linux/arch/sparc64/solaris/socksys.c	Mon Jan 12 15:15:44 1998
@@ -1,4 +1,4 @@
-/* $Id: socksys.c,v 1.1 1997/09/03 12:29:27 jj Exp $
+/* $Id: socksys.c,v 1.2 1997/09/08 11:29:38 jj Exp $
  * socksys.c: /dev/inet/ stuff for Solaris emulation.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -14,6 +14,7 @@
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/poll.h>
+#include <linux/file.h>
 
 #include <asm/uaccess.h>
 #include <asm/termios.h>
@@ -44,6 +45,7 @@
 static int socksys_open(struct inode * inode, struct file * filp)
 {
 	int family, type, protocol, fd;
+	struct dentry *dentry;
 	int (*sys_socket)(int,int,int) =
 		(int (*)(int,int,int))SUNOS(97);
 	
@@ -68,6 +70,17 @@
 	}
 	fd = sys_socket(family, type, protocol);
 	if (fd < 0) return fd;
+	dentry = filp->f_dentry;
+	filp->f_dentry = current->files->fd[fd]->f_dentry;
+	filp->f_dentry->d_inode->i_rdev = inode->i_rdev;
+	filp->f_dentry->d_inode->i_flock = inode->i_flock;
+	filp->f_dentry->d_inode->u.socket_i.file = filp;
+	filp->f_op = &socksys_file_ops;
+	dput(dentry);
+	FD_CLR(fd, &current->files->close_on_exec);
+	FD_CLR(fd, &current->files->open_fds);
+	put_filp(current->files->fd[fd]);
+	current->files->fd[fd] = NULL;
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.78/linux/drivers/block/README.fd linux/drivers/block/README.fd
--- v2.1.78/linux/drivers/block/README.fd	Tue Nov 18 17:22:07 1997
+++ linux/drivers/block/README.fd	Mon Jan 12 14:57:50 1998
@@ -85,16 +85,20 @@
 	This is needed on HP Omnibooks, which don't have a workable
 	DMA channel for the floppy driver. This option is also useful
 	if you frequently get "Unable to allocate DMA memory" messages.
-	Indeed, dma memory needs to be continuous in physical, and is
-	thus harder to find, whereas non-dma buffers may be allocated
-	in virtual memory. However, I advise against this if you have
-	an FDC without a FIFO (8272A or 82072). 82072A and later are
-	OK. You also need at least a 486 to use nodma.
+	Indeed, dma memory needs to be continuous in physical memory,
+	and is thus harder to find, whereas non-dma buffers may be
+	allocated in virtual memory. However, I advise against this if
+	you have an FDC without a FIFO (8272A or 82072). 82072A and
+	later are OK). You also need at least a 486 to use nodma.
 	If you use nodma mode, I suggest you also set the FIFO
 	threshold to 10 or lower, in order to limit the number of data
 	transfer interrupts.
-	
- floppy=dma
+
+	If you have a FIFO-able FDC, the floppy driver automatically
+	falls back on non DMA mode if no DMA-able memory can be found.
+	If you want to avoid this, explicitely ask for 'yesdma'.
+
+ floppy=yesdma
 	Tells the floppy driver that a workable DMA channel is available
 	(the default).
 
diff -u --recursive --new-file v2.1.78/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v2.1.78/linux/drivers/block/floppy.c	Tue Jan  6 09:37:33 1998
+++ linux/drivers/block/floppy.c	Mon Jan 12 14:57:50 1998
@@ -148,11 +148,32 @@
 #include <asm/io.h>
 #include <asm/uaccess.h>
 
-static int use_virtual_dma=0; /* virtual DMA for Intel */
+static int can_use_virtual_dma=2;
+/* =======
+ * can use virtual DMA:
+ * 0 = use of virtual DMA disallowed by config
+ * 1 = use of virtual DMA prescribed by config
+ * 2 = no virtual DMA preference configured.  By default try hard DMA,
+ * but fall back on virtual DMA when not enough memory available
+ */
+
+static int use_virtual_dma=0;
+/* =======
+ * use virtual DMA
+ * 0 using hard DMA
+ * 1 using virtual DMA
+ * This variable is set to virtual when a DMA mem problem arises, and
+ * reset back in floppy_grab_irq_and_dma.
+ * It is not safe to reset it in other circumstances, because the floppy
+ * driver may have several buffers in use at once, and we do currently not
+ * record each buffers capabilities
+ */
+
 static unsigned short virtual_dma_port=0x3f0;
 void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs);
 static int set_dor(int fdc, char mask, char data);
 static inline int __get_order(unsigned long size);
+#define K_64	0x10000		/* 64KB */
 #include <asm/floppy.h>
 
 
@@ -189,6 +210,20 @@
 #define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,__get_order(size))
 #endif
 
+static inline void fallback_on_nodma_alloc(char **addr, size_t l)
+{
+#ifdef FLOPPY_CAN_FALLBACK_ON_NODMA
+	if(*addr)
+		return; /* we have the memory */
+	if(can_use_virtual_dma != 2)
+		return; /* no fallback allowed */
+	printk("DMA memory shortage. Temporarily falling back on virtual DMA\n");
+	*addr = (char *) nodma_mem_alloc(l);
+#else
+	return;
+#endif
+}
+
 /* End dma memory related stuff */
 
 static unsigned int fake_change = 0;
@@ -258,7 +293,6 @@
  */
 #define MAX_DISK_SIZE 4 /* 3984*/
 
-#define K_64	0x10000		/* 64KB */
 
 /*
  * globals used by 'result()'
@@ -1015,17 +1049,20 @@
 		FDCS->reset=1;
 		return;
 	}
-	if (CROSS_64KB(raw_cmd->kernel_data, raw_cmd->length)) {
-		printk("DMA crossing 64-K boundary %p-%p\n",
-		       raw_cmd->kernel_data,
-		       raw_cmd->kernel_data + raw_cmd->length);
+#endif
+	INT_OFF;
+	fd_disable_dma();
+#ifdef fd_dma_setup
+	if(fd_dma_setup(raw_cmd->kernel_data, raw_cmd->length, 
+			(raw_cmd->flags & FD_RAW_READ)?
+			DMA_MODE_READ : DMA_MODE_WRITE,
+			FDCS->address) < 0) {
+		INT_ON;
 		cont->done(0);
 		FDCS->reset=1;
 		return;
 	}
-#endif
-	INT_OFF;
-	fd_disable_dma();
+#else	
 	fd_clear_dma_ff();
 	fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length);
 	fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ)?
@@ -1034,6 +1071,7 @@
 	fd_set_dma_count(raw_cmd->length);
 	virtual_dma_port = FDCS->address;
 	fd_enable_dma();
+#endif
 	INT_ON;
 	floppy_disable_hlt();
 }
@@ -1844,19 +1882,29 @@
 		DPRINT("calling disk change from floppy_ready\n");
 	}
 #endif
-
 	if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
 	   disk_change(current_drive) &&
 	   !DP->select_delay)
 		twaddle(); /* this clears the dcl on certain drive/controller
 			    * combinations */
 
+#ifdef fd_chose_dma_mode
+	if ((raw_cmd->flags & FD_RAW_READ) || 
+	    (raw_cmd->flags & FD_RAW_WRITE))
+		fd_chose_dma_mode(raw_cmd->kernel_data,
+				  raw_cmd->length);
+#endif
+
 	if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){
 		perpendicular_mode();
 		fdc_specify(); /* must be done here because of hut, hlt ... */
 		seek_floppy();
-	} else
+	} else {
+		if ((raw_cmd->flags & FD_RAW_READ) || 
+		    (raw_cmd->flags & FD_RAW_WRITE))
+			fdc_specify();
 		setup_rw_floppy();
+	}
 }
 
 static void floppy_start(void)
@@ -2403,6 +2451,7 @@
 #endif
 }
 
+#if 0
 static inline int check_dma_crossing(char *start, 
 				     unsigned long length, char *message)
 {
@@ -2413,6 +2462,7 @@
 	} else
 		return 0;
 }
+#endif
 
 /*
  * Formulate a read/write request.
@@ -2571,9 +2621,9 @@
 					indirect, direct, sector_t);
 				return 0;
 			}
-			check_dma_crossing(raw_cmd->kernel_data, 
+/*			check_dma_crossing(raw_cmd->kernel_data, 
 					   raw_cmd->length, 
-					   "end of make_raw_request [1]");
+					   "end of make_raw_request [1]");*/
 			return 2;
 		}
 	}
@@ -2619,8 +2669,8 @@
 	raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1;
 	raw_cmd->length <<= 9;
 #ifdef FLOPPY_SANITY_CHECK
-	check_dma_crossing(raw_cmd->kernel_data, raw_cmd->length, 
-			   "end of make_raw_request");
+	/*check_dma_crossing(raw_cmd->kernel_data, raw_cmd->length, 
+	  "end of make_raw_request");*/
 	if ((raw_cmd->length < current_count_sectors << 9) ||
 	    (raw_cmd->kernel_data != CURRENT->buffer &&
 	     CT(COMMAND) == FD_WRITE &&
@@ -3008,6 +3058,8 @@
 			if (ptr->length <= 0)
 				return -EINVAL;
 			ptr->kernel_data =(char*)fd_dma_mem_alloc(ptr->length);
+			fallback_on_nodma_alloc(&ptr->kernel_data,
+						ptr->length);
 			if (!ptr->kernel_data)
 				return -ENOMEM;
 			ptr->buffer_length = ptr->length;
@@ -3578,18 +3630,22 @@
 			try = 32; /* Only 24 actually useful */
 
 		tmp=(char *)fd_dma_mem_alloc(1024 * try);
-		if (!tmp) {
+		if (!tmp && !floppy_track_buffer) {
 			try >>= 1; /* buffer only one side */
 			INFBOUND(try, 16);
 			tmp= (char *)fd_dma_mem_alloc(1024*try);
 		}
-		if (!tmp) {
+		if(!tmp &&  !floppy_track_buffer) {
+			fallback_on_nodma_alloc(&tmp, 2048 * try);
+		}
+		if (!tmp && !floppy_track_buffer) {
 			DPRINT("Unable to allocate DMA memory\n");
 			RETERR(ENXIO);
 		}
-		if (floppy_track_buffer)
-			fd_dma_mem_free((unsigned long)tmp,try*1024);
-		else {
+		if (floppy_track_buffer) {
+			if(tmp)
+				fd_dma_mem_free((unsigned long)tmp,try*1024);
+		} else {
 			buffer_min = buffer_max = -1;
 			floppy_track_buffer = tmp;
 			max_buffer_sectors = try;
@@ -3886,9 +3942,9 @@
 	{ "silent_dcl_clear", floppy_set_flags, 0, 1, FD_SILENT_DCL_CLEAR },
 	{ "debug", floppy_set_flags, 0, 1, FD_DEBUG },
 
-	{ "nodma", 0, &use_virtual_dma, 1, 0 },
-	{ "omnibook", 0, &use_virtual_dma, 1, 0 },
-	{ "dma", 0, &use_virtual_dma, 0, 0 },
+	{ "nodma", 0, &can_use_virtual_dma, 1, 0 },
+	{ "omnibook", 0, &can_use_virtual_dma, 1, 0 },
+	{ "yesdma", 0, &can_use_virtual_dma, 0, 0 },
 
 	{ "fifo_depth", 0, &fifo_depth, 0xa, 0 },
 	{ "nofifo", 0, &no_fifo, 0x20, 0 },
@@ -3937,6 +3993,7 @@
 
 static int have_no_fdc= -EIO;
 
+
 __initfunc(int floppy_init(void))
 {
 	int i,unit,drive;
@@ -3972,6 +4029,7 @@
 #endif
 	}
 
+	use_virtual_dma = can_use_virtual_dma & 1;
 	fdc_state[0].address = FDC1;
 	if (fdc_state[0].address == -1) {
 		unregister_blkdev(MAJOR_NR,"fd");
@@ -4017,6 +4075,8 @@
 			FDCS->address = -1;
 			continue;
 		}
+		if(can_use_virtual_dma == 2 && FDCS->version < FDC_82072A)
+			can_use_virtual_dma = 0;
 
 		have_no_fdc = 0;
 		/* Not all FDCs seem to be able to handle the version command
@@ -4032,7 +4092,7 @@
 	initialising=0;
 	if (have_no_fdc) {
 		DPRINT("no floppy controllers found\n");
-		unregister_blkdev(MAJOR_NR,"fd");
+		unregister_blkdev(MAJOR_NR,"fd");		
 	}
 	return have_no_fdc;
 }
@@ -4063,6 +4123,7 @@
 		usage_count--;
 		return -1;
 	}
+
 	for (fdc=0; fdc< N_FDC; fdc++){
 		if (FDCS->address != -1){
 			if (check_region(FDCS->address, 6) < 0 ||
diff -u --recursive --new-file v2.1.78/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.1.78/linux/drivers/block/ide-cd.c	Tue Jan  6 09:37:33 1998
+++ linux/drivers/block/ide-cd.c	Sat Jan 10 10:42:55 1998
@@ -2772,6 +2772,17 @@
 	return nslots;
 }
 
+static void ide_cdrom_add_settings(ide_drive_t *drive)
+{
+	int major = HWIF(drive)->major;
+	int minor = drive->select.b.unit << PARTN_BITS;
+
+	ide_add_setting(drive,	"breada_readahead",	SETTING_RW,					BLKRAGET,		BLKRASET,		TYPE_INT,	0,	255,				1,	2,	&read_ahead[major],		NULL);
+	ide_add_setting(drive,	"file_readahead",	SETTING_RW,					BLKFRAGET,		BLKFRASET,		TYPE_INTA,	0,	INT_MAX,			1,	1024,	&max_readahead[major][minor],	NULL);
+	ide_add_setting(drive,	"max_kb_per_request",	SETTING_RW,					BLKSECTGET,		BLKSECTSET,		TYPE_INTA,	1,	255,				1,	2,	&max_sectors[major][minor],	NULL);
+	ide_add_setting(drive,	"dsc_overlap",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,	1,	&drive->dsc_overlap,		NULL);
+}
+
 static
 int ide_cdrom_setup (ide_drive_t *drive)
 {
@@ -2884,6 +2895,7 @@
 		info->devinfo.handle = NULL;
 		return 1;
 	}
+	ide_cdrom_add_settings(drive);
 	return 0;
 }
 
@@ -2942,13 +2954,6 @@
 	return 0;
 }
 
-int ide_cdrom_init (void);
-static ide_module_t ide_cdrom_module = {
-	IDE_DRIVER_MODULE,
-	ide_cdrom_init,
-	NULL
-};
-
 static ide_driver_t ide_cdrom_driver = {
 	"ide-cdrom",			/* name */
 	IDECD_VERSION,			/* version */
@@ -2969,6 +2974,13 @@
 	NULL				/* proc */
 };
 
+int ide_cdrom_init (void);
+static ide_module_t ide_cdrom_module = {
+	IDE_DRIVER_MODULE,
+	ide_cdrom_init,
+	&ide_cdrom_driver,
+	NULL
+};
 
 #ifdef MODULE
 int init_module (void)
@@ -2981,7 +2993,7 @@
 	ide_drive_t *drive;
 	int failed = 0;
 
-	while ((drive = ide_scan_devices (ide_cdrom, &ide_cdrom_driver, failed)) != NULL)
+	while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, &ide_cdrom_driver, failed)) != NULL)
 		if (ide_cdrom_cleanup (drive)) {
 			printk ("%s: cleanup_module() called while still busy\n", drive->name);
 			failed++;
@@ -2997,7 +3009,7 @@
 	int failed = 0;
 
 	MOD_INC_USE_COUNT;
-	while ((drive = ide_scan_devices (ide_cdrom, NULL, failed++)) != NULL) {
+	while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, NULL, failed++)) != NULL) {
 		info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL);
 		if (info == NULL) {
 			printk ("%s: Can't allocate a cdrom structure\n", drive->name);
diff -u --recursive --new-file v2.1.78/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.1.78/linux/drivers/block/ide-disk.c	Sun Dec 21 22:36:12 1997
+++ linux/drivers/block/ide-disk.c	Sat Jan 10 10:42:55 1998
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide-disk.c	Version 1.03  Nov  30, 1997
+ *  linux/drivers/block/ide-disk.c	Version 1.04  Jan   7, 1998
  *
  *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
  */
@@ -13,11 +13,12 @@
  * Version 1.00		move disk only code from ide.c to ide-disk.c
  *			support optional byte-swapping of all data
  * Version 1.01		fix previous byte-swapping code
- * Verions 1.02		remove ", LBA" from drive identification msgs
- * Verions 1.03		fix display of id->buf_size for big-endian
+ * Version 1.02		remove ", LBA" from drive identification msgs
+ * Version 1.03		fix display of id->buf_size for big-endian
+ * Version 1.04		add /proc configurable settings and S.M.A.R.T support
  */
 
-#define IDEDISK_VERSION	"1.03"
+#define IDEDISK_VERSION	"1.04"
 
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
 
@@ -372,17 +373,13 @@
 {
 	MOD_INC_USE_COUNT;
 	if (drive->removable && drive->usage == 1) {
-		byte door_lock[] = {WIN_DOORLOCK,0,0,0};
-		struct request rq;
 		check_disk_change(inode->i_rdev);
-		ide_init_drive_cmd (&rq);
-		rq.buffer = door_lock;
 		/*
 		 * Ignore the return code from door_lock,
 		 * since the open() has already succeeded,
 		 * and the door_lock is irrelevant at this point.
 		 */
-		(void) ide_do_drive_cmd(drive, &rq, ide_wait);
+		(void) ide_wait_cmd(drive, WIN_DOORLOCK, 0, 0, 0, NULL);
 	}
 	return 0;
 }
@@ -390,12 +387,8 @@
 static void idedisk_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
 {
 	if (drive->removable && !drive->usage) {
-		byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0};
-		struct request rq;
 		invalidate_buffers(inode->i_rdev);
-		ide_init_drive_cmd (&rq);
-		rq.buffer = door_unlock;
-		(void) ide_do_drive_cmd(drive, &rq, ide_wait);
+		(void) ide_wait_cmd(drive, WIN_DOORUNLOCK, 0, 0, 0, NULL);
 	}
 	MOD_DEC_USE_COUNT;
 }
@@ -481,18 +474,106 @@
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
+static int smart_enable(ide_drive_t *drive)
+{
+	return ide_wait_cmd(drive, WIN_SMART, 0, SMART_ENABLE, 0, NULL);
+}
+
+static int get_smart_values(ide_drive_t *drive, byte *buf)
+{
+	(void) smart_enable(drive);
+	return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_VALUES, 1, buf);
+}
+
+static int get_smart_thresholds(ide_drive_t *drive, byte *buf)
+{
+	(void) smart_enable(drive);
+	return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_THRESHOLDS, 1, buf);
+}
+
+static int proc_idedisk_read_smart_thresholds
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *)data;
+	int		len = 0, i = 0;
+
+	if (!get_smart_thresholds(drive, page)) {
+		unsigned short *val = ((unsigned short *)page) + 2;
+		char *out = ((char *)val) + (SECTOR_WORDS * 4);
+		page = out;
+		do {
+			out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
+			val += 1;
+		} while (i < (SECTOR_WORDS * 2));
+		len = out - page;
+	}
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_idedisk_read_smart_values
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *)data;
+	int		len = 0, i = 0;
+
+	if (!get_smart_values(drive, page)) {
+		unsigned short *val = ((unsigned short *)page) + 2;
+		char *out = ((char *)val) + (SECTOR_WORDS * 4);
+		page = out;
+		do {
+			out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
+			val += 1;
+		} while (i < (SECTOR_WORDS * 2));
+		len = out - page;
+	}
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
 static ide_proc_entry_t idedisk_proc[] = {
 	{ "cache", proc_idedisk_read_cache, NULL },
 	{ "geometry", proc_ide_read_geometry, NULL },
+	{ "smart_values", proc_idedisk_read_smart_values, NULL },
+	{ "smart_thresholds", proc_idedisk_read_smart_thresholds, NULL },
 	{ NULL, NULL, NULL }
 };
 
-int idedisk_init (void);
-static ide_module_t idedisk_module = {
-	IDE_DRIVER_MODULE,
-	idedisk_init,
-	NULL
-};
+static int set_multcount(ide_drive_t *drive, int arg)
+{
+	struct request rq;
+
+	if (drive->special.b.set_multmode)
+		return -EBUSY;
+	ide_init_drive_cmd (&rq);
+	drive->mult_req = arg;
+	drive->special.b.set_multmode = 1;
+	(void) ide_do_drive_cmd (drive, &rq, ide_wait);
+	return (drive->mult_count == arg) ? 0 : -EIO;
+}
+
+static int set_nowerr(ide_drive_t *drive, int arg)
+{
+	drive->nowerr = arg;
+	drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
+	return 0;
+}
+
+static void idedisk_add_settings(ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	int major = HWIF(drive)->major;
+	int minor = drive->select.b.unit << PARTN_BITS;
+
+	ide_add_setting(drive,	"bios_cyl",		SETTING_RW,					-1,			-1,			TYPE_SHORT,	0,	1023,				1,	1,	&drive->bios_cyl,		NULL);
+	ide_add_setting(drive,	"bios_head",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	255,				1,	1,	&drive->bios_head,		NULL);
+	ide_add_setting(drive,	"bios_sect",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	63,				1,	1,	&drive->bios_sect,		NULL);
+	ide_add_setting(drive,	"bswap",		SETTING_READ,					-1,			-1,			TYPE_BYTE,	0,	1,				1,	1,	&drive->bswap,			NULL);
+	ide_add_setting(drive,	"multcount",		id ? SETTING_RW : SETTING_READ,			HDIO_GET_MULTCOUNT,	HDIO_SET_MULTCOUNT,	TYPE_BYTE,	0,	id ? id->max_multsect : 0,	1,	2,	&drive->mult_count,		set_multcount);
+	ide_add_setting(drive,	"nowerr",		SETTING_RW,					HDIO_GET_NOWERR,	HDIO_SET_NOWERR,	TYPE_BYTE,	0,	1,				1,	1,	&drive->nowerr,			set_nowerr);
+	ide_add_setting(drive,	"breada_readahead",	SETTING_RW,					BLKRAGET,		BLKRASET,		TYPE_INT,	0,	255,				1,	2,	&read_ahead[major],		NULL);
+	ide_add_setting(drive,	"file_readahead",	SETTING_RW,					BLKFRAGET,		BLKFRASET,		TYPE_INTA,	0,	INT_MAX,			1,	1024,	&max_readahead[major][minor],	NULL);
+	ide_add_setting(drive,	"max_kb_per_request",	SETTING_RW,					BLKSECTGET,		BLKSECTSET,		TYPE_INTA,	1,	255,				1,	2,	&max_sectors[major][minor],	NULL);
+
+}
 
 /*
  *	IDE subdriver functions, registered with ide.c
@@ -517,6 +598,14 @@
 	idedisk_proc		/* proc */
 };
 
+int idedisk_init (void);
+static ide_module_t idedisk_module = {
+	IDE_DRIVER_MODULE,
+	idedisk_init,
+	&idedisk_driver,
+	NULL
+};
+
 static int idedisk_cleanup (ide_drive_t *drive)
 {
 	return ide_unregister_subdriver(drive);
@@ -527,6 +616,8 @@
 	struct hd_driveid *id = drive->id;
 	unsigned long capacity, check;
 	
+	idedisk_add_settings(drive);
+
 	if (id == NULL)
 		return;
 
@@ -615,7 +706,7 @@
 	int failed = 0;
 	
 	MOD_INC_USE_COUNT;
-	while ((drive = ide_scan_devices (ide_disk, NULL, failed++)) != NULL) {
+	while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, NULL, failed++)) != NULL) {
 
 		/* SunDisk drives: ignore "second" drive;   can mess up non-Sun systems!  FIXME */
 		struct hd_driveid *id = drive->id;
@@ -650,7 +741,7 @@
 	ide_drive_t *drive;
 	int failed = 0;
 
-	while ((drive = ide_scan_devices (ide_disk, &idedisk_driver, failed)) != NULL)
+	while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, &idedisk_driver, failed)) != NULL)
 		if (idedisk_cleanup (drive)) {
 			printk (KERN_ERR "%s: cleanup_module() called while still busy\n", drive->name);
 			failed++;
diff -u --recursive --new-file v2.1.78/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c
--- v2.1.78/linux/drivers/block/ide-floppy.c	Sun Dec 21 22:36:12 1997
+++ linux/drivers/block/ide-floppy.c	Sat Jan 10 10:42:55 1998
@@ -1327,6 +1327,20 @@
 	return 0;
 }
 
+static void idefloppy_add_settings(ide_drive_t *drive)
+{
+	int major = HWIF(drive)->major;
+	int minor = drive->select.b.unit << PARTN_BITS;
+
+	ide_add_setting(drive,	"bios_cyl",		SETTING_RW,					-1,			-1,			TYPE_SHORT,	0,	1023,				1,	1,	&drive->bios_cyl,		NULL);
+	ide_add_setting(drive,	"bios_head",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	255,				1,	1,	&drive->bios_head,		NULL);
+	ide_add_setting(drive,	"bios_sect",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	63,				1,	1,	&drive->bios_sect,		NULL);
+	ide_add_setting(drive,	"breada_readahead",	SETTING_RW,					BLKRAGET,		BLKRASET,		TYPE_INT,	0,	255,				1,	2,	&read_ahead[major],		NULL);
+	ide_add_setting(drive,	"file_readahead",	SETTING_RW,					BLKFRAGET,		BLKFRASET,		TYPE_INTA,	0,	INT_MAX,			1,	1024,	&max_readahead[major][minor],	NULL);
+	ide_add_setting(drive,	"max_kb_per_request",	SETTING_RW,					BLKSECTGET,		BLKSECTSET,		TYPE_INTA,	1,	255,				1,	2,	&max_sectors[major][minor],	NULL);
+
+}
+
 /*
  *	Driver initialization.
  */
@@ -1351,6 +1365,7 @@
 	}
 
 	(void) idefloppy_get_capacity (drive);
+	idefloppy_add_settings(drive);
 }
 
 static int idefloppy_cleanup (ide_drive_t *drive)
@@ -1369,13 +1384,6 @@
 	{ NULL, NULL, NULL }
 };
 
-int idefloppy_init (void);
-static ide_module_t idefloppy_module = {
-	IDE_DRIVER_MODULE,
-	idefloppy_init,
-	NULL
-};
-
 /*
  *	IDE subdriver functions, registered with ide.c
  */
@@ -1399,6 +1407,14 @@
 	idefloppy_proc		/* proc */
 };
 
+int idefloppy_init (void);
+static ide_module_t idefloppy_module = {
+	IDE_DRIVER_MODULE,
+	idefloppy_init,
+	&idefloppy_driver,
+	NULL
+};
+
 /*
  *	idefloppy_init will register the driver for each floppy.
  */
@@ -1409,7 +1425,7 @@
 	int failed = 0;
 
 	MOD_INC_USE_COUNT;
-	while ((drive = ide_scan_devices (ide_floppy, NULL, failed++)) != NULL) {
+	while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, NULL, failed++)) != NULL) {
 		if (!idefloppy_identify_device (drive, drive->id)) {
 			printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name);
 			continue;
@@ -1442,7 +1458,7 @@
 	ide_drive_t *drive;
 	int failed = 0;
 
-	while ((drive = ide_scan_devices (ide_floppy, &idefloppy_driver, failed)) != NULL)
+	while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, &idefloppy_driver, failed)) != NULL)
 		if (idefloppy_cleanup (drive)) {
 			printk ("%s: cleanup_module() called while still busy\n", drive->name);
 			failed++;
diff -u --recursive --new-file v2.1.78/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c
--- v2.1.78/linux/drivers/block/ide-probe.c	Sun Dec 21 22:36:12 1997
+++ linux/drivers/block/ide-probe.c	Sat Jan 10 10:42:55 1998
@@ -624,6 +624,11 @@
 
 	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) ;
 	hwif->gd = *gdp = gd;			/* link onto tail of list */
+
+	for (unit = 0; unit < units; ++unit) {
+		if (hwif->drives[unit].present)
+			ide_add_generic_settings(hwif->drives + unit);
+	}
 }
 
 static int hwif_init (ide_hwif_t *hwif)
diff -u --recursive --new-file v2.1.78/linux/drivers/block/ide-proc.c linux/drivers/block/ide-proc.c
--- v2.1.78/linux/drivers/block/ide-proc.c	Fri Jan  2 14:37:01 1998
+++ linux/drivers/block/ide-proc.c	Sat Jan 10 10:42:55 1998
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide-proc.c	Version 1.02	December 31, 1997
+ *  linux/drivers/block/ide-proc.c	Version 1.03	January   2, 1998
  *
  *  Copyright (C) 1997-1998	Mark Lord
  */
@@ -38,8 +38,15 @@
  * If there is an error *anywhere* in the string of registers/data
  * then *none* of the writes will be performed.
  *
- * Also useful, "cat /proc/ide0/hda/identify" will issue an IDENTIFY
- * (or PACKET_IDENTIFY) command to /dev/hda, and then dump out the
+ * Drive/Driver settings can be retrieved by reading the drive's
+ * "settings" files.  e.g.    "cat /proc/ide0/hda/settings"
+ * To write a new value "val" into a specific setting "name", use:
+ *   echo "name:val" >/proc/ide/ide0/hda/settings
+ *
+ * Also useful, "cat /proc/ide0/hda/[identify, smart_values,
+ * smart_thresholds, capabilities]" will issue an IDENTIFY /
+ * PACKET_IDENTIFY / SMART_READ_VALUES / SMART_READ_THRESHOLDS /
+ * SENSE CAPABILITIES command to /dev/hda, and then dump out the
  * returned data as 256 16-bit words.  The "hdparm" utility will
  * be updated someday soon to use this mechanism.
  *
@@ -80,6 +87,16 @@
 	return digit;
 }
 
+static int ide_getdigit(char c)
+{
+	int digit;
+	if (isdigit(c))
+		digit = c - '0';
+	else
+		digit = -1;
+	return digit;
+}
+
 static int xx_xx_parse_error (const char *data, unsigned long len, const char *msg)
 {
 	char errbuf[16];
@@ -237,6 +254,24 @@
 	return xx_xx_parse_error(start, startn, msg);
 }
 
+static int proc_ide_read_drivers
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	char		*out = page;
+	int		len;
+	ide_module_t	*p = ide_modules;
+	ide_driver_t	*driver;
+
+	while (p) {
+		driver = (ide_driver_t *) p->info;
+		if (p->type == IDE_DRIVER_MODULE && driver)
+			out += sprintf(out, "%s version %s\n", driver->name, driver->version);
+		p = p->next;
+	}
+	len = out - page;
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
 static int proc_ide_read_config
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
@@ -325,22 +360,9 @@
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
-static int proc_ide_get_identify (ide_drive_t *drive, byte *buf)
+static int proc_ide_get_identify(ide_drive_t *drive, byte *buf)
 {
-	struct request rq;
-	byte *end;
-
-	ide_init_drive_cmd(&rq);
-	rq.buffer = buf;
-	*buf++ = (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY;
-	*buf++ = 0;
-	*buf++ = 0;
-	*buf++ = 1;
-	end = buf + (SECTOR_WORDS * 4);
-	while (buf != end)
-		*buf++ = 0;	/* pre-zero it, in case identify fails */
-	(void) ide_do_drive_cmd(drive, &rq, ide_wait);
-	return 0;
+	return ide_wait_cmd(drive, (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY, 0, 0, 1, buf);
 }
 
 static int proc_ide_read_identify
@@ -366,25 +388,130 @@
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
 	ide_drive_t	*drive = (ide_drive_t *) data;
-	int		major = HWIF(drive)->major;
-	int		minor = drive->select.b.unit << PARTN_BITS;
+	ide_settings_t	*setting = (ide_settings_t *) drive->settings;
 	char		*out = page;
-	int		len;
+	int		len, rc, mul_factor, div_factor;
 
-	out += sprintf(out,"multcount    %i\n", drive->mult_count);
-	out += sprintf(out,"io_32bit     %i\n", drive->io_32bit);
-	out += sprintf(out,"unmaskirq    %i\n", drive->unmask);
-	out += sprintf(out,"using_dma    %i\n", drive->using_dma);
-	out += sprintf(out,"nowerr       %i\n", drive->bad_wstat == BAD_R_STAT);
-	out += sprintf(out,"keepsettings %i\n", drive->keep_settings);
-	out += sprintf(out,"nice         %i/%i/%i\n", drive->nice0, drive->nice1, drive->nice2);
-	out += sprintf(out,"dsc_overlap  %i\n", drive->dsc_overlap);
-	out += sprintf(out,"max_sectors  %i\n", max_sectors[major][minor]);
-	out += sprintf(out,"readahead    %i\n", max_readahead[major][minor] / 1024);
+	out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
+	out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
+	while(setting) {
+		mul_factor = setting->mul_factor;
+		div_factor = setting->div_factor;
+		out += sprintf(out, "%-24s", setting->name);
+		if ((rc = ide_read_setting(drive, setting)) >= 0)
+			out += sprintf(out, "%-16d", rc * mul_factor / div_factor);
+		else
+			out += sprintf(out, "%-16s", "write-only");
+		out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
+		if (setting->rw & SETTING_READ)
+			out += sprintf(out, "r");
+		if (setting->rw & SETTING_WRITE)
+			out += sprintf(out, "w");
+		out += sprintf(out, "\n");
+		setting = setting->next;
+	}
 	len = out - page;
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
+#define MAX_LEN	30
+
+static int proc_ide_write_settings
+	(struct file *file, const char *buffer, unsigned long count, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+	ide_hwif_t	*hwif = HWIF(drive);
+	char		name[MAX_LEN + 1];
+	int		for_real = 0, len;
+	unsigned long	n, flags;
+	const char	*start = NULL;
+	ide_settings_t	*setting;
+
+	if (!suser())
+		return -EACCES;
+
+	/*
+	 * Skip over leading whitespace
+	 */
+	while (count && isspace(*buffer)) {
+		--count;
+		++buffer;
+	}
+	/*
+	 * Do one full pass to verify all parameters,
+	 * then do another to actually write the pci regs.
+	 */
+	save_flags(flags);
+	do {
+		const char *p;
+		if (for_real) {
+			unsigned long timeout = jiffies + (3 * HZ);
+			ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup);
+			ide_hwgroup_t *mategroup = NULL;
+			if (hwif->mate && hwif->mate->hwgroup)
+				mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup);
+			cli();	/* ensure all PCI writes are done together */
+			while (mygroup->active || (mategroup && mategroup->active)) {
+				restore_flags(flags);
+				if (0 < (signed long)(jiffies - timeout)) {
+					printk("/proc/ide/%s/pci: channel(s) busy, cannot write\n", hwif->name);
+					return -EBUSY;
+				}
+				cli();
+			}
+		}
+		p = buffer;
+		n = count;
+		while (n > 0) {
+			int d, digits;
+			unsigned int val = 0;
+			start = p;
+
+			while (n > 0 && *p != ':') {
+				--n;
+				p++;
+			}
+			if (*p != ':')
+				goto parse_error;
+			len = IDE_MIN(p - start, MAX_LEN);
+			strncpy(name, start, IDE_MIN(len, MAX_LEN));
+			name[len] = 0;
+
+			if (n > 0) {
+				--n;
+				p++;
+			} else
+				goto parse_error;
+			
+			digits = 0;
+			while (n > 0 && (d = ide_getdigit(*p)) >= 0) {
+				val = (val * 10) + d;
+				--n;
+				++p;
+				++digits;
+			}
+			if (n > 0 && !isspace(*p))
+				goto parse_error;
+			while (n > 0 && isspace(*p)) {
+				--n;
+				++p;
+			}
+			setting = ide_find_setting_by_name(drive, name);
+			if (!setting)
+				goto parse_error;
+
+			if (for_real)
+				ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor);
+		}
+	} while (!for_real++);
+	restore_flags(flags);
+	return count;
+parse_error:
+	restore_flags(flags);
+	printk("proc_ide_write_settings(): parse error\n");
+	return -EINVAL;
+}
+
 int proc_ide_read_capacity
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
@@ -437,6 +564,18 @@
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
+static int proc_ide_write_driver
+	(struct file *file, const char *buffer, unsigned long count, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+
+	if (!suser())
+		return -EACCES;
+	if (ide_replace_subdriver(drive, buffer))
+		return -EINVAL;
+	return count;
+}
+
 static int proc_ide_read_media
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
@@ -461,13 +600,12 @@
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
-
 static ide_proc_entry_t generic_drive_entries[] = {
-	{ "driver", proc_ide_read_driver, NULL },
+	{ "driver", proc_ide_read_driver, proc_ide_write_driver },
 	{ "identify", proc_ide_read_identify, NULL },
 	{ "media", proc_ide_read_media, NULL },
 	{ "model", proc_ide_read_dmodel, NULL },
-	{ "settings", proc_ide_read_settings, NULL },
+	{ "settings", proc_ide_read_settings, proc_ide_write_settings },
 	{ NULL, NULL, NULL }
 };
 
@@ -497,9 +635,16 @@
 	}
 }
 
-static void create_proc_ide_drives (ide_hwif_t *hwif, struct proc_dir_entry *parent)
+static int proc_ide_readlink(struct proc_dir_entry *de, char *page)
+{
+	int n = (de->name[2] - 'a') / 2;
+	return sprintf(page, "ide%d/%s", n, de->name);
+}
+
+static void create_proc_ide_drives (ide_hwif_t *hwif, struct proc_dir_entry *parent, struct proc_dir_entry *root)
 {
 	int	d;
+	struct proc_dir_entry *ent;
 
 	for (d = 0; d < MAX_DRIVES; d++) {
 		ide_drive_t *drive = &hwif->drives[d];
@@ -509,6 +654,12 @@
 		drive->proc = create_proc_entry(drive->name, S_IFDIR, parent);
 		if (drive->proc)
 			ide_add_proc_entries(drive, generic_drive_entries);
+
+		ent = create_proc_entry(drive->name, S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, root);
+		if (!ent) return;
+		ent->data = drive;
+		ent->readlink_proc = proc_ide_readlink;
+		ent->nlink = 1;
 	}
 }
 
@@ -555,14 +706,18 @@
 		ent->data = hwif;
 		ent->read_proc  = proc_ide_read_type;
 
-		create_proc_ide_drives(hwif, hwif_ent);
+		create_proc_ide_drives(hwif, hwif_ent, parent);
 	}
 }
 
 void proc_ide_init(void)
 {
-	struct proc_dir_entry *ent;
-	ent = create_proc_entry("ide", S_IFDIR, 0);
+	struct proc_dir_entry *root, *ent;
+	root = create_proc_entry("ide", S_IFDIR, 0);
+	if (!root) return;
+	create_proc_ide_interfaces(root);
+
+	ent = create_proc_entry("drivers", 0, root);
 	if (!ent) return;
-	create_proc_ide_interfaces(ent);
+	ent->read_proc  = proc_ide_read_drivers;
 }
diff -u --recursive --new-file v2.1.78/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v2.1.78/linux/drivers/block/ide-tape.c	Sun Dec 21 22:36:12 1997
+++ linux/drivers/block/ide-tape.c	Sat Jan 10 10:42:55 1998
@@ -1,7 +1,7 @@
 /*
- * linux/drivers/block/ide-tape.c	Version 1.12		Dec   7, 1997
+ * linux/drivers/block/ide-tape.c	Version 1.13		Jan   2, 1998
  *
- * Copyright (C) 1995, 1996 Gadi Oxman <gadio@netvision.net.il>
+ * Copyright (C) 1995 - 1998 Gadi Oxman <gadio@netvision.net.il>
  *
  * This driver was constructed as a student project in the software laboratory
  * of the faculty of electrical engineering in the Technion - Israel's
@@ -211,6 +211,7 @@
  *                        of bytes written to the tape was not an integral
  *                        number of tape blocks.
  *                       Add support for INTERRUPT DRQ devices.
+ * Ver 1.13  Jan  2 98   Add "speed == 0" work-around for HP COLORADO 5GB
  *
  * Here are some words from the first releases of hd.c, which are quoted
  * in ide.c and apply here as well:
@@ -320,7 +321,7 @@
  *		sharing a (fast) ATA-2 disk with any (slow) new ATAPI device.
  */
 
-#define IDETAPE_VERSION "1.12"
+#define IDETAPE_VERSION "1.13"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -675,7 +676,7 @@
 	 */
 	int nr_stages;				/* Number of currently used stages */
 	int nr_pending_stages;			/* Number of pending stages */
-	int max_stages;				/* We will not allocate more than this number of stages */
+	int max_stages, min_pipeline, max_pipeline; /* We will not allocate more than this number of stages */
 	idetape_stage_t *first_stage;		/* The first stage which will be removed from the pipeline */
 	idetape_stage_t *active_stage;		/* The currently active stage */
 	idetape_stage_t *next_stage;		/* Will be serviced after the currently active request */
@@ -1409,12 +1410,15 @@
 static void idetape_increase_max_pipeline_stages (ide_drive_t *drive)
 {
 	idetape_tape_t *tape = drive->driver_data;
+	int increase = (tape->max_pipeline - tape->min_pipeline) / 10;
 	
 #if IDETAPE_DEBUG_LOG
 	printk (KERN_INFO "Reached idetape_increase_max_pipeline_stages\n");
 #endif /* IDETAPE_DEBUG_LOG */
 
-	tape->max_stages = IDE_MIN (tape->max_stages + IDETAPE_INCREASE_STAGES_RATE, IDETAPE_MAX_PIPELINE_STAGES);
+	tape->max_stages += increase;
+	tape->max_stages = IDE_MAX(tape->max_stages, tape->min_pipeline);
+	tape->max_stages = IDE_MIN(tape->max_stages, tape->max_pipeline);
 }
 
 /*
@@ -2520,7 +2524,7 @@
 	while (tape->first_stage != NULL)
 		idetape_remove_stage_head (drive);
 	tape->nr_pending_stages = 0;
-	tape->max_stages = IDETAPE_MIN_PIPELINE_STAGES;
+	tape->max_stages = tape->min_pipeline;
 }
 
 /*
@@ -2610,7 +2614,7 @@
 	 *	 as some systems are constantly on, and the system load
 	 *	 can be totally different on the next backup).
 	 */
-	tape->max_stages = IDETAPE_MIN_PIPELINE_STAGES;
+	tape->max_stages = tape->min_pipeline;
 #if IDETAPE_DEBUG_BUGS
 	if (tape->first_stage != NULL || tape->next_stage != NULL || tape->last_stage != NULL || tape->nr_stages != 0) {
 		printk (KERN_ERR "ide-tape: ide-tape pipeline bug\n");		
@@ -3460,6 +3464,15 @@
 	capabilities->speed = ntohs (capabilities->speed);
 	capabilities->buffer_size = ntohs (capabilities->buffer_size);
 
+	if (!capabilities->speed) {
+		printk("ide-tape: %s: overriding capabilities->speed (assuming 650KB/sec)\n", drive->name);
+		capabilities->speed = 650;
+	}
+	if (!capabilities->max_speed) {
+		printk("ide-tape: %s: overriding capabilities->max_speed (assuming 650KB/sec)\n", drive->name);
+		capabilities->max_speed = 650;
+	}
+
 	tape->capabilities = *capabilities;		/* Save us a copy */
 	tape->tape_block_size = capabilities->blk512 ? 512:1024;
 #if IDETAPE_DEBUG_LOG
@@ -3493,6 +3506,24 @@
 #endif /* IDETAPE_DEBUG_LOG */
 }
 
+static void idetape_add_settings(ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+/*
+ *			drive	setting name	read/write	ioctl	ioctl		data type	min			max			mul_factor			div_factor			data pointer				set function
+ */
+	ide_add_setting(drive,	"buffer",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				2,				&tape->capabilities.buffer_size,	NULL);
+	ide_add_setting(drive,	"pipeline_min",	SETTING_RW,	-1,	-1,		TYPE_INT,	0,			0xffff,			tape->stage_size / 1024,	1,				&tape->min_pipeline,			NULL);
+	ide_add_setting(drive,	"pipeline",	SETTING_RW,	-1,	-1,		TYPE_INT,	0,			0xffff,			tape->stage_size / 1024,	1,				&tape->max_stages,			NULL);
+	ide_add_setting(drive,	"pipeline_max",	SETTING_RW,	-1,	-1,		TYPE_INT,	0,			0xffff,			tape->stage_size / 1024,	1,				&tape->max_pipeline,			NULL);
+	ide_add_setting(drive,	"pipeline_used",SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			tape->stage_size / 1024,	1,				&tape->nr_stages,			NULL);
+	ide_add_setting(drive,	"speed",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				1,				&tape->capabilities.speed,		NULL);
+	ide_add_setting(drive,	"stage",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1024,				&tape->stage_size,			NULL);
+	ide_add_setting(drive,	"tdsc",		SETTING_RW,	-1,	-1,		TYPE_INT,	IDETAPE_DSC_RW_MIN,	IDETAPE_DSC_RW_MAX,	1000,				HZ,				&tape->best_dsc_rw_frequency,		NULL);
+	ide_add_setting(drive,	"dsc_overlap",	SETTING_RW,	-1,	-1,		TYPE_BYTE,	0,			1,			1,				1,				&drive->dsc_overlap,			NULL);
+}
+
 /*
  *	ide_setup is called to:
  *
@@ -3521,7 +3552,9 @@
 	tape->name[0] = 'h'; tape->name[1] = 't'; tape->name[2] = '0' + minor;
 	tape->chrdev_direction = idetape_direction_none;
 	tape->pc = tape->pc_stack;
-	tape->max_stages = IDETAPE_MIN_PIPELINE_STAGES;
+	tape->min_pipeline = IDETAPE_MIN_PIPELINE_STAGES;
+	tape->max_pipeline = IDETAPE_MAX_PIPELINE_STAGES;
+	tape->max_stages = tape->min_pipeline;
 	*((unsigned short *) &gcw) = drive->id->config;
 	if (gcw.drq_type == 1)
 		set_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags);
@@ -3577,6 +3610,8 @@
 		drive->name, tape->name, tape->capabilities.speed, (tape->capabilities.buffer_size * 512) / tape->stage_size,
 		tape->stage_size / 1024, tape->max_stages * tape->stage_size / 1024,
 		tape->best_dsc_rw_frequency * 1000 / HZ, drive->using_dma ? ", DMA":"");
+
+	idetape_add_settings(drive);
 }
 
 static int idetape_cleanup (ide_drive_t *drive)
@@ -3605,18 +3640,6 @@
 	return 0;
 }
 
-static int proc_idetape_read_buffer
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	idetape_tape_t	*tape = drive->driver_data;
-	char		*out = page;
-	int		len;
-
-	len = sprintf(out,"%d\n", tape->capabilities.buffer_size / 2);
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
 static int proc_idetape_read_name
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
@@ -3629,72 +3652,11 @@
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
-static int proc_idetape_read_pipeline
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	idetape_tape_t	*tape = drive->driver_data;
-	char		*out = page;
-	int		len;
-
-	len = sprintf(out,"%d\n", tape->max_stages * tape->stage_size / 1024);
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static int proc_idetape_read_speed
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	idetape_tape_t	*tape = drive->driver_data;
-	char		*out = page;
-	int		len;
-
-	len = sprintf(out,"%d\n", tape->capabilities.speed);
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static int proc_idetape_read_stage
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	idetape_tape_t	*tape = drive->driver_data;
-	char		*out = page;
-	int		len;
-
-	len = sprintf(out,"%d\n", tape->stage_size / 1024);
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static int proc_idetape_read_tdsc
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	idetape_tape_t	*tape = drive->driver_data;
-	char		*out = page;
-	int		len;
-
-	len = sprintf(out,"%lu\n", tape->best_dsc_rw_frequency * 1000 / HZ);
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
 static ide_proc_entry_t idetape_proc[] = {
-	{ "buffer", proc_idetape_read_buffer, NULL },
 	{ "name", proc_idetape_read_name, NULL },
-	{ "pipeline", proc_idetape_read_pipeline, NULL },
-	{ "speed", proc_idetape_read_speed, NULL },
-	{ "stage", proc_idetape_read_stage, NULL },
-	{ "tdsc", proc_idetape_read_tdsc, NULL },
 	{ NULL, NULL, NULL }
 };
 
-int idetape_init (void);
-
-static ide_module_t idetape_module = {
-	IDE_DRIVER_MODULE,
-	idetape_init,
-	NULL
-};
-
 /*
  *	IDE subdriver functions, registered with ide.c
  */
@@ -3718,6 +3680,14 @@
 	idetape_proc		/* proc */
 };
 
+int idetape_init (void);
+static ide_module_t idetape_module = {
+	IDE_DRIVER_MODULE,
+	idetape_init,
+	&idetape_driver,
+	NULL
+};
+
 /*
  *	Our character device supporting functions, passed to register_chrdev.
  */
@@ -3751,7 +3721,7 @@
 		for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++ )
 			idetape_chrdevs[minor].drive = NULL;
 
-	if ((drive = ide_scan_devices (ide_tape, NULL, failed++)) == NULL) {
+	if ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) == NULL) {
 		ide_register_module (&idetape_module);
 		MOD_DEC_USE_COUNT;
 		return 0;
@@ -3780,7 +3750,7 @@
 		idetape_setup (drive, tape, minor);
 		idetape_chrdevs[minor].drive = drive;
 		supported++; failed--;
-	} while ((drive = ide_scan_devices (ide_tape, NULL, failed++)) != NULL);
+	} while ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) != NULL);
 	if (!idetape_chrdev_present && !supported) {
 		unregister_chrdev (IDETAPE_MAJOR, "ht");
 	} else
diff -u --recursive --new-file v2.1.78/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.1.78/linux/drivers/block/ide.c	Sun Dec 21 22:36:13 1997
+++ linux/drivers/block/ide.c	Sat Jan 10 10:42:55 1998
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 6.11  December 5, 1997
+ *  linux/drivers/block/ide.c	Version 6.12  January  2, 1998
  *
  *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
  */
@@ -98,6 +98,8 @@
  * Version 6.11		fix probe error in ide_scan_devices()
  *			fix ancient "jiffies" polling bugs
  *			mask all hwgroup interrupts on each irq entry
+ * Version 6.12		integrate ioctl and proc interfaces
+ *			fix parsing of "idex=" command line parameter
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -151,7 +153,7 @@
 /*
  * ide_modules keeps track of the available IDE chipset/probe/driver modules.
  */
-static ide_module_t *ide_modules = NULL;
+ide_module_t *ide_modules = NULL;
 
 /*
  * This is declared extern in ide.h, for access by other IDE modules:
@@ -809,7 +811,8 @@
 {
 	struct request *rq = HWGROUP(drive)->rq;
 	byte *args = (byte *) rq->buffer;
-	byte test, stat = GET_STAT();
+	byte stat = GET_STAT();
+	int retries = 10;
 
 	ide_sti();
 	if ((stat & DRQ_STAT) && args && args[3]) {
@@ -817,12 +820,11 @@
 		drive->io_32bit = 0;
 		ide_input_data(drive, &args[4], args[3] * SECTOR_WORDS);
 		drive->io_32bit = io_32bit;
-		stat = GET_STAT();
+		while (((stat = GET_STAT()) & BUSY_STAT) && retries--)
+			udelay(100);
 	}
-	test = stat;
-	if (drive->media == ide_cdrom)
-		test = stat &~BUSY_STAT;
-	if (OK_STAT(test,READY_STAT,BAD_STAT))
+
+	if (OK_STAT(stat, READY_STAT, BAD_STAT))
 		ide_end_drive_cmd (drive, stat, GET_ERR());
 	else
 		ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
@@ -901,6 +903,10 @@
 		printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n",
 		 drive->name, args[0], args[1], args[2], args[3]);
 #endif
+		if (args[0] == WIN_SMART) {
+			OUT_BYTE(0x4f, IDE_LCYL_REG);
+			OUT_BYTE(0xc2, IDE_HCYL_REG);
+		}
 		OUT_BYTE(args[2],IDE_FEATURE_REG);
 		ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
 		return;
@@ -1556,6 +1562,22 @@
 	return 0;
 }
 
+int ide_replace_subdriver(ide_drive_t *drive, const char *driver)
+{
+	if (!drive->present || drive->busy || drive->usage)
+		goto abort;
+	if (drive->driver != NULL && DRIVER(drive)->cleanup(drive))
+		goto abort;
+	strncpy(drive->driver_req, driver, 9);
+	ide_init_module(IDE_DRIVER_MODULE);
+	drive->driver_req[0] = 0;
+	ide_init_module(IDE_DRIVER_MODULE);
+	if (DRIVER(drive) && !strcmp(DRIVER(drive)->name, driver))
+		return 0;
+abort:
+	return 1;
+}
+
 void ide_unregister (unsigned int index)
 {
 	struct gendisk *gd, **gdp;
@@ -1697,20 +1719,229 @@
 	return hwif->present ? index : -1;
 }
 
+void ide_add_setting(ide_drive_t *drive, char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
+{
+	ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL;
+
+	while ((*p) && strcmp((*p)->name, name) < 0)
+		p = &((*p)->next);
+	if ((setting = kmalloc(sizeof(*setting), GFP_KERNEL)) == NULL)
+		goto abort;
+	memset(setting, 0, sizeof(*setting));
+	if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL)
+		goto abort;
+	strcpy(setting->name, name);		setting->rw = rw;
+	setting->read_ioctl = read_ioctl;	setting->write_ioctl = write_ioctl;
+	setting->data_type = data_type;		setting->min = min;
+	setting->max = max;			setting->mul_factor = mul_factor;
+	setting->div_factor = div_factor;	setting->data = data;
+	setting->set = set;			setting->next = *p;
+	if (drive->driver)
+		setting->auto_remove = 1;
+	*p = setting;
+	return;
+abort:
+	if (setting)
+		kfree(setting);
+}
+
+void ide_remove_setting(ide_drive_t *drive, char *name)
+{
+	ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting;
+
+	while ((*p) && strcmp((*p)->name, name))
+		p = &((*p)->next);
+	if ((setting = (*p)) == NULL)
+		return;
+	(*p) = setting->next;
+	kfree(setting->name);
+	kfree(setting);
+}
+
+static ide_settings_t *ide_find_setting_by_ioctl(ide_drive_t *drive, int cmd)
+{
+	ide_settings_t *setting = drive->settings;
+
+	while (setting) {
+		if (setting->read_ioctl == cmd || setting->write_ioctl == cmd)
+			break;
+		setting = setting->next;
+	}
+	return setting;
+}
+
+ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name)
+{
+	ide_settings_t *setting = drive->settings;
+
+	while (setting) {
+		if (strcmp(setting->name, name) == 0)
+			break;
+		setting = setting->next;
+	}
+	return setting;
+}
+
+static void auto_remove_settings(ide_drive_t *drive)
+{
+	ide_settings_t *setting;
+repeat:
+	setting = drive->settings;
+	while (setting) {
+		if (setting->auto_remove) {
+			ide_remove_setting(drive, setting->name);
+			goto repeat;
+		}
+		setting = setting->next;
+	}
+}
+
+int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting)
+{
+	if (!(setting->rw & SETTING_READ))
+		return -EINVAL;
+	switch(setting->data_type) {
+		case TYPE_BYTE:
+			return *((u8 *) setting->data);
+		case TYPE_SHORT:
+			return *((u16 *) setting->data);
+		case TYPE_INT:
+		case TYPE_INTA:
+			return *((u32 *) setting->data);
+		default:
+			return -EINVAL;
+	}
+}
+
+int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val)
+{
+	unsigned long flags;
+	int i, rc = 0;
+	u32 *p;
+
+	if (!suser())
+		return -EACCES;
+	if (!(setting->rw & SETTING_WRITE))
+		return -EPERM;
+	if (val < setting->min || val > setting->max)
+		return -EINVAL;
+	save_flags(flags);
+	cli();
+	if (setting->set)
+		rc = setting->set(drive, val);
+	else switch (setting->data_type) {
+		case TYPE_BYTE:
+			*((u8 *) setting->data) = val;
+			break;
+		case TYPE_SHORT:
+			*((u16 *) setting->data) = val;
+			break;
+		case TYPE_INT:
+			*((u32 *) setting->data) = val;
+			break;
+		case TYPE_INTA:
+			p = (u32 *) setting->data;
+			for (i = 0; i < 1 << PARTN_BITS; i++, p++)
+				*p = val;
+			break;
+	}
+	restore_flags(flags);
+	return rc;
+}
+
+static int set_io_32bit(ide_drive_t *drive, int arg)
+{
+	drive->io_32bit = arg;
+#ifdef CONFIG_BLK_DEV_DTC2278
+	if (HWIF(drive)->chipset == ide_dtc2278)
+		HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
+#endif /* CONFIG_BLK_DEV_DTC2278 */
+	return 0;
+}
+
+static int set_using_dma(ide_drive_t *drive, int arg)
+{
+	if (!drive->driver || !DRIVER(drive)->supports_dma)
+		return -EPERM;
+	if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
+		return -EPERM;
+	if (HWIF(drive)->dmaproc(arg ? ide_dma_on : ide_dma_off, drive))
+		return -EIO;
+	return 0;
+}
+
+static int set_pio_mode(ide_drive_t *drive, int arg)
+{
+	struct request rq;
+
+	if (!HWIF(drive)->tuneproc)
+		return -ENOSYS;
+	if (drive->special.b.set_tune)
+		return -EBUSY;
+	ide_init_drive_cmd(&rq);
+	drive->tune_req = (byte) arg;
+	drive->special.b.set_tune = 1;
+	(void) ide_do_drive_cmd (drive, &rq, ide_wait);
+	return 0;
+}
+
+void ide_add_generic_settings(ide_drive_t *drive)
+{
+/*
+ *			drive	setting name		read/write access				read ioctl		write ioctl		data type	min	max				mul_factor	div_factor	data pointer			set function
+ */
+	ide_add_setting(drive,	"io_32bit",		drive->no_io_32bit ? SETTING_READ : SETTING_RW,	HDIO_GET_32BIT,		HDIO_SET_32BIT,		TYPE_BYTE,	0,	1 + (SUPPORT_VLB_SYNC << 1),	1,		1,		&drive->io_32bit,		set_io_32bit);
+	ide_add_setting(drive,	"keepsettings",		SETTING_RW,					HDIO_GET_KEEPSETTINGS,	HDIO_SET_KEEPSETTINGS,	TYPE_BYTE,	0,	1,				1,		1,		&drive->keep_settings,		NULL);
+	ide_add_setting(drive,	"nice1",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->nice1,			NULL);
+	ide_add_setting(drive,	"pio_mode",		SETTING_WRITE,					-1,			HDIO_SET_PIO_MODE,	TYPE_BYTE,	0,	255,				1,		1,		NULL,				set_pio_mode);
+	ide_add_setting(drive,	"slow",			SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->slow,			NULL);
+	ide_add_setting(drive,	"unmaskirq",		drive->no_unmask ? SETTING_READ : SETTING_RW,	HDIO_GET_UNMASKINTR,	HDIO_SET_UNMASKINTR,	TYPE_BYTE,	0,	1,				1,		1,		&drive->unmask,			NULL);
+	ide_add_setting(drive,	"using_dma",		SETTING_RW,					HDIO_GET_DMA,		HDIO_SET_DMA,		TYPE_BYTE,	0,	1,				1,		1,		&drive->using_dma,		set_using_dma);
+}
+
+int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf)
+{
+	struct request rq;
+	byte buffer[4];
+
+	if (!buf)
+		buf = buffer;
+	memset(buf, 0, 4 + SECTOR_WORDS * 4 * sectors);
+	ide_init_drive_cmd(&rq);
+	rq.buffer = buf;
+	*buf++ = cmd;
+	*buf++ = nsect;
+	*buf++ = feature;
+	*buf++ = sectors;
+	return ide_do_drive_cmd(drive, &rq, ide_wait);
+}
+
 static int ide_ioctl (struct inode *inode, struct file *file,
 			unsigned int cmd, unsigned long arg)
 {
 	int err, major, minor;
 	ide_drive_t *drive;
-	unsigned long flags;
 	struct request rq;
 	kdev_t dev;
+	ide_settings_t *setting;
 
 	if (!inode || !(dev = inode->i_rdev))
 		return -EINVAL;
 	major = MAJOR(dev); minor = MINOR(dev);
 	if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
 		return -ENODEV;
+
+	if ((setting = ide_find_setting_by_ioctl(drive, cmd)) != NULL) {
+		if (cmd == setting->read_ioctl) {
+			err = ide_read_setting(drive, setting);
+			return err >= 0 ? put_user(err, (long *) arg) : err;
+		} else {
+			if ((MINOR(inode->i_rdev) & PARTN_MASK))
+				return -EINVAL;
+			return ide_write_setting(drive, setting, arg);
+		}
+	}
+
 	ide_init_drive_cmd (&rq);
 	switch (cmd) {
 		case HDIO_GETGEO:
@@ -1730,54 +1961,13 @@
 			invalidate_buffers(inode->i_rdev);
 			return 0;
 
-		case BLKRASET:
-			if (!suser()) return -EACCES;
-			if(arg > 0xff) return -EINVAL;
-			read_ahead[MAJOR(inode->i_rdev)] = arg;
-			return 0;
-
-		case BLKRAGET:
-			return put_user(read_ahead[MAJOR(inode->i_rdev)], (long *) arg);
-
 	 	case BLKGETSIZE:   /* Return device size */
 			return put_user(drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects, (long *) arg);
 
-		case BLKFRASET:
-			if (!suser()) return -EACCES;
-			max_readahead[major][minor] = arg;
-			return 0;
-
-		case BLKFRAGET:
-			return put_user(max_readahead[major][minor], (long *) arg);
-
-		case BLKSECTSET:
-			if (!suser()) return -EACCES;
-			if (!arg || arg > 0xff) return -EINVAL;
-			max_sectors[major][minor] = arg;
-			return 0;
-
-		case BLKSECTGET:
-			return put_user(max_sectors[major][minor], (long *) arg);
-
 		case BLKRRPART: /* Re-read partition tables */
 			if (!suser()) return -EACCES;
 			return ide_revalidate_disk(inode->i_rdev);
 
-		case HDIO_GET_KEEPSETTINGS:
-			return put_user(drive->keep_settings, (long *) arg);
-
-		case HDIO_GET_UNMASKINTR:
-			return put_user(drive->unmask, (long *) arg);
-
-		case HDIO_GET_DMA:
-			return put_user(drive->using_dma, (long *) arg);
-
-		case HDIO_GET_32BIT:
-			return put_user(drive->io_32bit, (long *) arg);
-
-		case HDIO_GET_MULTCOUNT:
-			return put_user(drive->mult_count, (long *) arg);
-
 		case HDIO_GET_IDENTITY:
 			if (MINOR(inode->i_rdev) & PARTN_MASK)
 				return -EINVAL;
@@ -1792,99 +1982,13 @@
 #endif
 			return 0;
 
-		case HDIO_GET_NOWERR:
-			return put_user(drive->bad_wstat == BAD_R_STAT, (long *) arg);
-
 		case HDIO_GET_NICE:
-		{
-			long nice = 0;
-
-			nice |= drive->dsc_overlap << IDE_NICE_DSC_OVERLAP;
-			nice |= drive->atapi_overlap << IDE_NICE_ATAPI_OVERLAP;
-			nice |= drive->nice0 << IDE_NICE_0;
-			nice |= drive->nice1 << IDE_NICE_1;
-			nice |= drive->nice2 << IDE_NICE_2;
-			return put_user(nice, (long *) arg);
-		}
-
-		case HDIO_SET_DMA:
-			if (!suser()) return -EACCES;
-			if (drive->driver != NULL && !DRIVER(drive)->supports_dma)
-				return -EPERM;
-			if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
-				return -EPERM;
-		case HDIO_SET_KEEPSETTINGS:
-		case HDIO_SET_UNMASKINTR:
-		case HDIO_SET_NOWERR:
-			if (arg > 1)
-				return -EINVAL;
-		case HDIO_SET_32BIT:
-			if (!suser()) return -EACCES;
-			if ((MINOR(inode->i_rdev) & PARTN_MASK))
-				return -EINVAL;
-			save_flags(flags);
-			cli();
-			switch (cmd) {
-				case HDIO_SET_DMA:
-					if (!(HWIF(drive)->dmaproc)) {
-						restore_flags(flags);
-						return -EPERM;
-					}
-					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;
-					break;
-				case HDIO_SET_UNMASKINTR:
-					if (arg && drive->no_unmask) {
-						restore_flags(flags);
-						return -EPERM;
-					}
-					drive->unmask = arg;
-					break;
-				case HDIO_SET_NOWERR:
-					drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
-					break;
-				case HDIO_SET_32BIT:
-					if (arg > (1 + (SUPPORT_VLB_SYNC<<1))) {
-						restore_flags(flags);
-						return -EINVAL;
-					}
-					if (arg && drive->no_io_32bit) {
-						restore_flags(flags);
-						return -EPERM;
-					}
-					drive->io_32bit = arg;
-#ifdef CONFIG_BLK_DEV_DTC2278
-					if (HWIF(drive)->chipset == ide_dtc2278)
-						HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
-#endif /* CONFIG_BLK_DEV_DTC2278 */
-					break;
-			}
-			restore_flags(flags);
-			return 0;
-
-		case HDIO_SET_MULTCOUNT:
-			if (!suser()) return -EACCES;
-			if (MINOR(inode->i_rdev) & PARTN_MASK)
-				return -EINVAL;
-			if (drive->id && arg > drive->id->max_multsect)
-				return -EINVAL;
-			save_flags(flags);
-			cli();
-			if (drive->special.b.set_multmode) {
-				restore_flags(flags);
-				return -EBUSY;
-			}
-			drive->mult_req = arg;
-			drive->special.b.set_multmode = 1;
-			restore_flags(flags);
-			(void) ide_do_drive_cmd (drive, &rq, ide_wait);
-			return (drive->mult_count == arg) ? 0 : -EIO;
-
+			return put_user(drive->dsc_overlap	<<	IDE_NICE_DSC_OVERLAP	|
+					drive->atapi_overlap	<<	IDE_NICE_ATAPI_OVERLAP	|
+					drive->nice0		<< 	IDE_NICE_0		|
+					drive->nice1		<<	IDE_NICE_1		|
+					drive->nice2		<<	IDE_NICE_2,
+					(long *) arg);
 		case HDIO_DRIVE_CMD:
 		{
 			byte args[4], *argbuf = args;
@@ -1901,31 +2005,13 @@
 					return -ENOMEM;
 				memcpy(argbuf, args, 4);
 			}
-			rq.buffer = argbuf;
-			err = ide_do_drive_cmd(drive, &rq, ide_wait);
+			err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf);
 			if (copy_to_user((void *)arg, argbuf, argsize))
 				err = -EFAULT;
 			if (argsize > 4)
 				kfree(argbuf);
 			return err;
 		}
-		case HDIO_SET_PIO_MODE:
-			if (!suser()) return -EACCES;
-			if (MINOR(inode->i_rdev) & PARTN_MASK)
-				return -EINVAL;
-			if (!HWIF(drive)->tuneproc)
-				return -ENOSYS;
-			save_flags(flags);
-			cli();
-			if (drive->special.b.set_tune) {
-				restore_flags(flags);
-				return -EBUSY;
-			}
-			drive->tune_req = (byte) arg;
-			drive->special.b.set_tune = 1;
-			restore_flags(flags);
-			(void) ide_do_drive_cmd (drive, &rq, ide_wait);
-			return 0;
 
 		case HDIO_SCAN_HWIF:
 		{
@@ -2223,7 +2309,7 @@
 		if (i > 0 || i <= -7) {			/* is parameter a chipset name? */
 			if (hwif->chipset != ide_unknown)
 				goto bad_option;	/* chipset already specified */
-			if (i != -7 && hw != 0)
+			if (i <= -7 && hw != 0)
 				goto bad_hwif;		/* chipset drivers are for "ide0=" only */
 			if (ide_hwifs[hw^1].chipset != ide_unknown)
 				goto bad_option;	/* chipset for 2nd port already specified */
@@ -2578,7 +2664,7 @@
 	if (d->special == NULL)		d->special = default_special;
 }
 
-ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n)
+ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n)
 {
 	unsigned int unit, index, i;
 
@@ -2588,12 +2674,15 @@
 search:
 	for (index = 0, i = 0; index < MAX_HWIFS; ++index) {
 		ide_hwif_t *hwif = &ide_hwifs[index];
-		if (hwif->present) {
-			for (unit = 0; unit < MAX_DRIVES; ++unit) {
-				ide_drive_t *drive = &hwif->drives[unit];
-				if (drive->present && drive->media == media && drive->driver == driver && ++i > n)
-					return drive;
-			}
+		if (!hwif->present)
+			continue;
+		for (unit = 0; unit < MAX_DRIVES; ++unit) {
+			ide_drive_t *drive = &hwif->drives[unit];
+			char *req = drive->driver_req;
+			if (*req && !strstr(name, req))
+				continue;
+			if (drive->present && drive->media == media && drive->driver == driver && ++i > n)
+				return drive;
 		}
 	}
 	return NULL;
@@ -2642,6 +2731,7 @@
 	}
 	ide_remove_proc_entries(drive, DRIVER(drive)->proc);
 	ide_remove_proc_entries(drive, generic_subdriver_entries);
+	auto_remove_settings(drive);
 	drive->driver = NULL;
 	restore_flags(flags);
 	return 0;
@@ -2735,6 +2825,8 @@
 EXPORT_SYMBOL(ide_stall_queue);
 EXPORT_SYMBOL(ide_add_proc_entries);
 EXPORT_SYMBOL(ide_remove_proc_entries);
+EXPORT_SYMBOL(ide_add_setting);
+EXPORT_SYMBOL(ide_remove_setting);
 EXPORT_SYMBOL(proc_ide_read_geometry);
 
 EXPORT_SYMBOL(ide_register);
diff -u --recursive --new-file v2.1.78/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.1.78/linux/drivers/block/ide.h	Fri Jan  2 14:37:01 1998
+++ linux/drivers/block/ide.h	Mon Jan 12 16:43:36 1998
@@ -206,23 +206,23 @@
 	special_t	special;	/* special action flags */
 	unsigned present	: 1;	/* drive is physically present */
 	unsigned noprobe 	: 1;	/* from:  hdx=noprobe */
-	unsigned keep_settings  : 1;	/* restore settings after drive reset */
+	byte     keep_settings;		/* restore settings after drive reset */
 	unsigned busy		: 1;	/* currently doing revalidate_disk() */
 	unsigned removable	: 1;	/* 1 if need to do check_media_change */
-	unsigned using_dma	: 1;	/* disk is using dma for read/write */
+	byte     using_dma;		/* disk is using dma for read/write */
 	unsigned forced_geom	: 1;	/* 1 if hdx=c,h,s was given at boot */
-	unsigned unmask		: 1;	/* flag: okay to unmask other irqs */
+	byte     unmask;		/* flag: okay to unmask other irqs */
 	unsigned no_unmask	: 1;	/* disallow setting unmask bit */
 	unsigned no_io_32bit	: 1;	/* disallow enabling 32bit I/O */
 	unsigned nobios		: 1;	/* flag: do not probe bios for drive */
-	unsigned slow		: 1;	/* flag: slow data port */
+	byte     slow;			/* flag: slow data port */
 	unsigned autotune	: 2;	/* 1=autotune, 2=noautotune, 0=default */
 	unsigned revalidate	: 1;	/* request revalidation */
-	unsigned bswap		: 1;	/* flag: byte swap data */
-	unsigned dsc_overlap	: 1;	/* flag: DSC overlap */
+	byte     bswap;			/* flag: byte swap data */
+	byte     dsc_overlap;		/* flag: DSC overlap */
 	unsigned atapi_overlap	: 1;	/* flag: ATAPI overlap (not supported) */
 	unsigned nice0		: 1;	/* flag: give obvious excess bandwidth */
-	unsigned nice1		: 1;	/* flag: give potential excess bandwidth */
+	byte     nice1;			/* flag: give potential excess bandwidth */
 	unsigned nice2		: 1;	/* flag: give a share in our own bandwidth */
 #if FAKE_FDISK_FOR_EZDRIVE
 	unsigned remap_0_to_1	: 1;	/* flag: partitioned with ezdrive */
@@ -236,6 +236,7 @@
 	byte 		tune_req;	/* requested drive tuning setting */
 	byte		io_32bit;	/* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
 	byte		bad_wstat;	/* used for ignoring WRERR_STAT */
+	byte		nowerr;		/* used for ignoring WRERR_STAT */
 	byte		sect0;		/* offset of first sector for DM6:DDO */
 	byte 		usage;		/* current "open()" count for drive */
 	byte 		head;		/* "real" number of heads */
@@ -253,6 +254,8 @@
 	void 		*driver;	/* (ide_driver_t *) */
 	void		*driver_data;	/* extra driver data */
 	struct proc_dir_entry *proc;	/* /proc/ide/ directory entry */
+	void		*settings;	/* /proc/ide/ drive settings */
+	char		driver_req[10];	/* requests specific driver */
 	} ide_drive_t;
 
 /*
@@ -362,6 +365,43 @@
 	} ide_hwgroup_t;
 
 /*
+ * configurable drive settings
+ */
+
+#define TYPE_INT	0
+#define TYPE_INTA	1
+#define TYPE_BYTE	2
+#define TYPE_SHORT	3
+
+#define SETTING_READ	(1 << 0)
+#define SETTING_WRITE	(1 << 1)
+#define SETTING_RW	(SETTING_READ | SETTING_WRITE)
+
+typedef int (ide_procset_t)(ide_drive_t *, int);
+typedef struct ide_settings_s {
+	char			*name;
+	int			rw;
+	int			read_ioctl;
+	int			write_ioctl;
+	int			data_type;
+	int			min;
+	int			max;
+	int			mul_factor;
+	int			div_factor;
+	void			*data;
+	ide_procset_t		*set;
+	int			auto_remove;
+	struct ide_settings_s	*next;
+} ide_settings_t;
+
+void ide_add_setting(ide_drive_t *drive, char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set);
+void ide_remove_setting(ide_drive_t *drive, char *name);
+ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name);
+int ide_read_setting(ide_drive_t *t, ide_settings_t *setting);
+int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val);
+void ide_add_generic_settings(ide_drive_t *drive);
+
+/*
  * /proc/ide interface
  */
 typedef struct {
@@ -407,6 +447,7 @@
 typedef void	(ide_pre_reset_proc)(ide_drive_t *);
 typedef unsigned long (ide_capacity_proc)(ide_drive_t *);
 typedef void	(ide_special_proc)(ide_drive_t *);
+typedef void	(ide_setting_proc)(ide_drive_t *);
 
 typedef struct ide_driver_s {
 	const char			*name;
@@ -442,6 +483,7 @@
 typedef struct ide_module_s {
 	int				type;
 	ide_module_init_proc		*init;
+	void				*info;
 	struct ide_module_s		*next;
 } ide_module_t;
 
@@ -455,6 +497,7 @@
  */
 #ifndef _IDE_C
 extern	ide_hwif_t	ide_hwifs[];		/* master data repository */
+extern  ide_module_t	*ide_modules;
 #endif
 
 /*
@@ -588,6 +631,11 @@
 void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err);
 
 /*
+ * Issue ATA command and wait for completion.
+ */
+int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf);
+
+/*
  * ide_system_bus_speed() returns what we think is the system VESA/PCI
  * bus speed (in MHz).  This is used for calculating interface PIO timings.
  * The default is 40 for known PCI systems, 50 otherwise.
@@ -651,9 +699,10 @@
 
 int ide_register_module (ide_module_t *module);
 void ide_unregister_module (ide_module_t *module);
-ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n);
+ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n);
 int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version);
 int ide_unregister_subdriver (ide_drive_t *drive);
+int ide_replace_subdriver(ide_drive_t *drive, const char *driver);
 
 #ifdef CONFIG_BLK_DEV_IDEPCI
 unsigned long ide_find_free_region (unsigned short size) __init;
diff -u --recursive --new-file v2.1.78/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.78/linux/drivers/char/Config.in	Fri Jan  2 14:37:02 1998
+++ linux/drivers/char/Config.in	Mon Jan 12 14:46:16 1998
@@ -110,7 +110,10 @@
 fi
 tristate 'Video For Linux' CONFIG_VIDEO_DEV
 dep_tristate 'BT848 Video For Linux' CONFIG_VIDEO_BT848 $CONFIG_VIDEO_DEV
-dep_tristate 'Quickcam BW Video For Linux' CONFIG_VIDEO_BWQCAM $CONFIG_VIDEO_DEV
+if [ "$CONFIG_PARPORT" != "n" ]; then
+  dep_tristate 'Quickcam BW Video For Linux' CONFIG_VIDEO_BWQCAM $CONFIG_VIDEO_DEV
+  dep_tristate 'Colour QuickCam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV
+fi
 dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV
 tristate '/dev/nvram support' CONFIG_NVRAM
 tristate 'PC joystick support' CONFIG_JOYSTICK
diff -u --recursive --new-file v2.1.78/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.1.78/linux/drivers/char/Makefile	Sun Dec 21 22:36:13 1997
+++ linux/drivers/char/Makefile	Mon Jan 12 14:46:16 1998
@@ -317,6 +317,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_VIDEO_CQCAM),y)
+L_OBJS += c-qcam.o
+else
+  ifeq ($(CONFIG_VIDEO_CQCAM),m)
+    M_OBJS += c-qcam.o
+  endif
+endif
+
 ifeq ($(CONFIG_VIDEO_PMS),y)
 L_OBJS += pms.o
 else
diff -u --recursive --new-file v2.1.78/linux/drivers/char/acquirewdt.c linux/drivers/char/acquirewdt.c
--- v2.1.78/linux/drivers/char/acquirewdt.c	Wed Dec 10 11:12:43 1997
+++ linux/drivers/char/acquirewdt.c	Mon Jan 12 15:12:42 1998
@@ -46,7 +46,6 @@
  
 #define WDT_STOP 0x43
 #define WDT_START 0x443
-#define WATCHDOG_MINOR 130
 
 #define WD_TIMO (100*60)		/* 1 minute */
 
@@ -181,7 +180,7 @@
 static struct miscdevice acq_miscdev=
 {
 	WATCHDOG_MINOR,
-	"Acquire WDT",
+	"watchdog",
 	&acq_fops
 };
 
diff -u --recursive --new-file v2.1.78/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.1.78/linux/drivers/char/bttv.c	Tue Jan  6 09:37:33 1998
+++ linux/drivers/char/bttv.c	Mon Jan 12 14:46:27 1998
@@ -523,21 +523,21 @@
  
 static struct tunertype tuners[] = {
 	{"Temic PAL", TEMIC, PAL,
-		16*140.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2},
+		16*140.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2, 623},
 	{"Philips PAL_I", Philips, PAL_I,
-		16*140.25,16*463.25,0x00,0x00,0x00,0x00,0x00},
+		16*140.25,16*463.25,0x00,0x00,0x00,0x00,0x00, 623},
 	{"Philips NTSC", Philips, NTSC,
-		16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,0xc0},
+		16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,0xc0, 732},
 	{"Philips SECAM", Philips, SECAM,
-		16*168.25,16*447.25,0xA3,0x93,0x33,0x8e,0xc0},
+		16*168.25,16*447.25,0xA3,0x93,0x33,0x8e,0xc0, 623},
 	{"NoTuner", NoTuner, NOTUNER,
-		0        ,0        ,0x00,0x00,0x00,0x00,0x00},
+		0        ,0        ,0x00,0x00,0x00,0x00,0x00, 0},
 	{"Philips PAL", Philips, PAL,
-		16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,0xc0},
+		16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,0xc0, 623},
 	{"Temic NTSC", TEMIC, NTSC,
-		16*157.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2},
+		16*157.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2, 732},
 	{"TEMIC PAL_I", TEMIC, PAL_I,
-		0        ,0        ,0x00,0x00,0x00,0x00,0xc2},
+		0        ,0        ,0x00,0x00,0x00,0x00,0xc2, 623},
 }; 
 
 /*
@@ -553,6 +553,7 @@
 
 	audio(btv, AUDIO_MUTE);
 	udelay(AUDIO_MUTE_DELAY);
+	
 	if (freq < tun->thresh1) 
 		config = tun->VHF_L;
 	else if (freq < tun->thresh2) 
@@ -560,9 +561,17 @@
 	else
 		config = tun->UHF;
 
-	div=freq+623; /* div=((freq+16*38.9));*/
-  
+	if(freq < tun->thresh1)
+		config = tun->VHF_L;
+	else if(freq < tun->thresh2)
+		config = tun->VHF_H;
+	else
+		config=tun->UHF;
+		
+	div=freq+tun->IFPCoff;
+	  
 	div&=0x7fff;
+	
 	if (I2CWrite(btv, btv->tuneradr, (div>>8)&0x7f, div&0xff, 1)<0)
 		return;
 	I2CWrite(btv, btv->tuneradr, tun->config, config, 1);
diff -u --recursive --new-file v2.1.78/linux/drivers/char/bw-qcam.c linux/drivers/char/bw-qcam.c
--- v2.1.78/linux/drivers/char/bw-qcam.c	Tue Jan  6 09:37:33 1998
+++ linux/drivers/char/bw-qcam.c	Mon Jan 12 14:46:16 1998
@@ -1,7 +1,10 @@
 /*
  *    QuickCam Driver For Video4Linux.
  *
+ *	This version only works as a module.
+ *
  *	Video4Linux conversion work by Alan Cox.
+ *	Parport compatibility by Phil Blundell.
  */
 
 /* qcam-lib.c -- Library for programming with the Connectix QuickCam.
@@ -44,41 +47,41 @@
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
+#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/malloc.h>
 #include <linux/mm.h>
-#include <linux/ioport.h>
-#include <asm/io.h>
+#include <linux/parport.h>
 #include <linux/sched.h>
-#include <linux/videodev.h>
 #include <linux/version.h>
+#include <linux/videodev.h>
 #include <asm/uaccess.h>
 
 #include "bw-qcam.h"
 
 extern __inline__ int read_lpstatus(struct qcam_device *q)
 {
-	return inb_p(q->port+1);
+	return parport_read_status(q->pport);
 }
 
 extern __inline__ int read_lpcontrol(struct qcam_device *q)
 {
-	return inb_p(q->port+2);
+	return parport_read_control(q->pport);
 }
 
 extern __inline__ int read_lpdata(struct qcam_device *q)
 {
-	return inb_p(q->port);
+	return parport_read_data(q->pport);
 }
 
 extern __inline__ void write_lpdata(struct qcam_device *q, int d)
 {
-	outb_p(d, q->port);
+	parport_write_data(q->pport, d);
 }
 
-extern __inline__ void write_lpcontrol(struct qcam_device *q,int d)
+extern __inline__ void write_lpcontrol(struct qcam_device *q, int d)
 {
-	outb(d, q->port+2);
+	parport_write_control(q->pport, d);
 }
 
 static int qc_waithand(struct qcam_device *q, int val);
@@ -122,21 +125,25 @@
 /* Initialize the QuickCam driver control structure.  This is where
  * defaults are set for people who don't have a config file.*/
 
-static struct qcam_device *qcam_init(int port)
+static struct qcam_device *qcam_init(struct parport *port)
 {
 	struct qcam_device *q;
 	
-	if(check_region(port,3))
-	{
-		printk(KERN_ERR "qcam: I/O port 0x%03X in use.\n", port);
+	q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL);
+
+	q->pport = port;
+	q->pdev = parport_register_device(port, "bw-qcam", NULL, NULL,
+					  NULL, 0, NULL);
+	if (q->pdev == NULL) 
+	{
+		printk(KERN_ERR "bw-qcam: couldn't register for %s.\n",
+		       port->name);
+		kfree(q);
 		return NULL;
 	}
 	
-	q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL);
-	
 	memcpy(&q->vdev, &qcam_template, sizeof(qcam_template));
 
-	q->port = port;		/* Port 0 == Autoprobe */
 	q->port_mode = (QC_ANY | QC_NOTSET);
 	q->width = 320;
 	q->height = 240;
@@ -295,7 +302,7 @@
 
 	/* Be liberal in what you accept...  */
 
-	if (count > 20 && count < 250)
+	if (count > 30 && count < 200)
 		return 1;	/* found */
 	else
 		return 0;	/* not found */
@@ -742,7 +749,9 @@
 			qcam->bpp = p.depth;
 			
 			qc_setscanmode(qcam);
+			parport_claim_or_block(qcam->pdev);
 			qc_set(qcam);
+			parport_release(qcam->pdev);
 			return 0;
 		}
 		case VIDIOCSWIN:
@@ -816,9 +825,11 @@
 {
 	struct qcam_device *qcam=(struct qcam_device *)v;
 	int len;
+	parport_claim_or_block(qcam->pdev);
 	/* Probably should have a semaphore against multiple users */
 	qc_reset(qcam);
 	len=qc_capture(qcam, buf,count);
+	parport_release(qcam->pdev);
 	return len;
 }
 
@@ -840,72 +851,83 @@
 	0
 };
 
+#define MAX_CAMS 4
+static struct qcam_device *qcams[MAX_CAMS];
+static unsigned int num_cams = 0;
 
-#ifdef MODULE
-
-int io=0x378;
-
-MODULE_PARM(io,"i");
+int init_bwqcam(struct parport *port)
+{
+	struct qcam_device *qcam;
 
-static struct qcam_device *qcam;
+	if (num_cams == MAX_CAMS)
+	{
+		printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS);
+		return -ENOSPC;
+	}
 
-int init_module(void)
-{
-	qcam=qcam_init(io);
+	qcam=qcam_init(port);
 	if(qcam==NULL)
 		return -ENODEV;
 		
+	parport_claim_or_block(qcam->pdev);
+
 	qc_reset(qcam);
 	
 	if(qc_detect(qcam)==0)
 	{
+		parport_release(qcam->pdev);
+		parport_unregister_device(qcam->pdev);
 		kfree(qcam);
-		printk(KERN_ERR "bw_qcam: No quickcam detected at 0x%03X\n", io);
 		return -ENODEV;
 	}
 	qc_calibrate(qcam);
+
+	parport_release(qcam->pdev);
 	
-	printk(KERN_INFO "Connectix Quickcam at 0x%03X\n", qcam->port);
+	printk(KERN_INFO "Connectix Quickcam on %s\n", qcam->pport->name);
 	
 	if(video_register_device(&qcam->vdev)==-1)
+	{
+		parport_unregister_device(qcam->pdev);
+		kfree(qcam);
 		return -ENODEV;
+	}
+
+	qcams[num_cams++] = qcam;
+
 	return 0;
 }
 
-void cleanup_module(void)
+void close_bwqcam(struct qcam_device *qcam)
 {
 	video_unregister_device(&qcam->vdev);
+	parport_unregister_device(qcam->pdev);
 	kfree(qcam);
 }
 
-#else
+#ifdef MODULE
+int init_module(void)
+{
+	struct parport *port;
+
+	for (port = parport_enumerate(); port; port=port->next)
+		init_bwqcam(port);
 
-void init_bw_qcams(void)
+	return (num_cams)?0:-ENODEV;
+}
+
+void cleanup_module(void)
 {
-	int io_ports[3]={0x278,0x378, 0x3BC};
-	struct qcam_device *qcam;
-	int i;
-	
-	for(i=0;i<3;i++)
-	{
-		qcam=qcam_init(io_ports[i]);
-		if(qcam==NULL)
-			continue;
-		
-		qc_reset(qcam);
-	
-		if(qc_detect(qcam)==0)
-		{
-			kfree(qcam);
-			continue;
-		}
-		qc_calibrate(qcam);
-	
-		printk(KERN_INFO "Connectix Quickcam at 0x%03X\n", qcam->port);
-	
-		if(video_register_device(&qcam->vdev)==-1)
-			return -ENODEV;
-	}
+	unsigned int i;
+	for (i = 0; i < num_cams; i++)
+		close_bwqcam(qcams[i]);
 }
+#else
+__initfunc(int init_bwqcams(struct video_init *unused))
+{
+	struct parport *port;
 
+	for (port = parport_enumerate(); port; port=port->next)
+		init_bwqcam(port);
+}
 #endif
diff -u --recursive --new-file v2.1.78/linux/drivers/char/bw-qcam.h linux/drivers/char/bw-qcam.h
--- v2.1.78/linux/drivers/char/bw-qcam.h	Fri Jan  2 14:37:02 1998
+++ linux/drivers/char/bw-qcam.h	Mon Jan 12 14:46:16 1998
@@ -50,11 +50,12 @@
 
 struct qcam_device {
 	struct video_device vdev;
+	struct pardevice *pdev;
+	struct parport *pport;
 	int width, height;
 	int bpp;
 	int mode;
 	int contrast, brightness, whitebal;
-	int port;
 	int port_mode;
 	int transfer_scale;
 	int top, left;
diff -u --recursive --new-file v2.1.78/linux/drivers/char/c-qcam.c linux/drivers/char/c-qcam.c
--- v2.1.78/linux/drivers/char/c-qcam.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/c-qcam.c	Mon Jan 12 14:46:16 1998
@@ -0,0 +1,775 @@
+/*
+ *	Video4Linux: Colour QuickCam driver
+ *
+ *	Philip Blundell <philb@gnu.org>, December 30 1997
+ *
+ *	Largely untested (seems to work at 24bpp with a bidirectional port,
+ *	though). 
+ */
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/parport.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/videodev.h>
+#include <asm/uaccess.h>
+
+#include "c-qcam.h"
+
+static __inline__ void qcam_set_ack(struct qcam_device *qcam, unsigned int i)
+{
+	/* note: the QC specs refer to the PCAck pin by voltage, not
+	   software level.  PC ports have builtin inverters. */
+	parport_frob_control(qcam->pport, 8, i?8:0);
+}
+
+static __inline__ unsigned int qcam_ready1(struct qcam_device *qcam)
+{
+	return (parport_read_status(qcam->pport) & 0x8)?1:0;
+
+}
+
+static __inline__ unsigned int qcam_ready2(struct qcam_device *qcam)
+{
+	return (parport_read_data(qcam->pport) & 0x1)?1:0;
+}
+
+static inline unsigned int qcam_await_ready1(struct qcam_device *qcam, int value)
+{
+	unsigned long oldjiffies = jiffies;
+	unsigned int i;
+
+	for (oldjiffies = jiffies; (jiffies - oldjiffies) < (HZ/25); )
+		if (qcam_ready1(qcam) == value)
+			return 0;
+
+	/* If the camera didn't respond within 1/25 second, poll slowly 
+	   for a while. */
+	for (i = 0; i < 50; i++)
+	{
+		if (qcam_ready1(qcam) == value)
+			return 0;
+		current->state=TASK_INTERRUPTIBLE;
+		current->timeout = jiffies+HZ/10;
+		schedule();
+	}
+
+	/* Probably somebody pulled the plug out.  Not much we can do. */
+	printk(KERN_ERR "c-qcam: ready1 timeout (%d) %x %x\n", value,
+	       parport_read_status(qcam->pport),
+	       parport_read_control(qcam->pport));
+	return 1;
+}
+
+static inline unsigned int qcam_await_ready2(struct qcam_device *qcam, int value)
+{
+	unsigned long oldjiffies = jiffies;
+	unsigned int i;
+
+	for (oldjiffies = jiffies; (jiffies - oldjiffies) < (HZ/25); )
+		if (qcam_ready2(qcam) == value)
+			return 0;
+
+	/* If the camera didn't respond within 1/25 second, poll slowly 
+	   for a while. */
+	for (i = 0; i < 50; i++)
+	{
+		if (qcam_ready2(qcam) == value)
+			return 0;
+		current->state=TASK_INTERRUPTIBLE;
+		current->timeout = jiffies+HZ/10;
+		schedule();
+	}
+
+	/* Probably somebody pulled the plug out.  Not much we can do. */
+	printk(KERN_ERR "c-qcam: ready2 timeout (%d) %x %x %x\n", value,
+	       parport_read_status(qcam->pport),
+	       parport_read_control(qcam->pport),
+	       parport_read_data(qcam->pport));
+	return 1;
+}
+
+static inline int qcam_read_data(struct qcam_device *qcam)
+{
+	unsigned int idata;
+	qcam_set_ack(qcam, 0);
+	if (qcam_await_ready1(qcam, 1)) return -1;
+	idata = parport_read_status(qcam->pport) & 0xf0;
+	qcam_set_ack(qcam, 1);
+	if (qcam_await_ready1(qcam, 0)) return -1;
+	idata |= (parport_read_status(qcam->pport) >> 4);
+	return idata;
+}
+
+static int qcam_write_data(struct qcam_device *qcam, unsigned int data)
+{
+	unsigned int idata;
+	parport_write_data(qcam->pport, data);
+	idata = qcam_read_data(qcam);
+	if (data != idata) 
+	{
+		printk(KERN_WARNING "cqcam: sent %x but received %x\n", data, 
+		       idata);
+		return 1;
+	} 
+	return 0;
+}
+
+static inline int qcam_set(struct qcam_device *qcam, unsigned int cmd, unsigned int data)
+{
+	if (qcam_write_data(qcam, cmd))
+		return -1;
+	if (qcam_write_data(qcam, data))
+		return -1;
+	return 0;
+}
+
+static inline int qcam_get(struct qcam_device *qcam, unsigned int cmd)
+{
+	if (qcam_write_data(qcam, cmd))
+		return -1;
+	return qcam_read_data(qcam);
+}
+
+static int qc_detect(struct qcam_device *qcam)
+{
+	unsigned int stat, ostat, i, count = 0;
+
+	parport_write_control(qcam->pport, 0xc);
+
+	/* look for a heartbeat */
+	ostat = stat = parport_read_status(qcam->pport);
+	for (i=0; i<250; i++) 
+	{
+		udelay(1000);
+		stat = parport_read_status(qcam->pport);
+		if (ostat != stat) 
+		{
+			if (++count >= 3) return 1;
+			ostat = stat;
+		}
+	}
+
+	/* no (or flatline) camera, give up */
+	return 0;
+}
+
+static void qc_reset(struct qcam_device *qcam)
+{
+	parport_write_control(qcam->pport, 0xc);
+	parport_write_control(qcam->pport, 0x8);
+	udelay(1000);
+	parport_write_control(qcam->pport, 0xc);
+	udelay(1000);          
+}
+
+/* Reset the QuickCam and program for brightness, contrast,
+ * white-balance, and resolution. */
+
+static void qc_setup(struct qcam_device *q)
+{
+	qc_reset(q);
+
+	/* Set the brightness.  */
+       	qcam_set(q, 11, q->brightness);
+
+	/* Set the height.  */
+	qcam_set(q, 17, q->height);
+
+	/* Set the width.  */
+	qcam_set(q, 19, q->width/2);
+
+	/* Set top and left.  */
+	qcam_set(q, 0xd, q->top);
+	qcam_set(q, 0xf, q->left);
+
+	/* Set contrast and white balance.  */
+	qcam_set(q, 0x19, q->contrast);
+	qcam_set(q, 0x1f, q->whitebal);
+	
+	/* Set the speed.  */
+	qcam_set(q, 45, 2);
+}
+
+/* Read some bytes from the camera and put them in the buffer. 
+   nbytes should be a multiple of 3, because bidirectional mode gives
+   us three bytes at a time.  */
+
+static unsigned int qcam_read_bytes(struct qcam_device *q, unsigned char *buf, unsigned int nbytes)
+{
+	unsigned int bytes = 0;
+	qcam_set_ack(q, 0);
+	if (q->bidirectional)
+	{
+		/* It's a bidirectional port */
+		while (bytes < nbytes)
+		{
+			unsigned int lo1, hi1, lo2, hi2;
+			if (qcam_await_ready2(q, 1)) return bytes;
+			lo1 = parport_read_data(q->pport) >> 1;
+			hi1 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10;
+			qcam_set_ack(q, 1);
+			if (qcam_await_ready2(q, 0)) return bytes;
+			lo2 = parport_read_data(q->pport) >> 1;
+			hi2 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10;
+			qcam_set_ack(q, 0);
+			buf[bytes++] = (lo1 | ((hi1 & 1)<<7));
+			buf[bytes++] = ((hi1 & 0x1e)<<3) | ((hi2 & 0x1e)>>1);
+			buf[bytes++] = (lo2 | ((hi2 & 1)<<7));
+		}
+	}
+	else
+	{
+		/* It's a unidirectional port */
+		while (bytes < nbytes)
+		{
+			unsigned int hi, lo;
+			if (qcam_await_ready1(q, 1)) return bytes;
+			hi = (parport_read_status(q->pport) & 0xf0);
+			qcam_set_ack(q, 1);
+			if (qcam_await_ready1(q, 0)) return bytes;
+			lo = (parport_read_status(q->pport) & 0xf0);
+			qcam_set_ack(q, 0);
+			/* flip some bits; cqcam gets this wrong */
+			buf[bytes++] = (hi | lo) ^ 0x88;
+		}
+	}
+	return bytes;
+}
+
+/* Convert the data the camera gives us into the desired output format. 
+   At the moment this is a no-op because read_bytes() does all the 
+   required stuff, for 24bpp at least.  */
+static size_t qcam_munge_buffer(struct qcam_device *q, char *inbuf, size_t inlen, char *outbuf, size_t outlen)
+{
+	size_t outptr = 0;
+	switch (q->bpp)
+	{
+	case 24:
+		while (inlen && (outptr <= (outlen-3)))
+		{
+			unsigned char r, g, b;
+			r = inbuf[0];
+			g = inbuf[1];
+			b = inbuf[2];
+			put_user(r, outbuf+(outptr++));
+			put_user(g, outbuf+(outptr++));
+			put_user(b, outbuf+(outptr++));
+			inlen -= 3;
+			inbuf += 3;
+		}
+		break;
+	default:
+		printk("c-qcam: can't convert this format (%d).\n", q->bpp);
+		return 0;
+	}
+	return outptr;
+}
+
+static long qc_capture(struct qcam_device *q, char *buf, unsigned long len)
+{
+	unsigned int tbpp = 0, tdecimation = 0, lines, pixelsperline, bitsperxfer;
+	unsigned int is_bi_dir = q->bidirectional;
+	size_t wantlen, outptr = 0;
+	char *tmpbuf = kmalloc(768, GFP_KERNEL);
+	if (tmpbuf == NULL)
+	{
+		printk(KERN_ERR "cqcam: couldn't allocate a buffer.\n");
+		return -ENOMEM;
+	}
+
+	/* Wait for camera to become ready */
+	for (;;)
+	{
+		int i = qcam_get(q, 41);
+		if (i == -1) {
+			qc_setup(q);
+			kfree(tmpbuf);
+			return -EIO;
+		}
+		if (i & 0x80)
+			schedule();
+		else
+			break;
+	}
+
+	switch (q->bpp) 
+	{
+	case 24: tbpp = QC_24BPP; break;
+	case 32: tbpp = QC_32BPP; break;
+	case 16: tbpp = QC_16BPP; break;
+	default: printk("qcam: Bad bpp.\n");
+	}
+	switch (q->transfer_scale) {
+	case 1: tdecimation = QC_1_1; break;
+	case 2: tdecimation = QC_2_1; break;
+	case 4: tdecimation = QC_4_1; break;
+	default: printk("qcam: Bad decimation.\n");
+	}
+	
+	qcam_set(q, 7, (tbpp | tdecimation) + ((is_bi_dir)?1:0) + 1);
+	
+	lines = q->height / q->transfer_scale;
+	pixelsperline = q->width / q->transfer_scale;
+	bitsperxfer = (is_bi_dir) ? 24 : 8;
+
+	if (is_bi_dir)
+	{
+		/* Turn the port around */
+		parport_frob_control(q->pport, 0x20, 0x20);
+		udelay(3000);
+		qcam_set_ack(q, 0);
+		if (qcam_await_ready1(q, 1)) {
+			kfree(tmpbuf);
+			qc_setup(q);
+			return -EIO;
+		}
+		qcam_set_ack(q, 1);
+		if (qcam_await_ready1(q, 0)) {
+			kfree(tmpbuf);
+			qc_setup(q);
+			return -EIO;
+		}
+	}
+
+	wantlen = lines * pixelsperline * q->bpp / 8;
+
+	while (wantlen)
+	{
+		size_t t, s, o;
+		s = (wantlen > 768)?768:wantlen;
+		t = qcam_read_bytes(q, tmpbuf, s);
+		if (outptr < len)
+		{
+			o = qcam_munge_buffer(q, tmpbuf, t, buf + outptr, 
+					      len - outptr);
+			outptr += o;
+		}
+		wantlen -= t;
+		if (t < s) 
+			break;
+		if (need_resched)
+			schedule();
+	}
+
+	len = outptr;
+
+	if (wantlen)
+	{
+		printk("qcam: short read.\n");
+		if (is_bi_dir)
+			parport_frob_control(q->pport, 0x20, 0);
+		qc_setup(q);
+		kfree(tmpbuf);
+		return len;
+	}
+
+	if (is_bi_dir)
+	{
+		int l;
+		do {
+			l = qcam_read_bytes(q, tmpbuf, 3);
+			if (need_resched)
+				schedule();
+		} while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e));
+		if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
+			printk("qcam: bad EOF\n");
+		qcam_set_ack(q, 0);
+		if (qcam_await_ready1(q, 1))
+		{
+			printk("qcam: no ack after EOF\n");
+			parport_frob_control(q->pport, 0x20, 0);
+			qc_setup(q);
+			kfree(tmpbuf);
+			return len;
+		}
+		parport_frob_control(q->pport, 0x20, 0);
+		udelay(3000);
+		qcam_set_ack(q, 1);
+		if (qcam_await_ready1(q, 0))
+		{
+			printk("qcam: no ack to port turnaround\n");
+			qc_setup(q);
+			kfree(tmpbuf);
+			return len;
+		}
+	}
+	else
+	{
+		int l;
+		do {
+			l = qcam_read_bytes(q, tmpbuf, 1);
+			if (need_resched)
+				schedule();
+		} while (l && tmpbuf[0] == 0x7e);
+		l = qcam_read_bytes(q, tmpbuf+1, 2);
+		if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
+			printk("qcam: bad EOF\n");
+	}
+
+	kfree(tmpbuf);
+
+	qcam_write_data(q, 0);
+
+	return len;
+}
+
+/*
+ *	Video4linux interfacing
+ */
+
+static int qcam_open(struct video_device *dev, int flags)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void qcam_close(struct video_device *dev)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static int qcam_init_done(struct video_device *dev)
+{
+	return 0;
+}
+
+static long qcam_write(struct video_device *v, const char *buf, unsigned long count, int noblock)
+{
+	return -EINVAL;
+}
+
+static int qcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+	struct qcam_device *qcam=(struct qcam_device *)dev;
+	
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability b;
+			strcpy(b.name, "Quickcam");
+			b.type = VID_TYPE_CAPTURE|VID_TYPE_SCALES;
+			b.channels = 1;
+			b.audios = 0;
+			b.maxwidth = 320;
+			b.maxheight = 240;
+			b.minwidth = 80;
+			b.minheight = 60;
+			if(copy_to_user(arg, &b,sizeof(b)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCGCHAN:
+		{
+			struct video_channel v;
+			if(copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if(v.channel!=0)
+				return -EINVAL;
+			v.flags=0;
+			v.tuners=0;
+			/* Good question.. its composite or SVHS so.. */
+			v.type = VIDEO_TYPE_CAMERA;
+			strcpy(v.name, "Camera");
+			if(copy_to_user(arg, &v, sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSCHAN:
+		{
+			int v;
+			if(copy_from_user(&v, arg,sizeof(v)))
+				return -EFAULT;
+			if(v!=0)
+				return -EINVAL;
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner v;
+			if(copy_from_user(&v, arg, sizeof(v))!=0)
+				return -EFAULT;
+			if(v.tuner)
+				return -EINVAL;
+			strcpy(v.name, "Format");
+			v.rangelow=0;
+			v.rangehigh=0;
+			v.flags= 0;
+			v.mode = VIDEO_MODE_AUTO;
+			if(copy_to_user(arg,&v,sizeof(v))!=0)
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner v;
+			if(copy_from_user(&v, arg, sizeof(v))!=0)
+				return -EFAULT;
+			if(v.tuner)
+				return -EINVAL;
+			if(v.mode!=VIDEO_MODE_AUTO)
+				return -EINVAL;
+			return 0;
+		}
+		case VIDIOCGPICT:
+		{
+			struct video_picture p;
+			p.colour=0x8000;
+			p.hue=0x8000;
+			p.brightness=qcam->brightness<<8;
+			p.contrast=qcam->contrast<<8;
+			p.whiteness=qcam->whitebal<<8;
+			p.depth=qcam->bpp;
+			p.palette=VIDEO_PALETTE_RGB24;
+			if(copy_to_user(arg, &p, sizeof(p)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSPICT:
+		{
+			struct video_picture p;
+			if(copy_from_user(&p, arg, sizeof(p)))
+				return -EFAULT;
+
+			if (p.palette != VIDEO_PALETTE_RGB24)
+				return -EINVAL;
+			
+			/*
+			 *	Now load the camera.
+			 */
+			qcam->brightness = p.brightness>>8;
+			qcam->contrast = p.contrast>>8;
+			qcam->whitebal = p.whiteness>>8;
+			qcam->bpp = p.depth;
+			
+			parport_claim_or_block(qcam->pdev);
+			qc_setup(qcam); 
+			parport_release(qcam->pdev);
+			return 0;
+		}
+		case VIDIOCSWIN:
+		{
+			struct video_window vw;
+			if(copy_from_user(&vw, arg,sizeof(vw)))
+				return -EFAULT;
+			if(vw.flags)
+				return -EINVAL;
+			if(vw.clipcount)
+				return -EINVAL;
+			if(vw.height<60||vw.height>240)
+				return -EINVAL;
+			if(vw.width<80||vw.width>320)
+				return -EINVAL;
+				
+			qcam->width = 320;
+			qcam->height = 240;
+			qcam->transfer_scale = 4;
+			
+			if(vw.width>=160 && vw.height>=120)
+			{
+				qcam->transfer_scale = 2;
+			}
+			if(vw.width>=320 && vw.height>=240)
+			{
+				qcam->width = 320;
+				qcam->height = 240;
+				qcam->transfer_scale = 1;
+			}
+			/* Ok we figured out what to use from our wide choice */
+			parport_claim_or_block(qcam->pdev);
+			qc_setup(qcam);
+			parport_release(qcam->pdev);
+			return 0;
+		}
+		case VIDIOCGWIN:
+		{
+			struct video_window vw;
+			vw.x=0;
+			vw.y=0;
+			vw.width=qcam->width/qcam->transfer_scale;
+			vw.height=qcam->height/qcam->transfer_scale;
+			vw.chromakey=0;
+			vw.flags=0;
+			if(copy_to_user(arg, &vw, sizeof(vw)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCCAPTURE:
+			return -EINVAL;
+		case VIDIOCGFBUF:
+			return -EINVAL;
+		case VIDIOCSFBUF:
+			return -EINVAL;
+		case VIDIOCKEY:
+			return 0;
+		case VIDIOCGFREQ:
+			return -EINVAL;
+		case VIDIOCSFREQ:
+			return -EINVAL;
+		case VIDIOCGAUDIO:
+			return -EINVAL;
+		case VIDIOCSAUDIO:
+			return -EINVAL;
+		default:
+			return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static long qcam_read(struct video_device *v, char *buf, unsigned long count,  int noblock)
+{
+	struct qcam_device *qcam=(struct qcam_device *)v;
+	int len;
+	parport_claim_or_block(qcam->pdev);
+	/* Probably should have a semaphore against multiple users */
+	len = qc_capture(qcam, buf,count); 
+	parport_release(qcam->pdev);
+	return len;
+}
+
+/* video device template */
+static struct video_device qcam_template=
+{
+	"Colour Quickcam",
+	VID_TYPE_CAPTURE,
+	VID_HARDWARE_QCAM_C,
+	qcam_open,
+	qcam_close,
+	qcam_read,
+	qcam_write,
+	qcam_ioctl,
+	NULL,
+	qcam_init_done,
+	NULL,
+	0,
+	0
+};
+
+/* Initialize the QuickCam driver control structure. */
+
+static struct qcam_device *qcam_init(struct parport *port)
+{
+	struct qcam_device *q;
+	
+	q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL);
+
+	q->pport = port;
+	q->pdev = parport_register_device(port, "c-qcam", NULL, NULL,
+					  NULL, 0, NULL);
+
+	q->bidirectional = (q->pport->modes & PARPORT_MODE_PCPS2)?1:0;
+
+	if (q->pdev == NULL) 
+	{
+		printk(KERN_ERR "c-qcam: couldn't register for %s.\n",
+		       port->name);
+		kfree(q);
+		return NULL;
+	}
+	
+	memcpy(&q->vdev, &qcam_template, sizeof(qcam_template));
+
+	q->width = 320;
+	q->height = 240;
+	q->bpp = 32;
+	q->transfer_scale = 1;
+	q->contrast = 192;
+	q->brightness = 240;
+	q->whitebal = 128;
+	q->top = 1;
+	q->left = 14;
+	return q;
+}
+
+#define MAX_CAMS 4
+static struct qcam_device *qcams[MAX_CAMS];
+static unsigned int num_cams = 0;
+
+int init_cqcam(struct parport *port)
+{
+	struct qcam_device *qcam;
+
+	if (num_cams == MAX_CAMS)
+	{
+		printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS);
+		return -ENOSPC;
+	}
+
+	qcam = qcam_init(port);
+	if (qcam==NULL)
+		return -ENODEV;
+		
+	parport_claim_or_block(qcam->pdev);
+
+	qc_reset(qcam);
+	
+	if (qc_detect(qcam)==0)
+	{
+		parport_release(qcam->pdev);
+		parport_unregister_device(qcam->pdev);
+		kfree(qcam);
+		return -ENODEV;
+	}
+
+	qc_setup(qcam);
+
+	parport_release(qcam->pdev);
+	
+	printk(KERN_INFO "Connectix Colour Quickcam on %s\n", 
+	       qcam->pport->name);
+	
+	if (video_register_device(&qcam->vdev)==-1)
+	{
+		parport_unregister_device(qcam->pdev);
+		kfree(qcam);
+		return -ENODEV;
+	}
+
+	qcams[num_cams++] = qcam;
+
+	return 0;
+}
+
+void close_cqcam(struct qcam_device *qcam)
+{
+	video_unregister_device(&qcam->vdev);
+	parport_unregister_device(qcam->pdev);
+	kfree(qcam);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	struct parport *port;
+
+	for (port = parport_enumerate(); port; port=port->next)
+		init_cqcam(port);
+
+	return (num_cams)?0:-ENODEV;
+}
+
+void cleanup_module(void)
+{
+	unsigned int i;
+	for (i = 0; i < num_cams; i++)
+		close_cqcam(qcams[i]);
+}
+#else
+__initfunc(int init_colour_qcams(struct video_init *unused))
+{
+	struct parport *port;
+
+	for (port = parport_enumerate(); port; port=port->next)
+		init_cqcam(port);
+}
+#endif
diff -u --recursive --new-file v2.1.78/linux/drivers/char/c-qcam.h linux/drivers/char/c-qcam.h
--- v2.1.78/linux/drivers/char/c-qcam.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/c-qcam.h	Mon Jan 12 14:46:16 1998
@@ -0,0 +1,18 @@
+struct qcam_device {
+	struct video_device vdev;
+	struct pardevice *pdev;
+	struct parport *pport;
+	int width, height;
+	int bpp;
+	int contrast, brightness, whitebal;
+	int transfer_scale;
+	int top, left;
+	unsigned int bidirectional;
+};
+
+#define QC_1_1 0
+#define QC_2_1 2
+#define QC_4_1 4
+#define QC_16BPP 8
+#define QC_32BPP 16
+#define QC_24BPP 24
diff -u --recursive --new-file v2.1.78/linux/drivers/char/pcwd.c linux/drivers/char/pcwd.c
--- v2.1.78/linux/drivers/char/pcwd.c	Tue Dec 23 16:30:59 1997
+++ linux/drivers/char/pcwd.c	Mon Jan 12 15:12:42 1998
@@ -33,6 +33,7 @@
  * 971210       Disable board on initialisation in case board already ticking.
  * 971222       Changed open/close for temperature handling
  *              Michael Meskes <meskes@debian.org>.
+ * 980112       Used minor numbers from include/linux/miscdevice.h
  */
 
 #include <linux/module.h>
@@ -67,10 +68,6 @@
 static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
 
 #define WD_VER                  "1.0 (11/18/96)"
-#define	WD_MINOR		130	/* Minor device number */
-#ifndef	TEMP_MINOR
-#define	TEMP_MINOR		131	/* Uses the same as WDT */
-#endif
 
 /*
  * It should be noted that PCWD_REVISION_B was removed because A and B
@@ -382,7 +379,7 @@
 {
         switch (MINOR(ino->i_rdev))
         {
-                case WD_MINOR:
+                case WATCHDOG_MINOR:
                     if (is_open)
                         return -EBUSY;
                     MOD_INC_USE_COUNT;
@@ -424,7 +421,7 @@
 static int pcwd_close(struct inode *ino, struct file *filep)
 {
 	MOD_DEC_USE_COUNT;
-	if (MINOR(ino->i_rdev)==WD_MINOR)
+	if (MINOR(ino->i_rdev)==WATCHDOG_MINOR)
 	{
 	        is_open = 0;
 #ifndef CONFIG_WATCHDOG_NOWAYOUT
@@ -527,8 +524,8 @@
 };
 
 static struct miscdevice pcwd_miscdev = {
-	WD_MINOR,
-	"pcwatchdog",
+	WATCHDOG_MINOR,
+	"watchdog",
 	&pcwd_fops
 };
 
diff -u --recursive --new-file v2.1.78/linux/drivers/char/psaux.c linux/drivers/char/psaux.c
--- v2.1.78/linux/drivers/char/psaux.c	Sat Oct 25 02:44:15 1997
+++ linux/drivers/char/psaux.c	Mon Jan 12 15:13:24 1998
@@ -30,6 +30,9 @@
  * 3-Jul-96, 22-Aug-96 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
  *
  * Cleanup by Martin Mares, 01-Jun-97 (now uses the new PC kbd include)
+ *
+ * Renamed misc. name to "psaux",more in keeping with Documentation/devices.txt
+ * 13-Jan-1998, Richard Gooch <rgooch@atnf.csiro.au>
  */
 
 /* Uncomment the following line if your mouse needs initialization. */
@@ -589,7 +592,7 @@
  * forget about the Aux port and use the *_qp functions.
  */
 static struct miscdevice psaux_mouse = {
-	PSMOUSE_MINOR, "ps2aux", &psaux_fops
+	PSMOUSE_MINOR, "psaux", &psaux_fops
 };
 
 __initfunc(int psaux_init(void))
diff -u --recursive --new-file v2.1.78/linux/drivers/char/softdog.c linux/drivers/char/softdog.c
--- v2.1.78/linux/drivers/char/softdog.c	Wed Dec 10 11:12:43 1997
+++ linux/drivers/char/softdog.c	Mon Jan 12 15:12:42 1998
@@ -37,7 +37,6 @@
 #include <linux/init.h>
 #include <asm/uaccess.h>
 
-#define WATCHDOG_MINOR	130
 #define TIMER_MARGIN	60		/* (secs) Default is 1 minute */
 
 static int soft_margin = TIMER_MARGIN;	/* in seconds */
@@ -177,7 +176,7 @@
 static struct miscdevice softdog_miscdev=
 {
 	WATCHDOG_MINOR,
-	"softdog",
+	"watchdog",
 	&softdog_fops
 };
 
diff -u --recursive --new-file v2.1.78/linux/drivers/char/videodev.c linux/drivers/char/videodev.c
--- v2.1.78/linux/drivers/char/videodev.c	Mon Dec  1 12:04:12 1997
+++ linux/drivers/char/videodev.c	Mon Jan 12 14:46:16 1998
@@ -52,11 +52,14 @@
 #ifdef CONFIG_VIDEO_BT848
 	{"bttv", init_bttv_cards},
 #endif	
+#ifdef CONFIG_VIDEO_CQCAM
+	{"c-qcam", init_colour_qcams},
+#endif	
 #ifdef CONFIG_VIDEO_BWQCAM
-	{"bttv", init_bw_qcams},
+	{"bw-qcam", init_bw_qcams},
 #endif	
 #ifdef CONFIG_VIDEO_PMS
-	{"bttv", init_pms_cards},
+	{"PMS", init_pms_cards},
 #endif	
 	{"end", NULL}
 };
diff -u --recursive --new-file v2.1.78/linux/drivers/char/wd501p.h linux/drivers/char/wd501p.h
--- v2.1.78/linux/drivers/char/wd501p.h	Thu Dec 12 06:51:09 1996
+++ linux/drivers/char/wd501p.h	Mon Jan 12 15:12:42 1998
@@ -20,9 +20,6 @@
  */
 
 #include <linux/config.h>
- 
-#define WATCHDOG_MINOR		130	/* Watchdog timer     */
-#define TEMP_MINOR		131	/* Temperature Sensor */
 
 #define WDT_COUNT0		(io+0)
 #define WDT_COUNT1		(io+1)
diff -u --recursive --new-file v2.1.78/linux/drivers/char/wdt.c linux/drivers/char/wdt.c
--- v2.1.78/linux/drivers/char/wdt.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/char/wdt.c	Mon Jan 12 15:12:42 1998
@@ -333,7 +333,7 @@
 static struct miscdevice wdt_miscdev=
 {
 	WATCHDOG_MINOR,
-	"wdt",
+	"watchdog",
 	&wdt_fops
 };
 
diff -u --recursive --new-file v2.1.78/linux/drivers/fc4/Config.in linux/drivers/fc4/Config.in
--- v2.1.78/linux/drivers/fc4/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/fc4/Config.in	Mon Jan 12 15:19:36 1998
@@ -0,0 +1,17 @@
+#
+# FC4 device configuration
+#
+mainmenu_option next_comment
+comment 'Fibre Channel support'
+
+tristate 'Fibre Channel and FC4 SCSI support' CONFIG_FC4 m
+if [ ! "$CONFIG_FC4" = "n" ]; then
+  comment 'FC4 drivers'
+  tristate 'Sun SOC/Sbus' CONFIG_FC4_SOC m
+  comment 'FC4 targets'
+  dep_tristate 'SparcSTORAGE Array 100 and 200 series' CONFIG_SCSI_PLUTO "$CONFIG_SCSI"
+else
+  define_bool CONFIG_FC4_SOC n
+  define_bool CONFIG_SCSI_PLUTO n
+fi
+endmenu
diff -u --recursive --new-file v2.1.78/linux/drivers/fc4/Makefile linux/drivers/fc4/Makefile
--- v2.1.78/linux/drivers/fc4/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/fc4/Makefile	Mon Jan 12 15:19:36 1998
@@ -0,0 +1,39 @@
+# File: drivers/fc4/Makefile
+#
+# Makefile for the Linux Fibre Channel device drivers.
+#
+
+L_TARGET := fc4.a
+M_OBJS   :=
+MOD_LIST_NAME := FC4_MODULES
+
+include ../../.config
+
+ifeq ($(CONFIG_FC4),y)
+  FC4 = fc.o
+  ifeq ($(CONFIG_MODULES),y)
+    O_TARGET := fc_n_syms.o
+    O_OBJS   := fc.o
+    OX_OBJS  := fc_syms.o
+    FC4      := $(O_TARGET)
+  endif
+  L_OBJS += $(FC4)
+else
+  ifeq ($(CONFIG_FC4),m)
+    MIX_OBJS += fc_syms.o
+    M_OBJS += fc4.o
+  endif
+endif
+
+ifeq ($(CONFIG_FC4_SOC),y)
+L_OBJS += soc.o
+else
+  ifeq ($(CONFIG_FC4_SOC),m)
+  M_OBJS += soc.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+fc4.o: $(MIX_OBJS) fc.o
+	$(LD) $(LD_RFLAG) -r -o $@ $(MIX_OBJS) fc.o
diff -u --recursive --new-file v2.1.78/linux/drivers/fc4/fc.c linux/drivers/fc4/fc.c
--- v2.1.78/linux/drivers/fc4/fc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/fc4/fc.c	Mon Jan 12 15:19:36 1998
@@ -0,0 +1,688 @@
+/* fc.c: Generic Fibre Channel and FC4 SCSI driver.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Jiri Hanika (geo@ff.cuni.cz)
+ *
+ * Sources:
+ *	Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
+ *	dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/semaphore.h>
+#include "fcp_scsi.h"
+#include "../scsi/hosts.h"
+
+/* #define FCDEBUG */
+
+#define fc_printk printk ("%s: ", fc->name); printk 
+
+#ifdef FCDEBUG
+#define FCD(x)  fc_printk x;
+#define FCND(x)	printk ("FC: "); printk x;
+#else
+#define FCD(x)
+#define FCND(x)
+#endif
+
+#ifdef __sparc__
+static inline void *fc_dma_alloc(long size, char *name, dma_handle *dma)
+{
+	return (void *) sparc_dvma_malloc (size, "FCP SCSI cmd & rsp queues", dma);
+}
+
+static inline dma_handle fc_sync_dma_entry(void *buf, int len, fc_channel *fc)
+{
+	return mmu_get_scsi_one (buf, len, fc->dev->my_bus);
+}
+
+static inline void fc_sync_dma_exit(void *buf, long size, fc_channel *fc)
+{
+	mmu_release_scsi_one ((u32)(long)buf, size, fc->dev->my_bus);
+}
+
+static inline void fc_sync_dma_entry_sg(struct scatterlist *list, int count, fc_channel *fc)
+{
+	mmu_get_scsi_sgl((struct mmu_sglist *)list, count - 1, fc->dev->my_bus);
+}
+
+static inline void fc_sync_dma_exit_sg(struct scatterlist *list, int count, fc_channel *fc)
+{
+	mmu_release_scsi_sgl ((struct mmu_sglist *)list, count - 1, fc->dev->my_bus);
+}
+#else
+#error Port this
+#endif							       
+
+#define FCP_CMND(SCpnt) ((fcp_cmnd *)&(SCpnt->SCp))
+#define FC_SCMND(SCpnt) ((fc_channel *)(SCpnt->host->hostdata[0]))
+#define SC_FCMND(fcmnd) ((Scsi_Cmnd *)((long)fcmnd - (long)&(((Scsi_Cmnd *)0)->SCp)))
+
+static void fcp_scsi_insert_queue (fc_channel *fc, int no, fcp_cmnd *fcmd)
+{
+	if (!fc->scsi_que[no]) {
+		fc->scsi_que[no] = fcmd;
+		fcmd->next = fcmd;
+		fcmd->prev = fcmd;
+	} else {
+		fc->scsi_que[no]->prev->next = fcmd;
+		fcmd->prev = fc->scsi_que[no]->prev;
+		fc->scsi_que[no]->prev = fcmd;
+		fcmd->next = fc->scsi_que[no];
+	}
+}
+
+static void fcp_scsi_remove_queue (fc_channel *fc, int no, fcp_cmnd *fcmd)
+{
+	if (fcmd == fcmd->next) {
+		fc->scsi_que[no] = NULL;
+		return;
+	}
+	if (fcmd == fc->scsi_que[no])
+		fc->scsi_que[no] = fcmd->next;
+	fcmd->prev->next = fcmd->next;
+	fcmd->next->prev = fcmd->prev;
+}
+
+fc_channel *fc_channels = NULL;
+
+#define LSMAGIC	0x2a3b4d2a
+typedef struct {
+	/* Must be first */
+	struct semaphore sem;
+	int magic;
+	int count;
+	logi *logi;
+	fcp_cmnd *fcmds;
+	atomic_t todo;
+	struct timer_list timer;
+	int grace[1];
+} ls;
+
+#define LSOMAGIC 0x2a3c4e3c
+typedef struct {
+	/* Must be first */
+	struct semaphore sem;
+	int magic;
+	int count;
+	fcp_cmnd *fcmds;
+	atomic_t todo;
+	struct timer_list timer;
+} lso;
+
+static void fcp_login_timeout(unsigned long data)
+{
+	ls *l = (ls *)data;
+	FCND(("Login timeout\n"))
+	up(&l->sem);
+}
+
+static void fcp_login_done(fc_channel *fc, int i, int status)
+{
+	fcp_cmnd *fcmd;
+	logi *plogi;
+	fc_hdr *fch;
+	ls *l = (ls *)fc->ls;
+	
+	FCD(("Login done %d %d\n", i, status))
+	if (i < l->count) {
+		if (fc->state == FC_STATE_FPORT_OK) {
+			FCD(("Additional FPORT_OK received with status %d\n", status))
+			return;
+		}
+		switch (status) {
+		case FC_STATUS_OK: /* Oh, we found a fabric */
+		case FC_STATUS_P_RJT: /* Oh, we haven't found any */
+			fc->state = FC_STATE_FPORT_OK;
+			fcmd = l->fcmds + i;
+			plogi = l->logi + 3 * i;
+			fc_sync_dma_exit (plogi, 3 * sizeof(logi), fc);
+			plogi->code = LS_PLOGI;
+			memcpy (&plogi->nport_wwn, &fc->wwn_nport, sizeof(fc_wwn));
+			memcpy (&plogi->node_wwn, &fc->wwn_node, sizeof(fc_wwn));
+			memcpy (&plogi->common, fc->common_svc, sizeof(common_svc_parm));
+			memcpy (&plogi->class1, fc->class_svcs, 3*sizeof(svc_parm));
+			fch = &fcmd->fch;
+			fcmd->token += l->count;
+			fcmd->rsp += sizeof(logi);
+			FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, fc->did);
+			FILL_FCHDR_SID(fch, fc->sid);
+#ifdef FCDEBUG
+			{
+				int i;
+				unsigned *x = (unsigned *)plogi;
+				printk ("logi: ");
+				for (i = 0; i < 21; i++)
+					printk ("%08x ", x[i]);
+				printk ("\n");
+			}
+#endif			
+			fc_sync_dma_entry (plogi, 3 * sizeof(logi), fc);
+			if (fc->hw_enque (fc, fcmd))
+				printk ("FC: Cannot enque PLOGI packet on %s\n", fc->name);
+			break;
+		case FC_STATUS_ERR_OFFLINE:
+			fc->state = FC_STATE_MAYBEOFFLINE;
+			FCD (("FC is offline %d\n", l->grace[i]))
+			break;
+		default:
+			printk ("FLOGI failed for %s with status %d\n", fc->name, status);
+			/* Do some sort of error recovery here */
+			break;
+		}
+	} else {
+		i -= l->count;
+		if (fc->state != FC_STATE_FPORT_OK) {
+			FCD(("Unexpected N-PORT rsp received"))
+			return;
+		}
+		switch (status) {
+		case FC_STATUS_OK:
+			plogi = l->logi + 3 * i;
+			fc_sync_dma_exit (plogi, 3 * sizeof(logi), fc);
+			if (!fc->wwn_dest.lo && !fc->wwn_dest.hi) {
+				memcpy (&fc->wwn_dest, &plogi[1].node_wwn, sizeof(fc_wwn)); 
+				FCD(("Dest WWN %08x%08x\n", *(u32 *)&fc->wwn_dest, fc->wwn_dest.lo))
+			} else if (fc->wwn_dest.lo != plogi[1].node_wwn.lo ||
+				   fc->wwn_dest.hi != plogi[1].node_wwn.hi) {
+				printk ("%s: mismatch in wwns. Got %08x%08x, expected %08x%08x\n",
+					fc->name,
+					*(u32 *)&plogi[1].node_wwn, plogi[1].node_wwn.lo,
+					*(u32 *)&fc->wwn_dest, fc->wwn_dest.lo);
+			}
+			fc->state = FC_STATE_ONLINE;
+			printk ("%s: ONLINE\n", fc->name);
+			if (atomic_dec_and_test (&l->todo))
+				up(&l->sem);
+			break;
+		case FC_STATUS_ERR_OFFLINE:
+			fc->state = FC_STATE_OFFLINE;
+			fc_sync_dma_exit (l->logi + 3 * i, 3 * sizeof(logi), fc);
+			printk ("%s: FC is offline\n", fc->name);
+			if (atomic_dec_and_test (&l->todo))
+				up(&l->sem);
+			break;
+		default:
+			printk ("PLOGI failed for %s with status %d\n", fc->name, status);
+			/* Do some sort of error recovery here */
+			break;
+		}
+	}
+}
+
+void fcp_register(fc_channel *fc, u8 type, int unregister)
+{
+	int size, i;
+
+	if (type == TYPE_SCSI_FCP) {
+		if (!unregister) {
+			fc->scsi_cmd_pool = 
+				(fcp_cmd *) fc_dma_alloc (fc->can_queue * (sizeof (fcp_cmd) + fc->rsp_size), 
+							  "FCP SCSI cmd & rsp queues", &fc->dma_scsi_cmd);
+			fc->scsi_rsp_pool = (char *)(fc->scsi_cmd_pool + fc->can_queue);
+			fc->dma_scsi_rsp = fc->dma_scsi_cmd + fc->can_queue * sizeof (fcp_cmd);
+			fc->scsi_bitmap_end = ((fc->can_queue + 63) & ~63);
+			size = fc->scsi_bitmap_end / 8;
+			fc->scsi_bitmap = kmalloc (size, GFP_KERNEL);
+			memset (fc->scsi_bitmap, 0, size);
+			for (i = fc->can_queue; i < fc->scsi_bitmap_end; i++)
+				set_bit (i, fc->scsi_bitmap);
+			fc->scsi_free = fc->can_queue;
+			fc->token_tab = (fcp_cmnd **)kmalloc(fc->can_queue * sizeof(fcp_cmnd*), GFP_KERNEL);
+		} else {
+			fc->scsi_name[0] = 0;
+			kfree (fc->scsi_bitmap);
+			kfree (fc->token_tab);
+		}
+	} else
+		printk ("FC: %segistering unknown type %02x\n", unregister ? "Unr" : "R", type);
+}
+
+static void fcp_scsi_done(Scsi_Cmnd *SCpnt);
+
+static inline void fcp_scsi_receive(fc_channel *fc, int token, int status, fc_hdr *fch)
+{
+	fcp_cmnd *fcmd;
+	fcp_rsp  *rsp;
+	int host_status;
+	Scsi_Cmnd *SCpnt;
+	int sense_len;
+	int rsp_status;
+
+	fcmd = fc->token_tab[token];
+	if (!fcmd) return;
+	rsp = (fcp_rsp *) (fc->scsi_rsp_pool + fc->rsp_size * token);
+	SCpnt = SC_FCMND(fcmd);
+
+	if (SCpnt->done != fcp_scsi_done)
+		return;
+
+	rsp_status = rsp->fcp_status;
+	switch (status) {
+	case FC_STATUS_OK:
+		host_status=DID_OK;
+		
+		if (rsp_status & FCP_STATUS_RESID) {
+#ifdef FCDEBUG
+			FCD(("Resid %d\n", rsp->fcp_resid))
+			{
+				fcp_cmd *cmd = fc->scsi_cmd_pool + token;
+				int i;
+				
+				printk ("Command ");
+				for (i = 0; i < sizeof(fcp_cmd); i+=4)
+					printk ("%08x ", *(u32 *)(((char *)cmd)+i));
+				printk ("\nResponse ");
+				for (i = 0; i < fc->rsp_size; i+=4)
+					printk ("%08x ", *(u32 *)(((char *)rsp)+i));
+				printk ("\n");
+			}
+#endif			
+		}
+
+		if (rsp_status & FCP_STATUS_SENSE_LEN) {
+			sense_len = rsp->fcp_sense_len;
+			if (sense_len > sizeof(SCpnt->sense_buffer)) sense_len = sizeof(SCpnt->sense_buffer);
+			memcpy(SCpnt->sense_buffer, ((char *)(rsp+1)), sense_len);
+		}
+		
+		if (fcmd->data) {
+			if (SCpnt->use_sg)
+				fc_sync_dma_exit_sg((struct scatterlist *)SCpnt->buffer, SCpnt->use_sg, fc);
+			else
+				fc_sync_dma_exit(SCpnt->request_buffer, SCpnt->request_bufflen, fc);
+		}
+		break;
+	default:
+		host_status=DID_ERROR;
+		FCD(("Wrong FC status %d for token %d\n", status, token))
+		break;
+	}
+
+	if (status_byte(rsp_status) == QUEUE_FULL) {
+		printk ("%s: (%d,%d) Received rsp_status 0x%x\n", fc->name, SCpnt->channel, SCpnt->target, rsp_status);
+	}	
+	
+	SCpnt->result = (host_status << 16) | (rsp_status & 0xff);
+	SCpnt->done = fcmd->done;
+	fcmd->done=NULL;
+	clear_bit(token, fc->scsi_bitmap);
+	fc->scsi_free++;
+	SCpnt->scsi_done(SCpnt);
+}
+
+void fcp_receive_solicited(fc_channel *fc, int proto, int token, int status, fc_hdr *fch)
+{
+	FCD(("Receive solicited %d %d %d\n", proto, token, status))
+	switch (proto) {
+	case TYPE_SCSI_FCP:
+		fcp_scsi_receive(fc, token, status, fch); break;
+	case TYPE_EXTENDED_LS:
+		if (fc->ls && ((ls *)(fc->ls))->magic == LSMAGIC) {
+			ls *l = (ls *)fc->ls;
+			int i = (token >= l->count) ? token - l->count : token;
+
+			/* Make us sure */
+			if ((unsigned)i < l->count && l->fcmds[i].fc == fc) {
+				fcp_login_done(fc, token, status);
+				break;
+			}
+		}
+		break;
+	case PROTO_OFFLINE:
+		if (fc->ls && ((lso *)(fc->ls))->magic == LSOMAGIC) {
+			lso *l = (lso *)fc->ls;
+
+			if ((unsigned)token < l->count && l->fcmds[token].fc == fc) {
+				/* Wow, OFFLINE response arrived :) */
+				FCD(("OFFLINE Response arrived\n"))
+				fc->state = FC_STATE_OFFLINE;
+				if (atomic_dec_and_test (&l->todo))
+					up(&l->sem);
+			}
+		}
+		break;
+		
+	default:
+		break;
+	}
+}
+
+void fcp_state_change(fc_channel *fc, int state)
+{
+	FCD(("state_change %d %d\n", state, fc->state))
+	if (state == FC_STATE_ONLINE && fc->state == FC_STATE_MAYBEOFFLINE)
+		fc->state = FC_STATE_UNINITED;
+	else if (state == FC_STATE_ONLINE)
+		printk (KERN_WARNING "%s: state change to ONLINE\n", fc->name);
+	else
+		printk (KERN_ERR "%s: state change to OFFLINE\n", fc->name);
+}
+
+int fcp_initialize(fc_channel *fcchain, int count)
+{
+	fc_channel *fc;
+	fcp_cmnd *fcmd;
+	int i, retry, ret;
+	ls *l;
+
+	FCND(("fcp_inititialize %08lx\n", (long)fcp_init))
+	FCND(("fc_channels %08lx\n", (long)fc_channels))
+	FCND((" SID %d DID %d\n", fcchain->sid, fcchain->did))
+	l = kmalloc(sizeof (ls) + count * sizeof(int), GFP_KERNEL);
+	if (!l) {
+		printk ("FC: Cannot allocate memory for initialization\n");
+		return -ENOMEM;
+	}
+	memset (l, 0, sizeof(ls) + count * sizeof(int));
+	l->magic = LSMAGIC;
+	l->count = count;
+	FCND(("FCP Init for %d channels\n", count))
+	l->sem = MUTEX_LOCKED;
+	l->timer.function = fcp_login_timeout;
+	l->timer.data = (unsigned long)l;
+	atomic_set (&l->todo, count);
+	l->logi = kmalloc (count * 3 * sizeof(logi), GFP_DMA);
+	l->fcmds = kmalloc (count * sizeof(fcp_cmnd), GFP_KERNEL);
+	if (!l->logi || !l->fcmds) {
+		if (l->logi) kfree (l->logi);
+		if (l->fcmds) kfree (l->fcmds);
+		kfree (l);
+		printk ("FC: Cannot allocate DMA memory for initialization\n");
+		return -ENOMEM;
+	}
+	memset (l->logi, 0, count * 3 * sizeof(logi));
+	memset (l->fcmds, 0, count * sizeof(fcp_cmnd));
+	FCND(("Initializing FLOGI packets\n"))
+	for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
+		fc_hdr *fch;
+
+		FCD(("SID %d DID %d\n", fc->sid, fc->did))
+		fc->state = FC_STATE_UNINITED;
+		fcmd = l->fcmds + i;
+		fc->login = fcmd;
+		fc->ls = (void *)l;
+		fch = &fcmd->fch;
+		FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT);
+		FILL_FCHDR_SID(fch, 0);
+		FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
+		FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
+		FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
+		fch->param = 0;
+		l->logi [3 * i].code = LS_FLOGI;
+		fcmd->cmd = fc_sync_dma_entry (l->logi + 3 * i, 3 * sizeof(logi), fc);
+		fcmd->rsp = fcmd->cmd + sizeof(logi);
+		fcmd->cmdlen = sizeof(logi);
+		fcmd->rsplen = sizeof(logi);
+		fcmd->data = (dma_handle)NULL;
+		fcmd->class = FC_CLASS_SIMPLE;
+		fcmd->proto = TYPE_EXTENDED_LS;
+		fcmd->token = i;
+		fcmd->fc = fc;
+	}
+	for (retry = 0; retry < 8; retry++) {
+		FCND(("Sending FLOGI/PLOGI packets\n"))
+		for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
+			if (fc->state == FC_STATE_ONLINE || fc->state == FC_STATE_OFFLINE)
+				continue;
+			disable_irq(fc->irq);
+			if (fc->state == FC_STATE_MAYBEOFFLINE) {
+				if (!l->grace[i]) {
+					l->grace[i]++;
+					FCD(("Grace\n"))
+				} else {
+					fc->state = FC_STATE_OFFLINE;
+					enable_irq(fc->irq);
+					fc_sync_dma_exit (l->logi + 3 * i, 3 * sizeof(logi), fc);
+					if (atomic_dec_and_test (&l->todo))
+						goto all_done;
+				}
+			}
+			ret = fc->hw_enque (fc, fc->login);
+			enable_irq(fc->irq);
+			if (ret) printk ("FC: Cannot enque FLOGI packet on %s\n", fc->name);
+		}
+		
+		l->timer.expires = jiffies + 5 * HZ;
+		add_timer(&l->timer);
+
+		down(&l->sem);
+		if (!atomic_read(&l->todo)) {
+			FCND(("All channels answered in time\n"))
+			break; /* All fc channels have answered us */
+		}
+	}
+all_done:
+	for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i += 3) {
+		switch (fc->state) {
+		case FC_STATE_ONLINE: break;
+		case FC_STATE_OFFLINE: break;
+		default: fc_sync_dma_exit (l->logi + i, 3 * sizeof(logi), fc);
+			break;
+		}
+		fc->ls = NULL;
+	}
+	del_timer(&l->timer);
+	kfree (l->logi);
+	kfree (l->fcmds);
+	kfree (l);
+	return 0;
+}
+
+int fcp_forceoffline(fc_channel *fcchain, int count)
+{
+	fc_channel *fc;
+	fcp_cmnd *fcmd;
+	int i, ret;
+	lso l;
+
+	memset (&l, 0, sizeof(lso));
+	l.count = count;
+	l.magic = LSOMAGIC;
+	FCND(("FCP Force Offline for %d channels\n", count))
+	l.sem = MUTEX_LOCKED;
+	l.timer.function = fcp_login_timeout;
+	l.timer.data = (unsigned long)&l;
+	atomic_set (&l.todo, count);
+	l.fcmds = kmalloc (count * sizeof(fcp_cmnd), GFP_KERNEL);
+	if (!l.fcmds) {
+		kfree (l.fcmds);
+		printk ("FC: Cannot allocate memory for forcing offline\n");
+		return -ENOMEM;
+	}
+	memset (l.fcmds, 0, count * sizeof(fcp_cmnd));
+	FCND(("Initializing OFFLINE packets\n"))
+	for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
+		fc->state = FC_STATE_UNINITED;
+		fcmd = l.fcmds + i;
+		fc->login = fcmd;
+		fc->ls = (void *)&l;
+		fcmd->class = FC_CLASS_OFFLINE;
+		fcmd->proto = PROTO_OFFLINE;
+		fcmd->token = i;
+		fcmd->fc = fc;
+		disable_irq(fc->irq);
+		ret = fc->hw_enque (fc, fc->login);
+		enable_irq(fc->irq);
+		if (ret) printk ("FC: Cannot enque OFFLINE packet on %s\n", fc->name);
+	}
+		
+	l.timer.expires = jiffies + 5 * HZ;
+	add_timer(&l.timer);
+	down(&l.sem);
+	del_timer(&l.timer);
+	
+	kfree (l.fcmds);
+	return 0;
+}
+
+int fcp_init(fc_channel *fcchain)
+{
+	fc_channel *fc;
+	int count=0;
+	
+	for (fc = fcchain; fc; fc = fc->next) {
+		fc->fcp_register = fcp_register;
+		fc->fcp_state_change = fcp_state_change;
+		count++;
+	}
+
+	fcp_initialize (fcchain, count);
+	
+	if (!fc_channels)
+		fc_channels = fcchain;
+	else {
+		for (fc = fc_channels; fc->next; fc = fc->next);
+		fc->next = fcchain;
+	}
+	return 0;
+}
+
+static void fcp_scsi_done (Scsi_Cmnd *SCpnt)
+{
+	if (FCP_CMND(SCpnt)->done)
+		FCP_CMND(SCpnt)->done(SCpnt);
+}
+
+static int fcp_scsi_queue_it(fc_channel *fc, Scsi_Cmnd *SCpnt, fcp_cmnd *fcmd, int prepare)
+{
+	if (prepare) {
+		long i;
+		fcp_cmd *cmd;
+		u32 fcp_cntl;
+
+		i = find_first_zero_bit (fc->scsi_bitmap, fc->scsi_bitmap_end);
+		set_bit (i, fc->scsi_bitmap);
+		fcmd->token = i;
+		cmd = fc->scsi_cmd_pool + i;
+		FCD(("Chose token %ld %08lx\n", i, (long)cmd))
+		if (fc->encode_addr (SCpnt, cmd->fcp_addr)) {
+			/* Invalid channel/id/lun and couldn't map it into fcp_addr */
+			clear_bit (i, fc->scsi_bitmap);
+			SCpnt->result = (DID_BAD_TARGET << 16);
+			SCpnt->scsi_done(SCpnt);
+			return 0;
+		}
+		fc->scsi_free--;
+		fc->token_tab[fcmd->token] = fcmd;
+
+		if (SCpnt->device->tagged_supported) {
+			if (jiffies - fc->ages[SCpnt->channel * fc->targets + SCpnt->target] > (5 * 60 * HZ)) {
+				fc->ages[SCpnt->channel * fc->targets + SCpnt->target] = jiffies;
+				fcp_cntl = FCP_CNTL_QTYPE_ORDERED;
+			} else
+				fcp_cntl = FCP_CNTL_QTYPE_SIMPLE;
+		} else
+			fcp_cntl = FCP_CNTL_QTYPE_UNTAGGED;
+		if (!SCpnt->request_bufflen && !SCpnt->use_sg) {
+			cmd->fcp_cntl = fcp_cntl;
+			fcmd->data = (dma_handle)NULL;
+		} else {
+			switch (SCpnt->cmnd[0]) {
+			case WRITE_6:
+			case WRITE_10:
+			case WRITE_12:
+				cmd->fcp_cntl = (FCP_CNTL_WRITE | fcp_cntl); break;
+			default:
+				cmd->fcp_cntl = (FCP_CNTL_READ | fcp_cntl); break;
+			}
+			if (!SCpnt->use_sg) {
+				cmd->fcp_data_len = SCpnt->request_bufflen;
+				fcmd->data = fc_sync_dma_entry ((char *)SCpnt->request_buffer,
+								SCpnt->request_bufflen, fc);
+			} else {
+				struct scatterlist *sg = (struct scatterlist *)SCpnt->buffer;
+
+				FCD(("XXX: Use_sg %d %d\n", SCpnt->use_sg, sg->length))
+				if (SCpnt->use_sg > 1) printk ("%s: SG for use_sg > 1 not handled yet\n", fc->name);
+				fc_sync_dma_entry_sg (sg, SCpnt->use_sg, fc);
+				fcmd->data = sg->dvma_address;
+				cmd->fcp_data_len = sg->length;
+			}
+		}
+		memcpy (cmd->fcp_cdb, SCpnt->cmnd, SCpnt->cmd_len);
+		memset (cmd->fcp_cdb+SCpnt->cmd_len, 0, sizeof(cmd->fcp_cdb)-SCpnt->cmd_len);
+		FCD(("XXX: %04x.%04x.%04x.%04x - %08x%08x%08x\n", cmd->fcp_addr[0], cmd->fcp_addr[1], cmd->fcp_addr[2], cmd->fcp_addr[3], *(u32 *)SCpnt->cmnd, *(u32 *)(SCpnt->cmnd+4), *(u32 *)(SCpnt->cmnd+8)))
+	}
+	FCD(("Trying to enque %08x\n", (int)fcmd))
+	if (!fc->scsi_que[1]) {
+		if (!fc->hw_enque (fc, fcmd)) {
+			FCD(("hw_enque succeeded for %08x\n", (int)fcmd))
+			return 0;
+		}
+	}
+	FCD(("Putting into que1 %08x\n", (int)fcmd))
+	fcp_scsi_insert_queue (fc, 1, fcmd);
+	return 0;
+}
+
+int fcp_scsi_queuecommand(Scsi_Cmnd *SCpnt, void (* done)(Scsi_Cmnd *))
+{
+	fcp_cmnd *fcmd = FCP_CMND(SCpnt);
+	fc_channel *fc = FC_SCMND(SCpnt);
+	
+	FCD(("Entering SCSI queuecommand %08x\n", (int)fcmd))
+	if (SCpnt->done != fcp_scsi_done) {
+		fcmd->done = SCpnt->done;
+		SCpnt->done = fcp_scsi_done;
+		SCpnt->scsi_done = done;
+		fcmd->proto = TYPE_SCSI_FCP;
+		if (!fc->scsi_free) {
+			FCD(("Putting into que0\n"))
+			fcp_scsi_insert_queue (fc, 0, fcmd);
+			return 1;
+		}
+		return fcp_scsi_queue_it(fc, SCpnt, fcmd, 1);
+	}
+	return fcp_scsi_queue_it(fc, SCpnt, fcmd, 0);
+}
+
+void fcp_queue_empty(fc_channel *fc)
+{
+	fcp_cmnd *fcmd;
+	FCD(("Queue empty\n"))
+	while ((fcmd = fc->scsi_que[1])) {
+		/* The hw tell us we can try again queue some packet */
+		if (fc->hw_enque (fc, fcmd))
+			return;
+		fcp_scsi_remove_queue (fc, 1, fcmd);
+	}
+}
+
+int fcp_scsi_abort(Scsi_Cmnd *SCpnt)
+{
+	printk ("FC: AIEEE, abort!\n");
+	return 0;
+}
+
+int fcp_scsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+{
+	printk ("FC: AIEEE, reset!\n");
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return 0;
+}
+
+void cleanup_module(void)
+{
+}
+#endif
diff -u --recursive --new-file v2.1.78/linux/drivers/fc4/fc.h linux/drivers/fc4/fc.h
--- v2.1.78/linux/drivers/fc4/fc.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/fc4/fc.h	Mon Jan 12 15:19:36 1998
@@ -0,0 +1,222 @@
+/* fc.h: Definitions for Fibre Channel Physical and Signaling Interface.
+ *
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ * Sources:
+ *	Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
+ *	dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
+ */
+
+#ifndef __FC_H
+#define __FC_H
+
+/* World Wide Name */
+#define NAAID_IEEE	1
+#define NAAID_IEEE_EXT	2
+#define NAAID_LOCAL	3
+#define NAAID_IP	4
+#define NAAID_CCITT	12
+#define NAAID_CCITT_GRP	14
+
+typedef struct {
+	u32	naaid:4;
+	u32	nportid:12;
+	u32	hi:16;
+	u32	lo;
+} fc_wwn;
+
+/* Frame header for FC-PH frames */
+
+/* r_ctl field */
+#define R_CTL_DEVICE_DATA	0x00	/* FC4 Device_Data frame */
+#define R_CTL_EXTENDED_SVC	0x20	/* Extended Link_Data frame */
+#define R_CTL_FC4_SVC		0x30	/* FC4 Link_Data frame */
+#define R_CTL_VIDEO		0x40	/* Video_Data frame */
+#define R_CTL_BASIC_SVC		0x80	/* Basic Link_Data frame */
+#define R_CTL_LINK_CTL		0xc0	/* Link_Control frame */
+/* FC4 Device_Data frames */
+#define R_CTL_UNCATEGORIZED	0x00
+#define R_CTL_SOLICITED_DATA	0x01
+#define R_CTL_UNSOL_CONTROL	0x02
+#define R_CTL_SOLICITED_CONTROL	0x03
+#define R_CTL_UNSOL_DATA	0x04
+#define R_CTL_XFER_RDY		0x05
+#define R_CTL_COMMAND		0x06
+#define R_CTL_STATUS		0x07
+/* Basic Link_Data frames */
+#define R_CTL_LS_NOP		0x80
+#define R_CTL_LS_ABTS		0x81
+#define R_CTL_LS_RMC		0x82
+#define R_CTL_LS_BA_ACC		0x84
+#define R_CTL_LS_BA_RJT		0x85
+/* Extended Link_Data frames */
+#define R_CTL_ELS_REQ		0x22
+#define R_CTL_ELS_RSP		0x23
+/* Link_Control frames */
+#define R_CTL_ACK_1		0xc0
+#define R_CTL_ACK_N		0xc1
+#define R_CTL_P_RJT		0xc2
+#define R_CTL_F_RJT		0xc3
+#define R_CTL_P_BSY		0xc4
+#define R_CTL_F_BSY_DF		0xc5
+#define R_CTL_F_BSY_LC		0xc6
+#define R_CTL_LCR		0xc7
+
+/* type field */
+#define TYPE_BASIC_LS		0x00
+#define TYPE_EXTENDED_LS	0x01
+#define TYPE_IS8802		0x04
+#define TYPE_IS8802_SNAP	0x05
+#define TYPE_SCSI_FCP		0x08
+#define TYPE_SCSI_GPP		0x09
+#define TYPE_HIPP_FP		0x0a
+#define TYPE_IPI3_MASTER	0x11
+#define TYPE_IPI3_SLAVE		0x12
+#define TYPE_IPI3_PEER		0x13
+
+/* f_ctl field */
+#define F_CTL_FILL_BYTES	0x000003
+#define F_CTL_XCHG_REASSEMBLE	0x000004
+#define F_CTL_RO_PRESENT	0x000008
+#define F_CTL_ABORT_SEQ		0x000030
+#define F_CTL_CONTINUE_SEQ	0x0000c0
+#define F_CTL_INVALIDATE_XID	0x004000
+#define F_CTL_XID_REASSIGNED	0x008000
+#define F_CTL_SEQ_INITIATIVE	0x010000
+#define F_CTL_CHAINED_SEQ	0x020000
+#define F_CTL_END_CONNECT	0x040000
+#define F_CTL_END_SEQ		0x080000
+#define F_CTL_LAST_SEQ		0x100000
+#define F_CTL_FIRST_SEQ		0x200000
+#define F_CTL_SEQ_CONTEXT	0x400000
+#define F_CTL_XCHG_CONTEXT	0x800000
+
+typedef struct {
+	u32	r_ctl:8,	did:24;
+	u32	xxx1:8,		sid:24;
+	u32	type:8,		f_ctl:24;
+	u32	seq_id:8,	df_ctl:8,	seq_cnt:16;
+	u16	ox_id,		rx_id;
+	u32	param;
+} fc_hdr;
+/* The following are ugly macros to make setup of this structure faster */
+#define FILL_FCHDR_RCTL_DID(fch, r_ctl, did) *(u32 *)(fch) = ((r_ctl) << 24) | (did);
+#define FILL_FCHDR_SID(fch, sid) *((u32 *)(fch)+1) = (sid);
+#define FILL_FCHDR_TYPE_FCTL(fch, type, f_ctl) *((u32 *)(fch)+2) = ((type) << 24) | (f_ctl);
+#define FILL_FCHDR_SEQ_DF_SEQ(fch, seq_id, df_ctl, seq_cnt) *((u32 *)(fch)+3) = ((seq_id) << 24) | ((df_ctl) << 16) | (seq_cnt);
+#define FILL_FCHDR_OXRX(fch, ox_id, rx_id) *((u32 *)(fch)+4) = ((ox_id) << 16) | (rx_id);
+
+/* Well known addresses */
+#define FS_GENERAL_MULTICAST	0xfffff7
+#define FS_WELL_KNOWN_MULTICAST	0xfffff8
+#define FS_HUNT_GROUP		0xfffff9
+#define FS_MANAGEMENT_SERVER	0xfffffa
+#define FS_TIME_SERVER		0xfffffb
+#define FS_NAME_SERVER		0xfffffc
+#define FS_FABRIC_CONTROLLER	0xfffffd
+#define FS_FABRIC_F_PORT	0xfffffe
+#define FS_BROADCAST		0xffffff
+
+/* Reject frames */
+/* The param field should be cast to this structure */
+typedef struct {
+	u8	action;
+	u8	reason;
+	u8	xxx;
+	u8	vendor_unique;
+} rjt_param;
+
+/* Reject action codes */
+#define RJT_RETRY			0x01
+#define RJT_NONRETRY			0x02
+
+/* Reject reason codes */
+#define RJT_INVALID_DID			0x01
+#define RJT_INVALID_SID			0x02
+#define RJT_NPORT_NOT_AVAIL_TEMP	0x03
+#define RJT_NPORT_NOT_AVAIL_PERM	0x04
+#define RJT_CLASS_NOT_SUPPORTED		0x05
+#define RJT_DELIMITER_ERROR		0x06
+#define RJT_TYPE_NOT_SUPPORTED		0x07
+#define RJT_INVALID_LINK_CONTROL	0x08
+#define RJT_INVALID_R_CTL		0x09
+#define RJT_INVALID_F_CTL		0x0a
+#define RJT_INVALID_OX_ID		0x0b
+#define RJT_INVALID_RX_ID		0x0c
+#define RJT_INVALID_SEQ_ID		0x0d
+#define RJT_INVALID_DF_CTL		0x0e
+#define RJT_INVALID_SEQ_CNT		0x0f
+#define RJT_INVALID_PARAMETER		0x10
+#define RJT_EXCHANGE_ERROR		0x11
+#define RJT_PROTOCOL_ERROR		0x12
+#define RJT_INCORRECT_LENGTH		0x13
+#define RJT_UNEXPECTED_ACK		0x14
+#define RJT_UNEXPECTED_LINK_RESP	0x15
+#define RJT_LOGIN_REQUIRED		0x16
+#define RJT_EXCESSIVE_SEQUENCES		0x17
+#define RJT_CANT_ESTABLISH_EXCHANGE	0x18
+#define RJT_SECURITY_NOT_SUPPORTED	0x19
+#define RJT_FABRIC_NA			0x1a
+#define RJT_VENDOR_UNIQUE		0xff
+
+
+#define SP_F_PORT_LOGIN			0x10
+
+/* Extended SVC commands */
+#define LS_RJT			0x01000000
+#define LS_ACC			0x02000000
+#define LS_PLOGI		0x03000000
+#define LS_FLOGI		0x04000000
+#define LS_LOGO			0x05000000
+#define LS_ABTX			0x06000000
+#define LS_RCS			0x07000000
+#define LS_RES			0x08000000
+#define LS_RSS			0x09000000
+#define LS_RSI			0x0a000000
+#define LS_ESTS			0x0b000000
+#define LS_ESTC			0x0c000000
+#define LS_ADVC			0x0d000000
+#define LS_RTV			0x0e000000
+#define LS_RLS			0x0f000000
+#define LS_ECHO			0x10000000
+#define LS_TEST			0x11000000
+#define LS_RRQ			0x12000000
+#define LS_IDENT		0x20000000
+#define LS_DISPLAY		0x21000000
+
+typedef struct {
+	u8	fcph_hi, fcph_lo;
+	u16	buf2buf_credit;
+	u8	common_features;
+	u8	xxx1;
+	u16	buf2buf_size;
+	u8	xxx2;
+	u8	total_concurrent;
+	u16	off_by_info;
+	u32	e_d_tov;
+} common_svc_parm;
+
+typedef struct {
+	u16	serv_opts;
+	u16	initiator_ctl;
+	u16	rcpt_ctl;
+	u16	recv_size;
+	u8	xxx1;
+	u8	concurrent_seqs;
+	u16	end2end_credit;
+	u16	open_seqs_per_xchg;
+	u16	xxx2;
+} svc_parm;
+
+/* Login */
+typedef struct {
+	u32		code;
+	common_svc_parm	common;
+	fc_wwn		nport_wwn;
+	fc_wwn		node_wwn;
+	svc_parm	class1;
+	svc_parm	class2;
+	svc_parm	class3;
+} logi;
+
+#endif /* !(__FC_H) */
diff -u --recursive --new-file v2.1.78/linux/drivers/fc4/fc_syms.c linux/drivers/fc4/fc_syms.c
--- v2.1.78/linux/drivers/fc4/fc_syms.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/fc4/fc_syms.c	Mon Jan 12 15:19:36 1998
@@ -0,0 +1,28 @@
+/*
+ * We should not even be trying to compile this if we are not doing
+ * a module.
+ */
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#ifdef CONFIG_MODULES
+
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include "fcp_scsi.h"
+
+EXPORT_SYMBOL(fcp_init);
+EXPORT_SYMBOL(fcp_queue_empty);
+EXPORT_SYMBOL(fcp_receive_solicited);
+EXPORT_SYMBOL(fc_channels);
+
+/* SCSI stuff */
+EXPORT_SYMBOL(fcp_scsi_queuecommand);
+EXPORT_SYMBOL(fcp_scsi_abort);
+EXPORT_SYMBOL(fcp_scsi_reset);
+
+#endif /* CONFIG_MODULES */
diff -u --recursive --new-file v2.1.78/linux/drivers/fc4/fcp.h linux/drivers/fc4/fcp.h
--- v2.1.78/linux/drivers/fc4/fcp.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/fc4/fcp.h	Mon Jan 12 15:19:36 1998
@@ -0,0 +1,94 @@
+/* fcp.h: Definitions for Fibre Channel Protocol.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ */
+
+#ifndef __FCP_H
+#define __FCP_H
+
+/* FCP addressing is hierarchical with up to 4 layers, MS first.
+   Exact meaning of the addresses is up to the vendor */
+
+/* fcp_cntl field */   
+#define FCP_CNTL_WRITE		0x00000001	/* Initiator write */
+#define FCP_CNTL_READ		0x00000002	/* Initiator read */
+#define FCP_CNTL_ABORT_TSK	0x00000200	/* Abort task set */
+#define FCP_CNTL_CLR_TASK	0x00000400	/* Clear task set */
+#define FCP_CNTL_RESET		0x00002000	/* Reset */
+#define FCP_CNTL_CLR_ACA	0x00004000	/* Clear ACA */
+#define FCP_CNTL_KILL_TASK	0x00008000	/* Terminate task */
+#define FCP_CNTL_QTYPE_MASK	0x00070000	/* Tagged queueing type */
+#define 	FCP_CNTL_QTYPE_SIMPLE		0x00000000
+#define 	FCP_CNTL_QTYPE_HEAD_OF_Q	0x00010000
+#define		FCP_CNTL_QTYPE_ORDERED		0x00020000
+#define 	FCP_CNTL_QTYPE_ACA_Q_TAG	0x00040000
+#define 	FCP_CNTL_QTYPE_UNTAGGED		0x00050000
+
+typedef struct {
+	u16	fcp_addr[4];
+	u32	fcp_cntl;
+	u8	fcp_cdb[16];
+	u32	fcp_data_len;
+} fcp_cmd;
+
+/* fcp_status field */
+#define	FCP_STATUS_MASK		0x000000ff	/* scsi status of command */
+#define FCP_STATUS_RSP_LEN	0x00000100	/* response_len != 0 */
+#define FCP_STATUS_SENSE_LEN	0x00000200	/* sense_len != 0 */
+#define FCP_STATUS_RESID	0x00000400	/* resid != 0 */
+
+typedef struct {
+	u32	xxx[2];
+	u32	fcp_status;
+	u32	fcp_resid;
+	u32	fcp_sense_len;
+	u32	fcp_response_len;
+	/* u8	fcp_sense[fcp_sense_len]; */
+	/* u8	fcp_response[fcp_response_len]; */
+} fcp_rsp;
+
+/* fcp errors */
+
+/* rsp_info_type field */
+#define FCP_RSP_SCSI_BUS_ERR	0x01
+#define FCP_RSP_SCSI_PORT_ERR	0x02
+#define FCP_RSP_CARD_ERR	0x03
+
+/* isp_status field */
+#define FCP_RSP_CMD_COMPLETE	0x0000
+#define FCP_RSP_CMD_INCOMPLETE	0x0001
+#define FCP_RSP_CMD_DMA_ERR	0x0002
+#define FCP_RSP_CMD_TRAN_ERR	0x0003
+#define FCP_RSP_CMD_RESET	0x0004
+#define FCP_RSP_CMD_ABORTED	0x0005
+#define FCP_RSP_CMD_TIMEOUT	0x0006
+#define FCP_RSP_CMD_OVERRUN	0x0007
+
+/* isp_state_flags field */
+#define FCP_RSP_ST_GOT_BUS	0x0100
+#define FCP_RSP_ST_GOT_TARGET	0x0200
+#define FCP_RSP_ST_SENT_CMD	0x0400
+#define FCP_RSP_ST_XFRD_DATA	0x0800
+#define FCP_RSP_ST_GOT_STATUS	0x1000
+#define FCP_RSP_ST_GOT_SENSE	0x2000
+
+/* isp_stat_flags field */
+#define FCP_RSP_STAT_DISC	0x0001
+#define FCP_RSP_STAT_SYNC	0x0002
+#define FCP_RSP_STAT_PERR	0x0004
+#define FCP_RSP_STAT_BUS_RESET	0x0008
+#define FCP_RSP_STAT_DEV_RESET	0x0010
+#define FCP_RSP_STAT_ABORTED	0x0020
+#define FCP_RSP_STAT_TIMEOUT	0x0040
+#define FCP_RSP_STAT_NEGOTIATE	0x0080
+
+typedef struct {
+	u8	rsp_info_type;
+	u8	xxx;
+	u16	isp_status;
+	u16	isp_state_flags;
+	u16	isp_stat_flags;
+} fcp_scsi_err;
+
+#endif /* !(__FCP_H) */
diff -u --recursive --new-file v2.1.78/linux/drivers/fc4/fcp_scsi.h linux/drivers/fc4/fcp_scsi.h
--- v2.1.78/linux/drivers/fc4/fcp_scsi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/fc4/fcp_scsi.h	Mon Jan 12 15:19:36 1998
@@ -0,0 +1,140 @@
+/* fcp_scsi.h: Generic SCSI on top of FC4 - interface defines.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#ifndef _FCP_SCSI_H
+#define _FCP_SCSI_H
+
+#include <linux/types.h>
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+
+#include "fc.h"
+#include "fcp.h"
+
+#ifdef __sparc__
+
+#include <asm/sbus.h>
+typedef u32	dma_handle;
+
+#else
+#error Need to port FC layer to your architecture
+#endif
+
+#define FC_CLASS_OUTBOUND	0x01
+#define FC_CLASS_INBOUND	0x02
+#define FC_CLASS_SIMPLE		0x03
+#define FC_CLASS_IO_WRITE	0x04
+#define FC_CLASS_IO_READ	0x05
+#define FC_CLASS_UNSOLICITED	0x06
+#define FC_CLASS_OFFLINE	0x08
+
+#define PROTO_OFFLINE		0x02
+
+struct _fc_channel; 
+
+typedef struct fcp_cmnd {
+	struct fcp_cmnd		*next;
+	struct fcp_cmnd		*prev;
+	void			(*done)(Scsi_Cmnd *);
+	int			proto;
+	int		 	token;
+	/* FCP SCSI stuff */
+	dma_handle		data;
+	/* From now on this cannot be touched for proto == TYPE_SCSI_FCP */
+	fc_hdr			fch;
+	dma_handle		cmd;
+	dma_handle		rsp;
+	int			cmdlen;
+	int			rsplen;
+	int			class;
+	int			datalen;
+	/* This is just used as a verification during login */
+	struct _fc_channel	*fc;
+} fcp_cmnd;
+
+typedef struct _fc_channel {
+	struct _fc_channel	*next;
+	int			irq;
+	int			state;
+	int			sid;
+	int			did;
+	char			name[16];
+	void			(*fcp_register)(struct _fc_channel *, u8, int);
+	void			(*fcp_state_change)(struct _fc_channel *, int);
+	void			(*reset)(struct _fc_channel *);
+	int			(*hw_enque)(struct _fc_channel *, fcp_cmnd *);
+	fc_wwn			wwn_node;
+	fc_wwn			wwn_nport;
+	fc_wwn			wwn_dest;
+	common_svc_parm		*common_svc;
+	svc_parm		*class_svcs;
+#ifdef __sparc__	
+	struct linux_sbus_device *dev;
+#endif
+	/* FCP SCSI stuff */
+	int			can_queue;
+	int			rsp_size;
+	fcp_cmd			*scsi_cmd_pool;
+	char			*scsi_rsp_pool;
+	dma_handle		dma_scsi_cmd, dma_scsi_rsp;
+	long			*scsi_bitmap;
+	long			scsi_bitmap_end;
+	int			scsi_free;
+	int			(*encode_addr)(Scsi_Cmnd *cmnd, u16 *addr);
+	fcp_cmnd		*scsi_que[2];
+	char			scsi_name[4];
+	fcp_cmnd		**token_tab;
+	int			channels;
+	int			targets;
+	long			*ages;
+	/* LOGIN stuff */
+	fcp_cmnd		*login;
+	void			*ls;
+} fc_channel;
+
+extern fc_channel *fc_channels;
+
+#define FC_STATE_UNINITED	0
+#define FC_STATE_ONLINE		1
+#define FC_STATE_OFFLINE	2
+#define FC_STATE_RESETING	3
+#define FC_STATE_FPORT_OK	4
+#define FC_STATE_MAYBEOFFLINE	5
+
+#define FC_STATUS_OK			0
+#define FC_STATUS_P_RJT			2
+#define FC_STATUS_F_RJT			3
+#define FC_STATUS_P_BSY			4
+#define FC_STATUS_F_BSY			5
+#define FC_STATUS_ERR_OFFLINE		0x11
+#define FC_STATUS_TIMEOUT		0x12
+#define FC_STATUS_ERR_OVERRUN		0x13
+#define FC_STATUS_UNKNOWN_CQ_TYPE	0x20
+#define FC_STATUS_BAD_SEG_CNT		0x21
+#define FC_STATUS_MAX_XCHG_EXCEEDED	0x22
+#define FC_STATUS_BAD_XID		0x23
+#define FC_STATUS_XCHG_BUSY		0x24
+#define FC_STATUS_BAD_POOL_ID		0x25
+#define FC_STATUS_INSUFFICIENT_CQES	0x26
+#define FC_STATUS_ALLOC_FAIL		0x27
+#define FC_STATUS_BAD_SID		0x28
+#define FC_STATUS_NO_SEQ_INIT		0x29
+
+void fcp_queue_empty(fc_channel *);
+int fcp_init(fc_channel *);
+void fcp_receive_solicited(fc_channel *, int, int, int, fc_hdr *);
+
+#define for_each_fc_channel(fc)				\
+	for (fc = fc_channels; fc; fc = fc->next)
+	
+#define for_each_online_fc_channel(fc) 			\
+	for_each_fc_channel(fc)				\
+		if (fc->state == FC_STATE_ONLINE)
+
+int fcp_scsi_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int fcp_scsi_abort(Scsi_Cmnd *);
+int fcp_scsi_reset(Scsi_Cmnd *, unsigned int);
+
+#endif /* !(_FCP_SCSI_H) */
diff -u --recursive --new-file v2.1.78/linux/drivers/fc4/soc.c linux/drivers/fc4/soc.c
--- v2.1.78/linux/drivers/fc4/soc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/fc4/soc.c	Mon Jan 12 15:19:36 1998
@@ -0,0 +1,710 @@
+/* soc.c: Sparc SUNW,soc (Serial Optical Channel) Fibre Channel Sbus adapter support.
+ *
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Jiri Hanika (geo@ff.cuni.cz)
+ *
+ * Sources:
+ *	Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
+ *	dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
+ */
+
+static char *version =
+        "soc.c:v1.0 24/Nov/97 Jakub Jelinek (jj@sunsite.mff.cuni.cz), Jiri Hanika (geo@ff.cuni.cz)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <asm/byteorder.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/auxio.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+
+/* #define SOCDEBUG */
+/* #define HAVE_SOC_UCODE */
+
+#include "fcp_scsi.h"
+#include "soc.h"
+#ifdef HAVE_SOC_UCODE
+#include "soc_asm.c"
+#endif
+
+#define soc_printk printk ("soc%d: ", s->soc_no); printk 
+
+#ifdef SOCDEBUG
+#define SOD(x)  soc_printk x;
+#else
+#define SOD(x)
+#endif
+
+#define for_each_soc(s) for (s = socs; s; s = s->next)
+struct soc *socs = NULL;
+
+static inline void soc_disable(struct soc *s)
+{
+	s->regs->imask = 0; s->regs->cmd = SOC_CMD_SOFT_RESET;
+}
+
+static inline void soc_enable(struct soc *s)
+{
+	SOD(("enable %08x\n", s->cfg))
+	s->regs->sae = 0; s->regs->cfg = s->cfg;
+	s->regs->cmd = SOC_CMD_RSP_QALL; 
+	SOC_SETIMASK(s, SOC_IMASK_RSP_QALL | SOC_IMASK_SAE);
+}
+
+static void soc_reset(fc_channel *fc)
+{
+	soc_port *port = (soc_port *)fc;
+	struct soc *s = port->s;
+	
+	/* FIXME */
+	soc_disable(s);
+	s->req[0].seqno = 1;
+	s->req[1].seqno = 1;
+	s->rsp[0].seqno = 1;
+	s->rsp[1].seqno = 1;
+	s->req[0].in = 0;
+	s->req[1].in = 0;
+	s->rsp[0].in = 0;
+	s->rsp[1].in = 0;
+	s->req[0].out = 0;
+	s->req[1].out = 0;
+	s->rsp[0].out = 0;
+	s->rsp[1].out = 0;
+
+	/* FIXME */
+	soc_enable(s);
+}
+
+static void inline soc_solicited (struct soc *s)
+{
+	fc_hdr fchdr;
+	soc_rsp *hwrsp;
+	soc_cq *sw_cq;
+	int token;
+	int status;
+	fc_channel *fc;
+
+	sw_cq = &s->rsp[SOC_SOLICITED_RSP_Q];
+
+	if (sw_cq->pool == NULL)
+		sw_cq->pool =
+			(soc_req *)(s->xram + (xram_get_32low ((xram_p)&sw_cq->hw_cq->address) / sizeof(u16)));
+	sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+	SOD (("soc_solicited, %d packets arrived\n", (sw_cq->in - sw_cq->out) & sw_cq->last))
+	for (;;) {
+		hwrsp = (soc_rsp *)sw_cq->pool + sw_cq->out;
+		token = xram_get_32low ((xram_p)&hwrsp->shdr.token);
+		status = xram_get_32low ((xram_p)&hwrsp->status);
+		fc = (fc_channel *)(&s->port[(token >> 11) & 1]);
+		
+		if (status == SOC_OK)
+			fcp_receive_solicited(fc, token >> 12, token & ((1 << 11) - 1), FC_STATUS_OK, NULL);
+		else {
+			xram_copy_from(&fchdr, (xram_p)&hwrsp->fchdr, sizeof(fchdr));
+			/* We have intentionally defined FC_STATUS_* constants to match SOC_* constants, otherwise
+			   we'd have to translate status */
+			fcp_receive_solicited(fc, token >> 12, token & ((1 << 11) - 1), status, &fchdr);
+		}
+			
+		if (++sw_cq->out > sw_cq->last) {
+			sw_cq->seqno++;
+			sw_cq->out = 0;
+		}
+		
+		if (sw_cq->out == sw_cq->in) {
+			sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+			if (sw_cq->out == sw_cq->in) {
+				/* Tell the hardware about it */
+				s->regs->cmd = (sw_cq->out << 24) | (SOC_CMD_RSP_QALL & ~(SOC_CMD_RSP_Q0 << SOC_SOLICITED_RSP_Q));
+				/* Read it, so that we're sure it has been updated */
+				s->regs->cmd;
+				sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+				if (sw_cq->out == sw_cq->in)
+					break;
+			}
+		}
+	}
+}
+
+static void inline soc_request (struct soc *s, u32 cmd)
+{
+	SOC_SETIMASK(s, s->imask & ~(cmd & SOC_CMD_REQ_QALL));
+
+	SOD(("Queues available %08x OUT %X %X\n", cmd, xram_get_8((xram_p)&s->req[0].hw_cq->out), xram_get_8((xram_p)&s->req[0].hw_cq->out)))
+	if (s->port[s->curr_port].fc.state != FC_STATE_OFFLINE) {
+		fcp_queue_empty ((fc_channel *)&(s->port[s->curr_port]));
+		if (((s->req[1].in + 1) & s->req[1].last) != (s->req[1].out))
+			fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port]));
+	} else
+		fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port]));
+	if (s->port[1 - s->curr_port].fc.state != FC_STATE_OFFLINE)
+		s->curr_port ^= 1;
+}
+
+static void inline soc_unsolicited (struct soc *s)
+{
+	soc_rsp *hwrsp, *hwrspc;
+	soc_cq *sw_cq;
+	int count;
+	int status;
+	int flags;
+	fc_channel *fc;
+
+	sw_cq = &s->rsp[SOC_UNSOLICITED_RSP_Q];
+	if (sw_cq->pool == NULL)
+		sw_cq->pool =
+			(soc_req *)(s->xram + (xram_get_32low ((xram_p)&sw_cq->hw_cq->address) / sizeof(u16)));
+
+	sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+	SOD (("soc_unsolicited, %d packets arrived\n", (sw_cq->in - sw_cq->out) & sw_cq->last))
+	while (sw_cq->in != sw_cq->out) {
+		/* ...real work per entry here... */
+		hwrsp = (soc_rsp *)sw_cq->pool + sw_cq->out;
+		hwrspc = NULL;
+		flags = hwrsp->shdr.flags;
+		count = xram_get_8 ((xram_p)&hwrsp->count);
+		fc = (fc_channel *)&s->port[flags & SOC_PORT_B];
+		SOD(("FC %08lx fcp_state_change %08lx\n", (long)fc, (long)fc->fcp_state_change))
+		
+		if (count != 1) {
+			/* Ugh, continuation entries */
+			u8 in;
+
+			if (count != 2) {
+				printk("%s: Too many continuations entries %d\n", fc->name, count);
+				goto update_out;
+			}
+			
+			in = sw_cq->in;
+			if (in < sw_cq->out) in += sw_cq->last + 1;
+			if (in < sw_cq->out + 2) {
+				/* Ask the hardware about it if they haven't arrived yet */
+				s->regs->cmd = (sw_cq->out << 24) | (SOC_CMD_RSP_QALL & ~(SOC_CMD_RSP_Q0 << SOC_UNSOLICITED_RSP_Q));
+				/* Read it, so that we're sure it has been updated */
+				s->regs->cmd;
+				sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+				in = sw_cq->in;
+				if (in < sw_cq->out) in += sw_cq->last + 1;
+				if (in < sw_cq->out + 2) /* Nothing came, let us wait */
+					return;
+			}
+			if (sw_cq->out == sw_cq->last)
+				hwrspc = (soc_rsp *)sw_cq->pool;
+			else
+				hwrspc = hwrsp + 1;
+		}
+		
+		switch (flags & ~SOC_PORT_B) {
+		case SOC_STATUS:
+			status = xram_get_32low ((xram_p)&hwrsp->status);
+			switch (status) {
+			case SOC_ONLINE:
+				fc->fcp_state_change(fc, FC_STATE_ONLINE);
+				break;
+			case SOC_OFFLINE:
+				fc->fcp_state_change(fc, FC_STATE_OFFLINE);
+				break;
+			default:
+				printk ("%s: Unknown STATUS no %d\n", fc->name, status);
+				break;
+			}
+			break;
+		case (SOC_UNSOLICITED|SOC_FC_HDR):
+			{
+				int r_ctl = xram_get_8 ((xram_p)&hwrsp->fchdr);
+				unsigned len;
+				char buf[64];
+				
+				if ((r_ctl & 0xf0) == R_CTL_EXTENDED_SVC) {
+					len = xram_get_32 ((xram_p)&hwrsp->shdr.bytecnt);
+					if (len < 4 || !hwrspc)
+						printk ("%s: Invalid R_CTL %02x continuation entries\n", fc->name, r_ctl);
+					else {
+						if (len > 60) len = 60;
+						xram_copy_from (buf, (xram_p)hwrspc, (len + 3) & ~3);
+						if (*(u32 *)buf == LS_DISPLAY) {
+							int i;
+							
+							for (i = 4; i < len; i++)
+								if (buf[i] == '\n') buf[i] = ' ';
+							buf[len] = 0;
+							printk ("%s message: %s\n", fc->name, buf + 4);
+						} else
+							printk ("%s: Unknown LS_CMD %02x\n", fc->name, buf[0]);
+					}
+				} else
+					printk ("%s: Unsolicited R_CTL %02x not handled\n", fc->name, r_ctl);
+			}
+			break;
+		default:
+			printk ("%s: Unexpected flags %08x\n", fc->name, flags);
+			break;
+		}
+update_out:
+		if (++sw_cq->out > sw_cq->last) {
+			sw_cq->seqno++;
+			sw_cq->out = 0;
+		}
+		
+		if (hwrspc) {
+			if (++sw_cq->out > sw_cq->last) {
+				sw_cq->seqno++;
+				sw_cq->out = 0;
+			}
+		}
+		
+		if (sw_cq->out == sw_cq->in) {
+			sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+			if (sw_cq->out == sw_cq->in) {
+				/* Tell the hardware about it */
+				s->regs->cmd = (sw_cq->out << 24) | (SOC_CMD_RSP_QALL & ~(SOC_CMD_RSP_Q0 << SOC_UNSOLICITED_RSP_Q));
+				/* Read it, so that we're sure it has been updated */
+				s->regs->cmd;
+				sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+			}
+		}
+	}
+}
+
+static void soc_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	u32 cmd;
+	register struct soc *s = (struct soc *)dev_id;
+
+	cmd = s->regs->cmd;
+	for (; (cmd = SOC_INTR (s, cmd)); cmd = s->regs->cmd) {
+		if (cmd & SOC_CMD_RSP_Q1) soc_unsolicited (s);
+		if (cmd & SOC_CMD_RSP_Q0) soc_solicited (s);
+		if (cmd & SOC_CMD_REQ_QALL) soc_request (s, cmd);
+	}
+}
+
+#define TOKEN(proto, port, token) (((proto)<<12)|(token)|(port))
+
+static int soc_hw_enque (fc_channel *fc, fcp_cmnd *fcmd)
+{
+	soc_port *port = (soc_port *)fc;
+	struct soc *s = port->s;
+	int qno;
+	soc_cq *sw_cq;
+	int cq_next_in;
+	soc_req *request;
+	fc_hdr *fch;
+	int i;
+
+	if (fcmd->proto == TYPE_SCSI_FCP)
+		qno = 1;
+	else
+		qno = 0;
+	SOD(("Putting a FCP packet type %d into hw queue %d\n", fcmd->proto, qno))
+	if (s->imask & (SOC_IMASK_REQ_Q0 << qno))
+		return -EIO;
+	sw_cq = s->req + qno;
+	cq_next_in = (sw_cq->in + 1) & sw_cq->last;
+	
+	if (cq_next_in == sw_cq->out 
+		    && cq_next_in == (sw_cq->out = xram_get_8((xram_p)&sw_cq->hw_cq->out))) {
+		SOD(("%d IN %d OUT %d LAST %d\n", qno, sw_cq->in, sw_cq->out, sw_cq->last))
+		SOC_SETIMASK(s, s->imask | (SOC_IMASK_REQ_Q0 << qno));
+		return -EBUSY;  // If queue full, just say NO
+	}
+	
+	request = sw_cq->pool + sw_cq->in;
+	fch = &request->fchdr;
+	
+	switch (fcmd->proto) {
+	case TYPE_SCSI_FCP:
+		request->shdr.token = TOKEN(TYPE_SCSI_FCP, port->mask, fcmd->token); 
+		request->data[0].base = fc->dma_scsi_cmd + fcmd->token * sizeof(fcp_cmd);
+		request->data[0].count = sizeof(fcp_cmd);
+		request->data[1].base = fc->dma_scsi_rsp + fcmd->token * fc->rsp_size;
+		request->data[1].count = fc->rsp_size;
+		if (fcmd->data) {
+			request->shdr.segcnt = 3;
+			i = fc->scsi_cmd_pool[fcmd->token].fcp_data_len;
+			request->shdr.bytecnt = i;
+			request->data[2].base = fcmd->data;
+			request->data[2].count = i;
+			request->type = (fc->scsi_cmd_pool[fcmd->token].fcp_cntl & FCP_CNTL_WRITE) ?
+				SOC_CQTYPE_IO_WRITE : SOC_CQTYPE_IO_READ;
+		} else {
+			request->shdr.segcnt = 2;
+			request->shdr.bytecnt = 0;
+			request->data[2].base = 0;
+			request->data[2].count = 0;
+			request->type = SOC_CQTYPE_SIMPLE;
+		}
+		FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fc->did);
+		FILL_FCHDR_SID(fch, fc->sid);
+		FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
+		FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
+		FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
+		fch->param = 0;
+		request->shdr.flags = port->flags;
+		request->shdr.class = 2;
+		break;
+		
+	case PROTO_OFFLINE:
+		memset (request, 0, sizeof(*request));
+		request->shdr.token = TOKEN(PROTO_OFFLINE, port->mask, fcmd->token); 
+		request->type = SOC_CQTYPE_OFFLINE;
+		FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fc->did);
+		FILL_FCHDR_SID(fch, fc->sid);
+		FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
+		FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
+		FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
+		request->shdr.flags = port->flags;
+		break;
+
+	default: 
+		request->shdr.token = TOKEN(fcmd->proto, port->mask, fcmd->token);
+		request->shdr.class = 2;
+		request->shdr.flags = port->flags;
+		memcpy (fch, &fcmd->fch, sizeof(fc_hdr));
+		request->data[0].count = fcmd->cmdlen;
+		request->data[1].count = fcmd->rsplen;
+		request->type = fcmd->class;
+		switch (fcmd->class) {
+		case FC_CLASS_OUTBOUND:
+			request->data[0].base = fcmd->cmd;
+			request->data[0].count = fcmd->cmdlen;
+			request->type = SOC_CQTYPE_OUTBOUND;
+			request->shdr.bytecnt = fcmd->cmdlen;
+			request->shdr.segcnt = 1;
+			break;
+		case FC_CLASS_INBOUND:
+			request->data[0].base = fcmd->rsp;
+			request->data[0].count = fcmd->rsplen;
+			request->type = SOC_CQTYPE_INBOUND;
+			request->shdr.bytecnt = 0;
+			request->shdr.segcnt = 1;
+			break;
+		case FC_CLASS_SIMPLE:
+			request->data[0].base = fcmd->cmd;
+			request->data[1].base = fcmd->rsp;
+			request->data[0].count = fcmd->cmdlen;
+			request->data[1].count = fcmd->rsplen;
+			request->type = SOC_CQTYPE_SIMPLE;
+			request->shdr.bytecnt = fcmd->cmdlen;
+			request->shdr.segcnt = 2;
+			break;
+		case FC_CLASS_IO_READ:
+		case FC_CLASS_IO_WRITE:
+			request->data[0].base = fcmd->cmd;
+			request->data[1].base = fcmd->rsp;
+			request->data[0].count = fcmd->cmdlen;
+			request->data[1].count = fcmd->rsplen;
+			request->type = (fcmd->class == FC_CLASS_IO_READ) ? SOC_CQTYPE_IO_READ : SOC_CQTYPE_IO_WRITE;
+			if (fcmd->data) {
+				request->data[2].base = fcmd->data;
+				request->data[2].count = fcmd->datalen;
+				request->shdr.bytecnt = fcmd->datalen;
+				request->shdr.segcnt = 3;
+			} else {
+				request->shdr.bytecnt = 0;
+				request->shdr.segcnt = 2;
+			}
+			break;
+		}
+		break;
+	}
+
+	request->count = 1;
+	request->flags = 0;
+	request->seqno = sw_cq->seqno;
+	
+	/* And now tell the SOC about it */
+
+	if (++sw_cq->in > sw_cq->last) {
+		sw_cq->in = 0;
+		sw_cq->seqno++;
+	}
+	
+	SOD(("Putting %08x into cmd\n", SOC_CMD_RSP_QALL | (sw_cq->in << 24) | (SOC_CMD_REQ_Q0 << qno)))
+	
+	s->regs->cmd = SOC_CMD_RSP_QALL | (sw_cq->in << 24) | (SOC_CMD_REQ_Q0 << qno);
+	/* Read so that command is completed */	
+	s->regs->cmd;
+	
+	return 0;
+}
+
+static inline void soc_download_fw(struct soc *s)
+{
+#ifdef HAVE_SOC_UCODE
+	xram_copy_to (s->xram, soc_ucode, sizeof(soc_ucode));
+	xram_bzero (s->xram + (sizeof(soc_ucode)/sizeof(u16)), 32768 - sizeof(soc_ucode));
+#endif
+}
+
+/* Check for what the best SBUS burst we can use happens
+ * to be on this machine.
+ */
+static inline void soc_init_bursts(struct soc *s, struct linux_sbus_device *sdev)
+{
+	int bsizes, bsizes_more;
+
+	bsizes = (prom_getintdefault(sdev->prom_node,"burst-sizes",0xff) & 0xff);
+	bsizes_more = (prom_getintdefault(sdev->my_bus->prom_node, "burst-sizes", 0xff) & 0xff);
+	bsizes &= bsizes_more;
+	if ((bsizes & 0x7f) == 0x7f)
+		s->cfg = SOC_CFG_BURST_64;
+	else if ((bsizes & 0x3f) == 0x3f) 
+		s->cfg = SOC_CFG_BURST_32;
+	else if ((bsizes & 0x1f) == 0x1f)
+		s->cfg = SOC_CFG_BURST_16;
+	else
+		s->cfg = SOC_CFG_BURST_4;
+}
+
+static inline void soc_init(struct linux_sbus_device *sdev, int no)
+{
+#ifdef __sparc_v9__
+        struct devid_cookie dcookie;
+#endif
+	unsigned char tmp[60];
+	int propl;
+	struct soc *s;
+	static unsigned version_printed = 0;
+	soc_hw_cq cq[8];
+	int size, i;
+	int irq;
+	
+	s = kmalloc (sizeof (struct soc), GFP_KERNEL);
+	if (!s) return;
+	memset (s, 0, sizeof(struct soc));
+	s->soc_no = no;
+
+	SOD(("socs %08lx soc_intr %08lx soc_hw_enque %08x\n", (long)socs, (long)soc_intr, (long)soc_hw_enque))	
+	if (version_printed++ == 0)
+		printk (version);
+		
+	s->next = socs;
+	socs = s;
+	s->port[0].fc.dev = sdev;
+	s->port[1].fc.dev = sdev;
+	s->port[0].s = s;
+	s->port[1].s = s;
+
+	s->port[0].fc.next = &s->port[1].fc;
+
+	/* World Wide Name of SOC */
+	propl = prom_getproperty (sdev->prom_node, "soc-wwn", tmp, sizeof(tmp));
+	if (propl != sizeof (fc_wwn)) {
+		s->wwn.naaid = NAAID_IEEE;
+		s->wwn.lo = 0x12345678;
+	} else
+		memcpy (&s->wwn, tmp, sizeof (fc_wwn));
+		
+	propl = prom_getproperty (sdev->prom_node, "port-wwns", tmp, sizeof(tmp));
+	if (propl != 2 * sizeof (fc_wwn)) {
+		s->port[0].fc.wwn_nport.naaid = NAAID_IEEE_EXT;
+		s->port[0].fc.wwn_nport.hi = s->wwn.hi;
+		s->port[0].fc.wwn_nport.lo = s->wwn.lo;
+		s->port[1].fc.wwn_nport.naaid = NAAID_IEEE_EXT;
+		s->port[1].fc.wwn_nport.nportid = 1;
+		s->port[1].fc.wwn_nport.hi = s->wwn.hi;
+		s->port[1].fc.wwn_nport.lo = s->wwn.lo;
+	} else {
+		memcpy (&s->port[0].fc.wwn_nport, tmp, sizeof (fc_wwn));
+		memcpy (&s->port[1].fc.wwn_nport, tmp + sizeof (fc_wwn), sizeof (fc_wwn));
+	}
+	memcpy (&s->port[0].fc.wwn_node, &s->wwn, sizeof (fc_wwn));
+	memcpy (&s->port[1].fc.wwn_node, &s->wwn, sizeof (fc_wwn));
+	SOD(("Got wwns %08x%08x ports %08x%08x and %08x%08x\n", 
+		*(u32 *)&s->port[0].fc.wwn_nport, s->port[0].fc.wwn_nport.lo,
+		*(u32 *)&s->port[0].fc.wwn_nport, s->port[0].fc.wwn_nport.lo,
+		*(u32 *)&s->port[1].fc.wwn_nport, s->port[1].fc.wwn_nport.lo))
+		
+	s->port[0].fc.sid = 1;
+	s->port[1].fc.sid = 17;
+	s->port[0].fc.did = 2;
+	s->port[1].fc.did = 18;
+	
+	s->port[0].fc.reset = soc_reset;
+	s->port[1].fc.reset = soc_reset;
+	
+	/* Setup the reg property for this device. */
+	prom_apply_sbus_ranges(sdev->my_bus, sdev->reg_addrs, sdev->num_registers, sdev);
+                                                                                                                                               	
+	if (sdev->num_registers == 1) {
+		/* Probably SunFire onboard SOC */
+		s->xram = (xram_p)
+			sparc_alloc_io (sdev->reg_addrs [0].phys_addr, 0, 
+				sdev->reg_addrs [0].reg_size, "soc_xram",
+				sdev->reg_addrs [0].which_io, 0);
+		s->regs = (struct soc_regs *)((char *)s->xram + 0x10000);
+	} else {
+		/* Probably SOC sbus card */
+		s->xram = (xram_p)
+			sparc_alloc_io (sdev->reg_addrs [1].phys_addr, 0, 
+				sdev->reg_addrs [1].reg_size, "soc_xram",
+				sdev->reg_addrs [1].which_io, 0);
+		s->regs = (struct soc_regs *)
+			sparc_alloc_io (sdev->reg_addrs [2].phys_addr, 0, 
+				sdev->reg_addrs [2].reg_size, "soc_regs",
+				sdev->reg_addrs [2].which_io, 0);
+	}
+	
+	soc_init_bursts(s, sdev);
+	
+	SOD(("Disabling SOC\n"))
+	
+	soc_disable (s);
+	
+	irq = sdev->irqs[0].pri;
+
+#ifndef __sparc_v9__	
+	if (request_irq (irq, soc_intr, SA_SHIRQ, "SOC", (void *)s)) {
+		soc_printk ("Cannot order irq %d to go\n", irq);
+		return;
+	}
+#else
+	dcookie.real_dev_id = s;
+	dcookie.imap = dcookie.iclr = 0;
+	dcookie.pil = -1;
+	dcookie.bus_cookie = sdev->my_bus;
+	if (request_irq (irq, soc_intr, (SA_SHIRQ | SA_SBUS | SA_DCOOKIE), "SOC", &dcookie)) {
+		soc_printk ("Cannot order irq %d to go\n", irq);
+		return;
+	}
+	irq = dcookie.ret_ino;
+#endif
+
+	SOD(("SOC uses IRQ%d\n", irq))
+	
+	s->port[0].fc.irq = irq;
+	s->port[1].fc.irq = irq;
+	
+	sprintf (s->port[0].fc.name, "soc%d port A", no);
+	sprintf (s->port[1].fc.name, "soc%d port B", no);
+	s->port[0].flags = SOC_FC_HDR | SOC_PORT_A;
+	s->port[1].flags = SOC_FC_HDR | SOC_PORT_B;
+	s->port[1].mask = (1 << 11);
+	
+	s->port[0].fc.hw_enque = soc_hw_enque;
+	s->port[1].fc.hw_enque = soc_hw_enque;
+	
+	soc_download_fw (s);
+	
+	SOD(("Downloaded firmware\n"))
+
+	/* Now setup xram circular queues */
+	memset (cq, 0, sizeof(cq));
+	
+	size = (SOC_CQ_REQ0_SIZE + SOC_CQ_REQ1_SIZE) * sizeof(soc_req);
+	s->req[0].pool = (soc_req *) sparc_dvma_malloc (size, "SOC request queues", &cq[0].address);
+	s->req[1].pool = s->req[0].pool + SOC_CQ_REQ0_SIZE;
+	
+	s->req[0].hw_cq = (soc_hw_cq *)(s->xram + SOC_CQ_REQ_OFFSET);
+	s->req[1].hw_cq = (soc_hw_cq *)(s->xram + SOC_CQ_REQ_OFFSET + sizeof(soc_hw_cq) / sizeof(u16));
+	s->rsp[0].hw_cq = (soc_hw_cq *)(s->xram + SOC_CQ_RSP_OFFSET);
+	s->rsp[1].hw_cq = (soc_hw_cq *)(s->xram + SOC_CQ_RSP_OFFSET + sizeof(soc_hw_cq) / sizeof(u16));
+	
+	cq[1].address = cq[0].address + (SOC_CQ_REQ0_SIZE * sizeof(soc_req));
+	cq[4].address = 1;
+	cq[5].address = 1;
+	cq[0].last = SOC_CQ_REQ0_SIZE - 1;
+	cq[1].last = SOC_CQ_REQ1_SIZE - 1;
+	cq[4].last = SOC_CQ_RSP0_SIZE - 1;
+	cq[5].last = SOC_CQ_RSP1_SIZE - 1;
+	for (i = 0; i < 8; i++)
+		cq[i].seqno = 1;
+	
+	s->req[0].last = SOC_CQ_REQ0_SIZE - 1;
+	s->req[1].last = SOC_CQ_REQ1_SIZE - 1;
+	s->rsp[0].last = SOC_CQ_RSP0_SIZE - 1;
+	s->rsp[1].last = SOC_CQ_RSP1_SIZE - 1;
+	
+	s->req[0].seqno = 1;
+	s->req[1].seqno = 1;
+	s->rsp[0].seqno = 1;
+	s->rsp[1].seqno = 1;
+
+	xram_copy_to (s->xram + SOC_CQ_REQ_OFFSET, cq, sizeof(cq));
+	
+	/* Make our sw copy of SOC service parameters */
+	xram_copy_from (s->serv_params, s->xram + 0x140, sizeof (s->serv_params));
+	
+	s->port[0].fc.common_svc = (common_svc_parm *)s->serv_params;
+	s->port[0].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20);
+	s->port[1].fc.common_svc = (common_svc_parm *)&s->serv_params;
+	s->port[1].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20);
+	
+	soc_enable (s);
+	
+	SOD(("Enabled SOC\n"))
+}
+
+#ifndef MODULE
+__initfunc(int soc_probe(void))
+#else
+int init_module(void)
+#endif
+{
+	struct linux_sbus *bus;
+	struct linux_sbus_device *sdev = 0;
+	struct soc *s;
+	int cards = 0;
+
+	for_each_sbus(bus) {
+		for_each_sbusdev(sdev, bus) {
+			if(!strcmp(sdev->prom_name, "SUNW,soc")) {
+				soc_init(sdev, cards);
+				cards++;
+			}
+		}
+	}
+	if (!cards) return -EIO;
+
+	for_each_soc(s)
+		if (s->next)
+			s->port[1].fc.next = &s->next->port[0].fc;
+	fcp_init (&socs->port[0].fc);
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	struct soc *s;
+	int irq;
+	struct linux_sbus_device *sdev;
+	
+	for_each_soc(s) {
+		/* FIXME: Tell FC layer we say good bye */
+		irq = s->port[0].fc.irq;
+		disable_irq (irq);
+		free_irq (irq, s);
+		sdev = s->port[0].fc.dev;
+		if (sdev->num_registers == 1)
+			sparc_free_io ((char *)s->xram, sdev->reg_addrs [0].reg_size);
+		else {
+			sparc_free_io ((char *)s->xram, sdev->reg_addrs [1].reg_size);
+			sparc_free_io ((char *)s->regs, sdev->reg_addrs [2].reg_size);
+		}
+		/* FIXME: sparc_dvma_free() ??? */
+	}
+}
+#endif
diff -u --recursive --new-file v2.1.78/linux/drivers/fc4/soc.h linux/drivers/fc4/soc.h
--- v2.1.78/linux/drivers/fc4/soc.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/fc4/soc.h	Mon Jan 12 15:19:36 1998
@@ -0,0 +1,275 @@
+/* soc.h: Definitions for Sparc SUNW,soc Fibre Channel Sbus driver.
+ *
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#ifndef __SOC_H
+#define __SOC_H
+
+#include "fc.h"
+#include "fcp.h"
+#include "fcp_scsi.h"
+
+/* Hardware structures and constants first {{{ */
+
+struct soc_regs {
+	volatile u32 cfg;			/* Config Register */
+	volatile u32 sae;			/* Slave Access Error Register */
+	volatile u32 cmd;			/* Command & Status Register */
+	volatile u32 imask;			/* Interrupt Mask Register */
+};
+
+/* Config Register */
+#define SOC_CFG_EXT_RAM_BANK_MASK	0x07000000
+#define SOC_CFG_EEPROM_BANK_MASK	0x00030000
+#define SOC_CFG_BURST64_MASK		0x00000700
+#define SOC_CFG_SBUS_PARITY_TEST	0x00000020
+#define SOC_CFG_SBUS_PARITY_CHECK	0x00000010
+#define SOC_CFG_SBUS_ENHANCED		0x00000008
+#define SOC_CFG_BURST_MASK		0x00000007
+/* Bursts */
+#define SOC_CFG_BURST_4			0x00000000
+#define SOC_CFG_BURST_16		0x00000004
+#define SOC_CFG_BURST_32		0x00000005
+#define SOC_CFG_BURST_64		0x00000006
+
+/* Slave Access Error Register */
+#define SOC_SAE_ALIGNMENT		0x00000004
+#define SOC_SAE_UNSUPPORTED		0x00000002
+#define SOC_SAE_PARITY			0x00000001
+
+/* Command & Status Register */
+#define SOC_CMD_RSP_QALL		0x000f0000
+#define SOC_CMD_RSP_Q0			0x00010000
+#define SOC_CMD_RSP_Q1			0x00020000
+#define SOC_CMD_RSP_Q2			0x00040000
+#define SOC_CMD_RSP_Q3			0x00080000
+#define SOC_CMD_REQ_QALL		0x00000f00
+#define SOC_CMD_REQ_Q0			0x00000100
+#define SOC_CMD_REQ_Q1			0x00000200
+#define SOC_CMD_REQ_Q2			0x00000400
+#define SOC_CMD_REQ_Q3			0x00000800
+#define SOC_CMD_SAE			0x00000080
+#define SOC_CMD_INTR_PENDING		0x00000008
+#define SOC_CMD_NON_QUEUED		0x00000004
+#define SOC_CMD_IDLE			0x00000002
+#define SOC_CMD_SOFT_RESET		0x00000001
+
+/* Interrupt Mask Register */
+#define SOC_IMASK_RSP_QALL		0x000f0000
+#define SOC_IMASK_RSP_Q0		0x00010000
+#define SOC_IMASK_RSP_Q1		0x00020000
+#define SOC_IMASK_RSP_Q2		0x00040000
+#define SOC_IMASK_RSP_Q3		0x00080000
+#define SOC_IMASK_REQ_QALL		0x00000f00
+#define SOC_IMASK_REQ_Q0		0x00000100
+#define SOC_IMASK_REQ_Q1		0x00000200
+#define SOC_IMASK_REQ_Q2		0x00000400
+#define SOC_IMASK_REQ_Q3		0x00000800
+#define SOC_IMASK_SAE			0x00000080
+#define SOC_IMASK_NON_QUEUED		0x00000004
+
+#define SOC_INTR(s, cmd) \
+	(((cmd & SOC_CMD_RSP_QALL) | ((~cmd) & SOC_CMD_REQ_QALL)) \
+	 & s->imask)
+	 
+#define SOC_SETIMASK(s, i) \
+	(s)->imask = (i); (s)->regs->imask = (i)
+
+/* XRAM
+ *
+ * This is a 64KB register area. It accepts only halfword access.
+ * That's why here are the following inline functions...
+ */
+ 
+typedef u16 *xram_p;
+
+/* Get 32bit number from XRAM */
+static inline u32 xram_get_32 (xram_p x)
+{
+	return (((u32)*x) << 16) | (x[1]);
+}
+
+/* Like the above, but when we don't care about the high 16 bits */
+static inline u32 xram_get_32low (xram_p x)
+{
+	return (u32)x[1];
+}
+
+static inline u8 xram_get_8 (xram_p x)
+{
+	if (((long)x) & 1) {
+		x = (xram_p)((long)x - 1);
+		return (u8)*x;
+	} else
+		return (u8)(*x >> 8);
+}
+
+static inline void xram_copy_from (void *p, xram_p x, int len)
+{
+	for (len >>= 2; len > 0; len--, x += 2) {
+		*((u32 *)p)++ = (((u32)(*x)) << 16) | (x[1]);
+	}
+}
+
+static inline void xram_copy_to (xram_p x, void *p, int len)
+{
+	register u32 tmp;
+	for (len >>= 2; len > 0; len--, x += 2) {
+		tmp = *((u32 *)p)++;
+		*x = tmp >> 16;
+		x[1] = tmp;
+	}
+}
+
+static inline void xram_bzero (xram_p x, int len)
+{
+	for (len >>= 1; len > 0; len--) *x++ = 0;
+}
+
+/* Circular Queue */
+
+/* These two are in sizeof(u16) units */
+#define SOC_CQ_REQ_OFFSET	0x100
+#define SOC_CQ_RSP_OFFSET	0x110
+
+typedef struct {
+	u32			address;
+	u8			in;
+	u8			out;
+	u8			last;
+	u8			seqno;
+} soc_hw_cq;
+
+#define SOC_PORT_A	0x0000	/* From/To Port A */
+#define SOC_PORT_B	0x0001	/* From/To Port A */
+#define SOC_FC_HDR	0x0002  /* Contains FC Header */
+#define SOC_NORSP	0x0004  /* Don't generate response nor interrupt */
+#define SOC_NOINT	0x0008  /* Generate response but not interrupt */
+#define SOC_XFERRDY	0x0010  /* Generate XFERRDY */
+#define SOC_IGNOREPARAM	0x0020	/* Ignore PARAM field in the FC header */
+#define SOC_COMPLETE	0x0040  /* Command completed */
+#define SOC_UNSOLICITED	0x0080	/* For request this is the packet to establish unsolicited pools, */
+				/* for rsp this is unsolicited packet */
+#define SOC_STATUS	0x0100	/* State change (on/off line) */
+
+typedef struct {
+	u32			token;
+	u16			flags;
+	u8			class;
+	u8			segcnt;
+	u32			bytecnt;
+} soc_hdr;
+
+typedef struct {
+	u32			base;
+	u32			count;
+} soc_data;
+
+#define SOC_CQTYPE_OUTBOUND	0x01
+#define SOC_CQTYPE_INBOUND	0x02
+#define SOC_CQTYPE_SIMPLE	0x03
+#define SOC_CQTYPE_IO_WRITE	0x04
+#define SOC_CQTYPE_IO_READ	0x05
+#define SOC_CQTYPE_UNSOLICITED	0x06
+#define SOC_CQTYPE_DIAG		0x07
+#define SOC_CQTYPE_OFFLINE	0x08
+#define SOC_CQTYPE_RESPONSE	0x10
+#define SOC_CQTYPE_INLINE	0x20
+
+#define SOC_CQFLAGS_CONT	0x01
+#define SOC_CQFLAGS_FULL	0x02
+#define SOC_CQFLAGS_BADHDR	0x04
+#define SOC_CQFLAGS_BADPKT	0x08
+
+typedef struct {
+	soc_hdr			shdr;
+	soc_data		data[3];
+	fc_hdr			fchdr;
+	u8			count;
+	u8			type;
+	u8			flags;
+	u8			seqno;
+} soc_req;
+
+#define SOC_OK			0
+#define SOC_P_RJT		2
+#define SOC_F_RJT		3
+#define SOC_P_BSY		4
+#define SOC_F_BSY		5
+#define SOC_ONLINE		0x10
+#define SOC_OFFLINE		0x11
+#define SOC_TIMEOUT		0x12
+#define SOC_OVERRUN		0x13
+#define SOC_UNKOWN_CQ_TYPE	0x20
+#define SOC_BAD_SEG_CNT		0x21
+#define SOC_MAX_XCHG_EXCEEDED	0x22
+#define SOC_BAD_XID		0x23
+#define SOC_XCHG_BUSY		0x24
+#define SOC_BAD_POOL_ID		0x25
+#define SOC_INSUFFICIENT_CQES	0x26
+#define SOC_ALLOC_FAIL		0x27
+#define SOC_BAD_SID		0x28
+#define SOC_NO_SEG_INIT		0x29
+
+typedef struct {
+	soc_hdr			shdr;
+	u32			status;
+	soc_data		data;
+	u8			xxx1[12];
+	fc_hdr			fchdr;
+	u8			count;
+	u8			type;
+	u8			flags;
+	u8			seqno;
+} soc_rsp;
+
+/* }}} */
+
+/* Now our software structures and constants we use to drive the beast {{{ */
+
+#define SOC_CQ_REQ0_SIZE	4
+#define SOC_CQ_REQ1_SIZE	64
+#define SOC_CQ_RSP0_SIZE	8
+#define SOC_CQ_RSP1_SIZE	4
+
+#define SOC_SOLICITED_RSP_Q	0
+#define SOC_UNSOLICITED_RSP_Q	1
+
+struct soc;
+
+typedef struct {
+	/* This must come first */
+	fc_channel		fc;
+	struct soc		*s;
+	u16			flags;
+	u16			mask;
+} soc_port; 
+
+typedef struct {
+	soc_hw_cq		*hw_cq;	/* Related XRAM cq */
+	soc_req			*pool;
+	u8			in;
+	u8			out;
+	u8			last;
+	u8			seqno;
+} soc_cq;
+
+struct soc {
+	soc_port		port[2]; /* Every SOC has one or two FC ports */
+	soc_cq			req[2]; /* Request CQs */
+	soc_cq			rsp[2]; /* Response CQs */
+	int			soc_no;
+	struct soc_regs		*regs;
+	xram_p			xram;
+	fc_wwn			wwn;
+	u32			imask;	/* Our copy of regs->imask */
+	u32			cfg;	/* Our copy of regs->cfg */
+	char			serv_params[80];
+	struct soc		*next;
+	int			curr_port; /* Which port will have priority to fcp_queue_empty */
+};
+
+/* }}} */
+
+#endif /* !(__SOC_H) */
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/Makefile linux/drivers/macintosh/Makefile
--- v2.1.78/linux/drivers/macintosh/Makefile	Mon Aug 18 18:19:45 1997
+++ linux/drivers/macintosh/Makefile	Mon Jan 12 15:18:13 1998
@@ -14,12 +14,12 @@
 
 L_TARGET := macintosh.a
 M_OBJS   :=
-L_OBJS   := via-cuda.o adb.o nvram.o
+L_OBJS   := via-cuda.o adb.o nvram.o macio-adb.o
 
-ifeq ($(CONFIG_SERIAL),y)
+ifeq ($(CONFIG_SERIAL)$(CONFIG_PMAC),yy)
   LX_OBJS += macserial.o
 else
-  ifeq ($(CONFIG_SERIAL),m)
+  ifeq ($(CONFIG_SERIAL)$(CONFIG_PMAC),my)
     MX_OBJS += macserial.o
   endif
 endif
@@ -30,7 +30,19 @@
 
 ifdef CONFIG_VT
   ifdef CONFIG_PMAC_CONSOLE
-    L_OBJS += pmac-cons.o control.o platinum.o valkyrie.o
+    L_OBJS += pmac-cons.o
+    ifdef CONFIG_CONTROL_VIDEO
+      L_OBJS += control.o
+    endif
+    ifdef CONFIG_PLATINUM_VIDEO
+      L_OBJS += platinum.o
+    endif
+    ifdef CONFIG_VALKYRIE_VIDEO
+      L_OBJS += valkyrie.o
+    endif
+    ifdef CONFIG_CHIPS_VIDEO
+      L_OBJS += chips.o
+    endif
     ifdef CONFIG_ATY_VIDEO
       L_OBJS += aty.o
     endif
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/adb.c linux/drivers/macintosh/adb.c
--- v2.1.78/linux/drivers/macintosh/adb.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/macintosh/adb.c	Mon Jan 12 15:18:13 1998
@@ -1,5 +1,6 @@
 /*
- * Device driver for the /dev/adb device on macintoshes.
+ * Device driver for the Apple Desktop Bus
+ * and the /dev/adb device on macintoshes.
  *
  * Copyright (C) 1996 Paul Mackerras.
  */
@@ -10,15 +11,99 @@
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <asm/prom.h>
+#include <asm/adb.h>
 #include <asm/cuda.h>
 #include <asm/uaccess.h>
+#include <asm/hydra.h>
+
+enum adb_hw adb_hardware;
+int (*adb_send_request)(struct adb_request *req, int sync);
+int (*adb_autopoll)(int on);
+
+static struct adb_handler {
+	void (*handler)(unsigned char *, int, struct pt_regs *, int);
+} adb_handler[16];
+
+static int adb_nodev(void)
+{
+	return -1;
+}
+
+void adb_init(void)
+{
+	adb_hardware = ADB_NONE;
+	adb_send_request = (void *) adb_nodev;
+	adb_autopoll = (void *) adb_nodev;
+	via_cuda_init();
+	macio_adb_init();
+	if (adb_hardware == ADB_NONE)
+		printk(KERN_WARNING "Warning: no ADB interface detected\n");
+	else
+		adb_autopoll(1);
+}
+
+int
+adb_request(struct adb_request *req, void (*done)(struct adb_request *),
+	    int flags, int nbytes, ...)
+{
+	va_list list;
+	int i;
+	struct adb_request sreq;
+
+	if (req == NULL) {
+		req = &sreq;
+		flags |= ADBREQ_SYNC;
+	}
+	req->nbytes = nbytes;
+	req->done = done;
+	req->reply_expected = flags & ADBREQ_REPLY;
+	va_start(list, nbytes);
+	for (i = 0; i < nbytes; ++i)
+		req->data[i] = va_arg(list, int);
+	va_end(list);
+	return adb_send_request(req, flags & ADBREQ_SYNC);
+}
+
+/* Ultimately this should return the number of devices with
+   the given default id. */
+int
+adb_register(int default_id,
+	     void (*handler)(unsigned char *, int, struct pt_regs *, int))
+{
+	if (adb_handler[default_id].handler != 0)
+		panic("Two handlers for ADB device %d\n", default_id);
+	adb_handler[default_id].handler = handler;
+	return 1;
+}
+
+void
+adb_input(unsigned char *buf, int nb, struct pt_regs *regs, int autopoll)
+{
+	int i, id;
+	static int dump_adb_input = 0;
+
+	id = buf[0] >> 4;
+	if (dump_adb_input) {
+		printk(KERN_INFO "adb packet: ");
+		for (i = 0; i < nb; ++i)
+			printk(" %x", buf[i]);
+		printk(", id = %d\n", id);
+	}
+	if (adb_handler[id].handler != 0) {
+		(*adb_handler[id].handler)(buf, nb, regs, autopoll);
+	}
+}
+
+/*
+ * /dev/adb device driver.
+ */
 
 #define ADB_MAJOR	56	/* major number for /dev/adb */
 
 extern void adbdev_init(void);
 
 struct adbdev_state {
-	struct cuda_request req;
+	struct adb_request req;
 };
 
 static struct wait_queue *adb_wait;
@@ -31,7 +116,7 @@
 	add_wait_queue(&adb_wait, &wait);
 	current->state = TASK_INTERRUPTIBLE;
 
-	while (!state->req.got_reply) {
+	while (!state->req.complete) {
 		if (file->f_flags & O_NONBLOCK) {
 			ret = -EAGAIN;
 			break;
@@ -49,11 +134,11 @@
 	return ret;
 }
 
-static void adb_write_done(struct cuda_request *req)
+static void adb_write_done(struct adb_request *req)
 {
-	if (!req->got_reply) {
+	if (!req->complete) {
 		req->reply_len = 0;
-		req->got_reply = 1;
+		req->complete = 1;
 	}
 	wake_up_interruptible(&adb_wait);
 }
@@ -62,7 +147,7 @@
 {
 	struct adbdev_state *state;
 
-	if (MINOR(inode->i_rdev) > 0)
+	if (MINOR(inode->i_rdev) > 0 || adb_hardware == ADB_NONE)
 		return -ENXIO;
 	state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);
 	if (state == 0)
@@ -78,7 +163,7 @@
 
 	if (state) {
 		file->private_data = NULL;
-		if (state->req.reply_expected && !state->req.got_reply)
+		if (state->req.reply_expected && !state->req.complete)
 			if (adb_wait_reply(state, file))
 				return 0;
 		kfree(state);
@@ -86,13 +171,13 @@
 	return 0;
 }
 
-static long long adb_lseek(struct file *file, long long offset, int origin)
+static long long adb_lseek(struct file *file, loff_t offset, int origin)
 {
 	return -ESPIPE;
 }
 
-static long adb_read(struct inode *inode, struct file *file,
-		     char *buf, unsigned long count)
+static ssize_t adb_read(struct file *file, char *buf,
+			size_t count, loff_t *ppos)
 {
 	int ret;
 	struct adbdev_state *state = file->private_data;
@@ -112,17 +197,18 @@
 	if (ret)
 		return ret;
 
-	ret = state->req.reply_len;
-	copy_to_user(buf, state->req.reply, ret);
 	state->req.reply_expected = 0;
+	ret = state->req.reply_len;
+	if (copy_to_user(buf, state->req.reply, ret))
+		return -EFAULT;
 
 	return ret;
 }
 
-static long adb_write(struct inode *inode, struct file *file,
-		      const char *buf, unsigned long count)
+static ssize_t adb_write(struct file *file, const char *buf,
+			 size_t count, loff_t *ppos)
 {
-	int ret;
+	int ret, i;
 	struct adbdev_state *state = file->private_data;
 
 	if (count < 2 || count > sizeof(state->req.data))
@@ -131,7 +217,7 @@
 	if (ret)
 		return ret;
 
-	if (state->req.reply_expected && !state->req.got_reply) {
+	if (state->req.reply_expected && !state->req.complete) {
 		/* A previous request is still being processed.
 		   Wait for it to finish. */
 		ret = adb_wait_reply(state, file);
@@ -141,10 +227,27 @@
 
 	state->req.nbytes = count;
 	state->req.done = adb_write_done;
-	copy_from_user(state->req.data, buf, count);
-	state->req.reply_expected = 1;
-	state->req.got_reply = 0;
-	cuda_send_request(&state->req);
+	state->req.complete = 0;
+	if (copy_from_user(state->req.data, buf, count))
+		return -EFAULT;
+
+	switch (adb_hardware) {
+	case ADB_NONE:
+		return -ENXIO;
+	case ADB_VIACUDA:
+		state->req.reply_expected = 1;
+		cuda_send_request(&state->req);
+		break;
+	default:
+		if (state->req.data[0] != ADB_PACKET)
+			return -EINVAL;
+		for (i = 1; i < state->req.nbytes; ++i)
+			state->req.data[i] = state->req.data[i+1];
+		state->req.reply_expected =
+			((state->req.data[0] & 0xc) == 0xc);
+		adb_send_request(&state->req, 0);
+		break;
+	}
 
 	return count;
 }
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/ati-gt.h linux/drivers/macintosh/ati-gt.h
--- v2.1.78/linux/drivers/macintosh/ati-gt.h	Mon Aug 18 18:19:45 1997
+++ linux/drivers/macintosh/ati-gt.h	Mon Jan 12 15:18:13 1998
@@ -1,149 +1,203 @@
-#if 0		/* not filled inaty_gt_reg_init yet */
-/* Register values for 1280x1024, 75Hz mode (20) */
+/* the usage for the following structs vary from the gx and vt:
+and sdram and sgram gt's
+	pll registers (sdram) 6,7,11;
+	crtc_h_sync_strt_wid[3];
+	dsp1[3] (sdram,sgram,unused)
+	dsp2[3] (offset regbase+24, depends on colour mode);
+	crtc_h_tot_disp,crtc_v_tot_disp,crtc_v_sync_strt_wid,unused;
+	pll registers (sgram) 7,11;
+*/
+
+/* Register values for 1280x1024, 75Hz mode (20). no 16/32 */
 static struct aty_regvals aty_gt_reg_init_20 = {
-	{ 0x10, 0x28, 0x3c },
-	{ },
-	{ }	/* pixel clock = 134.61MHz for V=74.81Hz */
+	{ 0x41, 0xf9, 0x04 },
+	{ 0xe02a7, 0x1401a6, 0 },
+	{ 0x260957, 0x2806d6, 0 },
+	{ 0x10006b6, 0x20006b6, 0x30006b6 },
+
+	0x9f00d2, 0x03ff0429, 0x30400, 0,
+	{ 0xb5, 0x04 }
 };
 
+#if 0
 /* Register values for 1280x960, 75Hz mode (19) */
 static struct aty_regvals aty_gt_reg_init_19 = {
-	{ 0x10, 0x28, 0x3c },
-	{ },
-	{ }	/* pixel clock = 126.01MHz for V=75.01 Hz */
 };
+#endif
 
 /* Register values for 1152x870, 75Hz mode (18) */
 static struct aty_regvals aty_gt_reg_init_18 = {
-	{ 0x10, 0x28, 0x50 },
-	{ },
-	{ }	/* pixel clock = 100.33MHz for V=75.31Hz */
+	{ 0x41, 0xe6, 0x04 },
+	{ 0x300295, 0x300194, 0x300593 },
+	{ 0x260a1c, 0x380561, 0},
+	{ 0x1000744, 0x2000744, 0x3000744 },
+
+	0x8f00b5, 0x3650392, 0x230368, 0,
+	{ 0xe6, 0x04 }
 };
 
-/* Register values for 1024x768, 75Hz mode (17) */
+/* Register values for 1024x768, 75Hz mode (17), 32 bpp untested */
 static struct aty_regvals aty_gt_reg_init_17 = {
-	{ 0x10, 0x28, 0x50 },
-	{ },
-	{ }	/* pixel clock = 79.55MHz for V=74.50Hz */
+	{ 0x41, 0xb5, 0x04 },
+	{ 0xc0283, 0xc0182, 0xc0581 },
+	{ 0x36066d, 0x3806d6, 0},
+	{ 0xa0049e, 0x100049e, 0x200049e },
+
+	0x7f00a3, 0x2ff031f, 0x30300, 0,
+	{ 0xb8, 0x04 }
 };
 
-/* Register values for 1024x768, 72Hz mode (15) */
-static struct aty_regvals aty_gt_reg_init_15 = {
-	{ 0x10, 0x28, 0x50 },
-	{ },
-	{ }	/* pixel clock = 78.12MHz for V=72.12Hz */
+#if 0
+/* Register values for x, Hz mode (16) */
+static struct aty_regvals aty_gt_reg_init_16 = {
 };
-
 #endif
 
-
-/* Register values for 1280x1024, 60Hz mode (20) */
-static struct aty_regvals aty_gt_reg_init_20 = {
-   { 0, 0, 0 },
-
-    { 0x310086, 0x310084, 0x310084 },
-    { 0x3070200, 0x30e0300, 0x30e0300 },
-    { 0x2002312, 0x3002312, 0x3002312 },
-
-    0x7f00a5, 0x2ff0325, 0x260302, 0x20100000,
-    { 0x88, 0x7 }
-};     
-
-/* Register values for 1024x768, 75Hz mode (17) */
-static struct aty_regvals aty_gt_reg_init_17 = {
-    { 0, 0, 0 },
-
-    { 0xc0085, 0xc0083, 0xc0083 },
-    { 0x3070200, 0x30e0300, 0x30e0300 },
-    { 0x2002312, 0x3002312, 0x3002312 },
-
-    0x7f00a3, 0x2ff031f, 0x30300, 0x20100000,
-    { 0x41, 0x3 }
-};
-
-/* Register values for 1024x768, 72Hz mode (15) */
+/* Register values for 1024x768, 70Hz mode (15) */
 static struct aty_regvals aty_gt_reg_init_15 = {
-    { 0, 0, 0 },
-
-    { 0x310086, 0x310084, 0x310084 },
-    { 0x3070200, 0x30e0300, 0x30e0300 },
-    { 0x2002312, 0x3002312, 0x3002312 },
-
-    0x7f00a5, 0x2ff0325, 0x260302, 0x20100000,
-    { 0x88, 0x7 }
+	{ 0x41, 0xad, 0x04 },
+	{ 0x310284, 0x310183, 0x310582 },
+	{ 0x0, 0x380727 },
+	{ 0x0 },
+	0x7f00a5, 0x2ff0325, 0x260302,
 };    
 
 /* Register values for 1024x768, 60Hz mode (14) */
 static struct aty_regvals aty_gt_reg_init_14 = {
-	{ 0, 0, 0 },
+	{ 0x40, 0xe1, 0x14 },
+	{ 0x310284, 0x310183, 0x310582 },
+	{ 0x3607c0, 0x380840, 0x0 },
+	{ 0xa80592, 0x1000592, 0x0 },
 
-	{ 0x310086, 0x310084, 0x310084 },
-	{ 0x3060200, 0x30d0300, 0x30d0300 },
-	{ 0x2002312, 0x3002312, 0x3002312 },
-
-	0x7f00a7, 0x2ff0325, 0x260302, 0x20100000,
-	{ 0x6c, 0x6 }
+	0x7f00a7, 0x2ff0325, 0x260302, 0,
+	{ 0xe1, 0x14 }
 };  
 
 /* Register values for 832x624, 75Hz mode (13) */
 static struct aty_regvals aty_gt_reg_init_13 = {
-	{ 0x200, 0x200, 0x200 },
-
-	{ 0x28006f, 0x28006d, 0x28006c },
-	{ 0x3050200, 0x30b0300, 0x30e0600 },
-	{ 0x2002312, 0x3002312, 0x6002312 },
+	{ 0x40, 0xc6, 0x14 },
+	{ 0x28026d, 0x28016c, 0x28056b },
+	{ 0x3608cf, 0x380960, 0 },
+	{ 0xb00655, 0x1000655, 0x2000655 },
 
-	0x67008f, 0x26f029a, 0x230270, 0x1a100040,
-        { 0x4f, 0x05 }
+	0x67008f, 0x26f029a, 0x230270, 0,
+	{ 0xc6, 0x14 }
 };
 
-#if 0		/* not filled in yet */
 /* Register values for 800x600, 75Hz mode (12) */
 static struct aty_regvals aty_gt_reg_init_12 = {
-	{ 0x10, 0x28, 0x50 },
-	{ },
-	{ }	/* pixel clock = 49.11MHz for V=74.40Hz */
+	{ 0x42, 0xe4, 0x04 },
+	{ 0xa0267, 0xa0166, 0x0a0565},
+	{ 0x360a33, 0x48056d, 0},
+	{ 0xc00755, 0x1000755, 0x02000755},
+
+	0x630083, 0x2570270, 0x30258, 0,
+	{ 0xe4, 0x4 }
 };
 
 /* Register values for 800x600, 72Hz mode (11) */
 static struct aty_regvals aty_gt_reg_init_11 = {
-	{ 0x10, 0x28, 0x50 },
-	{ },
-	{ }	/* pixel clock = 49.63MHz for V=71.66Hz */
+	{ 0x42, 0xe6, 0x04 },
+	{ 0xf026c, 0xf016b, 0xf056a },
+	{ 0x360a1d, 0x480561, 0},
+	{ 0xc00745, 0x1000745, 0x2000745 },
+	
+	0x630081, 0x02570299, 0x6027c
 };
 
 /* Register values for 800x600, 60Hz mode (10) */
 static struct aty_regvals aty_gt_reg_init_10 = {
-	{ 0x10, 0x28, 0x50 },
-	{ },
-	{ }	/* pixel clock = 41.41MHz for V=59.78Hz */
+	{ 0x42, 0xb8, 0x04 },
+	{ 0x10026a, 0x100169, 0x100568 },
+	{ 0x460652, 0x4806ba, 0},
+	{ 0x68048b, 0xa0048b, 0x100048b },
+
+	0x630083, 0x02570273, 0x40258, 0,
+	{ 0xb8, 0x4 }
+};
+
+/* Register values for 800x600, 56Hz mode (9) */
+static struct aty_regvals aty_gt_reg_init_9 = {
+	{ 0x42, 0xf9, 0x14 },
+	{ 0x90268, 0x90167, 0x090566 },
+	{ 0x460701, 0x480774, 0},
+	{ 0x700509, 0xa80509, 0x1000509 },
+
+	0x63007f, 0x2570270, 0x20258
+};
+
+#if 0
+/* Register values for 768x576, 50Hz mode (8) */
+static struct aty_regvals aty_gt_reg_init_8 = {
 };
 
 /* Register values for 640x870, 75Hz Full Page Display (7) */
 static struct aty_regvals aty_gt_reg_init_7 = {
-	{ 0x10, 0x30, 0x68 },
-	{ },
-	{ }	/* pixel clock = 57.29MHz for V=75.01Hz */
 };
 #endif
 
 /* Register values for 640x480, 67Hz mode (6) */
 static struct aty_regvals aty_gt_reg_init_6 = {
-	{ 0x200, 0x200, 0x200 },
-
-	{ 0x28005b, 0x280059, 0x280058 },
-	{ 0x3040200, 0x3060300, 0x30c0600 },
-	{ 0x2002312, 0x3002312, 0x6002312 },
+	{ 0x42, 0xd1, 0x14 },
+	{ 0x280259, 0x280158, 0x280557 },
+	{ 0x460858, 0x4808e2, 0},
+	{ 0x780600, 0xb00600, 0x1000600 },
 
-	0x4f006b, 0x1df020c, 0x2301e2, 0x14100040,
-        { 0x35, 0x07 }
+	0x4f006b, 0x1df020c, 0x2301e2, 0,
+	{ 0x8b, 0x4 }
 };
 
-#if 0		/* not filled in yet */
 /* Register values for 640x480, 60Hz mode (5) */
 static struct aty_regvals aty_gt_reg_init_5 = {
-	{ 0x200, 0x200, 0x200 },
-	{ },
-        { 0x35, 0x07 }
+	{ 0x43, 0xe8, 0x04 },
+	{ 0x2c0253, 0x2c0152, 0x2c0551 },
+	{ 0x460a06, 0x580555, 0},
+	{ 0x880734, 0xc00734, 0x1000734 },
+
+	0x4f0063, 0x1df020c, 0x2201e9, 0,
+	{ 0xe8, 0x04 }
+};
+
+#if 0
+/* Register values for x, Hz mode (4) */
+static struct aty_regvals aty_gt_reg_init_4 = {
+};
+
+/* Register values for x, Hz mode (3) */
+static struct aty_regvals aty_gt_reg_init_3 = {
+};
+
+/* Register values for x, Hz mode (2) */
+static struct aty_regvals aty_gt_reg_init_2 = {
+};
+
+/* Register values for x, Hz mode (1) */
+static struct aty_regvals aty_gt_reg_init_1 = {
 };
 #endif
+
+/* yikes, more data structures (dsp2)
+ * XXX kludge for sgram
+ */
+static int sgram_dsp[20][3] = {
+	{0,0,0},
+	{0,0,0},
+	{0,0,0},
+	{0,0,0},
+	{0x5203d7,0x7803d9,0xb803dd}, //5
+	{0x940666,0xe0066a,0x1700672}, //6
+	{0,0,0},
+	{0,0,0},
+	{0x88055f,0xd80563,0x170056b}, //9
+	{0x8404d9,0xb804dd,0x17004e5}, //10
+	{0x7803e2,0xb803e6,0x17003ee}, //11
+	{0x7803eb,0xb803ef,0x17003f7}, //12
+	{0xe806c5,0x17006cd,0x2e006dd}, //13
+	{0xe005f6,0x17005fe,0x2e0060e}, //14
+	{0xd8052c,0x1700534,0x2e00544}, //15
+	{0,0,0},
+	{0xb804f2,0x17004e5,0x2e0050a}, //17
+	{0xb803e6,0x17003ee,0x2e003fe}, //18
+	{0,0,0},
+	{0,0,0},
+};
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/ati-gx.h linux/drivers/macintosh/ati-gx.h
--- v2.1.78/linux/drivers/macintosh/ati-gx.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/macintosh/ati-gx.h	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,149 @@
+#if 0		/* not filled inaty_gt_reg_init yet */
+/* Register values for 1280x1024, 75Hz mode (20) */
+static struct aty_regvals aty_gx_reg_init_20 = {
+	{ 0x10, 0x28, 0x3c },
+	{ },
+	{ }	/* pixel clock = 134.61MHz for V=74.81Hz */
+};
+
+/* Register values for 1280x960, 75Hz mode (19) */
+static struct aty_regvals aty_gx_reg_init_19 = {
+	{ 0x10, 0x28, 0x3c },
+	{ },
+	{ }	/* pixel clock = 126.01MHz for V=75.01 Hz */
+};
+
+/* Register values for 1152x870, 75Hz mode (18) */
+static struct aty_regvals aty_gx_reg_init_18 = {
+	{ 0x10, 0x28, 0x50 },
+	{ },
+	{ }	/* pixel clock = 100.33MHz for V=75.31Hz */
+};
+
+/* Register values for 1024x768, 75Hz mode (17) */
+static struct aty_regvals aty_gx_reg_init_17 = {
+	{ 0x10, 0x28, 0x50 },
+	{ },
+	{ }	/* pixel clock = 79.55MHz for V=74.50Hz */
+};
+
+/* Register values for 1024x768, 72Hz mode (15) */
+static struct aty_regvals aty_gx_reg_init_15 = {
+	{ 0x10, 0x28, 0x50 },
+	{ },
+	{ }	/* pixel clock = 78.12MHz for V=72.12Hz */
+};
+
+#endif
+
+
+/* Register values for 1280x1024, 60Hz mode (20) */
+static struct aty_regvals aty_gx_reg_init_20 = {
+   { 0, 0, 0 },
+
+    { 0x310086, 0x310084, 0x310084 },
+    { 0x3070200, 0x30e0300, 0x30e0300 },
+    { 0x2002312, 0x3002312, 0x3002312 },
+
+    0x7f00a5, 0x2ff0325, 0x260302, 0x20100000,
+    { 0x88, 0x7 }
+};     
+
+/* Register values for 1024x768, 75Hz mode (17) */
+static struct aty_regvals aty_gx_reg_init_17 = {
+    { 0, 0, 0 },
+
+    { 0xc0085, 0xc0083, 0xc0083 },
+    { 0x3070200, 0x30e0300, 0x30e0300 },
+    { 0x2002312, 0x3002312, 0x3002312 },
+
+    0x7f00a3, 0x2ff031f, 0x30300, 0x20100000,
+    { 0x41, 0x3 }
+};
+
+/* Register values for 1024x768, 72Hz mode (15) */
+static struct aty_regvals aty_gx_reg_init_15 = {
+    { 0, 0, 0 },
+
+    { 0x310086, 0x310084, 0x310084 },
+    { 0x3070200, 0x30e0300, 0x30e0300 },
+    { 0x2002312, 0x3002312, 0x3002312 },
+
+    0x7f00a5, 0x2ff0325, 0x260302, 0x20100000,
+    { 0x88, 0x7 }
+};    
+
+/* Register values for 1024x768, 60Hz mode (14) */
+static struct aty_regvals aty_gx_reg_init_14 = {
+	{ 0, 0, 0 },
+
+	{ 0x310086, 0x310084, 0x310084 },
+	{ 0x3060200, 0x30d0300, 0x30d0300 },
+	{ 0x2002312, 0x3002312, 0x3002312 },
+
+	0x7f00a7, 0x2ff0325, 0x260302, 0x20100000,
+	{ 0x6c, 0x6 }
+};  
+
+/* Register values for 832x624, 75Hz mode (13) */
+static struct aty_regvals aty_gx_reg_init_13 = {
+	{ 0x200, 0x200, 0x200 },
+
+	{ 0x28006f, 0x28006d, 0x28006c },
+	{ 0x3050200, 0x30b0300, 0x30e0600 },
+	{ 0x2002312, 0x3002312, 0x6002312 },
+
+	0x67008f, 0x26f029a, 0x230270, 0x1a100040,
+        { 0x4f, 0x05 }
+};
+
+#if 0		/* not filled in yet */
+/* Register values for 800x600, 75Hz mode (12) */
+static struct aty_regvals aty_gx_reg_init_12 = {
+	{ 0x10, 0x28, 0x50 },
+	{ },
+	{ }	/* pixel clock = 49.11MHz for V=74.40Hz */
+};
+
+/* Register values for 800x600, 72Hz mode (11) */
+static struct aty_regvals aty_gx_reg_init_11 = {
+	{ 0x10, 0x28, 0x50 },
+	{ },
+	{ }	/* pixel clock = 49.63MHz for V=71.66Hz */
+};
+
+/* Register values for 800x600, 60Hz mode (10) */
+static struct aty_regvals aty_gx_reg_init_10 = {
+	{ 0x10, 0x28, 0x50 },
+	{ },
+	{ }	/* pixel clock = 41.41MHz for V=59.78Hz */
+};
+
+/* Register values for 640x870, 75Hz Full Page Display (7) */
+static struct aty_regvals aty_gx_reg_init_7 = {
+	{ 0x10, 0x30, 0x68 },
+	{ },
+	{ }	/* pixel clock = 57.29MHz for V=75.01Hz */
+};
+#endif
+
+/* Register values for 640x480, 67Hz mode (6) */
+static struct aty_regvals aty_gx_reg_init_6 = {
+	{ 0x200, 0x200, 0x200 },
+
+	{ 0x28005b, 0x280059, 0x280058 },
+	{ 0x3040200, 0x3060300, 0x30c0600 },
+	{ 0x2002312, 0x3002312, 0x6002312 },
+
+	0x4f006b, 0x1df020c, 0x2301e2, 0x14100040,
+        { 0x35, 0x07 }
+};
+
+#if 0		/* not filled in yet */
+/* Register values for 640x480, 60Hz mode (5) */
+static struct aty_regvals aty_gx_reg_init_5 = {
+	{ 0x200, 0x200, 0x200 },
+	{ },
+        { 0x35, 0x07 }
+};
+#endif
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/ati-vt.h linux/drivers/macintosh/ati-vt.h
--- v2.1.78/linux/drivers/macintosh/ati-vt.h	Mon Aug 18 18:19:45 1997
+++ linux/drivers/macintosh/ati-vt.h	Mon Jan 12 15:18:13 1998
@@ -1,147 +1,147 @@
-#if 0		/* not filled inaty_vt_reg_init yet */
-/* Register values for 640x870, 75Hz Full Page Display (7) */
-static struct aty_regvals aty_vt_reg_init_7 = {
-	{ 0x10, 0x30, 0x68 },
-	{ },
-	{ }	/* pixel clock = 57.29MHz for V=75.01Hz */
-};
-
-/* Register values for 1024x768, 72Hz mode (15) */
-static struct aty_regvals aty_vt_reg_init_15 = {
-	{ 0x10, 0x28, 0x50 },
-	{ },
-	{ }	/* pixel clock = 78.12MHz for V=72.12Hz */
-};
-
-#endif
-
 /* Register values for 1280x1024, 60Hz mode (20) */
 static struct aty_regvals aty_vt_reg_init_20 = {
-	{ 0, 0, 0 },
-	{ 0x2e02a7, 0x2e02a7, 0x2e02a7},
-	{ 0x3070200, 0x3070200, 0x3070200},
-	{ 0xa00cb22, 0xb00cb23, 0xe00cb24 },
-	
-	0x9f00d2, 0x3ff0429, 0x30400, 0x28000000,
-	{ 0x00, 0xaa }
+  { 0, 0, 0 },
+     
+  { 0x002e02a7, 0x002e02a7, 0 },
+  { 0x03070200, 0x03070200, 0 },
+  { 0x0a00cb22, 0x0b00cb23, 0 },
+     
+    0x009f00d2, 0x03ff0429, 0x00030400, 0x28000000,
+  { 0x00, 0xaa }
+};
+
+/* Register values for 1280x960, 75Hz mode (19) */
+static struct aty_regvals aty_vt_reg_init_19 = {
+  { 0, 0, 0 },
+  { 0x003202a3, 0x003201a2, 0 },
+  { 0x030b0200, 0x030b0300, 0 },
+  { 0x0a00cb22, 0x0b00cb23, 0 },
+
+    0x009f00d1, 0x03bf03e7, 0x000303c0, 0x28000000,
+  { 0x00, 0xc6 }
 };
 
 /* Register values for 1152x870, 75Hz mode (18) */
 static struct aty_regvals aty_vt_reg_init_18 = {
-	{ 0, 0, 0 },
-	{ 0x300295, 0x300194, 0x300194 },
-	{ 0x3060200, 0x30e0300, 0x30e0300 },
-	{ 0xa00cb21, 0xb00cb22, 0xe00cb23 },
-    
-    0x8f00b5, 0x3650392, 0x230368, 0x24000000,
-    { 0x00, 0x9d }
-	/* pixel clock = 100.33MHz for V=75.31Hz */
+  { 0, 0, 0 },
+     
+  { 0x00300295, 0x00300194, 0 },
+  { 0x03080200, 0x03080300, 0 },
+  { 0x0a00cb21, 0x0b00cb22, 0 },
+     
+    0x008f00b5, 0x03650392, 0x00230368, 0x24000000,
+  { 0x00, 0x9d }
 };
 
 /* Register values for 1024x768, 75Hz mode (17) */
 static struct aty_regvals aty_vt_reg_init_17 = {
-    { 0, 0, 0 },
+  { 0, 0, 0 },
 
-    { 0x2c0283, 0x2c0182, 0x2c0182 },
-    { 0x3060200, 0x30a0300, 0x30a0300 },
-    { 0xa00cb21, 0xb00cb22, 0xe00cb23 },
-    
-    0x7f00a3, 0x2ff031f, 0x30300, 0x20000000,
-    { 0x01, 0xf7 }
+  { 0x002c0283, 0x002c0182, 0 },
+  { 0x03080200, 0x03080300, 0 },
+  { 0x0a00cb21, 0x0b00cb22, 0 },
+     
+    0x007f00a3, 0x02ff031f, 0x00030300, 0x20000000,
+  { 0x01, 0xf7 }
 };
 
-/* Register values for 1024x768, 72Hz mode (15) */
+/* Register values for 1024x768, 70Hz mode (15) */
 static struct aty_regvals aty_vt_reg_init_15 = {
-    { 0, 0, 0 },
+  { 0, 0, 0 },
+  { 0x00310284, 0x00310183, 0 },
+  { 0x03080200, 0x03080300, 0 },
+  { 0x0a00cb21, 0x0b00cb22, 0 },
 
-    { 0x310284, 0x310183, 0x310183 },
-    { 0x3060200, 0x3090300, 0x3090600 },
-    { 0xa00cb21, 0xb00cb22, 0xe00cb23 },
-    
-    0x7f00a5, 0x2ff0325, 0x260302, 0x20000000,
-    { 0x01, 0xeb }
+    0x007f00a5, 0x02ff0325, 0x00260302, 0x20000000,
+  { 0x01, 0xeb }
 };    
 
 /* Register values for 1024x768, 60Hz mode (14) */
 static struct aty_regvals aty_vt_reg_init_14 = {
-	{ 0, 0, 0 },
-    { 0x310284, 0x310183, 0x310183 },
-    { 0x3060200, 0x3080300, 0x30f0600 },
-    { 0xa00cb21, 0xb00cb22, 0xe00cb23 },
+  { 0, 0, 0 },
+     
+  { 0x00310284, 0x00310183, 0x00310582 }, /* 32 bit 0x00310582 */
+  { 0x03080200, 0x03080300, 0x03070600 }, /* 32 bit 0x03070600 */
+  { 0x0a00cb21, 0x0b00cb22, 0x0e00cb23 },
     
-    0x7f00a7, 0x2ff0325, 0x260302, 0x20000000,
-    { 0x01, 0xcc }
+    0x007f00a7, 0x02ff0325, 0x00260302, 0x20000000,
+  { 0x01, 0xcc }
 };  
 
 /* Register values for 832x624, 75Hz mode (13) */
 static struct aty_regvals aty_vt_reg_init_13 = {
-	{ 0, 0, 0 },
+  { 0, 0, 0 },
 
-    { 0x28026d, 0x28016c, 0x28016c },
-    { 0x3060200, 0x3080300, 0x30f0600 },
-    { 0xa00cb21, 0xb00cb21, 0xe00cb22 },
-    
-    0x67008f, 0x26f029a, 0x230270, 0x1a000000,
-    { 0x01, 0xb4 }
+  { 0x0028026d, 0x0028016c, 0x0028056b },
+  { 0x03080200, 0x03070300, 0x03090600 },
+  { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 },
+
+    0x0067008f, 0x026f029a, 0x00230270, 0x1a000000,
+  { 0x01, 0xb4 }
 };
 
 /* Register values for 800x600, 75Hz mode (12) */
 static struct aty_regvals aty_vt_reg_init_12 = {
-	{ 0, 0, 0 },
-    { 0x2a0267, 0x2a0166, 0x2a0565 },
-    { 0x3040200, 0x3060300, 0x30d0600 },
-    { 0xa00cb21, 0xb00cb21, 0xe00cb22 },
-    
-    0x630083, 0x2570270, 0x30258, 0x19000000,
-    { 0x01, 0x9c }
-	/* pixel clock = 49.11MHz for V=74.40Hz */
+  { 0, 0, 0 },
+     
+  { 0x002a0267, 0x002a0166, 0x002a0565 },
+  { 0x03040200, 0x03060300, 0x03070600 },
+  { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 },
+     
+    0x00630083, 0x02570270, 0x00030258, 0x19000000,
+  { 0x01, 0x9c }
 };
 
 /* Register values for 800x600, 72Hz mode (11) */
 static struct aty_regvals aty_vt_reg_init_11 = {
-	{ 0, 0, 0 },
-    
-    { 0x2f026c, 0x2f016b, 0x2f056a },
-    { 0x3040200, 0x3060300, 0x30d0600 },
-    { 0xa00cb21, 0xb00cb21, 0xe00cb22 },
-    
-    0x630081, 0x257029b, 0x6027c, 0x19000000,
-    { 0x01, 0x9d }
-	/* pixel clock = 49.63MHz for V=71.66Hz */
+  { 0, 0, 0 },
+     
+  { 0x002f026c, 0x002f016b, 0x002f056a },
+  { 0x03050200, 0x03070300, 0x03090600 },
+  { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 },
+
+    0x00630081, 0x02570299, 0x0006027c, 0x19000000,
+  { 0x01, 0x9d }
 };
 
 /* Register values for 800x600, 60Hz mode (10) */
 static struct aty_regvals aty_vt_reg_init_10 = {
-	{ 0, 0, 0 },
-    { 0x30026a, 0x300169, 0x300568 },
-    { 0x3030200, 0x3050300, 0x30b0600 },
-    { 0xa00cb21, 0xb00cb21, 0xe00cb22 },
-    
-    0x630083, 0x2570273, 0x40258, 0x19000000,
-    { 0x02, 0xfb }
-/* pixel clock = 41.41MHz for V=59.78Hz */
+  { 0, 0, 0 },
+     
+  { 0x0030026a, 0x00300169, 0x00300568 },
+  { 0x03050200, 0x03070300, 0x03090600 },
+  { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 },
+     
+    0x00630083, 0x02570273, 0x00040258, 0x19000000,
+  { 0x02, 0xfb }
 };
 
 /* Register values for 640x480, 67Hz mode (6) */
 static struct aty_regvals aty_vt_reg_init_6 = {
-	{ 0, 0, 0 },
-
-    { 0x280259, 0x280158, 0x280557 },
-    { 0x3030200, 0x3040300, 0x3080600 },
-    { 0xa00cb21, 0xb00cb21, 0xe00cb22 },
-    
-    0x4f006b, 0x1df020c, 0x2301e2, 0x14000000,
-    { 0x02, 0xbe }
+  { 0, 0, 0 },
+      
+  { 0x00280259, 0x00280158, 0x00280557 },
+  { 0x03050200, 0x03070300, 0x030a0600 },
+  { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 },
+      
+    0x004f006b, 0x01df020c, 0x002301e2, 0x14000000,
+  { 0x02, 0xbe }
 };
 
 /* Register values for 640x480, 60Hz mode (5) */
 static struct aty_regvals aty_vt_reg_init_5 = {
-	{ 0, 0, 0 },
-	
-    { 0x2c0253, 0x2c0152, 0x2c0551 },
-    { 0x3030200, 0x3040300, 0x3060600 },
-    { 0xa00cb21, 0xb00cb21, 0xe00cb22 },
-    
-    0x4f0063, 0x1df020c, 0x2201e9, 0x14000000,
-    { 0x02, 0x9e }
-};
+  { 0, 0, 0 },
+      
+  { 0x002c0253, 0x002c0152, 0x002c0551 },
+  { 0x03050200, 0x03070300, 0x03090600 },
+  { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 },
+
+    0x004f0063, 0x01df020c, 0x002201e9, 0x14000000,
+  { 0x02, 0x9e }
+};
+                               /*     8 bit       15 bit      32 bit   */
+static int vt_mem_cntl[3][3] = { { 0x0A00CB21, 0x0B00CB21, 0x0E00CB21 },  /* 1 MB VRAM */
+                                 { 0x0A00CB22, 0x0B00CB22, 0x0E00CB22 },  /* 2 MB VRAM */
+                                { 0x0200053B, 0x0300053B, 0x0600053B }   /* 4 M B VRAM */
+ };
+
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/aty.c linux/drivers/macintosh/aty.c
--- v2.1.78/linux/drivers/macintosh/aty.c	Mon Aug 18 18:19:45 1997
+++ linux/drivers/macintosh/aty.c	Mon Jan 12 15:18:13 1998
@@ -4,6 +4,7 @@
  * Copyright (C) 1997 Michael AK Tesch
  *  written with much help from Jon Howell
  *  changes to support the vt chip set by harry ac eaton
+ *  gt chipset support, scrollback console by anthony tong <atong@uiuc.edu>
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -18,11 +19,11 @@
 #include <linux/pci.h>
 #include <linux/bios32.h>
 #include <linux/nvram.h>
+#include <linux/selection.h>
+#include <linux/vt_kern.h>
 #include <asm/prom.h>
 #include <asm/io.h>
 #include <asm/pci-bridge.h>
-#include <linux/selection.h>
-#include <linux/vt_kern.h>
 #include "pmac-cons.h"
 #include "aty.h"
 
@@ -34,82 +35,105 @@
 	unsigned char cntl;
 };
 
-struct aty_regvals {
-     int	offset[3];		 /* first pixel address */
+typedef struct aty_regvals {
+	int offset[3];		/* first pixel address */
 
-     int	crtc_h_sync_strt_wid[3]; /* depth dependant */
-     int	crtc_gen_cntl[3];
-     int	mem_cntl[3];
-
-     int	crtc_h_tot_disp;         /* mode dependant */
-     int	crtc_v_tot_disp;
-     int	crtc_v_sync_strt_wid;
-     int	crtc_off_pitch;
-
-     unsigned char clock_val[2];        /* vals for 20 and 21 */
+	int crtc_h_sync_strt_wid[3];	/* depth dependant */
+	int crtc_gen_cntl[3];
+	int mem_cntl[3];
+
+	int crtc_h_tot_disp;	/* mode dependant */
+	int crtc_v_tot_disp;
+	int crtc_v_sync_strt_wid;
+	int crtc_off_pitch;
+
+	unsigned char clock_val[2];	/* vals for 20 and 21 */
+} aty_regvals;
+
+struct rage_regvals {
+	int h_total, h_sync_start, h_sync_width;
+	int v_total, v_sync_start, v_sync_width;
+	int h_sync_neg, v_sync_neg;
 };
 
-/*static void set_aty_clock(unsigned char *params);*/
 static int aty_vram_reqd(int vmode, int cmode);
+static aty_regvals *get_aty_struct(void);
 
 static unsigned char *frame_buffer;
 static int total_vram;		/* total amount of video memory, bytes */
-static int is_vt_chip;		/* whether a vt chip type was detected */
+static int chip_type;		/* what chip type was detected */
 
-static unsigned long aty_regbase;
+static unsigned long ati_regbase;
 static struct aty_cmap_regs *aty_cmap_regs;
 
+#if 0
 /* this array contains the number of bytes/line for each mode and color depth */
 static int pitch[20][3] = {
-	{0,0,0},  /* mode 1?? */
-	{0,0,0},  /* mode 2?? */
-	{0,0,0},  /* mode 3??*/
-	{0,0,0},  /* mode 4?? */
-	{640, 1280, 2560},  /* mode 5 */
-	{640, 1280, 2560},  /* mode 6 */
-	{640, 1280, 2560},  /* mode 7 */
-	{800, 1600, 3200},  /* mode 8 */
-	{0,0,0},  /* mode 9 ??*/
-	{800, 1600, 3200},  /* mode 10 */
-	{800, 1600, 3200},  /* mode 11 */
-	{800, 1600, 3200},  /* mode 12 */
-	{832, 1664, 3328},  /* mode 13 */
-	{1024, 2048, 4096},  /* mode 14 */
-	{1024, 2048, 4096},  /* mode 15 */
-	{1024, 2048, 4096},  /* mode 16 */
-	{1024, 2048, 4096},  /* mode 17 */
-	{1152, 2304, 4608},  /* mode 18 */
-	{1280, 2560, 5120},  /* mode 19 */
-	{1280, 2560, 5120}   /* mode 20 */
+	{512, 1024, 2048},		/* mode 1 */
+	{512, 1024, 2048},		/* mode 2 */
+	{640, 1024, 2048},		/* mode 3 */
+	{640, 1024, 2048},		/* mode 4 */
+	{640, 1280, 2560},		/* mode 5 */
+	{640, 1280, 2560},		/* mode 6 */
+	{640, 1280, 2560},		/* mode 7 */
+	{800, 1600, 3200},		/* mode 8 */
+	{768, 1536, 2072},		/* mode 9 */
+	{800, 1600, 3200},		/* mode 10 */
+	{800, 1600, 3200},		/* mode 11 */
+	{800, 1600, 3200},		/* mode 12 */
+	{832, 1664, 3328},		/* mode 13 */
+	{1024, 2048, 4096},		/* mode 14 */
+	{1024, 2048, 4096},		/* mode 15 */
+	{1024, 2048, 4096},		/* mode 16 */
+	{1024, 2048, 4096},		/* mode 17 */
+	{1152, 2304, 4608},		/* mode 18 */
+	{1280, 2560, 5120},		/* mode 19 */
+	{1280, 2560, 5120}		/* mode 20 */
 };
+#endif
 
+#include "ati-gx.h"
 #include "ati-gt.h"
 #include "ati-vt.h"
 
 static struct aty_regvals *aty_gt_reg_init[20] = {
 	NULL, NULL, NULL, NULL,
+	&aty_gt_reg_init_5,
 	&aty_gt_reg_init_6,
-	&aty_gt_reg_init_6,
-	NULL,
 	NULL, NULL,
-	NULL,
-	NULL,NULL,
+	&aty_gt_reg_init_9,
+	&aty_gt_reg_init_10,
+	&aty_gt_reg_init_11,
+	&aty_gt_reg_init_12,
 	&aty_gt_reg_init_13,
 	&aty_gt_reg_init_14,
 	&aty_gt_reg_init_15,
 	NULL,
 	&aty_gt_reg_init_17,
-	NULL,
+	&aty_gt_reg_init_18,
 	NULL,
 	&aty_gt_reg_init_20
 };
 
-static struct aty_regvals *aty_vt_reg_init[20] = {
+static struct aty_regvals *aty_gx_reg_init[20] = {
 	NULL, NULL, NULL, NULL,
-	&aty_vt_reg_init_5,
-	&aty_vt_reg_init_6,
+	&aty_gx_reg_init_6,
+	&aty_gx_reg_init_6,
+	NULL, NULL, NULL, NULL, NULL, NULL,
+	&aty_gx_reg_init_13,
+	&aty_gx_reg_init_14,
+	&aty_gx_reg_init_15,
 	NULL,
+	&aty_gx_reg_init_17,
 	NULL, NULL,
+	&aty_gx_reg_init_20
+};
+
+static struct aty_regvals *aty_vt_reg_init[21] = {
+	NULL, NULL, NULL, NULL,
+	&aty_vt_reg_init_5,
+	&aty_vt_reg_init_6,
+	NULL, NULL, NULL,
 	&aty_vt_reg_init_10,
 	&aty_vt_reg_init_11,
 	&aty_vt_reg_init_12,
@@ -119,330 +143,489 @@
 	NULL,
 	&aty_vt_reg_init_17,
 	&aty_vt_reg_init_18,
-	NULL,
+	&aty_vt_reg_init_19,
 	&aty_vt_reg_init_20
 };
 
-static inline int aty_vram_reqd(int vmode, int cmode)
+static inline int
+aty_vram_reqd(int vmode, int cmode)
 {
-	return vmode_attrs[vmode-1].vres
-		* pitch[vmode-1][cmode];
+	return vmode_attrs[vmode - 1].vres * 
+		(vmode_attrs[vmode - 1].hres << cmode);
 }
 
-extern inline unsigned aty_ld_rev(volatile unsigned long addr)
+extern inline unsigned aty_ld_le32(volatile unsigned long addr)
 {
-     unsigned val;
+	register unsigned long temp = ati_regbase,val;
 
-     (long)addr += (long)aty_regbase;
-     asm volatile("lwbrx %0,0,%1" : "=r" (val) : "r" (addr));
-     return val;
+	asm("lwbrx %0,%1,%2": "=r"(val):"r"(addr), "r"(temp));
+	return val;
 }
 
-extern inline void aty_st_rev(volatile unsigned long addr, unsigned val)
+extern inline void aty_st_le32(volatile unsigned long addr, unsigned val)
 {
-     (long)addr += (long)aty_regbase;
-     asm volatile("stwbrx %0,0,%1" : : "r" (val), "r" (addr) : "memory");
+	register unsigned long temp = ati_regbase;
+	asm("stwbrx %0,%1,%2": : "r"(val), "r"(addr), "r"(temp):"memory");
 }
 
-extern inline unsigned char aty_ld_byte(volatile unsigned long addr)
+extern inline unsigned char aty_ld_8(volatile unsigned long addr)
 {
-     unsigned char val;
-
-     val = *(char*)((long)addr+(long)aty_regbase);
-     return val;
+	return *(char *) ((long) addr + (long) ati_regbase);
 }
 
-extern inline void aty_st_byte(volatile unsigned long addr, unsigned char val)
+extern inline void aty_st_8(volatile unsigned long addr, unsigned char val)
 {
-     *(unsigned char*)(addr+(unsigned long)aty_regbase) = val;
+	*(unsigned char *) (addr + (unsigned long) ati_regbase) = val;
 }
 
-void
-aty_st_514( int offset, char val )
+static void aty_st_514(int offset, char val)
 {
-     aty_WaitQueue(5);
-     aty_st_byte( DAC_CNTL, 1);
-     aty_st_byte( DAC_W_INDEX, offset & 0xff );   /* right addr byte */
-     aty_st_byte( DAC_DATA, (offset>>8) & 0xff ); /* left addr byte */
-     aty_st_byte( DAC_MASK, val );
-     aty_st_byte( DAC_CNTL, 0 );
+	aty_WaitQueue(5);
+	aty_st_8(DAC_CNTL, 1);
+	aty_st_8(DAC_W_INDEX, offset & 0xff);	/* right addr byte */
+	aty_st_8(DAC_DATA, (offset >> 8) & 0xff);	/* left addr byte */
+	eieio();
+	aty_st_8(DAC_MASK, val);
+	eieio();
+	aty_st_8(DAC_CNTL, 0);
 }
 
-void
-aty_st_pll( int offset, char val )
+static void
+aty_st_pll(int offset, char val)
 {
-     aty_WaitQueue(3);
-     aty_st_byte( CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN );   /* write addr byte */
-     aty_st_byte( CLOCK_CNTL + 2, val); /* write the register value */
-     aty_st_byte( CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN);
+	aty_WaitQueue(3);
+	aty_st_8(CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN);	/* write addr byte */
+	eieio();
+	aty_st_8(CLOCK_CNTL + 2, val);	/* write the register value */
+	eieio();
+	aty_st_8(CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN);
+}
+
+#if 0 // unused
+static char
+aty_ld_pll(int offset)
+{
+	aty_WaitQueue(2);
+	aty_st_8(CLOCK_CNTL + 1, offset << 2);
+	eieio();
+	return aty_ld_8(CLOCK_CNTL + 2);
 }
+#endif
 
-#if 0
 unsigned char
-aty_ld_514( int offset )
+aty_ld_514(int offset)
 {
-/* do the same thing as aty_st_514, just read the DAC_MASK instead of writing*/
+/* do the same thing as aty_st_514, just read the DAC_MASK instead of writing */
+	char val;
+
+	aty_WaitQueue(5);
+	aty_st_8(DAC_CNTL, 1);
+	aty_st_8(DAC_W_INDEX, offset & 0xff);	/* right addr byte */
+	aty_st_8(DAC_DATA, (offset >> 8) & 0xff);	/* left addr byte */
+	val = aty_ld_8(DAC_MASK);
+	eieio();
+	aty_st_8(DAC_CNTL, 0);
+	return val;
 }
-#endif
 
 void
 aty_set_palette(unsigned char red[], unsigned char green[],
 		unsigned char blue[], int index, int ncolors)
 {
-     int i,scale;
+	int i, scale;
+
+	aty_WaitQueue(2);
+
+	i = aty_ld_8(DAC_CNTL) & 0xfc;
+	if (chip_type == MACH64_GT_ID)
+		i |= 0x2;		/*DAC_CNTL|0x2 turns off the extra brightness for gt*/
+	aty_st_8(DAC_CNTL, i);
+	aty_st_8(DAC_REGS + DAC_MASK, 0xff);
+	eieio();
+	scale = (chip_type != MACH64_GX_ID)
+		? ((color_mode == CMODE_16) ? 3 : 0) : 0;
+
+	for (i = 0; i < ncolors; ++i) {
+		aty_WaitQueue(4);
+		aty_cmap_regs->windex = (index + i) << scale; eieio();
+		aty_cmap_regs->lut = red[i];	eieio();
+		aty_cmap_regs->lut = green[i];	eieio();
+		aty_cmap_regs->lut = blue[i];	eieio();
+	}
+}
+
+static aty_regvals
+*get_aty_struct()
+{
+	int v = video_mode - 1;
+
+	switch (chip_type) {
+	case MACH64_GT_ID:
+		return aty_gt_reg_init[v];
+		break;
+	case MACH64_VT_ID:
+		return aty_vt_reg_init[v];
+		break;
+	default: /* default to MACH64_GX_ID */
+		return aty_gx_reg_init[v];
+		break;
+	}
+}
+
+static int
+read_aty_sense(void)
+{
+	int sense, i;
 
-     aty_WaitQueue(2);
-     aty_st_byte(DAC_CNTL, aty_ld_byte(DAC_CNTL) & 0xfc);
-     aty_st_byte(DAC_REGS + DAC_MASK, 0xff);
-     eieio();
-     scale = (is_vt_chip) ? ((color_mode == CMODE_16) ? 3 : 0) : 0;
-     for (i = 0; i < ncolors; ++i) {
-	  aty_WaitQueue(4);
-          aty_cmap_regs->windex = (index + i) << scale;	eieio();
-          aty_cmap_regs->lut = red[i];		eieio();
-          aty_cmap_regs->lut = green[i];	eieio();
-          aty_cmap_regs->lut = blue[i];		eieio();
-     }
-   
+	aty_st_le32(MON_SENSE, 0x31003100);	/* drive outputs high */
+	__delay(200);
+	aty_st_le32(MON_SENSE, 0);		/* turn off outputs */
+	__delay(2000);
+	i = aty_ld_le32(MON_SENSE);		/* get primary sense value */
+	sense = ((i & 0x3000) >> 3) | (i & 0x100);
+
+	/* drive each sense line low in turn and collect the other 2 */
+	aty_st_le32(MON_SENSE, 0x20000000);	/* drive A low */
+	__delay(2000);
+	i = aty_ld_le32(MON_SENSE);
+	sense |= ((i & 0x1000) >> 7) | ((i & 0x100) >> 4);
+	aty_st_le32(MON_SENSE, 0x20002000);	/* drive A high again */
+	__delay(200);
+
+	aty_st_le32(MON_SENSE, 0x10000000);	/* drive B low */
+	__delay(2000);
+	i = aty_ld_le32(MON_SENSE);
+	sense |= ((i & 0x2000) >> 10) | ((i & 0x100) >> 6);
+	aty_st_le32(MON_SENSE, 0x10001000);	/* drive B high again */
+	__delay(200);
+
+	aty_st_le32(MON_SENSE, 0x01000000);	/* drive C low */
+	__delay(2000);
+	sense |= (aty_ld_le32(MON_SENSE) & 0x3000) >> 12;
+	aty_st_le32(MON_SENSE, 0);		/* turn off outputs */
+
+	return sense;
 }
 
 void
 map_aty_display(struct device_node *dp)
 {
-     int i, sense;
-     unsigned long addr;
-     unsigned char bus, devfn;
-     unsigned short cmd;
-
-     if (dp->next != 0)
-	  printk("Warning: only using first ATI card detected\n");
-     if (dp->n_addrs != 1)
-	  panic("expecting 1 addresses for ATY (got %d)", dp->n_addrs);
-
-     aty_regbase = (int)ioremap((0x7ffc00 + dp->addrs[0].address), 0x1000);
-     aty_cmap_regs = (struct aty_cmap_regs *)(aty_regbase+0xC0);
-
-     /* enable memory-space accesses using config-space command register */
-     if (pci_device_loc(dp, &bus, &devfn) == 0) {
-	  pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
-	  if (cmd != 0xffff) {
-	       cmd |= PCI_COMMAND_MEMORY;
-	       pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
-	  }
-     }
-
-     switch( aty_ld_rev(MEM_CNTL)&MEM_SIZE_ALIAS ) {
-     case MEM_SIZE_512K:
-	  total_vram = 0x80000;
-	  break;
-     case MEM_SIZE_1M:
-	  total_vram = 0x100000;
-	  break;
-     case MEM_SIZE_2M:
-	  total_vram = 0x200000;
-	  break;
-     case MEM_SIZE_4M:
-	  total_vram = 0x400000;
-	  break;
-     case MEM_SIZE_6M:
-	  total_vram = 0x600000;
-	  break;
-     case MEM_SIZE_8M:
-	  total_vram = 0x800000;
-	  break;
-     default:
-	  total_vram = 0x80000;
-     }
+	struct aty_regvals *init;
+	int i, sense;
+	unsigned long addr;
+	unsigned char bus, devfn;
+	unsigned short cmd;
+
+	if (dp->next != 0)
+	printk("Warning: only using first ATI card detected\n");
+	if (dp->n_addrs != 1 && dp->n_addrs != 3)
+	printk("Warning: expecting 1 or 3 addresses for ATY (got %d)",
+	       dp->n_addrs);
+
+	ati_regbase = (int) ioremap((0x7ffc00 + dp->addrs[0].address), 0x1000);
+	aty_cmap_regs = (struct aty_cmap_regs *) (ati_regbase + 0xC0);
+
+	/* enable memory-space accesses using config-space command register */
+	if (pci_device_loc(dp, &bus, &devfn) == 0) {
+		pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
+		if (cmd != 0xffff) {
+			cmd |= PCI_COMMAND_MEMORY;
+			pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
+		}
+	}
+	chip_type = (aty_ld_le32(CONFIG_CHIP_ID) & CFG_CHIP_TYPE);
+
+	i = aty_ld_le32(MEM_CNTL);
+	if (chip_type != MACH64_GT_ID)
+		switch (i & MEM_SIZE_ALIAS) {
+		case MEM_SIZE_512K:
+			total_vram = 0x80000;
+			break;
+		case MEM_SIZE_1M:
+			total_vram = 0x100000;
+			break;
+		case MEM_SIZE_2M:
+			total_vram = 0x200000;
+			break;
+		case MEM_SIZE_4M:
+			total_vram = 0x400000;
+			break;
+		case MEM_SIZE_6M:
+			total_vram = 0x600000;
+			break;
+		case MEM_SIZE_8M:
+			total_vram = 0x800000;
+			break;
+		default:
+			total_vram = 0x80000;
+		}
+	else
+		switch (i & 0xF) { /* 0xF used instead of MEM_SIZE_ALIAS */
+		case MEM_SIZE_512K:
+			total_vram = 0x80000;
+			break;
+		case MEM_SIZE_1M:
+			total_vram = 0x100000;
+			break;
+		case MEM_SIZE_2M_GTB:
+			total_vram = 0x200000;
+			break;
+		case MEM_SIZE_4M_GTB:
+			total_vram = 0x400000;
+			break;
+		case MEM_SIZE_6M_GTB:
+			total_vram = 0x600000;
+			break;
+		case MEM_SIZE_8M_GTB:
+			total_vram = 0x800000;
+			break;
+		default:
+			total_vram = 0x80000;
+		}
+
 #if 1
-     printk("aty_display_init: node = %p, addrs = ", dp->node);
-     printk(" %x(%x)", dp->addrs[0].address, dp->addrs[0].size);
-     printk(", intrs =");
-     for (i = 0; i < dp->n_intrs; ++i)
-	  printk(" %x", dp->intrs[i]);
-     printk( "\nregbase: %x pci loc: %x:%x total_vram: %x cregs: %x\n", (int)aty_regbase, 
-	     bus, devfn, total_vram, (int)aty_cmap_regs );
+	printk("aty_display_init: node = %p, addrs = ", dp->node);
+	printk(" %x(%x)", dp->addrs[0].address, dp->addrs[0].size);
+	printk(", intrs =");
+	for (i = 0; i < dp->n_intrs; ++i)
+	printk(" %x", dp->intrs[i]);
+	printk("\nregbase: %x pci loc: %x:%x total_vram: %x cregs: %x\n", (int) ati_regbase,
+		bus, devfn, total_vram, (int) aty_cmap_regs);
 #endif
-     is_vt_chip = ((aty_ld_rev(CONFIG_CHIP_ID) & CFG_CHIP_TYPE) == MACH64_VT_ID);
-     /* Map in frame buffer */
-     addr = dp->addrs[0].address;
-
-     /* use the big-endian aperture (??) */
-     addr += 0x800000;
-     frame_buffer = ioremap(addr, 0x800000);
-     
-
-     /*	sense = read_aty_sense(); XXX not yet, just give it mine */
-     sense = 0x62b;
-     if (video_mode == VMODE_NVRAM) {
-	  video_mode = nvram_read_byte(NV_VMODE);
-	  if (video_mode <= 0 || video_mode > VMODE_MAX
-	      || ((is_vt_chip) ? aty_vt_reg_init[video_mode-1] : aty_gt_reg_init[video_mode-1]) == 0)
-	       video_mode = VMODE_CHOOSE;
-     }
-     if (video_mode == VMODE_CHOOSE)
-	  video_mode = map_monitor_sense(sense);
-     if (((is_vt_chip) ? aty_vt_reg_init[video_mode-1] : aty_gt_reg_init[video_mode-1]) == 0)
-	  video_mode = VMODE_640_480_60;
-
-     /*
-      * Reduce the pixel size if we don't have enough VRAM.
-      */
-
-     if (color_mode == CMODE_NVRAM)
-        color_mode = nvram_read_byte(NV_CMODE);
-     if (color_mode < CMODE_8 || color_mode > CMODE_32)
-	color_mode = CMODE_8;
-     while (aty_vram_reqd(video_mode, color_mode) > total_vram) {
-     	while (color_mode > CMODE_8
-	    && aty_vram_reqd(video_mode, color_mode) > total_vram)
-	  --color_mode;
-        /*
-	 * adjust the video mode smaller if there still is not enough VRAM
+	/* Map in frame buffer */
+	addr = dp->addrs[0].address;
+
+	/* use the big-endian aperture (??) */
+	addr += 0x800000;
+	frame_buffer = ioremap(addr, 0x800000);
+
+	sense = read_aty_sense();
+	printk("monitor sense = %x\n", sense);
+	if (video_mode == VMODE_NVRAM) {
+		video_mode = nvram_read_byte(NV_VMODE);
+		init = get_aty_struct();
+		if (video_mode <= 0 || video_mode > VMODE_MAX || init == 0)
+			video_mode = VMODE_CHOOSE;
+	}
+	if (video_mode == VMODE_CHOOSE)
+		video_mode = map_monitor_sense(sense);
+
+	init = get_aty_struct();
+	if (!init)
+		video_mode = VMODE_640_480_60;
+
+	/*
+	 * Reduce the pixel size if we don't have enough VRAM.
 	 */
-	if (aty_vram_reqd(video_mode, color_mode) > total_vram)
-	  while ((((is_vt_chip) ? aty_vt_reg_init[--video_mode - 1]
-	        : aty_gt_reg_init[--video_mode -1 ]) == 0) && (video_mode > VMODE_640_480_60)) ;
-    }
-}
 
-#if 0
-static void
-set_aty_clock(unsigned char *params)
-{
-     /* done in aty_init...probably need to change for different modes */
-     printk("tried to set ATY clock\n");
+	if (color_mode == CMODE_NVRAM)
+		color_mode = nvram_read_byte(NV_CMODE);
+	if (color_mode < CMODE_8 || color_mode > CMODE_32)
+		color_mode = CMODE_8;
+
+	while (aty_vram_reqd(video_mode, color_mode) > total_vram) {
+		while (color_mode > CMODE_8
+			&& aty_vram_reqd(video_mode, color_mode) > total_vram)
+			--color_mode;
+		/*
+		 * adjust the video mode smaller if there still is not enough VRAM
+		 */
+		if (aty_vram_reqd(video_mode, color_mode) > total_vram) {
+			do {
+				video_mode--;
+				init = get_aty_struct();
+			} while ((init == 0) && (video_mode > VMODE_640_480_60));
+		}
+	}
+
+	if (chip_type == MACH64_GT_ID && (aty_ld_le32(CONFIG_STAT0) & 7) == 5
+			&& init->crtc_gen_cntl[1] == 0) {
+		video_mode = 6; color_mode = 0;
+	}
+	return;
 }
-#endif
 
 void
 RGB514_Program(int cmode)
 {
-     typedef struct {
-          char    pixel_dly;
-          char    misc2_cntl;
-          char    pixel_rep;
-          char    pixel_cntl_index;
-          char    pixel_cntl_v1;
-     } RGB514_DAC_Table;
-
-     static  RGB514_DAC_Table RGB514DAC_Tab[8] = {
-          { 0, 0x41, 0x03, 0x71, 0x45 },          // 8bpp
-          { 0, 0x45, 0x04, 0x0c, 0x01 },          // 555
-          { 0, 0x45, 0x06, 0x0e, 0x00 },          // XRGB
-     };
-     RGB514_DAC_Table *pDacProgTab;
-
-     pDacProgTab = &RGB514DAC_Tab[cmode];
-
-     aty_st_514(0x90, 0x00);
-     aty_st_514(0x04, pDacProgTab->pixel_dly);
-     aty_st_514(0x05, 0x00);
-
-     aty_st_514(0x2, 0x1);
-     aty_st_514(0x71, pDacProgTab->misc2_cntl);
-     aty_st_514(0x0a, pDacProgTab->pixel_rep);
+	typedef struct {
+		char pixel_dly;
+		char misc2_cntl;
+		char pixel_rep;
+		char pixel_cntl_index;
+		char pixel_cntl_v1;
+	} RGB514_DAC_Table;
+
+	static RGB514_DAC_Table RGB514DAC_Tab[8] = {
+		{0, 0x41, 0x03, 0x71, 0x45},	// 8bpp
+		{0, 0x45, 0x04, 0x0c, 0x01},	// 555
+		{0, 0x45, 0x06, 0x0e, 0x00},	// XRGB
+	};
+	RGB514_DAC_Table *pDacProgTab;
+
+	pDacProgTab = &RGB514DAC_Tab[cmode];
+
+	aty_st_514(0x90, 0x00);
+	aty_st_514(0x04, pDacProgTab->pixel_dly);
+	aty_st_514(0x05, 0x00);
+
+	aty_st_514(0x2, 0x1);
+	aty_st_514(0x71, pDacProgTab->misc2_cntl);
+	aty_st_514(0x0a, pDacProgTab->pixel_rep);
 
-     aty_st_514(pDacProgTab->pixel_cntl_index, pDacProgTab->pixel_cntl_v1);
+	aty_st_514(pDacProgTab->pixel_cntl_index, pDacProgTab->pixel_cntl_v1);
 }
 
-/* The vt chipset seems to need a specialized color table for 15 bit mode */
 void
-VT_Program(int cmode)
+aty_init()
 {
-#if 0
-	int i;
-	if (cmode != CMODE_8) {
-		aty_WaitQueue(2);
-		aty_cmap_regs->mask = 0xff; eieio();
-		aty_cmap_regs->windex = 0; eieio();
-		for (i = 0; i < 0x100; i++) {
-			aty_WaitQueue(3);
-			aty_cmap_regs->lut = i;
-			aty_cmap_regs->lut = i;
-			aty_cmap_regs->lut = i; eieio();
+	int i, hres;
+	struct aty_regvals *init = get_aty_struct();
+	int vram_type = aty_ld_le32(CONFIG_STAT0) & 7;
+
+	if (init == 0) /* paranoia, shouldn't get here */
+		panic("aty: display mode %d not supported", video_mode);
+
+	n_scanlines = vmode_attrs[video_mode - 1].vres;
+	hres = vmode_attrs[video_mode - 1].hres;
+	pixel_size = 1 << color_mode;
+	line_pitch = vmode_attrs[video_mode - 1].hres << color_mode;
+	row_pitch = line_pitch * 16;
+
+	/* clear FIFO errors */
+	aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL) | BUS_HOST_ERR_ACK
+		| BUS_FIFO_ERR_ACK);
+
+	/* Reset engine */
+	i = aty_ld_le32(GEN_TEST_CNTL);
+	aty_st_le32(GEN_TEST_CNTL, i & ~GUI_ENGINE_ENABLE);
+	eieio();
+	aty_WaitIdleEmpty();
+	aty_st_le32(GEN_TEST_CNTL, i | GUI_ENGINE_ENABLE);
+	aty_WaitIdleEmpty();
+
+	if ( chip_type != MACH64_GT_ID ) {
+		i = aty_ld_le32(CRTC_GEN_CNTL);
+		aty_st_le32(CRTC_GEN_CNTL, i | CRTC_EXT_DISP_EN);
+	}
+
+	if ( chip_type == MACH64_GX_ID ) { 
+		i = aty_ld_le32(GEN_TEST_CNTL);
+		aty_st_le32(GEN_TEST_CNTL, i | GEN_OVR_OUTPUT_EN );
+	}
+
+	switch (chip_type) {
+	case MACH64_VT_ID:
+		aty_st_pll(PLL_MACRO_CNTL, 0xb5);
+		aty_st_pll(PLL_REF_DIV, 0x2d);
+		aty_st_pll(PLL_GEN_CNTL, 0x14);
+		aty_st_pll(MCLK_FB_DIV, 0xbd);
+		aty_st_pll(PLL_VCLK_CNTL, 0x0b);
+		aty_st_pll(VCLK_POST_DIV, init->clock_val[0]);
+		aty_st_pll(VCLK0_FB_DIV, init->clock_val[1]);
+		aty_st_pll(VCLK1_FB_DIV, 0xd6);
+		aty_st_pll(VCLK2_FB_DIV, 0xee);
+		aty_st_pll(VCLK3_FB_DIV, 0xf8);
+		aty_st_pll(PLL_XCLK_CNTL, 0x0);
+		aty_st_pll(PLL_TEST_CTRL, 0x0);
+		aty_st_pll(PLL_TEST_COUNT, 0x0);
+		break;
+	case MACH64_GT_ID:
+		if (vram_type == 5) {
+			aty_st_pll(0, 0xcd);
+			aty_st_pll(PLL_MACRO_CNTL, 
+				  video_mode >= VMODE_1024_768_60? 0xd3: 0xd5);
+			aty_st_pll(PLL_REF_DIV, 0x21);
+			aty_st_pll(PLL_GEN_CNTL, 0x44);
+			aty_st_pll(MCLK_FB_DIV, 0xe8);
+			aty_st_pll(PLL_VCLK_CNTL, 0x03);
+			aty_st_pll(VCLK_POST_DIV, init->offset[0]);
+			aty_st_pll(VCLK0_FB_DIV, init->offset[1]);
+			aty_st_pll(VCLK1_FB_DIV, 0x8e);
+			aty_st_pll(VCLK2_FB_DIV, 0x9e);
+			aty_st_pll(VCLK3_FB_DIV, 0xc6);
+			aty_st_pll(PLL_XCLK_CNTL, init->offset[2]);
+			aty_st_pll(12, 0xa6);
+			aty_st_pll(13, 0x1b);
+		} else {
+			aty_st_pll(PLL_MACRO_CNTL, 0xd5);
+			aty_st_pll(PLL_REF_DIV, 0x21);
+			aty_st_pll(PLL_GEN_CNTL, 0xc4); 
+			aty_st_pll(MCLK_FB_DIV, 0xda);
+			aty_st_pll(PLL_VCLK_CNTL, 0x03);
+			/* offset actually holds clock values */
+			aty_st_pll(VCLK_POST_DIV, init->offset[0]);
+			aty_st_pll(VCLK0_FB_DIV, init->offset[1]);
+			aty_st_pll(VCLK1_FB_DIV, 0x8e);
+			aty_st_pll(VCLK2_FB_DIV, 0x9e);
+			aty_st_pll(VCLK3_FB_DIV, 0xc6);
+			aty_st_pll(PLL_TEST_CTRL, 0x0);
+			aty_st_pll(PLL_XCLK_CNTL, init->offset[2]);
+			aty_st_pll(12, 0xa0);
+			aty_st_pll(13, 0x1b);
 		}
+		break;
+	default:
+		RGB514_Program(color_mode);
+		aty_WaitIdleEmpty();
+		aty_st_514(0x06, 0x02);
+		aty_st_514(0x10, 0x01);
+		aty_st_514(0x70, 0x01);
+		aty_st_514(0x8f, 0x1f);
+		aty_st_514(0x03, 0x00);
+		aty_st_514(0x05, 0x00);
+		aty_st_514(0x20, init->clock_val[0]);
+		aty_st_514(0x21, init->clock_val[1]);
+		break;
 	}
-#endif
-}
 
-void
-aty_init()
-{
-     int i, yoff, hres;
-     unsigned *p;
-     struct aty_regvals *init;
-
-     if (video_mode <= 0 || video_mode > VMODE_MAX
-	 || (init = ((is_vt_chip) ? aty_vt_reg_init[video_mode-1] : aty_gt_reg_init[video_mode-1])) == 0)
-	  panic("aty: display mode %d not supported", video_mode);
-     n_scanlines = vmode_attrs[video_mode-1].vres;
-     hres = vmode_attrs[video_mode-1].hres;
-     pixel_size = 1 << color_mode;
-     line_pitch = pitch[video_mode-1][color_mode];
-     row_pitch = line_pitch * 16;
-
-     aty_st_rev(BUS_CNTL, aty_ld_rev(BUS_CNTL) | BUS_HOST_ERR_ACK | BUS_FIFO_ERR_ACK);
-		
-     /* Reset engine */
-     i = aty_ld_rev(GEN_TEST_CNTL);
-     aty_st_rev(GEN_TEST_CNTL, i & ~GUI_ENGINE_ENABLE);
-     eieio();
-     aty_WaitIdleEmpty();
-     aty_st_rev(GEN_TEST_CNTL, i | GUI_ENGINE_ENABLE);
-     aty_WaitIdleEmpty();
-			
-     i = aty_ld_byte(CRTC_GEN_CNTL+3);
-     aty_st_byte(CRTC_GEN_CNTL+3, i | (CRTC_EXT_DISP_EN >> 24));
-
-     i = aty_ld_byte(GEN_TEST_CNTL);
-     aty_st_byte(GEN_TEST_CNTL, i | GEN_OVR_OUTPUT_EN );
-
-     if (!is_vt_chip) {		
-     	RGB514_Program(color_mode);
-
-     	aty_WaitIdleEmpty();
-
-     	aty_st_514(0x06, 0x02);
-     	aty_st_514(0x10, 0x01);
-     	aty_st_514(0x70, 0x01);
-     	aty_st_514(0x8f, 0x1f);
-     	aty_st_514(0x03, 0x00);
-     	aty_st_514(0x05, 0x00);
-     
-     	aty_st_514(0x20, init->clock_val[0]);
-    	aty_st_514(0x21, init->clock_val[1]);
-     } else {
-	VT_Program(color_mode);
-    	aty_st_pll(PLL_MACRO_CNTL, 0xb5);
-    	aty_st_pll(PLL_REF_DIV, 0x2d);
-    	aty_st_pll(PLL_GEN_CNTL, 0x14);
-    	aty_st_pll(MCLK_FB_DIV, 0xbd);
-    	aty_st_pll(PLL_VCLK_CNTL, 0x0b);
-    	aty_st_pll(VCLK_POST_DIV, init->clock_val[0]);
-    	aty_st_pll(VCLK0_FB_DIV, init->clock_val[1]);
-    	aty_st_pll(VCLK1_FB_DIV, 0xd6);
-    	aty_st_pll(VCLK2_FB_DIV, 0xee);
-    	aty_st_pll(VCLK3_FB_DIV, 0xf8);
-    	aty_st_pll(PLL_XCLK_CNTL, 0x0);
-    	aty_st_pll(PLL_TEST_CTRL, 0x0);
-     	aty_st_pll(PLL_TEST_COUNT, 0x0);
-     }
- 	  	
-     aty_ld_byte( DAC_REGS ); /* clear counter */
-			
-     aty_WaitIdleEmpty();
-
-     aty_st_rev(CRTC_H_TOTAL_DISP, init->crtc_h_tot_disp);
-     aty_st_rev(CRTC_H_SYNC_STRT_WID, init->crtc_h_sync_strt_wid[color_mode]);
-     aty_st_rev(CRTC_V_TOTAL_DISP, init->crtc_v_tot_disp);
-     aty_st_rev(CRTC_V_SYNC_STRT_WID, init->crtc_v_sync_strt_wid);
-
-     aty_st_byte(CLOCK_CNTL, 0);
-     aty_st_byte(CLOCK_CNTL, CLOCK_STROBE);
-
-     aty_st_rev(CRTC_OFF_PITCH, init->crtc_off_pitch);
-     
-     aty_st_rev(CRTC_VLINE_CRNT_VLINE, 0x14e01d0);
+	aty_ld_8(DAC_REGS);	/* clear counter */
+	aty_WaitIdleEmpty();
+
+	aty_st_le32(CRTC_H_TOTAL_DISP, init->crtc_h_tot_disp);
+	aty_st_le32(CRTC_H_SYNC_STRT_WID, init->crtc_h_sync_strt_wid[color_mode]);
+	aty_st_le32(CRTC_V_TOTAL_DISP, init->crtc_v_tot_disp);
+	aty_st_le32(CRTC_V_SYNC_STRT_WID, init->crtc_v_sync_strt_wid);
+
+	aty_st_8(CLOCK_CNTL, 0);
+	aty_st_8(CLOCK_CNTL, CLOCK_STROBE);
+
+	aty_st_le32(CRTC_VLINE_CRNT_VLINE, 0);
+
+	if (chip_type == MACH64_GT_ID) {
+		aty_st_le32(BUS_CNTL, 0x7b23a040);
+
+		/* we calculate this so we can use a scrollback buffer.
+		 * this should theoretically work with other ati's
+		 * OFF_PITCH == (((hres + 7) & 0xfff8) >> 3) << 22 
+		 */
+		ati_set_origin(0);
+
+		/* need to set DSP values !! assume sdram */
+		i = init->crtc_gen_cntl[0] - (0x100000 * color_mode);
+		if ( vram_type == 5 )
+			i = init->crtc_gen_cntl[1] - (0x100000 * color_mode);
+		aty_st_le32(DSP_CONFIG, i); 
+
+		i = aty_ld_le32(MEM_CNTL) & MEM_SIZE_ALIAS;
+		if ( vram_type == 5 ) {
+			i |= ((1 * color_mode) << 26) | 0x4215b0;
+			aty_st_le32(DSP_ON_OFF,sgram_dsp[video_mode-1][color_mode]);
+
+		//aty_st_le32(CLOCK_CNTL,8192);
+		} else {
+			i |= ((1 * color_mode) << 26) | 0x300090;
+			aty_st_le32(DSP_ON_OFF, init->mem_cntl[color_mode]);
+		}
+
+		aty_st_le32(MEM_CNTL, i);
+		aty_st_le32(EXT_MEM_CNTL, 0x5000001);
+
+		/* if (total_vram > 0x400000)	
+			i |= 0x538; this not been verified on > 4Megs!! */
+	} else {
+		aty_st_le32(CRTC_OFF_PITCH, init->crtc_off_pitch);
+
 /* The magic constant below translates into: 
  * 5   = No RDY delay, 1 wait st for mem write, increment during burst transfer
  * 9   = DAC access delayed, 1 wait state for DAC
@@ -455,75 +638,115 @@
  * at some point it would be good to experiment with bench marks to see if
  * we can gain some speed by fooling with the wait states etc.
  */
-     aty_st_rev(BUS_CNTL, 0x590e10ff);
-     i = aty_ld_rev(MEM_CNTL) & MEM_SIZE_ALIAS;
-     if (total_vram >= 0x400000)
-        aty_st_rev(MEM_CNTL, (init->mem_cntl[color_mode] &0xffff0000) | 0x0538 | i);
-     else
-        aty_st_rev(MEM_CNTL, (init->mem_cntl[color_mode] & ~MEM_SIZE_ALIAS) | i ); 
-     aty_st_rev(CRTC_INT_CNTL, 0x2);
-     aty_WaitIdleEmpty();
+		if (chip_type == MACH64_VT_ID)
+			aty_st_le32(BUS_CNTL, 0x680000f9);
+		else
+			aty_st_le32(BUS_CNTL, 0x590e10ff);
+
+		switch (total_vram) {
+		case 0x00100000:
+			aty_st_le32(MEM_CNTL, vt_mem_cntl[0][color_mode]);
+			break;
+		case 0x00200000:
+			aty_st_le32(MEM_CNTL, vt_mem_cntl[1][color_mode]);
+			break;
+		case 0x00400000:
+			aty_st_le32(MEM_CNTL, vt_mem_cntl[2][color_mode]);
+			break;
+		default:
+			i = aty_ld_le32(MEM_CNTL) & 0x000F;
+			aty_st_le32(MEM_CNTL, (init->mem_cntl[color_mode] & 0xFFFFFFF0) | i);
+		}
+	}
 /* These magic constants are harder to figure out
- * on the vt chipset bit 3 set makes the screen brighter
+ * on the vt chipset bit 2 set makes the screen brighter
  * and bit 15 makes the screen black! But nothing else
  * seems to matter for the vt DAC_CNTL
  */
-     if (is_vt_chip)
-	aty_st_rev(DAC_CNTL, 0x47012104);
-     else
-        aty_st_rev(DAC_CNTL, 0x47012100);
-
-     aty_st_byte(DAC_MASK, 0xff);
-
-     aty_st_rev(CRTC_INT_CNTL, 0x00000002);
-     
-     aty_st_byte(CRTC_FIFO, ((char*)&init->crtc_gen_cntl[color_mode])[1] );
-     aty_st_byte(CRTC_PIX_WIDTH, ((char*)&init->crtc_gen_cntl[color_mode])[2] );
-     aty_st_byte(CRTC_EXT_DISP, ((char*)&init->crtc_gen_cntl[color_mode])[0] );
-     
-     aty_st_rev(GEN_TEST_CNTL, 0x300); /* gui_en block_en*/
-
-     pmac_init_palette();		/* Initialize colormap */
-     yoff = (n_scanlines % 16) / 2;
-     fb_start = frame_buffer + yoff * line_pitch + init->offset[color_mode];
-
-     /* Clear screen */
-     p = (unsigned *) fb_start;
-
-     for (i = n_scanlines * line_pitch * pixel_size / sizeof(unsigned); i != 0; --i)
-	  *p++ = 0;
-     display_info.height = n_scanlines;
-     display_info.width = hres;
-     display_info.depth = pixel_size * 8;
-     display_info.pitch = line_pitch;
-     display_info.mode = video_mode;
-     strncpy(display_info.name, "ATY Mach64", sizeof(display_info.name));
-     display_info.fb_address = (unsigned long) frame_buffer + init->offset[color_mode];
-     display_info.cmap_adr_address = (unsigned long) &aty_cmap_regs->windex;
-     display_info.cmap_data_address = (unsigned long) &aty_cmap_regs->lut;
-     display_info.disp_reg_address = aty_regbase;
+	switch (chip_type) {
+	case MACH64_GT_ID:
+		i = 0x86010102;
+		break;
+	case MACH64_VT_ID:
+		i = 0x87010184;
+		break;
+	default:
+		i = 0x47012100;
+		break;
+	}
+
+	aty_st_le32(DAC_CNTL, i);
+	aty_st_8(DAC_MASK, 0xff);
+
+	switch (color_mode) {
+	case CMODE_16:
+		i = CRTC_PIX_WIDTH_15BPP; break;
+	/*case CMODE_24: */
+	case CMODE_32:
+		i = CRTC_PIX_WIDTH_32BPP; break;
+	case CMODE_8:
+	default:
+		i = CRTC_PIX_WIDTH_8BPP; break;
+	}
+
+	if (chip_type != MACH64_GT_ID) {
+		aty_st_le32(CRTC_INT_CNTL, 0x00000002);
+		aty_st_le32(GEN_TEST_CNTL, GUI_ENGINE_ENABLE | BLOCK_WRITE_ENABLE);	/* gui_en block_en */
+		i |= init->crtc_gen_cntl[color_mode];
+	}
+	/* Gentlemen, start your crtc engine */
+	aty_st_le32(CRTC_GEN_CNTL, CRTC_EXT_DISP_EN | CRTC_EXT_EN | i);
+	pmac_init_palette();	/* Initialize colormap */
+
+	/* clear screen */
+	fb_start = frame_buffer + (((n_scanlines % 16) * line_pitch) >> 1);
+	memsetw((unsigned short *) fb_start, 0, total_vram);
+
+	display_info.height = n_scanlines;
+	display_info.width = hres;
+	display_info.depth = pixel_size << 3;
+	display_info.pitch = line_pitch;
+	display_info.mode = video_mode;
+	strncpy(display_info.name, "ATY Mach64", sizeof(display_info.name));
+	switch ( chip_type ) {
+	case MACH64_GX_ID: strcat(display_info.name,"GX");
+		break;
+	case MACH64_VT_ID: strcat(display_info.name,"VT");
+		break;
+	case MACH64_GT_ID: strcat(display_info.name,"GT");
+		break;
+	default: strcat(display_info.name,"unknown");
+		break;
+	}
+	display_info.fb_address = (chip_type != MACH64_GT_ID) ?
+		(unsigned long) frame_buffer + init->offset[color_mode] :
+		(unsigned long) frame_buffer;
+	display_info.cmap_adr_address = (unsigned long) &aty_cmap_regs->windex;
+	display_info.cmap_data_address = (unsigned long) &aty_cmap_regs->lut;
+	display_info.disp_reg_address = ati_regbase;
 }
 
 int
 aty_setmode(struct vc_mode *mode, int doit)
 {
-	int cmode;
-#if 0
+	int cmode,old_vmode=video_mode;
+	struct aty_regvals *init;
+
+#if 1
 	if (mode->mode == 21) {
-		printk("hace: about to set 0x%x to 0x%x\n",mode->depth, mode->pitch & 0xff);
-		aty_st_byte(mode->depth, mode->pitch & 0xff);
+		printk("hace: about to set 0x%x to 0x%x\n", mode->depth, mode->pitch & 0xff);
+		aty_st_8(mode->depth, mode->pitch & 0xff);
 		return 0;
 	}
-
 	if (mode->mode == 0) {
-		printk("hace: 0x%x contains 0x%x\n",mode->depth, aty_ld_byte(mode->depth));
+		printk("hace: 0x%x contains 0x%x\n", mode->depth, aty_ld_8(mode->depth));
 		return 0;
 	}
-#endif		
-	
-	if (mode->mode <= 0 || mode->mode > VMODE_MAX
-	    || ((is_vt_chip) ? aty_vt_reg_init[mode->mode-1] : aty_gt_reg_init[mode->mode-1]) == NULL)
+#endif
+
+	if (mode->mode <= 0 || mode->mode > VMODE_MAX )
 		return -EINVAL;
+
 	switch (mode->depth) {
 	case 24:
 	case 32:
@@ -539,12 +762,25 @@
 	default:
 		return -EINVAL;
 	}
-	if (aty_vram_reqd(mode->mode, cmode) > total_vram) {
+	if (aty_vram_reqd(mode->mode, cmode) > total_vram)
 		return -EINVAL;
+
+	video_mode = mode->mode;
+	init = get_aty_struct();
+
+	/* Check if we know about the wanted video mode */
+	if ( init == 0 || init->crtc_h_sync_strt_wid[cmode] == 0
+	   || (chip_type != MACH64_GT_ID && init->crtc_gen_cntl[cmode] == 0)
+	   || (chip_type == MACH64_GT_ID && (aty_ld_le32(CONFIG_STAT0) & 7) == 5
+	   && init->crtc_gen_cntl[1] == 0)) {
+			video_mode = old_vmode;
+			return -EINVAL;
 	}
+
 	if (doit) {
 		video_mode = mode->mode;
 		color_mode = cmode;
+		hide_cursor();
 		aty_init();
 	}
 	return 0;
@@ -555,7 +791,7 @@
 {
 	char gen_cntl;
 
-	gen_cntl = aty_ld_byte(CRTC_GEN_CNTL);
+	gen_cntl = aty_ld_8(CRTC_GEN_CNTL);
 	if (blank_mode & VESA_VSYNC_SUSPEND)
 		gen_cntl |= 0x8;
 	if (blank_mode & VESA_HSYNC_SUSPEND)
@@ -564,5 +800,33 @@
 		gen_cntl |= 0x40;
 	if (blank_mode == VESA_NO_BLANKING)
 		gen_cntl &= ~(0x4c);
-	aty_st_byte(CRTC_GEN_CNTL, gen_cntl);
+	aty_st_8(CRTC_GEN_CNTL, gen_cntl);
 }
+
+/* handle video scrollback; offset is in # of characters */
+void
+ati_set_origin(unsigned short offset)
+{
+	register int x = (vmode_attrs[video_mode - 1].hres + 7) & 0xfff8,
+		lines = offset / video_num_columns, reg;
+
+	reg = ((x >> 3) << 22) |	/* calculate pitch */
+	  ((lines * video_font_height * x * (1<<color_mode)) >> 3); /*offset*/
+
+	aty_st_le32(CRTC_OFF_PITCH, reg);
+	aty_st_le32(DST_OFF_PITCH, reg);
+	aty_st_le32(SRC_OFF_PITCH, reg);
+
+#if 0
+	fb_start = display_info.fb_address = 
+		(unsigned long) frame_buffer + ((lines-2) * 
+		vmode_attrs[video_mode-1].hres) << (4 + color_mode);
+#endif
+}
+
+int
+ati_vram(void)
+{
+	return total_vram;
+}
+
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/aty.h linux/drivers/macintosh/aty.h
--- v2.1.78/linux/drivers/macintosh/aty.h	Mon Aug 18 18:19:45 1997
+++ linux/drivers/macintosh/aty.h	Mon Jan 12 15:18:13 1998
@@ -14,9 +14,12 @@
 extern void aty_init(void);
 extern int aty_setmode(struct vc_mode *mode, int doit);
 extern void aty_set_palette(unsigned char red[], unsigned char green[],
-			    unsigned char blue[], int index, int ncolors);
+	unsigned char blue[], int index, int ncolors);
 extern void aty_set_blanking(int blank_mode);
 
+extern void ati_set_origin(unsigned short offset);
+extern int ati_vram(void);
+
 /*
  * most of the rest of this file comes from ATI sample code
  */
@@ -49,6 +52,9 @@
 #define CRTC_FIFO               0x001e
 #define CRTC_EXT_DISP           0x001f
 
+#define DSP_CONFIG              0x0020  /* Dword offset 08 */
+#define DSP_ON_OFF              0x0024  /* Dword offset 09 */
+
 #define OVR_CLR                 0x0040  /* Dword offset 10 */
 #define OVR_WID_LEFT_RIGHT      0x0044  /* Dword offset 11 */
 #define OVR_WID_TOP_BOTTOM      0x0048  /* Dword offset 12 */
@@ -59,6 +65,8 @@
 #define CUR_HORZ_VERT_POSN      0x006C  /* Dword offset 1B */
 #define CUR_HORZ_VERT_OFF       0x0070  /* Dword offset 1C */
 
+#define MON_SENSE		0x0078
+
 #define SCRATCH_REG0            0x0080  /* Dword offset 20 */
 #define SCRATCH_REG1            0x0084  /* Dword offset 21 */
 
@@ -67,6 +75,7 @@
 
 #define BUS_CNTL                0x00A0  /* Dword offset 28 */
 
+#define EXT_MEM_CNTL			0x00AC	/* Dword offset 2B */
 #define MEM_CNTL                0x00B0  /* Dword offset 2C */
 
 #define MEM_VGA_WP_SEL          0x00B4  /* Dword offset 2D */
@@ -181,9 +190,9 @@
 #define CRTC_PIX_BY_2_EN	0x00000020
 #define CRTC_BLANK		0x00000040
 
-#define CRTC_PIX_WIDTH_MASK		0x00000700
-#define CRTC_PIX_WIDTH_4BPP		0x00000100
-#define CRTC_PIX_WIDTH_8BPP		0x00000200
+#define CRTC_PIX_WIDTH_MASK	0x00000700
+#define CRTC_PIX_WIDTH_4BPP	0x00000100
+#define CRTC_PIX_WIDTH_8BPP	0x00000200
 #define CRTC_PIX_WIDTH_15BPP	0x00000300
 #define CRTC_PIX_WIDTH_16BPP	0x00000400
 #define CRTC_PIX_WIDTH_24BPP	0x00000500
@@ -252,6 +261,15 @@
 #define GUI_ENGINE_ENABLE       0x100
 #define BLOCK_WRITE_ENABLE      0x200
 
+/* DSP_CONFIG register constants */
+#define DSP_XCLKS_PER_QW        0x00003fff
+#define DSP_LOOP_LATENCY        0x000f0000
+#define DSP_PRECISION           0x00700000
+
+/* DSP_ON_OFF register constants */
+#define DSP_OFF                 0x000007ff
+#define DSP_ON                  0x07ff0000
+
 /* CLOCK_CNTL register constants */
 #define CLOCK_SEL		0x0f
 #define CLOCK_DIV		0x30
@@ -592,23 +610,23 @@
 #define MACH64_NUM_FREQS	50
 
 /* Wait until "v" queue entries are free */
-#define aty_WaitQueue(v)    { while ((aty_ld_rev(FIFO_STAT) & 0xffff) > \
+#define aty_WaitQueue(v)    { while ((aty_ld_le32(FIFO_STAT) & 0xffff) > \
 			 ((unsigned short)(0x8000 >> (v)))); }
 
 /* Wait until GP is idle and queue is empty */
 #define aty_WaitIdleEmpty() { aty_WaitQueue(16); \
-			  while ((aty_ld_rev(GUI_STAT) & 1) != 0); }
+			  while ((aty_ld_le32(GUI_STAT) & 1) != 0); }
 
 #define SKIP_2(_v) ((((_v)<<1)&0xfff8)|((_v)&0x3)|(((_v)&0x80)>>5))
 
 #define MACH64_BIT_BLT(_srcx, _srcy, _dstx, _dsty, _w, _h, _dir) \
 { \
     aty_WaitQueue(5); \
-    aty_st_rev(SRC_Y_X, (((_srcx) << 16) | ((_srcy) & 0x0000ffff))); \
-    aty_st_rev(SRC_WIDTH1, (_w)); \
-    aty_st_rev(DST_CNTL, (_dir)); \
-    aty_st_rev(DST_Y_X, (((_dstx) << 16) | ((_dsty) & 0x0000ffff))); \
-    aty_st_rev(DST_HEIGHT_WIDTH, (((_w) << 16) | ((_h) & 0x0000ffff))); \
+    aty_st_le32(SRC_Y_X, (((_srcx) << 16) | ((_srcy) & 0x0000ffff))); \
+    aty_st_le32(SRC_WIDTH1, (_w)); \
+    aty_st_le32(DST_CNTL, (_dir)); \
+    aty_st_le32(DST_Y_X, (((_dstx) << 16) | ((_dsty) & 0x0000ffff))); \
+    aty_st_le32(DST_HEIGHT_WIDTH, (((_w) << 16) | ((_h) & 0x0000ffff))); \
 }
 #endif /* REGMACH64_H */
 
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/chips.c linux/drivers/macintosh/chips.c
--- v2.1.78/linux/drivers/macintosh/chips.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/macintosh/chips.c	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,167 @@
+/*
+ * chips.c: Console support for PowerBook 3400/2400 chips65550 display adaptor.
+ *
+ * Copyright (C) 1997 Fabio Riccardi.
+ *
+ * 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/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/vc_ioctl.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/selection.h>
+#include <asm/prom.h>
+#include <asm/io.h>
+#include <asm/pci-bridge.h>
+#include "pmac-cons.h"
+#include "chips.h"
+
+
+static unsigned char *frame_buffer;
+static unsigned char *blitter_regs;
+static unsigned char *io_space;
+
+void
+map_chips_display(struct device_node *dp)
+{
+    unsigned char bus, devfn;
+    unsigned short cmd;
+    unsigned long addr;
+
+    addr = dp->addrs[0].address;
+    frame_buffer = ioremap(addr + 0x800000, 0x100000);
+    blitter_regs = ioremap(addr + 0xC00000, 4096);
+    
+    printk("Mapped chips65550 frame buffer at %p, blitter at %p\n", frame_buffer, blitter_regs);
+    
+    if (pci_device_loc(dp, &bus, &devfn) == 0) {
+	pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
+	cmd |= 3;	// enable memory and IO space
+	pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
+	io_space = ioremap((unsigned long) pci_io_base(bus), 4096);
+	printk("Mapped chips65550 IO space at %p\n", io_space);
+    }
+
+    video_mode = VMODE_800_600_60;
+    color_mode = CMODE_8;
+}
+
+#define write_xr(num,val)	{ out_8(io_space + 0x3D6, num); out_8(io_space + 0x3D7, val); }
+#define read_xr(num,var)	{ out_8(io_space + 0x3D6, num); var = in_8(io_space + 0x3D7); }
+#define write_fr(num,val)	{ out_8(io_space + 0x3D0, num); out_8(io_space + 0x3D1, val); }
+#define read_fr(num,var)	{ out_8(io_space + 0x3D0, num); var = in_8(io_space + 0x3D1); }
+#define write_cr(num,val)	{ out_8(io_space + 0x3D4, num); out_8(io_space + 0x3D5, val); }
+#define read_cr(num,var)	{ out_8(io_space + 0x3D4, num); var = in_8(io_space + 0x3D5); }
+
+void
+chips_init()
+{
+    unsigned *p;
+    int i, hres;
+
+    if (video_mode != VMODE_800_600_60)
+	panic("chips65550: display mode %d not supported", video_mode);
+
+    if (color_mode != CMODE_8 && color_mode != CMODE_16)
+	panic("chips65550: color mode %d not supported", color_mode);
+    
+    n_scanlines = 600;
+    hres        = 800;
+    pixel_size  = 1 << color_mode;
+    line_pitch  = hres * pixel_size;
+    row_pitch = line_pitch * 16;
+
+    if (color_mode == CMODE_16) {
+	write_cr(0x13, 200);		// 16 bit display width (decimal)
+	write_xr(0x81, 0x14);		// 15 bit (TrueColor) color mode
+	write_xr(0x82, 0x00);		// disable palettes
+	write_xr(0x20, 0x10);		// 16 bit blitter mode
+	// write_xr(0x80, 0x00);	// 6 bit DAC
+	// write_fr(0x11, 0X50);	// No dither, 5 bits/color
+    } else if (color_mode == CMODE_8) {
+	write_cr(0x13, 100);		// 8 bit display width (decimal)
+	write_xr(0x81, 0x12);		// 8 bit color mode
+	write_xr(0x82, 0x08);		// Graphics gamma enable
+	write_xr(0x20, 0x00);		// 8 bit blitter mode
+	// write_xr(0x80, 0x82);	// 8 bit DAC, CRT overscan
+	// write_fr(0x11, 0XE0);	// Res Dither on, 6 bits/pixel
+    }
+
+    pmac_init_palette();	/* Initialize colormap */
+
+    fb_start = frame_buffer;
+
+    printk(KERN_INFO "hres = %d height = %d pitch = %d\n",
+	   hres, n_scanlines, line_pitch);
+
+    display_info.height = n_scanlines;
+    display_info.width = hres;
+    display_info.depth = pixel_size * 8;
+    display_info.pitch = line_pitch;
+    display_info.mode = video_mode;
+    strncpy(display_info.name, "chips65550", sizeof(display_info.name));
+    display_info.fb_address = (unsigned long) frame_buffer;
+    display_info.cmap_adr_address = 0;
+    display_info.cmap_data_address = 0;
+    display_info.disp_reg_address = 0;
+
+    /* Clear screen */
+    p = (unsigned *) frame_buffer;
+    for (i = n_scanlines * line_pitch / sizeof(unsigned); i != 0; --i)
+	*p++ = 0;
+}
+
+int
+chips_setmode(struct vc_mode *mode, int doit)
+{
+    int cmode;
+
+    switch (mode->depth) {
+    case 16:
+	cmode = CMODE_16;
+	break;
+    case 8:
+    case 0:		/* (default) */
+	cmode = CMODE_8;
+	break;
+    default:
+	return -EINVAL;
+    }
+
+    if (mode->mode != VMODE_800_600_60)
+	return -EINVAL;
+    
+    if (doit) {
+	video_mode = mode->mode;
+	color_mode = cmode;
+	chips_init();
+    }
+
+    return 0;
+}
+
+void
+chips_set_palette(unsigned char red[], unsigned char green[],
+		  unsigned char blue[], int index, int ncolors)
+{
+    int i;
+
+    for (i = 0; i < ncolors; ++i) {
+	out_8(&io_space[0x3C8], index + i);
+	udelay(1);
+	out_8(&io_space[0x3C9], red[i]);
+	out_8(&io_space[0x3C9], green[i]);
+	out_8(&io_space[0x3C9], blue[i]);
+    }
+}
+
+void
+chips_set_blanking(int blank_mode)
+{
+}
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/chips.h linux/drivers/macintosh/chips.h
--- v2.1.78/linux/drivers/macintosh/chips.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/macintosh/chips.h	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,17 @@
+/*
+ * Exported procedures for the chips65550 display driver on PowerBook 3400/2400
+ *
+ * Copyright (C) 1997 Fabio Riccardi.
+ *
+ * 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.
+ */
+
+extern void map_chips_display(struct device_node *);
+extern void chips_init(void);
+extern int chips_setmode(struct vc_mode *mode, int doit);
+extern void chips_set_palette(unsigned char red[], unsigned char green[],
+				 unsigned char blue[], int index, int ncolors);
+extern void chips_set_blanking(int blank_mode);
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/control.c linux/drivers/macintosh/control.c
--- v2.1.78/linux/drivers/macintosh/control.c	Mon Aug 18 18:19:45 1997
+++ linux/drivers/macintosh/control.c	Mon Jan 12 15:18:13 1998
@@ -16,6 +16,7 @@
 #include <linux/nvram.h>
 #include <asm/prom.h>
 #include <asm/io.h>
+#include <asm/adb.h>
 #include <asm/cuda.h>
 #include <linux/selection.h>
 #include "pmac-cons.h"
@@ -377,13 +378,13 @@
 static void
 set_control_clock(unsigned char *params)
 {
-	struct cuda_request req;
+	struct adb_request req;
 	int i;
 
 	for (i = 0; i < 3; ++i) {
 		cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
 			     0x50, i + 1, params[i]);
-		while (!req.got_reply)
+		while (!req.complete)
 			cuda_poll();
 	}
 }
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/mac_keyb.c linux/drivers/macintosh/mac_keyb.c
--- v2.1.78/linux/drivers/macintosh/mac_keyb.c	Mon Aug 18 18:19:45 1997
+++ linux/drivers/macintosh/mac_keyb.c	Mon Jan 12 15:18:13 1998
@@ -18,6 +18,7 @@
 
 #include <asm/keyboard.h>
 #include <asm/bitops.h>
+#include <asm/adb.h>
 #include <asm/cuda.h>
 
 #include <linux/kbd_kern.h>
@@ -27,15 +28,19 @@
 #define KEYB_LEDREG	2	/* register # for leds on ADB keyboard */
 #define MOUSE_DATAREG	0	/* reg# for movement/button codes from mouse */
 
-unsigned char kbd_read_mask = 0;	/* XXX */
-
 static void kbd_repeat(unsigned long);
 static struct timer_list repeat_timer = { NULL, NULL, 0, 0, kbd_repeat };
 static int last_keycode;
 
-static void keyboard_input(unsigned char *, int, struct pt_regs *);
+static void keyboard_input(unsigned char *, int, struct pt_regs *, int);
 static void input_keycode(int, int);
-static void leds_done(struct cuda_request *);
+static void leds_done(struct adb_request *);
+
+/* XXX: Hook for mouse driver */
+void (*adb_mouse_interrupt_hook) (char *, int);
+int adb_emulate_button2;
+int adb_emulate_button3;
+extern int console_loglevel;
 
 extern struct kbd_struct kbd_table[];
 
@@ -75,14 +80,11 @@
 	if (!raw_mode) {
 		/*
 		 * Convert R-shift/control/option to L version.
-		 * Remap keycode 0 (A) to the unused keycode 0x5a.
-		 * Other parts of the system assume 0 is not a valid keycode.
 		 */
 		switch (keycode) {
 		case 0x7b: keycode = 0x38; break; /* R-shift */
 		case 0x7c: keycode = 0x3a; break; /* R-option */
 		case 0x7d: keycode = 0x36; break; /* R-control */
-		case 0:	   keycode = 0x5a; break; /* A */
 		}
 	}
 	*keycodep = keycode;
@@ -95,15 +97,15 @@
 }
 
 static void
-keyboard_input(unsigned char *data, int nb, struct pt_regs *regs)
+keyboard_input(unsigned char *data, int nb, struct pt_regs *regs, int apoll)
 {
 	/* first check this is from register 0 */
-	if (nb != 5 || (data[2] & 3) != KEYB_KEYREG)
+	if (nb != 3 || (data[0] & 3) != KEYB_KEYREG)
 		return;		/* ignore it */
 	kbd_pt_regs = regs;
-	input_keycode(data[3], 0);
-	if (!(data[4] == 0xff || (data[4] == 0x7f && data[3] == 0x7f)))
-		input_keycode(data[4], 0);
+	input_keycode(data[1], 0);
+	if (!(data[2] == 0xff || (data[2] == 0x7f && data[1] == 0x7f)))
+		input_keycode(data[2], 0);
 }
 
 static void
@@ -114,10 +116,66 @@
 
  	kbd = kbd_table + fg_console;
 	up_flag = (keycode & 0x80);
-        keycode &= 0x7f;
+	keycode &= 0x7f;
+
 	if (!repeat)
 		del_timer(&repeat_timer);
 
+	/*
+	 * XXX: Add mouse button 2+3 fake codes here if mouse open.
+	 *	Keep track of 'button' states here as we only send 
+	 *	single up/down events!
+	 *	Really messy; might need to check if keyboard is in
+	 *	VC_RAW mode.
+	 *	Might also want to know how many buttons need to be emulated.
+	 *	-> hide this as function in arch/m68k/mac ?
+	 */
+	if (adb_mouse_interrupt_hook || console_loglevel == 10) {
+		unsigned char button, button2, button3, fake_event;
+		static unsigned char button2state=0, button3state=0; /* up */
+		/* faked ADB packet */
+		static char data[4] = { 0, 0x80, 0x80, 0x80 };
+
+		button = 0;
+		fake_event = 0;
+		switch (keycode) {	/* which 'button' ? */
+			case 0x7c:	/* R-option */
+				button2 = (!up_flag);		/* new state */
+				if (button2 != button2state)	/* change ? */
+					button = 2; 
+				button2state = button2;		/* save state */
+				fake_event = 2;
+				break; 
+			case 0x7d:	/* R-control */
+				button3 = (!up_flag);		/* new state */
+				if (button3 != button3state)	/* change ? */ 
+					button = 3; 
+				button3state = button3; 	/* save state */
+				fake_event = 3;
+				break; 
+		}
+		if (fake_event && console_loglevel >= 8)
+			printk("fake event: button2 %d button3 %d button %d\n",
+				 button2state, button3state, button);
+		if (button) {		/* there's been a button state change */
+			/* fake a mouse packet : send all bytes, change one! */
+			data[button] = (up_flag ? 0x80 : 0);
+			if (adb_mouse_interrupt_hook)
+				adb_mouse_interrupt_hook(data, -1);
+			else
+				printk("mouse_fake: data %x %x %x buttons %x \n", 
+					data[1], data[2], data[3],
+					~( (data[1] & 0x80 ? 0 : 4) 
+					 | (data[2] & 0x80 ? 0 : 1) 
+					 | (data[3] & 0x80 ? 0 : 2) )&7 );
+		}
+		/*
+		 * XXX: testing mouse emulation ... don't process fake keys!
+		 */
+		if (fake_event)
+			return;
+	}
+
 	if (kbd->kbdmode != VC_RAW) {
 		if (!up_flag && !dont_repeat[keycode]) {
 			last_keycode = keycode;
@@ -126,11 +184,23 @@
 		}
 
 		/*
-		 * XXX fix caps-lock behaviour by turning the key-up
-		 * transition into a key-down transition.
+		 * adb kludge!! Imitate pc caps lock behaviour by
+		 * generating an up/down event for each time caps
+		 * is pressed/released. Also, makes sure that the
+		 * LED are handled.  atong@uiuc.edu
 		 */
-		if (keycode == 0x39 && up_flag && vc_kbd_led(kbd, VC_CAPSLOCK))
-			up_flag = 0;
+		 switch (keycode) {
+		 /*case 0xb9:*/
+		 case 0x39:
+			handle_scancode(0x39);
+			handle_scancode(0xb9);
+		 	mark_bh(KEYBOARD_BH);
+		 	return;
+		 case 0x47:
+		 /*case 0xc7:*/
+		 	mark_bh(KEYBOARD_BH);
+		 	break;
+		 }
 	}
 
 	handle_scancode(keycode + up_flag);
@@ -148,7 +218,7 @@
 }
 
 static void
-mouse_input(unsigned char *data, int nb, struct pt_regs *regs)
+mouse_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll)
 {
   /* [ACA:23-Mar-97] Three button mouse support.  This is designed to
      function with MkLinux DR-2.1 style X servers.  It only works with
@@ -230,6 +300,18 @@
   */
 	struct kbd_struct *kbd;
 
+	if (adb_mouse_interrupt_hook)
+		adb_mouse_interrupt_hook(data, nb);
+	else
+		if (console_loglevel == 10)
+		    printk("mouse_input: data %x %x %x buttons %x dx %d dy %d \n", 
+			data[1], data[2], data[3],
+			~((data[1] & 0x80 ? 0 : 4) 
+			| (data[2] & 0x80 ? 0 : 1)
+			| (data[3] & 0x80 ? 0 : 2))&7,
+			((data[2]&0x7f) < 64 ? (data[2]&0x7f) : (data[2]&0x7f)-128 ),
+			((data[1]&0x7f) < 64 ? -(data[1]&0x7f) : 128-(data[1]&0x7f) ) );
+
 	kbd = kbd_table + fg_console;
 
 	/* Only send mouse codes when keyboard is in raw mode. */
@@ -239,13 +321,13 @@
 
 		/* Send first button, second button and movement. */
 		put_queue( 0x7e );
-		put_queue( data[3] );
-		put_queue( data[4] );
+		put_queue( data[1] );
+		put_queue( data[2] );
 
 		/* [ACA: Are there any two-button ADB mice that use handler 1 or 2?] */
 
 		/* Store the button state. */
-		uchButtonSecond = (data[4] & 0x80);
+		uchButtonSecond = (data[2] & 0x80);
 
 		/* Send second button. */
 		if (uchButtonSecond != uch_ButtonStateSecond) {
@@ -254,12 +336,12 @@
 		}
 
 		/* Macintosh 3-button mouse (handler 4). */
-		if ((nb == 6) && (data[1] & 0x40)) {
+		if ((nb == 6) && autopoll /*?*/) {
 			static unsigned char uch_ButtonStateThird = 0;
 			unsigned char uchButtonThird;
 
 			/* Store the button state for speed. */
-			uchButtonThird = (data[5] & 0x80);
+			uchButtonThird = (data[3] & 0x80);
 
 			/* Send third button. */
 			if (uchButtonThird != uch_ButtonStateThird) {
@@ -282,20 +364,20 @@
     7,		/* caps + num + scroll lock */
 };
 
-static struct cuda_request led_request;
+static struct adb_request led_request;
 static int leds_pending;
 
 void mackbd_leds(unsigned char leds)
 {
-	if (led_request.got_reply) {
-		cuda_request(&led_request, leds_done, 4, ADB_PACKET,
+	if (led_request.complete) {
+		adb_request(&led_request, leds_done, 0, 3,
 			     ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG),
 			     0xff, ~mac_ledmap[leds]);
 	} else
 		leds_pending = leds | 0x100;
 }
 
-static void leds_done(struct cuda_request *req)
+static void leds_done(struct adb_request *req)
 {
 	int leds;
 
@@ -308,36 +390,32 @@
 
 void mackbd_init_hw(void)
 {
-	struct cuda_request req;
+	struct adb_request req;
+
+	/* initialize mouse interrupt hook */
+	adb_mouse_interrupt_hook = NULL;
+	/* assume broken mouse :-) - should be adjusted based on 
+	 * result of the mouse setup !! (or passed as  kernel option) */
+	adb_emulate_button2 = 1;
+	adb_emulate_button3 = 1;
 
 	adb_register(ADB_KEYBOARD, keyboard_input);
 	adb_register(ADB_MOUSE, mouse_input);
 
-	/* turn on ADB auto-polling in the CUDA */
-	cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1);
-	while (!req.got_reply)
-	    cuda_poll();
-
 	/* turn off all leds */
-	cuda_request(&req, NULL, 4, ADB_PACKET,
-		     ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG), 0xff, 0xff);
-	while (!req.got_reply)
-	    cuda_poll();
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+		    ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG), 0xff, 0xff);
 
 	/* get the keyboard to send separate codes for
 	   left and right shift, control, option keys. */
-	cuda_request(&req, NULL, 4, ADB_PACKET,
-		     ADB_WRITEREG(ADB_KEYBOARD, 3), 0, 3);
-	while (!req.got_reply)
-	    cuda_poll();
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+		    ADB_WRITEREG(ADB_KEYBOARD, 3), 0, 3);
 
-	led_request.got_reply = 1;
+	led_request.complete = 1;
 
 	/* Try to switch the mouse (id 3) to handler 4, for three-button
 	   mode. (0x20 is Service Request Enable, 0x03 is Device ID). */
-	cuda_request(&req, NULL, 4, ADB_PACKET,
-		     ADB_WRITEREG(ADB_MOUSE, 3), 0x23, 4 );
-	while (!req.got_reply)
-		cuda_poll();
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+		    ADB_WRITEREG(ADB_MOUSE, 3), 0x23, 4 );
 }
 
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/macio-adb.c linux/drivers/macintosh/macio-adb.c
--- v2.1.78/linux/drivers/macintosh/macio-adb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/macintosh/macio-adb.c	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,211 @@
+/*
+ * Driver for the ADB controller in the Mac I/O (Hydra) chip.
+ */
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/prom.h>
+#include <asm/adb.h>
+#include <asm/io.h>
+#include <asm/hydra.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+struct preg {
+	unsigned char r;
+	char pad[15];
+};
+
+struct adb_regs {
+	struct preg intr;
+	struct preg data[9];
+	struct preg intr_enb;
+	struct preg dcount;
+	struct preg error;
+	struct preg ctrl;
+	struct preg autopoll;
+	struct preg active_hi;
+	struct preg active_lo;
+	struct preg test;
+};
+
+/* Bits in intr and intr_enb registers */
+#define DFB	1		/* data from bus */
+#define TAG	2		/* transfer access grant */
+
+/* Bits in dcount register */
+#define HMB	0x0f		/* how many bytes */
+#define APD	0x10		/* auto-poll data */
+
+/* Bits in error register */
+#define NRE	1		/* no response error */
+#define DLE	2		/* data lost error */
+
+/* Bits in ctrl register */
+#define TAR	1		/* transfer access request */
+#define DTB	2		/* data to bus */
+#define CRE	4		/* command response expected */
+#define ADB_RST	8		/* ADB reset */
+
+/* Bits in autopoll register */
+#define APE	1		/* autopoll enable */
+
+static volatile struct adb_regs *adb;
+static struct adb_request *current_req, *last_req;
+static unsigned char adb_rbuf[16];
+
+static void macio_adb_interrupt(int irq, void *arg, struct pt_regs *regs);
+static int macio_adb_send_request(struct adb_request *req, int sync);
+static int macio_adb_autopoll(int on);
+static void macio_adb_poll(void);
+static void completed(void);
+
+void macio_adb_init(void)
+{
+	struct device_node *adbs;
+
+	adbs = find_compatible_devices("adb", "chrp,adb0");
+	if (adbs == 0)
+		return;
+
+#if 1
+	{ int i;
+
+	printk("macio_adb_init: node = %p, addrs =", adbs->node);
+	for (i = 0; i < adbs->n_addrs; ++i)
+		printk(" %x(%x)", adbs->addrs[i].address, adbs->addrs[i].size);
+	printk(", intrs =");
+	for (i = 0; i < adbs->n_intrs; ++i)
+		printk(" %x", adbs->intrs[i]);
+	printk("\n"); }
+#endif
+	
+	adb = (volatile struct adb_regs *) adbs->addrs->address;
+
+	if (request_irq(openpic_to_irq(adbs->intrs[0]), macio_adb_interrupt,
+			0, "ADB", (void *)0)) {
+		printk(KERN_ERR "ADB: can't get irq %d\n",
+		       openpic_to_irq(adbs->intrs[0]));
+		return;
+	}
+
+	out_8(&adb->ctrl.r, 0);
+	out_8(&adb->intr.r, 0);
+	out_8(&adb->error.r, 0);
+	out_8(&adb->active_hi.r, 0xff); /* for now, set all devices active */
+	out_8(&adb->active_lo.r, 0xff);
+	out_8(&adb->autopoll.r, APE);
+	out_8(&adb->intr_enb.r, DFB | TAG);
+
+	adb_hardware = ADB_MACIO;
+	adb_send_request = macio_adb_send_request;
+	adb_autopoll = macio_adb_autopoll;
+}
+
+static int macio_adb_autopoll(int on)
+{
+	out_8(&adb->autopoll.r, on? APE: 0);
+	return 0;
+}
+
+
+/* Send an ADB command */
+static int macio_adb_send_request(struct adb_request *req, int sync)
+{
+	unsigned long mflags;
+
+	req->next = 0;
+	req->sent = 0;
+	req->complete = 0;
+	req->reply_len = 0;
+
+	save_flags(mflags);
+	cli();
+	if (current_req != 0) {
+		last_req->next = req;
+		last_req = req;
+	} else {
+		current_req = last_req = req;
+		out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR);
+	}
+	restore_flags(mflags);
+	
+	if (sync) {
+		while (!req->complete)
+			macio_adb_poll();
+	}
+
+	return 0;
+}
+
+static void macio_adb_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+	int i, n, err;
+	struct adb_request *req;
+
+	if (in_8(&adb->intr.r) & TAG) {
+		if ((req = current_req) != 0) {
+			/* put the current request in */
+			for (i = 0; i < req->nbytes; ++i)
+				out_8(&adb->data[i].r, req->data[i]);
+			out_8(&adb->dcount.r, req->nbytes & HMB);
+			req->sent = 1;
+			if (req->reply_expected) {
+				out_8(&adb->ctrl.r, DTB + CRE);
+			} else {
+				out_8(&adb->ctrl.r, DTB);
+				completed();
+			}
+		}
+		out_8(&adb->intr.r, 0);
+	}
+
+	if (in_8(&adb->intr.r) & DFB) {
+		err = in_8(&adb->error.r);
+		if (current_req && current_req->sent) {
+			/* this is the response to a command */
+			req = current_req;
+			if (err == 0) {
+				req->reply_len = in_8(&adb->dcount.r) & HMB;
+				for (i = 0; i < req->reply_len; ++i)
+					req->reply[i] = in_8(&adb->data[i].r);
+			}
+			completed();
+		} else if (err == 0) {
+			/* autopoll data */
+			n = in_8(&adb->dcount.r) & HMB;
+			for (i = 0; i < n; ++i)
+				adb_rbuf[i] = in_8(&adb->data[i].r);
+			adb_input(adb_rbuf, n, regs,
+				  in_8(&adb->dcount.r) & APD);
+		}
+		out_8(&adb->error.r, 0);
+		out_8(&adb->intr.r, 0);
+	}
+}
+
+static void completed(void)
+{
+	struct adb_request *req = current_req;
+
+	req->complete = 1;
+	current_req = req->next;
+	if (current_req)
+		out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR);
+	if (req->done)
+		(*req->done)(req);
+}
+
+static void macio_adb_poll(void)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (in_8(&adb->intr.r) != 0)
+		macio_adb_interrupt(0, 0, 0);
+	restore_flags(flags);
+}
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/mackeymap.c linux/drivers/macintosh/mackeymap.c
--- v2.1.78/linux/drivers/macintosh/mackeymap.c	Mon Aug 18 18:19:45 1997
+++ linux/drivers/macintosh/mackeymap.c	Mon Jan 12 15:18:13 1998
@@ -6,7 +6,7 @@
 #include <linux/kd.h>
 
 u_short plain_map[NR_KEYS] = {
-	0xf200,	0xfb73,	0xfb64,	0xfb66,	0xfb68,	0xfb67,	0xfb7a,	0xfb78,
+	0xfb61,	0xfb73,	0xfb64,	0xfb66,	0xfb68,	0xfb67,	0xfb7a,	0xfb78,
 	0xfb63,	0xfb76,	0xf200,	0xfb62,	0xfb71,	0xfb77,	0xfb65,	0xfb72,
 	0xfb79,	0xfb74,	0xf031,	0xf032,	0xf033,	0xf034,	0xf036,	0xf035,
 	0xf03d,	0xf039,	0xf037,	0xf02d,	0xf038,	0xf030,	0xf05d,	0xfb6f,
@@ -17,15 +17,15 @@
 	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
 	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
 	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
-	0xf306,	0xf307,	0xfb61,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
+	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
 	0xf104,	0xf105,	0xf106,	0xf102,	0xf107,	0xf108,	0xf200,	0xf10a,
 	0xf200,	0xf10c,	0xf200,	0xf209,	0xf200,	0xf109,	0xf200,	0xf10b,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf103,	0xf117,
-	0xf101,	0xf119,	0xf100,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf101,	0xf119,	0xf100,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
 };
 
-u_short shift_map[NR_KEYS] = {
-	0xf200,	0xfb53,	0xfb44,	0xfb46,	0xfb48,	0xfb47,	0xfb5a,	0xfb58,
+static u_short shift_map[NR_KEYS] = {
+	0xfb41,	0xfb53,	0xfb44,	0xfb46,	0xfb48,	0xfb47,	0xfb5a,	0xfb58,
 	0xfb43,	0xfb56,	0xf200,	0xfb42,	0xfb51,	0xfb57,	0xfb45,	0xfb52,
 	0xfb59,	0xfb54,	0xf021,	0xf040,	0xf023,	0xf024,	0xf05e,	0xf025,
 	0xf02b,	0xf028,	0xf026,	0xf05f,	0xf02a,	0xf029,	0xf07d,	0xfb4f,
@@ -36,15 +36,15 @@
 	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
 	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
 	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
-	0xf306,	0xf307,	0xfb41,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
+	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
 	0xf10e,	0xf10f,	0xf110,	0xf10c,	0xf111,	0xf112,	0xf200,	0xf10a,
 	0xf200,	0xf10c,	0xf200,	0xf203,	0xf200,	0xf113,	0xf200,	0xf10b,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf20b,	0xf116,	0xf10d,	0xf117,
-	0xf10b,	0xf20a,	0xf10a,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf10b,	0xf20a,	0xf10a,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
 };
 
-u_short altgr_map[NR_KEYS] = {
-	0xf200,	0xfb73,	0xf917,	0xf919,	0xfb68,	0xfb67,	0xfb7a,	0xfb78,
+static u_short altgr_map[NR_KEYS] = {
+	0xf914,	0xfb73,	0xf917,	0xf919,	0xfb68,	0xfb67,	0xfb7a,	0xfb78,
 	0xf916,	0xfb76,	0xf200,	0xf915,	0xfb71,	0xfb77,	0xf918,	0xfb72,
 	0xfb79,	0xfb74,	0xf200,	0xf040,	0xf200,	0xf024,	0xf200,	0xf200,
 	0xf200,	0xf05d,	0xf07b,	0xf05c,	0xf05b,	0xf07d,	0xf07e,	0xfb6f,
@@ -55,15 +55,15 @@
 	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
 	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
 	0xf200,	0xf200,	0xf90a,	0xf90b,	0xf90c,	0xf90d,	0xf90e,	0xf90f,
-	0xf910,	0xf911,	0xf914,	0xf912,	0xf913,	0xf200,	0xf200,	0xf200,
+	0xf910,	0xf911,	0xf200,	0xf912,	0xf913,	0xf200,	0xf200,	0xf200,
 	0xf510,	0xf511,	0xf512,	0xf50e,	0xf513,	0xf514,	0xf200,	0xf516,
 	0xf200,	0xf10c,	0xf200,	0xf202,	0xf200,	0xf515,	0xf200,	0xf517,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf50f,	0xf117,
-	0xf50d,	0xf119,	0xf50c,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf50d,	0xf119,	0xf50c,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
 };
 
-u_short ctrl_map[NR_KEYS] = {
-	0xf200,	0xf013,	0xf004,	0xf006,	0xf008,	0xf007,	0xf01a,	0xf018,
+static u_short ctrl_map[NR_KEYS] = {
+	0xf001,	0xf013,	0xf004,	0xf006,	0xf008,	0xf007,	0xf01a,	0xf018,
 	0xf003,	0xf016,	0xf200,	0xf002,	0xf011,	0xf017,	0xf005,	0xf012,
 	0xf019,	0xf014,	0xf200,	0xf000,	0xf01b,	0xf01c,	0xf01e,	0xf01d,
 	0xf200,	0xf200,	0xf01f,	0xf01f,	0xf07f,	0xf200,	0xf01d,	0xf00f,
@@ -74,15 +74,15 @@
 	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
 	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
 	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
-	0xf306,	0xf307,	0xf001,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
+	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
 	0xf104,	0xf105,	0xf106,	0xf102,	0xf107,	0xf108,	0xf200,	0xf10a,
 	0xf200,	0xf10c,	0xf200,	0xf204,	0xf200,	0xf109,	0xf200,	0xf10b,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf103,	0xf117,
-	0xf101,	0xf119,	0xf100,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf101,	0xf119,	0xf100,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
 };
 
-u_short shift_ctrl_map[NR_KEYS] = {
-	0xf200,	0xf013,	0xf004,	0xf006,	0xf008,	0xf007,	0xf01a,	0xf018,
+static u_short shift_ctrl_map[NR_KEYS] = {
+	0xf001,	0xf013,	0xf004,	0xf006,	0xf008,	0xf007,	0xf01a,	0xf018,
 	0xf003,	0xf016,	0xf200,	0xf002,	0xf011,	0xf017,	0xf005,	0xf012,
 	0xf019,	0xf014,	0xf200,	0xf000,	0xf200,	0xf200,	0xf200,	0xf200,
 	0xf200,	0xf200,	0xf200,	0xf01f,	0xf200,	0xf200,	0xf200,	0xf00f,
@@ -93,15 +93,15 @@
 	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
 	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
 	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
-	0xf306,	0xf307,	0xf001,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
+	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
 	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
 	0xf200,	0xf10c,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf200,	0xf117,
-	0xf200,	0xf119,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf20c,
+	0xf200,	0xf119,	0xf200,	0xf700,	0xf701,	0xf702,	0xf200,	0xf20c,
 };
 
-u_short alt_map[NR_KEYS] = {
-	0xf200,	0xf873,	0xf864,	0xf866,	0xf868,	0xf867,	0xf87a,	0xf878,
+static u_short alt_map[NR_KEYS] = {
+	0xf861,	0xf873,	0xf864,	0xf866,	0xf868,	0xf867,	0xf87a,	0xf878,
 	0xf863,	0xf876,	0xf200,	0xf862,	0xf871,	0xf877,	0xf865,	0xf872,
 	0xf879,	0xf874,	0xf831,	0xf832,	0xf833,	0xf834,	0xf836,	0xf835,
 	0xf83d,	0xf839,	0xf837,	0xf82d,	0xf838,	0xf830,	0xf85d,	0xf86f,
@@ -112,15 +112,15 @@
 	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
 	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
 	0xf200,	0xf200,	0xf900,	0xf901,	0xf902,	0xf903,	0xf904,	0xf905,
-	0xf906,	0xf907,	0xf861,	0xf908,	0xf909,	0xf200,	0xf200,	0xf200,
+	0xf906,	0xf907,	0xf200,	0xf908,	0xf909,	0xf200,	0xf200,	0xf200,
 	0xf504,	0xf505,	0xf506,	0xf502,	0xf507,	0xf508,	0xf200,	0xf50a,
 	0xf200,	0xf10c,	0xf200,	0xf209,	0xf200,	0xf509,	0xf200,	0xf50b,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf503,	0xf117,
-	0xf501,	0xf119,	0xf500,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf501,	0xf119,	0xf500,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
 };
 
-u_short ctrl_alt_map[NR_KEYS] = {
-	0xf200,	0xf813,	0xf804,	0xf806,	0xf808,	0xf807,	0xf81a,	0xf818,
+static u_short ctrl_alt_map[NR_KEYS] = {
+	0xf801,	0xf813,	0xf804,	0xf806,	0xf808,	0xf807,	0xf81a,	0xf818,
 	0xf803,	0xf816,	0xf200,	0xf802,	0xf811,	0xf817,	0xf805,	0xf812,
 	0xf819,	0xf814,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
 	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf80f,
@@ -131,11 +131,11 @@
 	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
 	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
 	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
-	0xf306,	0xf307,	0xf801,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
+	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
 	0xf504,	0xf505,	0xf506,	0xf502,	0xf507,	0xf508,	0xf200,	0xf50a,
 	0xf200,	0xf10c,	0xf200,	0xf200,	0xf200,	0xf509,	0xf200,	0xf50b,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf503,	0xf117,
-	0xf501,	0xf119,	0xf500,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf501,	0xf119,	0xf500,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
 };
 
 ushort *key_maps[MAX_NR_KEYMAPS] = {
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/mackeymap.map linux/drivers/macintosh/mackeymap.map
--- v2.1.78/linux/drivers/macintosh/mackeymap.map	Mon Aug 18 18:19:45 1997
+++ linux/drivers/macintosh/mackeymap.map	Mon Jan 12 15:18:14 1998
@@ -2,7 +2,8 @@
 keymaps 0-2,4-5,8,12
 # We use the Command (pretzel) key as Alt, and the Option key as AltGr.
 #
-keycode 0x00 =
+keycode 0x00 = a
+	altgr   keycode 0x00 = Hex_A
 keycode 0x01 = s               
 keycode 0x02 = d
 	altgr   keycode 0x02 = Hex_D   
@@ -162,9 +163,6 @@
 keycode 0x59 = KP_7            
 	alt     keycode 0x59 = Ascii_7         
 	altgr   keycode 0x59 = Hex_7
-# keycode 0 (A) is remapped to here         
-keycode 0x5a = a
-	altgr   keycode 0x00 = Hex_A
 keycode 0x5b = KP_8            
 	alt     keycode 0x5b = Ascii_8         
 	altgr   keycode 0x5b = Hex_8         
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/macserial.c linux/drivers/macintosh/macserial.c
--- v2.1.78/linux/drivers/macintosh/macserial.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/macintosh/macserial.c	Mon Jan 12 15:18:14 1998
@@ -104,6 +104,7 @@
 
 static void probe_sccs(void);
 static void change_speed(struct mac_serial *info);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
 
 static struct tty_struct *serial_table[NUM_CHANNELS];
 static struct termios *serial_termios[NUM_CHANNELS];
@@ -325,7 +326,7 @@
 
 		if (!tty)
 			continue;
-		queue_task(&tty->flip.tqueue, &tq_timer);
+		tty_flip_buffer_push(tty);
 
 		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
 			static int flip_buf_ovf;
@@ -398,7 +399,8 @@
 		if (status & DCD) {
 			wake_up_interruptible(&info->open_wait);
 		} else if (!(info->flags & ZILOG_CALLOUT_ACTIVE)) {
-			queue_task(&info->tqueue_hangup, &tq_scheduler);
+			if (info->tty)
+				tty_hangup(info->tty);
 		}
 	}
 
@@ -548,27 +550,6 @@
 	}
 }
 
-/*
- * This routine is called from the scheduler tqueue when the interrupt
- * routine has signalled that a hangup has occurred.  The path of
- * hangup processing is:
- *
- * 	serial interrupt routine -> (scheduler tqueue) ->
- * 	do_serial_hangup() -> tty->hangup() -> rs_hangup()
- * 
- */
-static void do_serial_hangup(void *private_)
-{
-	struct mac_serial	*info = (struct mac_serial *) private_;
-	struct tty_struct	*tty;
-	
-	tty = info->tty;
-	if (!tty)
-		return;
-
-	tty_hangup(tty);
-}
-
 static void rs_timer(void)
 {
 }
@@ -1196,21 +1177,25 @@
 }
 
 /*
- * This routine sends a break character out the serial port.
+ * rs_break - turn transmit break condition on/off
  */
-static void send_break(	struct mac_serial * info, int duration)
+static void rs_break(struct tty_struct *tty, int break_state)
 {
+	struct mac_serial *info = (struct mac_serial *) tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "rs_break"))
+		return;
 	if (!info->port)
 		return;
-	current->state = TASK_INTERRUPTIBLE;
-	current->timeout = jiffies + duration;
-	cli();
-	info->curregs[5] |= SND_BRK;
-	write_zsreg(info->zs_channel, 5, info->curregs[5]);
-	schedule();
-	info->curregs[5] &= ~SND_BRK;
+
+	save_flags(flags); cli();
+	if (break_state == -1)
+		info->curregs[5] |= SND_BRK;
+	else
+		info->curregs[5] &= ~SND_BRK;
 	write_zsreg(info->zs_channel, 5, info->curregs[5]);
-	sti();
+	restore_flags(flags);
 }
 
 static int rs_ioctl(struct tty_struct *tty, struct file * file,
@@ -1218,7 +1203,6 @@
 {
 	int error;
 	struct mac_serial * info = (struct mac_serial *)tty->driver_data;
-	int retval;
 
 	if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
 		return -ENODEV;
@@ -1231,31 +1215,6 @@
 	}
 	
 	switch (cmd) {
-		case TCSBRK:	/* SVID version: non-zero arg --> no break */
-			retval = tty_check_change(tty);
-			if (retval)
-				return retval;
-			tty_wait_until_sent(tty, 0);
-			if (!arg)
-				send_break(info, HZ/4);	/* 1/4 second */
-			return 0;
-		case TCSBRKP:	/* support for POSIX tcsendbreak() */
-			retval = tty_check_change(tty);
-			if (retval)
-				return retval;
-			tty_wait_until_sent(tty, 0);
-			send_break(info, arg ? arg*(HZ/10) : HZ/4);
-			return 0;
-		case TIOCGSOFTCAR:
-			return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
-		case TIOCSSOFTCAR:
-			error = get_user(arg, (int *) arg);
-			if (error)
-				return error;
-			tty->termios->c_cflag =
-				((tty->termios->c_cflag & ~CLOCAL) |
-				 (arg ? CLOCAL : 0));
-			return 0;
 		case TIOCMGET:
 			error = verify_area(VERIFY_WRITE, (void *) arg,
 				sizeof(unsigned int));
@@ -1326,7 +1285,6 @@
 {
 	struct mac_serial * info = (struct mac_serial *)tty->driver_data;
 	unsigned long flags;
-	unsigned long timeout;
 
 	if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
 		return;
@@ -1395,14 +1353,7 @@
 		 * Before we drop DTR, make sure the SCC transmitter
 		 * has completely drained.
 		 */
-		timeout = jiffies+HZ;
-		while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) {
-			current->state = TASK_INTERRUPTIBLE;
-			current->timeout = jiffies + info->timeout;
-			schedule();
-			if (jiffies > timeout)
-				break;
-		}
+		rs_wait_until_sent(tty, info->timeout);
 	}
 
 	shutdown(info);
@@ -1413,14 +1364,6 @@
 	tty->closing = 0;
 	info->event = 0;
 	info->tty = 0;
-	if (tty->ldisc.num != ldiscs[N_TTY].num) {
-		if (tty->ldisc.close)
-			(tty->ldisc.close)(tty);
-		tty->ldisc = ldiscs[N_TTY];
-		tty->termios->c_line = N_TTY;
-		if (tty->ldisc.open)
-			(tty->ldisc.open)(tty);
-	}
 	if (info->blocked_open) {
 		if (info->close_delay) {
 			current->state = TASK_INTERRUPTIBLE;
@@ -1436,6 +1379,42 @@
 }
 
 /*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	struct mac_serial *info = (struct mac_serial *) tty->driver_data;
+	unsigned long orig_jiffies, char_time;
+
+	if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent"))
+		return;
+
+	orig_jiffies = jiffies;
+	/*
+	 * Set the check interval to be 1/5 of the estimated time to
+	 * send a single character, and make it at least 1.  The check
+	 * interval should also be less than the timeout.
+	 */
+	char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
+	char_time = char_time / 5;
+	if (char_time == 0)
+		char_time = 1;
+	if (timeout)
+		char_time = MIN(char_time, timeout);
+	while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) {
+		current->state = TASK_INTERRUPTIBLE;
+		current->counter = 0;	/* make us low-priority */
+		current->timeout = jiffies + char_time;
+		schedule();
+		if (signal_pending(current))
+			break;
+		if (timeout && ((orig_jiffies + timeout) < jiffies))
+			break;
+	}
+	current->state = TASK_RUNNING;
+}
+
+/*
  * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
  */
 void rs_hangup(struct tty_struct *tty)
@@ -1473,10 +1452,8 @@
 	if (info->flags & ZILOG_CLOSING) {
 		interruptible_sleep_on(&info->close_wait);
 #ifdef SERIAL_DO_RESTART
-		if (info->flags & ZILOG_HUP_NOTIFY)
-			return -EAGAIN;
-		else
-			return -ERESTARTSYS;
+		return ((info->flags & ZILOG_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
 #else
 		return -EAGAIN;
 #endif
@@ -1618,6 +1595,21 @@
 	info->tty = tty;
 
 	/*
+	 * If the port is the middle of closing, bail out now
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ZILOG_CLOSING)) {
+		if (info->flags & ZILOG_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+		return ((info->flags & ZILOG_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
 	 * Start up serial port
 	 */
 	retval = startup(info);
@@ -1757,6 +1749,8 @@
 	serial_driver.stop = rs_stop;
 	serial_driver.start = rs_start;
 	serial_driver.hangup = rs_hangup;
+	serial_driver.break_ctl = rs_break;
+	serial_driver.wait_until_sent = rs_wait_until_sent;
 
 	/*
 	 * The callout device is just like normal device except for
@@ -1809,8 +1803,6 @@
 		info->blocked_open = 0;
 		info->tqueue.routine = do_softint;
 		info->tqueue.data = info;
-		info->tqueue_hangup.routine = do_serial_hangup;
-		info->tqueue_hangup.data = info;
 		info->callout_termios =callout_driver.init_termios;
 		info->normal_termios = serial_driver.init_termios;
 		info->open_wait = 0;
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/nvram.c linux/drivers/macintosh/nvram.c
--- v2.1.78/linux/drivers/macintosh/nvram.c	Tue Sep 23 16:48:47 1997
+++ linux/drivers/macintosh/nvram.c	Mon Jan 12 15:18:14 1998
@@ -28,30 +28,40 @@
 	return file->f_pos;
 }
 
-static long read_nvram(struct inode *inode, struct file *file,
-	char *buf, unsigned long count)
+static ssize_t read_nvram(struct file *file, char *buf,
+			  size_t count, loff_t *ppos)
 {
-	unsigned int i = file->f_pos;
+	unsigned int i;
 	char *p = buf;
 
-	for (; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count)
-		put_user(nvram_read_byte(i), p);
-	file->f_pos = i;
+	if (verify_area(VERIFY_WRITE, buf, count))
+		return -EFAULT;
+	if (*ppos >= NVRAM_SIZE)
+		return 0;
+	for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count)
+		if (__put_user(nvram_read_byte(i), p))
+			return -EFAULT;
+	*ppos = i;
 	return p - buf;
 }
 
-static long write_nvram(struct inode *inode, struct file *file,
-	const char *buf, unsigned long count)
+static ssize_t write_nvram(struct file *file, const char *buf,
+			   size_t count, loff_t *ppos)
 {
-	unsigned int i = file->f_pos;
+	unsigned int i;
 	const char *p = buf;
 	char c;
 
-	for (; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) {
-		get_user(c, p);
-		nvram_write_byte(i, c);
+	if (verify_area(VERIFY_READ, buf, count))
+		return -EFAULT;
+	if (*ppos >= NVRAM_SIZE)
+		return 0;
+	for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) {
+		if (__get_user(c, p))
+			return -EFAULT;
+		nvram_write_byte(c, i);
 	}
-	file->f_pos = i;
+	*ppos = i;
 	return p - buf;
 }
 
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/pmac-cons.c linux/drivers/macintosh/pmac-cons.c
--- v2.1.78/linux/drivers/macintosh/pmac-cons.c	Mon Aug 18 18:19:46 1997
+++ linux/drivers/macintosh/pmac-cons.c	Mon Jan 12 15:18:14 1998
@@ -19,7 +19,9 @@
 #include <asm/pgtable.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
+#include <asm/adb.h>
 #include <asm/cuda.h>
+#define INCLUDE_LINUX_LOGO_DATA
 #include <asm/linux_logo.h>
 #include <linux/selection.h>
 #include <linux/console_struct.h>
@@ -28,6 +30,7 @@
 #include "control.h"
 #include "platinum.h"
 #include "valkyrie.h"
+#include "chips.h"
 #ifdef CONFIG_ATY_VIDEO
 #include "aty.h"
 #endif
@@ -135,6 +138,8 @@
 static void invert_cursor(int);
 static int map_unknown(struct device_node *);
 static void unknown_init(void);
+static void unknown_set_palette(unsigned char red[], unsigned char green[],
+				unsigned char blue[], int index, int ncolors);
 
 struct display_interface {
 	char *name;
@@ -145,12 +150,22 @@
 			    unsigned char blue[], int index, int ncolors);
 	void (*set_blanking)(int blank_mode);
 } displays[] = {
+#ifdef CONFIG_CONTROL_VIDEO
 	{ "control", map_control_display, control_init,
 	  control_setmode, control_set_palette, control_set_blanking },
+#endif
+#ifdef CONFIG_PLATINUM_VIDEO
 	{ "platinum", map_platinum, platinum_init,
 	  platinum_setmode, platinum_set_palette, platinum_set_blanking },
+#endif
+#ifdef CONFIG_VALKYRIE_VIDEO
 	{ "valkyrie", map_valkyrie_display, valkyrie_init,
 	  valkyrie_setmode, valkyrie_set_palette, valkyrie_set_blanking },
+#endif
+#ifdef CONFIG_CHIPS_VIDEO
+	{ "chips65550", map_chips_display, chips_init,
+	  chips_setmode, chips_set_palette, chips_set_blanking },
+#endif
 #ifdef CONFIG_ATY_VIDEO
 	{ "ATY,mach64", map_aty_display, aty_init,
 	  aty_setmode, aty_set_palette, aty_set_blanking },
@@ -160,12 +175,16 @@
 	  aty_setmode, aty_set_palette, aty_set_blanking },
 	{ "ATY,mach64ii", map_aty_display, aty_init,
 	  aty_setmode, aty_set_palette, aty_set_blanking },
-#if 0	/* not right for 3D mach64 yet */
 	{ "ATY,264GT-B", map_aty_display, aty_init,
 	  aty_setmode, aty_set_palette, aty_set_blanking },
 	{ "ATY,mach64_3D_pcc", map_aty_display, aty_init,
-	  aty_setmode, aty_set_palette },
-#endif
+	  aty_setmode, aty_set_palette, aty_set_blanking },
+ 	{ "ATY,XCLAIM3D", map_aty_display, aty_init,
+	  aty_setmode, aty_set_palette, aty_set_blanking },
+ 	{ "ATY,XCLAIMVR", map_aty_display, aty_init,
+	  aty_setmode, aty_set_palette, aty_set_blanking },
+ 	{ "ATY,RAGEII_M", map_aty_display, aty_init, // untested!!
+	  aty_setmode, aty_set_palette, aty_set_blanking },
 #endif
 #ifdef CONFIG_IMSTT_VIDEO
 	{ "IMS,tt128mb", map_imstt_display, imstt_init, 
@@ -175,7 +194,7 @@
 };
 
 struct display_interface unknown_display = {
-	"unknown", NULL, unknown_init, NULL, NULL
+	"unknown", NULL, unknown_init, NULL, unknown_set_palette, NULL
 };
 
 static struct display_interface *current_display;
@@ -191,8 +210,6 @@
 unsigned char *fb_start;	/* addr of top left pixel of top left char */
 struct vc_mode display_info;
 
-extern int screen_initialized;	/* in arch/ppc/pmac/prom.c */
-
 #define cmapsz	(16*256)
 extern unsigned char vga_font[cmapsz];
 
@@ -500,8 +517,6 @@
 	struct vmode_attr *ap;
 
 	current_display = NULL;
-	if (serial_console) 
-		return;
 	for (disp = displays; disp->name != NULL; ++disp) {
 		dp = find_devices(disp->name);
 		if (dp == 0)
@@ -518,21 +533,23 @@
 		 * we may be able to use it in a limited fashion.
 		 * If there is, it has already been opened in prom_init().
 		 */
-		if (prom_display_path[0] != 0) {
-			dp = find_path_device(prom_display_path);
-			if (dp != 0 && map_unknown(dp))
+		int i;
+		for (i = 0; i < prom_num_displays; ++i) {
+			dp = find_path_device(prom_display_paths[i]);
+			if (dp != 0 && map_unknown(dp)) {
 				current_display = &unknown_display;
-			else
+				break;
+			} else {
 				printk(KERN_INFO "Can't use %s for display\n",
-				       prom_display_path);
+				       prom_display_paths[i]);
+			}
 		}
 	}
 
 	if (current_display == NULL
 	    || video_mode <= 0 || video_mode > VMODE_MAX) {
-		printk(KERN_INFO "No usable display device found"
-		       "- using serial console\n");
-		serial_console = 1;	/* no screen - fall back to serial */
+		printk(KERN_INFO "No usable display device found\n");
+		current_display = NULL;
 		return;
 	}
 	ap = &vmode_attrs[video_mode - 1];
@@ -590,15 +607,6 @@
 }
 
 void
-pmac_cons_setup(char *str, int *ints)
-{
-	if (strcmp(str, "ttya") == 0 || strcmp(str, "modem") == 0)
-		serial_console = 1;
-	else if (strcmp(str, "ttyb") == 0 || strcmp(str, "printer") == 0)
-		serial_console = 2;
-}
-
-void
 pmac_vmode_setup(char *str, int *ints)
 {
 	if (ints[0] >= 1)
@@ -611,9 +619,8 @@
 con_type_init(unsigned long mem_start, const char **type_p)
 {
 	if (current_display == NULL)
-		panic("no display available");
+		return mem_start;
 	current_display->init_interface();
-	screen_initialized = 1;		/* inhibits prom_print */
 	can_do_color = 1;
 	video_type = VIDEO_TYPE_PMAC;
 	*type_p = display_info.name;
@@ -624,6 +631,12 @@
 	return mem_start;
 }
 
+int
+con_is_present(void)
+{
+	return current_display != NULL;
+}
+
 static __inline__ void
 draw_logo_8(void)
 {
@@ -725,7 +738,14 @@
 	xy[1] = (LINUX_LOGO_HEIGHT + 16) / 16;
 	putconsxy(0, xy);
 
-	p = "PowerMac/Linux " UTS_RELEASE;
+	switch (_machine) {
+	    case _MACH_Pmac:
+		p = "PowerMac/Linux " UTS_RELEASE;
+		break;
+	    default:
+		p = "Linux/PPC " UTS_RELEASE;
+		break;
+	}
 	addr = (unsigned short *) video_mem_base + 2 * video_num_columns
 		+ LINUX_LOGO_WIDTH / 8 + 8;
 	for (; *p; ++p) {
@@ -868,6 +888,8 @@
 };
 
 static unsigned char *frame_buffer;
+static unsigned char *unknown_cmap_adr;
+static volatile unsigned char *unknown_cmap_data;
 
 static int map_unknown(struct device_node *dp)
 {
@@ -914,9 +936,6 @@
 			return 0;
 		}
 		address = dp->addrs[i].address;
-		/* temporary kludge for valkyrie */
-		if (strcmp(dp->name, "valkyrie") == 0)
-			address += 0x1000;
 	}
 	printk(KERN_INFO "%s: using address %x\n", dp->full_name, address);
 	frame_buffer = ioremap(address, len);
@@ -945,7 +964,15 @@
 	display_info.fb_address = (unsigned long) frame_buffer;
 	display_info.cmap_adr_address = 0;
 	display_info.cmap_data_address = 0;
-	display_info.disp_reg_address = 0;
+	unknown_cmap_adr = 0;
+	/* XXX kludge for ati */
+	if (strncmp(dp->name, "ATY,", 4) == 0) {
+		display_info.disp_reg_address = address + 0x7ffc00;
+		display_info.cmap_adr_address = address + 0x7ffcc0;
+		display_info.cmap_data_address = address + 0x7ffcc1;
+		unknown_cmap_adr = ioremap(address + 0x7ff000, 0x1000) + 0xcc0;
+		unknown_cmap_data = unknown_cmap_adr + 1;
+	}
 
 	return 1;
 }
@@ -963,6 +990,25 @@
 	p = (unsigned *) frame_buffer;
 	for (i = n_scanlines * line_pitch / sizeof(unsigned); i != 0; --i)
 		*p++ = 0;
+}
+
+static void
+unknown_set_palette(unsigned char red[], unsigned char green[],
+		    unsigned char blue[], int index, int ncolors)
+{
+	volatile unsigned char *a, *d;
+	int i;
+
+	if (unknown_cmap_adr == 0)
+		return;
+	a = unknown_cmap_adr;
+	d = unknown_cmap_data;
+	for (i = 0; i < ncolors; ++i) {
+		*a = index + i;	eieio();
+		*d = red[i];	eieio();
+		*d = green[i];	eieio();
+		*d = blue[i];	eieio();
+	}
 }
 
 
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/pmac-cons.h linux/drivers/macintosh/pmac-cons.h
--- v2.1.78/linux/drivers/macintosh/pmac-cons.h	Mon Aug 18 18:19:46 1997
+++ linux/drivers/macintosh/pmac-cons.h	Mon Jan 12 15:18:14 1998
@@ -9,8 +9,6 @@
  * 2 of the License, or (at your option) any later version.
  */
 
-extern int serial_console;	/* set to use serial port as console */
-
 /*
  * Video mode values.
  * These are supposed to be the same as the values that
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/valkyrie.c linux/drivers/macintosh/valkyrie.c
--- v2.1.78/linux/drivers/macintosh/valkyrie.c	Mon Aug 18 18:19:46 1997
+++ linux/drivers/macintosh/valkyrie.c	Mon Jan 12 15:18:14 1998
@@ -16,6 +16,7 @@
 #include <linux/nvram.h>
 #include <asm/prom.h>
 #include <asm/io.h>
+#include <asm/adb.h>
 #include <asm/cuda.h>
 #include <linux/selection.h>
 #include "pmac-cons.h"
@@ -209,13 +210,13 @@
 static void
 set_valkyrie_clock(unsigned char *params)
 {
-	struct cuda_request req;
+	struct adb_request req;
 	int i;
 
 	for (i = 0; i < 3; ++i) {
 		cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
 			     0x50, i + 1, params[i]);
-		while (!req.got_reply)
+		while (!req.complete)
 			cuda_poll();
 	}
 }
diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/via-cuda.c linux/drivers/macintosh/via-cuda.c
--- v2.1.78/linux/drivers/macintosh/via-cuda.c	Mon Aug 18 18:19:46 1997
+++ linux/drivers/macintosh/via-cuda.c	Mon Jan 12 15:18:14 1998
@@ -6,9 +6,6 @@
  * Bus) which connects to the keyboard and mouse.  The CUDA also
  * controls system power and the RTC (real time clock) chip.
  *
- * This file also contains routines to support access to ADB
- * devices via the /dev/adb interface.
- *
  * Copyright (C) 1996 Paul Mackerras.
  */
 #include <stdarg.h>
@@ -18,6 +15,7 @@
 #include <linux/delay.h>
 #include <linux/sched.h>
 #include <asm/prom.h>
+#include <asm/adb.h>
 #include <asm/cuda.h>
 #include <asm/io.h>
 #include <asm/system.h>
@@ -58,10 +56,6 @@
 #define IER_CLR		0		/* clear bits in IER */
 #define SR_INT		0x04		/* Shift register full/empty */
 
-static struct adb_handler {
-    void (*handler)(unsigned char *, int, struct pt_regs *);
-} adb_handler[16];
-
 static enum cuda_state {
     idle,
     sent_first_byte,
@@ -71,8 +65,8 @@
     awaiting_reply
 } cuda_state;
 
-static struct cuda_request *current_req;
-static struct cuda_request *last_req;
+static struct adb_request *current_req;
+static struct adb_request *last_req;
 static unsigned char cuda_rbuf[16];
 static unsigned char *reply_ptr;
 static int reading_reply;
@@ -82,6 +76,8 @@
 static void cuda_start(void);
 static void via_interrupt(int irq, void *arg, struct pt_regs *regs);
 static void cuda_input(unsigned char *buf, int nb, struct pt_regs *regs);
+static int cuda_adb_send_request(struct adb_request *req, int sync);
+static int cuda_adb_autopoll(int on);
 
 void
 via_cuda_init()
@@ -89,15 +85,10 @@
     struct device_node *vias;
 
     vias = find_devices("via-cuda");
-    if (vias == 0) {
-	printk(KERN_WARNING "Warning: no via-cuda\n");
-	vias = find_devices("via-pmu");
-	if (vias == 0)
-	    return;
-	printk(KERN_WARNING "Found via-pmu, using it as via-cuda\n");
-    }
+    if (vias == 0)
+	return;
     if (vias->next != 0)
-	printk("Warning: only using 1st via-cuda\n");
+	printk(KERN_WARNING "Warning: only using 1st via-cuda\n");
 
 #if 0
     { int i;
@@ -111,21 +102,34 @@
     printk("\n"); }
 #endif
 
-    if (vias->n_addrs != 1 || vias->n_intrs != 1)
-	panic("via-cuda: expecting 1 address and 1 interrupt");
+    if (vias->n_addrs != 1 || vias->n_intrs != 1) {
+	printk(KERN_ERR "via-cuda: expecting 1 address (%d) and 1 interrupt (%d)\n",
+	       vias->n_addrs, vias->n_intrs);
+	if (vias->n_addrs < 1 || vias->n_intrs < 1)
+	    return;
+    }
     via = (volatile unsigned char *) vias->addrs->address;
 
-    if (!init_via())
-	panic("init_via failed");
+    if (!init_via()) {
+	printk(KERN_ERR "init_via failed\n");
+	return;
+    }
 
     cuda_state = idle;
 
-    if (request_irq(vias->intrs[0], via_interrupt, 0, "VIA", (void *)0))
-	panic("VIA: can't get irq %d\n", vias->intrs[0]);
+    if (request_irq(vias->intrs[0], via_interrupt, 0, "VIA", (void *)0)) {
+	printk(KERN_ERR "VIA: can't get irq %d\n", vias->intrs[0]);
+	return;
+    }
 
     /* Clear and enable interrupts */
     via[IFR] = 0x7f; eieio();	/* clear interrupts by writing 1s */
     via[IER] = IER_SET|SR_INT; eieio();	/* enable interrupt from SR */
+
+    /* Set function pointers */
+    adb_hardware = ADB_VIACUDA;
+    adb_send_request = cuda_adb_send_request;
+    adb_autopoll = cuda_adb_autopoll;
 }
 
 #define WAIT_FOR(cond, what)				\
@@ -178,9 +182,42 @@
     return 1;
 }
 
+/* Send an ADB command */
+static int
+cuda_adb_send_request(struct adb_request *req, int sync)
+{
+    int i;
+
+    for (i = req->nbytes; i > 0; --i)
+	req->data[i] = req->data[i-1];
+    req->data[0] = ADB_PACKET;
+    ++req->nbytes;
+    req->reply_expected = 1;
+    i = cuda_send_request(req);
+    if (i)
+	return i;
+    if (sync) {
+	while (!req->complete)
+	    cuda_poll();
+    }
+    return 0;
+}
+
+/* Enable/disable autopolling */
+static int
+cuda_adb_autopoll(int on)
+{
+    struct adb_request req;
+
+    cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, on);
+    while (!req.complete)
+	cuda_poll();
+    return 0;
+}
+
 /* Construct and send a cuda request */
 int
-cuda_request(struct cuda_request *req, void (*done)(struct cuda_request *),
+cuda_request(struct adb_request *req, void (*done)(struct adb_request *),
 	     int nbytes, ...)
 {
     va_list list;
@@ -197,13 +234,13 @@
 }
 
 int
-cuda_send_request(struct cuda_request *req)
+cuda_send_request(struct adb_request *req)
 {
     unsigned long flags;
 
     req->next = 0;
     req->sent = 0;
-    req->got_reply = 0;
+    req->complete = 0;
     req->reply_len = 0;
     save_flags(flags); cli();
 
@@ -225,7 +262,7 @@
 cuda_start()
 {
     unsigned long flags;
-    struct cuda_request *req;
+    struct adb_request *req;
 
     /* assert cuda_state == idle */
     /* get the packet to send */
@@ -261,7 +298,7 @@
 via_interrupt(int irq, void *arg, struct pt_regs *regs)
 {
     int x, status;
-    struct cuda_request *req;
+    struct adb_request *req;
 
     if ((via[IFR] & SR_INT) == 0)
 	return;
@@ -351,7 +388,7 @@
 	if (reading_reply) {
 	    req = current_req;
 	    req->reply_len = reply_ptr - req->reply;
-	    req->got_reply = 1;
+	    req->complete = 1;
 	    current_req = req->next;
 	    if (req->done)
 		(*req->done)(req);
@@ -377,21 +414,11 @@
 static void
 cuda_input(unsigned char *buf, int nb, struct pt_regs *regs)
 {
-    int i, id;
-    static int dump_cuda_input = 0;
+    int i;
 
     switch (buf[0]) {
     case ADB_PACKET:
-	id = buf[2] >> 4;
-	if (dump_cuda_input) {
-	    printk(KERN_INFO "adb packet: ");
-	    for (i = 0; i < nb; ++i)
-		printk(" %x", buf[i]);
-	    printk(", id = %d\n", id);
-	}
-	if (adb_handler[id].handler != 0) {
-	    (*adb_handler[id].handler)(buf, nb, regs);
-	}
+	adb_input(buf+2, nb-2, regs, buf[1] & 0x40);
 	break;
 
     default:
@@ -400,16 +427,4 @@
 	    printk(" %.2x", buf[i]);
 	printk("\n");
     }
-}
-
-/* Ultimately this should return the number of devices with
-   the given default id. */
-int
-adb_register(int default_id,
-	     void (*handler)(unsigned char *, int, struct pt_regs *))
-{
-    if (adb_handler[default_id].handler != 0)
-	panic("Two handlers for ADB device %d\n", default_id);
-    adb_handler[default_id].handler = handler;
-    return 1;
 }
diff -u --recursive --new-file v2.1.78/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v2.1.78/linux/drivers/net/3c59x.c	Tue Jan  6 09:37:34 1998
+++ linux/drivers/net/3c59x.c	Mon Jan 12 15:28:18 1998
@@ -15,7 +15,7 @@
 */
 
 static char *version =
-"3c59x.c:v0.46B 9/25/97 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
+"3c59x.c:v0.47H 12/4/97 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
 
 /* "Knobs" that adjust features and parameters. */
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
@@ -89,9 +89,10 @@
 #endif
 #define virt_to_bus(addr)  ((unsigned long)addr)
 #define bus_to_virt(addr) ((void*)addr)
+#define NR_IRQS 16
 #else  /* 1.3.0 and later */
 #define RUN_AT(x) (jiffies + (x))
-#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len)
 #endif
 
 #ifdef SA_SHIRQ
@@ -111,8 +112,19 @@
 #define udelay(microsec)	do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
 #endif
 
-#if (LINUX_VERSION_CODE < 0x20123)
+#if LINUX_VERSION_CODE < 0x20115
 #define test_and_set_bit(val, addr) set_bit(val, addr)
+#elif defined(MODULE)
+MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
+MODULE_DESCRIPTION("3Com 3c590/3c900 series Vortex/Boomerang driver");
+MODULE_PARM(debug, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(compaq_ioaddr, "i");
+MODULE_PARM(compaq_irq, "i");
+MODULE_PARM(compaq_prod_id, "i");
 #endif
 
 /* "Knobs" for adjusting internal parameters. */
@@ -147,7 +159,8 @@
 
 /* Caution!  These entries must be consistent, with the EISA ones last. */
 static const int product_ids[] = {
-	0x5900, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001, 0x9050, 0x9051, 0, 0};
+	0x5900, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001, 0x9050, 0x9051, 0x9055,
+	0, 0};
 static const char *product_names[] = {
 	"3c590 Vortex 10Mbps",
 	"3c595 Vortex 100baseTX",
@@ -157,11 +170,12 @@
 	"3c900 Boomerang 10Mbps/Combo",
 	"3c905 Boomerang 100baseTx",
 	"3c905 Boomerang 100baseT4",
+	"3c905B Cyclone 100baseTx",
 	"3c592 EISA 10mbps Demon/Vortex",
 	"3c597 EISA Fast Demon/Vortex",
 };
-#define DEMON10_INDEX 8
-#define DEMON100_INDEX 9
+#define DEMON10_INDEX 9
+#define DEMON100_INDEX 10
 
 /*
 				Theory of Operation
@@ -424,7 +438,7 @@
 
 static int vortex_scan(struct device *dev);
 static struct device *vortex_found_device(struct device *dev, int ioaddr,
-										  int irq, int product_index,
+										  int irq, const char *product_name,
 										  int options, int card_idx);
 static int vortex_probe1(struct device *dev);
 static int vortex_open(struct device *dev);
@@ -472,10 +486,10 @@
 /* A list of all installed Vortex devices, for removing the driver module. */
 static struct device *root_vortex_dev = NULL;
 
+#ifdef MODULE
 /* Variables to work-around the Compaq PCI BIOS32 problem. */
-static int compaq_ioaddr = 0, compaq_irq = 0, compaq_prod_id = 0;
+static int compaq_ioaddr = 0, compaq_irq = 0;
 
-#ifdef MODULE
 static int debug = -1;
 
 int
@@ -510,6 +524,7 @@
 static int vortex_scan(struct device *dev)
 {
 	int cards_found = 0;
+	const char *product_name;
 
 #ifndef NO_PCI					/* Allow an EISA-only driver. */
 	/* Ideally we would detect all cards in slot order.  That would
@@ -522,10 +537,10 @@
 
 		for (;pci_index < 0xff; pci_index++) {
 			unsigned char pci_irq_line, pci_latency;
-			unsigned short pci_command, vendor, device;
+			unsigned short pci_command, new_command, vendor, device;
 			unsigned int pci_ioaddr;
-
 			int board_index = 0;
+
 			if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
 									pci_index, &pci_bus, &pci_device_fn)
 				!= PCIBIOS_SUCCESSFUL)
@@ -538,6 +553,8 @@
 									 PCI_INTERRUPT_LINE, &pci_irq_line);
 			pcibios_read_config_dword(pci_bus, pci_device_fn,
 									  PCI_BASE_ADDRESS_0, &pci_ioaddr);
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+									 PCI_COMMAND, &pci_command);
 			/* Remove I/O space marker in bit 0. */
 			pci_ioaddr &= ~3;
 
@@ -548,7 +565,13 @@
 				if (device == product_ids[board_index])
 					break;
 			}
-			if (product_ids[board_index] == 0) {
+			if (product_ids[board_index])
+				product_name = product_names[board_index];
+			else if ((device & 0xfff0) == 0x9000)
+				product_name = "3c900";
+			else if ((device & 0xfff0) == 0x9050)
+				product_name = "3c905";
+			else {
 				printk("Unknown 3Com PCI ethernet adapter type %4.4x detected:"
 					   " not configured.\n", device);
 				continue;
@@ -556,34 +579,34 @@
 			if (check_region(pci_ioaddr, VORTEX_TOTAL_SIZE))
 				continue;
 
+			new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
+			if (pci_command != new_command) {
+				printk(KERN_INFO "  The PCI BIOS has not enabled this"
+					   " device!  Updating PCI command %4.4x->%4.4x.\n",
+					   pci_command, new_command);
+				pcibios_write_config_word(pci_bus, pci_device_fn,
+										  PCI_COMMAND, new_command);
+			}
+
 			dev = vortex_found_device(dev, pci_ioaddr, pci_irq_line,
-									  board_index, dev && dev->mem_start
+									  product_name, dev && dev->mem_start
 									  ? dev->mem_start : options[cards_found],
 									  cards_found);
 
 			if (dev) {
-				/* Get and check the bus-master and latency values.
-				   Some PCI BIOSes fail to set the master-enable bit, and
+				/* Get and check the latency values.  On the 3c590 series
 				   the latency timer must be set to the maximum value to avoid
 				   data corruption that occurs when the timer expires during
-				   a transfer -- a bug in the Vortex chip. */
-				pcibios_read_config_word(pci_bus, pci_device_fn,
-										 PCI_COMMAND, &pci_command);
-				if ( ! (pci_command & PCI_COMMAND_MASTER)) {
-					printk("%s:  PCI Master Bit has not been set! "
-						   " Setting...\n", dev->name);
-					pci_command |= PCI_COMMAND_MASTER;
-					pcibios_write_config_word(pci_bus, pci_device_fn,
-											  PCI_COMMAND, pci_command);
-				}
+				   a transfer -- a bug in the Vortex chip only. */
+				unsigned char new_latency = (device&0xff00) == 0x5900 ? 248 : 32;
 				pcibios_read_config_byte(pci_bus, pci_device_fn,
 										 PCI_LATENCY_TIMER, &pci_latency);
-				if (pci_latency != 248) {
+				if (pci_latency < new_latency) {
 					printk("%s: Overriding PCI latency"
-						   " timer (CFLT) setting of %d, new value is 248.\n",
-						   dev->name, pci_latency);
+						   " timer (CFLT) setting of %d, new value is %d.\n",
+						   dev->name, pci_latency, new_latency);
 					pcibios_write_config_byte(pci_bus, pci_device_fn,
-											  PCI_LATENCY_TIMER, 248);
+											  PCI_LATENCY_TIMER, new_latency);
 				}
 				dev = 0;
 				cards_found++;
@@ -596,7 +619,8 @@
 	if (EISA_bus) {
 		static int ioaddr = 0x1000;
 		for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
-			int product_id, product_index;
+			int product_id;
+			char *product_name;
 			if (check_region(ioaddr, VORTEX_TOTAL_SIZE))
 				continue;
 			/* Check the standard EISA ID register for an encoded '3Com'. */
@@ -605,13 +629,13 @@
 			/* Check for a product that we support, 3c59{2,7} any rev. */
 			product_id = inw(ioaddr + 0xC82) & 0xF0FF;
 			if (product_id == 0x7059) 		/* 597 */
-				product_index = DEMON100_INDEX;
+				product_name = "3c597 EISA Fast Demon/Vortex";
 			else if (product_id == 0x2059)	/* 592 */
-				product_index = DEMON10_INDEX;
+				product_name = "3c592 EISA 10mbps Demon/Vortex";
 			else
 				continue;
 			vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
-								product_index,  dev && dev->mem_start
+								product_name,  dev && dev->mem_start
 								? dev->mem_start : options[cards_found],
 								cards_found);
 			dev = 0;
@@ -619,24 +643,25 @@
 		}
 	}
 
+#ifdef MODULE
 	/* Special code to work-around the Compaq PCI BIOS32 problem. */
 	if (compaq_ioaddr) {
-		vortex_found_device(dev, compaq_ioaddr, compaq_irq, compaq_prod_id,
+		vortex_found_device(dev, compaq_ioaddr, compaq_irq, "3Com Vortex",
 							dev && dev->mem_start ? dev->mem_start
 							: options[cards_found], cards_found);
 		cards_found++;
 		dev = 0;
 	}
+#endif
 
-	/* Finally check for a 3c515 on the ISA bus. */
-	/* (3c515 support omitted on this version.) */
+	/* 3c515 cards are now supported by the 3c515.c driver. */
 
 	return cards_found;
 }
 
 static struct device *
 vortex_found_device(struct device *dev, int ioaddr, int irq,
-					int product_index, int options, int card_idx)
+					const char *product_name, int options, int card_idx)
 {
 	struct vortex_private *vp;
 
@@ -654,7 +679,7 @@
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
 	dev->init = vortex_probe1;
-	vp->product_name = product_names[product_index];
+	vp->product_name = product_name;
 	vp->options = options;
 	if (card_idx >= 0) {
 		if (full_duplex[card_idx] >= 0)
@@ -687,7 +712,7 @@
 	dev->mtu = mtu;
 
 	vp  = (struct vortex_private *)dev->priv;
-	vp->product_name = product_names[product_index];
+	vp->product_name = product_name;
 	vp->options = options;
 	if (options >= 0) {
 		vp->media_override = ((options & 7) == 2)  ?  0  :  options & 7;
@@ -717,7 +742,7 @@
 	/* Read the station address from the EEPROM. */
 	EL3WINDOW(0);
 	for (i = 0; i < 0x18; i++) {
-		short *phys_addr = (short *)dev->dev_addr;
+		u16 *phys_addr = (u16 *)dev->dev_addr;
 		int timer;
 		outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
 		/* Pause for at least 162 us. for the read to take place. */
@@ -792,7 +817,7 @@
 	vp->info1 = eeprom[13];
 	vp->info2 = eeprom[15];
 	vp->capabilities = eeprom[16];
-	if ((vp->capabilities & 0x20) && vp->bus_master) {
+	if (vp->capabilities & 0x20) {
 	  vp->full_bus_master_tx = 1;
 	  printk("  Enabling bus-master transmits and %s receives.\n",
 			 (vp->info2 & 1) ? "early" : "whole-frame" );
@@ -1045,13 +1070,17 @@
 			vp->rx_ring[i].next = virt_to_bus(&vp->rx_ring[i+1]);
 			vp->rx_ring[i].status = 0;	/* Clear complete bit. */
 			vp->rx_ring[i].length = PKT_BUF_SZ | LAST_FRAG;
-			skb = dev_alloc_skb(PKT_BUF_SZ);
+			skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
 			vp->rx_skbuff[i] = skb;
 			if (skb == NULL)
 				break;			/* Bad news!  */
 			skb->dev = dev;			/* Mark as being used by this device. */
+#if LINUX_VERSION_CODE >= 0x10300
 			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 			vp->rx_ring[i].addr = virt_to_bus(skb->tail);
+#else
+			vp->rx_ring[i].addr = virt_to_bus(skb->data);
+#endif
 		}
 		vp->rx_ring[i-1].next = virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */
 		outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
@@ -1332,7 +1361,7 @@
 	/* Clear the Tx status stack. */
 	{
 		short tx_status;
-		int i = 4;
+		int i = 32;
 
 		while (--i > 0	&&	(tx_status = inb(ioaddr + TxStatus)) > 0) {
 			if (tx_status & 0x3C) {		/* A Tx-disabling error occurred.  */
@@ -1431,9 +1460,10 @@
 	int latency;
 	int i = max_interrupt_work;
 
-	if (dev->interrupt)
-		printk("%s: Re-entering the interrupt handler.\n", dev->name);
-	dev->interrupt = 1;
+	if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
+		return;
+	}
 
 	ioaddr = dev->base_addr;
 	latency = inb(ioaddr + Timer);
@@ -1560,7 +1590,16 @@
 					printk("%s: Host error, FIFO diagnostic register %4.4x.\n",
 						   dev->name, fifo_diag);
 				/* Adapter failure requires Tx/Rx reset and reinit. */
-				if (fifo_diag & 0x0400) {
+				if (lp->full_bus_master_tx) {
+					int j;
+					outw(TotalReset | 0xff, ioaddr + EL3_CMD);
+					for (j = 200; j >= 0 ; j--)
+					if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
+						break;
+					/* Re-enable the receiver. */
+					outw(RxEnable, ioaddr + EL3_CMD);
+					outw(TxEnable, ioaddr + EL3_CMD);
+				} else if (fifo_diag & 0x0400) {
 					int j;
 					outw(TxReset, ioaddr + EL3_CMD);
 					for (j = 20; j >= 0 ; j--)
@@ -1675,6 +1714,7 @@
 	int entry = vp->cur_rx % RX_RING_SIZE;
 	int ioaddr = dev->base_addr;
 	int rx_status;
+	int rx_work_limit = vp->dirty_rx + RX_RING_SIZE - vp->cur_rx;
 
 	if (vortex_debug > 5)
 		printk("   In boomerang_rx(), status %4.4x, rx_status %4.4x.\n",
@@ -1704,11 +1744,16 @@
 			if (pkt_len < rx_copybreak
 				&& (skb = DEV_ALLOC_SKB(pkt_len + 2)) != 0) {
 				skb->dev = dev;
+#if LINUX_VERSION_CODE >= 0x10300
 				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 				/* 'skb_put()' points to the start of sk_buff data area. */
 				memcpy(skb_put(skb, pkt_len),
 					   bus_to_virt(vp->rx_ring[entry].addr),
 					   pkt_len);
+#else
+				memcpy(skb->data, bus_to_virt(vp->rx_ring[entry].addr), pkt_len);
+				skb->len = pkt_len;
+#endif
 				rx_copy++;
 			} else{
 				void *temp;
@@ -1734,13 +1779,15 @@
 			vp->stats.rx_packets++;
 		}
 		entry = (++vp->cur_rx) % RX_RING_SIZE;
+		if (--rx_work_limit < 0)
+			break;
 	}
 	/* Refill the Rx ring buffers. */
 	for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) {
 		struct sk_buff *skb;
 		entry = vp->dirty_rx % RX_RING_SIZE;
 		if (vp->rx_skbuff[entry] == NULL) {
-			skb = dev_alloc_skb(PKT_BUF_SZ);
+			skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
 			if (skb == NULL)
 				break;			/* Bad news!  */
 			skb->dev = dev;			/* Mark as being used by this device. */
@@ -1914,7 +1961,7 @@
 static void
 set_rx_mode(struct device *dev)
 {
-	short ioaddr = dev->base_addr;
+	int ioaddr = dev->base_addr;
 	short new_mode;
 
 	if (dev->flags & IFF_PROMISC) {
@@ -1964,4 +2011,3 @@
  *  tab-width: 4
  * End:
  */
-
diff -u --recursive --new-file v2.1.78/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.1.78/linux/drivers/net/de4x5.c	Tue Dec 23 16:30:59 1997
+++ linux/drivers/net/de4x5.c	Sat Jan 10 10:46:51 1998
@@ -364,11 +364,16 @@
 			  Added generic MII PHY functionality to deal with
 			   newer PHY chips.
 			  Fix the mess in 2.1.67.
+      0.532   5-Jan-98    Fix bug in mii_get_phy() reported by 
+                           <redhat@cococo.net>.
+                          Fix bug in pci_probe() for 64 bit systems reported
+			   by <belliott@accessone.com>.
+      0.533   9-Jan-98    Fix more 64 bit bugs reported by <jal@cs.brown.edu>.
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:T0.531 1997/12/21 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.533 1998/1/9 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -763,7 +768,7 @@
     struct {
 	void *priv;                         /* Original kmalloc'd mem addr  */
 	void *buf;                          /* Original kmalloc'd mem addr  */
-	int lock;                           /* Lock the cache accesses      */
+	u_long lock;                        /* Lock the cache accesses      */
 	s32 csr0;                           /* Saved Bus Mode Register      */
 	s32 csr6;                           /* Saved Operating Mode Reg.    */
 	s32 csr7;                           /* Saved IRQ Mask Register      */
@@ -2076,8 +2081,8 @@
 {
     u_char pb, pbus, dev_num, dnum, dev_fn, timer;
     u_short dev_id, vendor, index, status;
-    u_int irq = 0, device, class = DE4X5_CLASS_CODE;
-    u_long iobase;
+    u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE;
+    u_long iobase = 0;                     /* Clear upper 32 bits in Alphas */
     struct bus_type *lp = &bus;
 
     if (lastPCI == NO_MORE_PCI) return;
@@ -2137,8 +2142,8 @@
 
 	    /* Get the board I/O address (64 bits on sparc64) */
 #ifndef __sparc_v9__
-	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, 
-				                               (int *)&iobase);
+	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp);
+	    iobase = tmp;
 #else
 	    iobase = pdev->base_address[0];
 #endif
@@ -2211,8 +2216,8 @@
 {
     u_char pb, dev_fn;
     u_short dev_id, dev_num, vendor, status;
-    u_int irq = 0, device, class = DE4X5_CLASS_CODE;
-    u_long iobase;
+    u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE;
+    u_long iobase = 0;                     /* Clear upper 32 bits in Alphas */
     int i, j;
     struct bus_type *lp = &bus;
 
@@ -2250,8 +2255,8 @@
 
 	/* Get the board I/O address (64 bits on sparc64) */
 #ifndef __sparc_v9__
-	pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, 
-				                               (int *)&iobase);
+	pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp);
+	iobase = tmp;
 #else
 	iobase = pdev->base_address[0];
 #endif
@@ -4985,7 +4990,9 @@
 	    lp->phy[k].spd.reg = GENERIC_REG;      /* ANLPA register         */
 	    lp->phy[k].spd.mask = GENERIC_MASK;    /* 100Mb/s technologies   */
 	    lp->phy[k].spd.value = GENERIC_VALUE;  /* TX & T4, H/F Duplex    */
-	    printk("%s: Found MII device not currently supported. Please mail the following dump to\nthe author:\n", dev->name);
+	    lp->mii_cnt++;
+	    lp->active++;
+	    printk("%s: Using generic MII device control. If the board doesn't operate, \nplease mail the following dump to the author:\n", dev->name);
 	    j = de4x5_debug;
 	    de4x5_debug |= DEBUG_MII;
 	    de4x5_dbg_mii(dev, k);
diff -u --recursive --new-file v2.1.78/linux/drivers/net/dmascc.c linux/drivers/net/dmascc.c
--- v2.1.78/linux/drivers/net/dmascc.c	Wed Dec 10 11:12:43 1997
+++ linux/drivers/net/dmascc.c	Wed Dec 31 16:00:00 1969
@@ -1,1260 +0,0 @@
-/*
- * $Id: dmascc.c,v 1.2 1997/12/02 16:49:49 oe1kib Exp $
- *
- * Driver for high-speed SCC boards (those with DMA support)
- * Copyright (C) 1997 Klaus Kudielka
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-
-#include <linux/module.h>
-#include <linux/dmascc.h>
-#include <linux/errno.h>
-#include <linux/if_arp.h>
-#include <linux/in.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/netdevice.h>
-#include <linux/sockios.h>
-#include <linux/tqueue.h>
-#include <linux/version.h>
-#include <asm/atomic.h>
-#include <asm/bitops.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/segment.h>
-#include <asm/uaccess.h>
-#include <net/ax25.h>
-#include <stdio.h>
-#include "z8530.h"
-
-
-/* Number of buffers per channel */
-
-#define NUM_TX_BUF      2          /* NUM_TX_BUF >= 1 (2 recommended) */
-#define NUM_RX_BUF      2          /* NUM_RX_BUF >= 1 (2 recommended) */
-#define BUF_SIZE        2016
-
-
-/* Cards supported */
-
-#define HW_PI           { "Ottawa PI", 0x300, 0x20, 0x10, 8, \
-                            0, 8, 1843200, 3686400 }
-#define HW_PI2          { "Ottawa PI2", 0x300, 0x20, 0x10, 8, \
-			    0, 8, 3686400, 7372800 }
-#define HW_TWIN         { "Gracilis PackeTwin", 0x200, 0x10, 0x10, 32, \
-			    0, 4, 6144000, 6144000 }
-
-#define HARDWARE        { HW_PI, HW_PI2, HW_TWIN }
-
-#define TYPE_PI         0
-#define TYPE_PI2        1
-#define TYPE_TWIN       2
-#define NUM_TYPES       3
-
-#define MAX_NUM_DEVS    32
-
-
-/* SCC chips supported */
-
-#define Z8530           0
-#define Z85C30          1
-#define Z85230          2
-
-#define CHIPNAMES       { "Z8530", "Z85C30", "Z85230" }
-
-
-/* I/O registers */
-
-/* 8530 registers relative to card base */
-#define SCCB_CMD        0x00
-#define SCCB_DATA       0x01
-#define SCCA_CMD        0x02
-#define SCCA_DATA       0x03
-
-/* 8254 registers relative to card base */
-#define TMR_CNT0        0x00
-#define TMR_CNT1        0x01
-#define TMR_CNT2        0x02
-#define TMR_CTRL        0x03
-
-/* Additional PI/PI2 registers relative to card base */
-#define PI_DREQ_MASK    0x04
-
-/* Additional PackeTwin registers relative to card base */
-#define TWIN_INT_REG    0x08
-#define TWIN_CLR_TMR1   0x09
-#define TWIN_CLR_TMR2   0x0a
-#define TWIN_SPARE_1    0x0b
-#define TWIN_DMA_CFG    0x08
-#define TWIN_SERIAL_CFG 0x09
-#define TWIN_DMA_CLR_FF 0x0a
-#define TWIN_SPARE_2    0x0b
-
-
-/* PackeTwin I/O register values */
-
-/* INT_REG */
-#define TWIN_SCC_MSK       0x01
-#define TWIN_TMR1_MSK      0x02
-#define TWIN_TMR2_MSK      0x04
-#define TWIN_INT_MSK       0x07
-
-/* SERIAL_CFG */
-#define TWIN_DTRA_ON       0x01
-#define TWIN_DTRB_ON       0x02
-#define TWIN_EXTCLKA       0x04
-#define TWIN_EXTCLKB       0x08
-#define TWIN_LOOPA_ON      0x10
-#define TWIN_LOOPB_ON      0x20
-#define TWIN_EI            0x80
-
-/* DMA_CFG */
-#define TWIN_DMA_HDX_T1    0x08
-#define TWIN_DMA_HDX_R1    0x0a
-#define TWIN_DMA_HDX_T3    0x14
-#define TWIN_DMA_HDX_R3    0x16
-#define TWIN_DMA_FDX_T3R1  0x1b
-#define TWIN_DMA_FDX_T1R3  0x1d
-
-
-/* Status values */
-
-/* tx_state */
-#define TX_IDLE    0
-#define TX_OFF     1
-#define TX_TXDELAY 2
-#define TX_ACTIVE  3
-#define TX_SQDELAY 4
-
-
-/* Data types */
-
-struct scc_hardware {
-  char *name;
-  int io_region;
-  int io_delta;
-  int io_size;
-  int num_devs;
-  int scc_offset;
-  int tmr_offset;
-  int tmr_hz;
-  int pclk_hz;
-};
-
-struct scc_priv {
-  char name[10];
-  struct enet_statistics stats;
-  struct scc_info *info;
-  int channel;
-  int cmd, data, tmr;
-  struct scc_param param;
-  char rx_buf[NUM_RX_BUF][BUF_SIZE];
-  int rx_len[NUM_RX_BUF];
-  int rx_ptr;
-  struct tq_struct rx_task;
-  int rx_head, rx_tail, rx_count;
-  int rx_over;
-  char tx_buf[NUM_TX_BUF][BUF_SIZE];
-  int tx_len[NUM_TX_BUF];
-  int tx_ptr;
-  int tx_head, tx_tail, tx_count;
-  int tx_sem, tx_state;
-  unsigned long tx_start;
-  int status;
-};
-
-struct scc_info {
-  int type;
-  int chip;
-  int open;
-  int scc_base;
-  int tmr_base;
-  int twin_serial_cfg;
-  struct device dev[2];
-  struct scc_priv priv[2];
-  struct scc_info *next;
-};
-
-
-/* Function declarations */
-
-int dmascc_init(void) __init;
-static int setup_adapter(int io, int h, int n) __init;
-
-static inline void write_scc(int ctl, int reg, int val);
-static inline int read_scc(int ctl, int reg);
-static int scc_open(struct device *dev);
-static int scc_close(struct device *dev);
-static int scc_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
-static int scc_send_packet(struct sk_buff *skb, struct device *dev);
-static struct enet_statistics *scc_get_stats(struct device *dev);
-static int scc_set_mac_address(struct device *dev, void *sa);
-static void scc_isr(int irq, void *dev_id, struct pt_regs * regs);
-static inline void z8530_isr(struct scc_info *info);
-static void rx_isr(struct device *dev);
-static void special_condition(struct device *dev, int rc);
-static void rx_bh(void *arg);
-static void tx_isr(struct device *dev);
-static void es_isr(struct device *dev);
-static void tm_isr(struct device *dev);
-static inline void delay(struct device *dev, int t);
-static inline unsigned char random(void);
-
-
-/* Initialization variables */
-
-static int io[MAX_NUM_DEVS] __initdata = { 0, };
-/* Beware! hw[] is also used in cleanup_module(). If __initdata also applies
-   to modules, we may not declare hw[] as __initdata */
-static struct scc_hardware hw[NUM_TYPES] __initdata = HARDWARE;
-static char ax25_broadcast[7] __initdata =
-  { 'Q'<<1, 'S'<<1, 'T'<<1, ' '<<1, ' '<<1, ' '<<1, '0'<<1 };
-static char ax25_test[7] __initdata =
-  { 'L'<<1, 'I'<<1, 'N'<<1, 'U'<<1, 'X'<<1, ' '<<1, '1'<<1 };
-
-
-/* Global variables */
-
-static struct scc_info *first = NULL;
-static unsigned long rand;
-
-
-
-/* Module functions */
-
-#ifdef MODULE
-
-
-MODULE_AUTHOR("Klaus Kudielka <oe1kib@oe1xtu.ampr.org>");
-MODULE_DESCRIPTION("Driver for high-speed SCC boards");
-MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NUM_DEVS) "i");
-
-
-int init_module(void)
-{
-  return dmascc_init();
-}
-
-
-void cleanup_module(void)
-{
-  int i;
-  struct scc_info *info;
-
-  while (first) {
-    info = first;
-
-    /* Unregister devices */
-    for (i = 0; i < 2; i++) {
-      if (info->dev[i].name)
-	unregister_netdev(&info->dev[i]);
-    }
-
-    /* Reset board */
-    if (info->type == TYPE_TWIN)
-      outb_p(0, info->dev[0].base_addr + TWIN_SERIAL_CFG);
-    write_scc(info->priv[0].cmd, R9, FHWRES);
-    release_region(info->dev[0].base_addr,
-		   hw[info->type].io_size);
-
-    /* Free memory */
-    first = info->next;
-    kfree_s(info, sizeof(struct scc_info));
-  }
-}
-
-
-#else
-
-
-__initfunc(void dmascc_setup(char *str, int *ints))
-{
-   int i;
-
-   for (i = 0; i < MAX_NUM_DEVS && i < ints[0]; i++)
-      io[i] = ints[i+1];
-}
-
-
-#endif
-
-
-/* Initialization functions */
-
-__initfunc(int dmascc_init(void))
-{
-  int h, i, j, n, base[MAX_NUM_DEVS], tcmd, t0, t1, status;
-  unsigned long time, start[MAX_NUM_DEVS], stop[MAX_NUM_DEVS];
-
-  /* Initialize random number generator */
-  rand = jiffies;
-
-  /* Cards found = 0 */
-  n = 0;
-
-  /* Run autodetection for each card type */
-  for (h = 0; h < NUM_TYPES; h++) {
-
-    if (io[0]) {
-      /* User-specified I/O address regions */
-      for (i = 0; i < hw[h].num_devs; i++) base[i] = 0;
-      for (i = 0; i < MAX_NUM_DEVS && io[i]; i++) {
-	j = (io[i] - hw[h].io_region) / hw[h].io_delta;
-	if (j >= 0 &&
-	    j < hw[h].num_devs && 
-	    hw[h].io_region + j * hw[h].io_delta == io[i])
-	  base[j] = io[i];
-      }
-    } else {
-      /* Default I/O address regions */
-      for (i = 0; i < hw[h].num_devs; i++)
-	base[i] = hw[h].io_region + i * hw[h].io_delta;
-    }
-
-    /* Check valid I/O address regions */
-    for (i = 0; i < hw[h].num_devs; i++)
-      if (base[i] && check_region(base[i], hw[h].io_size))
-	base[i] = 0;
-
-    /* Start timers */
-    for (i = 0; i < hw[h].num_devs; i++)
-      if (base[i]) {
-	tcmd = base[i] + hw[h].tmr_offset + TMR_CTRL;
-	t0   = base[i] + hw[h].tmr_offset + TMR_CNT0;
-	t1   = base[i] + hw[h].tmr_offset + TMR_CNT1;
-	/* Timer 0: LSB+MSB, Mode 3, TMR_0_HZ */
-	outb_p(0x36, tcmd);
-	outb_p((hw[h].tmr_hz/TMR_0_HZ) & 0xFF, t0);
-	outb_p((hw[h].tmr_hz/TMR_0_HZ) >> 8, t0);
-	/* Timer 1: LSB+MSB, Mode 0, HZ/10 */
-	outb_p(0x70, tcmd);
-	outb_p((TMR_0_HZ/HZ*10) & 0xFF, t1);
-	outb_p((TMR_0_HZ/HZ*10) >> 8, t1);
-	/* Timer 2: LSB+MSB, Mode 0 */
-	outb_p(0xb0, tcmd);
-      }
-
-    /* Initialize start values in case we miss the null count bit */
-    time = jiffies;
-    for (i = 0; i < hw[h].num_devs; i++) start[i] = time;
-
-    /* Timing loop */
-    while (jiffies - time < 12) {
-      for (i = 0; i < hw[h].num_devs; i++)
-	if (base[i]) {
-	  /* Read back Timer 1: Status */
-	  outb_p(0xE4, base[i] + hw[h].tmr_offset + TMR_CTRL);
-	  status = inb_p(base[i] + hw[h].tmr_offset + TMR_CNT1);
-	  if ((status & 0x3F) != 0x30) base[i] = 0;
-	  if (status & 0x40) start[i] = jiffies;
-	  if (~status & 0x80) stop[i] = jiffies;
-	}
-    }
-
-    /* Evaluate measurements */
-    for (i = 0; i < hw[h].num_devs; i++)
-      if (base[i]) {
-	time = stop[i] - start[i];
-	if (time < 9 || time > 11)
-	  /* The time expired doesn't match */
-	  base[i] = 0;
-	else {
-	  /* Ok, we have found an adapter */
-	  if (setup_adapter(base[i], h, n) == 0)
-	    n++;
-	}
-      }
-
-  } /* NUM_TYPES */
-
-  /* If any adapter was successfully initialized, return ok */
-  if (n) return 0;
-
-  /* If no adapter found, return error */
-  printk("dmascc: no adapters found\n");
-  return -EIO;
-}
-
-
-__initfunc(int setup_adapter(int io, int h, int n))
-{
-  int i, irq, chip;
-  struct scc_info *info;
-  struct device *dev;
-  struct scc_priv *priv;
-  unsigned long time;
-  unsigned int irqs;
-  int tmr = io + hw[h].tmr_offset;
-  int scc = io + hw[h].scc_offset;
-  int cmd = scc + SCCA_CMD;
-  char *chipnames[] = CHIPNAMES;
-
-  /* Reset 8530 */
-  write_scc(cmd, R9, FHWRES | MIE | NV);
-
-  /* Determine type of chip */
-  write_scc(cmd, R15, 1);
-  if (!read_scc(cmd, R15)) {
-    /* WR7' not present. This is an ordinary Z8530 SCC. */
-    chip = Z8530;
-  } else {
-    /* Put one character in TX FIFO */
-    write_scc(cmd, R8, 0);
-    if (read_scc(cmd, R0) & Tx_BUF_EMP) {
-      /* TX FIFO not full. This is a Z85230 ESCC with a 4-byte FIFO. */
-      chip = Z85230;
-    } else {
-      /* TX FIFO full. This is a Z85C30 SCC with a 1-byte FIFO. */
-      chip = Z85C30;
-    }
-  }
-  write_scc(cmd, R15, 0);
-
-  /* Start IRQ auto-detection */
-  sti();
-  irqs = probe_irq_on();
-
-  /* Enable interrupts */
-  switch (h) {
-  case TYPE_PI:
-  case TYPE_PI2:
-    outb_p(0, io + PI_DREQ_MASK);
-    write_scc(cmd, R15, CTSIE);
-    write_scc(cmd, R0, RES_EXT_INT);
-    write_scc(cmd, R1, EXT_INT_ENAB);
-    break;
-  case TYPE_TWIN:
-    outb_p(0, io + TWIN_DMA_CFG);
-    inb_p(io + TWIN_CLR_TMR1);
-    inb_p(io + TWIN_CLR_TMR2);
-    outb_p(TWIN_EI, io + TWIN_SERIAL_CFG);
-    break;
-  }
-
-  /* Start timer */
-  outb_p(1, tmr + TMR_CNT1);
-  outb_p(0, tmr + TMR_CNT1);
-  /* Wait and detect IRQ */
-  time = jiffies; while (jiffies - time < 2 + HZ / TMR_0_HZ);
-  irq = probe_irq_off(irqs);
-
-  /* Clear pending interrupt, disable interrupts */
-  switch (h) {
-  case TYPE_PI:
-  case TYPE_PI2:
-    write_scc(cmd, R1, 0);
-    write_scc(cmd, R15, 0);
-    write_scc(cmd, R0, RES_EXT_INT);
-    break;
-  case TYPE_TWIN:
-    inb_p(io + TWIN_CLR_TMR1);
-    outb_p(0, io + TWIN_SERIAL_CFG);
-    break;
-  }
-
-  if (irq <= 0) {
-    printk("dmascc: could not find irq of %s at %#3x (irq=%d)\n",
-	   hw[h].name, io, irq);
-    return -1;
-  }
-
-  /* Allocate memory */
-  info = kmalloc(sizeof(struct scc_info), GFP_KERNEL | GFP_DMA);
-  if (!info) {
-    printk("dmascc: could not allocate memory for %s at %#3x\n",
-	   hw[h].name, io);
-    return -1;
-  }
-
-  /* Set up data structures */
-  memset(info, 0, sizeof(struct scc_info));
-  info->type = h;
-  info->chip = chip;
-  info->scc_base = io + hw[h].scc_offset;
-  info->tmr_base = io + hw[h].tmr_offset;
-  info->twin_serial_cfg = 0;
-  for (i = 0; i < 2; i++) {
-    dev = &info->dev[i];
-    priv = &info->priv[i];
-    sprintf(priv->name, "dmascc%i", 2*n+i);
-    priv->info = info;
-    priv->channel = i;
-    priv->cmd = info->scc_base + (i ? SCCB_CMD : SCCA_CMD);
-    priv->data = info->scc_base + (i ? SCCB_DATA : SCCA_DATA);
-    priv->tmr = info->tmr_base + (i ? TMR_CNT2 : TMR_CNT1);
-    priv->param.pclk_hz = hw[h].pclk_hz;
-    priv->param.brg_tc = -1;
-    priv->param.clocks = TCTRxCP | RCRTxCP;
-    priv->param.txdelay = TMR_0_HZ * 10 / 1000;
-    priv->param.txtime = HZ * 3;
-    priv->param.sqdelay = TMR_0_HZ * 1 / 1000;
-    priv->param.slottime = TMR_0_HZ * 10 / 1000;
-    priv->param.waittime = TMR_0_HZ * 100 / 1000;
-    priv->param.persist = 32;
-    priv->rx_task.routine = rx_bh;
-    priv->rx_task.data = dev;
-    dev->priv = priv;
-    dev->name = priv->name;
-    dev->base_addr = io;
-    dev->irq = irq;
-    dev->open = scc_open;
-    dev->stop = scc_close;
-    dev->do_ioctl = scc_ioctl;
-    dev->hard_start_xmit = scc_send_packet;
-    dev->get_stats = scc_get_stats;
-    dev->hard_header = ax25_encapsulate;
-    dev->rebuild_header = ax25_rebuild_header;
-    dev->set_mac_address = scc_set_mac_address;
-    dev->type = ARPHRD_AX25;
-    dev->hard_header_len = 73;
-    dev->mtu = 1500;
-    dev->addr_len = 7;
-    dev->tx_queue_len = 64;
-    memcpy(dev->broadcast, ax25_broadcast, 7);
-    memcpy(dev->dev_addr, ax25_test, 7);
-    dev->flags = 0;
-    dev_init_buffers(dev);
-    if (register_netdev(dev)) {
-      printk("dmascc: could not register %s\n", dev->name);
-      dev->name = NULL;
-    }
-  }
-
-  request_region(io, hw[h].io_size, "dmascc");
-
-  info->next = first;
-  first = info;
-  printk("dmascc: found %s (%s) at %#3x, irq %d\n", hw[h].name,
-	 chipnames[chip], io, irq);
-  return 0;
-}
-
-
-/* Driver functions */
-
-static inline void write_scc(int ctl, int reg, int val)
-{
-  outb_p(reg, ctl);
-  outb_p(val, ctl);
-}
-
-
-static inline int read_scc(int ctl, int reg)
-{
-  outb_p(reg, ctl);
-  return inb_p(ctl);
-}
-
-
-static int scc_open(struct device *dev)
-{
-  struct scc_priv *priv = dev->priv;
-  struct scc_info *info = priv->info;
-  int io = dev->base_addr;
-  int cmd = priv->cmd;
-
-  /* Request IRQ if not already used by other channel */
-  if (!info->open) {
-    if (request_irq(dev->irq, scc_isr, SA_INTERRUPT, "dmascc", info))
-      return -EAGAIN;
-  }
-
-  /* Request DMA if required */
-  if (dev->dma && request_dma(dev->dma, "dmascc")) {
-    if (!info->open) free_irq(dev->irq, info);
-    return -EAGAIN;
-  }
-
-  /* Initialize local variables */
-  dev->tbusy = 0;
-  priv->rx_ptr = 0;
-  priv->rx_over = 0;
-  priv->rx_head = priv->rx_tail = priv->rx_count = 0;
-  priv->tx_state = TX_IDLE;
-  priv->tx_head = priv->tx_tail = priv->tx_count = 0;
-  priv->tx_ptr = 0;
-  priv->tx_sem = 0;
-
-  /* Reset channel */
-  write_scc(cmd, R9, (priv->channel ? CHRB : CHRA) | MIE | NV);
-  /* X1 clock, SDLC mode */
-  write_scc(cmd, R4, SDLC | X1CLK);
-  /* DMA */
-  write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN);
-  /* 8 bit RX char, RX disable */
-  write_scc(cmd, R3, Rx8);
-  /* 8 bit TX char, TX disable */
-  write_scc(cmd, R5, Tx8);
-  /* SDLC address field */
-  write_scc(cmd, R6, 0);
-  /* SDLC flag */
-  write_scc(cmd, R7, FLAG);
-  switch (info->chip) {
-  case Z85C30:
-    /* Select WR7' */
-    write_scc(cmd, R15, 1);
-    /* Auto EOM reset */
-    write_scc(cmd, R7, 0x02);
-    write_scc(cmd, R15, 0);
-    break;
-  case Z85230:
-    /* Select WR7' */
-    write_scc(cmd, R15, 1);
-    /* RX FIFO half full (interrupt only), Auto EOM reset,
-       TX FIFO empty (DMA only) */
-    write_scc(cmd, R7, dev->dma ? 0x22 : 0x0a);
-    write_scc(cmd, R15, 0);
-    break;
-  }
-  /* Preset CRC, NRZ(I) encoding */
-  write_scc(cmd, R10, CRCPS | (priv->param.nrzi ? NRZI : NRZ));
-
-  /* Configure baud rate generator */
-  if (priv->param.brg_tc >= 0) {
-    /* Program BR generator */
-    write_scc(cmd, R12, priv->param.brg_tc & 0xFF);
-    write_scc(cmd, R13, (priv->param.brg_tc>>8) & 0xFF);
-    /* BRG source = SYS CLK; enable BRG; DTR REQ function (required by
-       PackeTwin, not connected on the PI2); set DPLL source to BRG */
-    write_scc(cmd, R14, SSBR | DTRREQ | BRSRC | BRENABL);
-    /* Enable DPLL */
-    write_scc(cmd, R14, SEARCH | DTRREQ | BRSRC | BRENABL);
-  } else {
-    /* Disable BR generator */
-    write_scc(cmd, R14, DTRREQ | BRSRC);
-  }
-
-  /* Configure clocks */
-  if (info->type == TYPE_TWIN) {
-    /* Disable external TX clock receiver */
-    outb_p((info->twin_serial_cfg &=
-	    ~(priv->channel ? TWIN_EXTCLKB : TWIN_EXTCLKA)), 
-	   io + TWIN_SERIAL_CFG);
-  }
-  write_scc(cmd, R11, priv->param.clocks);
-  if ((info->type == TYPE_TWIN) && !(priv->param.clocks & TRxCOI)) {
-    /* Enable external TX clock receiver */
-    outb_p((info->twin_serial_cfg |=
-	    (priv->channel ? TWIN_EXTCLKB : TWIN_EXTCLKA)),
-	   io + TWIN_SERIAL_CFG);
-  }
-
-  /* Configure PackeTwin */
-  if (info->type == TYPE_TWIN) {
-    /* Assert DTR, enable interrupts */
-    outb_p((info->twin_serial_cfg |= TWIN_EI |
-	    (priv->channel ? TWIN_DTRB_ON : TWIN_DTRA_ON)),
-	   io + TWIN_SERIAL_CFG);
-  }
-
-  /* Read current status */
-  priv->status = read_scc(cmd, R0);
-  /* Enable SYNC, DCD, and CTS interrupts */
-  write_scc(cmd, R15, DCDIE | CTSIE | SYNCIE);
-
-  /* Configure PI2 DMA */
-  if (info->type <= TYPE_PI2) outb_p(1, io + PI_DREQ_MASK);
-
-  dev->start = 1;
-  info->open++;
-  MOD_INC_USE_COUNT;
-
-  return 0;
-}
-
-
-static int scc_close(struct device *dev)
-{
-  struct scc_priv *priv = dev->priv;
-  struct scc_info *info = priv->info;
-  int io = dev->base_addr;
-  int cmd = priv->cmd;
-
-  dev->start = 0;
-  info->open--;
-  MOD_DEC_USE_COUNT;
-
-  if (info->type == TYPE_TWIN)
-    /* Drop DTR */
-    outb_p((info->twin_serial_cfg &=
-	    (priv->channel ? ~TWIN_DTRB_ON : ~TWIN_DTRA_ON)),
-	   io + TWIN_SERIAL_CFG);
-
-  /* Reset channel, free DMA */
-  write_scc(cmd, R9, (priv->channel ? CHRB : CHRA) | MIE | NV);
-  if (dev->dma) {
-    if (info->type == TYPE_TWIN) outb_p(0, io + TWIN_DMA_CFG);
-    free_dma(dev->dma);
-  }
-
-  if (!info->open) {
-    if (info->type <= TYPE_PI2) outb_p(0, io + PI_DREQ_MASK);
-    free_irq(dev->irq, info);
-  }
-  return 0;
-}
-
-
-static int scc_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
-{
-  int rc;
-  struct scc_priv *priv = dev->priv;
-  
-  switch (cmd) {
-  case SIOCGSCCPARAM:
-    rc = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct scc_param));
-    if (rc) return rc;
-    copy_to_user(ifr->ifr_data, &priv->param, sizeof(struct scc_param));
-    return 0;
-  case SIOCSSCCPARAM:
-    if (!suser()) return -EPERM;
-    rc = verify_area(VERIFY_READ, ifr->ifr_data, sizeof(struct scc_param));
-    if (rc) return rc;
-    if (dev->start) return -EAGAIN;
-    copy_from_user(&priv->param, ifr->ifr_data, sizeof(struct scc_param));
-    dev->dma = priv->param.dma;
-    return 0;
-  default:
-    return -EINVAL;
-  }
-}
-
-
-static int scc_send_packet(struct sk_buff *skb, struct device *dev)
-{
-  struct scc_priv *priv = dev->priv;
-  struct scc_info *info = priv->info;
-  int cmd = priv->cmd;
-  unsigned long flags;
-  int i;
-
-  /* Block a timer-based transmit from overlapping */
-  if (test_and_set_bit(0, (void *) &priv->tx_sem) != 0) {
-    atomic_inc((void *) &priv->stats.tx_dropped);
-    dev_kfree_skb(skb, FREE_WRITE);
-    return 0;
-  }
-
-  /* Return with an error if we cannot accept more data */
-  if (dev->tbusy) {
-    priv->tx_sem = 0;
-    return -1;
-  }
-
-  /* Transfer data to DMA buffer */
-  i = priv->tx_head;
-  memcpy(priv->tx_buf[i], skb->data+1, skb->len-1);
-  priv->tx_len[i] = skb->len-1;
-
-  save_flags(flags);
-  cli();
-
-  /* Set the busy flag if we just filled up the last buffer */
-  priv->tx_head = (i + 1) % NUM_TX_BUF;
-  priv->tx_count++;
-  if (priv->tx_count == NUM_TX_BUF) dev->tbusy = 1;
-
-  /* Set new TX state */
-  if (priv->tx_state == TX_IDLE) {
-    /* Assert RTS, start timer */
-    priv->tx_state = TX_TXDELAY;
-    if (info->type <= TYPE_PI2) outb_p(0, dev->base_addr + PI_DREQ_MASK);
-    write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8);
-    if (info->type <= TYPE_PI2) outb_p(1, dev->base_addr + PI_DREQ_MASK);
-    priv->tx_start = jiffies;
-    delay(dev, priv->param.txdelay);
-  }
-
-  restore_flags(flags);
-
-  dev_kfree_skb(skb, FREE_WRITE);
-
-  priv->tx_sem = 0;
-  return 0;
-}
-
-
-static struct enet_statistics *scc_get_stats(struct device *dev)
-{
-  struct scc_priv *priv = dev->priv;
-
-  return &priv->stats;
-}
-
-
-static int scc_set_mac_address(struct device *dev, void *sa)
-{
-  memcpy(dev->dev_addr, ((struct sockaddr *)sa)->sa_data, dev->addr_len);
-  return 0;
-}
-
-
-static void scc_isr(int irq, void *dev_id, struct pt_regs * regs)
-{
-  struct scc_info *info = dev_id;
-  int is, io = info->dev[0].base_addr;
-
-  /* We're a fast IRQ handler and are called with interrupts disabled */
-
-  /* IRQ sharing doesn't make sense due to ISA's edge-triggered
-     interrupts, hence it is safe to return if we have found and
-     processed a single device. */
-
-  /* Interrupt processing: We loop until we know that the IRQ line is
-     low. If another positive edge occurs afterwards during the ISR,
-     another interrupt will be triggered by the interrupt controller
-     as soon as the IRQ level is enabled again (see asm/irq.h). */
-
-  switch (info->type) {
-  case TYPE_PI:
-  case TYPE_PI2:
-    outb_p(0, io + PI_DREQ_MASK);
-    z8530_isr(info);
-    outb_p(1, io + PI_DREQ_MASK);
-    return;
-  case TYPE_TWIN:
-    while ((is = ~inb_p(io + TWIN_INT_REG)) &
-	   TWIN_INT_MSK) {
-      if (is & TWIN_SCC_MSK) {
-	z8530_isr(info);
-      } else if (is & TWIN_TMR1_MSK) {
-	inb_p(io + TWIN_CLR_TMR1);
-	tm_isr(&info->dev[0]);
-      } else {
-	inb_p(io + TWIN_CLR_TMR2);
-	tm_isr(&info->dev[1]);
-      }
-    }
-    /* No interrupts pending from the PackeTwin */
-    return;
-  }
-}
-
-
-static inline void z8530_isr(struct scc_info *info)
-{
-  int is, a_cmd;
-  
-  a_cmd = info->scc_base + SCCA_CMD;
-
-  while ((is = read_scc(a_cmd, R3))) {
-    if (is & CHARxIP) {
-      rx_isr(&info->dev[0]);
-    } else if (is & CHATxIP) {
-      tx_isr(&info->dev[0]);
-    } else if (is & CHAEXT) {
-      es_isr(&info->dev[0]);
-    } else if (is & CHBRxIP) {
-      rx_isr(&info->dev[1]);
-    } else if (is & CHBTxIP) {
-      tx_isr(&info->dev[1]);
-    } else {
-      es_isr(&info->dev[1]);
-    }
-  }
-  /* Ok, no interrupts pending from this 8530. The INT line should
-     be inactive now. */
-}
-
-
-static void rx_isr(struct device *dev)
-{
-  struct scc_priv *priv = dev->priv;
-  int cmd = priv->cmd;
-
-  if (dev->dma) {
-    /* Check special condition and perform error reset. See 2.4.7.5. */
-    special_condition(dev, read_scc(cmd, R1));
-    write_scc(cmd, R0, ERR_RES);
-  } else {
-    /* Check special condition for each character. Error reset not necessary.
-       Same algorithm for SCC and ESCC. See 2.4.7.1 and 2.4.7.4. */
-    int rc;
-    while (read_scc(cmd, R0) & Rx_CH_AV) {
-      rc = read_scc(cmd, R1);
-      if (priv->rx_ptr < BUF_SIZE)
-	priv->rx_buf[priv->rx_head][priv->rx_ptr++] = read_scc(cmd, R8);
-      else {
-	priv->rx_over = 2;
-	read_scc(cmd, R8);
-      }
-      special_condition(dev, rc);
-    }
-  }
-}
-
-
-static void special_condition(struct device *dev, int rc)
-{
-  struct scc_priv *priv = dev->priv;
-  int cb, cmd = priv->cmd;
-
-  /* See Figure 2-15. Only overrun and EOF need to be checked. */
-  
-  if (rc & Rx_OVR) {
-    /* Receiver overrun */
-    priv->rx_over = 1;
-    if (!dev->dma) write_scc(cmd, R0, ERR_RES);
-  } else if (rc & END_FR) {
-    /* End of frame. Get byte count */
-    if (dev->dma) {
-	disable_dma(dev->dma);
-	clear_dma_ff(dev->dma);
-	cb = BUF_SIZE - get_dma_residue(dev->dma) - 2;
-    } else {
-	cb = priv->rx_ptr - 2;
-    }
-    if (priv->rx_over) {
-      /* We had an overrun */
-      priv->stats.rx_errors++;
-      if (priv->rx_over == 2) priv->stats.rx_length_errors++;
-      else priv->stats.rx_fifo_errors++;
-      priv->rx_over = 0;
-    } else if (rc & CRC_ERR) {
-      /* Count invalid CRC only if packet length >= minimum */
-      if (cb >= 8) {
-	priv->stats.rx_errors++;
-	priv->stats.rx_crc_errors++;
-      }
-    } else {
-      if (cb >= 8) {
-	/* Put good frame in FIFO */
-	priv->rx_len[priv->rx_head] = cb;
-	priv->rx_head = (priv->rx_head + 1) % NUM_RX_BUF;
-	priv->rx_count++;
-	if (priv->rx_count == NUM_RX_BUF) {
-	  /* Disable receiver if FIFO full */
-	  write_scc(cmd, R3, Rx8);
-	  priv->stats.rx_errors++;
-	  priv->stats.rx_over_errors++;
-	}
-	/* Mark bottom half handler */
-	queue_task(&priv->rx_task, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
-      }
-    }
-    /* Get ready for new frame */
-    if (dev->dma) {
-      set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]);
-      set_dma_count(dev->dma, BUF_SIZE);
-      enable_dma(dev->dma);
-    } else {
-      priv->rx_ptr = 0;
-    }
-  }
-}
-
-
-static void rx_bh(void *arg)
-{
-  struct device *dev = arg;
-  struct scc_priv *priv = dev->priv;
-  struct scc_info *info = priv->info;
-  int cmd = priv->cmd;
-  int i = priv->rx_tail;
-  int cb;
-  unsigned long flags;
-  struct sk_buff *skb;
-  unsigned char *data;
-
-  save_flags(flags);
-  cli();
-
-  while (priv->rx_count) {
-    restore_flags(flags);
-    cb = priv->rx_len[i];
-    /* Allocate buffer */
-    skb = dev_alloc_skb(cb+1);
-    if (skb == NULL) {
-      /* Drop packet */
-      priv->stats.rx_dropped++;
-    } else {
-      /* Fill buffer */
-      data = skb_put(skb, cb+1);
-      data[0] = 0;
-      memcpy(&data[1], priv->rx_buf[i], cb);
-      skb->dev = dev;
-      skb->protocol = ntohs(ETH_P_AX25);
-      skb->mac.raw = skb->data;
-      netif_rx(skb);
-      priv->stats.rx_packets++;
-    }
-    save_flags(flags);
-    cli();
-    /* Enable receiver if RX buffers have been unavailable */
-    if ((priv->rx_count == NUM_RX_BUF) && (priv->status & DCD)) {
-      if (info->type <= TYPE_PI2) outb_p(0, dev->base_addr + PI_DREQ_MASK);
-      write_scc(cmd, R3, RxENABLE | Rx8 | RxCRC_ENAB);
-      if (info->type <= TYPE_PI2) outb_p(1, dev->base_addr + PI_DREQ_MASK);
-    }
-    /* Move tail */
-    priv->rx_tail = i = (i + 1) % NUM_RX_BUF;
-    priv->rx_count--;
-  }
-
-  restore_flags(flags);
-}
-
-
-static void tx_isr(struct device *dev)
-{
-  struct scc_priv *priv = dev->priv;
-  int cmd = priv->cmd;
-  int i = priv->tx_tail, p = priv->tx_ptr;
-
-  /* Suspend TX interrupts if we don't want to send anything.
-     See Figure 2-22. */
-  if (p ==  priv->tx_len[i]) {
-    write_scc(cmd, R0, RES_Tx_P);
-    return;
-  }
-
-  /* Write characters */
-  while ((read_scc(cmd, R0) & Tx_BUF_EMP) && p < priv->tx_len[i]) {
-    write_scc(cmd, R8, priv->tx_buf[i][p++]);
-  }
-  priv->tx_ptr = p;
-
-}
-
-
-static void es_isr(struct device *dev)
-{
-  struct scc_priv *priv = dev->priv;
-  struct scc_info *info = priv->info;
-  int i, cmd = priv->cmd;
-  int st, dst, res;
-
-  /* Read status and reset interrupt bit */
-  st = read_scc(cmd, R0);
-  write_scc(cmd, R0, RES_EXT_INT);
-  dst = priv->status ^ st;
-  priv->status = st;
-
-  /* Since the EOM latch is reset automatically, we assume that
-     it has been zero if and only if we are in the TX_ACTIVE state.
-     Otherwise we follow 2.4.9.6. */
-
-  /* Transmit underrun */
-  if ((priv->tx_state == TX_ACTIVE) && (st & TxEOM)) {
-    /* Get remaining bytes */
-    i = priv->tx_tail;
-    if (dev->dma) {
-      disable_dma(dev->dma);
-      clear_dma_ff(dev->dma);
-      res = get_dma_residue(dev->dma);
-    } else {
-      res = priv->tx_len[i] - priv->tx_ptr;
-      if (res) write_scc(cmd, R0, RES_Tx_P);
-      priv->tx_ptr = 0;
-    }
-    /* Remove frame from FIFO */
-    priv->tx_tail = (i + 1) % NUM_TX_BUF;
-    priv->tx_count--;
-    dev->tbusy = 0;
-    /* Check if another frame is available and we are allowed to transmit */
-    if (priv->tx_count && (jiffies - priv->tx_start) < priv->param.txtime) {
-      if (dev->dma) {
-	set_dma_addr(dev->dma, (int) priv->tx_buf[priv->tx_tail]);
-	set_dma_count(dev->dma, priv->tx_len[priv->tx_tail]);
-	enable_dma(dev->dma);
-      } else {
-	/* If we have an ESCC, we are allowed to write data bytes
-	   immediately. Otherwise we have to wait for the next
-	   TX interrupt. See Figure 2-22. */
-	if (info->chip == Z85230) {
-	  tx_isr(dev);
-	}
-      }
-    } else {
-      /* No frame available. Disable interrupts. */
-      priv->tx_state = TX_SQDELAY;
-      delay(dev, priv->param.sqdelay);
-      write_scc(cmd, R15, DCDIE | CTSIE | SYNCIE);
-      write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN);
-    }
-    /* Update packet statistics */
-    if (res) {
-      priv->stats.tx_errors++;
-      priv->stats.tx_fifo_errors++;
-    } else {
-      priv->stats.tx_packets++;
-    }
-    /* Inform upper layers */
-    mark_bh(NET_BH);
-  }
-
-  /* DCD transition */
-  if ((priv->tx_state < TX_TXDELAY) && (dst & DCD)) {
-    /* Transmitter state change */
-    priv->tx_state = TX_OFF;
-    /* Enable or disable receiver */
-    if (st & DCD) {
-      if (dev->dma) {
-	/* Program DMA controller */
-	disable_dma(dev->dma);
-	clear_dma_ff(dev->dma);
-	set_dma_mode(dev->dma, DMA_MODE_READ);
-	set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]);
-	set_dma_count(dev->dma, BUF_SIZE);
-	enable_dma(dev->dma);
-	/* Configure PackeTwin DMA */
-	if (info->type == TYPE_TWIN) {
-	  outb_p((dev->dma == 1) ? TWIN_DMA_HDX_R1 : TWIN_DMA_HDX_R3,
-		 dev->base_addr + TWIN_DMA_CFG);
-	}
-	/* Sp. cond. intr. only, ext int enable */
-	write_scc(cmd, R1, EXT_INT_ENAB | INT_ERR_Rx |
-		  WT_RDY_RT | WT_FN_RDYFN | WT_RDY_ENAB);
-      } else {
-	/* Intr. on all Rx characters and Sp. cond., ext int enable */
-	write_scc(cmd, R1, EXT_INT_ENAB | INT_ALL_Rx | WT_RDY_RT |
-		  WT_FN_RDYFN);
-      }
-      if (priv->rx_count < NUM_RX_BUF) {
-	/* Enable receiver */
-	write_scc(cmd, R3, RxENABLE | Rx8 | RxCRC_ENAB);
-      }
-    } else {
-      /* Disable DMA */
-      if (dev->dma) disable_dma(dev->dma);
-      /* Disable receiver */
-      write_scc(cmd, R3, Rx8);
-      /* DMA disable, RX int disable, Ext int enable */
-      write_scc(cmd, R1, EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN);
-      /* Transmitter state change */
-      if (random() > priv->param.persist)
-	delay(dev, priv->param.slottime);
-      else {
-	if (priv->tx_count) {
-	  priv->tx_state = TX_TXDELAY;
-	  write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8);
-	  priv->tx_start = jiffies;
-	  delay(dev, priv->param.txdelay);
-	} else {
-	  priv->tx_state = TX_IDLE;
-	}
-      }
-    }
-  }
-
-  /* CTS transition */
-  if ((info->type <= TYPE_PI2) && (dst & CTS) && (~st & CTS)) {
-    /* Timer has expired */
-    tm_isr(dev);
-  }
-
-  /* /SYNC/HUNT transition */
-  if ((dst & SYNC_HUNT) && (~st & SYNC_HUNT)) {
-    /* Reset current frame and clear RX FIFO */
-    while (read_scc(cmd, R0) & Rx_CH_AV) read_scc(cmd, R8);
-    priv->rx_over = 0;
-    if (dev->dma) {
-      disable_dma(dev->dma);
-      clear_dma_ff(dev->dma);
-      set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]);
-      set_dma_count(dev->dma, BUF_SIZE);
-      enable_dma(dev->dma);
-    } else {
-      priv->rx_ptr = 0;
-    }
-  }
-}
-
-
-static void tm_isr(struct device *dev)
-{
-  struct scc_priv *priv = dev->priv;
-  struct scc_info *info = priv->info;
-  int cmd = priv->cmd;
-
-  switch (priv->tx_state) {
-  case TX_OFF:
-    if (~priv->status & DCD) {
-      if (random() > priv->param.persist) delay(dev, priv->param.slottime);
-      else {
-	if (priv->tx_count) {
-	  priv->tx_state = TX_TXDELAY;
-	  write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8);
-	  priv->tx_start = jiffies;
-	  delay(dev, priv->param.txdelay);
-	} else {
-	  priv->tx_state = TX_IDLE;
-	}
-      }
-    }
-    break;
-  case TX_TXDELAY:
-    priv->tx_state = TX_ACTIVE;
-    if (dev->dma) {
-      /* Program DMA controller */
-      disable_dma(dev->dma);
-      clear_dma_ff(dev->dma);
-      set_dma_mode(dev->dma, DMA_MODE_WRITE);
-      set_dma_addr(dev->dma, (int) priv->tx_buf[priv->tx_tail]);
-      set_dma_count(dev->dma, priv->tx_len[priv->tx_tail]);
-      enable_dma(dev->dma);
-      /* Configure PackeTwin DMA */
-      if (info->type == TYPE_TWIN) {
-	outb_p((dev->dma == 1) ? TWIN_DMA_HDX_T1 : TWIN_DMA_HDX_T3,
-	       dev->base_addr + TWIN_DMA_CFG);
-      }
-      /* Enable interrupts and DMA. On the PackeTwin, the DTR//REQ pin
-	 is used for TX DMA requests, but we enable the WAIT/DMA request
-	 pin, anyway */
-      write_scc(cmd, R15, TxUIE | DCDIE | CTSIE | SYNCIE);
-      write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN | WT_RDY_ENAB);
-    } else {
-      write_scc(cmd, R15, TxUIE | DCDIE | CTSIE | SYNCIE);
-      write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN | TxINT_ENAB);
-      tx_isr(dev);
-    }
-    if (info->chip == Z8530) write_scc(cmd, R0, RES_EOM_L);
-    break;
-  case TX_SQDELAY:
-    /* Disable transmitter */
-    write_scc(cmd, R5, TxCRC_ENAB | Tx8);
-    /* Transmitter state change: Switch to TX_OFF and wait at least
-       1 slottime. */
-    priv->tx_state = TX_OFF;    
-    if (~priv->status & DCD) delay(dev, priv->param.waittime);
-  }
-}
-
-
-static inline void delay(struct device *dev, int t)
-{
-  struct scc_priv *priv = dev->priv;
-  int tmr = priv->tmr;
-
-  outb_p(t & 0xFF, tmr);
-  outb_p((t >> 8) & 0xFF, tmr);
-}
-
-
-static inline unsigned char random(void)
-{
-  /* See "Numerical Recipes in C", second edition, p. 284 */
-  rand = rand * 1664525L + 1013904223L;
-  return (unsigned char) (rand >> 24);
-}
-
-
diff -u --recursive --new-file v2.1.78/linux/drivers/net/dummy.c linux/drivers/net/dummy.c
--- v2.1.78/linux/drivers/net/dummy.c	Sat Nov 29 11:25:10 1997
+++ linux/drivers/net/dummy.c	Mon Jan 12 15:28:18 1998
@@ -30,9 +30,9 @@
 
 /* To have statistics (just packets sent) define this */
 
-#include <linux/module.h>
-
+#include <linux/config.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
@@ -73,6 +73,13 @@
 {
 }
 
+#ifdef CONFIG_NET_FASTROUTE
+static int dummy_accept_fastpath(struct device *dev, struct dst_entry *dst)
+{
+	return -1;
+}
+#endif
+
 __initfunc(int dummy_init(struct device *dev))
 {
 	/* Initialize the device structure. */
@@ -92,7 +99,10 @@
 	ether_setup(dev);
 	dev->tx_queue_len = 0;
 	dev->flags |= IFF_NOARP;
-	dev->flags &= ~IFF_BROADCAST;
+	dev->flags &= ~(IFF_BROADCAST|IFF_MULTICAST);
+#ifdef CONFIG_NET_FASTROUTE
+	dev->accept_fastpath = dummy_accept_fastpath;
+#endif
 
 	return 0;
 }
diff -u --recursive --new-file v2.1.78/linux/drivers/net/ipddp.c linux/drivers/net/ipddp.c
--- v2.1.78/linux/drivers/net/ipddp.c	Sun Dec 21 22:36:13 1997
+++ linux/drivers/net/ipddp.c	Mon Jan 12 15:28:18 1998
@@ -195,6 +195,8 @@
         struct ipddp_route *rt;
         struct at_addr *our_addr;
 
+	/* Wow! I'll eat my hat if this routine is really called. --ANK */
+
 	/*
          * Find appropriate route to use, based only on IP number.
          */
diff -u --recursive --new-file v2.1.78/linux/drivers/net/myri_sbus.c linux/drivers/net/myri_sbus.c
--- v2.1.78/linux/drivers/net/myri_sbus.c	Sat Nov 29 11:25:10 1997
+++ linux/drivers/net/myri_sbus.c	Mon Jan 12 15:28:18 1998
@@ -714,7 +714,6 @@
 	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]));
@@ -725,16 +724,6 @@
 	pad[0] = MYRI_PAD_LEN;
 	pad[1] = 0xab;
 
-	/*
-	 *	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
@@ -755,59 +744,31 @@
 	return 0;	
 }
 
-int myri_header_cache(struct dst_entry *dst, struct neighbour *neigh,
-		       struct hh_cache *hh)
+int myri_header_cache(struct neighbour *neigh, struct hh_cache *hh)
 {
 	unsigned short type = hh->hh_type;
 	unsigned char *pad = (unsigned char *)hh->hh_data;
 	struct ethhdr *eth = (struct ethhdr *)(pad + MYRI_PAD_LEN);
-	struct device *dev = dst->dev;
+	struct device *dev = neigh->dev;
 
-	if (type == ETH_P_802_3)
+	if (type == __constant_htons(ETH_P_802_3))
 		return -1;
 
 	/* Refill MyriNet padding identifiers, this is just being anal. */
 	pad[0] = MYRI_PAD_LEN;
 	pad[1] = 0xab;
 
-	eth->h_proto = htons(type);
-
+	eth->h_proto = type;
 	memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
-
-	if (dev->flags & IFF_LOOPBACK) {
-		memset(eth->h_dest, 0, dev->addr_len);
-		hh->hh_uptodate = 1;
-		return 0;
-	}
-
-	if (type != ETH_P_IP) {
-		printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n",
-			dev->name, (int)eth->h_proto);
-		hh->hh_uptodate = 0;
-		return 0;
-	}
-
-#ifdef CONFIG_INET
-	hh->hh_uptodate = arp_find_1(eth->h_dest, dst, neigh);
-#else
-	hh->hh_uptodate = 0;
-#endif
+	memcpy(eth->h_dest, neigh->ha, dev->addr_len);
 	return 0;
 }
 
+
 /* Called by Address Resolution module to notify changes in address. */
-void myri_header_cache_update(struct hh_cache *hh, struct device *dev,
-			      unsigned char * haddr)
+void myri_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr)
 {
-	if (hh->hh_type != ETH_P_IP) {
-		printk(KERN_DEBUG "eth_header_cache_update: %04x cache is not "
-		       "implemented\n", hh->hh_type);
-		return;
-	}
-	hh->hh_data[0] = MYRI_PAD_LEN;
-	hh->hh_data[1] = 0xab;
-	memcpy(hh->hh_data+2, haddr, ETH_ALEN);
-	hh->hh_uptodate = 1;
+	memcpy(((u8*)hh->hh_data) + 2, haddr, dev->addr_len);
 }
 
 static int myri_change_mtu(struct device *dev, int new_mtu)
diff -u --recursive --new-file v2.1.78/linux/drivers/net/sdla_fr.c linux/drivers/net/sdla_fr.c
--- v2.1.78/linux/drivers/net/sdla_fr.c	Sat Nov 29 11:25:10 1997
+++ linux/drivers/net/sdla_fr.c	Mon Jan 12 14:46:16 1998
@@ -1,7 +1,8 @@
 /*****************************************************************************
 * sdla_fr.c	WANPIPE(tm) Multiprotocol WAN Link Driver. Frame relay module.
 *
-* Author:	Gene Kozin	<genek@compuserve.com>
+* Author(s):	Gene Kozin	
+*		Jaspreet Singh		<jaspreet@sangoma.com>
 *
 * Copyright:	(c) 1995-1997 Sangoma Technologies Inc.
 *
@@ -10,26 +11,52 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
-*
-* Jun 29, 1997  Alan Cox	 o Hacked up vendor source 1.0.3 to remove
-*				   C++ style comments, and a massive security
-*				   hole (the UDP management junk).
-*
+* Nov 26, 1997	Jaspreet Singh	o Improved load sharing with multiple boards
+*				o Added Cli() to protect enabling of interrupts
+*				  while polling is called.
+* Nov 24, 1997	Jaspreet Singh	o Added counters to avoid enabling of interrupts
+*				  when they have been disabled by another
+*				  interface or routine (eg. wpf_poll).
+* Nov 06, 1997	Jaspreet Singh	o Added INTR_TEST_MODE to avoid polling	
+*				  routine disable interrupts during interrupt
+*				  testing.
+* Oct 20, 1997  Jaspreet Singh  o Added hooks in for Router UP time.
+* Oct 16, 1997  Jaspreet Singh  o The critical flag is used to maintain flow
+*                                 control by avoiding RACE conditions.  The
+*                                 cli() and restore_flags() are taken out.
+*                                 The fr_channel structure is appended for 
+*                                 Driver Statistics.
+* Oct 15, 1997  Farhan Thawar    o updated if_send() and receive for IPX
+* Aug 29, 1997  Farhan Thawar    o Removed most of the cli() and sti()
+*                                o Abstracted the UDP management stuff
+*                                o Now use tbusy and critical more intelligently
+* Jul 21, 1997  Jaspreet Singh	 o Can configure T391, T392, N391, N392 & N393
+*				   through router.conf.
+*				 o Protected calls to sdla_peek() by adDing 
+*				   save_flags(), cli() and restore_flags().
+*				 o Added error message for Inactive DLCIs in
+*				   fr_event() and update_chan_state().
+*				 o Fixed freeing up of buffers using kfree() 
+*			           when packets are received.
+* Jul 07, 1997	Jaspreet Singh	 o Added configurable TTL for UDP packets 
+*				 o Added ability to discard multicast and 
+*				   broadcast source addressed packets
+* Jun 27, 1997	Jaspreet Singh	 o Added FT1 monitor capabilities 
+*				   New case (0x44) statement in if_send routine *				   Added a global variable rCount to keep track
+*			 	   of FT1 status enabled on the board.
 * May 29, 1997	Jaspreet Singh	 o Fixed major Flow Control Problem
 *				   With multiple boards a problem was seen where
 *				   the second board always stopped transmitting
 *				   packet after running for a while. The code
 *				   got into a stage where the interrupts were
 *				   disabled and dev->tbusy was set to 1.
-*				   This caused the If_send() routine to get into
-*				   the if clause for set_bit(0,dev->tbusy) 
+*                  		   This caused the If_send() routine to get into*                                  the if clause for it(0,dev->tbusy) 
 *				   forever.
 *				   The code got into this stage due to an 
 *				   interrupt occuring within the if clause for 
 *				   set_bit(0,dev->tbusy).  Since an interrupt 
 *				   disables furhter transmit interrupt and 
-* 				   makes dev->tbusy = 0, this effect was undone
-*                                  by making dev->tbusy = 1 in the if clause.
+* 				   makes dev->tbusy = 0, this effect was undone *                                  by making dev->tbusy = 1 in the if clause.
 *				   The Fix checks to see if Transmit interrupts
 *				   are disabled then do not make dev->tbusy = 1
 * 	   			   Introduced a global variable: int_occur and
@@ -51,7 +78,7 @@
 * Jan 30, 1997	Gene Kozin	Version 3.1.0
 *				 o implemented exec() entry point
 *				 o fixed a bug causing driver configured as
-*				   a FR switch to be stuck in WAN_DISCONNECTED
+*				   a FR switch to be stuck in WAN_
 *				   mode
 * Jan 02, 1997	Gene Kozin	Initial version.
 *****************************************************************************/
@@ -60,115 +87,237 @@
 #error	This code MUST be compiled as a kernel module!
 #endif
 
+#include <linux/config.h>	/* OS configuration options */
 #include <linux/kernel.h>	/* printk(), and other useful stuff */
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
 #include <linux/string.h>	/* inline memset(), etc. */
 #include <linux/malloc.h>	/* kmalloc(), kfree() */
+#include <linux/init.h>		/* __initfunc */
 #include <linux/wanrouter.h>	/* WAN router definitions */
 #include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
 #include <linux/if_arp.h>	/* ARPHRD_* defines */
-#include <linux/init.h>		/* __initfunc et al. */
 #include <asm/byteorder.h>	/* htons(), etc. */
 #include <asm/io.h>		/* for inb(), outb(), etc. */
 #include <asm/uaccess.h>
+#include <linux/time.h>	 	/* for do_gettimeofday */	
 
 #define	_GNUC_
 #include <linux/sdla_fr.h>	/* frame relay firmware API definitions */
-
 /****** Defines & Macros ****************************************************/
 
-#define	CMD_OK		0	/* normal firmware return code */
-#define	CMD_TIMEOUT	0xFF	/* firmware command timed out */
-#define	MAX_CMD_RETRY	10	/* max number of firmware retries */
+#define	MAX_CMD_RETRY	10		/* max number of firmware retries */
 
-#define	FR_HEADER_LEN	8	/* max encapsulation header size */
-#define	FR_CHANNEL_MTU	1500	/* unfragmented logical channel MTU */
+#define	FR_HEADER_LEN	8		/* max encapsulation header size */
+#define	FR_CHANNEL_MTU	1500		/* unfragmented logical channel MTU */
 
 /* Q.922 frame types */
-#define	Q922_UI		0x03	/* Unnumbered Info frame */
-#define	Q922_XID	0xAF	/* ??? */
+#define	Q922_UI		0x03		/* Unnumbered Info frame */
+#define	Q922_XID	0xAF		/* ??? */
 
+/* DLCI configured or not */
+#define DLCI_NOT_CONFIGURED	0x00
+#define DLCI_CONFIG_PENDING	0x01
+#define DLCI_CONFIGURED		0x02
+
+/* CIR enabled or not */
+#define CIR_ENABLED	0x00
+#define CIR_DISABLED	0x01
+
+/* Interrupt mode for DLCI = 0 */
+#define BUFFER_INTR_MODE	0x00
+#define DLCI_LIST_INTR_MODE	0x01
+
+/* Transmit Interrupt Status */
+#define DISABLED 		0x00
+#define WAITING_TO_BE_ENABLED	0x01
+
+/* For handle_IPXWAN() */
+#define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b)))
+ 
 /****** Data Structures *****************************************************/
 
 /* This is an extention of the 'struct device' we create for each network
  * interface to keep the rest of channel-specific data.
  */
-typedef struct fr_channel {
-	char name[WAN_IFNAME_SZ + 1];	/* interface name, ASCIIZ */
-	unsigned dlci;		/* logical channel number */
-	unsigned cir;		/* committed information rate */
-	char state;		/* channel state */
+typedef struct fr_channel
+{
+	char name[WAN_IFNAME_SZ+1];	/* interface name, ASCIIZ */
+	unsigned dlci_configured  ;	/* check whether configured or not */
+	unsigned cir_status;		/* check whether CIR enabled or not */
+	unsigned dlci;			/* logical channel number */
+	unsigned cir;			/* committed information rate */
+	unsigned bc;			/* committed burst size */
+	unsigned be;			/* excess burst size */
+	unsigned mc;			/* multicast support on or off */
+	unsigned tx_int_status;		/* Transmit Interrupt Status */	
+	unsigned short pkt_length;	/* Packet Length */
+	unsigned long router_start_time;/* Router start time in seconds */
+	unsigned long tick_counter;	/* counter for transmit time out */
+	char dev_pending_devtint;	/* interface pending dev_tint() */
+	char state;			/* channel state */
+	void* dlci_int_interface;	/* pointer to the DLCI Interface */ 
+	unsigned long IB_addr;		/* physical address of Interface Byte */
 	unsigned long state_tick;	/* time of the last state change */
-	sdla_t *card;		/* -> owner */
-	struct enet_statistics ifstats;		/* interface statistics */
+	sdla_t* card;			/* -> owner */
+	struct enet_statistics ifstats;	/* interface statistics */
+	
+	unsigned long if_send_entry;
+	unsigned long if_send_skb_null;
+	unsigned long if_send_broadcast;
+	unsigned long if_send_multicast;
+	unsigned long if_send_critical_ISR;
+	unsigned long if_send_critical_non_ISR;
+	unsigned long if_send_busy;
+	unsigned long if_send_busy_timeout;
+	unsigned long if_send_FPIPE_request;
+	unsigned long if_send_DRVSTATS_request;
+	unsigned long if_send_wan_disconnected;
+	unsigned long if_send_dlci_disconnected;
+	unsigned long if_send_no_bfrs;
+	unsigned long if_send_adptr_bfrs_full;
+	unsigned long if_send_bfrs_passed_to_adptr;
+
+	unsigned long rx_intr_no_socket;
+	unsigned long rx_intr_dev_not_started;
+	unsigned long rx_intr_FPIPE_request;
+	unsigned long rx_intr_DRVSTATS_request;
+	unsigned long rx_intr_bfr_not_passed_to_stack;
+	unsigned long rx_intr_bfr_passed_to_stack;
+
+	unsigned long UDP_FPIPE_mgmt_kmalloc_err;
+	unsigned long UDP_FPIPE_mgmt_direction_err;
+	unsigned long UDP_FPIPE_mgmt_adptr_type_err;
+	unsigned long UDP_FPIPE_mgmt_adptr_cmnd_OK;
+	unsigned long UDP_FPIPE_mgmt_adptr_cmnd_timeout;
+	unsigned long UDP_FPIPE_mgmt_adptr_send_passed;
+	unsigned long UDP_FPIPE_mgmt_adptr_send_failed;
+	unsigned long UDP_FPIPE_mgmt_not_passed_to_stack;
+	unsigned long UDP_FPIPE_mgmt_passed_to_stack;
+	unsigned long UDP_FPIPE_mgmt_no_socket;
+	unsigned long UDP_DRVSTATS_mgmt_kmalloc_err;
+	unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_OK;
+	unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_timeout;
+	unsigned long UDP_DRVSTATS_mgmt_adptr_send_passed;
+	unsigned long UDP_DRVSTATS_mgmt_adptr_send_failed;
+	unsigned long UDP_DRVSTATS_mgmt_not_passed_to_stack;
+	unsigned long UDP_DRVSTATS_mgmt_passed_to_stack;
+	unsigned long UDP_DRVSTATS_mgmt_no_socket;
+	unsigned long router_up_time;
 } fr_channel_t;
 
-typedef struct dlci_status {
-	unsigned short dlci PACKED;
-	unsigned char state PACKED;
+typedef struct dlci_status
+{
+	unsigned short dlci	PACKED;
+	unsigned char state	PACKED;
 } dlci_status_t;
 
+typedef struct dlci_IB_mapping
+{
+	unsigned short dlci		PACKED;
+	unsigned long  addr_value	PACKED;
+} dlci_IB_mapping_t;
+
+/* This structure is used for DLCI list Tx interrupt mode.  It is used to
+   enable interrupt bit and set the packet length for transmission
+ */
+typedef struct fr_dlci_interface 
+{
+	unsigned char gen_interrupt	PACKED;
+	unsigned short packet_length	PACKED;
+	unsigned char reserved		PACKED;
+} fr_dlci_interface_t; 
+
+static unsigned short num_frames;
+static unsigned long curr_trace_addr;
+static unsigned long start_trace_addr;
+static unsigned short available_buffer_space;
 static char TracingEnabled;
-/* variable for checking interrupts within the ISR routine */
-static int int_occur = 0;
+
+/* variable for keeping track of enabling/disabling FT1 monitor status */
+static int rCount = 0;
+
+extern void disable_irq(unsigned int);
+extern void enable_irq(unsigned int);
+
+/* variable for keeping track of number of interrupts generated during 
+ * interrupt test routine 
+ */
+static int Intr_test_counter;
+
 /****** Function Prototypes *************************************************/
 
 /* WAN link driver entry points. These are called by the WAN router module. */
-static int update(wan_device_t * wandev);
-static int new_if(wan_device_t * wandev, struct device *dev,
-		  wanif_conf_t * conf);
-static int del_if(wan_device_t * wandev, struct device *dev);
+static int update (wan_device_t* wandev);
+static int new_if (wan_device_t* wandev, struct device* dev,
+	wanif_conf_t* conf);
+static int del_if (wan_device_t* wandev, struct device* dev);
 
 /* WANPIPE-specific entry points */
-static int wpf_exec(struct sdla *card, void *u_cmd, void *u_data);
+static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data);
 
 /* Network device interface */
-static int if_init(struct device *dev);
-static int if_open(struct device *dev);
-static int if_close(struct device *dev);
-static int if_header(struct sk_buff *skb, struct device *dev,
-	    unsigned short type, void *daddr, void *saddr, unsigned len);
-static int if_rebuild_hdr(struct sk_buff *skb);
-static int if_send(struct sk_buff *skb, struct device *dev);
-static struct enet_statistics *if_stats(struct device *dev);
+static int if_init   (struct device* dev);
+static int if_open   (struct device* dev);
+static int if_close  (struct device* dev);
+static int if_header (struct sk_buff* skb, struct device* dev,
+	unsigned short type, void* daddr, void* saddr, unsigned len);
+static int if_rebuild_hdr (struct sk_buff* skb);
+static int if_send (struct sk_buff* skb, struct device* dev);
+static struct enet_statistics* if_stats (struct device* dev);
 
 /* Interrupt handlers */
-static void fr502_isr(sdla_t * card);
-static void fr508_isr(sdla_t * card);
-static void fr502_rx_intr(sdla_t * card);
-static void fr508_rx_intr(sdla_t * card);
-static void tx_intr(sdla_t * card);
-static void spur_intr(sdla_t * card);
+static void fr502_isr (sdla_t* card);
+static void fr508_isr (sdla_t* card);
+static void fr502_rx_intr (sdla_t* card);
+static void fr508_rx_intr (sdla_t* card);
+static void tx_intr (sdla_t* card);
+static void spur_intr (sdla_t* card);
 
 /* Background polling routines */
-static void wpf_poll(sdla_t * card);
+static void wpf_poll (sdla_t* card);
 
 /* Frame relay firmware interface functions */
-static int fr_read_version(sdla_t * card, char *str);
-static int fr_configure(sdla_t * card, fr_conf_t * conf);
-static int fr_set_intr_mode(sdla_t * card, unsigned mode, unsigned mtu);
-static int fr_comm_enable(sdla_t * card);
-static int fr_comm_disable(sdla_t * card);
-static int fr_get_err_stats(sdla_t * card);
-static int fr_get_stats(sdla_t * card);
-static int fr_add_dlci(sdla_t * card, int dlci, int num);
-static int fr_activate_dlci(sdla_t * card, int dlci, int num);
-static int fr_issue_isf(sdla_t * card, int isf);
-static int fr502_send(sdla_t * card, int dlci, int attr, int len, void *buf);
-static int fr508_send(sdla_t * card, int dlci, int attr, int len, void *buf);
+static int fr_read_version (sdla_t* card, char* str);
+static int fr_configure (sdla_t* card, fr_conf_t *conf);
+static int fr_dlci_configure (sdla_t* card, fr_dlc_conf_t *conf, unsigned dlci);
+static int fr_set_intr_mode (sdla_t* card, unsigned mode, unsigned mtu);
+static int fr_comm_enable (sdla_t* card);
+static int fr_comm_disable (sdla_t* card);
+static int fr_get_err_stats (sdla_t* card);
+static int fr_get_stats (sdla_t* card);
+static int fr_add_dlci (sdla_t* card, int dlci, int num);
+static int fr_activate_dlci (sdla_t* card, int dlci, int num);
+static int fr_issue_isf (sdla_t* card, int isf);
+static int fr502_send (sdla_t* card, int dlci, int attr, int len, void *buf);
+static int fr508_send (sdla_t* card, int dlci, int attr, int len, void *buf);
 
 /* Firmware asynchronous event handlers */
-static int fr_event(sdla_t * card, int event, fr_mbox_t * mbox);
-static int fr_modem_failure(sdla_t * card, fr_mbox_t * mbox);
-static int fr_dlci_change(sdla_t * card, fr_mbox_t * mbox);
+static int fr_event (sdla_t* card, int event, fr_mbox_t* mbox);
+static int fr_modem_failure (sdla_t *card, fr_mbox_t* mbox);
+static int fr_dlci_change (sdla_t *card, fr_mbox_t* mbox);
 
 /* Miscellaneous functions */
-static int update_chan_state(struct device *dev);
-static void set_chan_state(struct device *dev, int state);
-static struct device *find_channel(sdla_t * card, unsigned dlci);
-static int is_tx_ready(sdla_t * card);
-static unsigned int dec_to_uint(unsigned char *str, int len);
+static int update_chan_state (struct device* dev);
+static void set_chan_state (struct device* dev, int state);
+static struct device* find_channel (sdla_t* card, unsigned dlci);
+static int is_tx_ready (sdla_t* card, fr_channel_t* chan);
+static unsigned int dec_to_uint (unsigned char* str, int len);
+static int reply_udp( unsigned char *data, unsigned int mbox_len );
+
+static int intr_test( sdla_t* card );
+static void init_chan_statistics( fr_channel_t* chan );
+static void init_global_statistics( sdla_t* card );
+static void read_DLCI_IB_mapping( sdla_t* card, fr_channel_t* chan );
+
+/* Udp management functions */
+static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, int dlci, fr_channel_t* chan);
+static int process_udp_driver_call(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, int dlci, fr_channel_t* chan);
+static int udp_pkt_type( struct sk_buff *skb, sdla_t* card );
+
+/* IPX functions */
+static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming);
+static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number);
 
 /****** Public Functions ****************************************************/
 
@@ -198,22 +347,23 @@
 		return -EINVAL;
 	}
 	/* Initialize protocol-specific fields of adapter data space */
-	switch (card->hw.fwid) {
-	case SFID_FR502:
-		card->mbox = (void *) (card->hw.dpmbase + FR502_MBOX_OFFS);
-		card->rxmb = (void *) (card->hw.dpmbase + FR502_RXMB_OFFS);
-		card->flags = (void *) (card->hw.dpmbase + FR502_FLAG_OFFS);
-		card->isr = &fr502_isr;
-		break;
+	switch (card->hw.fwid) 
+	{
+		case SFID_FR502:
+			card->mbox = (void *) (card->hw.dpmbase + FR502_MBOX_OFFS);
+			card->rxmb = (void *) (card->hw.dpmbase + FR502_RXMB_OFFS);
+			card->flags = (void *) (card->hw.dpmbase + FR502_FLAG_OFFS);
+			card->isr = &fr502_isr;
+			break;
 
-	case SFID_FR508:
-		card->mbox = (void *) (card->hw.dpmbase + FR508_MBOX_OFFS);
-		card->flags = (void *) (card->hw.dpmbase + FR508_FLAG_OFFS);
-		card->isr = &fr508_isr;
-		break;
+		case SFID_FR508:
+			card->mbox = (void *) (card->hw.dpmbase + FR508_MBOX_OFFS);
+			card->flags = (void *) (card->hw.dpmbase + FR508_FLAG_OFFS);
+			card->isr = &fr508_isr;
+			break;
 
-	default:
-		return -EINVAL;
+		default:
+			return -EINVAL;
 	}
 
 	/* Read firmware version.  Note that when adapter initializes, it
@@ -243,21 +393,28 @@
 	u.cfg.cir_fwd = 16;
 	u.cfg.cir_bwd = u.cfg.bc_fwd = u.cfg.bc_bwd = u.cfg.cir_fwd;
 	u.cfg.options = 0x0081;	/* direct Rx, no CIR check */
-	switch (conf->u.fr.signalling) {
-	case WANOPT_FR_Q933:
-		u.cfg.options |= 0x0200;
-		break;
-	case WANOPT_FR_LMI:
-		u.cfg.options |= 0x0400;
-		break;
+	
+	switch (conf->u.fr.signalling) 
+	{
+		case WANOPT_FR_Q933:
+			u.cfg.options |= 0x0200;
+			break;
+		case WANOPT_FR_LMI:
+			u.cfg.options |= 0x0400;
+			break;
 	}
-	if (conf->station == WANOPT_CPE) {
+
+	if (conf->station == WANOPT_CPE) 
+	{
 		u.cfg.options |= 0x8000;	/* auto config DLCI */
-	} else {
+	}
+	else
+	{
 		u.cfg.station = 1;	/* switch emulation mode */
 		card->u.f.node_dlci = conf->u.fr.dlci ? conf->u.fr.dlci : 16;
 		card->u.f.dlci_num = min(max(conf->u.fr.dlci_num, 1), 100);
 	}
+
 	if (conf->clocking == WANOPT_INTERNAL)
 		u.cfg.port |= 0x0001
 		    ;
@@ -279,7 +436,8 @@
 		return -EIO
 		    ;
 
-	if (card->hw.fwid == SFID_FR508) {
+	if (card->hw.fwid == SFID_FR508) 
+	{
 		fr_buf_info_t *buf_info =
 		(void *) (card->hw.dpmbase + FR508_RXBC_OFFS);
 
@@ -392,6 +550,32 @@
 		kfree(chan);
 		return err;
 	}
+
+	/* place cir,be,bc and other channel specific information into the
+	 * chan structure 
+         */
+	if (conf->cir) 
+	{
+		chan->cir = max( 1, min( conf->cir, 512 ) );
+		chan->cir_status = CIR_ENABLED; 
+
+		if (conf->bc)
+			chan->bc = max( 1, min( conf->bc, 512 ) );
+		if (conf->be)
+			chan->be = max( 0, min( conf->be, 511) ); 
+
+	}
+	else
+		chan->cir_status = CIR_DISABLED;
+
+	chan->mc = conf->mc;
+
+	chan->dlci_configured = DLCI_NOT_CONFIGURED;	
+
+	chan->tx_int_status = DISABLED;	
+
+	init_chan_statistics( chan );
+
 	/* prepare network device data space for registration */
 	dev->name = chan->name;
 	dev->init = &if_init;
@@ -503,34 +687,146 @@
  *
  * Return 0 if O.k. or errno.
  */
+
 static int if_open(struct device *dev)
 {
 	fr_channel_t *chan = dev->priv;
 	sdla_t *card = chan->card;
+	struct device *dev2;
 	int err = 0;
-
+	fr508_flags_t *flags = card->flags;
+	struct timeval tv;
+	
 	if (dev->start)
-		return -EBUSY	/* only one open is allowed */
-		    ;
+		return -EBUSY;	/* only one open is allowed */
+
 	if (test_and_set_bit(0, (void *) &card->wandev.critical))
 		return -EAGAIN;
-	;
-	if (!card->open_cnt) {
-		if ((fr_comm_enable(card)) ||
-		    (fr_set_intr_mode(card, 0x03, card->wandev.mtu))) {
+
+	if (!card->open_cnt) 
+	{
+		Intr_test_counter = 0;
+		card->intr_mode = INTR_TEST_MODE;
+		err = intr_test( card );
+		
+		if ((err) || (Intr_test_counter !=(MAX_INTR_TEST_COUNTER +1))) {
+			printk(KERN_INFO 
+				"%s: Interrupt Test Failed, Counter: %i\n", 
+				card->devname, Intr_test_counter);
 			err = -EIO;
-			goto done;
+			card->wandev.critical = 0;
+			return err;
+		}
+
+		printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n"
+			,card->devname, Intr_test_counter);
+		
+		/* The following allocates and intializes a circular
+		 * link list of interfaces per card.
+		 */
+		
+		card->devs_struct = kmalloc(sizeof(load_sharing_t), GFP_KERNEL);
+		if (card->devs_struct == NULL)
+			return -ENOMEM;	
+		card->dev_to_devtint_next = card->devs_struct;
+		
+		for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) {
+			(card->devs_struct)->dev_ptr = dev2;
+			if(dev2->slave == NULL)
+				(card->devs_struct)->next = card->dev_to_devtint_next;
+			else {
+				(card->devs_struct)->next = kmalloc(
+					sizeof(load_sharing_t), GFP_KERNEL);
+				if ((card->devs_struct)->next == NULL)
+					return -ENOMEM;	
+				card->devs_struct = (card->devs_struct)->next;
+			}	
+		}
+
+		card->devs_struct = card->dev_to_devtint_next;
+	
+		card->intr_mode = BUFFER_INTR_MODE;	
+
+		/* 
+		check all the interfaces for the device to see if CIR has
+	 	been enabled for any DLCI(s). If so then use the DLCI list
+		Interrupt mode for fr_set_intr_mode(), otherwise use the			default global interrupt mode
+		*/ 
+	
+		for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) {
+			
+			if( ((fr_channel_t *)dev2->priv)->cir_status 
+							== CIR_ENABLED)  {
+				card->intr_mode = DLCI_LIST_INTR_MODE;
+				break;
+			}
+		}
+
+		/* 
+		 *	If you enable comms and then set ints, you get a Tx int as you
+		 *	perform the SET_INT_TRIGGERS command. So, we only set int
+		 *	triggers and then adjust the interrupt mask (to disable Tx ints)		before enabling comms. 
+		 */
+		if (card->intr_mode == BUFFER_INTR_MODE) 
+		{
+			if (fr_set_intr_mode(card, 0x03, card->wandev.mtu)) 
+			{
+				err = -EIO;
+				card->wandev.critical = 0;
+				return err;
+			}
+
+			printk( KERN_INFO 
+				"%s: Global Buffering Tx Interrupt Mode\n"
+				, card->devname);
+
+		} 
+		else if (card->intr_mode == DLCI_LIST_INTR_MODE) 
+		{	
+			if (fr_set_intr_mode(card, 0x83, card->wandev.mtu)) 
+			{
+				err = -EIO;
+				card->wandev.critical = 0;
+				return err;
+			}
+
+			printk( KERN_INFO "%s: DLCI list Tx Interrupt Mode\n", 
+				card->devname);
+
 		}
+
+		flags->imask &= ~0x02;
+	
+		if (fr_comm_enable(card)) 
+		{
+			err = -EIO;
+			card->wandev.critical = 0;
+			return err;
+		}	
+
 		wanpipe_set_state(card, WAN_CONNECTED);
 
-		if (card->wandev.station == WANOPT_CPE) {
+		if (card->wandev.station == WANOPT_CPE) 
+		{
 			/* CPE: issue full status enquiry */
 			fr_issue_isf(card, FR_ISF_FSE);
-		} else {	/* FR switch: activate DLCI(s) */
-			fr_add_dlci(card,
-				card->u.f.node_dlci, card->u.f.dlci_num);
-			fr_activate_dlci(card,
-				card->u.f.node_dlci, card->u.f.dlci_num);
+		}
+		else
+		{	/* FR switch: activate DLCI(s) */
+		
+			/* For Switch emulation we have to ADD and ACTIVATE
+			 * the DLCI(s) that were configured with the SET_DLCI_
+			 * CONFIGURATION command. Add and Activate will fail if
+			 * DLCI specified is not included in the list.
+			 *
+			 * Also If_open is called once for each interface. But
+			 * it does not get in here for all the interface. So
+		 	 * we have to pass the entire list of DLCI(s) to add 
+			 * activate routines.  
+			 */ 
+	
+			fr_add_dlci(card, card->u.f.node_dlci[0], card->u.f.dlci_num);
+			fr_activate_dlci(card, card->u.f.node_dlci, card->u.f.dlci_num);
 		}
 	}
 	dev->mtu = min(dev->mtu, card->wandev.mtu - FR_HEADER_LEN);
@@ -540,7 +836,8 @@
 	wanpipe_open(card);
 	update_chan_state(dev);
 
-      done:
+	do_gettimeofday( &tv );
+	chan->router_start_time = tv.tv_sec;
 	card->wandev.critical = 0;
 	return err;
 }
@@ -636,6 +933,33 @@
 	sdla_t *card = chan->card;
 	int retry = 0, err;
 	struct device *dev2;
+    	fr508_flags_t* adptr_flags = card->flags;
+	fr_dlci_interface_t* dlci_interface = chan->dlci_int_interface;
+	unsigned long host_cpu_flags;
+	int send_data = 0;
+   	++chan->if_send_entry;
+
+    	if (dev->tbusy) 
+    	{
+		/* If our device stays busy for at least 5 seconds then we will
+                 * kick start the device by making dev->tbusy = 0.  We expect
+                 * that our device never stays busy more than 5 seconds. So this                 * is only used as a last resort.
+                 */
+		
+		++chan->if_send_busy;
+		++chan->ifstats.collisions;
+
+		if ((jiffies - chan->tick_counter) < (5*HZ))
+			return 1;
+
+		printk(KERN_INFO "%s: Transmit timed out\n", chan->name);
+		
+		++chan->if_send_busy_timeout;
+
+		/* unbusy all the interfaces on the card */
+       		for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave)
+			dev2->tbusy = 0;
+    	}
 
 	if (test_and_set_bit(0, (void *) &card->wandev.critical)) {
 #ifdef _DEBUG_
@@ -645,60 +969,221 @@
 		dev_kfree_skb(skb, FREE_WRITE);
 		return 0;
 	}
-	if (test_and_set_bit(0, (void *) &dev->tbusy)) {
-#ifdef _DEBUG_
-		printk(KERN_INFO "%s: Tx collision on interface %s!\n",
-		       card->devname, dev->name);
-#endif
-		++chan->ifstats.collisions;
-		++card->wandev.stats.collisions;
+    	disable_irq(card->hw.irq);
+	++card->irq_dis_if_send_count;
 
-		retry = 1;
-		if (card->wandev.tx_int_enabled) {
-			for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) {
-				dev2->tbusy = 1;
+	if (test_and_set_bit(0, (void*)&card->wandev.critical)) 
+	{
+		if (card->wandev.critical == CRITICAL_IN_ISR) 
+		{	
+			++chan->if_send_critical_ISR;
+
+			if (card->intr_mode == DLCI_LIST_INTR_MODE)
+			{
+				/* The enable_tx_int flag is set here so that if
+			 	 * the critical flag is set due to an interrupt 
+				 * then we want to enable transmit interrupts 
+				 * again.
+			 	 */ 
+		
+				card->wandev.enable_tx_int = 1;
+
+				/* Setting this flag to WAITING_TO_BE_ENABLED 
+				 * specifies that interrupt bit has to be 
+				 * enabled for that particular interface. 
+				 * (delayed interrupt)
+			 	 */
+				
+				chan->tx_int_status = WAITING_TO_BE_ENABLED;
+
+				/* This is used for enabling dynamic calculation
+				 * of CIRs relative to the packet length.
+			 	 */
+	
+	       			chan->pkt_length = skb->len;			
+				dev->tbusy = 1;
+				chan->tick_counter = jiffies;
 			}
+			else
+			{
+				card->wandev.enable_tx_int = 1;
+				dev->tbusy = 1;
+				chan->tick_counter = jiffies;
+			}
+			save_flags(host_cpu_flags);
+			cli();
+			if ((!(--card->irq_dis_if_send_count)) && 
+					(!card->irq_dis_poll_count))
+				enable_irq(card->hw.irq);
+			restore_flags(host_cpu_flags);
+			return 1;
 		}
-	} else if (card->wandev.state != WAN_CONNECTED) {
+
+		++chan->if_send_critical_non_ISR;
+		++chan->ifstats.tx_dropped;
+		dev_kfree_skb(skb, FREE_WRITE);
+		save_flags(host_cpu_flags);
+		cli();
+                if ((!(--card->irq_dis_if_send_count)) &&
+					 (!card->irq_dis_poll_count))
+                	enable_irq(card->hw.irq);
+		restore_flags(host_cpu_flags);
+		return 0;
+	}
+ 	
+	card->wandev.critical = 0x21;
+    
+	if (card->wandev.state != WAN_CONNECTED) 
+	{
+		++chan->if_send_wan_disconnected;
 		++chan->ifstats.tx_dropped;
 		++card->wandev.stats.tx_dropped;
-	} else if (chan->state != WAN_CONNECTED) {
+	}
+	else if (chan->state != WAN_CONNECTED)
+	{
+		++chan->if_send_dlci_disconnected;
 		update_chan_state(dev);
 		++chan->ifstats.tx_dropped;
 		++card->wandev.stats.tx_dropped;
-	} else if (!is_tx_ready(card)) {
+	}
+	else if (!is_tx_ready(card, chan)) 
+	{
+		if (card->intr_mode == DLCI_LIST_INTR_MODE )
+		{
+			dlci_interface->gen_interrupt |= 0x40;
+			dlci_interface->packet_length = skb->len;	
+		}  
+       		dev->tbusy = 1;
+		chan->tick_counter = jiffies;
+
+		adptr_flags->imask |= 0x02;
+
+		++ chan->if_send_no_bfrs;
 		retry = 1;
-		if (card->wandev.tx_int_enabled) {
-			for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) {
-				dev2->tbusy = 1;
+	}
+	else 
+	{
+		send_data = 1;
+		/* If it's an IPX packet */
+		if( sendpacket[1] == 0x00 &&
+		    sendpacket[2] == 0x80 &&
+		    sendpacket[6] == 0x81 &&
+		    sendpacket[7] == 0x37) 
+		{
+			if( card->wandev.enable_IPX ) 
+			{
+				switch_net_numbers(sendpacket, 
+					card->wandev.network_number, 0);
+			}
+			else
+			{
+				/* increment some statistic here! */
+				send_data = 0;
 			}
 		}
-	} else {
-		err = (card->hw.fwid == SFID_FR508) ?
-		    fr508_send(card, chan->dlci, 0, skb->len, skb->data) :
-		    fr502_send(card, chan->dlci, 0, skb->len, skb->data);
-		if (err) {
-			++chan->ifstats.tx_errors;
-			++card->wandev.stats.tx_errors;
-		} else {
-			++chan->ifstats.tx_packets;
-			++card->wandev.stats.tx_packets;
+
+		if (send_data)
+		{
+        		err = (card->hw.fwid == SFID_FR508) ?
+        		fr508_send(card, chan->dlci, 0, skb->len, skb->data) :
+        		fr502_send(card, chan->dlci, 0, skb->len, skb->data);
+
+			if (err) 
+			{
+				if (card->intr_mode == DLCI_LIST_INTR_MODE) 
+				{
+					dlci_interface->gen_interrupt |= 0x40;
+					dlci_interface->packet_length = skb->len;	
+				} 
+                		dev->tbusy = 1;
+				chan->tick_counter = jiffies;
+            			adptr_flags->imask |= 0x02;
+				retry = 1;
+           			++ chan->if_send_adptr_bfrs_full;
+				++ chan->ifstats.tx_errors;
+            			++ card->wandev.stats.tx_errors;
+			} 
+			else
+			{
+				++ chan->if_send_bfrs_passed_to_adptr;
+				++chan->ifstats.tx_packets;
+				++card->wandev.stats.tx_packets;
+			}
 		}
 	}
-	if (!retry) {
+
+	if (!retry)
 		dev_kfree_skb(skb, FREE_WRITE);
-		dev->tbusy = 0;
-	}
+
 	card->wandev.critical = 0;
+	save_flags(host_cpu_flags);
+	cli();
+        if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count))
+        	enable_irq(card->hw.irq);
+	restore_flags(host_cpu_flags);
 	return retry;
 }
 
+/*
+ *	If incoming is 0 (outgoing)- if the net numbers is ours make it 0
+ *	if incoming is 1 - if the net number is 0 make it ours 
+ *
+ */
+ 
+static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming)
+{
+	unsigned long pnetwork_number;
+
+	pnetwork_number = (unsigned long)((sendpacket[14] << 24) + 
+			  (sendpacket[15] << 16) + (sendpacket[16] << 8) + 
+			  sendpacket[17]);
+
+	if (!incoming) {
+		if( pnetwork_number == network_number) {
+			sendpacket[14] = sendpacket[15] = sendpacket[16] = 
+					 sendpacket[17] = 0x00;
+		}
+	} else {
+		if( pnetwork_number == 0) {
+			sendpacket[14] = (unsigned char)(network_number >> 24);
+			sendpacket[15] = (unsigned char)((network_number & 
+					 0x00FF0000) >> 16);
+			sendpacket[16] = (unsigned char)((network_number & 
+					 0x0000FF00) >> 8);
+			sendpacket[17] = (unsigned char)(network_number & 
+					 0x000000FF);
+		}
+	}
+
+
+	pnetwork_number = (unsigned long)((sendpacket[26] << 24) + 
+			  (sendpacket[27] << 16) + (sendpacket[28] << 8) + 
+			  sendpacket[29]);
+
+	if( !incoming ) {
+		if( pnetwork_number == network_number) {
+			sendpacket[26] = sendpacket[27] = sendpacket[28] = 
+					 sendpacket[29] = 0x00;
+		}
+	} else {
+		if( pnetwork_number == 0 ) {
+			sendpacket[26] = (unsigned char)(network_number >> 24);
+			sendpacket[27] = (unsigned char)((network_number & 
+					 0x00FF0000) >> 16);
+			sendpacket[28] = (unsigned char)((network_number & 
+					 0x0000FF00) >> 8);
+			sendpacket[29] = (unsigned char)(network_number & 
+					 0x000000FF);
+		}
+	}
+} /* switch_net_numbers */
+
 
 /*============================================================================
  * Get ethernet-style interface statistics.
  * Return a pointer to struct enet_statistics.
  */
-static struct enet_statistics *if_stats(struct device *dev)
+static struct net_device_stats *if_stats(struct device *dev)
 {
 	fr_channel_t *chan = dev->priv;
 
@@ -714,18 +1199,19 @@
 {
 	fr502_flags_t *flags = card->flags;
 
-	switch (flags->iflag) {
-	case 0x01:		/* receive interrupt */
-		fr502_rx_intr(card);
-		break;
+	switch (flags->iflag) 
+	{
+		case 0x01:		/* receive interrupt */
+			fr502_rx_intr(card);
+			break;
 
-	case 0x02:		/* transmit interrupt */
-		flags->imask &= ~0x02;
-		tx_intr(card);
-		break;
+		case 0x02:		/* transmit interrupt */
+			flags->imask &= ~0x02;
+			tx_intr(card);
+			break;
 
-	default:
-		spur_intr(card);
+		default:
+			spur_intr(card);
 	}
 	flags->iflag = 0;
 }
@@ -737,36 +1223,186 @@
 {
 	fr508_flags_t *flags = card->flags;
 	fr_buf_ctl_t *bctl;
+	char *ptr = &flags->iflag;
+	struct device* dev = card->wandev.dev;
+	struct device* dev2;
+	int i;
+	unsigned long host_cpu_flags;
+	unsigned disable_tx_intr =1;
+	fr_channel_t* chan;
+	fr_dlci_interface_t* dlci_interface;	
 
-	if (int_occur) {
-#ifdef _DEBUG_
-		printk(KERN_INFO "%s:Interrupt Occurred within an ISR\n", card->devname);
-#endif
+	/* This flag prevents nesting of interrupts.  See sdla_isr() routine
+         * in sdlamain.c. 
+	 */
+	card->in_isr = 1;
+	
+	++card->statistics.isr_entry;
+
+	if (test_and_set_bit(0, (void*)&card->wandev.critical)) 
+	{
+ 		printk(KERN_INFO "fr508_isr: %s, wandev.critical set to 0x%02X, int type = 0x%02X\n", card->devname, card->wandev.critical, flags->iflag);
+		++card->statistics.isr_already_critical;
+		card->in_isr = 0;
 		return;
 	}
 	int_occur = 1;
+
+	/* For all interrupts set the critical flag to CRITICAL_RX_INTR.
+         * If the if_send routine is called with this flag set it will set
+         * the enable transmit flag to 1. (for a delayed interrupt)
+         */
+	card->wandev.critical = CRITICAL_IN_ISR;
+
+	card->dlci_int_mode_unbusy = 0;
+	card->buff_int_mode_unbusy = 0;	
+
 	switch (flags->iflag) {
-	case 0x01:		/* receive interrupt */
-		fr508_rx_intr(card);
-		break;
+		case 0x01:  /* receive interrupt */
+	    		++card->statistics.isr_rx;
+            		fr508_rx_intr(card);
+            		break;
+
+		case 0x02:  /* transmit interrupt */
+	    		++card->statistics.isr_tx;  
+	    		bctl = (void*)(flags->tse_offs - FR_MB_VECTOR + 
+				card->hw.dpmbase);
+			bctl->flag = 0xA0;
+		
+			if (card->intr_mode == DLCI_LIST_INTR_MODE )
+			{
+				/* Find the structure and make it unbusy */
+				dev = find_channel( card, flags->dlci);
+				dev->tbusy = 0;
+
+				/* This is used to perform devtint at the
+				 * end of the isr 
+				 */
+				card->dlci_int_mode_unbusy = 1;
+	
+				/* check to see if any other interfaces are
+				 * busy. If so then do not disable the tx
+				 * interrupts 
+				 */		 
+				for (dev2 = card->wandev.dev; dev2; 
+						dev2 = dev2->slave)
+				{
+					if ( dev2->tbusy == 1)
+					{
+						disable_tx_intr = 0;
+						break;
+            				}
+				}
+				if (disable_tx_intr)
+					flags->imask &= ~0x02;
+				  
+			}
+			else if (card->intr_mode == BUFFER_INTR_MODE) 
+			{
+				for (dev2 = card->wandev.dev; dev2; 
+							dev2 = dev2->slave) 
+				{
+					if ( !dev2 || !dev2->start ) 
+					{
+						++card->statistics.
+						tx_intr_dev_not_started;
+						continue;
+					}
+					if(dev2->tbusy)
+					{
+						card->buff_int_mode_unbusy = 1;
+						((fr_channel_t*)dev2->priv)->dev_pending_devtint = 1;
+						dev2->tbusy = 0;
+					} else 
+						((fr_channel_t*)dev2->priv)->dev_pending_devtint = 0;
+				}
+				flags->imask &= ~0x02;	
+			}
+            		break;
 
-	case 0x02:		/* transmit interrupt */
-		bctl = (void *) (flags->tse_offs - FR_MB_VECTOR +
-				 card->hw.dpmbase);
-		bctl->flag = 0x90;	/* disable further Tx interrupts */
-		tx_intr(card);
-		break;
+		case 0x08:
+	    		Intr_test_counter++;
+			++card->statistics.isr_intr_test;
+	    		break;	
+
+		default:
+	    		++card->statistics.isr_spurious;
+            		spur_intr(card);
+	    		printk(KERN_INFO "%s: Interrupt Type 0x%02X!\n", 
+				card->devname, flags->iflag);
+	    
+			printk(KERN_INFO "%s: ID Bytes = ",card->devname);
+ 	    		for(i = 0; i < 8; i ++)
+				printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i));
+	   	 	printk(KERN_INFO "\n");	
+            
+			break;
+    	}
 
-	default:
-		spur_intr(card);
-	}
-	int_occur = 0;
+	card->wandev.critical = CRITICAL_INTR_HANDLED;
+	if (card->wandev.enable_tx_int) 
+	{
+		if( card->intr_mode == DLCI_LIST_INTR_MODE )
+		{	
+			for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave)
+			{
+	 			chan = dev2->priv;
+				if ( chan->tx_int_status == 
+						WAITING_TO_BE_ENABLED ) 
+				{
+					dlci_interface = 
+						chan->dlci_int_interface;
+					dlci_interface->gen_interrupt |= 0x40;
+					dlci_interface->packet_length = 
+							chan->pkt_length;	
+					chan->tx_int_status = DISABLED;
+				}
+			}
+		}
+		card->wandev.enable_tx_int = 0;
+		flags->imask |= 0x02;
+		++card->statistics.isr_enable_tx_int;
+   	}
+	save_flags(host_cpu_flags);
+	cli();
+	card->in_isr = 0;
+	card->wandev.critical = 0xD1;
 	flags->iflag = 0;
+	card->wandev.critical = 0;
+	restore_flags(host_cpu_flags);
+
+	/*
+	 *	Device is now ready to send. The instant this is executed the If_Send
+	 *	routine is called. That is why this is put at the bottom of the ISR
+	 *	to prevent a endless loop condition caused by repeated Interrupts and
+	 *	enable_tx_int flag.
+	 */
+
+	if(card->dlci_int_mode_unbusy)
+		dev_tint(dev);
+	
+	if(card->buff_int_mode_unbusy)
+	{
+		for (;;) 
+		{
+			if (((fr_channel_t*)((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint == 1){
+		
+				((fr_channel_t*)((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint=0;
+				dev_tint((card->devs_struct)->dev_ptr);
+			}
+			if ((card->devs_struct)->next == card->dev_to_devtint_next)
+				break;
+			card->devs_struct = (card->devs_struct)->next;
+		}
+		card->devs_struct = (card->dev_to_devtint_next)->next;
+		card->dev_to_devtint_next = card->devs_struct; 
+	}
 }
 
 /*============================================================================
  * Receive interrupt handler.
  */
+
 static void fr502_rx_intr(sdla_t * card)
 {
 	fr_mbox_t *mbox = card->rxmb;
@@ -787,12 +1423,12 @@
 		/* Invalid channel, discard packet */
 		printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n",
 		       card->devname, dlci);
-		goto rx_done;
+		sdla_mapmem(&card->hw, FR_MB_VECTOR);
 	}
 	chan = dev->priv;
 	if (!dev->start) {
 		++chan->ifstats.rx_dropped;
-		goto rx_done;
+		sdla_mapmem(&card->hw, FR_MB_VECTOR);
 	}
 	/* Allocate socket buffer */
 	skb = dev_alloc_skb(len);
@@ -800,7 +1436,7 @@
 		printk(KERN_INFO "%s: no socket buffers available!\n",
 		       card->devname);
 		++chan->ifstats.rx_dropped;
-		goto rx_done;
+		sdla_mapmem(&card->hw, FR_MB_VECTOR);
 	}
 	/* Copy data to the socket buffer */
 	buf = skb_put(skb, len);
@@ -820,7 +1456,6 @@
 		++chan->ifstats.rx_packets;
 		++card->wandev.stats.rx_packets;
 	}
-      rx_done:
 	sdla_mapmem(&card->hw, FR_MB_VECTOR);
 }
 
@@ -835,68 +1470,133 @@
 	fr_channel_t *chan;
 	unsigned dlci, len, offs;
 	void *buf;
+	unsigned rx_count = 0;
+	fr508_flags_t* flags = card->flags;
+	char *ptr = &flags->iflag;
+	int i, err;
 
 	if (frbuf->flag != 0x01) {
 		printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n",
 		       card->devname, (unsigned) frbuf);
+		printk(KERN_INFO "%s: ID Bytes = ",card->devname);
+ 		for(i = 0; i < 8; i ++)
+			printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i));
+		printk(KERN_INFO "\n");
+	
+		++card->statistics.rx_intr_corrupt_rx_bfr;
 		return;
 	}
-	len = frbuf->length;
-	dlci = frbuf->dlci;
-	offs = frbuf->offset;
 
-	/* Find network interface for this packet */
-	dev = find_channel(card, dlci);
-	if (dev == NULL) {
-		/* Invalid channel, discard packet */
-		printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n",
-		       card->devname, dlci);
-		goto rx_done;
-	}
-	chan = dev->priv;
-	if (!dev->start) {
-		++chan->ifstats.rx_dropped;
-		goto rx_done;
-	}
-	/* Allocate socket buffer */
-	skb = dev_alloc_skb(len);
-	if (skb == NULL) {
-		printk(KERN_INFO "%s: no socket buffers available!\n",
-		       card->devname);
-		++chan->ifstats.rx_dropped;
-		goto rx_done;
-	}
-	/* Copy data to the socket buffer */
-	if ((offs + len) > card->u.f.rx_top + 1) {
-		unsigned tmp = card->u.f.rx_top - offs + 1;
+	do
+	{
+		len  = frbuf->length;
+		dlci = frbuf->dlci;
+		offs = frbuf->offset;
+
+		/* Find network interface for this packet */
+		dev = find_channel(card, dlci);
+		chan = dev->priv;
+        
+		if (dev == NULL) 
+		{
+			/* Invalid channel, discard packet */
+			printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n"
+				, card->devname, dlci);
+			++card->statistics.rx_intr_on_orphaned_DLCI; 
+        	}
+		else
+		{
+	   		skb = dev_alloc_skb(len); 
+ 			if (!dev->start || (skb == NULL)) 
+ 			{ 
+				++chan->ifstats.rx_dropped;
+				if(dev->start) 
+				{
+					printk(KERN_INFO 
+					"%s: no socket buffers available!\n", 
+					card->devname);
+		    			++chan->rx_intr_no_socket;
+                		
+				} 
+				else
+					++ chan->rx_intr_dev_not_started;
+			} 
+			else
+			{
+				/* Copy data to the socket buffer */
+				if ((offs + len) > card->u.f.rx_top + 1) 
+				{
+					unsigned tmp = card->u.f.rx_top - 
+							offs + 1;
+
+					buf = skb_put(skb, tmp);
+					sdla_peek(&card->hw, offs, buf, tmp);
+					offs = card->u.f.rx_base;
+                    			len -= tmp;
+                		}
+
+				buf = skb_put(skb, len);
+				sdla_peek(&card->hw, offs, buf, len);
+#ifdef CONFIG_SANGOMA_MANAGER
+				if (management_check(skb,card))
+				{
+					++chan->rx_intr_DRVSTATS_request;
+				}				
+				else
+#endif				
+				if (handle_IPXWAN(skb->data,card->devname, card->wandev.enable_IPX, card->wandev.network_number)) 
+				{
+					if (card->wandev.enable_IPX) 
+					{
+						fr508_send(card, dlci, 0, skb->len, skb->data);
+					} else {
+						/* increment some statistic! */
+					}
+				}
+				else 
+				{
+                    			/* Decapsulate packet and pass it up the
+				   	   protocol stack */
+					skb->dev = dev;
+
+					/* remove hardware header */
+					buf = skb_pull(skb, 1); 
+				
+					if (!wan_type_trans(skb, dev)) 
+					{
+						/* can't decapsulate packet */
+						dev_kfree_skb(skb, FREE_READ);
+						++chan->rx_intr_bfr_not_passed_to_stack;
+ 						++chan->ifstats.rx_errors;
+		                        	++card->wandev.stats.rx_errors;
+					}
+					else
+					{
+						netif_rx(skb);
+						++ chan->rx_intr_bfr_passed_to_stack;
+						++ chan->ifstats.rx_packets;
+						++ card->wandev.stats.rx_packets;
+					}
+                		}
+            		}
+       		}
+
+        	/* Release buffer element and calculate a pointer to the next 
+		   one */
+        	frbuf->flag = 0;
+		card->rxmb = ++frbuf;
+        
+		if ((void*)frbuf > card->u.f.rxmb_last)
+			card->rxmb = card->u.f.rxmb_base;
+
+		/* The loop put in is temporary, that is why the break is
+ 		 * placed here. (?????)
+		 */
+		break;
 
-		buf = skb_put(skb, tmp);
-		sdla_peek(&card->hw, offs, buf, tmp);
-		offs = card->u.f.rx_base;
-		len -= tmp;
-	}
-	buf = skb_put(skb, len);
-	sdla_peek(&card->hw, offs, buf, len);
-	/* Decapsulate packet and pass it up the protocol stack */
-	skb->dev = dev;
-	buf = skb_pull(skb, 1);	/* remove hardware header */
-	if (!wan_type_trans(skb, dev)) {
-		/* can't decapsulate packet */
-		dev_kfree_skb(skb, FREE_READ);
-		++chan->ifstats.rx_errors;
-		++card->wandev.stats.rx_errors;
-	} else {
-		netif_rx(skb);
-		++chan->ifstats.rx_packets;
-		++card->wandev.stats.rx_packets;
-	}
-      rx_done:
-	/* Release buffer element and calculate a pointer to the next one */
-	frbuf->flag = 0;
-	card->rxmb = ++frbuf;
-	if ((void *) frbuf > card->u.f.rxmb_last)
-		card->rxmb = card->u.f.rxmb_base
-		    ;
+	        frbuf = card->rxmb;
+
+    	} while (frbuf->flag && ((++ rx_count) < 4));
 }
 
 /*============================================================================
@@ -908,20 +1608,26 @@
 static void tx_intr(sdla_t * card)
 {
 	struct device *dev = card->wandev.dev;
-	int v = 0;
 
-	for (; dev; dev = dev->slave) {
-		if (!dev || !dev->start)
-			continue;
-		v += dev->tbusy;
+	if (card->intr_mode == BUFFER_INTR_MODE) 
+	{ 
+		for (; dev; dev = dev->slave) 
+		{
+			if ( !dev || !dev->start ) 
+			{ 
+				++ card->statistics.tx_intr_dev_not_started;
+				continue;
+			}
+	
+			dev->tbusy = 0;
+			dev_tint(dev);
+		}
+	}
+	else
+	{
 		dev->tbusy = 0;
+		dev_tint(dev);
 	}
-	card->wandev.tx_int_enabled = 0;
-	if (v)
-		mark_bh(NET_BH);
-/*
-   printk(KERN_INFO "%s: transmit interrupt!\n", card->devname);
- */
 }
 
 /*============================================================================
@@ -935,6 +1641,105 @@
 	printk(KERN_INFO "%s: spurious interrupt!\n", card->devname);
 }
 
+/*
+  Return 0 for non-IPXWAN packet
+         1 for IPXWAN packet or IPX is not enabled!
+
+*/
+
+static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number)
+{
+	int i;
+
+	if( sendpacket[1] == 0x00 &&
+	    sendpacket[2] == 0x80 &&
+	    sendpacket[6] == 0x81 &&
+	    sendpacket[7] == 0x37) { 
+
+		if(!enable_IPX) {
+			return 1;
+		}
+	} else {
+		return 0;
+	}
+
+	if( sendpacket[24] == 0x90 &&
+	    sendpacket[25] == 0x04)
+	{
+		if( sendpacket[10] == 0x02 &&
+		    sendpacket[42] == 0x00)
+		{
+			printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname);
+
+			for(i = 49; sendpacket[i] == 0x00; i += 5)
+			{
+				if( sendpacket[i + 4] != 0x02)
+				{
+					sendpacket[i + 1] = 0;
+				}
+			}
+
+			if( sendpacket[i] == 0x04 )
+			{
+				i += 8;
+			}
+
+			for(; sendpacket[i] == 0x80 ;)
+			{
+				sendpacket[i + 1] = 0;
+				i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4;
+			}
+
+			sendpacket[42] = 0x01;
+
+			printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname);
+		}
+		else if( sendpacket[42] == 0x02 )
+		{
+			printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname);
+
+			sendpacket[42] = 0x03;
+
+			sendpacket[59] = 'F';
+			sendpacket[60] = 'P';
+			sendpacket[61] = 'I';
+			sendpacket[62] = 'P';
+			sendpacket[63] = 'E';
+			sendpacket[64] = '-';
+			sendpacket[65] = CVHexToAscii(network_number >> 28);
+			sendpacket[66] = CVHexToAscii((network_number & 0x0F000000)>> 24);
+			sendpacket[67] = CVHexToAscii((network_number & 0x00F00000)>> 20);
+			sendpacket[68] = CVHexToAscii((network_number & 0x000F0000)>> 16);
+			sendpacket[69] = CVHexToAscii((network_number & 0x0000F000)>> 12);
+			sendpacket[70] = CVHexToAscii((network_number & 0x00000F00)>> 8);
+			sendpacket[71] = CVHexToAscii((network_number & 0x000000F0)>> 4);
+			sendpacket[72] = CVHexToAscii(network_number & 0x0000000F);
+			for(i = 73; i < 107; i+= 1)
+			{
+				sendpacket[i] = 0;
+			}
+
+			printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname);
+		}
+		else
+		{
+			printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname);
+			return 0;
+		}
+
+		sendpacket[43] = (unsigned char)(network_number >> 24);
+		sendpacket[44] = (unsigned char)((network_number & 0x00FF0000) >> 16);
+		sendpacket[45] = (unsigned char)((network_number & 0x0000FF00) >> 8);
+		sendpacket[46] = (unsigned char)(network_number & 0x000000FF);
+
+		return 1;
+	}
+
+	switch_net_numbers(sendpacket, network_number ,1);
+	return 0;
+}
+
+
 /****** Background Polling Routines  ****************************************/
 
 /*============================================================================
@@ -948,18 +1753,40 @@
  * 1. This routine may be called on interrupt context with all interrupts
  *    enabled. Beware!
  */
+
 static void wpf_poll(sdla_t * card)
 {
-	static unsigned long last_poll;
-	fr502_flags_t *flags;
+	fr508_flags_t *flags;
+	unsigned long host_cpu_flags;
 
-	if ((jiffies - last_poll) < HZ)
-		return
-		    ;
+	++card->statistics.poll_entry;
+
+ 	if (((jiffies - card->state_tick) < HZ) || 
+				(card->intr_mode == INTR_TEST_MODE))
+		return;
+
+	disable_irq(card->hw.irq);
+	++card->irq_dis_poll_count;
+
+	if (test_and_set_bit(0, (void *)&card->wandev.critical)) 
+	{
+		++ card->statistics.poll_already_critical;
+		save_flags(host_cpu_flags);
+		cli();
+		if ((!card->irq_dis_if_send_count) &&
+                		(!(--card->irq_dis_poll_count)))
+                	enable_irq(card->hw.irq);
+		restore_flags(host_cpu_flags);
+
+		return;
+	}
+
+	card->wandev.critical = 0x11;
+
+	++ card->statistics.poll_processed;
 
-	flags = card->flags;
 	if (flags->event) {
-		fr_mbox_t *mbox = card->mbox;
+		fr_mbox_t* mbox = card->mbox;
 		int err;
 
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
@@ -968,9 +1795,19 @@
 		if (err)
 			fr_event(card, err, mbox);
 	}
-	last_poll = jiffies;
+
+	card->wandev.critical = 0;
+
+	save_flags(host_cpu_flags);
+	cli();
+        if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count)))
+		enable_irq(card->hw.irq);
+	restore_flags(host_cpu_flags);
+
+	card->state_tick = jiffies;
 }
 
+
 /****** Frame Relay Firmware-Specific Functions *****************************/
 
 /*============================================================================
@@ -1005,17 +1842,17 @@
 {
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
-	int dlci = card->u.f.node_dlci;
 	int dlci_num = card->u.f.dlci_num;
 	int err, i;
 
 	do {
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		memcpy(mbox->data, conf, sizeof(fr_conf_t));
-		if (dlci_num)
-			for (i = 0; i < dlci_num; ++i)
-				((fr_conf_t *) mbox->data)->dlci[i] = dlci + i
-				    ;
+
+		if (dlci_num) for (i = 0; i < dlci_num; ++i)
+			((fr_conf_t*)mbox->data)->dlci[i] = 
+					card->u.f.node_dlci[i]; 
+		
 		mbox->cmd.command = FR_SET_CONFIG;
 		mbox->cmd.length =
 		    sizeof(fr_conf_t) + dlci_num * sizeof(short);
@@ -1026,6 +1863,30 @@
 }
 
 /*============================================================================
+ * Set DLCI configuration.
+ */
+static int fr_dlci_configure (sdla_t* card, fr_dlc_conf_t *conf, unsigned dlci)
+{
+	fr_mbox_t* mbox = card->mbox;
+	int retry = MAX_CMD_RETRY;
+	int err;
+
+	do
+	{
+		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
+		memcpy(mbox->data, conf, sizeof(fr_dlc_conf_t));
+		mbox->cmd.dlci = (unsigned short) dlci; 
+		mbox->cmd.command = FR_SET_CONFIG;
+		mbox->cmd.length = 0x0E;
+
+		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	
+	} while (err && retry--);
+	
+	return err;
+}
+
+/*============================================================================
  * Set interrupt mode.
  */
 static int fr_set_intr_mode(sdla_t * card, unsigned mode, unsigned mtu)
@@ -1112,12 +1973,12 @@
 	while (err && retry-- && fr_event(card, err, mbox));
 
 	if (!err) {
-		fr_comm_stat_t *stats = (void *) mbox->data;
+		fr_comm_stat_t* stats = (void*)mbox->data;
 
-		card->wandev.stats.rx_over_errors = stats->rx_overruns;
-		card->wandev.stats.rx_crc_errors = stats->rx_bad_crc;
-		card->wandev.stats.rx_missed_errors = stats->rx_aborts;
-		card->wandev.stats.rx_length_errors = stats->rx_too_long;
+		card->wandev.stats.rx_over_errors    = stats->rx_overruns;
+		card->wandev.stats.rx_crc_errors     = stats->rx_bad_crc;
+		card->wandev.stats.rx_missed_errors  = stats->rx_aborts;
+		card->wandev.stats.rx_length_errors  = stats->rx_too_long;
 		card->wandev.stats.tx_aborted_errors = stats->tx_aborts;
 	}
 	return err;
@@ -1153,6 +2014,7 @@
 /*============================================================================
  * Add DLCI(s) (Access Node only!). 
  */
+ 
 static int fr_add_dlci(sdla_t * card, int dlci, int num)
 {
 	fr_mbox_t *mbox = card->mbox;
@@ -1164,8 +2026,8 @@
 
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		for (i = 0; i < num; ++i)
-			dlci_list[i] = dlci + i
-			    ;
+			dlci_list[i] = card->u.f.node_dlci[i];
+
 		mbox->cmd.length = num * sizeof(short);
 		mbox->cmd.command = FR_ADD_DLCI;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
@@ -1188,8 +2050,8 @@
 
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		for (i = 0; i < num; ++i)
-			dlci_list[i] = dlci + i
-			    ;
+			dlci_list[i] = card->u.f.node_dlci[i];
+
 		mbox->cmd.length = num * sizeof(short);
 		mbox->cmd.command = FR_ACTIVATE_DLCI;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
@@ -1262,13 +2124,8 @@
 	if (!err) {
 		fr_buf_ctl_t *frbuf = (void *) (*(unsigned long *) mbox->data -
 					FR_MB_VECTOR + card->hw.dpmbase);
-		unsigned long flags;
-
-		save_flags(flags);
-		cli();
 		sdla_poke(&card->hw, frbuf->offset, buf, len);
 		frbuf->flag = 0x01;
-		restore_flags(flags);
 	}
 	return err;
 }
@@ -1284,33 +2141,51 @@
  */
 static int fr_event(sdla_t * card, int event, fr_mbox_t * mbox)
 {
-	switch (event) {
-	case FRRES_MODEM_FAILURE:
-		return fr_modem_failure(card, mbox);
+	fr508_flags_t* flags = card->flags;
+	char *ptr = &flags->iflag;
+	int i;
 
-	case FRRES_CHANNEL_DOWN:
-		wanpipe_set_state(card, WAN_DISCONNECTED);
-		return 1;
+	switch (event) 
+	{
+		case FRRES_MODEM_FAILURE:
+			return fr_modem_failure(card, mbox);
 
-	case FRRES_CHANNEL_UP:
-		wanpipe_set_state(card, WAN_CONNECTED);
-		return 1;
+		case FRRES_CHANNEL_DOWN:
+			wanpipe_set_state(card, WAN_DISCONNECTED);
+			return 1;
 
-	case FRRES_DLCI_CHANGE:
-		return fr_dlci_change(card, mbox);
+		case FRRES_CHANNEL_UP:
+			wanpipe_set_state(card, WAN_CONNECTED);
+			return 1;
 
-	case FRRES_DLCI_MISMATCH:
-		printk(KERN_INFO "%s: DLCI list mismatch!\n", card->devname);
-		return 1;
+		case FRRES_DLCI_CHANGE:
+			return fr_dlci_change(card, mbox);
 
-	case CMD_TIMEOUT:
-		printk(KERN_ERR "%s: command 0x%02X timed out!\n",
-		       card->devname, mbox->cmd.command);
-		break;
+		case FRRES_DLCI_MISMATCH:
+			printk(KERN_INFO "%s: DLCI list mismatch!\n", card->devname);
+			return 1;
 
-	default:
-		printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n",
-		       card->devname, mbox->cmd.command, event);
+		case CMD_TIMEOUT:
+			printk(KERN_ERR "%s: command 0x%02X timed out!\n",
+			       card->devname, mbox->cmd.command);
+			printk(KERN_INFO "%s: ID Bytes = ",card->devname);
+ 	    		for(i = 0; i < 8; i ++)
+				printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i));
+	   	 	printk(KERN_INFO "\n");	
+            		break;
+
+		case FRRES_DLCI_INACTIVE:
+			printk(KERN_ERR "%s: DLCI %u is inactive!\n", 
+				card->devname, mbox->cmd.dlci);
+			break;
+ 
+		case FRRES_CIR_OVERFLOW:
+			break;
+		case FRRES_BUFFER_OVERFLOW:
+			break; 
+		default:
+			printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n",
+				card->devname, mbox->cmd.command, event);
 	}
 	return 0;
 }
@@ -1324,10 +2199,11 @@
 {
 	printk(KERN_INFO "%s: physical link down! (modem error 0x%02X)\n",
 	       card->devname, mbox->data[0]);
-	switch (mbox->cmd.command) {
-	case FR_WRITE:
-	case FR_READ:
-		return 0;
+	switch (mbox->cmd.command) 
+	{
+		case FR_WRITE:
+		case FR_READ:
+			return 0;
 	}
 	return 1;
 }
@@ -1341,25 +2217,91 @@
 {
 	dlci_status_t *status = (void *) mbox->data;
 	int cnt = mbox->cmd.length / sizeof(dlci_status_t);
-
+	fr_dlc_conf_t cfg;
+	fr_channel_t *chan;
+	struct device* dev2;
+	
 	for (; cnt; --cnt, ++status) {
 		unsigned short dlci = status->dlci;
 		struct device *dev = find_channel(card, dlci);
 
-		if (status->state & 0x01) {
-			printk(KERN_INFO
-			       "%s: DLCI %u has been deleted!\n",
-			       card->devname, dlci);
-			if (dev && dev->start)
-				set_chan_state(dev, WAN_DISCONNECTED);
-		} else if (status->state & 0x02) {
-			printk(KERN_INFO
-			       "%s: DLCI %u becomes active!\n",
-			       card->devname, dlci);
-			if (dev && dev->start)
-				set_chan_state(dev, WAN_CONNECTED);
+		if (dev == NULL)
+		{
+			printk(KERN_INFO "%s: CPE contains unconfigured DLCI= %d\n", 
+				card->devname, dlci); 	
+		}
+		else
+		{
+			if (status->state & 0x01) 
+			{
+				printk(KERN_INFO
+				       "%s: DLCI %u has been deleted!\n",
+				       card->devname, dlci);
+				if (dev && dev->start)
+					set_chan_state(dev, WAN_DISCONNECTED);
+			}
+			else if (status->state & 0x02) 
+			{
+				printk(KERN_INFO
+					"%s: DLCI %u becomes active!\n",
+					card->devname, dlci);
+				chan = dev->priv;
+				/* This flag is used for configuring specific 
+				   DLCI(s) when they become active.
+			 	*/ 
+				chan->dlci_configured = DLCI_CONFIG_PENDING;
+			
+				if (dev && dev->start)
+					set_chan_state(dev, WAN_CONNECTED);
+			}
 		}
 	}
+	
+	for (dev2 =card->wandev.dev; dev2; dev2 = dev2->slave)
+	{	
+		chan = dev2->priv;
+		
+		if (chan->dlci_configured == DLCI_CONFIG_PENDING)
+		{
+			memset(&cfg, 0, sizeof(cfg));
+	
+			if ( chan->cir_status == CIR_DISABLED) 
+			{
+				cfg.cir_fwd = cfg.cir_bwd  = 16;
+				cfg.bc_fwd = cfg.bc_bwd = 16;
+				cfg.conf_flags = 0x0001;	
+				printk(KERN_INFO "%s: CIR Disabled for %s\n", 
+					card->devname, chan->name);	
+			}
+			else if (chan->cir_status == CIR_ENABLED) 
+			{	
+				cfg.cir_fwd = cfg.cir_bwd = chan->cir;
+				cfg.bc_fwd  = cfg.bc_bwd  = chan->bc;
+				cfg.be_fwd  = cfg.be_bwd  = chan->be;
+				cfg.conf_flags = 0x0000;
+				printk(KERN_INFO "%s: CIR Enabled for %s\n",
+					card->devname, chan->name);
+			
+			}
+			
+			if (fr_dlci_configure( card, &cfg , chan->dlci))
+			{
+				printk(KERN_INFO 
+					"%s: DLCI Configure failed for %d\n",
+					card->devname, chan->dlci);
+				return 1;	
+			}
+			
+			chan->dlci_configured = DLCI_CONFIGURED;
+			
+			/*
+			 *	Read the interface byte mapping into the channel 
+			 *	structure.
+			 */
+			if (card->intr_mode == DLCI_LIST_INTR_MODE)
+				read_DLCI_IB_mapping( card, chan );
+		}		
+	}
 	return 1;
 }
 
@@ -1375,6 +2317,7 @@
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err;
+	int dlci_found = 0;
 
 	do {
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
@@ -1387,12 +2330,19 @@
 		unsigned short *list = (void *) mbox->data;
 		int cnt = mbox->cmd.length / sizeof(short);
 
-		for (; cnt; --cnt, ++list) {
-			if (*list == chan->dlci) {
+		for (; cnt; --cnt, ++list) 
+		{
+			if (*list == chan->dlci) 
+			{
+				dlci_found = 1;
 				set_chan_state(dev, WAN_CONNECTED);
 				break;
 			}
 		}
+
+		if(!dlci_found) 
+			printk(KERN_INFO "%s: DLCI %u is inactive\n", 
+				card->devname, chan->dlci);
 	}
 	return err;
 }
@@ -1408,22 +2358,25 @@
 
 	save_flags(flags);
 	cli();
-	if (chan->state != state) {
-		switch (state) {
-		case WAN_CONNECTED:
-			printk(KERN_INFO "%s: interface %s connected!\n",
-			       card->devname, dev->name);
-			break;
-
-		case WAN_CONNECTING:
-			printk(KERN_INFO "%s: interface %s connecting...\n",
-			       card->devname, dev->name);
-			break;
 
-		case WAN_DISCONNECTED:
-			printk(KERN_INFO "%s: interface %s disconnected!\n",
-			       card->devname, dev->name);
-			break;
+	if (chan->state != state) 
+	{
+		switch (state) 
+		{
+			case WAN_CONNECTED:
+				printk(KERN_INFO "%s: interface %s connected!\n"
+					, card->devname, dev->name);
+				break;
+			case WAN_CONNECTING:
+				printk(KERN_INFO 
+					"%s: interface %s connecting...\n",
+					card->devname, dev->name);
+				break;
+			case WAN_DISCONNECTED:
+				printk (KERN_INFO 
+					"%s: interface %s disconnected!\n",
+					card->devname, dev->name);
+				break;
 		}
 		chan->state = state;
 	}
@@ -1452,20 +2405,19 @@
  * Return:	1 - Tx buffer(s) available
  *		0 - no buffers available
  */
+
 static int is_tx_ready(sdla_t * card)
 {
-	if (card->hw.fwid == SFID_FR508) {
-		fr508_flags_t *flags = card->flags;
+	if (card->hw.fwid == SFID_FR508) 
+	{
 		unsigned char sb = inb(card->hw.port);
 
 		if (sb & 0x02)
 			return 1;
-		flags->imask |= 0x02;
-		card->wandev.tx_int_enabled = 1;
 	} else {
-		fr502_flags_t *flags = card->flags;
+		fr502_flags_t* flags = card->flags;
 
-		if (flags->tx_ready)
+		if (flags->tx_ready) 
 			return 1;
 		flags->imask |= 0x02;
 	}
@@ -1485,6 +2437,112 @@
 	for (val = 0; len && is_digit(*str); ++str, --len)
 		val = (val * 10) + (*str - (unsigned) '0');
 	return val;
+}
+
+/*==============================================================================
+ * Perform the Interrupt Test by running the READ_CODE_VERSION command MAX_INTR_
+ * TEST_COUNTER times.
+ */
+static int intr_test( sdla_t* card )
+{
+	fr_mbox_t* mb = card->mbox;
+	int err,i;
+
+	/*
+	 *	 The critical flag is unset here because we want to get into the
+	 *	ISR without the flag already set. The If_open sets the flag.
+	 */
+	 
+	card->wandev.critical = 0;
+
+	err = fr_set_intr_mode( card, 0x08, card->wandev.mtu );
+	
+	if (err == CMD_OK) 
+	{
+		for ( i = 0; i < MAX_INTR_TEST_COUNTER; i++ ) 
+		{
+ 			/* Run command READ_CODE_VERSION */
+			memset(&mb->cmd, 0, sizeof(fr_cmd_t));
+			mb->cmd.length  = 0;
+			mb->cmd.command = 0x40;
+			err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
+			
+			if (err != CMD_OK) 
+				fr_event(card, err, mb);
+		}
+	}
+	else
+		return err;	
+
+	err = fr_set_intr_mode( card, 0, card->wandev.mtu );
+
+	if( err != CMD_OK ) 
+		return err;
+
+	card->wandev.critical = 1;
+	return 0;
+}
+
+/*==============================================================================
+ * Initializes the Statistics values in the Sdla_t structure.
+ */
+
+void init_global_statistics( sdla_t* card )
+{
+	/* Intialize global statistics for a card */
+	card->statistics.isr_entry 		  = 0;
+	card->statistics.isr_already_critical 	  = 0;
+	card->statistics.isr_rx			  = 0;
+	card->statistics.isr_tx			  = 0;
+	card->statistics.isr_intr_test		  = 0;
+	card->statistics.isr_spurious		  = 0;
+	card->statistics.isr_enable_tx_int	  = 0;
+	card->statistics.rx_intr_corrupt_rx_bfr	  = 0;
+	card->statistics.rx_intr_on_orphaned_DLCI = 0;
+	card->statistics.tx_intr_dev_not_started  = 0;
+	card->statistics.poll_entry		  = 0;
+	card->statistics.poll_already_critical	  = 0;
+	card->statistics.poll_processed		  = 0;
+}
+
+static void read_DLCI_IB_mapping( sdla_t* card, fr_channel_t* chan )
+{
+	fr_mbox_t* mbox = card->mbox;
+	int retry = MAX_CMD_RETRY;	
+	dlci_IB_mapping_t* result; 
+	int err, counter, found;	
+
+	do {
+		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
+		mbox->cmd.command = FR_READ_DLCI_IB_MAPPING;
+		
+		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	
+	} while (err && retry-- && fr_event(card, err, mbox));
+
+	if( mbox->cmd.result != 0)
+		printk(KERN_INFO "%s: Read DLCI IB Mapping failed\n", 
+			chan->name);
+
+	counter = mbox->cmd.length / sizeof(dlci_IB_mapping_t);
+	result = (void *)mbox->data;
+	
+	found = 0;
+	for (; counter; --counter, ++result) {
+		if ( result->dlci == chan->dlci ) {
+			printk( KERN_INFO "%s: DLCI= %d, IB addr = %lx for %s\n"
+				,card->devname,result->dlci, result->addr_value
+				,chan->name);
+			chan->IB_addr = result->addr_value;
+			chan->dlci_int_interface = (void*)(card->hw.dpmbase + 
+					( chan->IB_addr & 0x00001FFF));
+			found = 1;
+			break;	
+		} 
+	}
+	if (!found)
+		printk( KERN_INFO "%s: DLCI %d not found by IB MAPPING cmd\n", 
+		card->devname, chan->dlci);
 }
 
 /****** End *****************************************************************/
diff -u --recursive --new-file v2.1.78/linux/drivers/net/sdla_ppp.c linux/drivers/net/sdla_ppp.c
--- v2.1.78/linux/drivers/net/sdla_ppp.c	Sat Nov 29 11:25:10 1997
+++ linux/drivers/net/sdla_ppp.c	Mon Jan 12 14:46:16 1998
@@ -1,7 +1,7 @@
 /*****************************************************************************
 * sdla_ppp.c	WANPIPE(tm) Multiprotocol WAN Link Driver. PPP module.
 *
-* Author:	Gene Kozin	<genek@compuserve.com>
+* Author: 	Jaspreet Singh 	<jaspreet@sangoma.com>
 *
 * Copyright:	(c) 1995-1997 Sangoma Technologies Inc.
 *
@@ -10,8 +10,31 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
-* Jun 29, 1997  Alan Cox	o Dumped the idiot UDP management system.
-*
+* Nov 27, 1997	Jaspreet Singh	o Added protection against enabling of irqs 
+*				  while they have been disabled.
+* Nov 24, 1997  Jaspreet Singh  o Fixed another RACE condition caused by
+*                                 disabling and enabling of irqs.
+*                               o Added new counters for stats on disable/enable*                                 IRQs.
+* Nov 10, 1997	Jaspreet Singh	o Initialized 'skb->mac.raw' to 'skb->data'
+*				  before every netif_rx().
+*				o Free up the device structure in del_if().
+* Nov 07, 1997	Jaspreet Singh	o Changed the delay to zero for Line tracing
+*				  command.
+* Oct 20, 1997 	Jaspreet Singh	o Added hooks in for Router UP time.
+* Oct 16, 1997	Jaspreet Singh  o The critical flag is used to maintain flow
+*				  control by avoiding RACE conditions.  The 
+*				  cli() and restore_flags() are taken out.
+*				  A new structure, "ppp_private_area", is added 
+*				  to provide Driver Statistics.   
+* Jul 21, 1997 	Jaspreet Singh	o Protected calls to sdla_peek() by adding 
+*				  save_flags(), cli() and restore_flags().
+* Jul 07, 1997	Jaspreet Singh  o Added configurable TTL for UDP packets
+*				o Added ability to discard mulitcast and
+*				  broacast source addressed packets.
+* Jun 27, 1997 	Jaspreet Singh	o Added FT1 monitor capabilities
+*				  New case (0x25) statement in if_send routine.
+*				  Added a global variable rCount to keep track
+*				  of FT1 status enabled on the board.
 * May 22, 1997	Jaspreet Singh	o Added change in the PPP_SET_CONFIG command for
 *				508 card to reflect changes in the new 
 *				ppp508.sfm for supporting:continous transmission
@@ -59,65 +82,146 @@
 #define	STATIC		static
 #endif
 
-#define	CMD_OK		0	/* normal firmware return code */
-#define	CMD_TIMEOUT	0xFF	/* firmware command timed out */
-
-#define	PPP_DFLT_MTU	1500	/* default MTU */
-#define	PPP_MAX_MTU	4000	/* maximum MTU */
+#define	PPP_DFLT_MTU	1500		/* default MTU */
+#define	PPP_MAX_MTU	4000		/* maximum MTU */
 #define PPP_HDR_LEN	1
 
-#define	CONNECT_TIMEOUT	(90*HZ)	/* link connection timeout */
-#define	HOLD_DOWN_TIME	(30*HZ)	/* link hold down time */
+#define	CONNECT_TIMEOUT	(90*HZ)		/* link connection timeout */
+#define	HOLD_DOWN_TIME	(30*HZ)		/* link hold down time */
+
+/* For handle_IPXWAN() */
+#define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b)))
+ 
+/******Data Structures*****************************************************/
+
+/* This structure is placed in the private data area of the device structure.
+ * The card structure used to occupy the private area but now the following 
+ * structure will incorporate the card structure along with PPP specific data
+ */
+  
+typedef struct ppp_private_area
+{
+	sdla_t* card;	
+	unsigned long router_start_time;	/*router start time in sec */
+	unsigned long tick_counter;		/*used for 5 second counter*/
+	unsigned mc;				/*multicast support on or off*/
+	/* PPP specific statistics */
+	unsigned long if_send_entry;
+	unsigned long if_send_skb_null;
+	unsigned long if_send_broadcast;
+	unsigned long if_send_multicast;
+	unsigned long if_send_critical_ISR;
+	unsigned long if_send_critical_non_ISR;
+	unsigned long if_send_busy;
+	unsigned long if_send_busy_timeout;
+	unsigned long if_send_DRVSTATS_request;
+	unsigned long if_send_PTPIPE_request;
+	unsigned long if_send_wan_disconnected;
+	unsigned long if_send_adptr_bfrs_full;
+	unsigned long if_send_protocol_error;
+	unsigned long if_send_tx_int_enabled;
+	unsigned long if_send_bfr_passed_to_adptr;
+	
+	unsigned long rx_intr_no_socket;
+	unsigned long rx_intr_DRVSTATS_request;
+	unsigned long rx_intr_PTPIPE_request;
+	unsigned long rx_intr_bfr_not_passed_to_stack;
+	unsigned long rx_intr_bfr_passed_to_stack;
+
+	unsigned long UDP_PTPIPE_mgmt_kmalloc_err;
+	unsigned long UDP_PTPIPE_mgmt_adptr_type_err;
+	unsigned long UDP_PTPIPE_mgmt_direction_err;
+	unsigned long UDP_PTPIPE_mgmt_adptr_cmnd_timeout;
+	unsigned long UDP_PTPIPE_mgmt_adptr_cmnd_OK;
+	unsigned long UDP_PTPIPE_mgmt_passed_to_adptr;
+	unsigned long UDP_PTPIPE_mgmt_passed_to_stack;
+	unsigned long UDP_PTPIPE_mgmt_no_socket;	
+	
+	unsigned long UDP_DRVSTATS_mgmt_kmalloc_err;
+	unsigned long UDP_DRVSTATS_mgmt_adptr_type_err;
+	unsigned long UDP_DRVSTATS_mgmt_direction_err;
+	unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_timeout;
+	unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_OK;
+	unsigned long UDP_DRVSTATS_mgmt_passed_to_adptr;
+	unsigned long UDP_DRVSTATS_mgmt_passed_to_stack;
+	unsigned long UDP_DRVSTATS_mgmt_no_socket;	
+
+	unsigned long router_up_time; 
+
+}ppp_private_area_t;
+
+/* variable for keeping track of enabling/disabling FT1 monitor status */
+static int rCount = 0;
+
+extern void disable_irq(unsigned int);
+extern void enable_irq(unsigned int);
 
 /****** Function Prototypes *************************************************/
 
 /* WAN link driver entry points. These are called by the WAN router module. */
-static int update(wan_device_t * wandev);
-static int new_if(wan_device_t * wandev, struct device *dev,
-		  wanif_conf_t * conf);
-static int del_if(wan_device_t * wandev, struct device *dev);
+static int update (wan_device_t* wandev);
+static int new_if (wan_device_t* wandev, struct device* dev,
+	wanif_conf_t* conf);
+static int del_if (wan_device_t* wandev, struct device* dev);
 
 /* WANPIPE-specific entry points */
-static int wpp_exec(struct sdla *card, void *u_cmd, void *u_data);
+static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data);
 
 /* Network device interface */
-static int if_init(struct device *dev);
-static int if_open(struct device *dev);
-static int if_close(struct device *dev);
-static int if_header(struct sk_buff *skb, struct device *dev,
-	    unsigned short type, void *daddr, void *saddr, unsigned len);
-static int if_rebuild_hdr(struct sk_buff *skb);
-static int if_send(struct sk_buff *skb, struct device *dev);
-static struct enet_statistics *if_stats(struct device *dev);
+static int if_init   (struct device* dev);
+static int if_open   (struct device* dev);
+static int if_close  (struct device* dev);
+static int if_header (struct sk_buff* skb, struct device* dev,
+	unsigned short type, void* daddr, void* saddr, unsigned len);
+static int if_rebuild_hdr (struct sk_buff* skb);
+static int if_send (struct sk_buff* skb, struct device* dev);
+static struct enet_statistics* if_stats (struct device* dev);
+
 
 /* PPP firmware interface functions */
-static int ppp_read_version(sdla_t * card, char *str);
-static int ppp_configure(sdla_t * card, void *data);
-static int ppp_set_intr_mode(sdla_t * card, unsigned mode);
-static int ppp_comm_enable(sdla_t * card);
-static int ppp_comm_disable(sdla_t * card);
-static int ppp_get_err_stats(sdla_t * card);
-static int ppp_send(sdla_t * card, void *data, unsigned len, unsigned proto);
-static int ppp_error(sdla_t * card, int err, ppp_mbox_t * mb);
+static int ppp_read_version (sdla_t* card, char* str);
+static int ppp_configure (sdla_t* card, void* data);
+static int ppp_set_intr_mode (sdla_t* card, unsigned mode);
+static int ppp_comm_enable (sdla_t* card);
+static int ppp_comm_disable (sdla_t* card);
+static int ppp_get_err_stats (sdla_t* card);
+static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto);
+static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb);
 
 /* Interrupt handlers */
-STATIC void wpp_isr(sdla_t * card);
-static void rx_intr(sdla_t * card);
-static void tx_intr(sdla_t * card);
+STATIC void wpp_isr (sdla_t* card);
+static void rx_intr (sdla_t* card);
+static void tx_intr (sdla_t* card);
 
 /* Background polling routines */
-static void wpp_poll(sdla_t * card);
-static void poll_active(sdla_t * card);
-static void poll_connecting(sdla_t * card);
-static void poll_disconnected(sdla_t * card);
+static void wpp_poll (sdla_t* card);
+static void poll_active (sdla_t* card);
+static void poll_connecting (sdla_t* card);
+static void poll_disconnected (sdla_t* card);
 
 /* Miscellaneous functions */
-static int config502(sdla_t * card);
-static int config508(sdla_t * card);
-static void show_disc_cause(sdla_t * card, unsigned cause);
-static unsigned char bps_to_speed_code(unsigned long bps);
+static int config502 (sdla_t* card);
+static int config508 (sdla_t* card);
+static void show_disc_cause (sdla_t* card, unsigned cause);
+static unsigned char bps_to_speed_code (unsigned long bps);
+static int reply_udp( unsigned char *data, unsigned int mbox_len );
+static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, ppp_private_area_t* ppp_priv_area);
+static int process_udp_driver_call(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, ppp_private_area_t* ppp_priv_area); 
+static void init_ppp_tx_rx_buff( sdla_t* card );
+static int intr_test( sdla_t* card );
+static int udp_pkt_type( struct sk_buff *skb , sdla_t* card);
+static void init_ppp_priv_struct( ppp_private_area_t* ppp_priv_area);
+static void init_global_statistics( sdla_t* card );
 
+static int  Intr_test_counter;
 static char TracingEnabled;
+static unsigned long curr_trace_addr;
+static unsigned long start_trace_addr;
+static unsigned short available_buffer_space;
+
+/* IPX functions */
+static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming);
+static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto);
 /****** Public Functions ****************************************************/
 
 /*============================================================================
@@ -132,32 +236,38 @@
  * Return:	0	o.k.
  *		< 0	failure.
  */
-__initfunc(int wpp_init(sdla_t * card, wandev_conf_t * conf))
+int wpp_init (sdla_t* card, wandev_conf_t* conf)
 {
-	union {
+	union
+	{
 		char str[80];
 	} u;
 
 	/* Verify configuration ID */
 	if (conf->config_id != WANCONFIG_PPP) {
+		
 		printk(KERN_INFO "%s: invalid configuration ID %u!\n",
-		       card->devname, conf->config_id);
+			card->devname, conf->config_id);
 		return -EINVAL;
+
 	}
+
 	/* Initialize protocol-specific fields */
 	switch (card->hw.fwid) {
-	case SFID_PPP502:
-		card->mbox = (void *) (card->hw.dpmbase + PPP502_MB_OFFS);
-		card->flags = (void *) (card->hw.dpmbase + PPP502_FLG_OFFS);
-		break;
-
-	case SFID_PPP508:
-		card->mbox = (void *) (card->hw.dpmbase + PPP508_MB_OFFS);
-		card->flags = (void *) (card->hw.dpmbase + PPP508_FLG_OFFS);
-		break;
 
-	default:
-		return -EINVAL;
+		case SFID_PPP502:
+			card->mbox =(void*)(card->hw.dpmbase + PPP502_MB_OFFS);
+			card->flags=(void*)(card->hw.dpmbase + PPP502_FLG_OFFS);
+			break;
+
+		case SFID_PPP508:
+			card->mbox =(void*)(card->hw.dpmbase + PPP508_MB_OFFS);
+			card->flags=(void*)(card->hw.dpmbase + PPP508_FLG_OFFS);
+			break;
+
+		default:
+			return -EINVAL;
+
 	}
 
 	/* Read firmware version.  Note that when adapter initializes, it
@@ -166,28 +276,39 @@
 	 * around this, we execute the first command twice.
 	 */
 	if (ppp_read_version(card, NULL) || ppp_read_version(card, u.str))
-		return -EIO
-		    ;
-	printk(KERN_INFO "%s: running PPP firmware v%s\n",
-	       card->devname, u.str);
-
+		return -EIO;
+	
+	printk(KERN_INFO "%s: running PPP firmware v%s\n",card->devname, u.str); 
 	/* Adjust configuration and set defaults */
 	card->wandev.mtu = (conf->mtu) ?
-	    min(conf->mtu, PPP_MAX_MTU) : PPP_DFLT_MTU
-	    ;
-	card->wandev.bps = conf->bps;
-	card->wandev.interface = conf->interface;
-	card->wandev.clocking = conf->clocking;
-	card->wandev.station = conf->station;
-	card->isr = &wpp_isr;
-	card->poll = &wpp_poll;
-	card->exec = &wpp_exec;
-	card->wandev.update = &update;
-	card->wandev.new_if = &new_if;
-	card->wandev.del_if = &del_if;
-	card->wandev.state = WAN_DISCONNECTED;
-	card->wandev.udp_port = conf->udp_port;
-	TracingEnabled = '0';
+		min(conf->mtu, PPP_MAX_MTU) : PPP_DFLT_MTU;
+
+	card->wandev.bps	= conf->bps;
+	card->wandev.interface	= conf->interface;
+	card->wandev.clocking	= conf->clocking;
+	card->wandev.station	= conf->station;
+	card->isr		= &wpp_isr;
+	card->poll		= &wpp_poll;
+	card->exec		= &wpp_exec;
+	card->wandev.update	= &update;
+	card->wandev.new_if	= &new_if;
+	card->wandev.del_if	= &del_if;
+	card->wandev.state	= WAN_DISCONNECTED;
+        card->wandev.udp_port   = conf->udp_port;
+	card->wandev.ttl	= conf->ttl;
+	card->irq_dis_if_send_count = 0;
+        card->irq_dis_poll_count = 0;
+        TracingEnabled          = 0;
+
+	card->wandev.enable_IPX = conf->enable_IPX;
+	if (conf->network_number)
+		card->wandev.network_number = conf->network_number;
+	else
+		card->wandev.network_number = 0xDEADBEEF;
+
+	/* initialize global statistics */
+	init_global_statistics( card );
+
 	return 0;
 }
 
@@ -229,33 +350,59 @@
  * Return:	0	o.k.
  *		< 0	failure (channel will not be created)
  */
-static int new_if(wan_device_t * wandev, struct device *dev, wanif_conf_t * conf)
+static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf)
 {
-	sdla_t *card = wandev->private;
+	sdla_t* card = wandev->private;
+	ppp_private_area_t* ppp_priv_area;
 
 	if (wandev->ndev)
-		return -EEXIST
-		    ;
+		return -EEXIST;
+	
 	if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) {
+
 		printk(KERN_INFO "%s: invalid interface name!\n",
-		       card->devname);
+			card->devname);
 		return -EINVAL;
+
 	}
+
+	/* allocate and initialize private data */
+	ppp_priv_area = kmalloc(sizeof(ppp_private_area_t), GFP_KERNEL);
+	
+	if( ppp_priv_area == NULL )
+		return	-ENOMEM;
+	
+	memset(ppp_priv_area, 0, sizeof(ppp_private_area_t));
+	
+	ppp_priv_area->card = card; 
+	
 	/* initialize data */
 	strcpy(card->u.p.if_name, conf->name);
 
+	/* initialize data in ppp_private_area structure */
+	
+	init_ppp_priv_struct( ppp_priv_area );
+
+	ppp_priv_area->mc = conf->mc;
+
 	/* prepare network device data space for registration */
 	dev->name = card->u.p.if_name;
 	dev->init = &if_init;
-	dev->priv = card;
+	dev->priv = ppp_priv_area;
 	return 0;
 }
 
 /*============================================================================
  * Delete logical channel.
  */
-static int del_if(wan_device_t * wandev, struct device *dev)
+static int del_if (wan_device_t* wandev, struct device* dev)
 {
+	if (dev->priv) {
+
+                kfree(dev->priv);
+                dev->priv = NULL;
+        }
+
 	return 0;
 }
 
@@ -298,39 +445,42 @@
  * interface registration.  Returning anything but zero will fail interface
  * registration.
  */
-static int if_init(struct device *dev)
+static int if_init (struct device* dev)
 {
-	sdla_t *card = dev->priv;
-	wan_device_t *wandev = &card->wandev;
+	ppp_private_area_t* ppp_priv_area = dev->priv;
+	sdla_t* card = ppp_priv_area->card;
+	wan_device_t* wandev = &card->wandev;
 	int i;
 
 	/* Initialize device driver entry points */
-	dev->open = &if_open;
-	dev->stop = &if_close;
-	dev->hard_header = &if_header;
-	dev->rebuild_header = &if_rebuild_hdr;
-	dev->hard_start_xmit = &if_send;
-	dev->get_stats = &if_stats;
+	dev->open		= &if_open;
+	dev->stop		= &if_close;
+	dev->hard_header	= &if_header;
+	dev->rebuild_header	= &if_rebuild_hdr;
+	dev->hard_start_xmit	= &if_send;
+	dev->get_stats		= &if_stats;
+
 
 	/* Initialize media-specific parameters */
-	dev->family = AF_INET;	/* address family */
-	dev->type = ARPHRD_PPP;	/* ARP h/w type */
-	dev->mtu = wandev->mtu;
-	dev->hard_header_len = PPP_HDR_LEN;	/* media header length */
+	dev->family		= AF_INET;	/* address family */
+	dev->type		= ARPHRD_PPP;	/* ARP h/w type */
+	dev->mtu		= wandev->mtu;
+	dev->hard_header_len	= PPP_HDR_LEN;	/* media header length */
 
 	/* Initialize hardware parameters (just for reference) */
-	dev->irq = wandev->irq;
-	dev->dma = wandev->dma;
-	dev->base_addr = wandev->ioport;
-	dev->mem_start = wandev->maddr;
-	dev->mem_end = wandev->maddr + wandev->msize - 1;
-
-	/* Set transmit buffer queue length */
-	dev->tx_queue_len = 30;
-
+	dev->irq		= wandev->irq;
+	dev->dma		= wandev->dma;
+	dev->base_addr		= wandev->ioport;
+	dev->mem_start		= wandev->maddr;
+	dev->mem_end		= wandev->maddr + wandev->msize - 1;
+
+        /* Set transmit buffer queue length */
+        dev->tx_queue_len = 100;
+   
 	/* Initialize socket buffers */
 	for (i = 0; i < DEV_NUMBUFFS; ++i)
 		skb_queue_head_init(&dev->buffs[i]);
+	
 	return 0;
 }
 
@@ -341,64 +491,73 @@
  *
  * Return 0 if O.k. or errno.
  */
-static int if_open(struct device *dev)
+static int if_open (struct device* dev)
 {
-	sdla_t *card = dev->priv;
+	ppp_private_area_t* ppp_priv_area = dev->priv;
+	sdla_t* card = ppp_priv_area->card;
+	ppp_flags_t* flags = card->flags;
+	struct timeval tv;
 	int err = 0;
 
 	if (dev->start)
-		return -EBUSY	/* only one open is allowed */
-		    ;
-	if (test_and_set_bit(0, (void *) &card->wandev.critical))
+		return -EBUSY;		/* only one open is allowed */
+	
+	if (test_and_set_bit(0, (void*)&card->wandev.critical))
 		return -EAGAIN;
-	;
-	if ((card->hw.fwid == SFID_PPP502) ? config502(card) : config508(card)) {
+	
+	if ((card->hw.fwid == SFID_PPP502) ? config502(card) : config508(card)){
+
+		err = -EIO;
+		card->wandev.critical = 0;
+		return err;
+
+	}
+
+	Intr_test_counter = 0;
+	err = intr_test( card );
+
+	if( (err) || (Intr_test_counter != (MAX_INTR_TEST_COUNTER + 1))) {
+
+		printk(KERN_INFO "%s: Interrupt Test Failed, Counter: %i\n", 
+			card->devname, Intr_test_counter);
 		err = -EIO;
-		goto split;
+		card->wandev.critical = 0;
+		return err;
+
 	}
+	
+	printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n", 
+		card->devname, Intr_test_counter);
+	
 	/* Initialize Rx/Tx buffer control fields */
-	if (card->hw.fwid == SFID_PPP502) {
-		ppp502_buf_info_t *info =
-		(void *) (card->hw.dpmbase + PPP502_BUF_OFFS);
-
-		card->u.p.txbuf_base = (void *) (card->hw.dpmbase +
-						 info->txb_offs);
-		card->u.p.txbuf_last = (ppp_buf_ctl_t *) card->u.p.txbuf_base +
-		    (info->txb_num - 1);
-		card->u.p.rxbuf_base = (void *) (card->hw.dpmbase +
-						 info->rxb_offs);
-		card->u.p.rxbuf_last = (ppp_buf_ctl_t *) card->u.p.rxbuf_base +
-		    (info->rxb_num - 1);
-	} else {
-		ppp508_buf_info_t *info =
-		(void *) (card->hw.dpmbase + PPP508_BUF_OFFS);
+	init_ppp_tx_rx_buff( card );
+
+	if (ppp_set_intr_mode(card, 0x03)) {
+	
+		err = -EIO;
+		card->wandev.critical = 0;
+		return err;
 
-		card->u.p.txbuf_base = (void *) (card->hw.dpmbase +
-				       (info->txb_ptr - PPP508_MB_VECT));
-		card->u.p.txbuf_last = (ppp_buf_ctl_t *) card->u.p.txbuf_base +
-		    (info->txb_num - 1);
-		card->u.p.rxbuf_base = (void *) (card->hw.dpmbase +
-				       (info->rxb_ptr - PPP508_MB_VECT));
-		card->u.p.rxbuf_last = (ppp_buf_ctl_t *) card->u.p.rxbuf_base +
-		    (info->rxb_num - 1);
-		card->u.p.rx_base = info->rxb_base;
-		card->u.p.rx_top = info->rxb_end;
 	}
-	card->u.p.txbuf = card->u.p.txbuf_base;
-	card->rxmb = card->u.p.rxbuf_base;
 
-	if (ppp_set_intr_mode(card, 0x03) || ppp_comm_enable(card)) {
+	flags->imask &= ~0x02;
+
+	if (ppp_comm_enable(card)) {
+
 		err = -EIO;
-		goto split;
+		card->wandev.critical = 0;
+		return err;
+
 	}
+	
 	wanpipe_set_state(card, WAN_CONNECTING);
 	wanpipe_open(card);
 	dev->mtu = min(dev->mtu, card->wandev.mtu);
 	dev->interrupt = 0;
 	dev->tbusy = 0;
 	dev->start = 1;
-
-      split:
+	do_gettimeofday( &tv );
+	ppp_priv_area->router_start_time = tv.tv_sec;
 	card->wandev.critical = 0;
 	return err;
 }
@@ -408,13 +567,14 @@
  * o if this is the last open, then disable communications and interrupts.
  * o reset flags.
  */
-static int if_close(struct device *dev)
+static int if_close (struct device* dev)
 {
-	sdla_t *card = dev->priv;
+	ppp_private_area_t* ppp_priv_area = dev->priv;
+	sdla_t* card = ppp_priv_area->card;
 
-	if (test_and_set_bit(0, (void *) &card->wandev.critical))
+	if (test_and_set_bit(0, (void*)&card->wandev.critical))
 		return -EAGAIN;
-	;
+	
 	dev->start = 0;
 	wanpipe_close(card);
 	wanpipe_set_state(card, WAN_DISCONNECTED);
@@ -433,18 +593,21 @@
  *
  * Return:	media header length.
  */
-static int if_header(struct sk_buff *skb, struct device *dev,
-	     unsigned short type, void *daddr, void *saddr, unsigned len)
+static int if_header (struct sk_buff* skb, struct device* dev,
+	unsigned short type, void* daddr, void* saddr, unsigned len)
 {
-	switch (type) {
-	case ETH_P_IP:
-	case ETH_P_IPX:
-		skb->protocol = type;
-		break;
+	switch (type)
+	{
+		case ETH_P_IP:
+	
+		case ETH_P_IPX:
+			skb->protocol = type;
+			break;
 
-	default:
-		skb->protocol = 0;
+		default:
+			skb->protocol = 0;
 	}
+
 	return PPP_HDR_LEN;
 }
 
@@ -454,12 +617,13 @@
  * Return:	1	physical address resolved.
  *		0	physical address not resolved
  */
-static int if_rebuild_hdr(struct sk_buff *skb)
+static int if_rebuild_hdr (struct sk_buff* skb)
 {
-	sdla_t *card = skb->dev->priv;
+	ppp_private_area_t* ppp_priv_area = skb->dev->priv;
+	sdla_t* card = ppp_priv_area->card;
 
 	printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
-	       card->devname, skb->dev->name);
+		card->devname, dev->name);
 	return 1;
 }
 
@@ -480,55 +644,238 @@
  * 2. Setting tbusy flag will inhibit further transmit requests from the
  *    protocol stack and can be used for flow control with protocol layer.
  */
-static int if_send(struct sk_buff *skb, struct device *dev)
+static int if_send (struct sk_buff* skb, struct device* dev)
 {
-	sdla_t *card = dev->priv;
+	ppp_private_area_t* ppp_priv_area = dev->priv;
+	sdla_t* card = ppp_priv_area->card;
+	unsigned char *sendpacket;
+	unsigned long check_braddr, check_mcaddr;
+	unsigned long host_cpu_flags;
+	ppp_flags_t* flags = card->flags;
 	int retry = 0;
+	int err, udp_type;
+
+	++ppp_priv_area->if_send_entry;
+
+	if (skb == NULL) {
+
+		/* If we get here, some higher layer thinks we've missed an
+		 * tx-done interrupt.
+		 */
+		printk(KERN_INFO "%s: interface %s got kicked!\n",
+			card->devname, dev->name);
+		
+		++ppp_priv_area->if_send_skb_null;
+		
+		dev_tint(dev);
+		return 0;
 
-	if (test_and_set_bit(0, (void *) &card->wandev.critical)) {
-#ifdef _DEBUG_
-		printk(KERN_INFO "%s: if_send() hit critical section!\n",
-		       card->devname);
-#endif
-		return 1;
 	}
-	if (test_and_set_bit(0, (void *) &dev->tbusy)) {
-#ifdef _DEBUG_
-		printk(KERN_INFO "%s: Tx collision on interface %s!\n",
-		       card->devname, dev->name);
+
+	if (dev->tbusy) {
+
+		/* If our device stays busy for at least 5 seconds then we will
+		 * kick start the device by making dev->tbusy = 0.  We expect 
+		 * that our device never stays busy more than 5 seconds. So this
+		 * is only used as a last resort. 
+		 */
+              
+		++ppp_priv_area->if_send_busy;
+        	++card->wandev.stats.collisions;
+
+		if ((jiffies - ppp_priv_area->tick_counter) < (5*HZ)) {
+			return 1;
+		}
+
+		printk (KERN_INFO "%s: Transmit times out\n",card->devname);
+	
+		++ppp_priv_area->if_send_busy_timeout;
+
+		/* unbusy the card (because only one interface per card)*/
+		dev->tbusy = 0;
+	}	
+	sendpacket = skb->data;
+#ifdef CONFIG_SANGOMA_MANAGER
+	if(sangoma_ppp_manager(skb,card))
+	{
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
+	}
 #endif
-		++card->wandev.stats.collisions;
-		retry = 1;
-	} else if (card->wandev.state != WAN_CONNECTED)
-		++card->wandev.stats.tx_dropped
-		    ;
-	else if (!skb->protocol)
-		++card->wandev.stats.tx_errors
-		    ;
-	else if (ppp_send(card, skb->data, skb->len, skb->protocol)) {
-		ppp_flags_t *flags = card->flags;
+	disable_irq(card->hw.irq);
+	++card->irq_dis_if_send_count;
 
-		flags->imask |= 0x02;	/* unmask Tx interrupts */
-		retry = 1;
-	} else
-		++card->wandev.stats.tx_packets;
+    	if (test_and_set_bit(0, (void*)&card->wandev.critical)) 
+    	{
+    		if (card->wandev.critical == CRITICAL_IN_ISR) 
+    		{	
+			/* If the critical flag is set due to an Interrupt
+			 * then set enable transmit interrupt flag to enable
+			 * transmit interrupt. (delay interrupt)
+			 */
+			card->wandev.enable_tx_int = 1;
+	   		dev->tbusy = 1;
+			
+			/* set the counter to see if we get the interrupt in
+			 * 5 seconds. 
+			 */
+			ppp_priv_area->tick_counter = jiffies;
+	   		++ppp_priv_area->if_send_critical_ISR;
+			
+			save_flags(host_cpu_flags);
+                        cli();
+                        if ((!(--card->irq_dis_if_send_count)) &&
+                                        (!card->irq_dis_poll_count))
+                                enable_irq(card->hw.irq);
+                        restore_flags(host_cpu_flags);
+
+			return 1;
+
+		}
+
+    		dev_kfree_skb(skb, FREE_WRITE);
+		++ppp_priv_area->if_send_critical_non_ISR;
+		
+		save_flags(host_cpu_flags);
+                cli();
+                if ((!(--card->irq_dis_if_send_count)) &&
+                                         (!card->irq_dis_poll_count))
+                        enable_irq(card->hw.irq);
+                restore_flags(host_cpu_flags);
+	
+		return 0;
+	}
+
+
+	if (card->wandev.state != WAN_CONNECTED) {
+
+		++ppp_priv_area->if_send_wan_disconnected;
+        	++card->wandev.stats.tx_dropped;
+
+     	} else if (!skb->protocol) {
+		++ppp_priv_area->if_send_protocol_error;
+        	++card->wandev.stats.tx_errors;
+
+	} else {
+
+		/*If it's IPX change the network numbers to 0 if they're ours.*/
+		if( skb->protocol == ETH_P_IPX ) {
+			if(card->wandev.enable_IPX) {
+				switch_net_numbers( skb->data, 
+					card->wandev.network_number, 0);
+			} else {
+				++card->wandev.stats.tx_dropped;
+				goto tx_done;
+			}
+		}
 
-	if (!retry) {
+		if (ppp_send(card, skb->data, skb->len, skb->protocol)) {
+
+			retry = 1;
+			dev->tbusy = 1;
+			++ppp_priv_area->if_send_adptr_bfrs_full;
+			++ppp_priv_area->if_send_tx_int_enabled;
+			ppp_priv_area->tick_counter = jiffies;
+			++card->wandev.stats.tx_errors;
+			flags->imask |= 0x02;	/* unmask Tx interrupts */
+
+		} else {
+			++ppp_priv_area->if_send_bfr_passed_to_adptr;
+			++card->wandev.stats.tx_packets;
+		}
+    	}
+	
+tx_done:	
+	if (!retry){
 		dev_kfree_skb(skb, FREE_WRITE);
-		dev->tbusy = 0;
 	}
+
 	card->wandev.critical = 0;
+	
+	save_flags(host_cpu_flags);
+        cli();
+        if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count))
+                enable_irq(card->hw.irq);
+        restore_flags(host_cpu_flags);
+	
 	return retry;
 }
 
+/*
+   If incoming is 0 (outgoing)- if the net numbers is ours make it 0
+   if incoming is 1 - if the net number is 0 make it ours 
+
+*/
+static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming)
+{
+	unsigned long pnetwork_number;
+
+	pnetwork_number = (unsigned long)((sendpacket[6] << 24) + 
+			  (sendpacket[7] << 16) + (sendpacket[8] << 8) + 
+			  sendpacket[9]);
+
+	if (!incoming) 
+	{
+		/* If the destination network number is ours, make it 0 */
+		if( pnetwork_number == network_number) 
+		{
+			sendpacket[6] = sendpacket[7] = sendpacket[8] = 
+					 sendpacket[9] = 0x00;
+		}
+	} 
+	else
+	{
+		/* If the incoming network is 0, make it ours */
+		if( pnetwork_number == 0) 
+		{
+			sendpacket[6] = (unsigned char)(network_number >> 24);
+			sendpacket[7] = (unsigned char)((network_number & 
+					 0x00FF0000) >> 16);
+			sendpacket[8] = (unsigned char)((network_number & 
+					 0x0000FF00) >> 8);
+			sendpacket[9] = (unsigned char)(network_number & 
+					 0x000000FF);
+		}
+	}
+
+
+	pnetwork_number = (unsigned long)((sendpacket[18] << 24) + 
+			  (sendpacket[19] << 16) + (sendpacket[20] << 8) + 
+			  sendpacket[21]);
+
+	if( !incoming ) 
+	{
+		/* If the source network is ours, make it 0 */
+		if( pnetwork_number == network_number) 
+		{
+			sendpacket[18] = sendpacket[19] = sendpacket[20] = 
+					 sendpacket[21] = 0x00;
+		}
+	} 
+	else
+	{
+		/* If the source network is 0, make it ours */
+		if( pnetwork_number == 0 ) 
+		{
+			sendpacket[18] = (unsigned char)(network_number >> 24);
+			sendpacket[19] = (unsigned char)((network_number & 
+					 0x00FF0000) >> 16);
+			sendpacket[20] = (unsigned char)((network_number & 
+					 0x0000FF00) >> 8);
+			sendpacket[21] = (unsigned char)(network_number & 
+					 0x000000FF);
+		}
+	}
+} /* switch_net_numbers */
+
 /*============================================================================
  * Get ethernet-style interface statistics.
  * Return a pointer to struct enet_statistics.
  */
-
-static struct enet_statistics *if_stats(struct device *dev)
+static struct net_device_stats* if_stats (struct device* dev)
 {
-	sdla_t *card = dev->priv;
+	ppp_private_area_t* ppp_priv_area = dev->priv;
+	sdla_t* card = ppp_priv_area->card;
 
 	return &card->wandev.stats;
 }
@@ -539,125 +886,146 @@
  * Read firmware code version.
  *	Put code version as ASCII string in str. 
  */
-static int ppp_read_version(sdla_t * card, char *str)
+static int ppp_read_version (sdla_t* card, char* str)
 {
-	ppp_mbox_t *mb = card->mbox;
+	ppp_mbox_t* mb = card->mbox;
 	int err;
 
 	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
 	mb->cmd.command = PPP_READ_CODE_VERSION;
 	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
+
 	if (err != CMD_OK)
+ 
 		ppp_error(card, err, mb);
+
 	else if (str) {
+
 		int len = mb->cmd.length;
 
 		memcpy(str, mb->data, len);
 		str[len] = '\0';
+
 	}
+
 	return err;
 }
 
 /*============================================================================
  * Configure PPP firmware.
  */
-static int ppp_configure(sdla_t * card, void *data)
+static int ppp_configure (sdla_t* card, void* data)
 {
-	ppp_mbox_t *mb = card->mbox;
+	ppp_mbox_t* mb = card->mbox;
 	int data_len = (card->hw.fwid == SFID_PPP502) ?
-	sizeof(ppp502_conf_t) : sizeof(ppp508_conf_t);
+		sizeof(ppp502_conf_t) : sizeof(ppp508_conf_t);
 	int err;
 
 	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
 	memcpy(mb->data, data, data_len);
-	mb->cmd.length = data_len;
+	mb->cmd.length  = data_len;
 	mb->cmd.command = PPP_SET_CONFIG;
 	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-	if (err != CMD_OK)
+	
+	if (err != CMD_OK) 
 		ppp_error(card, err, mb);
+	
 	return err;
 }
 
 /*============================================================================
  * Set interrupt mode.
  */
-static int ppp_set_intr_mode(sdla_t * card, unsigned mode)
+static int ppp_set_intr_mode (sdla_t* card, unsigned mode)
 {
-	ppp_mbox_t *mb = card->mbox;
+	ppp_mbox_t* mb = card->mbox;
 	int err;
 
 	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
 	mb->data[0] = mode;
-	switch (card->hw.fwid) {
-	case SFID_PPP502:
-		mb->cmd.length = 1;
-		break;
-
-	case SFID_PPP508:
-	default:
-		mb->data[1] = card->hw.irq;
-		mb->cmd.length = 2;
+	
+	switch (card->hw.fwid)
+	{
+		case SFID_PPP502:
+			mb->cmd.length  = 1;
+			break;
+
+		case SFID_PPP508:
+		
+		default:
+			mb->data[1] = card->hw.irq;
+			mb->cmd.length = 2;
 	}
+	
 	mb->cmd.command = PPP_SET_INTR_FLAGS;
 	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-	if (err != CMD_OK)
+	
+	if (err != CMD_OK) 
 		ppp_error(card, err, mb);
+	
 	return err;
 }
 
 /*============================================================================
  * Enable communications.
  */
-static int ppp_comm_enable(sdla_t * card)
+static int ppp_comm_enable (sdla_t* card)
 {
-	ppp_mbox_t *mb = card->mbox;
+	ppp_mbox_t* mb = card->mbox;
 	int err;
 
 	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
 	mb->cmd.command = PPP_COMM_ENABLE;
 	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-	if (err != CMD_OK)
+	
+	if (err != CMD_OK) 
 		ppp_error(card, err, mb);
+	
 	return err;
 }
 
 /*============================================================================
  * Disable communications.
  */
-static int ppp_comm_disable(sdla_t * card)
+static int ppp_comm_disable (sdla_t* card)
 {
-	ppp_mbox_t *mb = card->mbox;
+	ppp_mbox_t* mb = card->mbox;
 	int err;
 
 	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
 	mb->cmd.command = PPP_COMM_DISABLE;
 	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-	if (err != CMD_OK)
+	
+	if (err != CMD_OK) 
 		ppp_error(card, err, mb);
+	
 	return err;
 }
 
 /*============================================================================
  * Get communications error statistics.
  */
-static int ppp_get_err_stats(sdla_t * card)
+static int ppp_get_err_stats (sdla_t* card)
 {
-	ppp_mbox_t *mb = card->mbox;
+	ppp_mbox_t* mb = card->mbox;
 	int err;
 
 	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
 	mb->cmd.command = PPP_READ_ERROR_STATS;
 	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-	if (err == CMD_OK) {
-		ppp_err_stats_t *stats = (void *) mb->data;
-
-		card->wandev.stats.rx_over_errors = stats->rx_overrun;
-		card->wandev.stats.rx_crc_errors = stats->rx_bad_crc;
-		card->wandev.stats.rx_missed_errors = stats->rx_abort;
-		card->wandev.stats.rx_length_errors = stats->rx_lost;
+	
+	if (err == CMD_OK) 
+	{	
+		ppp_err_stats_t* stats = (void*)mb->data;
+		card->wandev.stats.rx_over_errors    = stats->rx_overrun;
+		card->wandev.stats.rx_crc_errors     = stats->rx_bad_crc;
+		card->wandev.stats.rx_missed_errors  = stats->rx_abort;
+		card->wandev.stats.rx_length_errors  = stats->rx_lost;
 		card->wandev.stats.tx_aborted_errors = stats->tx_abort;
-	} else
+	
+	} else 
 		ppp_error(card, err, mb);
+	
 	return err;
 }
 
@@ -666,34 +1034,36 @@
  *	Return:	0 - o.k.
  *		1 - no transmit buffers available
  */
-static int ppp_send(sdla_t * card, void *data, unsigned len, unsigned proto)
+ 
+static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto)
 {
-	ppp_buf_ctl_t *txbuf = card->u.p.txbuf;
-	unsigned long addr, cpu_flags;
+	ppp_buf_ctl_t* txbuf = card->u.p.txbuf;
+	unsigned long addr;
 
 	if (txbuf->flag)
-		return 1
-		    ;
+                return 1;
+	
 	if (card->hw.fwid == SFID_PPP502)
-		addr = (txbuf->buf.o_p[1] << 8) + txbuf->buf.o_p[0];
-	else
+		addr = (txbuf->buf.o_p[1] << 8) + txbuf->buf.o_p[0]; 
+	else 
 		addr = txbuf->buf.ptr;
 
-	save_flags(cpu_flags);
-	cli();
+	
 	sdla_poke(&card->hw, addr, data, len);
-	restore_flags(cpu_flags);
-	txbuf->length = len;	/* frame length */
+
+	txbuf->length = len;		/* frame length */
+	
 	if (proto == ETH_P_IPX)
-		txbuf->proto = 0x01	/* protocol ID */
-		    ;
-	txbuf->flag = 1;	/* start transmission */
+		txbuf->proto = 0x01;	/* protocol ID */
+	
+	txbuf->flag = 1;		/* start transmission */
 
 	/* Update transmit buffer control fields */
 	card->u.p.txbuf = ++txbuf;
-	if ((void *) txbuf > card->u.p.txbuf_last)
-		card->u.p.txbuf = card->u.p.txbuf_base
-		    ;
+
+	if ((void*)txbuf > card->u.p.txbuf_last)
+		card->u.p.txbuf = card->u.p.txbuf_base;
+
 	return 0;
 }
 
@@ -706,20 +1076,23 @@
  *
  * Return zero if previous command has to be cancelled.
  */
-static int ppp_error(sdla_t * card, int err, ppp_mbox_t * mb)
+ 
+static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb)
 {
 	unsigned cmd = mb->cmd.command;
 
-	switch (err) {
-	case CMD_TIMEOUT:
-		printk(KERN_ERR "%s: command 0x%02X timed out!\n",
-		       card->devname, cmd);
-		break;
-
-	default:
-		printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n",
-		       card->devname, cmd, err);
+	switch (err) 
+	{
+		case CMD_TIMEOUT:
+			printk(KERN_ERR "%s: command 0x%02X timed out!\n",
+				card->devname, cmd);
+			break;
+
+		default:
+			printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n"
+				, card->devname, cmd, err);
 	}
+
 	return 0;
 }
 
@@ -728,112 +1101,361 @@
 /*============================================================================
  * PPP interrupt service routine.
  */
-STATIC void wpp_isr(sdla_t * card)
+STATIC void wpp_isr (sdla_t* card)
 {
-	ppp_flags_t *flags = card->flags;
+	ppp_flags_t* flags = card->flags;
+	char *ptr = &flags->iflag;
+	unsigned long host_cpu_flags;
+	struct device* dev = card->wandev.dev;
+	int i;
+	
+	card->in_isr = 1;
+	
+	++card->statistics.isr_entry;
+
+	if (set_bit(0, (void*)&card->wandev.critical)) {
+        	
+		++card->statistics.isr_already_critical;
+		printk (KERN_INFO "%s: Critical while in ISR!\n",card->devname);
+		card->in_isr = 0;
+		return;
+	
+	}
+
+	/* For all interrupts set the critical flag to CRITICAL_IN_ISR. 
+	 * If the if_send routine is called with this flag set it will set 
+	 * the enable transmit flag to 1. (for a delayed interrupt) 
+	 */
+	card->wandev.critical = CRITICAL_IN_ISR;
+
+	card->buff_int_mode_unbusy = 0;
 
 	switch (flags->iflag) {
-	case 0x01:		/* receive interrupt */
-		rx_intr(card);
-		break;
-
-	case 0x02:		/* transmit interrupt */
-		flags->imask &= ~0x02;
-		tx_intr(card);
-		break;
-
-	default:		/* unexpected interrupt */
-		printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n",
-		       card->devname, flags->iflag);
+
+		case 0x01:	/* receive interrupt */
+			++card->statistics.isr_rx;
+			rx_intr(card);
+			break;
+
+		case 0x02:	/* transmit interrupt */
+			++card->statistics.isr_tx;
+			flags->imask &= ~0x02;
+			dev->tbusy = 0;		
+			card->buff_int_mode_unbusy = 1;
+			break;
+
+		case 0x08:
+			++Intr_test_counter;
+			++card->statistics.isr_intr_test;
+			break;
+
+		default:	/* unexpected interrupt */
+			++card->statistics.isr_spurious;
+			printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n", 
+				card->devname, flags->iflag);
+			printk(KERN_INFO "%s: ID Bytes = ",card->devname);
+	 		for(i = 0; i < 8; i ++)
+				printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i));
+			printk(KERN_INFO "\n");	
+	}
+	
+	/* The critical flag is set to CRITICAL_INTR_HANDLED to let the
+	 * if_send call know that the interrupt is handled so that 
+	 * transmit interrupts are not enabled again.
+	 */ 
+
+	card->wandev.critical = CRITICAL_INTR_HANDLED;
+
+	/* If the enable transmit interrupt flag is set then enable transmit 
+	 * interrupt on the board. This only goes through if if_send is called 
+	 * and the critical flag is set due to an Interrupt. 
+	 */
+	if(card->wandev.enable_tx_int) {
+
+		flags->imask |= 0x02;
+		card->wandev.enable_tx_int = 0;
+		++card->statistics.isr_enable_tx_int;
+	
 	}
+	save_flags(host_cpu_flags);
+	cli();
+	card->in_isr = 0;
 	flags->iflag = 0;
+	card->wandev.critical = 0;
+	restore_flags(host_cpu_flags);
+
+	if(card->buff_int_mode_unbusy)	
+		mark_bh(NET_BH);
+	
 }
 
 /*============================================================================
  * Receive interrupt handler.
  */
-static void rx_intr(sdla_t * card)
+static void rx_intr (sdla_t* card)
 {
-	ppp_buf_ctl_t *rxbuf = card->rxmb;
-	struct device *dev = card->wandev.dev;
-	struct sk_buff *skb;
+	ppp_buf_ctl_t* rxbuf = card->rxmb;
+	struct device* dev = card->wandev.dev;
+	ppp_private_area_t* ppp_priv_area;
+	struct sk_buff* skb;
 	unsigned len;
-	void *buf;
+	void* buf;
+	int i, err;
+        ppp_flags_t* flags = card->flags;
+        char *ptr = &flags->iflag;
+	int udp_type;
+	
 
 	if (rxbuf->flag != 0x01) {
-		printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n",
-		       card->devname, (unsigned) rxbuf);
-		return;
-	}
-	if (!dev || !dev->start)
-		goto rx_done
-		    ;
-	len = rxbuf->length;
+	
 
-	/* Allocate socket buffer */
-	skb = dev_alloc_skb(len);
-	if (skb == NULL) {
-		printk(KERN_INFO "%s: no socket buffers available!\n",
-		       card->devname);
-		++card->wandev.stats.rx_dropped;
-		goto rx_done;
-	}
-	/* Copy data to the socket buffer */
-	if (card->hw.fwid == SFID_PPP502) {
-		unsigned addr = (rxbuf->buf.o_p[1] << 8) + rxbuf->buf.o_p[0];
+		printk(KERN_INFO 
+			"%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n", 
+			card->devname, (unsigned)rxbuf, rxbuf->flag);
+	
+		printk(KERN_INFO "%s: ID Bytes = ",card->devname);
+	 	
+		for(i = 0; i < 8; i ++)
+			printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i));
+		printk(KERN_INFO "\n");	
+		
+		++card->statistics.rx_intr_corrupt_rx_bfr;
+		return;
 
-		buf = skb_put(skb, len);
-		sdla_peek(&card->hw, addr, buf, len);
-	} else {
-		unsigned addr = rxbuf->buf.ptr;
+	}
+	
 
-		if ((addr + len) > card->u.p.rx_top + 1) {
-			unsigned tmp = card->u.p.rx_top - addr + 1;
+	if (dev && dev->start) {
+	
+		len  = rxbuf->length;
+		ppp_priv_area = dev->priv;
+
+		/* Allocate socket buffer */
+		skb = dev_alloc_skb(len);
+
+		if (skb != NULL) {
+		
+			/* Copy data to the socket buffer */
+			if (card->hw.fwid == SFID_PPP502) {
+		
+				unsigned addr = (rxbuf->buf.o_p[1] << 8) + 
+						rxbuf->buf.o_p[0]; 
+				buf = skb_put(skb, len);
+				sdla_peek(&card->hw, addr, buf, len);
+
+			} else {
+		
+				unsigned addr = rxbuf->buf.ptr;
+
+				if ((addr + len) > card->u.p.rx_top + 1) {
+			
+					unsigned tmp = card->u.p.rx_top - addr 
+							+ 1;
+					buf = skb_put(skb, tmp);
+					sdla_peek(&card->hw, addr, buf, tmp);
+					addr = card->u.p.rx_base;
+					len -= tmp;
+
+				}
+		
+				buf = skb_put(skb, len);
+				sdla_peek(&card->hw, addr, buf, len);
+			}
+
+			/* Decapsulate packet */
+        		switch (rxbuf->proto) {
+	
+				case 0x00:
+					skb->protocol = htons(ETH_P_IP);
+					break;
+
+				case 0x01:
+					skb->protocol = htons(ETH_P_IPX);
+					break;
+			}
+#ifdef CONFIG_SANGOMA_MANAGER
+			udp_type = udp_pkt_type( skb, card );
+
+			if (udp_type == UDP_DRVSTATS_TYPE){ 
+				++ppp_priv_area->rx_intr_DRVSTATS_request; 
+				process_udp_driver_call(
+					UDP_PKT_FRM_NETWORK, card, skb,
+					dev, ppp_priv_area);
+                                 dev_kfree_skb(skb, FREE_READ);
+
+			} else if (udp_type == UDP_PTPIPE_TYPE){
+				++ppp_priv_area->rx_intr_PTPIPE_request;
+				err = process_udp_mgmt_pkt(
+					UDP_PKT_FRM_NETWORK, card, 
+					skb, dev, ppp_priv_area);
+				dev_kfree_skb(skb, FREE_READ);
+			} else
+#endif
+				if (handle_IPXWAN(skb->data,card->devname, card->wandev.enable_IPX, card->wandev.network_number, skb->protocol)) {
+				
+				if( card->wandev.enable_IPX) {
+					ppp_send(card, skb->data, skb->len, ETH_P_IPX);
+                        		dev_kfree_skb(skb, FREE_READ);
+
+				} else {
+					++card->wandev.stats.rx_dropped;
+				}
+			} else {
+				/* Pass it up the protocol stack */
+	    			skb->dev = dev;
+				skb->mac.raw  = skb->data;
+	    			netif_rx(skb);
+			    	++card->wandev.stats.rx_packets;
+		    		++ppp_priv_area->rx_intr_bfr_passed_to_stack;	
+			}
+
+		} else {
+		
+			printk(KERN_INFO "%s: no socket buffers available!\n",
+				card->devname);
+			++card->wandev.stats.rx_dropped;
+			++ppp_priv_area->rx_intr_no_socket;
 
-			buf = skb_put(skb, tmp);
-			sdla_peek(&card->hw, addr, buf, tmp);
-			addr = card->u.p.rx_base;
-			len -= tmp;
 		}
-		buf = skb_put(skb, len);
-		sdla_peek(&card->hw, addr, buf, len);
-	}
 
-	/* Decapsulate packet */
-	switch (rxbuf->proto) {
-	case 0x00:
-		skb->protocol = htons(ETH_P_IP);
-		break;
-
-	case 0x01:
-		skb->protocol = htons(ETH_P_IPX);
-		break;
-	}
-
-	/* Pass it up the protocol stack */
-	skb->dev = dev;
-	netif_rx(skb);
-	++card->wandev.stats.rx_packets;
-      rx_done:
+	} else
+		++card->statistics.rx_intr_dev_not_started;
+
 	/* Release buffer element and calculate a pointer to the next one */
 	rxbuf->flag = (card->hw.fwid == SFID_PPP502) ? 0xFF : 0x00;
 	card->rxmb = ++rxbuf;
-	if ((void *) rxbuf > card->u.p.rxbuf_last)
-		card->rxmb = card->u.p.rxbuf_base
-		    ;
+	
+	if ((void*)rxbuf > card->u.p.rxbuf_last)
+		card->rxmb = card->u.p.rxbuf_base;
 }
 
 /*============================================================================
  * Transmit interrupt handler.
  */
-static void tx_intr(sdla_t * card)
+static void tx_intr (sdla_t* card)
 {
-	struct device *dev = card->wandev.dev;
+	struct device* dev = card->wandev.dev;
+
+	if (!dev || !dev->start) 
+	{
+		++card->statistics.tx_intr_dev_not_started;
+        	return;
+     	}
 
-	if (!dev || !dev->start)
-		return;
 	dev->tbusy = 0;
-	mark_bh(NET_BH);
+	dev_tint(dev);
+}
+
+static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto)
+{
+	int i;
+
+	if( proto == htons(ETH_P_IPX) ) 
+	{
+		/* It's an IPX packet */
+		if(!enable_IPX) 
+		{
+			//Return 1 so we don't pass it up the stack.
+			return 1;
+		}
+	}
+	else
+	{
+		/* It's not IPX so pass it up the stack. */
+		return 0;
+	}
+
+	if( sendpacket[16] == 0x90 &&
+	    sendpacket[17] == 0x04)
+	{
+		/* It's IPXWAN */
+
+		if( sendpacket[2] == 0x02 &&
+		    sendpacket[34] == 0x00)
+		{
+			/* It's a timer request packet */
+			printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname);
+
+			/* Go through the routing options and answer no to every */
+			/* option except Unnumbered RIP/SAP */
+			for(i = 41; sendpacket[i] == 0x00; i += 5)
+			{
+				/* 0x02 is the option for Unnumbered RIP/SAP */
+				if( sendpacket[i + 4] != 0x02)
+				{
+					sendpacket[i + 1] = 0;
+				}
+			}
+
+			/* Skip over the extended Node ID option */
+			if( sendpacket[i] == 0x04 )
+			{
+				i += 8;
+			}
+
+			/* We also want to turn off all header compression opt. */
+			for(; sendpacket[i] == 0x80 ;)
+			{
+				sendpacket[i + 1] = 0;
+				i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4;
+			}
+
+			/* Set the packet type to timer response */
+			sendpacket[34] = 0x01;
+
+			printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname);
+		}
+		else if( sendpacket[34] == 0x02 )
+		{
+			/* This is an information request packet */
+			printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname);
+
+			/* Set the packet type to information response */
+			sendpacket[34] = 0x03;
+
+			/* Set the router name */
+			sendpacket[51] = 'P';
+			sendpacket[52] = 'T';
+			sendpacket[53] = 'P';
+			sendpacket[54] = 'I';
+			sendpacket[55] = 'P';
+			sendpacket[56] = 'E';
+			sendpacket[57] = '-';
+			sendpacket[58] = CVHexToAscii(network_number >> 28);
+			sendpacket[59] = CVHexToAscii((network_number & 0x0F000000)>> 24);
+			sendpacket[60] = CVHexToAscii((network_number & 0x00F00000)>> 20);
+			sendpacket[61] = CVHexToAscii((network_number & 0x000F0000)>> 16);
+			sendpacket[62] = CVHexToAscii((network_number & 0x0000F000)>> 12);
+			sendpacket[63] = CVHexToAscii((network_number & 0x00000F00)>> 8);
+			sendpacket[64] = CVHexToAscii((network_number & 0x000000F0)>> 4);
+			sendpacket[65] = CVHexToAscii(network_number & 0x0000000F);
+			for(i = 66; i < 99; i+= 1)
+			{
+				sendpacket[i] = 0;
+			}
+
+			printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname);
+		}
+		else
+		{
+			printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname);
+			return 0;
+		}
+
+		/* Set the WNodeID to our network address */
+		sendpacket[35] = (unsigned char)(network_number >> 24);
+		sendpacket[36] = (unsigned char)((network_number & 0x00FF0000) >> 16);
+		sendpacket[37] = (unsigned char)((network_number & 0x0000FF00) >> 8);
+		sendpacket[38] = (unsigned char)(network_number & 0x000000FF);
+
+		return 1;
+	} else {
+		/* If we get here's its an IPX-data packet, so it'll get passed up the stack. */
+
+		/* switch the network numbers */
+		switch_net_numbers(sendpacket, network_number, 1);	
+		return 0;
+	}
 }
 
 /****** Background Polling Routines  ****************************************/
@@ -847,33 +1469,100 @@
  * 1. This routine may be called on interrupt context with all interrupts
  *    enabled. Beware!
  */
-static void wpp_poll(sdla_t * card)
+static void wpp_poll (sdla_t* card)
 {
-	switch (card->wandev.state) {
-	case WAN_CONNECTED:
-		poll_active(card);
-		break;
-
-	case WAN_CONNECTING:
-		poll_connecting(card);
-		break;
-
-	case WAN_DISCONNECTED:
-		poll_disconnected(card);
-		break;
+	struct device* dev = card->wandev.dev;
+	ppp_flags_t* adptr_flags = card->flags;
+	unsigned long host_cpu_flags;
+
+	++card->statistics.poll_entry;
+
+	/* The wpp_poll is called continously by the WANPIPE thread to allow
+	 * for line state housekeeping. However if we are in a connected state
+	 * then we do not need to go through all the checks everytime. When in
+	 * connected state execute wpp_poll once every second.
+	 */
+
+	if (card->wandev.state == WAN_CONNECTED)
+	{ 	
+		if ((jiffies - card->state_tick) < HZ )
+         		return;
+	}
+
+ 	disable_irq(card->hw.irq);
+  	++card->irq_dis_poll_count; 
+
+	if (set_bit(0, (void *)&card->wandev.critical)) 
+	{
+		++card->statistics.poll_already_critical;
+		printk(KERN_INFO "%s: critical inside wpp_poll\n", 
+			card->devname);
+		save_flags(host_cpu_flags);
+                cli();
+                if ((!card->irq_dis_if_send_count) &&
+                                (!(--card->irq_dis_poll_count)))
+                        enable_irq(card->hw.irq);
+                restore_flags(host_cpu_flags);
+
+		return;
 	}
+
+   	++card->statistics.poll_processed;
+    	
+    	if (dev && dev->tbusy && !(adptr_flags->imask & 0x02)) 
+    	{	
+		++card->statistics.poll_tbusy_bad_status;
+		printk(KERN_INFO "%s: Wpp_Poll: tbusy = 0x01, imask = 0x%02X\n"
+			, card->devname, adptr_flags->imask);
+    	}
+
+	switch(card->wandev.state)
+	{
+		case WAN_CONNECTED:
+			card->state_tick = jiffies;
+			poll_active(card);
+			break;
+
+		case WAN_CONNECTING:
+			poll_connecting(card);
+			break;
+
+		case WAN_DISCONNECTED:
+			poll_disconnected(card);
+			break;
+
+		default:
+			printk(KERN_INFO "%s: Unknown Poll State 0x%02X\n", 
+				card->devname, card->wandev.state);
+			break;
+	}
+
+	card->wandev.critical = 0;
+ 	
+	save_flags(host_cpu_flags);
+        cli();
+        if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count)))
+                enable_irq(card->hw.irq);
+        restore_flags(host_cpu_flags);
+
 }
 
 /*============================================================================
  * Monitor active link phase.
  */
-static void poll_active(sdla_t * card)
+static void poll_active (sdla_t* card)
 {
-	ppp_flags_t *flags = card->flags;
+	ppp_flags_t* flags = card->flags;
+
+	/* We check the lcp_state to see if we are in DISCONNECTED state.
+	 * We are considered to be connected for lcp states 0x06, 0x07, 0x08
+	 * and 0x09.
+	 */
+	if ((flags->lcp_state <= 0x05) || (flags->disc_cause & 0x03)) {
 
-	if (flags->disc_cause & 0x03) {
 		wanpipe_set_state(card, WAN_DISCONNECTED);
 		show_disc_cause(card, flags->disc_cause);
+	
 	}
 }
 
@@ -881,13 +1570,16 @@
  * Monitor link establishment phase.
  * o if connection timed out, disconnect the link.
  */
-static void poll_connecting(sdla_t * card)
+static void poll_connecting (sdla_t* card)
 {
-	ppp_flags_t *flags = card->flags;
-
-	if (flags->lcp_state == 0x09) {
+	ppp_flags_t* flags = card->flags;
+	
+	if (flags->lcp_state == 0x09) 
+	{
 		wanpipe_set_state(card, WAN_CONNECTED);
-	} else if (flags->disc_cause & 0x03) {
+	}
+	else if (flags->disc_cause & 0x03)
+	{	
 		wanpipe_set_state(card, WAN_DISCONNECTED);
 		show_disc_cause(card, flags->disc_cause);
 	}
@@ -898,14 +1590,16 @@
  *  o if interface is up and the hold-down timeout has expired, then retry
  *    connection.
  */
-static void poll_disconnected(sdla_t * card)
+static void poll_disconnected (sdla_t* card)
 {
-	struct device *dev = card->wandev.dev;
+	struct device* dev = card->wandev.dev;
 
 	if (dev && dev->start &&
-	    ((jiffies - card->state_tick) > HOLD_DOWN_TIME)) {
-		wanpipe_set_state(card, WAN_CONNECTING);
-		ppp_comm_enable(card);
+	    ((jiffies - card->state_tick) > HOLD_DOWN_TIME)) 
+	{	
+		wanpipe_set_state(card, WAN_CONNECTING);	
+		if(ppp_comm_enable(card) == CMD_OK)
+			init_ppp_tx_rx_buff( card );
 	}
 }
 
@@ -914,7 +1608,7 @@
 /*============================================================================
  * Configure S502 adapter.
  */
-static int config502(sdla_t * card)
+static int config502 (sdla_t* card)
 {
 	ppp502_conf_t cfg;
 
@@ -923,33 +1617,34 @@
 
 	if (card->wandev.clocking)
 		cfg.line_speed = bps_to_speed_code(card->wandev.bps);
-	cfg.txbuf_num = 4;
-	cfg.mtu_local = card->wandev.mtu;
-	cfg.mtu_remote = card->wandev.mtu;
-	cfg.restart_tmr = 30;
-	cfg.auth_rsrt_tmr = 30;
-	cfg.auth_wait_tmr = 300;
-	cfg.mdm_fail_tmr = 5;
-	cfg.dtr_drop_tmr = 1;
-	cfg.connect_tmout = 0;	/* changed it from 900 */
-	cfg.conf_retry = 10;
-	cfg.term_retry = 2;
-	cfg.fail_retry = 5;
-	cfg.auth_retry = 10;
-	cfg.ip_options = 0x80;
-	cfg.ipx_options = 0xA0;
-	cfg.conf_flags |= 0x0E;
+		
+	cfg.txbuf_num		= 4;
+	cfg.mtu_local		= card->wandev.mtu;
+	cfg.mtu_remote		= card->wandev.mtu;
+	cfg.restart_tmr		= 30;
+	cfg.auth_rsrt_tmr	= 30;
+	cfg.auth_wait_tmr	= 300;
+	cfg.mdm_fail_tmr	= 5;
+	cfg.dtr_drop_tmr	= 1;
+	cfg.connect_tmout	= 0;	/* changed it from 900 */
+	cfg.conf_retry		= 10;
+	cfg.term_retry		= 2;
+	cfg.fail_retry		= 5;
+	cfg.auth_retry		= 10;
+	cfg.ip_options		= 0x80;
+	cfg.ipx_options		= 0xA0;
+        cfg.conf_flags         |= 0x0E;
 /*
-   cfg.ip_local         = dev->pa_addr;
-   cfg.ip_remote                = dev->pa_dstaddr;
- */
+	cfg.ip_local		= dev->pa_addr;
+	cfg.ip_remote		= dev->pa_dstaddr;
+*/
 	return ppp_configure(card, &cfg);
 }
 
 /*============================================================================
  * Configure S508 adapter.
  */
-static int config508(sdla_t * card)
+static int config508 (sdla_t* card)
 {
 	ppp508_conf_t cfg;
 
@@ -957,111 +1652,275 @@
 	memset(&cfg, 0, sizeof(ppp508_conf_t));
 
 	if (card->wandev.clocking)
-		cfg.line_speed = card->wandev.bps
-		    ;
+		cfg.line_speed = card->wandev.bps;
+
 	if (card->wandev.interface == WANOPT_RS232)
 		cfg.conf_flags |= 0x0020;
-	;
-	cfg.conf_flags |= 0x300;	/*send Configure-Request packets forever */
-	cfg.txbuf_percent = 60;	/* % of Tx bufs */
-	cfg.mtu_local = card->wandev.mtu;
-	cfg.mtu_remote = card->wandev.mtu;
-	cfg.restart_tmr = 30;
-	cfg.auth_rsrt_tmr = 30;
-	cfg.auth_wait_tmr = 300;
-	cfg.mdm_fail_tmr = 5;
-	cfg.dtr_drop_tmr = 1;
-	cfg.connect_tmout = 0;	/* changed it from 900 */
-	cfg.conf_retry = 10;
-	cfg.term_retry = 2;
-	cfg.fail_retry = 5;
-	cfg.auth_retry = 10;
-	cfg.ip_options = 0x80;
-	cfg.ipx_options = 0xA0;
+
+        cfg.conf_flags 	|= 0x300; /*send Configure-Request packets forever*/
+	cfg.txbuf_percent	= 60;	/* % of Tx bufs */
+	cfg.mtu_local		= card->wandev.mtu;
+	cfg.mtu_remote		= card->wandev.mtu;
+	cfg.restart_tmr		= 30;
+	cfg.auth_rsrt_tmr	= 30;
+	cfg.auth_wait_tmr	= 300;
+	cfg.mdm_fail_tmr	= 100;
+	cfg.dtr_drop_tmr	= 1;
+	cfg.connect_tmout	= 0; 	/* changed it from 900 */
+	cfg.conf_retry		= 10;
+	cfg.term_retry		= 2;
+	cfg.fail_retry		= 5;
+	cfg.auth_retry		= 10;
+	cfg.ip_options		= 0x80;
+	cfg.ipx_options		= 0xA0;
 /*
-   cfg.ip_local         = dev->pa_addr;
-   cfg.ip_remote                = dev->pa_dstaddr;
- */
+	cfg.ip_local		= dev->pa_addr;
+	cfg.ip_remote		= dev->pa_dstaddr;
+*/
 	return ppp_configure(card, &cfg);
 }
 
 /*============================================================================
  * Show disconnection cause.
  */
-static void show_disc_cause(sdla_t * card, unsigned cause)
+static void show_disc_cause (sdla_t* card, unsigned cause)
 {
-	if (cause & 0x0002)
-		printk(KERN_INFO
-		       "%s: link terminated by peer\n", card->devname);
-	else if (cause & 0x0004)
-		printk(KERN_INFO
-		       "%s: link terminated by user\n", card->devname);
-	else if (cause & 0x0008)
-		printk(KERN_INFO
-		       "%s: authentication failed\n", card->devname);
+	if (cause & 0x0002) 
+		printk(KERN_INFO "%s: link terminated by peer\n", 
+			card->devname);
+
+	else if (cause & 0x0004) 
+		printk(KERN_INFO "%s: link terminated by user\n", 
+			card->devname);
+
+	else if (cause & 0x0008) 
+		printk(KERN_INFO "%s: authentication failed\n", card->devname);
+	
 	else if (cause & 0x0010)
+		printk(KERN_INFO 
+			"%s: authentication protocol negotiation failed\n", 
+			card->devname);
+
+	else if (cause & 0x0020) 
 		printk(KERN_INFO
-		       "%s: authentication protocol negotiation failed\n",
-		       card->devname);
-	else if (cause & 0x0020)
-		printk(KERN_INFO
-		       "%s: peer's request for authentication rejected\n",
-		       card->devname);
-	else if (cause & 0x0040)
-		printk(KERN_INFO
-		     "%s: MRU option rejected by peer\n", card->devname);
-	else if (cause & 0x0080)
-		printk(KERN_INFO
-		       "%s: peer's MRU was too small\n", card->devname);
-	else if (cause & 0x0100)
-		printk(KERN_INFO
-		       "%s: failed to negotiate peer's LCP options\n",
-		       card->devname);
-	else if (cause & 0x0200)
-		printk(KERN_INFO
-		       "%s: failed to negotiate peer's IPCP options\n",
-		       card->devname);
-	else if (cause & 0x0400)
-		printk(KERN_INFO
-		       "%s: failed to negotiate peer's IPXCP options\n",
-		       card->devname);
+		"%s: peer's request for authentication rejected\n",
+		card->devname);
+
+	else if (cause & 0x0040) 
+		printk(KERN_INFO "%s: MRU option rejected by peer\n", 
+		card->devname);
+
+	else if (cause & 0x0080) 
+		printk(KERN_INFO "%s: peer's MRU was too small\n", 
+		card->devname);
+
+	else if (cause & 0x0100) 
+		printk(KERN_INFO "%s: failed to negotiate peer's LCP options\n",
+		card->devname);
+
+	else if (cause & 0x0200) 
+		printk(KERN_INFO "%s: failed to negotiate peer's IPCP options\n"
+		, card->devname);
+
+	else if (cause & 0x0400) 
+		printk(KERN_INFO 
+			"%s: failed to negotiate peer's IPXCP options\n",
+			card->devname);
 }
 
 /*============================================================================
  * Convert line speed in bps to a number used by S502 code.
  */
-static unsigned char bps_to_speed_code(unsigned long bps)
+static unsigned char bps_to_speed_code (unsigned long bps)
 {
-	unsigned char number;
+	unsigned char	number;
 
-	if (bps <= 1200)
+	if (bps <= 1200)        
 		number = 0x01;
-	else if (bps <= 2400)
+	else if (bps <= 2400)   
 		number = 0x02;
-	else if (bps <= 4800)
+	else if (bps <= 4800)   
 		number = 0x03;
-	else if (bps <= 9600)
+	else if (bps <= 9600)   
 		number = 0x04;
-	else if (bps <= 19200)
+	else if (bps <= 19200)  
 		number = 0x05;
-	else if (bps <= 38400)
+	else if (bps <= 38400)  
 		number = 0x06;
-	else if (bps <= 45000)
+	else if (bps <= 45000)  
 		number = 0x07;
-	else if (bps <= 56000)
+	else if (bps <= 56000)  
 		number = 0x08;
-	else if (bps <= 64000)
+	else if (bps <= 64000)  
 		number = 0x09;
-	else if (bps <= 74000)
+	else if (bps <= 74000)  
 		number = 0x0A;
-	else if (bps <= 112000)
+	else if (bps <= 112000) 	
 		number = 0x0B;
-	else if (bps <= 128000)
+	else if (bps <= 128000) 
 		number = 0x0C;
-	else
+	else 
 		number = 0x0D;
 
 	return number;
+}
+
+/*=============================================================================
+ * Initial the ppp_private_area structure.
+ */
+
+static void init_ppp_priv_struct( ppp_private_area_t* ppp_priv_area )
+{
+	ppp_priv_area->if_send_entry 			= 0;
+	ppp_priv_area->if_send_skb_null 		= 0;
+	ppp_priv_area->if_send_broadcast 		= 0;
+	ppp_priv_area->if_send_multicast 		= 0;
+	ppp_priv_area->if_send_critical_ISR 		= 0;
+	ppp_priv_area->if_send_critical_non_ISR 	= 0;
+	ppp_priv_area->if_send_busy 			= 0;
+	ppp_priv_area->if_send_busy_timeout 		= 0;
+	ppp_priv_area->if_send_DRVSTATS_request		= 0;
+	ppp_priv_area->if_send_PTPIPE_request 		= 0;
+	ppp_priv_area->if_send_wan_disconnected 	= 0;
+	ppp_priv_area->if_send_adptr_bfrs_full 		= 0;
+	ppp_priv_area->if_send_bfr_passed_to_adptr 	= 0;
+	
+	ppp_priv_area->rx_intr_no_socket 		= 0;
+	ppp_priv_area->rx_intr_DRVSTATS_request 	= 0;
+	ppp_priv_area->rx_intr_PTPIPE_request 		= 0;
+	ppp_priv_area->rx_intr_bfr_not_passed_to_stack 	= 0;
+	ppp_priv_area->rx_intr_bfr_passed_to_stack 	= 0;
+
+	ppp_priv_area->UDP_PTPIPE_mgmt_kmalloc_err 		= 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_adptr_type_err 		= 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_direction_err 		= 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_timeout	= 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_OK 		= 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_adptr 		= 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_stack 		= 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_no_socket 		= 0; 	
+
+	ppp_priv_area->UDP_DRVSTATS_mgmt_kmalloc_err 		= 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_type_err 	= 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_direction_err 		= 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout	= 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_OK 		= 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_adptr 	= 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_stack 	= 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_no_socket 		= 0; 	
+}
+
+/*============================================================================
+ * Initialize Global Statistics
+ */
+static void init_global_statistics( sdla_t* card )
+{
+	card->statistics.isr_entry 		= 0;
+	card->statistics.isr_already_critical 	= 0;
+	card->statistics.isr_tx 		= 0;
+	card->statistics.isr_rx 		= 0;
+	card->statistics.isr_intr_test		= 0;
+	card->statistics.isr_spurious 		= 0;
+	card->statistics.isr_enable_tx_int 	= 0;
+	card->statistics.rx_intr_corrupt_rx_bfr = 0;
+	card->statistics.rx_intr_dev_not_started= 0;
+	card->statistics.tx_intr_dev_not_started= 0;
+	card->statistics.poll_entry		= 0;
+	card->statistics.poll_already_critical 	= 0;
+	card->statistics.poll_processed 	= 0;
+	card->statistics.poll_tbusy_bad_status 	= 0;
+
+}
+
+/*============================================================================
+ * Initialize Receive and Transmit Buffers.
+ */
+static void init_ppp_tx_rx_buff( sdla_t* card )
+{
+
+	if (card->hw.fwid == SFID_PPP502) 
+	{	
+		ppp502_buf_info_t* info =
+			(void*)(card->hw.dpmbase + PPP502_BUF_OFFS);
+
+		card->u.p.txbuf_base = 
+			(void*)(card->hw.dpmbase + info->txb_offs);
+
+		card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base +
+			(info->txb_num - 1);
+
+		card->u.p.rxbuf_base = 
+			(void*)(card->hw.dpmbase + info->rxb_offs);
+
+		card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base +
+			(info->rxb_num - 1);
+	}
+	else
+	{	
+		ppp508_buf_info_t* info =
+			(void*)(card->hw.dpmbase + PPP508_BUF_OFFS);
+
+		card->u.p.txbuf_base = (void*)(card->hw.dpmbase +
+			(info->txb_ptr - PPP508_MB_VECT));
+
+		card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base +
+			(info->txb_num - 1);
+
+		card->u.p.rxbuf_base = (void*)(card->hw.dpmbase +
+			(info->rxb_ptr - PPP508_MB_VECT));
+
+		card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base +
+			(info->rxb_num - 1);
+
+		card->u.p.rx_base = info->rxb_base;
+		card->u.p.rx_top  = info->rxb_end;
+	}
+
+	card->u.p.txbuf = card->u.p.txbuf_base;
+	card->rxmb = card->u.p.rxbuf_base;
+
+}
+
+/*=============================================================================
+ * Perform the Interrupt Test by running the READ_CODE_VERSION command MAX_INTR
+ * _TEST_COUNTER times.
+ */
+static int intr_test( sdla_t* card )
+{
+	ppp_mbox_t* mb = card->mbox;
+	int err,i;
+
+	/* The critical flag is unset because during intialization (if_open) 
+	 * we want the interrupts to be enabled so that when the wpp_isr is
+	 * called it does not exit due to critical flag set.
+	 */ 
+	 
+	card->wandev.critical = 0;
+
+	err = ppp_set_intr_mode( card, 0x08 );
+	
+	if ( err == CMD_OK ) 
+	{ 	
+		for (i=0; i<MAX_INTR_TEST_COUNTER; i++) 
+		{	
+			/* Run command READ_CODE_VERSION */
+			memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
+			mb->cmd.length  = 0;
+			mb->cmd.command = 0x10;
+			err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
+			
+			if (err != CMD_OK) 
+				ppp_error(card, err, mb);
+		}
+	}
+	else return err;
+
+	err = ppp_set_intr_mode( card, 0 );
+	if (err != CMD_OK) 
+		return err;
+
+	card->wandev.critical = 1;
+	return 0;
 }
 
 /****** End *****************************************************************/
diff -u --recursive --new-file v2.1.78/linux/drivers/net/sdla_x25.c linux/drivers/net/sdla_x25.c
--- v2.1.78/linux/drivers/net/sdla_x25.c	Thu Jul 17 10:06:05 1997
+++ linux/drivers/net/sdla_x25.c	Mon Jan 12 14:46:16 1998
@@ -10,6 +10,19 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
+* Nov 27, 1997	Jaspreet Singh	 o Added protection against enabling of irqs
+*				   when they are disabled.
+* Nov 17, 1997  Farhan Thawar    o Added IPX support
+*				 o Changed if_send() to now buffer packets when
+*				   the board is busy
+*				 o Removed queueing of packets via the polling
+*				   routing
+*				 o Changed if_send() critical flags to properly
+*				   handle race conditions
+* Nov 06, 1997  Farhan Thawar    o Added support for SVC timeouts
+*				 o Changed PVC encapsulation to ETH_P_IP
+* Jul 21, 1997  Jaspreet Singh	 o Fixed freeing up of buffers using kfree()
+*				   when packets are received.
 * Mar 11, 1997  Farhan Thawar   Version 3.1.1
 *                                o added support for V35
 *                                o changed if_send() to return 0 if
@@ -49,12 +62,15 @@
 #define	MAX_CMD_RETRY	10		/* max number of firmware retries */
 
 #define	X25_CHAN_MTU	4096		/* unfragmented logical channel MTU */
-#define	X25_HRDHDR_SZ	6		/* max encapsulation header size */
+#define	X25_HRDHDR_SZ	7		/* max encapsulation header size */
 #define	X25_CONCT_TMOUT	(90*HZ)		/* link connection timeout */
 #define	X25_RECON_TMOUT	(10*HZ)		/* link connection timeout */
 #define	CONNECT_TIMEOUT	(90*HZ)		/* link connection timeout */
 #define	HOLD_DOWN_TIME	(30*HZ)		/* link hold down time */
 
+/* For IPXWAN */
+#define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b)))
+
 /****** Data Structures *****************************************************/
 
 /* This is an extention of the 'struct device' we create for each network
@@ -72,7 +88,10 @@
 	char drop_sequence;		/* mark sequence for dropping */
 	unsigned long state_tick;	/* time of the last state change */
 	unsigned idle_timeout;		/* sec, before disconnecting */
+	unsigned long i_timeout_sofar;  /* # of sec's we've been idle */
 	unsigned hold_timeout;		/* sec, before re-connecting */
+	unsigned long tick_counter;	/* counter for transmit time out */
+	char devtint;			/* Weather we should dev_tint() */
 	struct sk_buff* rx_skb;		/* receive socket buffer */
 	struct sk_buff* tx_skb;		/* transmit socket buffer */
 	sdla_t* card;			/* -> owner */
@@ -167,6 +186,13 @@
 static unsigned int hex_to_uint (unsigned char* str, int len);
 static void parse_call_info (unsigned char* str, x25_call_info_t* info);
 
+/* IPX functions */
+static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming);
+static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto);
+
+extern void disable_irq(unsigned int);
+extern void enable_irq(unsigned int);
+
 /****** Global Data **********************************************************
  * Note: All data must be explicitly initialized!!!
  */
@@ -232,7 +258,7 @@
 	u.cfg.hdlcWindow	= 7;
 	u.cfg.pktWindow		= 2;
 	u.cfg.station		= 1;		/* DTE */
-	u.cfg.options		= 0x0010;	/* disable D-bit pragmatics */
+	u.cfg.options		= 0x00B0;	/* disable D-bit pragmatics */
 	u.cfg.ccittCompat	= 1988;
 	u.cfg.t10t20		= 30;
 	u.cfg.t11t21		= 30;
@@ -243,6 +269,7 @@
 	u.cfg.r10r20		= 5;
 	u.cfg.r12r22		= 5;
 	u.cfg.r13r23		= 5;
+	u.cfg.responseOpt	= 1;		/* RR's after every packet */
 
 	if (conf->clocking != WANOPT_EXTERNAL)
 		u.cfg.baudRate = bps_to_speed_code(conf->bps)
@@ -267,7 +294,7 @@
 	else if (conf->mtu >= 128)
 		card->wandev.mtu = 128
 	;
-	else conf->mtu = 64;
+	else card->wandev.mtu = 64;
 	u.cfg.defPktSize = u.cfg.pktMTU = card->wandev.mtu;
 
 	if (conf->u.x25.hi_pvc)
@@ -322,6 +349,15 @@
 	card->wandev.new_if	= &new_if;
 	card->wandev.del_if	= &del_if;
 	card->wandev.state	= WAN_DISCONNECTED;
+	card->wandev.enable_tx_int = 0;
+	card->irq_dis_if_send_count = 0;
+        card->irq_dis_poll_count = 0;
+	card->wandev.enable_IPX = conf->enable_IPX;
+	
+	if (conf->network_number)
+		card->wandev.network_number = conf->network_number;
+	else
+		card->wandev.network_number = 0xDEADBEEF;
 	return 0;
 }
 
@@ -386,13 +422,18 @@
 	memset(chan, 0, sizeof(x25_channel_t));
 	strcpy(chan->name, conf->name);
 	chan->card = card;
+	chan->protocol = ETH_P_IP;
+	chan->tx_skb = chan->rx_skb = NULL;
 
 	/* verify media address */
 	if (conf->addr[0] == '@')		/* SVC */
 	{
 		chan->svc = 1;
-		chan->protocol = ETH_P_IP;
 		strncpy(chan->addr, &conf->addr[1], WAN_ADDRESS_SZ);
+
+		/* Set channel timeouts (default if not specified) */
+		chan->idle_timeout = (conf->idle_timeout) ? conf->idle_timeout : 					90;
+		chan->hold_timeout = (conf->hold_timeout) ? conf->hold_timeout :					10;
 	}
 	else if (is_digit(conf->addr[0]))	/* PVC */
 	{
@@ -435,6 +476,7 @@
 /*============================================================================
  * Delete logical channel.
  */
+
 static int del_if (wan_device_t* wandev, struct device* dev)
 {
 	if (dev->priv)
@@ -450,6 +492,7 @@
 /*============================================================================
  * Execute adapter interface command.
  */
+
 static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data)
 {
 	TX25Mbox* mbox = card->mbox;
@@ -525,6 +568,9 @@
 	dev->mem_start	= wandev->maddr;
 	dev->mem_end	= wandev->maddr + wandev->msize - 1;
 
+        /* Set transmit buffer queue length */
+        dev->tx_queue_len = 10;
+
 	/* Initialize socket buffers */
 	for (i = 0; i < DEV_NUMBUFFS; ++i)
 		skb_queue_head_init(&dev->buffs[i])
@@ -659,33 +705,86 @@
 {
 	x25_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
-	int queued = 0;
+	struct device *dev2;
+	TX25Status* status = card->flags;
+	unsigned long host_cpu_flags;
 
-	if (test_and_set_bit(0, (void*)&card->wandev.critical))
+	if (skb == NULL)
 	{
+		/* If we get here, some higher layer thinks we've missed a
+		 * tx-done interrupt.
+		 */
 #ifdef _DEBUG_
-		printk(KERN_INFO "%s: if_send() hit critical section!\n",
-			card->devname)
+		printk(KERN_INFO "%s: interface %s got kicked!\n",
+			card->devname, dev->name)
 		;
 #endif
-		dev_kfree_skb(skb, FREE_WRITE);
+		dev_tint(dev);
 		return 0;
 	}
 
-	if (test_and_set_bit(0, (void*)&dev->tbusy))
+	if (dev->tbusy)
 	{
-#ifdef _DEBUG_
-		printk(KERN_INFO "%s: Tx collision on interface %s!\n",
+		++chan->ifstats.rx_dropped;	
+		if ((jiffies - chan->tick_counter) < (5*HZ))
+		{
+			return dev->tbusy;
+		}
+		printk(KERN_INFO "%s: Transmit time out %s!\n",
 			card->devname, dev->name)
 		;
-#endif
-		++chan->ifstats.collisions;
-		++card->wandev.stats.collisions;
-	        dev_kfree_skb(skb, FREE_WRITE);
-	        card->wandev.critical = 0;
-	        return 0;
+		for( dev2 = card->wandev.dev; dev2; dev2 = dev2->slave)
+		{
+	        	dev2->tbusy = 0;
+		}
 	}
-	else if (chan->protocol && (chan->protocol != skb->protocol))
+	chan->tick_counter = jiffies;
+
+	disable_irq(card->hw.irq);
+	++card->irq_dis_if_send_count;
+
+	if (set_bit(0, (void*)&card->wandev.critical)) 
+	{
+		printk(KERN_INFO "Hit critical in if_send()!\n");
+		if (card->wandev.critical == CRITICAL_IN_ISR) 
+		{
+			card->wandev.enable_tx_int = 1;
+			dev->tbusy = 1;
+			
+			save_flags(host_cpu_flags);
+                        cli();
+                        if ((!(--card->irq_dis_if_send_count)) &&
+                                        (!card->irq_dis_poll_count))
+                                enable_irq(card->hw.irq);
+                        restore_flags(host_cpu_flags);
+			
+			return dev->tbusy;
+		}
+		dev_kfree_skb(skb, FREE_WRITE);
+		
+		save_flags(host_cpu_flags);
+                cli();
+                if ((!(--card->irq_dis_if_send_count)) &&
+                                         (!card->irq_dis_poll_count))
+                        enable_irq(card->hw.irq);
+                restore_flags(host_cpu_flags);
+
+		return dev->tbusy;
+	}
+
+	/* Below is only until we have per-channel IPX going.... */
+	if(!(chan->svc))
+	{
+		chan->protocol = skb->protocol;
+	}
+
+	if (card->wandev.state != WAN_CONNECTED)
+	{
+		++chan->ifstats.tx_dropped
+	;
+	}
+	/* Below is only until we have per-channel IPX going.... */
+	else if ( (chan->svc) && (chan->protocol && (chan->protocol != skb->protocol)))
 	{
 		printk(KERN_INFO
 			"%s: unsupported Ethertype 0x%04X on interface %s!\n",
@@ -693,41 +792,57 @@
 		;
 		++chan->ifstats.tx_errors;
 	}
-	else if (card->wandev.state != WAN_CONNECTED)
-		++chan->ifstats.tx_dropped
-	;
 	else switch (chan->state)
 	{
-	case WAN_CONNECTED:
-		dev->trans_start = jiffies;
-		queued = chan_send(dev, skb);
-		if (queued) chan->tx_skb = skb;
-		break;
-
 	case WAN_DISCONNECTED:
 		/* Try to establish connection. If succeded, then start
 		 * transmission, else drop a packet.
 		 */
-		if (chan_connect(dev) == 0)
+		if (chan_connect(dev) != 0)
 		{
-			dev->trans_start = jiffies;
-			queued = chan_send(dev, skb);
-			if (queued) chan->tx_skb = skb;
+			++chan->ifstats.tx_dropped;
+			++card->wandev.stats.tx_dropped;
 			break;
 		}
-		/* else fall through */
+		/* fall through */
+
+	case WAN_CONNECTED:
+		if( skb->protocol == ETH_P_IPX ) {
+			if(card->wandev.enable_IPX) {
+				switch_net_numbers( skb->data, 
+					card->wandev.network_number, 0);
+			} else {
+				++card->wandev.stats.tx_dropped;
+				++chan->ifstats.tx_dropped;
+				goto tx_done;
+			}
+		}
+		dev->trans_start = jiffies;
+		if(chan_send(dev, skb))
+		{
+			dev->tbusy = 1;
+			status->imask |= 0x2;
+		}
+		break;
 
 	default:
-		++chan->ifstats.tx_dropped;
+		++chan->ifstats.tx_dropped;	
+		++card->wandev.stats.tx_dropped;
 	}
 
-	if (!queued)
+tx_done:
+	if (!dev->tbusy)
 	{
 		dev_kfree_skb(skb, FREE_WRITE);
-		dev->tbusy = 0;
 	}
 	card->wandev.critical = 0;
-	return 0;
+	save_flags(host_cpu_flags);
+        cli();
+        if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count))
+                enable_irq(card->hw.irq);
+        restore_flags(host_cpu_flags);
+
+	return dev->tbusy;
 }
 
 /*============================================================================
@@ -749,6 +864,24 @@
 static void wpx_isr (sdla_t* card)
 {
 	TX25Status* status = card->flags;
+	struct device *dev;
+	unsigned long host_cpu_flags;
+
+	card->in_isr = 1;
+	card->buff_int_mode_unbusy = 0;
+
+	if (test_and_set_bit(0, (void*)&card->wandev.critical)) {
+
+ 		printk(KERN_INFO "wpx_isr: %s, wandev.critical set to 0x%02X, int type = 0x%02X\n", card->devname, card->wandev.critical, status->iflags);
+		card->in_isr = 0;
+		return;
+	}
+
+	/* For all interrupts set the critical flag to CRITICAL_RX_INTR.
+         * If the if_send routine is called with this flag set it will set
+         * the enable transmit flag to 1. (for a delayed interrupt)
+         */
+	card->wandev.critical = CRITICAL_IN_ISR;
 
 	switch (status->iflags)
 	{
@@ -758,6 +891,8 @@
 
 	case 0x02:		/* transmit interrupt */
 		tx_intr(card);
+		card->buff_int_mode_unbusy = 1;
+		status->imask &= ~0x2;
 		break;
 
 	case 0x04:		/* modem status interrupt */
@@ -768,10 +903,33 @@
 		event_intr(card);
 		break;
 
-	default:		/* unwanter interrupt */
+	default:		/* unwanted interrupt */
 		spur_intr(card);
 	}
+
+	card->wandev.critical = CRITICAL_INTR_HANDLED;
+	if( card->wandev.enable_tx_int)
+	{
+		card->wandev.enable_tx_int = 0;
+		status->imask |= 0x2;
+	}
+	save_flags(host_cpu_flags);
+	cli();
+	card->in_isr = 0;
 	status->iflags = 0;	/* clear interrupt condition */
+	card->wandev.critical = 0;
+	restore_flags(host_cpu_flags);
+
+	if(card->buff_int_mode_unbusy)
+	{
+		for(dev = card->wandev.dev; dev; dev = dev->slave)
+		{
+			if(((x25_channel_t*)dev->priv)->devtint)
+			{
+				dev_tint(dev);
+			}	
+		}
+	}
 }
 
 /*============================================================================
@@ -812,6 +970,7 @@
 	}
 
 	chan = dev->priv;
+	chan->i_timeout_sofar = jiffies;
 	if (chan->drop_sequence)
 	{
 		if (!(qdm & 0x01)) chan->drop_sequence = 0;
@@ -870,8 +1029,29 @@
 	}
 	else
 	{
-		netif_rx(skb);
-		++chan->ifstats.rx_packets;
+		if( handle_IPXWAN(skb->data, card->devname, card->wandev.enable_IPX, card->wandev.network_number, skb->protocol))
+		{
+			if( card->wandev.enable_IPX )
+			{
+				if(chan_send(dev, skb))
+				{
+					chan->tx_skb = skb;
+				}
+				else
+				{
+					dev_kfree_skb(skb, FREE_WRITE);
+				}
+			}
+			else
+			{
+				/* increment IPX packet dropped statistic */
+			}
+		}
+		else
+		{
+			netif_rx(skb);
+			++chan->ifstats.rx_packets;
+		}
 	}
 }
 
@@ -882,6 +1062,16 @@
  */
 static void tx_intr (sdla_t* card)
 {
+	struct device *dev;
+
+	
+	/* unbusy all devices and then dev_tint(); */
+	for(dev = card->wandev.dev; dev; dev = dev->slave)
+	{
+		((x25_channel_t*)dev->priv)->devtint = dev->tbusy; 
+		dev->tbusy = 0;
+	}
+
 }
 
 /*============================================================================
@@ -922,6 +1112,25 @@
  */
 static void wpx_poll (sdla_t* card)
 {
+	unsigned long host_cpu_flags;
+
+	disable_irq(card->hw.irq);
+	++card->irq_dis_poll_count;
+
+	if (set_bit(0, (void*)&card->wandev.critical)) {
+
+ 		printk(KERN_INFO "%s: critical in polling!\n",card->devname);
+	 	
+		save_flags(host_cpu_flags);
+                cli();
+		if ((!card->irq_dis_if_send_count) &&
+                                (!(--card->irq_dis_poll_count)))
+                        enable_irq(card->hw.irq);
+                restore_flags(host_cpu_flags);
+		
+		return;
+	}
+
 	switch(card->wandev.state)
 	{
 	case WAN_CONNECTED:
@@ -935,6 +1144,15 @@
 	case WAN_DISCONNECTED:
 		poll_disconnected(card);
 	}
+
+	card->wandev.critical = 0;
+
+	save_flags(host_cpu_flags);
+        cli();
+        if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count)))
+                enable_irq(card->hw.irq);
+        restore_flags(host_cpu_flags);
+
 }
 
 /*============================================================================
@@ -948,7 +1166,8 @@
 	if (status->gflags & X25_HDLC_ABM)
 	{
 		wanpipe_set_state(card, WAN_CONNECTED);
-		x25_set_intr_mode(card, 0x81);	/* enable Rx interrupts */
+		x25_set_intr_mode(card, 0x83);	/* enable Rx interrupts */
+		status->imask &= ~0x2;		/* mask Tx interupts */
 	}
 	else if ((jiffies - card->state_tick) > CONNECT_TIMEOUT)
 	    disconnect(card)
@@ -998,14 +1217,16 @@
 
 		/* If SVC has been idle long enough, close virtual circuit */
 
-/*
-		unsigned long flags;
-
-		save_flags(flags);
-		cli();
-
-		restore_flags(flags);
-*/
+		if(( chan->svc )&&( chan->state == WAN_CONNECTED ))
+		{
+			if( (jiffies - chan->i_timeout_sofar) / HZ > chan->idle_timeout )
+			{
+				//Close svc
+				printk(KERN_INFO "%s: Closing down Idle link %s on LCN %d\n",card->devname,chan->name,chan->lcn); 
+				chan->i_timeout_sofar = jiffies;
+				chan_disc(dev);
+			}
+		}
 	}
 }
 
@@ -1644,7 +1865,10 @@
 		accept = 1;
 		break;
 
-	case NLPID_SNAP:
+	case NLPID_SNAP: /* IPX datagrams */
+		chan->protocol = ETH_P_IPX;
+		accept = 1;
+		break;
 	default:
 		printk(KERN_INFO
 			"%s: unsupported NLPID 0x%02X in incomming call "
@@ -1853,8 +2077,8 @@
 {
 	x25_channel_t* chan = dev->priv;
 
-	set_chan_state(dev, WAN_DISCONNECTED);
 	if (chan->svc) x25_clear_call(chan->card, chan->lcn, 0, 0);
+	set_chan_state(dev, WAN_DISCONNECTED);
 	return 0;
 }
 
@@ -1878,6 +2102,7 @@
 			card->devname, dev->name)
 			;
 			*(unsigned short*)dev->dev_addr = htons(chan->lcn);
+			chan->i_timeout_sofar = jiffies;
 			break;
 
 		case WAN_CONNECTING:
@@ -1941,6 +2166,7 @@
 	switch(x25_send(card, chan->lcn, qdm, len, skb->data))
 	{
 	case 0x00:	/* success */
+		chan->i_timeout_sofar = jiffies;
 		if (qdm)
 		{
 			skb_pull(skb, len);
@@ -1954,6 +2180,7 @@
 
 	default:	/* failure */
 		++chan->ifstats.tx_errors;
+/*		return 1; */
 	}
 	return 0;
 }
@@ -2080,5 +2307,172 @@
 	}
 	return val;
 }
+
+
+static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto)
+{
+	int i;
+
+	if( proto == htons(ETH_P_IPX) ) {
+		/* It's an IPX packet */
+		if(!enable_IPX) {
+			/* Return 1 so we don't pass it up the stack. */
+			return 1;
+		}
+	} else {
+		/* It's not IPX so pass it up the stack. */
+		return 0;
+	}
+
+	if( sendpacket[16] == 0x90 &&
+	    sendpacket[17] == 0x04)
+	{
+		/* It's IPXWAN */
+
+		if( sendpacket[2] == 0x02 &&
+		    sendpacket[34] == 0x00)
+		{
+			/* It's a timer request packet */
+			printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname);
+
+			/* Go through the routing options and answer no to every */
+			/* option except Unnumbered RIP/SAP */
+			for(i = 41; sendpacket[i] == 0x00; i += 5)
+			{
+				/* 0x02 is the option for Unnumbered RIP/SAP */
+				if( sendpacket[i + 4] != 0x02)
+				{
+					sendpacket[i + 1] = 0;
+				}
+			}
+
+			/* Skip over the extended Node ID option */
+			if( sendpacket[i] == 0x04 )
+			{
+				i += 8;
+			}
+
+			/* We also want to turn off all header compression opt. */
+			for(; sendpacket[i] == 0x80 ;)
+			{
+				sendpacket[i + 1] = 0;
+				i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4;
+			}
+
+			/* Set the packet type to timer response */
+			sendpacket[34] = 0x01;
+
+			printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname);
+		}
+		else if( sendpacket[34] == 0x02 )
+		{
+			/* This is an information request packet */
+			printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname);
+
+			/* Set the packet type to information response */
+			sendpacket[34] = 0x03;
+
+			/* Set the router name */
+			sendpacket[51] = 'X';
+			sendpacket[52] = 'T';
+			sendpacket[53] = 'P';
+			sendpacket[54] = 'I';
+			sendpacket[55] = 'P';
+			sendpacket[56] = 'E';
+			sendpacket[57] = '-';
+			sendpacket[58] = CVHexToAscii(network_number >> 28);
+			sendpacket[59] = CVHexToAscii((network_number & 0x0F000000)>> 24);
+			sendpacket[60] = CVHexToAscii((network_number & 0x00F00000)>> 20);
+			sendpacket[61] = CVHexToAscii((network_number & 0x000F0000)>> 16);
+			sendpacket[62] = CVHexToAscii((network_number & 0x0000F000)>> 12);
+			sendpacket[63] = CVHexToAscii((network_number & 0x00000F00)>> 8);
+			sendpacket[64] = CVHexToAscii((network_number & 0x000000F0)>> 4);
+			sendpacket[65] = CVHexToAscii(network_number & 0x0000000F);
+			for(i = 66; i < 99; i+= 1)
+			{
+				sendpacket[i] = 0;
+			}
+
+			/* printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname); */
+		}
+		else
+		{
+			printk(KERN_WARNING "%s: Unknown IPXWAN packet!\n",devname);
+			return 0;
+		}
+
+		/* Set the WNodeID to our network address */
+		sendpacket[35] = (unsigned char)(network_number >> 24);
+		sendpacket[36] = (unsigned char)((network_number & 0x00FF0000) >> 16);
+		sendpacket[37] = (unsigned char)((network_number & 0x0000FF00) >> 8);
+		sendpacket[38] = (unsigned char)(network_number & 0x000000FF);
+
+		return 1;
+	} else {
+		/* If we get here its an IPX-data packet, so it'll get passed up the stack. */
+
+		/* switch the network numbers */
+		switch_net_numbers(sendpacket, network_number, 1);	
+		return 0;
+	}
+}
+
+/*
+   If incoming is 0 (outgoing)- if the net numbers is ours make it 0
+   if incoming is 1 - if the net number is 0 make it ours 
+
+*/
+static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming)
+{
+	unsigned long pnetwork_number;
+
+	pnetwork_number = (unsigned long)((sendpacket[6] << 24) + 
+			  (sendpacket[7] << 16) + (sendpacket[8] << 8) + 
+			  sendpacket[9]);
+
+	if (!incoming) {
+		/* If the destination network number is ours, make it 0 */
+		if( pnetwork_number == network_number) {
+			sendpacket[6] = sendpacket[7] = sendpacket[8] = 
+					 sendpacket[9] = 0x00;
+		}
+	} else {
+		/* If the incoming network is 0, make it ours */
+		if( pnetwork_number == 0) {
+			sendpacket[6] = (unsigned char)(network_number >> 24);
+			sendpacket[7] = (unsigned char)((network_number & 
+					 0x00FF0000) >> 16);
+			sendpacket[8] = (unsigned char)((network_number & 
+					 0x0000FF00) >> 8);
+			sendpacket[9] = (unsigned char)(network_number & 
+					 0x000000FF);
+		}
+	}
+
+
+	pnetwork_number = (unsigned long)((sendpacket[18] << 24) + 
+			  (sendpacket[19] << 16) + (sendpacket[20] << 8) + 
+			  sendpacket[21]);
+
+	if( !incoming ) {
+		/* If the source network is ours, make it 0 */
+		if( pnetwork_number == network_number) {
+			sendpacket[18] = sendpacket[19] = sendpacket[20] = 
+					 sendpacket[21] = 0x00;
+		}
+	} else {
+		/* If the source network is 0, make it ours */
+		if( pnetwork_number == 0 ) {
+			sendpacket[18] = (unsigned char)(network_number >> 24);
+			sendpacket[19] = (unsigned char)((network_number & 
+					 0x00FF0000) >> 16);
+			sendpacket[20] = (unsigned char)((network_number & 
+					 0x0000FF00) >> 8);
+			sendpacket[21] = (unsigned char)(network_number & 
+					 0x000000FF);
+		}
+	}
+} /* switch_net_numbers */
+
 
 /****** End *****************************************************************/
diff -u --recursive --new-file v2.1.78/linux/drivers/net/sdlamain.c linux/drivers/net/sdlamain.c
--- v2.1.78/linux/drivers/net/sdlamain.c	Thu Jul 17 10:06:05 1997
+++ linux/drivers/net/sdlamain.c	Mon Jan 12 14:46:16 1998
@@ -2,6 +2,7 @@
 * sdlamain.c	WANPIPE(tm) Multiprotocol WAN Link Driver.  Main module.
 *
 * Author:	Gene Kozin	<genek@compuserve.com>
+*		Jaspreet Singh	<jaspreet@sangoma.com>
 *
 * Copyright:	(c) 1995-1997 Sangoma Technologies Inc.
 *
@@ -10,6 +11,21 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
+* Nov 28, 1997	Jaspreet Singh	Changed DRV_RELEASE to 1
+* Nov 10, 1997	Jaspreet Singh	Changed sti() to restore_flags();
+* Nov 06, 1997 	Jaspreet Singh	Changed DRV_VERSION to 4 and DRV_RELEASE to 0
+* Oct 20, 1997 	Jaspreet Singh	Modified sdla_isr routine so that card->in_isr
+*				assignments are taken out and placed in the
+*				sdla_ppp.c, sdla_fr.c and sdla_x25.c isr
+*				routines. Took out 'wandev->tx_int_enabled' and
+*				replaced it with 'wandev->enable_tx_int'. 
+* May 29, 1997	Jaspreet Singh	Flow Control Problem
+*				added "wandev->tx_int_enabled=1" line in the
+*				init module. This line intializes the flag for 
+*				preventing Interrupt disabled with device set to
+*				busy
+* Jan 15, 1997	Gene Kozin	Version 3.1.0
+*				 o added UDP management stuff
 * Jan 02, 1997	Gene Kozin	Initial version.
 *****************************************************************************/
 
@@ -39,8 +55,8 @@
 #define	STATIC		static
 #endif
 
-#define	DRV_VERSION	3		/* version number */
-#define	DRV_RELEASE	0		/* release (minor version) number */
+#define	DRV_VERSION	4		/* version number */
+#define	DRV_RELEASE	1		/* release (minor version) number */
 #define	MAX_CARDS	8		/* max number of adapters */
 
 #ifndef	CONFIG_WANPIPE_CARDS		/* configurable option */
@@ -132,6 +148,7 @@
 		wandev->magic    = ROUTER_MAGIC;
 		wandev->name     = card->devname;
 		wandev->private  = card;
+		wandev->enable_tx_int = 0;
 		wandev->setup    = &setup;
 		wandev->shutdown = &shutdown;
 		wandev->ioctl    = &ioctl;
@@ -388,7 +405,7 @@
 /****** Driver IOCTL Hanlers ************************************************/
 
 /*============================================================================
- * Dump adpater memory to user buffer.
+ * Dump adapter memory to user buffer.
  * o verify request structure
  * o copy request structure to kernel data space
  * o verify length/offset
@@ -403,6 +420,7 @@
 	sdla_dump_t dump;
 	unsigned winsize;
 	unsigned long oldvec;	/* DPM window vector */
+	unsigned long flags;
 	int err = 0;
 
 	if(copy_from_user((void*)&dump, (void*)u_dump, sizeof(sdla_dump_t)))
@@ -413,6 +431,7 @@
 		return -EINVAL;
 		
 	winsize = card->hw.dpmsize;
+	save_flags(flags);
         cli();				/* >>> critical section start <<< */
 	oldvec = card->hw.vector;
 	while (dump.length)
@@ -438,7 +457,7 @@
 		(char*)dump.ptr += len;
 	}
 	sdla_mapmem(&card->hw, oldvec);	/* restore DPM window position */
-	sti();				/* >>> critical section end <<< */
+	restore_flags(flags);		/* >>> critical section end <<< */
 	return err;
 }
 
@@ -483,10 +502,10 @@
 		;
 		return;
 	}
-	card->in_isr = 1;
+
 	sdla_intack(&card->hw);
-	if (card->isr) card->isr(card);
-	card->in_isr = 0;
+	if (card->isr) 
+		card->isr(card);
 
 #undef	card
 }
@@ -507,13 +526,13 @@
 		sdla_t* card = &card_array[i];
 
 		if ((card->wandev.state != WAN_UNCONFIGURED) && card->poll &&
-		    !test_and_set_bit(0, (void*)&card->wandev.critical))
+		    !card->wandev.critical)
 		{
 			card->poll(card);
-			card->wandev.critical = 0;
 		}
 	}
-	if (active) queue_task(&sdla_tq, &tq_scheduler);
+	if (active)
+		queue_task(&sdla_tq, &tq_scheduler);
 }
 
 /*============================================================================
diff -u --recursive --new-file v2.1.78/linux/drivers/net/sunlance.c linux/drivers/net/sunlance.c
--- v2.1.78/linux/drivers/net/sunlance.c	Thu Sep  4 17:07:31 1997
+++ linux/drivers/net/sunlance.c	Mon Jan 12 15:28:18 1998
@@ -1,4 +1,4 @@
-/* $Id: sunlance.c,v 1.68 1997/08/15 06:44:36 davem Exp $
+/* $Id: sunlance.c,v 1.69 1998/01/09 16:42:52 jj Exp $
  * lance.c: Linux/Sparc/Lance driver
  *
  *	Written 1995, 1996 by Miguel de Icaza
@@ -53,12 +53,15 @@
  *
  * 1.10:
  *	 1/26/97: Modularize driver. (ecd@skynet.be)
+ *
+ * 1.11:
+ *	12/27/97: Added sun4d support. (jj@sunsite.mff.cuni.cz)
  */
 
 #undef DEBUG_DRIVER
 
 static char *version =
-	"sunlance.c:v1.10 26/Jan/97 Miguel de Icaza (miguel@nuclecu.unam.mx)\n";
+	"sunlance.c:v1.11 27/Dec/97 Miguel de Icaza (miguel@nuclecu.unam.mx)\n";
 
 static char *lancestr = "LANCE";
 static char *lancedma = "LANCE DMA";
@@ -679,14 +682,25 @@
 			printk ("Lance: Can't get irq %d\n", dev->irq);
 			return -EAGAIN;
 		}
+	}
+#else
+	if (sparc_cpu_model == sun4d) {
+		struct devid_cookie dcookie;
 
-	} else
-#endif
-	if (request_irq (dev->irq, &lance_interrupt, SA_SHIRQ,
+		dcookie.real_dev_id = dev;
+		dcookie.bus_cookie = (void *)dev->base_addr;
+		if(request_irq(dev->irq, &lance_interrupt,
+			       (SA_SHIRQ | SA_DCOOKIE),
+			       lancestr, &dcookie)) {
+			printk ("Lance: Can't get irq %d\n", dev->irq);
+			return -EAGAIN;
+		}
+	} else if (request_irq (dev->irq, &lance_interrupt, SA_SHIRQ,
 			 lancestr, (void *) dev)) {
 		printk ("Lance: Can't get irq %d\n", dev->irq);
 		return -EAGAIN;
 	}
+#endif	
 
 	/* Stop the Lance */
 	ll->rap = LE_CSR0;
diff -u --recursive --new-file v2.1.78/linux/drivers/net/tulip.c linux/drivers/net/tulip.c
--- v2.1.78/linux/drivers/net/tulip.c	Sat Nov 29 11:25:10 1997
+++ linux/drivers/net/tulip.c	Mon Jan 12 15:28:18 1998
@@ -1,7 +1,7 @@
-/* tulip.c: A DEC 21040 ethernet driver for linux. */
+/* tulip.c: A DEC 21040-family ethernet driver for linux. */
 /*
-   NOTICE: this version works with kernels 1.1.82 and later only!
-	Written 1994,1995 by Donald Becker.
+   NOTICE: THIS IS THE ALPHA TEST VERSION!
+	Written 1994-1997 by Donald Becker.
 
 	This software may be used and distributed according to the terms
 	of the GNU Public License, incorporated herein by reference.
@@ -13,47 +13,69 @@
 	Center of Excellence in Space Data and Information Sciences
 	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 
-	Subscribe to linux-tulip@cesdis.gsfc.nasa.gov and linux-tulip-bugs@cesdis.gsfc.nasa.gov
-	for late breaking news and exciting develovements.
-	
-	This has Baldur Norddahl's one liner fix for the AC/AE boards. If it
-	stops working please change TINTR_ENABLE to 0xFFFFFFFF
+	Support and updates available at
+	http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
 */
 
-static char *version =
-"tulip.c:v0.10 8/11/95 becker@cesdis.gsfc.nasa.gov\n"
-"        +0.72 4/17/96 "
-"http://www.dsl.tutics.tut.ac.jp/~linux/tulip\n"
-"        +0.02 12/15/96 mjacob@feral.com (2.0.27)\n";
+static const char *version = "tulip.c:v0.83 10/19/97 becker@cesdis.gsfc.nasa.gov\n";
 
 /* A few user-configurable values. */
 
-/* Default to using 10baseT (i.e. AUI/10base2/100baseT port) port. */
-#define	TULIP_10TP_PORT		0
-#define	TULIP_100TP_PORT	1
-#define	TULIP_AUI_PORT		1
-#define	TULIP_BNC_PORT		2
-#define	TULIP_MAX_PORT		3
-#define	TULIP_AUTO_PORT		-1
+/* Used to pass the full-duplex flag, etc. */
+static int full_duplex[8] = {0, };
+static int options[8] = {0, };
+static int mtu[8] = {0, };			/* Jumbo MTU for interfaces. */
+
+/* Set if the PCI BIOS detects the chips on a multiport board backwards. */
+#ifdef REVERSE_PROBE_ORDER
+static int reverse_probe = 1;
+#else
+static int reverse_probe = 0;
+#endif
+
+/* Keep the ring sizes a power of two for efficiency.
+   Making the Tx ring too large decreases the effectiveness of channel
+   bonding and packet priority.
+   There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE	16
+#define RX_RING_SIZE	16
+
+/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
+#ifdef __alpha__
+static const rx_copybreak = 1518;
+#else
+static const rx_copybreak = 100;
+#endif
 
-#ifndef	TULIP_PORT
-#define	TULIP_PORT			TULIP_10TP_PORT
+/* The following example shows how to always use the 10base2 port. */
+#ifdef notdef
+#define TULIP_DEFAULT_MEDIA 1		/* 1 == 10base2 */
+#define TULIP_NO_MEDIA_SWITCH		/* Don't switch from this port */
 #endif
 
 /* Define to force full-duplex operation on all Tulip interfaces. */
 /* #define  TULIP_FULL_DUPLEX 1 */
 
-/* Define to fix port. */
-/* #define  TULIP_FIX_PORT 1 */
-
-/* Define to probe only first detected device */
-/*#define	TULIP_MAX_CARDS 1*/
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  ((2000*HZ)/1000)
 
+#include <linux/config.h>
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
 #include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
+#include <linux/timer.h>
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
@@ -61,9 +83,7 @@
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/bios32.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <asm/byteorder.h>
+#include <asm/processor.h>		/* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -72,35 +92,116 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+   This is only in the support-all-kernels source code. */
+#include <linux/version.h>		/* Evil, but neccessary */
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300
+#define RUN_AT(x) (x)			/* What to put in timer->expires.  */
+#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
+#define virt_to_bus(addr)  ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+
+#else  /* 1.3.0 and later */
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338
+#ifdef MODULE
+#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#else
+#undef MOD_INC_USE_COUNT
+#define MOD_INC_USE_COUNT
+#undef MOD_DEC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+#endif /* 1.3.38 */
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#endif
+#if (LINUX_VERSION_CODE >= 0x20100)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#ifdef SA_SHIRQ
+#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
+#else
+#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
+#endif
+
+#if (LINUX_VERSION_CODE < 0x20123)
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
+
+/* This my implementation of shared IRQs, now only used for 1.2.13. */
+#ifdef HAVE_SHARED_IRQ
+#define USE_SHARED_IRQ
+#include <linux/shared_irq.h>
+#endif
+
 /* The total size is unusually large: The 21040 aligns each of its 16
    longword-wide registers on a quadword boundary. */
 #define TULIP_TOTAL_SIZE 0x80
 
+#ifdef HAVE_DEVLIST
+struct netdev_entry tulip_drv =
+{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL};
+#endif
+
+#ifdef TULIP_DEBUG
+int tulip_debug = TULIP_DEBUG;
+#else
+int tulip_debug = 1;
+#endif
+
 /*
 				Theory of Operation
 
 I. Board Compatibility
 
-This device driver is designed for the DECchip 21040 "Tulip", Digital's
-single-chip ethernet controller for PCI, as used on the SMC EtherPower
-ethernet adapter.  It also works with boards based the 21041 (new/experimental)
-and 21140 (10/100mbps).
+This device driver is designed for the DECchip "Tulip", Digital's
+single-chip ethernet controllers for PCI.  Supported members of the family
+are the 21040, 21041, 21140, 21140A and 21142.  These chips are used on
+many PCI boards including the SMC EtherPower series.
 
 
 II. Board-specific settings
 
 PCI bus devices are configured by the system at boot time, so no jumpers
-need to be set on the board.  The system BIOS should be set to assign the
-PCI INTA signal to an otherwise unused system IRQ line.  While it's
-physically possible to shared PCI interrupt lines, the kernel doesn't
-support it. 
+need to be set on the board.  The system BIOS preferably should assign the
+PCI INTA signal to an otherwise unused system IRQ line.
+Note: Kernel versions earlier than 1.3.73 do not support shared PCI
+interrupt lines.
 
 III. Driver operation
 
 IIIa. Ring buffers
+
 The Tulip can use either ring buffers or lists of Tx and Rx descriptors.
-The current driver uses a statically allocated Rx ring of descriptors and
-buffers, and a list of the Tx buffers.
+This driver uses statically allocated rings of Rx and Tx descriptors, set at
+compile time by RX/TX_RING_SIZE.  This version of the driver allocates skbuffs
+for the Rx ring buffers at open() time and passes the skb->data field to the
+Tulip as receive data buffers.  When an incoming frame is less than
+RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is
+copied to the new skbuff.  When the incoming frame is larger, the skbuff is
+passed directly up the protocol stack and replaced by a newly allocated
+skbuff.
+
+The RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames.  For small frames the copying cost is negligible (esp. considering
+that we are pre-loading the cache with immediately useful header
+information).  For large frames the copying cost is non-trivial, and the
+larger copy might flush the cache of useful data.  A subtle aspect of this
+choice is that the Tulip only receives into longword aligned buffers, thus
+the IP header at offset 14 isn't longword aligned for further processing.
+Copied frames are put into the new skbuff at an offset of "+2", thus copying
+has the beneficial effect of aligning the IP header and preloading the
+cache.
 
 IIIC. Synchronization
 The driver runs as two independent, single-threaded flows of control.  One
@@ -124,659 +225,1468 @@
 
 Thanks to Duke Kamstra of SMC for providing an EtherPower board.
 
+IVb. References
+
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+http://www.digital.com  (search for current 21*4* datasheets and "21X4 SROM")
+http://www.national.com/pf/DP/DP83840.html
+
+IVc. Errata
+
 The DEC databook doesn't document which Rx filter settings accept broadcast
 packets.  Nor does it document how to configure the part to configure the
 serial subsystem for normal (vs. loopback) operation or how to have it
 autoswitch between internal 10baseT, SIA and AUI transceivers.
 
-The databook claims that CSR13, CSR14, and CSR15 should each be the last
-register of the set CSR12-15 written.   Hmmm, now how is that possible?
-*/
+The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last
+register of the set CSR12-15 written.  Hmmm, now how is that possible?  */
+
 
 /* A few values that may be tweaked. */
-/* Keep the ring sizes a power of two for efficiency. */
-#define TX_RING_SIZE	4
-#define RX_RING_SIZE	4
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
-/* This is a mysterious value that can be written to CSR11 in the 21040
-   to detect a full-duplex frame.  No one knows what it should be, but if
-   left at its default value some 10base2(!) packets trigger a
-   full-duplex-request interrupt. */
+/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
+   to support a pre-NWay full-duplex signaling mechanism using short frames.
+   No one knows what it should be, but if left at its default value some
+   10base2(!) packets trigger a full-duplex-request interrupt. */
 #define FULL_DUPLEX_MAGIC	0x6969
 
+#ifndef PCI_VENDOR_ID_DEC		/* Now defined in linux/pci.h */
+#define PCI_VENDOR_ID_DEC			0x1011
+#define PCI_DEVICE_ID_TULIP			0x0002		/* 21040. */
+#define PCI_DEVICE_ID_TULIP_FAST	0x0009		/* 21140. */
+#endif
+
+#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS
+#define PCI_DEVICE_ID_DEC_TULIP_PLUS	0x0014		/* 21041. */
+#endif
+#ifndef PCI_DEVICE_ID_DEC_TULIP_21142
+#define PCI_DEVICE_ID_DEC_TULIP_21142	0x0019
+#endif
+
+#ifndef PCI_VENDOR_ID_LITEON
+#define PCI_VENDOR_ID_LITEON	0x11AD
+#define PCI_DEVICE_ID_PNIC		0x0002
+#define PCI_DEVICE_ID_PNIC_X	0x0168
+#else
+/* Now PCI_VENDOR_ID_LITEON is defined, but device IDs have different names */
+#define PCI_DEVICE_ID_PNIC		PCI_DEVICE_ID_LITEON_LNE100TX
+#define PCI_DEVICE_ID_PNIC_X	0x0168
+#endif
+
 /* The rest of these values should never change. */
 
-#define PCI_DEVICE_ID_NONE	0xFFFF
-#define	ETHNAMSIZ	8
-#define	ROUND_UP(size, n)	((size + n - 1) & ~(n - 1))
+static void tulip_timer(unsigned long data);
+
+/* A table describing the chip types. */
+static struct tulip_chip_table {
+  int device_id;
+  char *chip_name;
+  int flags;
+  void (*media_timer)(unsigned long data);
+} tulip_tbl[] = {
+  { PCI_DEVICE_ID_DEC_TULIP, "Digital DS21040 Tulip", 0, tulip_timer },
+  { PCI_DEVICE_ID_DEC_TULIP_PLUS, "Digital DS21041 Tulip", 0, tulip_timer },
+  { PCI_DEVICE_ID_DEC_TULIP_FAST, "Digital DS21140 Tulip", 0, tulip_timer },
+  { PCI_DEVICE_ID_DEC_TULIP_21142, "Digital DS21142/3 Tulip", 0, tulip_timer },
+  { PCI_DEVICE_ID_PNIC_X, "Lite-On 82c168 PNIC", 0, tulip_timer },
+  {0, 0, 0, 0},
+};
+/* This matches the table above. */
+enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, LC82C168};
+
+static const char * const medianame[] = {
+  "10baseT", "10base2", "AUI", "100baseTx",
+  "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx",
+  "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII",
+  "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
+};
+/* A full-duplex map for above. */
+static const char media_fd[] =
+{0,0,0,0,  0xff,0xff,0,0,  0xff,0,0xff,0x01, 0,0,0xff,0 };
+/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/
+static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
+static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
+static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
+
+static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
+static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, };
+static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
 
 /* Offsets to the Command and Status Registers, "CSRs".  All accesses
    must be longword instructions and quadword aligned. */
 enum tulip_offsets {
-				/* 21040		21041		21140		*/
-	CSR0=0,		/* BUS mode								*/
-    CSR1=0x08,	/* TX poll demand						*/
-	CSR2=0x10,	/* RX poll demand						*/
-	CSR3=0x18,	/* RX ring base addr					*/
-	CSR4=0x20,	/* TX ring base addr					*/
-	CSR5=0x28,	/* Status								*/
-	CSR6=0x30,	/* Command mode							*/
-	CSR7=0x38,	/* Interrupt Mask						*/
-	CSR8=0x40,	/* Missed frame counter					*/
-	CSR9=0x48,	/* Eth.addrROM	SROM mii	SROM mii	*/
-	CSR10=0x50,	/* Diagn.		boot ROM	-			*/
-	CSR11=0x58,	/* Full duplex	G.P. timer	G.P. timer	*/
-	CSR12=0x60,	/* SIA status				G.P.		*/
-	CSR13=0x68,	/* SIA connectivity			-			*/
-	CSR14=0x70,	/* SIA TX/RX				-			*/
-	CSR15=0x78	/* SIA general				watchdog	*/
+	CSR0=0,    CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
+	CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
+	CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
+
+/* The bits in the CSR5 status registers, mostly interrupt sources. */
+enum status_bits {
+	TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
+	RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
+	TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02,
+	TxIntr=0x01,
 };
 
-/* description of CSR0 bus mode register */
-#define	TBMOD_RESERVED		0xfff80000	/* I don't know */
-#define	TBMOD_RESET			0x00000001
-#define	TBMOD_BIGENDIAN		0x00000080
-/*
-	   Cache alignment bits 15:14	     Burst length 13:8
-    	0000	No alignment  0x00000000 unlimited		0800 8 longwords
-		4000	8  longwords		0100 1 longword		1000 16 longwords
-		8000	16 longwords		0200 2 longwords	2000 32 longwords
-		C000	32  longwords		0400 4 longwords
-*/
-#define	TBMOD_ALIGN0		0x00000000	/* no cache alignment */
-#define	TBMOD_ALIGN8		0x00004000	/* 8 longwords */
-#define	TBMOD_ALIGN16		0x00008000
-#define	TBMOD_ALIGN32		(TBMOD_ALIGN8|TBMOD_ALIGN16)
-#define	TBMOD_BURST0		0x00000000	/* unlimited=rx buffer size */
-#define	TBMOD_BURST1		0x00000100	/* 1 longwords */
-#define	TBMOD_BURST2		0x00000200
-#define	TBMOD_BURST4		0x00000400
-#define	TBMOD_BURST8		0x00000800
-#define	TBMOD_BURST16		0x00001000
-#define	TBMOD_BURST32		0x00002000
-
-/* description of CSR1 Tx poll demand register */
-/* description of CSR2 Rx poll demand register */
-#define	TPOLL_START			0x00000001	/* ? */
-#define	TPOLL_TRIGGER		0x00000000	/* ? */
-
-/* description of CSR5 status register from de4x5.h */
-#define	TSTAT_BUSERROR		0x03800000
-#define	TSTAT_SYSERROR		0x00002000
-#define	TSTAT_TxSTAT		0x00700000
-#define	TSTAT_RxSTAT		0x000e0000
-#define	TSTAT_LKFAIL		0x00001000
-#define	TSTAT_NORINTR		0x00010000	/* Normal interrupt */
-#define	TSTAT_ABNINTR		0x00008000	/* Abnormal interrupt */
-#define	TSTAT_RxMISSED		0x00000100	/* Rx frame missed */
-#define	TSTAT_RxUNABL		0x00000080
-#define	TSTAT_RxINTR		0x00000040
-#define	TSTAT_LKPASS		0x00000010
-#define	TSTAT_TEXPIRED		0x00000800	/* Timer Expired */
-#define	TSTAT_TxTOUT		0x00000008
-#define	TSTAT_TxUNABL		0x00000004
-#define	TSTAT_TxINTR		0x00000001
-#define	TSTAT_CLEARINTR		0x0001ffff	/* clear all interrupt sources */
-
-/* description of CSR6 command mode register */
-#define	TCMOD_SCRM			0x01000000	/* scrambler mode */
-#define	TCMOD_PCS			0x00800000	/* PCS function */
-#define	TCMOD_TxTHMODE		0x00400000	/* Tx threshold mode */
-#define	TCMOD_SW100TP		0x00040000  /* 21140: 100MB */
-#define	TCMOD_CAPTURE		0x00020000	/* capture effect */
-#define	TCMOD_FULLDUPLEX	0x00000200
-#define TCMOD_TH128			0x00008000	/* 10 - 128 bytes threshold */
-#define	TCMOD_TxSTART		0x00002000
-#define	TCMOD_RxSTART		0x00000002
-#define	TCMOD_ALLMCAST		0x00000080	/* pass all multicast */
-#define	TCMOD_PROMISC		0x00000040	/* promisc */
-#define	TCMOD_BOFFCOUNTER	0x00000020	/* backoff counter */
-#define	TCMOD_INVFILTER		0x00000010	/* invert filtering */
-#define	TCMOD_HONLYFILTER	0x00000004	/* hash only filtering */
-#define TCMOD_HPFILTER		0x00000001	/* hash/perfect Rx filtering */
-#define	TCMOD_MODEMASK		(TCMOD_ALLMCAST|TCMOD_PROMISC)
-#define	TCMOD_FILTERMASK	(TCMOD_HONLYFILTER|TCMOD_HPFILTER|TCMOD_INVFILTER)
-#define	TCMOD_TRxSTART		(TCMOD_TxSTART|TCMOD_RxSTART)
-#define	TCMOD_BASE			(TCMOD_CAPTURE|TCMOD_BOFFCOUNTER)
-#define	TCMOD_10TP			(TCMOD_TxTHMODE|TCMOD_BASE)
-#define	TCMOD_100TP			(TCMOD_SCRM|TCMOD_PCS|TCMOD_SW100TP|TCMOD_BASE)
-#define	TCMOD_AUTO			(TCMOD_SW100TP|TCMOD_TH128|TCMOD_10TP)
-
-/* description of CSR7 interrupt mask register */
-#define	TINTR_ENABLE		0xFFFFBBFF
-#define	TINTR_DISABLE		0x00000000
-
-/* description of CSR11 G.P. timer (21041/21140) register */
-#define	TGEPT_COUNT			0x0001FFFF
-
-/* description of CSR12 SIA status(2104x)/GP(21140) register */
-#define	TSIAS_CONERROR		0x00000002	/* connection error */
-#define	TSIAS_LNKERROR		0x00000004	/* link error */
-#define TSIAS_ACTERROR		0x00000200  /* port Rx activity */
-#define TSIAS_RxACTIVE		0x00000100  /* port Rx activity */
-
-#define	TGEPR_LK10NG		0x00000080	/* 10Mbps N.G. (R) */
-#define	TGEPR_LK100NG		0x00000040	/* 100Mbps N.G. (R) */
-#define	TGEPR_DETECT		0x00000020	/* detect signal (R) */
-#define	TGEPR_HALFDUPLEX	0x00000008	/* half duplex (W) */
-#define	TGEPR_PHYLOOPBACK	0x00000004	/* PHY loopback (W) */
-#define	TGEPR_FORCEALED		0x00000002	/* force activity LED on (W) */
-#define	TGEPR_FORCE100		0x00000001	/* force 100Mbps mode */
-
-/* description of CSR13 SIA connectivity register */
-#define	TSIAC_OUTEN			0x0000e000	/* 21041: Output enable */
-#define TSIAC_SELED			0x00000f00	/* 21041: AUI or TP with LEDs */
-#define	TSIAC_INEN			0x00001000	/* 21041: Input enable */
-#define	TSIAC_NO10TP		0x00000008	/* 10baseT(0) or not(1) */
-#define TSIAC_CONFIG		0x00000004	/* Configuration */
-#define	TSIAC_SWRESET		0x00000001	/* 21041: software reset */
-#define	TSIAC_RESET			0x00000000	/* reset */
-#define	TSIAC_C21041		(TSIAC_OUTEN|TSIAC_SELED|TSIAC_SWRESET)
-#define	TSIAC_C21040		TSIAC_CONFIG
-
-/* description of CSR14 SIA TX/RX register */
-#define	TSIAX_NO10TP		0x0000f73d
-#define	TSIAX_10TP			0x0000ff3f
-
-/* description of CSR15 SIA general register */
-#define	TSIAG_SWBNCAUI		0x00000008 /* BNC(0) or AUI(1) */
-#define	TSIAG_BNC			0x00000006
-#define	TSIAG_AUI			(TSIAG_BNC|TSIAG_SWBNCAUI)
-#define	TSIAG_10TP			0x00000000
-
-/* description of rx_ring.status */
-#define	TRING_OWN			0x80000000	/* Owned by chip */
-#define	TRING_CLEAR			0x00000000	/* clear */
-#define	TRING_ERROR			0x00008000	/* error summary */
-#define	TRING_ETxTO			0x00004000	/* Tx time out */
-#define	TRING_ELCOLL		0x00000200	/* late collision */
-#define	TRING_EFCOLL		0x00000100	/* fatal collision */
-#define	TRING_ELCARR		0x00000800	/* carrier lost */
-#define	TRING_ENCARR		0x00000400	/* no carrier */
-#define	TRING_ENOHB			0x00000080	/* heartbeat fail */
-#define	TRING_ELINK			0x00000004	/* link fail */
-#define	TRING_EUFLOW		0x00000002	/* underflow */
-
-#define	TRING_ELEN			0x00004000	/* length error */
-#define	TRING_FDESC			0x00000200	/* first descriptor */
-#define	TRING_LDESC			0x00000100	/* last descriptor */
-#define	TRING_ERUNT			0x00000800	/* runt frame */
-#define	TRING_ELONG			0x00000080	/* frame too long */
-#define	TRING_EWATCHDOG		0x00000010	/* receive watchdog */
-#define	TRING_EDRBIT		0x00000004	/* dribble bit */
-#define	TRING_ECRC			0x00000002	/* CRC error */
-#define	TRING_EOVERFLOW		0x00000001	/* overflow */
-
-#define	TRING_RxDESCMASK	(TRING_FDESC|TRING_LDESC)
-#define	TRING_RxLENGTH		(TRING_ERUNT|TRING_ELONG|TRING_EWATCHDOG)
-#define	TRING_RxFRAME		(TRING_EDRBIT)
-#define	TRING_RxCRC			(TRING_ECRC)
-#define	TRING_RxFIFO		(TRING_EOVERFLOW)
-#define	TRING_TxABORT		(TRING_ETxTO|TRING_EFCOLL|TRING_ELINK)
-#define	TRING_TxCARR		(TRING_ELCARR|TRING_ENCARR)
-#define	TRING_TxWINDOW		(TRING_ELCOLL)
-#define	TRING_TxFIFO		(TRING_EUFLOW)
-#define	TRING_TxHEARTBEAT	(TRING_ENOHB)
 /* The Tulip Rx and Tx buffer descriptors. */
 struct tulip_rx_desc {
 	s32 status;
 	s32 length;
-	u32 buffer1, buffer2;			/* We use only buffer 1.  */
+	u32 buffer1, buffer2;
 };
 
 struct tulip_tx_desc {
 	s32 status;
 	s32 length;
-	u32 buffer1, buffer2;			/* We use only buffer 1.  */
+	u32 buffer1, buffer2;				/* We use only buffer 1.  */
+};
+
+struct medialeaf {
+	u8 type;
+	u8 media;
+	unsigned char *leafdata;
+};
+
+struct mediatable {
+	u16 defaultmedia;
+	u8 leafcount, csr12dir;				/* General purpose pin directions. */
+	unsigned has_mii:1;
+	struct medialeaf mleaf[0];
+};
+
+struct mediainfo {
+	struct mediainfo *next;
+	int info_type;
+	int index;
+	struct non_mii { char media; unsigned char csr12val; char bitnum, flags;}  non_mii;
+	unsigned char *info;
 };
 
 struct tulip_private {
+	char devname[8];			/* Used only for kernel debugging. */
+	const char *product_name;
+	struct device *next_module;
 	struct tulip_rx_desc rx_ring[RX_RING_SIZE];
 	struct tulip_tx_desc tx_ring[TX_RING_SIZE];
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
 	struct sk_buff* tx_skbuff[TX_RING_SIZE];
-	char rx_buffs[RX_RING_SIZE][PKT_BUF_SZ];
-	/* temporary Rx buffers. */
+	/* The addresses of receive-in-place skbuffs. */
+	struct sk_buff* rx_skbuff[RX_RING_SIZE];
+	char *rx_buffs;				/* Address of temporary Rx buffers. */
+	u32 setup_frame[48];		/* Pseudo-Tx frame to init address table. */
+	int chip_id;
+	int revision;
+#if LINUX_VERSION_CODE > 0x20139
 	struct net_device_stats stats;
-	int setup_frame[48];	/* Pseudo-Tx frame to init address table. */
-	void (*port_select)(struct device *dev);
-	int (*port_fail)(struct device *dev);
-	struct device *next_module;
-	char *signature;
+#else
+	struct enet_statistics stats;
+#endif
+	struct timer_list timer;	/* Media selection timer. */
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+	int fc_bit;
+#endif
 	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
 	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
 	unsigned int tx_full:1;				/* The Tx queue is full. */
 	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
-	unsigned int port_fix:1;			/* Fix if_port to specified port. */
+	unsigned int full_duplex_lock:1;
+	unsigned int default_port:4;		/* Last dev->if_port value. */
+	unsigned int media2:4;				/* Secondary monitored media port. */
+	unsigned int medialock:1;			/* Don't sense media type. */
+	unsigned int mediasense:1;			/* Media sensing in progress. */
+	unsigned int csr6;					/* Current CSR6 control settings. */
+	unsigned char eeprom[128];			/* Serial EEPROM contents. */
+	signed char phys[4];				/* MII device addresses. */
+	struct mediatable *mtable;
+	int cur_index;						/* Current media index. */
+	unsigned char pci_bus, pci_device_fn;
+	int pad0, pad1;						/* Used for 8-byte alignment */
 };
 
-struct eeprom {
-    union {
-		struct { /* broken EEPROM structure */
-			u_char addr[ETH_ALEN];
-		} ng;
-		struct { /* DEC EtherWorks
-					and other cards which have correct eeprom structure */
-			u_char dum1[20];
-			u_char addr[ETH_ALEN];
-		} ok;
-    } hw;
-#define	ng_addr	hw.ng.addr
-#define	ok_addr	hw.ok.addr
-#define	EE_SIGNLEN	14		/* should be 102 ? */
-	u_char sign[EE_SIGNLEN];
-};
-
-static int read_eeprom(unsigned long ioaddr, struct eeprom *eepp);
+static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
+								   int chip_id, int options);
+static void parse_eeprom(struct device *dev);
+static int read_eeprom(int ioaddr, int location);
+static int mdio_read(int ioaddr, int phy_id, int location);
+static void select_media(struct device *dev, int startup);
 static int tulip_open(struct device *dev);
+static void tulip_timer(unsigned long data);
+static void tulip_tx_timeout(struct device *dev);
 static void tulip_init_ring(struct device *dev);
 static int tulip_start_xmit(struct sk_buff *skb, struct device *dev);
 static int tulip_rx(struct device *dev);
-static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs);
 static int tulip_close(struct device *dev);
-static struct net_device_stats *tulip_get_stats(struct device *dev);
+static struct enet_statistics *tulip_get_stats(struct device *dev);
+#ifdef NEW_MULTICAST
 static void set_multicast_list(struct device *dev);
+#else
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+#endif
+#ifdef CONFIG_NET_FASTROUTE
+#include <linux/if_arp.h>
+#include <net/ip.h>
+
+static int tulip_accept_fastpath(struct device *dev, struct dst_entry *dst);
+#endif
 
-#define	generic21140_fail	NULL
-static void generic21040_select(struct device *dev);
-static void generic21140_select(struct device *dev);
-static void generic21041_select(struct device *dev);
-static void auto21140_select(struct device *dev);
-static void cogent21140_select(struct device *dev);
-static int generic21040_fail(struct device *dev);
-static int generic21041_fail(struct device *dev);
+
 
 #ifdef MODULE
 /* A list of all installed Tulip devices, for removing the driver module. */
 static struct device *root_tulip_dev = NULL;
 #endif
 
-static struct {
-	void (*port_select)(struct device *dev);
-	int (*port_fail)(struct device *dev);
-	unsigned int vendor_id, device_id;
-	char *signature;
-	unsigned int array:1;
-} cardVendor[] = {
-	{generic21140_select, generic21140_fail,
-		 0x0000c000, PCI_DEVICE_ID_DEC_TULIP_FAST, "smc9332", 0},
-	{generic21041_select, generic21041_fail,
-		 0x0000c000, PCI_DEVICE_ID_DEC_TULIP_PLUS, "smc8432", 0},
-	{generic21040_select, generic21040_fail,
-		 0x0000c000, PCI_DEVICE_ID_DEC_TULIP, "old smc8432", 0},
-	{auto21140_select, generic21140_fail,
-		 0x0000f400, PCI_DEVICE_ID_DEC_TULIP_FAST, "LA100PCI", 0},
-	{cogent21140_select, generic21140_fail,
-		 0x00009200, PCI_DEVICE_ID_DEC_TULIP_FAST, "cogent_em110", 0},
-	{generic21040_select, generic21040_fail,
-		0x00009200, PCI_DEVICE_ID_DEC_TULIP, "cogent_em96x", 1},
-	{generic21140_select, generic21140_fail,
-		 0x0000f800, PCI_DEVICE_ID_DEC_TULIP_FAST, "DE500", 0},
-	{generic21041_select, generic21041_fail,
-		 0x0000f800, PCI_DEVICE_ID_DEC_TULIP_PLUS, "DE450", 0},
-	{generic21040_select, generic21040_fail,
-		 0x0000f800, PCI_DEVICE_ID_DEC_TULIP, "DE43x", 0},
-	{generic21040_select, generic21040_fail,
-		 0x0040c700, PCI_DEVICE_ID_DEC_TULIP, "EN9400", 0},
-	{generic21040_select, generic21040_fail,
-		 0x00c09500, PCI_DEVICE_ID_DEC_TULIP, "ZNYX312", 1},
-	{generic21040_select, generic21040_fail,
-		 0x08002b00, PCI_DEVICE_ID_DEC_TULIP, "QSILVER's", 0},
-	{generic21040_select, generic21040_fail,
-		 0, PCI_DEVICE_ID_DEC_TULIP, "21040", 0},
-	{generic21140_select, generic21140_fail,
-		 0, PCI_DEVICE_ID_DEC_TULIP_FAST, "21140", 0},
-	{generic21041_select, generic21041_fail,
-		 0, PCI_DEVICE_ID_DEC_TULIP_PLUS, "21041", 0},
-	{NULL, NULL, 0, 0, "Unknown", 0}
-};
+/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region,
+   but now receives directly into full-sized skbuffs that are allocated
+   at open() time.
+   This allows the probe routine to use the old driver initialization
+   interface. */
+
+int tulip_probe(struct device *dev)
+{
+	int cards_found = 0;
+	static int pci_index = 0;	/* Static, for multiple probe calls. */
+
+	/* Ideally we would detect all network cards in slot order.  That would
+	   be best done a central PCI probe dispatch, which wouldn't work
+	   well with the current structure.  So instead we detect just the
+	   Tulip cards in slot order. */
+
+	if (pcibios_present()) {
+		unsigned char pci_bus, pci_device_fn;
+
+		for (;pci_index < 0xff; pci_index++) {
+			unsigned char pci_irq_line, pci_latency;
+			unsigned short pci_command, vendor, device;
+			unsigned int pci_ioaddr, chip_idx = 0;
+
+			if (pcibios_find_class
+				(PCI_CLASS_NETWORK_ETHERNET << 8,
+				 reverse_probe ? 0xfe - pci_index : pci_index,
+				 &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
+				if (reverse_probe)
+					continue;
+				else
+					break;
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+									 PCI_VENDOR_ID, &vendor);
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+									 PCI_DEVICE_ID, &device);
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_INTERRUPT_LINE, &pci_irq_line);
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+									  PCI_BASE_ADDRESS_0, &pci_ioaddr);
+			/* Remove I/O space marker in bit 0. */
+			pci_ioaddr &= ~3;
 
-
-/* Serial EEPROM section.
-   A "bit" grungy, but we work our way through bit-by-bit :->. */
-/*  EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK	0x02	/* EEPROM shift clock. */
-#define EE_CS			0x01	/* EEPROM chip select. */
-#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
-#define EE_WRITE_0		0x01
-#define EE_WRITE_1		0x05
-#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
-#define EE_ENB			(0x4800 | EE_CS)
+			if (vendor != PCI_VENDOR_ID_DEC
+				&& vendor != PCI_VENDOR_ID_LITEON)
+				continue;
+			if (vendor == PCI_VENDOR_ID_LITEON)
+				device = PCI_DEVICE_ID_PNIC_X;
 
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD	(5 << 6)
-#define EE_READ_CMD		(6 << 6)
-#define EE_ERASE_CMD	(7 << 6)
+			for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++)
+				if (device == tulip_tbl[chip_idx].device_id)
+					break;
+			if (tulip_tbl[chip_idx].chip_name == 0) {
+				printk(KERN_INFO "Unknown Digital PCI ethernet chip type"
+					   " %4.4x"" detected: not configured.\n", device);
+				continue;
+			}
+			if (tulip_debug > 2)
+				printk(KERN_DEBUG "Found DEC PCI Tulip at I/O %#x, IRQ %d.\n",
+					   pci_ioaddr, pci_irq_line);
+
+			if (check_region(pci_ioaddr, TULIP_TOTAL_SIZE))
+				continue;
 
 #ifdef MODULE
-static int if_port=TULIP_AUTO_PORT;
-#ifdef TULIP_FULL_DUPLEX
-static int full_duplex=1;
+			dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx,
+						 cards_found);
 #else
-static int full_duplex=0;
-#endif
+			dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, -1);
 #endif
 
-#define	tio_write(val, port)	outl(val, ioaddr + port)
-#define	tio_read(port)			inl(ioaddr + port)
+			if (dev) {
+			  /* Get and check the bus-master and latency values. */
+			  pcibios_read_config_word(pci_bus, pci_device_fn,
+									   PCI_COMMAND, &pci_command);
+			  if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+				printk(KERN_INFO "  PCI Master Bit has not been set! Setting...\n");
+				pci_command |= PCI_COMMAND_MASTER;
+				pcibios_write_config_word(pci_bus, pci_device_fn,
+										  PCI_COMMAND, pci_command);
+			  }
+			  pcibios_read_config_byte(pci_bus, pci_device_fn,
+									   PCI_LATENCY_TIMER, &pci_latency);
+			  if (pci_latency < 10) {
+				printk(KERN_INFO "  PCI latency timer (CFLT) is unreasonably"
+					   " low at %d.  Setting to 64 clocks.\n", pci_latency);
+				pcibios_write_config_byte(pci_bus, pci_device_fn,
+										  PCI_LATENCY_TIMER, 64);
+			  } else if (tulip_debug > 1)
+				printk(KERN_INFO "  PCI latency timer (CFLT) is %#x.\n",
+					   pci_latency);
+			  /* Bring the 21143 out power-down mode. */
+			  if (device == PCI_DEVICE_ID_DEC_TULIP_21142)
+				pcibios_write_config_dword(pci_bus, pci_device_fn,
+										  	0x40, 0x40000000);
+			  dev = 0;
+			  cards_found++;
+			}
+		}
+	}
 
-static void inline
-tio_sia_write(unsigned long ioaddr, u32 val13, u32 val14, u32 val15)
-{
-	tio_write(0,CSR13);
-	tio_write(val15,CSR15);
-	tio_write(val14,CSR14);
-	tio_write(val13,CSR13);
+	return cards_found ? 0 : -ENODEV;
 }
 
-/*
-   card_type returns 1 if the card is 'etherarray'
-*/
-
-__initfunc(static int
-card_type(struct tulip_private *tp, int device_id, int vendor_id))
+static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
+								   int chip_id, int board_idx)
 {
-	int n;
+	static int did_version = 0;			/* Already printed version info. */
+	struct tulip_private *tp;
+	/* See note below on the multiport cards. */
+	static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
+	static int last_irq = 0;
+	int i;
+	unsigned short sum;
 
-	for (n = 0; cardVendor[n].device_id; n ++)
-		if (cardVendor[n].device_id == device_id
-			&& (cardVendor[n].vendor_id == vendor_id
-				|| cardVendor[n].vendor_id == 0)) break;
-	tp->port_select = cardVendor[n].port_select;
-	tp->port_fail = cardVendor[n].port_fail;
-	tp->signature = cardVendor[n].signature;
-	return(cardVendor[n].array ? 1: 0);
-}
-
-__initfunc(static int
-read_eeprom(unsigned long ioaddr, struct eeprom *eepp))
-{
-    int i, n;
-    unsigned short val = 0;
-    int read_cmd = EE_READ_CMD;
-    u_char *p=(u_char *)eepp;
-
-	for (n = 0; n < sizeof(struct eeprom) / 2; n ++, read_cmd ++) {
-		tio_write(EE_ENB & ~EE_CS, CSR9);
-		tio_write(EE_ENB, CSR9);
-
-		/* Shift the read command bits out. */
-		for (i = 10; i >= 0; i--) {
-			short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
-			tio_write(EE_ENB | dataval, CSR9);
-			udelay(100);
-			tio_write(EE_ENB | dataval | EE_SHIFT_CLK, CSR9);
-			udelay(150);
-			tio_write(EE_ENB | dataval, CSR9);
-			udelay(250);
-		}
-		tio_write(EE_ENB, CSR9);
-
-		for (i = 16; i > 0; i--) {
-			tio_write(EE_ENB | EE_SHIFT_CLK, CSR9);
-			udelay(100);
-			val = (val << 1)
-				| ((tio_read(CSR9) & EE_DATA_READ) ? 1 : 0);
-			tio_write(EE_ENB, CSR9);
-			udelay(100);
-		}
-
-		/* Terminate the EEPROM access. */
-		tio_write(EE_ENB & ~EE_CS, CSR9);
-		*p ++ = le16_to_cpu(val);
-		*p ++ = le16_to_cpu(val) >> 8;
-    }
-	/* broken eeprom ? */
-	p = (u_char *)eepp;
-	for (i = 0; i < 8; i ++)
-		if (p[i] != p[15 - i] || p[i] != p[16 + i]) return(0);
-	return(-1); /* broken */
-}
+	if (tulip_debug > 0  &&  did_version++ == 0)
+		printk(KERN_INFO "%s", version);
 
-/* Is this required ? */
-static int
-generic21040_fail(struct device *dev)
-{
-	unsigned long ioaddr = dev->base_addr;
+	dev = init_etherdev(dev, 0);
 
-	return(tio_read(CSR12) & TSIAS_CONERROR);
-}
+	printk(KERN_INFO "%s: %s at %#3x,",
+		   dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
 
-static int
-generic21041_fail(struct device *dev)
-{
-	unsigned long ioaddr = dev->base_addr;
-	u32 csr12 = tio_read(CSR12);
+	/* Stop the chip's Tx and Rx processes. */
+	outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+	/* Clear the missed-packet counter. */
+	(volatile)inl(ioaddr + CSR8);
 
-	return((!(csr12 & TSIAS_CONERROR)
-			|| !(csr12 & TSIAS_LNKERROR)) ? 0: 1);
-}
+	if (chip_id == DC21041) {
+		if (inl(ioaddr + CSR9) & 0x8000) {
+			printk(" 21040 compatible mode,");
+			chip_id = DC21040;
+		} else {
+			printk(" 21041 mode,");
+		}
+	}
 
-static void
-generic21040_select(struct device *dev)
-{
-	unsigned long ioaddr = dev->base_addr;
-	const char *media;
+	/* The station address ROM is read byte serially.  The register must
+	   be polled, waiting for the value to be read bit serially from the
+	   EEPROM.
+	   */
+	sum = 0;
+	if (chip_id == DC21040) {
+		outl(0, ioaddr + CSR9);		/* Reset the pointer with a dummy write. */
+		for (i = 0; i < 6; i++) {
+			int value, boguscnt = 100000;
+			do
+				value = inl(ioaddr + CSR9);
+			while (value < 0  && --boguscnt > 0);
+			dev->dev_addr[i] = value;
+			sum += value & 0xff;
+		}
+	} else if (chip_id == LC82C168) {
+		for (i = 0; i < 3; i++) {
+			int value, boguscnt = 100000;
+			outl(0x600 | i, ioaddr + 0x98);
+			do
+				value = inl(ioaddr + CSR9);
+			while (value < 0  && --boguscnt > 0);
+			((u16*)dev->dev_addr)[i] = value;
+			sum += value & 0xffff;
+		}
+	} else {	/* Must be a new chip, with a serial EEPROM interface. */
+		/* We read the whole EEPROM, and sort it out later.  DEC has a
+		   specification _Digital Semiconductor 21X4 Serial ROM Format_
+		   but early vendor boards just put the address in the first six
+		   EEPROM locations. */
+		unsigned char ee_data[128];
+		int sa_offset = 0;
+
+		for (i = 0; i < sizeof(ee_data)/2; i++)
+			((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
+
+		/* Detect the simple EEPROM format by the duplicated station addr. */
+		for (i = 0; i < 8; i ++)
+			if (ee_data[i] != ee_data[16+i])
+				sa_offset = 20;
+		for (i = 0; i < 6; i ++) {
+			dev->dev_addr[i] = ee_data[i + sa_offset];
+			sum += ee_data[i + sa_offset];
+		}
+	}
+	/* Lite-On boards have the address byte-swapped. */
+	if (dev->dev_addr[0] == 0xA0  &&  dev->dev_addr[1] == 0x00)
+		for (i = 0; i < 6; i+=2) {
+			char tmp = dev->dev_addr[i];
+			dev->dev_addr[i] = dev->dev_addr[i+1];
+			dev->dev_addr[i+1] = tmp;
+		}
+	/* On the Zynx 315 Etherarray and other multiport boards only the
+	   first Tulip has an EEPROM.
+	   The addresses of the subsequent ports are derived from the first.
+	   Many PCI BIOSes also incorrectly report the IRQ line, so we correct
+	   that here as well. */
+	if (sum == 0  || sum == 6*0xff) {
+		printk(" EEPROM not present,");
+		for (i = 0; i < 5; i++)
+			dev->dev_addr[i] = last_phys_addr[i];
+		dev->dev_addr[i] = last_phys_addr[i] + 1;
+#if defined(__i386)		/* This BIOS bug doesn't exist on Alphas. */
+		irq = last_irq;
+#endif
+	}
+	for (i = 0; i < 6; i++)
+		printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]);
+	printk(", IRQ %d.\n", irq);
+	last_irq = irq;
 
-	dev->if_port &= 3;
-	switch (dev->if_port)
-	{
-	case TULIP_10TP_PORT:
-		media = "10baseT";
+	/* We do a request_region() only to register /proc/ioports info. */
+	request_region(ioaddr, TULIP_TOTAL_SIZE, tulip_tbl[chip_id].chip_name);
+
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+
+	/* Make certain the data structures are quadword aligned. */
+	tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
+	memset(tp, 0, sizeof(*tp));
+	dev->priv = tp;
+
+#ifdef MODULE
+	tp->next_module = root_tulip_dev;
+	root_tulip_dev = dev;
+#endif
+
+	tp->chip_id = chip_id;
+
+#ifdef TULIP_FULL_DUPLEX
+	tp->full_duplex = 1;
+	tp->full_duplex_lock = 1;
+#endif
+#ifdef TULIP_DEFAULT_MEDIA
+	tp->default_port = TULIP_DEFAULT_MEDIA;
+#endif
+#ifdef TULIP_NO_MEDIA_SWITCH
+	tp->medialock = 1;
+#endif
+
+	/* The lower four bits are the media type. */
+	if (board_idx >= 0) {
+		tp->full_duplex = (options[board_idx]&16) || full_duplex[board_idx]>0;
+		if (tp->full_duplex)
+			tp->full_duplex_lock = 1;
+		tp->default_port = options[board_idx] & 15;
+		if (tp->default_port)
+			tp->medialock = 1;
+		if (mtu[board_idx] > 0)
+			dev->mtu = mtu[board_idx];
+	}
+
+	/* This is logically part of probe1(), but too complex to write inline. */
+	if (chip_id != DC21040  &&  chip_id != LC82C168)
+		parse_eeprom(dev);
+
+	if (tp->mtable  &&  tp->mtable->has_mii) {
+		int phy, phy_idx;
+		/* Find the connected MII xcvrs.
+		   Doing this in open() would allow detecting external xcvrs later,
+		   but takes much time. */
+		for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
+			 phy++) {
+			int mii_status = mdio_read(ioaddr, phy, 0);
+			if (mii_status != 0xffff  && mii_status != 0x0000) {
+				tp->phys[phy_idx++] = phy;
+				printk(KERN_INFO "%s:  MII transceiver found at MDIO address %d.\n",
+					   dev->name, phy);
+			}
+		}
+		if (phy_idx == 0) {
+			printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n",
+				   dev->name);
+			tp->phys[0] = 1;
+		}
+	}
+
+	/* The Tulip-specific entries in the device structure. */
+	dev->open = &tulip_open;
+	dev->hard_start_xmit = &tulip_start_xmit;
+	dev->stop = &tulip_close;
+	dev->get_stats = &tulip_get_stats;
+#ifdef HAVE_MULTICAST
+	dev->set_multicast_list = &set_multicast_list;
+#endif
+#ifdef CONFIG_NET_FASTROUTE
+	dev->accept_fastpath = tulip_accept_fastpath;
+#endif
+
+	/* Reset the xcvr interface and turn on heartbeat. */
+	switch (chip_id) {
+	case DC21041:
+		outl(0x00000000, ioaddr + CSR13);
+		outl(0xFFFFFFFF, ioaddr + CSR14);
+		outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
+		outl(inl(ioaddr + CSR6) | 0x200, ioaddr + CSR6);
+		outl(0x0000EF05, ioaddr + CSR13);
 		break;
-	case TULIP_AUI_PORT:
-		media = "AUI";
+	case DC21140:  case DC21142:
+		if (tp->mtable)
+			outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
 		break;
-	case TULIP_BNC_PORT:
-		media = "BNC";
+	case DC21040:
+		outl(0x00000000, ioaddr + CSR13);
+		outl(0x00000004, ioaddr + CSR13);
 		break;
-	default:
-		media = "unknown type";
+	case LC82C168:
+		outl(0x33, ioaddr + CSR12);
+		outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
 		break;
 	}
-	printk("%s: enabling %s port.\n", dev->name, media);
-	/* Set the full duplex match frame. */
-	tio_write(FULL_DUPLEX_MAGIC, CSR11);
-	tio_write(TSIAC_RESET, CSR13);
-	/* Reset the serial interface */
-	tio_write((dev->if_port ? TSIAC_NO10TP: 0) | TSIAC_C21040, CSR13);
+
+	return dev;
 }
+
+/* Serial EEPROM section. */
+/* The main routine to parse the very complicated SROM structure.
+   Search www.digital.com for "21X4 SROM" to get details.
+   This code is very complex, and will require changes to support
+   additional cards, so I'll be verbose about what is going on.
+   */
+
+/* Known cards that have old-style EEPROMs. */
+static struct fixups {
+  char *name;
+  unsigned char addr0, addr1, addr2;
+  u16 newtable[32];				/* Max length below. */
+} eeprom_fixups[] = {
+  {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
+						  0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
+  {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f,
+							   0x0000, 0x009E, /* 10baseT */
+							   0x0903, 0x006D, /* 100baseTx */ }},
+  {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x013f,
+								 0x0103, 0x006D, /* 100baseTx */ }},
+  {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313,
+							   0x1001, 0x009E, /* 10base2, CSR12 0x10*/
+							   0x0000, 0x009E, /* 10baseT */
+							   0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }},
+  {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F,
+							0x1B01, 0x0000, /* 10base2,   CSR12 0x1B */
+							0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ 
+							0x0B00, 0x009E, /* 10baseT,   CSR12 0x0B */
+   }},
+  {0, 0, 0, 0, {}}};
+
+static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
+ "21142 non-MII PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
+
+#define EEPROM_SIZE 128
+static void parse_eeprom(struct device *dev)
+{
+	/* The last media info list parsed, for multiport boards.  */
+	static struct mediatable *last_mediatable = NULL;
+	static unsigned char *last_ee_data = NULL;
+	static controller_index = 0;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	unsigned char *ee_data = tp->eeprom;
+	int i;
 
-#if 0
-static void
-generic_timer(struct device *dev, u32 count)
-{
-	unsigned long ioaddr = dev->base_addr;
+	tp->mtable = 0;
+	for (i = 0; i < EEPROM_SIZE/2; i++)
+		((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
 
-	tio_write(count, CSR11);
-	while (tio_read(CSR11) & TGEPT_COUNT);
+	/* Detect an old-style (SA only) EEPROM layout:
+	   memcmp(eedata, eedata+16, 8). */
+	for (i = 0; i < 8; i ++)
+		if (ee_data[i] != ee_data[16+i])
+			break;
+	if (i >= 8) {
+		if (ee_data[0] == 0xff) {
+			if (last_mediatable) {
+				controller_index++;
+				printk(KERN_INFO "%s:  Controller %d of multiport board.\n",
+					   dev->name, controller_index);
+				tp->mtable = last_mediatable;
+				ee_data = last_ee_data;
+				goto subsequent_board;
+			} else
+				printk(KERN_INFO "%s:  Missing EEPROM, this interface may "
+					   "not work correctly!\n",
+			   dev->name);
+			return;
+		}
+	  /* Do a fix-up based on the vendor half of the station address prefix. */
+	  for (i = 0; eeprom_fixups[i].name; i++) {
+		if (dev->dev_addr[0] == eeprom_fixups[i].addr0
+			&&  dev->dev_addr[1] == eeprom_fixups[i].addr1
+			&&  dev->dev_addr[2] == eeprom_fixups[i].addr2) {
+		  if (dev->dev_addr[2] == 0xE8  &&  ee_data[0x1a] == 0x55)
+			  i++;			/* An Accton EN1207, not an outlaw Maxtech. */
+		  memcpy(ee_data + 26, eeprom_fixups[i].newtable,
+				 sizeof(eeprom_fixups[i].newtable));
+		  printk(KERN_INFO "%s: Old format EEPROM on '%s' board.  Using"
+				 " substitute media control info.\n",
+				 dev->name, eeprom_fixups[i].name);
+		  break;
+		}
+	  }
+	  if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
+		printk(KERN_INFO "%s: Old style EEPROM -- no media selection information.\n",
+			   dev->name);
+		return;
+	  }
+	}
+	if (tulip_debug > 1) {
+	  printk(KERN_DEBUG "read_eeprom:");
+	  for (i = 0; i < 64; i++) {
+		printk("%s%4.4x", (i & 7) == 0 ? "\n" KERN_DEBUG : " ",
+			   read_eeprom(ioaddr, i));
+	  }
+	  printk("\n");
+	}
+	
+	controller_index = 0;
+	if (ee_data[19] > 1) {		/* Multiport board. */
+		last_ee_data = ee_data;
+	}
+subsequent_board:
+
+	if (tp->chip_id == DC21041) {
+		unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3];
+		short media = *(u16 *)p;
+		int count = p[2];
+
+		printk(KERN_INFO "%s:21041 Media information at %d, default media "
+			   "%4.4x (%s).\n", dev->name, ee_data[27], media,
+			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
+		for (i = 0; i < count; i++) {
+			unsigned char media_code = p[3 + i*7];
+			u16 *csrvals = (u16 *)&p[3 + i*7 + 1];
+			printk(KERN_INFO "%s:  21041 media %2.2x (%s),"
+				   " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n",
+				   dev->name, media_code & 15, medianame[media_code & 15],
+				   csrvals[0], csrvals[1], csrvals[2]);
+		}
+	} else {
+		unsigned char *p = (void *)ee_data + ee_data[27];
+		unsigned char csr12dir = 0;
+		int count;
+		struct mediatable *mtable;
+		u16 media = *((u16 *)p)++;
+
+		if (tp->chip_id == DC21140)
+			csr12dir = *p++;
+		count = *p++;
+		mtable = (struct mediatable *)
+			kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf),
+					GFP_KERNEL);
+		if (mtable == NULL)
+			return;				/* Horrible, impossible failure. */
+		last_mediatable = tp->mtable = mtable;
+		mtable->defaultmedia = media;
+		mtable->leafcount = count;
+		mtable->csr12dir = csr12dir;
+		mtable->has_mii = 0;
+
+		printk(KERN_INFO "%s:  EEPROM default media type %s.\n", dev->name,
+			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
+		for (i = 0; i < count; i++) {
+			struct medialeaf *leaf = &mtable->mleaf[i];
+			
+			if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
+				leaf->type = 0;
+				leaf->media = p[0] & 0x3f;
+				leaf->leafdata = p;
+				p += 4;
+			} else {
+				leaf->type = p[1];
+				if (p[1] & 1) {
+					mtable->has_mii = 1;
+					leaf->media = 11;
+				} else
+					leaf->media = p[2] & 0x0f;
+				leaf->leafdata = p + 2;
+				p += (p[0] & 0x3f) + 1;
+			}
+			if (tulip_debug > 1  &&  leaf->media == 11) {
+				unsigned char *bp = leaf->leafdata;
+				printk(KERN_INFO "%s:  MII interface PHY %d, setup/reset "
+					   "sequences %d/%d long, capabilities %2.2x %2.2x.\n",
+					   dev->name, bp[0], bp[1], bp[1 + bp[1]*2],
+					   bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
+			}
+			printk(KERN_INFO "%s:  Index #%d - Media %s (#%d) described "
+				   "by a %s (%d) block.\n",
+				   dev->name, i, medianame[leaf->media], leaf->media,
+				   block_name[leaf->type], leaf->type);
+		}
+	}
 }
+/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/
+
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK	0x02	/* EEPROM shift clock. */
+#define EE_CS			0x01	/* EEPROM chip select. */
+#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
+#define EE_WRITE_0		0x01
+#define EE_WRITE_1		0x05
+#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
+#define EE_ENB			(0x4800 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+   The 1.2 code is a "nasty" timing loop, but PC compatible machines are
+   *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro.  */
+#ifdef _LINUX_DELAY_H
+#define eeprom_delay(nanosec)	udelay((nanosec + 999)/1000)
+#else
+#define eeprom_delay(nanosec)	do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
 #endif
 
-static void
-generic21041_select(struct device *dev)
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD	(5 << 6)
+#define EE_READ_CMD		(6 << 6)
+#define EE_ERASE_CMD	(7 << 6)
+
+static int read_eeprom(int ioaddr, int location)
 {
-	unsigned long ioaddr = dev->base_addr;
-	u32 tsiac = TSIAC_C21041;
-	u32 tsiax = TSIAX_10TP;
-	u32 tsiag = TSIAG_10TP;
-
-	switch(dev->if_port) {
-	case TULIP_AUI_PORT:
-		tsiac |= TSIAC_NO10TP;
-		tsiax = TSIAX_NO10TP;
-		tsiag = TSIAG_AUI;
-		break;
-	case TULIP_BNC_PORT:
-		tsiac |= TSIAC_NO10TP;
-		tsiax = TSIAX_NO10TP;
-		tsiag = TSIAG_BNC;
-		break;
-	default:
-		dev->if_port = TULIP_10TP_PORT;
-		break;
+	int i;
+	unsigned short retval = 0;
+	int ee_addr = ioaddr + CSR9;
+	int read_cmd = location | EE_READ_CMD;
+	
+	outl(EE_ENB & ~EE_CS, ee_addr);
+	outl(EE_ENB, ee_addr);
+	
+	/* Shift the read command bits out. */
+	for (i = 10; i >= 0; i--) {
+		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+		outl(EE_ENB | dataval, ee_addr);
+		eeprom_delay(100);
+		outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay(150);
+		outl(EE_ENB | dataval, ee_addr);	/* Finish EEPROM a clock tick. */
+		eeprom_delay(250);
 	}
-	tio_sia_write(ioaddr, tsiac, tsiax, tsiag);
-	if (dev->start)
-		printk("%s: enabling %s port.\n", dev->name,
-			   (dev->if_port == TULIP_AUI_PORT) ? "AUI":
-			   (dev->if_port == TULIP_BNC_PORT) ? "BNC": "10TP");
-}
+	outl(EE_ENB, ee_addr);
+	
+	for (i = 16; i > 0; i--) {
+		outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay(100);
+		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
+		outl(EE_ENB, ee_addr);
+		eeprom_delay(100);
+	}
+
+	/* Terminate the EEPROM access. */
+	outl(EE_ENB & ~EE_CS, ee_addr);
+	return retval;
+}
+
+/* Read and write the MII registers using software-generated serial
+   MDIO protocol.  It is just different enough from the EEPROM protocol
+   to not share code.  The maxium data clock rate is 2.5 Mhz. */
+#define MDIO_SHIFT_CLK	0x10000
+#define MDIO_DATA_WRITE0 0x00000
+#define MDIO_DATA_WRITE1 0x20000
+#define MDIO_ENB		0x00000		/* Ignore the 0x02000 databook setting. */
+#define MDIO_ENB_IN		0x40000
+#define MDIO_DATA_READ	0x80000
+#ifdef _LINUX_DELAY_H
+#define mdio_delay()	udelay(1)
+#else
+#define mdio_delay()	__SLOW_DOWN_IO
+#endif
 
-static void
-auto21140_select(struct device *dev)
+static int mdio_read(int ioaddr, int phy_id, int location)
 {
 	int i;
-	unsigned long ioaddr = dev->base_addr;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
-	/* kick port */
-	tio_write(TPOLL_TRIGGER, CSR1);
-	tio_write(TINTR_ENABLE, CSR7);
-	tio_write(TCMOD_AUTO|TCMOD_TRxSTART, CSR6);
-	dev->if_port = !(tio_read(CSR12) & TGEPR_FORCEALED);
-	printk("%s: probed %s port.\n",
-		   dev->name, dev->if_port ? "100TX" : "10TP");
-	tio_write((dev->if_port ? TGEPR_FORCE100: 0)
-			  | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);	
-	tio_write(TINTR_DISABLE, CSR7);
-	i = tio_read(CSR8) & 0xffff;
-	tio_write(TCMOD_AUTO, CSR6);
+	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+	unsigned short retval = 0;
+	int mdio_addr = ioaddr + CSR9;
+
+	/* Establish sync by sending at least 32 logic ones. */ 
+	for (i = 32; i >= 0; i--) {
+		outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
+		mdio_delay();
+		outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+	}
+	/* Shift the read command bits out. */
+	for (i = 17; i >= 0; i--) {
+		int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
+
+		outl(dataval, mdio_addr);
+		mdio_delay();
+		outl(dataval | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+		outl(dataval, mdio_addr);
+		mdio_delay();
+	}
+	outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+	mdio_delay();
+	outl(MDIO_ENB_IN, mdio_addr);
+
+	for (i = 16; i > 0; i--) {
+		outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+		retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+		outl(MDIO_ENB_IN, mdio_addr);
+		mdio_delay();
+	}
+	/* Clear out extra bits. */
+	for (i = 16; i > 0; i--) {
+		outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+		outl(MDIO_ENB_IN, mdio_addr);
+		mdio_delay();
+	}
+	return retval;
 }
 
-static void
-cogent21140_select(struct device *dev)
-{
-	unsigned long ioaddr = dev->base_addr, csr6;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	dev->if_port &= 1;
-	csr6 = tio_read(CSR6) &
-		~(TCMOD_10TP|TCMOD_100TP|TCMOD_TRxSTART|TCMOD_SCRM);
-	/* Stop the transmit process. */
-	tio_write(csr6 | TCMOD_RxSTART, CSR6);
-	printk("%s: enabling %s port.\n",
-		   dev->name, dev->if_port ? "100baseTx" : "10baseT");
-	/* Turn on the output drivers */
-	tio_write(0x0000013F, CSR12);
-	tio_write((dev->if_port ? TGEPR_FORCE100: 0)
-			  | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);
-	tio_write((dev->if_port ? TCMOD_100TP: TCMOD_10TP)
-			  | TCMOD_TRxSTART | TCMOD_TH128 | csr6, CSR6);
-}
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+/* Enable receiver */
 
-static void
-generic21140_select(struct device *dev)
+void tulip_xon(struct device *dev)
 {
-	unsigned long ioaddr = dev->base_addr, csr6;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
-	dev->if_port &= 1;
-	csr6 = tio_read(CSR6) &
-		~(TCMOD_10TP|TCMOD_100TP|TCMOD_TRxSTART|TCMOD_SCRM);
+	struct tulip_private *lp = (struct tulip_private *)dev->priv;
 
-	/* Stop the transmit process. */
-	tio_write(csr6 | TCMOD_RxSTART, CSR6);
+	clear_bit(lp->fc_bit, &netdev_fc_xoff);
 	if (dev->start)
-		printk("%s: enabling %s port.\n",
-			   dev->name, dev->if_port ? "100TX" : "10TP");
-	tio_write((dev->if_port ? TCMOD_100TP: TCMOD_10TP)
-			  | TCMOD_TRxSTART | TCMOD_TH128 | csr6, CSR6);
-	tio_write((dev->if_port ? TGEPR_FORCE100: 0)
-			  | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);
+				outl(lp->csr6 | 0x2002, dev->base_addr + CSR6);
 }
+#endif
 
+
 static int
 tulip_open(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-	int i;
+	int ioaddr = dev->base_addr;
+	int i = 0;
 
-	/* Reset the chip, holding bit 0 set at least 10 PCI cycles. */
-	tio_write(tio_read(CSR0)|TBMOD_RESET, CSR0);
-	udelay(1000);
-	/* Deassert reset.  Set 8 longword cache alignment, 8 longword burst.
-	   -> Set 32 longword cache alignment, unlimited longword burst ?
+	/* On some chip revs we must set the MII/SYM port before the reset!? */
+	if (tp->mtable  &&  tp->mtable->has_mii)
+		outl(0x00040000, ioaddr + CSR6);
+
+	/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
+	outl(0x00000001, ioaddr + CSR0);
+#ifdef _LINUX_DELAY_H
+	udelay(2);
+#else
+	SLOW_DOWN_IO;
+#endif
+	/* Deassert reset.
+	   486: Set 8 longword cache alignment, 8 longword burst.
+	   586: Set 16 longword cache alignment, no burst limit.
+	   Cache alignment bits 15:14	     Burst length 13:8
+		0000	No alignment  0x00000000 unlimited		0800 8 longwords
+		4000	8  longwords		0100 1 longword		1000 16 longwords
+		8000	16 longwords		0200 2 longwords	2000 32 longwords
+		C000	32  longwords		0400 4 longwords
 	   Wait the specified 50 PCI cycles after a reset by initializing
 	   Tx and Rx queues and the address filter list. */
-	tio_write(tio_read(CSR0)|TBMOD_ALIGN32|TBMOD_BURST0, CSR0);
+#if defined(__alpha__)
+	/* ToDo: Alpha setting could be better. */
+	outl(0x00200000 | 0xE000, ioaddr + CSR0);
+#elif defined(__powerpc__)
+	outl(0x00200080 | 0x8000, ioaddr + CSR0);
+#elif defined(__i386)
+#if defined(MODULE)
+	/* When a module we don't have 'x86' to check. */
+	outl(0x00200000 | 0x4800, ioaddr + CSR0);
+#else
+#ifndef ORIGINAL_TEXT
+#ifndef __SMP__
+#define x86 ((struct cpuinfo_x86*)cpu_data)->x86
+#else
+#error What should we make here?
+#endif
+#endif
+	outl(0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0);
+	if (x86 <= 4)
+	  printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache "
+			 "alignment to %x.\n", dev->name,
+			 0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000));
+#endif
+#else
+	outl(0x00200000 | 0x4800, ioaddr + CSR0);
+#warning Processor architecture undefined!
+#endif
 
-	if (request_irq(dev->irq, (void *)&tulip_interrupt, SA_SHIRQ,
-					tp->signature, dev))
+#ifdef SA_SHIRQ
+	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ,
+					tulip_tbl[tp->chip_id].chip_name, dev)) {
 		return -EAGAIN;
+	}
+#else
+	if (irq2dev_map[dev->irq] != NULL
+		|| (irq2dev_map[dev->irq] = dev) == NULL
+		|| dev->irq == 0
+		|| request_irq(dev->irq, &tulip_interrupt, 0,
+					   tulip_tbl[tp->chip_id].chip_name)) {
+		return -EAGAIN;
+	}
+#endif
+
+	if (tulip_debug > 1)
+		printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq);
+
+	MOD_INC_USE_COUNT;
 
 	tulip_init_ring(dev);
 
+	/* This is set_rx_mode(), but without starting the transmitter. */
 	/* Fill the whole address filter table with our physical address. */
-	{ 
-		unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
-		int *setup_frm = tp->setup_frame, i;
+	{
+		u16 *eaddrs = (u16 *)dev->dev_addr;
+		u32 *setup_frm = tp->setup_frame, i;
 
 		/* You must add the broadcast address when doing perfect filtering! */
-		*setup_frm++ = cpu_to_le32(0xffff);
-		*setup_frm++ = cpu_to_le32(0xffff);
-		*setup_frm++ = cpu_to_le32(0xffff);
+		*setup_frm++ = 0xffff;
+		*setup_frm++ = 0xffff;
+		*setup_frm++ = 0xffff;
 		/* Fill the rest of the accept table with our physical address. */
 		for (i = 1; i < 16; i++) {
-			*setup_frm++ = cpu_to_le32(eaddrs[0]);
-			*setup_frm++ = cpu_to_le32(eaddrs[1]);
-			*setup_frm++ = cpu_to_le32(eaddrs[2]);
+			*setup_frm++ = eaddrs[0];
+			*setup_frm++ = eaddrs[1];
+			*setup_frm++ = eaddrs[2];
+		}
+		/* Put the setup frame on the Tx list. */
+		tp->tx_ring[0].length = 0x08000000 | 192;
+		tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame);
+		tp->tx_ring[0].status = 0x80000000;
+
+		tp->cur_tx++;
+	}
+
+	outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3);
+	outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
+
+	if (dev->if_port == 0)
+	  dev->if_port = tp->default_port;
+	if (tp->chip_id == DC21041  &&  dev->if_port > 4)
+		/* Invalid: Select initial TP, autosense, autonegotiate.  */
+		dev->if_port = 4;
+
+	/* Allow selecting a default media. */
+	if (tp->mtable == NULL)
+		goto media_picked;
+	if (dev->if_port)
+		for (i = 0; i < tp->mtable->leafcount; i++)
+		  if (tp->mtable->mleaf[i].media ==
+			  (dev->if_port == 12 ? 0 : dev->if_port)) {
+			printk(KERN_INFO "%s: Using user-specified media %s.\n",
+				   dev->name, medianame[dev->if_port]);
+			goto media_picked;
+		  }
+	if ((tp->mtable->defaultmedia & 0x0800) == 0)
+		for (i = 0; i < tp->mtable->leafcount; i++)
+		  if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) {
+			printk(KERN_INFO "%s: Using EEPROM-set media %s.\n",
+				   dev->name, medianame[tp->mtable->mleaf[i].media]);
+			goto media_picked;
+		  }
+	for (i = tp->mtable->leafcount - 1;
+		 (media_fd[tp->mtable->mleaf[i].media] & 2) && i > 0; i--)
+	  ;
+media_picked:
+
+	tp->cur_index = i;
+	tp->csr6 = 0;
+	select_media(dev, 1);
+
+	/* Start the chip's Tx to process setup frame. */
+	outl(tp->csr6, ioaddr + CSR6);
+	outl(tp->csr6 | 0x2000, ioaddr + CSR6);
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+
+	/* Enable interrupts by setting the interrupt mask. */
+	outl(0x0001ebef, ioaddr + CSR7);
+	outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+	outl(0, ioaddr + CSR2);		/* Rx poll demand */
+
+	if (tulip_debug > 2) {
+		printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR13 %8.8x.\n",
+			   dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5),
+			   inl(ioaddr + CSR13));
+	}
+	/* Set the timer to switch to check for link beat and perhaps switch
+	   to an alternate media type. */
+	init_timer(&tp->timer);
+	tp->timer.expires = RUN_AT((24*HZ)/10);			/* 2.4 sec. */
+	tp->timer.data = (unsigned long)dev;
+	tp->timer.function = &tulip_timer;				/* timer handler */
+	add_timer(&tp->timer);
+
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+	tp->fc_bit = netdev_register_fc(dev, tulip_xon);
+#endif
+#ifdef CONFIG_NET_FASTROUTE
+	dev->tx_semaphore = 1;
+#endif
+	return 0;
+}
+
+/* Set up the transceiver control registers for the selected media type. */
+static void select_media(struct device *dev, int startup)
+{
+	int ioaddr = dev->base_addr;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct mediatable *mtable = tp->mtable;
+	u32 new_csr6;
+	int check_mii =0, i;
+
+	if (mtable) {
+		struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
+		unsigned char *p = mleaf->leafdata;
+		switch (mleaf->type) {
+		case 0:					/* 21140 non-MII xcvr. */
+			if (tulip_debug > 1)
+				printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver with control"
+					   " setting %2.2x.\n",
+					   dev->name, p[1]);
+			dev->if_port = p[0];
+			if (startup)
+				outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+			outl(p[1], ioaddr + CSR12);
+			new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
+			break;
+		case 1:
+			if (startup) {
+				outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+				dev->if_port = 11;
+				if (tulip_debug > 2)
+					printk(KERN_DEBUG "%s:  Doing a reset sequence of length %d.\n",
+						   dev->name, p[2 + p[1]]);
+				for (i = 0; i < p[2 + p[1]]; i++)
+					outl(p[3 + p[1] + i], ioaddr + CSR12);
+				if (tulip_debug > 2)
+					printk(KERN_DEBUG "%s  Doing a transceiver setup sequence of length %d.\n",
+						   dev->name, p[1]);
+				for (i = 0; i < p[1]; i++)
+					outl(p[2 + i], ioaddr + CSR12);
+			}
+			check_mii = 1;
+			new_csr6 = 0x020C0000;
+			break;
+		case 2: case 4: {
+			u16 *setup = (u16*)&p[1];
+			dev->if_port = p[0] & 15;
+			if (tulip_debug > 1)
+				printk(KERN_DEBUG "%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n",
+					   dev->name, medianame[dev->if_port], setup[0], setup[1]);
+			if (p[0] & 0x40) {	/* SIA (CSR13-15) setup values are provided. */
+				outl(0, ioaddr + CSR13);
+				outl(setup[1], ioaddr + CSR14);
+				outl(setup[2], ioaddr + CSR15);
+				outl(setup[0], ioaddr + CSR13);
+				setup += 3;
+			} else {
+				outl(0, ioaddr + CSR13);
+				outl(t21142_csr14[dev->if_port], ioaddr + CSR14);
+				outl(t21142_csr15[dev->if_port], ioaddr + CSR15);
+				outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
+			}
+			outl(setup[0]<<16, ioaddr + CSR15);	/* Direction */
+			outl(setup[1]<<16, ioaddr + CSR15);	/* Data */
+			if (mleaf->type == 4)
+				new_csr6 = 0x02000000 | ((setup[2] & 0x71) << 18);
+			else
+				new_csr6 = 0x82420000;
+			break;
+		}
+		case 3: {
+			int init_length = p[1];
+			u16 * init_sequence = (u16*)(p + 2);
+			int reset_length = p[2 + init_length*2];
+			u16 * reset_sequence = (u16*)&p[3 + init_length*2];
+
+			dev->if_port = 11;
+			if (startup) {
+				if (tulip_debug > 2)
+					printk(KERN_DEBUG "%s:  Doing a 21142 reset sequence of length %d.\n",
+						   dev->name, reset_length);
+				for (i = 0; i < reset_length; i++)
+					outl(reset_sequence[i] << 16, ioaddr + CSR15);
+			}
+			if (tulip_debug > 2)
+				printk(KERN_DEBUG "%s: Doing a 21142 xcvr setup sequence of length %d.\n",
+					   dev->name, init_length);
+			for (i = 0; i < init_length; i++)
+				outl(init_sequence[i] << 16, ioaddr + CSR15);
+			check_mii = 1;
+			new_csr6 = 0x020C0000;
+			break;
+		}
+		default:
+		  new_csr6 = 0x020C0000;
+		}
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n",
+				   dev->name, medianame[dev->if_port],
+				   inl(ioaddr + CSR12) & 0xff);
+	} else if (tp->chip_id == DC21140) {
+		/* Set media type to MII @ 100mbps: 0x020C0000 */
+		new_csr6 = 0x020C0000;
+		dev->if_port = 11;
+		if (tulip_debug > 1) {
+			printk(KERN_DEBUG "%s: Unknown media control, assuming MII, CSR12 %2.2x.\n",
+				   dev->name, inl(ioaddr + CSR12) & 0xff);
 		}
-		/* Put the setup frame on the Tx list. */
-		tp->tx_ring[0].length = cpu_to_le32(0x08000000 | 192);
-		tp->tx_ring[0].buffer1 = cpu_to_le32(virt_to_bus(tp->setup_frame));
-		tp->tx_ring[0].buffer2 = cpu_to_le32(0);
-		tp->tx_ring[0].status = cpu_to_le32(TRING_OWN);
-		barrier();
-		tp->cur_tx++, tp->dirty_tx++;
+	} else if (tp->chip_id == DC21041) {
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n",
+				   dev->name, medianame[dev->if_port & 15],
+				   inl(ioaddr + CSR12) & 0xffff);
+		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+		outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
+		outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
+		outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
+		new_csr6 = 0x80020000;
+	} else if (tp->chip_id == LC82C168) {
+		if (startup)
+			dev->if_port = 3;
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: LiteOn PHY status is %3.3x, CSR12 %4.4x,"
+				   " media %s.\n",
+				   dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12),
+				   medianame[dev->if_port]);
+		if (dev->if_port == 3  ||  dev->if_port == 5) {
+			outl(0x33, ioaddr + CSR12);
+			new_csr6 = 0x01860000;
+			if (startup)
+				outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */
+			else
+				outl(0x1F868, ioaddr + 0xB8);
+		} else {
+			outl(0x32, ioaddr + CSR12);
+			new_csr6 = 0x00420000;
+			outl(0x1F078, ioaddr + 0xB8);
+		}
+	} else {					/* 21040 */
+		/* Turn on the xcvr interface. */
+		int csr12 = inl(ioaddr + CSR12);
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n",
+				   dev->name, dev->if_port ? "AUI" : "10baseT", csr12);
+		new_csr6 = (dev->if_port ? 0x01860000 : 0x00420000);
+		/* Set the full duplux match frame. */
+		outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
+		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+		outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
 	}
 
-	tio_write(virt_to_bus(tp->rx_ring), CSR3);
-	tio_write(virt_to_bus(tp->tx_ring), CSR4);
+	tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
+	return;
+}
 
-	dev->tbusy = 0;
-	dev->interrupt = 0;
-	dev->start = 1;
-	/*
-	 * process setup frame completely prior to fiddling with media.
-	 */
-	tio_write((tio_read(CSR6) & ~TCMOD_PROMISC) | TCMOD_TxSTART, CSR6);
-	tio_write(TPOLL_TRIGGER, CSR1);
-	sti();
-	for (i = 0; i < 1000; i++) {
-		if (((s32)le32_to_cpu(tp->tx_ring[0].status)) >= 0) {
+static void tulip_timer(unsigned long data)
+{
+	struct device *dev = (struct device *)data;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	u32 csr12 = inl(ioaddr + CSR12);
+	int next_tick = 0;
+
+	if (tulip_debug > 3) {
+		printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x "
+			   "SIA %8.8x %8.8x %8.8x %8.8x.\n",
+			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6),
+			   csr12, inl(ioaddr + CSR13),
+			   inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+	}
+	switch (tp->chip_id) {
+	case DC21040:
+		if (csr12 & 0x0002) { /* Network error */
+			printk(KERN_INFO "%s: No 10baseT link beat found, switching to %s media.\n",
+				   dev->name, dev->if_port ? "10baseT" : "AUI");
+			dev->if_port ^= 1;
+			outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
+			dev->trans_start = jiffies;
+		}
+		break;
+	case DC21041:
+		if (tulip_debug > 2)
+			printk(KERN_DEBUG "%s: 21041 media tick  CSR12 %8.8x.\n",
+				   dev->name, csr12);
+		switch (dev->if_port) {
+		case 0: case 3: case 4:
+		  if (csr12 & 0x0004) { /*LnkFail */
+			/* 10baseT is dead.  Check for activity on alternate port. */
+			tp->mediasense = 1;
+			if (csr12 & 0x0200)
+				dev->if_port = 2;
+			else
+				dev->if_port = 1;
+			printk(KERN_INFO "%s: No 21041 10baseT link beat, Media switched to %s.\n",
+				   dev->name, medianame[dev->if_port]);
+			outl(0, ioaddr + CSR13); /* Reset */
+			outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
+			outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
+			outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
+			next_tick = 10*HZ;			/* 2.4 sec. */
+		  } else
+			next_tick = 30*HZ;
+		  break;
+		case 1:					/* 10base2 */
+		case 2:					/* AUI */
+		  if (csr12 & 0x0100) {
+			next_tick = (30*HZ);			/* 30 sec. */
+			tp->mediasense = 0;
+		  } else if ((csr12 & 0x0004) == 0) {
+			printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n", dev->name);
+			dev->if_port = 0;
+			select_media(dev, 0);
+			next_tick = (24*HZ)/10;				/* 2.4 sec. */
+		  } else if (tp->mediasense || (csr12 & 0x0002)) {
+			dev->if_port = 3 - dev->if_port; /* Swap ports. */
+			select_media(dev, 0);
+			next_tick = 20*HZ;
+		  } else {
+			next_tick = 20*HZ;
+		  }
+		  break;
+		}
+		break;
+	case LC82C168: {
+		int phy_reg = inl(ioaddr + 0xB8);
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n",
+				   dev->name, phy_reg, inl(ioaddr + CSR5));
+		if (phy_reg & 0x04000000) {	/* Remote link fault */
+			outl(0x0201F078, ioaddr + 0xB8);
+			next_tick = (24*HZ)/10;
+		} else
+			next_tick = 10*HZ;
+		if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */
+			if (tulip_debug > 1)
+				printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, "
+					   "CSR5 %8.8x, PHY %3.3x.\n",
+					   dev->name, medianame[dev->if_port], csr12,
+					   inl(ioaddr + CSR5), inl(ioaddr + 0xB8));
+			if (dev->if_port == 0) {
+				dev->if_port = 3;
+			} else
+				dev->if_port = 0;
+			next_tick = (24*HZ)/10;
+			select_media(dev, 0);
+			outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+			outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+			dev->trans_start = jiffies;
+		}
+		break;
+	}
+	case DC21140:  case DC21142: {
+		struct medialeaf *mleaf;
+		unsigned char *p;
+		if (tp->mtable == NULL) {	/* No EEPROM info, use generic code. */
+			/* Assume this is like a SMC card, and check its link beat bit. */
+			if ((dev->if_port == 0 && (csr12 & 0x0080)) ||
+				(dev->if_port == 1 && (csr12 & 0x0040) == 0)) {
+				dev->if_port ^= 1;
+				/* Stop the transmit process. */
+				tp->csr6 = (dev->if_port ? 0x03860000 : 0x02420000);
+				outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+				printk(KERN_INFO "%s: link beat timed out, CSR12 is 0x%2.2x, switching to"
+					   " %s media.\n", dev->name,
+					   csr12 & 0xff,
+					   dev->if_port ? "100baseTx" : "10baseT");
+				outl(tp->csr6 | 0xA002, ioaddr + CSR6);
+				dev->trans_start = jiffies;
+				next_tick = (24*HZ)/10;
+			} else {
+				next_tick = 10*HZ;
+				if (tulip_debug > 2)
+					printk(KERN_DEBUG "%s: network media monitor 0x%2.2x, link"
+						   " beat detected as %s.\n", dev->name,
+						   csr12 & 0xff,
+						   dev->if_port ? "100baseTx" : "10baseT");
+			}
 			break;
 		}
-		udelay(1000);
+	  mleaf = &tp->mtable->mleaf[tp->cur_index];
+	  p = mleaf->leafdata;
+	  switch (mleaf->type) {
+	  case 0: case 4: {
+		/* Type 0 non-MII or #4 SYM transceiver.  Check the link beat bit. */
+		  s8 bitnum = p[mleaf->type == 4 ? 5 : 2];
+		  if (tulip_debug > 2)
+			  printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x bit %d is"
+					 " %d, expecting %d.\n",
+					 dev->name, csr12, (bitnum >> 1) & 7,
+					 (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
+					 (bitnum >= 0));
+		  /* Check that the specified bit has the proper value. */
+		  if ((bitnum < 0) !=
+			  ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
+			  if (tulip_debug > 1)
+				  printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name,
+						 medianame[mleaf->media]);
+			  break;
+		  }
+		  if (tp->medialock)
+			  break;
+	  select_next_media:
+		  if (--tp->cur_index < 0) {
+			/* We start again, but should instead look for default. */
+			tp->cur_index = tp->mtable->leafcount - 1;
+		  }
+		  dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
+		  if (media_fd[dev->if_port])
+			goto select_next_media; /* Skip FD entries. */
+		  if (tulip_debug > 1)
+			  printk(KERN_DEBUG "%s: No link beat on media %s,"
+					 " trying transceiver type %s.\n",
+					 dev->name, medianame[mleaf->media & 15],
+					 medianame[tp->mtable->mleaf[tp->cur_index].media]);
+		  select_media(dev, 0);
+		  /* Restart the transmit process. */
+		  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+		  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+		  next_tick = (24*HZ)/10;
+		  break;
+	  }
+	  case 1:  case 3:		/* 21140, 21142 MII */
+		  {
+			  int mii_reg1 = mdio_read(ioaddr, tp->phys[0], 1);
+			  int mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5);
+			  printk(KERN_INFO "%s: MII monitoring tick: CSR12 %2.2x, "
+					 "MII status %4.4x, Link partner report %4.4x.\n",
+					 dev->name, csr12, mii_reg1, mii_reg5);
+#ifdef notdef
+			  if (mii_reg1 != 0xffff  &&  (mii_reg1 & 0x0004) == 0)
+				  goto select_next_media;
+#else
+			  if (mii_reg1 != 0xffff  &&  (mii_reg1 & 0x0004) == 0)
+				  printk(KERN_INFO "%s: No link beat on the MII interface, "
+						 "status then %4.4x now %4.4x.\n",
+						 dev->name, mii_reg1,
+						 mdio_read(ioaddr, tp->phys[0], 1));
+#endif
+			  if (mii_reg5 == 0xffff  ||  mii_reg5 == 0x0000)
+				  ;				/* No MII device or no link partner report */
+			  else if (tp->full_duplex_lock)
+				  ;
+			  else if ((mii_reg5 & 0x0100) != 0
+					   || (mii_reg5 & 0x00C0) == 0x0040) {
+				  /* 100baseTx-FD  or  10T-FD, but not 100-HD */
+				  if (tp->full_duplex == 0) {
+					  tp->full_duplex = 1;
+					  tp->csr6 |= 0x0200;
+					  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+					  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+				  }
+				  if (tulip_debug > 0) /* Gurppp, should be >1 */
+					  printk(KERN_INFO "%s: Setting %s-duplex based on MII"
+							 " Xcvr #%d parter capability of %4.4x.\n",
+							 dev->name, full_duplex ? "full" : "half",
+							 tp->phys[0], mii_reg5);
+			  }
+		  }
+		  break;
+	  case 2:					/* 21142 serial block has no link beat. */
+	  default:
+		  break;
+	  }
+	}
+	break;
+	default:					/* Invalid chip type. */
+	  break;
+	}
+	if (next_tick) {
+		tp->timer.expires = RUN_AT(next_tick);
+		add_timer(&tp->timer);
 	}
-	if (i == 500) {
-		printk("%s: initial setup frame didn't complete.\n", dev->name);
-		dev->start = 0;
-		dev->tbusy = 1;
-		tio_write(TINTR_DISABLE, CSR7);
-		tio_write(tio_read(CSR6) & ~(TCMOD_TRxSTART), CSR6);
-		tio_write(TSIAC_CONFIG, CSR13);
-		tio_write(0, CSR13);
-		free_irq(dev->irq, dev);
-		return (-EIO);
-	}
-	/*
-	 * Whack the chip to stop it and *then* do initial media setup.
-	 */
-	tio_write((tio_read(CSR6) & ~(TCMOD_PROMISC|TCMOD_TxSTART)), CSR6);
-	if (tp->port_select)
-		tp->port_select(dev);
-	/* Start the chip's Tx and Rx processes. */
-	tio_write(tio_read(CSR6) | TCMOD_TRxSTART
-			  | (tp->full_duplex ? TCMOD_FULLDUPLEX:0), CSR6);
-	/* Enable interrupts by setting the interrupt mask. */
-	tio_write(TINTR_ENABLE, CSR7);
+}
 
-	MOD_INC_USE_COUNT;
-	return 0;
+static void tulip_tx_timeout(struct device *dev)
+{
+  struct tulip_private *tp = (struct tulip_private *)dev->priv;
+  int ioaddr = dev->base_addr;
+
+  if (tp->mtable && tp->mtable->has_mii) {
+	/* Do nothing -- the media monitor should handle this. */
+	if (tulip_debug > 1)
+	  printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
+			 dev->name);
+  } else if (tp->chip_id == DC21040) {
+	  if (inl(ioaddr + CSR12) & 0x0002) {
+		  printk(KERN_INFO "%s: transmit timed out, switching to %s media.\n",
+				 dev->name, dev->if_port ? "10baseT" : "AUI");
+		  dev->if_port ^= 1;
+		  outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
+	  }
+	  dev->trans_start = jiffies;
+	  return;
+  } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142) {
+	/* Stop the transmit process. */
+	outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+	dev->if_port ^= 1;
+	printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, "
+		   "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
+		   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
+		   inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+	printk(KERN_WARNING "%s: transmit timed out, switching to %s media.\n",
+		   dev->name, dev->if_port ? "100baseTx" : "10baseT");
+	outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+	tp->stats.tx_errors++;
+	dev->trans_start = jiffies;
+	return;
+  } else if (tp->chip_id == DC21041) {
+	u32 csr12 = inl(ioaddr + CSR12);
+
+	printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, CSR12 %8.8x,"
+		   " CSR13 %8.8x, CSR14 %8.8x, resetting...\n",
+		   dev->name, inl(ioaddr + CSR5), csr12,
+		   inl(ioaddr + CSR13), inl(ioaddr + CSR14));
+	tp->mediasense = 1;
+	if (dev->if_port == 1 || dev->if_port == 2)
+		if (csr12 & 0x0004) {
+			dev->if_port = 2 - dev->if_port;
+		} else
+			dev->if_port = 0;
+	else
+		dev->if_port = 1;
+	select_media(dev, 0);
+	tp->stats.tx_errors++;
+	dev->trans_start = jiffies;
+	return;
+  } else
+	printk(KERN_WARNING "%s: transmit timed out, status %8.8x, CSR12 %8.8x,"
+		   " resetting...\n",
+		   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
+#ifdef way_too_many_messages
+  printk("  Rx ring %8.8x: ", (int)tp->rx_ring);
+  for (i = 0; i < RX_RING_SIZE; i++)
+	printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
+  printk("\n  Tx ring %8.8x: ", (int)tp->tx_ring);
+  for (i = 0; i < TX_RING_SIZE; i++)
+	printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
+  printk("\n");
+#endif
+
+  /* Perhaps we should reinitialize the hardware here. */
+  dev->if_port = 0;
+  /* Stop and restart the chip's Tx processes . */
+  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+  /* Trigger an immediate transmit demand. */
+  outl(0, ioaddr + CSR1);
+
+  dev->trans_start = jiffies;
+  tp->stats.tx_errors++;
+  return;
 }
 
+
 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
 static void
 tulip_init_ring(struct device *dev)
@@ -789,76 +1699,73 @@
 	tp->dirty_rx = tp->dirty_tx = 0;
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
-		tp->rx_ring[i].status = cpu_to_le32(TRING_OWN);
-		tp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ);
-		tp->rx_ring[i].buffer1 = cpu_to_le32(virt_to_bus(tp->rx_buffs[i]));
-		tp->rx_ring[i].buffer2 = cpu_to_le32(virt_to_bus(&tp->rx_ring[i+1]));
-	}
-	/* Mark the last entry as wrapping the ring. */ 
-	tp->rx_ring[i-1].length = cpu_to_le32(PKT_BUF_SZ | 0x02000000);
-	tp->rx_ring[i-1].buffer2 = cpu_to_le32(virt_to_bus(&tp->rx_ring[0]));
+		tp->rx_ring[i].status = 0x80000000;	/* Owned by Tulip chip */
+		tp->rx_ring[i].length = PKT_BUF_SZ;
+		{
+			/* Note the receive buffer must be longword aligned.
+			   dev_alloc_skb() provides 16 byte alignment.  But do *not*
+			   use skb_reserve() to align the IP header! */
+			struct sk_buff *skb;
+			skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
+			tp->rx_skbuff[i] = skb;
+			if (skb == NULL)
+				break;			/* Bad news!  */
+			skb->dev = dev;			/* Mark as being used by this device. */
+#if LINUX_VERSION_CODE > 0x10300
+			tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail);
+#else
+			tp->rx_ring[i].buffer1 = virt_to_bus(skb->data);
+#endif
+		}
+		tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
+	}
+	/* Mark the last entry as wrapping the ring. */
+	tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000;
+	tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
 
 	/* The Tx buffer descriptor is filled in as needed, but we
 	   do need to clear the ownership bit. */
 	for (i = 0; i < TX_RING_SIZE; i++) {
-		tp->tx_ring[i].status = cpu_to_le32(0x00000000);
+		tp->tx_skbuff[i] = 0;
+		tp->tx_ring[i].status = 0x00000000;
+		tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
 	}
+	tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
 }
 
 static int
 tulip_start_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-	int entry, len;
-	unsigned long daddr;
-
-	/* Transmitter timeout, serious problems. */
-	if (dev->tbusy || (tp->port_fail && tp->port_fail(dev))) {
-		int tickssofar = jiffies - dev->trans_start;
-		int i;
-		if (tickssofar < 40) return(1);
-		if (tp->port_select) {
-			if (!tp->port_fix) dev->if_port ++;
-			tp->port_select(dev);
-			dev->trans_start = jiffies;
-			return(0);
-		}
-		printk("%s: transmit timed out, status %8.8x,"
-			   "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
-			   dev->name, tio_read(CSR5), tio_read(CSR12),
-			   tio_read(CSR13), tio_read(CSR14), tio_read(CSR15));
-#if !defined(__alpha__) && !defined(__sparc_v9__)
-		printk("  Rx ring %8.8x: ", (int)tp->rx_ring);
-#endif
-		for (i = 0; i < RX_RING_SIZE; i++)
-			printk(" %8.8x", (unsigned int)le32_to_cpu(tp->rx_ring[i].status));
-#if !defined(__alpha__) && !defined(__sparc_v9__)
-		printk("\n  Tx ring %8.8x: ", (int)tp->tx_ring);
-#endif
-		for (i = 0; i < TX_RING_SIZE; i++)
-			printk(" %8.8x", (unsigned int)le32_to_cpu(tp->tx_ring[i].status));
-		printk("\n");
-
-		tp->stats.tx_errors++;
-		/* Perhaps we should reinitialize the hardware here. */
-		dev->if_port = 0;
-		tio_write(TSIAC_CONFIG, CSR13);
-		/* Start the chip's Tx and Rx processes . */
-		tio_write(TCMOD_10TP | TCMOD_TRxSTART, CSR6);
-		/* Trigger an immediate transmit demand. */
-		tio_write(TPOLL_TRIGGER, CSR1);
-
-		dev->tbusy=0;
-		dev->trans_start = jiffies;
-		return(0);
+	int entry;
+	u32 flag;
+
+#ifdef ORIGINAL_TEXT
+#ifndef final_version
+	if (skb == NULL || skb->len <= 0) {
+		printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n",
+			   dev->name);
+		dev_tint(dev);
+		return 0;
 	}
+#endif
+#endif
 
+#ifdef CONFIG_NET_FASTROUTE
+	cli();
+	dev->tx_semaphore = 0;
+#endif
 	/* 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 this ever occurs the queue layer is doing something evil! */
+	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
 	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
-		printk("%s: Transmitter access conflict.\n", dev->name);
+#ifdef CONFIG_NET_FASTROUTE
+		sti();
+#endif
+		if (jiffies - dev->trans_start >= TX_TIMEOUT)
+				tulip_tx_timeout(dev);
+#ifdef CONFIG_NET_FASTROUTE
+		dev->tx_semaphore = 0;
+#endif
 		return 1;
 	}
 
@@ -868,90 +1775,108 @@
 	/* Calculate the next Tx descriptor entry. */
 	entry = tp->cur_tx % TX_RING_SIZE;
 
-	tp->tx_full = 1;
-	/*
-	 * If skb is == -1, then this is a funky setup_frame redo.
-	 */
-	if (skb == (struct sk_buff *) -1) {
-		daddr = virt_to_bus((char *)tp->setup_frame);
-#ifdef NO_ANK_FIX
-		len = 192;
-#else
-		len = 192 | 0x08000000;
-#endif
-		skb = NULL;
+	tp->tx_skbuff[entry] = skb;
+	tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
+
+	if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
+	  flag = 0x60000000; /* No interrupt */
+	  dev->tbusy = 0;
+	} else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
+	  flag = 0xe0000000; /* Tx-done intr. */
+	  dev->tbusy = 0;
+	} else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
+	  flag = 0x60000000; /* No Tx-done intr. */
+	  dev->tbusy = 0;
 	} else {
-		daddr = virt_to_bus(skb->data);
-		len = skb->len;
-		tp->stats.tx_bytes+=len;
+	  /* Leave room for set_rx_mode() to fill entries. */
+	  flag = 0xe0000000; /* Tx-done intr. */
+	  tp->tx_full = 1;
 	}
-	tp->tx_skbuff[entry] = skb;
-	tp->tx_ring[entry].length = cpu_to_le32(len |
-		(entry == TX_RING_SIZE-1 ? 0xe2000000 : 0xe0000000));
-	tp->tx_ring[entry].buffer1 = cpu_to_le32(daddr);
-	tp->tx_ring[entry].buffer2 = cpu_to_le32(0);
-	tp->tx_ring[entry].status = cpu_to_le32(TRING_OWN);	/* Pass ownership to the chip. */
-	barrier();
+	if (entry == TX_RING_SIZE-1)
+		flag |= 0xe2000000;
 
+	tp->tx_ring[entry].length = skb->len | flag;
+	tp->tx_ring[entry].status = 0x80000000;	/* Pass ownership to the chip. */
 	tp->cur_tx++;
-
 	/* Trigger an immediate transmit demand. */
-	tio_write(TPOLL_TRIGGER, CSR1);
+	outl(0, dev->base_addr + CSR1);
 
 	dev->trans_start = jiffies;
+#ifdef CONFIG_NET_FASTROUTE
+	dev->tx_semaphore = 0;
+	sti();
+#endif
 
-	return(0);
+	return 0;
 }
 
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
-static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs)
 {
-	struct device *dev = (struct device *)dev_id;
+#ifdef SA_SHIRQ		/* Use the now-standard shared IRQ implementation. */
+	struct device *dev = (struct device *)dev_instance;
+#else
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
+
 	struct tulip_private *lp;
-	int csr5, boguscnt=10;
-	unsigned long ioaddr;
+	int csr5, ioaddr, boguscnt = 12;
 
 	if (dev == NULL) {
-		printk ("tulip_interrupt(): irq %d for unknown device.\n", irq);
+		printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n",
+				irq);
 		return;
 	}
 
 	ioaddr = dev->base_addr;
 	lp = (struct tulip_private *)dev->priv;
 	if (dev->interrupt)
-		printk("%s: Re-entering the interrupt handler.\n", dev->name);
+		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
 
 	dev->interrupt = 1;
 
 	do {
-		csr5 = tio_read(CSR5);
+		csr5 = inl(ioaddr + CSR5);
 		/* Acknowledge all of the current interrupt sources ASAP. */
-		tio_write(csr5 & TSTAT_CLEARINTR, CSR5);
-		/* check interrupt ? */
-		if ((csr5 & (TSTAT_NORINTR|TSTAT_ABNINTR)) == 0) break;
+		outl(csr5 & 0x0001ffff, ioaddr + CSR5);
+
+		if (tulip_debug > 4)
+			printk(KERN_DEBUG "%s: interrupt  csr5=%#8.8x new csr5=%#8.8x.\n",
+				   dev->name, csr5, inl(dev->base_addr + CSR5));
+
+		if ((csr5 & 0x00018000) == 0)
+			break;
 
-		if (csr5 & TSTAT_RxINTR)			/* Rx interrupt */
+		if (csr5 & RxIntr)
 			tulip_rx(dev);
 
-		if (csr5 & TSTAT_TxINTR) {		/* Tx-done interrupt */
-			int dirty_tx = lp->dirty_tx;
+		if (csr5 & (TxNoBuf | TxDied | TxIntr)) {
+			int dirty_tx;
 
-			while (dirty_tx < lp->cur_tx) {
+			for (dirty_tx = lp->dirty_tx; dirty_tx < lp->cur_tx; dirty_tx++) {
 				int entry = dirty_tx % TX_RING_SIZE;
-				int status = le32_to_cpu(lp->tx_ring[entry].status);
+				int status = lp->tx_ring[entry].status;
 
 				if (status < 0)
 					break;			/* It still hasn't been Txed */
+				/* Check for Rx filter setup frames. */
+				if (lp->tx_skbuff[entry] == NULL)
+				  continue;
 
-				if (status & TRING_ERROR) {
+				if (status & 0x8000) {
 					/* There was an major error, log it. */
+#ifndef final_version
+					if (tulip_debug > 1)
+						printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
+							   dev->name, status);
+#endif
 					lp->stats.tx_errors++;
-					if (status & TRING_TxABORT) lp->stats.tx_aborted_errors++;
-					if (status & TRING_TxCARR) lp->stats.tx_carrier_errors++;
-					if (status & TRING_TxWINDOW) lp->stats.tx_window_errors++;
-					if (status & TRING_TxFIFO) lp->stats.tx_fifo_errors++;
-					if ((status & TRING_TxHEARTBEAT) && !lp->full_duplex)
+					if (status & 0x4104) lp->stats.tx_aborted_errors++;
+					if (status & 0x0C00) lp->stats.tx_carrier_errors++;
+					if (status & 0x0200) lp->stats.tx_window_errors++;
+					if (status & 0x0002) lp->stats.tx_fifo_errors++;
+					if ((status & 0x0080) && lp->full_duplex == 0)
 						lp->stats.tx_heartbeat_errors++;
 #ifdef ETHER_STATS
 					if (status & 0x0100) lp->stats.collisions16++;
@@ -965,15 +1890,14 @@
 				}
 
 				/* Free the original skb. */
-				if (lp->tx_skbuff[entry] != NULL)
-					dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
-				dirty_tx++;
+				dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
+				lp->tx_skbuff[entry] = 0;
 			}
 
 #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);
+			if (lp->cur_tx - dirty_tx > TX_RING_SIZE) {
+				printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+					   dev->name, dirty_tx, lp->cur_tx, lp->tx_full);
 				dirty_tx += TX_RING_SIZE;
 			}
 #endif
@@ -990,34 +1914,52 @@
 		}
 
 		/* Log errors. */
-		if (csr5 & TSTAT_ABNINTR) {	/* Abnormal error summary bit. */
-			if (csr5 & TSTAT_TxTOUT) lp->stats.tx_errors++; /* Tx babble. */
-			if (csr5 & TSTAT_RxMISSED) { 		/* Missed a Rx frame. */
+		if (csr5 & 0x8000) {	/* Abnormal error summary bit. */
+			if (csr5 & TxJabber) lp->stats.tx_errors++;
+			if (csr5 & TxFIFOUnderflow) {
+			  lp->csr6 |= 0x00200000;  /* Reconfigure to store-n-forward. */
+			  /* Restart the transmit process. */
+			  outl(lp->csr6 | 0x0002, ioaddr + CSR6);
+			  outl(lp->csr6 | 0x2002, ioaddr + CSR6);
+			}
+			if (csr5 & RxDied) {		/* Missed a Rx frame. */
 				lp->stats.rx_errors++;
-				lp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff;
+				lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 			}
-			if (csr5 & TSTAT_TEXPIRED) {
-				printk("%s: Something Wicked happened! %8.8x.\n",
+			if (csr5 & TimerInt) {
+				printk(KERN_ERR "%s: Something Wicked happened! %8.8x.\n",
 					   dev->name, csr5);
 				/* Hmmmmm, it's not clear what to do here. */
 			}
+			/* Clear all error sources, included undocumented ones! */
+			outl(0x000f7ba, ioaddr + CSR5);
 		}
 		if (--boguscnt < 0) {
-			printk("%s: Too much work at interrupt, csr5=0x%8.8x.\n",
+			printk(KERN_WARNING "%s: Too much work at interrupt, csr5=0x%8.8x.\n",
 				   dev->name, csr5);
 			/* Clear all interrupt sources. */
-			tio_write(TSTAT_CLEARINTR, CSR5);
+			outl(0x0001ffff, ioaddr + CSR5);
 			break;
 		}
 	} while (1);
 
-	/* Special code for testing *only*. */
+	if (tulip_debug > 3)
+		printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
+			   dev->name, inl(ioaddr + CSR5));
+
+	/* Code that should never be run!  Perhaps remove after testing.. */
 	{
 		static int stopit = 10;
 		if (dev->start == 0  &&  --stopit < 0) {
-			printk("%s: Emergency stop, looping startup interrupt.\n",
-				   dev->name);
+			printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n"
+				   KERN_ERR "%s: Disabling interrupt handler %d to avoid "
+				   "locking up the machine.\n",
+				   dev->name, dev->name, dev->irq);
+#ifdef SA_SHIRQ
 			free_irq(irq, dev);
+#else
+			free_irq(irq);
+#endif
 		}
 	}
 
@@ -1025,433 +1967,522 @@
 	return;
 }
 
+#ifdef CONFIG_NET_FASTROUTE
+/* DMAing cards are the most easy in this respect,
+   they are able to make fast route to any device.
+
+   Now we allow to make it only to another ethernet card.
+ */
+static int tulip_accept_fastpath(struct device *dev, struct dst_entry *dst)
+{
+	struct device *odev = dst->dev;
+
+	if (dst->ops->protocol != __constant_htons(ETH_P_IP))
+			return -1;
+	if (odev->type != ARPHRD_ETHER || odev->accept_fastpath == NULL)
+			return -1;
+
+	return 0;
+}
+
+/*
+   Return values:
+
+   0 - packet has gone by fast path.
+   1 - fast path is OK, but device deferred xmit. (semifast path)
+
+   2 - fast path is hit, but packet is a bit strange. (NI)
+   3 - oom
+
+   4 - fast path miss.
+ */
+
+static int tulip_fast_forward(struct device *dev, int entry, int len)
+{
+	struct tulip_private *lp = (struct tulip_private *)dev->priv;
+	struct sk_buff *skb = lp->rx_skbuff[entry];
+	struct ethhdr *eth = (void*)skb->data;
+
+	if (eth->h_proto == __constant_htons(ETH_P_IP)) {
+		struct rtable *rt;
+		struct iphdr *iph;
+		unsigned h;
+
+		iph = (struct iphdr*)(skb->data + ETH_HLEN);
+		h = (*(u8*)&iph->daddr^*(u8*)&iph->saddr)&NETDEV_FASTROUTE_HMASK;
+		rt = (struct rtable*)(dev->fastpath[h]);
+		if (rt &&
+			((u16*)&iph->daddr)[0] == ((u16*)&rt->key.dst)[0] &&
+			((u16*)&iph->daddr)[1] == ((u16*)&rt->key.dst)[1] &&
+			((u16*)&iph->saddr)[0] == ((u16*)&rt->key.src)[0] &&
+			((u16*)&iph->saddr)[1] == ((u16*)&rt->key.src)[1] &&
+			rt->u.dst.obsolete == 0) {
+			struct device *odev = rt->u.dst.dev;
+
+			dev_fastroute_stat.hits++;
+
+			if (*(u8*)iph != 0x45 ||
+				(eth->h_dest[0]&1) ||
+				!neigh_is_valid(rt->u.dst.neighbour) ||
+				iph->ttl <= 1)
+					goto alas2;
+
+			ip_decrease_ttl(iph);
+
+			if (1) {
+				struct sk_buff *skb2 = DEV_ALLOC_SKB(PKT_BUF_SZ);
+				if (skb2 == NULL)
+						goto oom;
+				lp->rx_ring[entry].buffer1 = virt_to_bus(skb2->tail);
+				skb2->dev = dev;
+				lp->rx_skbuff[entry] = skb2;
+			}
+
+			skb_put(skb, len);
+
+			ip_statistics.IpInReceives++;
+			ip_statistics.IpForwDatagrams++;
+
+			/* Could use hh cache */
+			memcpy(eth->h_source, odev->dev_addr, 6);
+			memcpy(eth->h_dest, rt->u.dst.neighbour->ha, 6);
+			skb->dev = odev;
+
+#ifdef FAST_SKB_RECYCLE /* DO NOT DEFINE IT! READ COMMENT */
+			/* We could use fast buffer recycling here if odev
+			   is not DMAing.
+
+			   The only problem is that we must allocate skb2
+			   BEFORE we lose skb, otherwise we would make hole in
+			   tulip rx array. Hence, to implement FAST_SKB_RECYCLE
+			   we need always keep at least one skb in a safe place.
+			 */
+			atomic_inc(&skb->users);
+#endif
+
+			if (odev->tx_semaphore &&
+				odev->tbusy == 0 &&
+				odev->interrupt == 0 &&
+				odev->hard_start_xmit(skb, odev) == 0) {
+#ifdef FAST_SKB_RECYCLE
+				if (atomic_read(&skb->users) == 1) {
+					skb->tail = skb->data;
+					skb->len = 0;
+				}
+#endif
+				dev_fastroute_stat.succeed++;
+				return 0;
+			}
+#ifdef FAST_SKB_RECYCLE
+			atomic_dec(&skb->users);
+#endif
+
+			/* Otherwise... */
+			skb->pkt_type = PACKET_FASTROUTE;
+			skb->nh.raw = skb->data + ETH_HLEN;
+			skb->protocol = __constant_htons(ETH_P_IP);
+			dev_fastroute_stat.deferred++;
+			return 1;
+		}
+	}
+	return 4;
+
+oom:
+	return 3;
+
+alas2:
+#ifdef not_yet
+	skb->dst = dst_clone(&rt->u.dst);
+	return 2;
+#else
+	return 4;
+#endif
+}
+#endif
+
 static int
 tulip_rx(struct device *dev)
 {
 	struct tulip_private *lp = (struct tulip_private *)dev->priv;
 	int entry = lp->cur_rx % RX_RING_SIZE;
-	int i;
 
+	if (tulip_debug > 4)
+		printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
+			   lp->rx_ring[entry].status);
 	/* If we own the next entry, it's a new packet. Send it up. */
-	while (((s32)le32_to_cpu(lp->rx_ring[entry].status)) >= 0) {
-		int status = le32_to_cpu(lp->rx_ring[entry].status);
+	while (lp->rx_ring[entry].status >= 0) {
+		int status = lp->rx_ring[entry].status;
 
-		if ((status & TRING_RxDESCMASK) != TRING_RxDESCMASK) {
-			printk("%s: Ethernet frame spanned multiple buffers,"
-				   "status %8.8x!\n", dev->name, status);
-		} else if (status & TRING_ERROR) {
+		if ((status & 0x0300) != 0x0300) {
+			if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */
+			  printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
+					 "multiple buffers, status %8.8x!\n", dev->name, status);
+			  lp->stats.rx_length_errors++;
+			}
+		} else if (status & 0x8000) {
 			/* There was a fatal error. */
 			lp->stats.rx_errors++; /* end of a packet.*/
-			if (status & TRING_RxLENGTH) lp->stats.rx_length_errors++;
-			if (status & TRING_RxFRAME) lp->stats.rx_frame_errors++;
-			if (status & TRING_RxCRC) lp->stats.rx_crc_errors++;
-			if (status & TRING_RxFIFO) lp->stats.rx_fifo_errors++;
+			if (status & 0x0890) lp->stats.rx_length_errors++;
+			if (status & 0x0004) lp->stats.rx_frame_errors++;
+			if (status & 0x0002) lp->stats.rx_crc_errors++;
+			if (status & 0x0001) lp->stats.rx_fifo_errors++;
 		} else {
 			/* Malloc up new buffer, compatible with net-2e. */
 			/* Omit the four octet CRC from the length. */
-			short pkt_len = (le32_to_cpu(lp->rx_ring[entry].status) >> 16) - 4;
+			short pkt_len = (lp->rx_ring[entry].status >> 16) - 4;
 			struct sk_buff *skb;
+			int rx_in_place = 0;
+
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+			if (netdev_dropping)
+					goto throttle;
+#endif
 
-			skb = dev_alloc_skb(pkt_len + 2);
+			skb = lp->rx_skbuff[entry];
+
+#ifdef CONFIG_NET_FASTROUTE
+			switch (tulip_fast_forward(dev, entry, pkt_len)) {
+			case 0:	
+					goto gone;
+			case 1:
+					goto semi_gone;
+			case 2:
+					break;
+			case 3:
+					skb = NULL;
+					goto memory_squeeze;
+			}
+#endif
+			/* Check if the packet is long enough to just accept without
+			   copying to a properly sized skbuff. */
+			if (pkt_len > rx_copybreak) {
+				struct sk_buff *newskb;
+				char *temp;
+
+				/* Get a fresh skbuff to replace the filled one. */
+				newskb = DEV_ALLOC_SKB(PKT_BUF_SZ);
+				if (newskb == NULL) {
+					skb = NULL;		/* No memory, drop the packet. */
+					goto memory_squeeze;
+				}
+				/* Pass up the skb already on the Rx ring. */
+				temp = skb_put(skb, pkt_len);
+				if (bus_to_virt(lp->rx_ring[entry].buffer1) != temp)
+					printk(KERN_ERR "%s: Internal consistency error -- the "
+						   "skbuff addresses do not match"
+						   " in tulip_rx: %p vs. %p / %p.\n", dev->name,
+						   bus_to_virt(lp->rx_ring[entry].buffer1),
+						   skb->head, temp);
+				rx_in_place = 1;
+				lp->rx_skbuff[entry] = newskb;
+				newskb->dev = dev;
+				/* Longword alignment required: do not skb_reserve(2)! */
+				lp->rx_ring[entry].buffer1 = virt_to_bus(newskb->tail);
+			} else
+				skb = DEV_ALLOC_SKB(pkt_len + 2);
+memory_squeeze:
 			if (skb == NULL) {
-				printk("%s: Memory squeeze, deferring packet.\n",
+				int i;
+				printk(KERN_WARNING "%s: Memory squeeze, deferring packet.\n",
 					   dev->name);
 				/* Check that at least two ring entries are free.
 				   If not, free one and mark stats->rx_dropped++. */
-				for (i=0; i < RX_RING_SIZE; i++)
-					if (((s32)le32_to_cpu(lp->rx_ring[(entry+i) % RX_RING_SIZE].status)) < 0)
+				for (i = 0; i < RX_RING_SIZE; i++)
+					if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0)
 						break;
 
 				if (i > RX_RING_SIZE -2) {
 					lp->stats.rx_dropped++;
-					lp->rx_ring[entry].status = cpu_to_le32(TRING_OWN);
+					lp->rx_ring[entry].status = 0x80000000;
 					lp->cur_rx++;
 				}
 				break;
 			}
 			skb->dev = dev;
-			skb_reserve(skb, 2);
-			memcpy(skb_put(skb, pkt_len),
-				   bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len);
-			/* Needed for 1.3.x */
-			skb->protocol = eth_type_trans(skb,dev);
+			if (! rx_in_place) {
+				skb_reserve(skb, 2);	/* 16 byte align the data fields */
+#if LINUX_VERSION_CODE < 0x20200  || defined(__alpha__)
+				memcpy(skb_put(skb, pkt_len),
+					   bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len);
+#else
+#ifdef ORIGINAL_TEXT
+#warning Code untested
+#else
+#error Code is wrong, and it has nothing to do with 2.2 :-)
+#endif
+				eth_copy_and_sum(skb, bus_to_virt(lp->rx_ring[entry].buffer1),
+									pkt_len, 0);
+				skb_put(skb, pkt_len);
+#endif
+			}
+#if LINUX_VERSION_CODE > 0x10300
+			skb->protocol = eth_type_trans(skb, dev);
+#else
+			skb->len = pkt_len;
+#endif
+#ifdef CONFIG_NET_FASTROUTE
+semi_gone:
+#endif
 			netif_rx(skb);
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+			if (netdev_dropping) {
+throttle:
+				if (lp->fc_bit) {
+					outl(lp->csr6 | 0x2000, dev->base_addr + CSR6);
+					set_bit(lp->fc_bit, &netdev_fc_xoff);
+				}
+			}
+#endif
+#ifdef CONFIG_NET_FASTROUTE
+gone:
+#endif
 			lp->stats.rx_packets++;
-			lp->stats.rx_bytes+=skb->len;
+#ifndef ORIGINAL_TEXT
+			lp->stats.rx_bytes += pkt_len;
+#endif
 		}
 
-		lp->rx_ring[entry].status = cpu_to_le32(TRING_OWN);
+		lp->rx_ring[entry].status = 0x80000000;
 		entry = (++lp->cur_rx) % RX_RING_SIZE;
 	}
-	return(0);
+
+	return 0;
 }
 
 static int
 tulip_close(struct device *dev)
 {
-	unsigned long ioaddr = dev->base_addr;
+	int ioaddr = dev->base_addr;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int i;
 
+#ifdef CONFIG_NET_FASTROUTE
+	dev->tx_semaphore = 0;
+#endif
 	dev->start = 0;
 	dev->tbusy = 1;
 
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+	if (tp->fc_bit) {
+		int bit = tp->fc_bit;
+		tp->fc_bit = 0;
+		netdev_unregister_fc(bit);
+	}
+#endif
+
+	if (tulip_debug > 1)
+		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
+			   dev->name, inl(ioaddr + CSR5));
+
 	/* Disable interrupts by clearing the interrupt mask. */
-	tio_write(TINTR_DISABLE, CSR7);
+	outl(0x00000000, ioaddr + CSR7);
 	/* Stop the chip's Tx and Rx processes. */
-	tio_write(tio_read(CSR6) & ~(TCMOD_TRxSTART), CSR6);
-	/* Leave the card in 10baseT state. */
-	tio_write(TSIAC_CONFIG, CSR13);
+	outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+	/* 21040 -- Leave the card in 10baseT state. */
+	if (tp->chip_id == DC21040)
+		outl(0x00000004, ioaddr + CSR13);
 
-	tp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff;
+	tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 
-	tio_write(0, CSR13);
-/*	tio_write(0, CSR8);	wake up chip ? */
+	del_timer(&tp->timer);
 
+#ifdef SA_SHIRQ
 	free_irq(dev->irq, dev);
+#else
+	free_irq(dev->irq);
+	irq2dev_map[dev->irq] = 0;
+#endif
 
-	MOD_DEC_USE_COUNT;
-	return(0);
-}
+	/* Free all the skbuffs in the Rx queue. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb = tp->rx_skbuff[i];
+		tp->rx_skbuff[i] = 0;
+		tp->rx_ring[i].status = 0;		/* Not owned by Tulip chip. */
+		tp->rx_ring[i].length = 0;
+		tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
+		if (skb) {
+#if LINUX_VERSION_CODE < 0x20100
+			skb->free = 1;
+#endif
+			dev_kfree_skb(skb, FREE_WRITE);
+		}
+	}
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		if (tp->tx_skbuff[i])
+			dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE);
+		tp->tx_skbuff[i] = 0;
+	}
 
-static int
-tulip_config(struct device *dev, struct ifmap *map)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 
-	if (map->port == 0xff) return(-EINVAL);
-	dev->if_port = map->port;
-	tp->port_fix = 1;
-	if (tp->port_select) tp->port_select(dev);
-	return(0);
+	MOD_DEC_USE_COUNT;
+
+	return 0;
 }
 
-static struct net_device_stats *tulip_get_stats(struct device *dev)
+static struct enet_statistics *
+tulip_get_stats(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	/*	unsigned long ioaddr = dev->base_addr;*/
+	int ioaddr = dev->base_addr;
+
+	if (dev->start)
+		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 
-	return(&tp->stats);
+	return &tp->stats;
 }
 
-/*
- *	Set or clear the multicast filter for this adaptor.
- */
+/* Set or clear the multicast filter for this adaptor.
+   Note that we only use exclusion around actually queueing the
+   new frame, not around filling tp->setup_frame.  This is non-deterministic
+   when re-entered but still correct. */
+
+/* The little-endian AUTODIN32 ethernet CRC calculation.
+   N.B. Do not use for bulk data, use a table-based routine instead.
+   This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+	unsigned int crc = 0xffffffff;	/* Initial value. */
+	while(--length >= 0) {
+		unsigned char current_octet = *data++;
+		int bit;
+		for (bit = 8; --bit >= 0; current_octet >>= 1) {
+			if ((crc ^ current_octet) & 1) {
+				crc >>= 1;
+				crc ^= ethernet_polynomial_le;
+			} else
+				crc >>= 1;
+		}
+	}
+	return crc;
+}
 
+#ifdef NEW_MULTICAST
 static void set_multicast_list(struct device *dev)
+#else
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+#endif
 {
-	unsigned long ioaddr = dev->base_addr;
-	int csr6 = tio_read(CSR6) & ~(TCMOD_MODEMASK|TCMOD_FILTERMASK);
+	int ioaddr = dev->base_addr;
+	int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 
-	if (dev->flags&IFF_PROMISC) 
-	{			/* Set promiscuous. why ALLMULTI ? */
-		tio_write(csr6 | TCMOD_PROMISC | TCMOD_ALLMCAST, CSR6);
-		/* Log any net taps. */
-		printk("%s: Promiscuous mode enabled.\n", dev->name);
-	}
-	else if (dev->mc_count > 14 || (dev->flags&IFF_ALLMULTI)) 
-	{
+	tp->csr6 &= ~0x00D5;
+	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
+		outl(csr6 | 0x00C0, ioaddr + CSR6);
+		/* Unconditionally log net taps. */
+		printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
+		tp->csr6 |= 0xC0;
+	} else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
 		/* Too many to filter perfectly -- accept all multicasts. */
-		tio_write(csr6 | TCMOD_ALLMCAST, CSR6);
-	}
-	else
-	{
-		struct tulip_private *tp = (struct tulip_private *)dev->priv;
-		struct dev_mc_list *dmi=dev->mc_list;
-		int *setup_frm = tp->setup_frame;
-		unsigned short *eaddrs;
+		outl(csr6 | 0x0080, ioaddr + CSR6);
+		tp->csr6 |= 0x80;
+	} else {
+		u32 *setup_frm = tp->setup_frame;
+		struct dev_mc_list *mclist;
+		u16 *eaddrs;
+		u32 tx_flags;
 		int i;
 
-		/* We have < 15 addresses that we can use the wonderful
-		   16 address perfect filtering of the Tulip.  Note that only
-		   the low shortword of setup_frame[] is valid. */
-		tio_write(csr6 | 0x0000, CSR6);
-		for (i = 0; i < dev->mc_count; i ++) {
-			eaddrs=(unsigned short *)dmi->dmi_addr;
-			dmi=dmi->next;
-			*setup_frm++ = cpu_to_le32(*eaddrs++);
-			*setup_frm++ = cpu_to_le32(*eaddrs++);
-			*setup_frm++ = cpu_to_le32(*eaddrs++);
-		}
-		/* Fill the rest of the table with our physical address. */
-		eaddrs = (unsigned short *)dev->dev_addr;
-		/* Always accept broadcast packets */
-		*setup_frm++ = cpu_to_le32(0xffff);
-		*setup_frm++ = cpu_to_le32(0xffff);
-		*setup_frm++ = cpu_to_le32(0xffff);
+		if (dev->mc_count > 14) { /* Must use a multicast hash table. */
+		  u16 hash_table[32];
+		  memset(hash_table, 0, sizeof(hash_table));
+		  /* This should work on big-endian machines as well. */
+		  for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+			   i++, mclist = mclist->next)
+			  set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
+					  hash_table);
+		  /* Copy the hash table to the setup frame.
+			 NOTE that only the LOW SHORTWORD of setup_frame[] is valid! */
+		  for (i = 0; i < 32; i++)
+			*setup_frm++ = hash_table[i];
+		  setup_frm += 7;
+		  tx_flags = 0x08400000 | 192;
+		  /* Too clever: i > 15 for fall-though. */
+		} else {
+		  /* We have <= 15 addresses so we can use the wonderful
+			 16 address perfect filtering of the Tulip. */
+		  for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+			   i++, mclist = mclist->next) {
+			/* Note that only the low shortword of setup_frame[] is valid!
+			   This code may require tweaking for non-x86 architectures! */
+			eaddrs = (u16 *)mclist->dmi_addr;
+			*setup_frm++ = *eaddrs++;
+			*setup_frm++ = *eaddrs++;
+			*setup_frm++ = *eaddrs++;
+		  }
+		  /* Fill the rest of the table with our physical address.
+			 Once again, only the low shortword or setup_frame[] is valid! */
+		  *setup_frm++ = 0xffff;
+		  *setup_frm++ = 0xffff;
+		  *setup_frm++ = 0xffff;
+		  tx_flags = 0x08000000 | 192;
+		}
+		eaddrs = (u16 *)dev->dev_addr;
 		do {
-			*setup_frm++ = cpu_to_le32(eaddrs[0]);
-			*setup_frm++ = cpu_to_le32(eaddrs[1]);
-			*setup_frm++ = cpu_to_le32(eaddrs[2]);
+		  *setup_frm++ = eaddrs[0];
+		  *setup_frm++ = eaddrs[1];
+		  *setup_frm++ = eaddrs[2];
 		} while (++i < 15);
-
 		/* Now add this frame to the Tx list. */
-		tulip_start_xmit((struct sk_buff *) -1, dev);
-	}
-}
-
-__initfunc(int
-tulip_hwinit(struct device *dev, unsigned long ioaddr,
-			 int irq, int device_id))
-{
-	/* See note below on the Znyx 315 etherarray. */
-	static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
-	static int last_irq;
-	char detect_mesg[80], *mesgp=detect_mesg;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int i;
-	unsigned short sum, bitsum;
-
-	if (check_region(ioaddr, TULIP_TOTAL_SIZE) != 0) {
-		printk("tulip_hwinit: region already allocated at %#3lx.\n",
-			   ioaddr);
-		return(-1);
-	}
-
-	mesgp += sprintf(mesgp, "(DEC 21%d4%d Tulip",
-					 device_id == PCI_DEVICE_ID_DEC_TULIP_FAST,
-					 device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS);
-
-	/* Stop the chip's Tx and Rx processes. */
-	tio_write(tio_read(CSR6) & ~TCMOD_TRxSTART, CSR6);
-	/* Clear the missed-packet counter. */
-	i = tio_read(CSR8) & 0xffff;
-
-	if (device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS
-	    && (tio_read(CSR9) & 0x8000)) {
-		mesgp += sprintf(mesgp, " treat as 21040");
-	    device_id = PCI_DEVICE_ID_DEC_TULIP;
-	}
-	
-	/* The station address ROM is read byte serially.  The register must
-	   be polled, waiting for the value to be read bit serially from the
-	   EEPROM.
-	   */
-	sum = 0;
-	if (device_id == PCI_DEVICE_ID_DEC_TULIP) {
-		tio_write(0, CSR9);
-	    /* Reset the pointer with a dummy write. */
-	    bitsum = 0xff;
-	    for (i = 0; i < 6; i++) {
-			int value, boguscnt = 100000;
-			do
-				value = tio_read(CSR9);
-			while (value < 0  && --boguscnt > 0);
-			dev->dev_addr[i] = value;
-			sum += value & 0xFF;
-			bitsum &= value;
-	    }
-	} else {
-	    /* Must be a 21140/21041, with a serial EEPROM interface. */
-	    struct eeprom eep;
-	    u_char *addr;
-
-	    if (read_eeprom(ioaddr, &eep) < 0) {
-			addr = eep.ng_addr;/* broken EEPROM structure */
-	    } else {
-			addr = eep.ok_addr;/* DEC EtherWorks */
-	    }
-	    for (i = 0; i < ETH_ALEN; i++) {
-			sum += addr[i];
-			dev->dev_addr[i] = addr[i];
-	    }
-	}
-	/* Make certain the data structures are quadword aligned. */
-
-	mesgp += sprintf(mesgp, ") at %016lx, ", ioaddr);
-
-	/* On the Zynx 315 etherarray boards only the first Tulip has an EEPROM.
-	   The addresses of the subsequent ports are derived from the first. */
-	if (sum == 0) {
-		for (i = 0; i < ETH_ALEN - 1; i++)
-			dev->dev_addr[i] = last_phys_addr[i];
-		dev->dev_addr[i] = last_phys_addr[i] + 1;
-		irq = last_irq;
-	}
-	for (i = 0; i < ETH_ALEN - 1; i++)
-		mesgp += sprintf(mesgp, "%2.2x:", dev->dev_addr[i]);
-	mesgp += sprintf(mesgp, "%2.2x, IRQ %d\n",
-					 last_phys_addr[i] = dev->dev_addr[i], irq);
-	last_irq = irq;
-
-	/* copy ethernet address */
-	if (card_type(tp, device_id,
-				  htonl((*(int*)dev->dev_addr) & 0xFFFFFF)))
-		for (i = 0; i < ETH_ALEN - 1; i++)
-			last_phys_addr[i] = dev->dev_addr[i];
-	/* We do a request_region() only to register /proc/ioports info. */
-	request_region(ioaddr, TULIP_TOTAL_SIZE, tp->signature);
-
-	dev->base_addr = ioaddr;
-	dev->irq = irq;
-
-	/* The Tulip-specific entries in the device structure. */
-	dev->open = &tulip_open;
-	dev->hard_start_xmit = &tulip_start_xmit;
-	dev->stop = &tulip_close;
-	dev->get_stats = &tulip_get_stats;
-	dev->set_config = &tulip_config;
-	dev->set_multicast_list = &set_multicast_list;
-
-#ifdef	MODULE
-	if (if_port == TULIP_AUTO_PORT)
-		if_port = TULIP_PORT;
-	else
-		tp->port_fix = 1;
-	dev->if_port = if_port;
-	tp->full_duplex = full_duplex;
-	tp->next_module = root_tulip_dev;
-	root_tulip_dev = dev;
-#else
-#ifdef TULIP_FULL_DUPLEX
-	tp->full_duplex = 1;
-#endif
-	dev->if_port = TULIP_PORT;
-#endif
-#ifdef	TULIP_FIX_PORT
-	tp->port_fix = 1;
-#endif
-
-	printk("%s: %s %s", dev->name, tp->signature, detect_mesg);
-
-	/* Reset the xcvr interface and turn on heartbeat. */
-	tio_write(TSIAC_RESET, CSR13);
-	tio_write(TSIAC_CONFIG, CSR13);
-
-	return(0);
-}
-
-__initfunc(int tulip_probe(struct device *dev))
-{
-	static struct device *tulip_head=NULL;
-#ifdef __sparc_v9__
-	struct pci_dev *pdev;
-#else
-	u_char btmp;
-	u_int itmp;
-#endif
-	u_char pci_bus, pci_device_fn, pci_latency;
-	u_int pci_irq;
-	unsigned long pci_ioaddr;
-	u_short pci_command, vendor_id, device_id;
-	u_int pci_chips[] = {
-		PCI_DEVICE_ID_DEC_TULIP,
-		PCI_DEVICE_ID_DEC_TULIP_FAST,
-		PCI_DEVICE_ID_DEC_TULIP_PLUS,
-		PCI_DEVICE_ID_NONE
-	};
-	int num=0, cno;
-	static int pci_index = 0;
-
-    if (!pcibios_present())
-			return(-ENODEV);
-
-	for (; pci_index < 0xff; pci_index++) {
-		if (pcibios_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
-							   &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
-				break;
-
-#ifdef __sparc_v9__
-		for(pdev = pci_devices; pdev; pdev = pdev->next)
-				if(pdev->bus->number == pci_bus &&
-				   pdev->devfn == pci_device_fn)
-						break;
-		if(!pdev)
-				panic("tulip: Cannot find pci_dev for [%x:%x]\n",
-					  pci_bus, pci_device_fn);
-#endif
-
-		/* get vendor id */
-		pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID,
-								 &vendor_id);
-		/* get device id */
-		pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID,
-								 &device_id);
-
-#ifndef __sparc_v9__
-		/* get IO address */
-		pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0,
-								  &itmp);
-		pci_ioaddr = itmp;
-
-		/* get IRQ */
-		pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE,
-								 &btmp);
-		pci_irq = btmp;
-#else
-		/* get IO address */
-		pci_ioaddr = pdev->base_address[0];
-
-		/* get IRQ */
-		pci_irq = pdev->irq;
-#endif
-
-		/* Remove I/O space marker in bit 0. */
-		pci_ioaddr &= ~3;
-		if (vendor_id != PCI_VENDOR_ID_DEC)
-				continue;
-
-		for (cno = 0; pci_chips[cno] != PCI_DEVICE_ID_NONE; cno++)
-				if (device_id == pci_chips[cno])
-						break;
-		if (pci_chips[cno] == PCI_DEVICE_ID_NONE) {
-			printk("Unknown Digital PCI ethernet chip type %4.4x detected:"
-				   " not configured.\n", device_id);
-			continue;
-		}
-		dev = init_etherdev(NULL, ROUND_UP(sizeof(struct device) +
-										   sizeof (struct tulip_private) +
-										   ETHNAMSIZ, 8));
-		if (dev == NULL)
-				break;
+		if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
+			/* Same setup recently queued, we need not add it. */
+		} else {
+			unsigned long flags;
+			unsigned int entry;
+			
+			save_flags(flags); cli();
+			entry = tp->cur_tx++ % TX_RING_SIZE;
+
+			if (entry != 0) {
+			  /* Avoid a chip errata by prefixing a dummy entry. */
+			  tp->tx_skbuff[entry] = 0;
+			  tp->tx_ring[entry].length =
+				(entry == TX_RING_SIZE-1) ? 0x02000000 : 0;
+			  tp->tx_ring[entry].buffer1 = 0;
+			  tp->tx_ring[entry].status = 0x80000000;
+			  entry = tp->cur_tx++ % TX_RING_SIZE;
+			}
 
-		if (!tulip_head) {
-			printk(version);
-			tulip_head = dev;
-		}
-
-		/* Get and check the bus-master and latency values. */
-		pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND,
-								 &pci_command);
-		if ((pci_command & PCI_COMMAND_MASTER) == 0) {
-			printk("  PCI Master Bit has not been set!"
-				   " Setting...\n");
-			pci_command |= PCI_COMMAND_MASTER;
-			pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND,
-									  pci_command);
-		}
-
-		pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER,
-								 &pci_latency);
-		
-		if (pci_latency < 10) {
-			printk("  PCI latency timer (CFLT) is"
-				   " unreasonably low at %d."
-				   "  Setting to 100 clocks.\n", pci_latency);
-			pcibios_write_config_byte(pci_bus, pci_device_fn,
-									  PCI_LATENCY_TIMER, 100);
-		}
-
-		if (tulip_hwinit(dev, pci_ioaddr, pci_irq, pci_chips[cno]) < 0) {
-			continue;
-		}
-		num++;
-#ifdef	TULIP_MAX_CARDS
-		if (num >= TULIP_MAX_CARDS) return(0);
-#endif
+			tp->tx_skbuff[entry] = 0;
+			/* Put the setup frame on the Tx list. */
+			if (entry == TX_RING_SIZE-1)
+			  tx_flags |= 0x02000000;		/* Wrap ring. */
+			tp->tx_ring[entry].length = tx_flags;
+			tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
+			tp->tx_ring[entry].status = 0x80000000;
+			if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
+				dev->tbusy = 1;
+				tp->tx_full = 1;
+			}
+			restore_flags(flags);
+			/* Trigger an immediate transmit demand. */
+			outl(0, ioaddr + CSR1);
+		}
+		outl(csr6 | 0x0000, ioaddr + CSR6);
 	}
-	return(num > 0 ? 0: -ENODEV);
 }
-
+
 #ifdef MODULE
+#if LINUX_VERSION_CODE > 0x20118
+MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
+MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver");
+MODULE_PARM(debug, "i");
+MODULE_PARM(reverse_probe, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
+#endif
 
-/* The parameters that may be passed in... */
-/* This driver does nothing with options yet.  It will later be used to
-   pass the full-duplex flag, etc. */
-int debug = -1;
+/* An additional parameter that may be passed in... */
+static int debug = -1;
 
 int
 init_module(void)
 {
+	if (debug >= 0)
+		tulip_debug = debug;
+
 	root_tulip_dev = NULL;
 	return tulip_probe(NULL);
 }
@@ -1463,21 +2494,21 @@
 
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	while (root_tulip_dev) {
-		next_dev =
-		   ((struct tulip_private *) root_tulip_dev->priv)->next_module;
+		next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module;
 		unregister_netdev(root_tulip_dev);
 		release_region(root_tulip_dev->base_addr, TULIP_TOTAL_SIZE);
 		kfree(root_tulip_dev);
 		root_tulip_dev = next_dev;
 	}
 }
-#endif /* MODULE */
 
+#endif  /* MODULE */
 
 /*
  * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c tulip.c"
+ *  compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c"
  *  c-indent-level: 4
+ *  c-basic-offset: 4
  *  tab-width: 4
  * End:
  */
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/audio/Makefile linux/drivers/sbus/audio/Makefile
--- v2.1.78/linux/drivers/sbus/audio/Makefile	Mon Mar 17 14:54:27 1997
+++ linux/drivers/sbus/audio/Makefile	Mon Jan 12 15:15:45 1998
@@ -25,11 +25,11 @@
 
 ifeq ($(CONFIG_SPARCAUDIO_AMD7930),y)
 M=y
-O_OBJS += amd7930.o
+OX_OBJS += amd7930.o
 else
   ifeq ($(CONFIG_SPARCAUDIO_AMD7930),m)
   MM=y
-  M_OBJS += amd7930.o
+  MX_OBJS += amd7930.o
   endif
 endif
 
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/audio/amd7930.c linux/drivers/sbus/audio/amd7930.c
--- v2.1.78/linux/drivers/sbus/audio/amd7930.c	Tue May 13 22:41:12 1997
+++ linux/drivers/sbus/audio/amd7930.c	Mon Jan 12 15:15:45 1998
@@ -1,12 +1,18 @@
 /*
  * drivers/sbus/audio/amd7930.c
  *
- * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
+ * Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu)
  *
  * This is the lowlevel driver for the AMD7930 audio chip found on all
  * sun4c machines and some sun4m machines.
  *
- * XXX Add note about the fun of getting the docs.
+ * The amd7930 is actually an ISDN chip which has a very simple
+ * integrated audio encoder/decoder. When Sun decided on what chip to
+ * use for audio, they had the brilliant idea of using the amd7930 and
+ * only connecting the audio encoder/decoder pins.
+ *
+ * Thanks to the AMD engineer who was able to get us the AMD79C30
+ * databook which has all the programming information and gain tables.
  */
 
 #include <linux/config.h>
@@ -29,26 +35,78 @@
 
 #define MAX_DRIVERS 1
 
-/* Private information we store for each amd7930 chip. */
-struct amd7930_info {
-	/* Current buffer that the driver is playing. */
+static struct sparcaudio_driver drivers[MAX_DRIVERS];
+static int num_drivers;
+
+/* Each amd7930 chip has two bi-directional B channels and a D
+ * channel available to the uproc.  This structure handles all
+ * the buffering needed to transmit and receive via a single channel.
+ */
+
+#define CHANNEL_AVAILABLE	0x00
+#define CHANNEL_INUSE_AUDIO_IN	0x01
+#define CHANNEL_INUSE_AUDIO_OUT	0x02
+#define CHANNEL_INUSE_ISDN_B1	0x04
+#define CHANNEL_INUSE_ISDN_B2	0x08
+#define CHANNEL_INUSE		0xff
+
+struct amd7930_channel {
+	/* Channel status */
+	unsigned char channel_status;
+
+	/* Current buffer that the driver is playing on channel */
 	volatile __u8 * output_ptr;
 	volatile unsigned long output_count;
+	unsigned char xmit_idle_char;
 
-	/* Current record buffer. */
+	/* Callback routine (and argument) when output is done on */
+	void (*output_callback)();
+	void * output_callback_arg;
+
+	/* Current buffer that the driver is recording on channel */
 	volatile __u8 * input_ptr;
 	volatile unsigned long input_count;
+	volatile unsigned long input_limit;
+
+	/* Callback routine (and argument) when input is done on */
+	void (*input_callback)();
+	void * input_callback_arg;
+};
+
+/* Private information we store for each amd7930 chip. */
+struct amd7930_info {
+	struct amd7930_channel D;
+	struct amd7930_channel Bb;
+	struct amd7930_channel Bc;
+
+	/* Pointers to which B channels are being used for what
+	 * These three fields (Baudio, Bisdn[0], and Bisdn[1]) will either
+	 * be NULL or point to one of the Bb/Bc structures above.
+	 */
+	struct amd7930_channel *Baudio;
+	struct amd7930_channel *Bisdn[2];
 
 	/* Device registers information. */
 	struct amd7930 *regs;
 	unsigned long regs_size;
 	struct amd7930_map map;
 
+	/* Volume information. */
+	int pgain, rgain, mgain;
+
 	/* Device interrupt information. */
 	int irq;
 	volatile int ints_on;
+
+
+	/* Someone to signal when the ISDN LIU state changes */
+	int liu_state;
+	void (*liu_callback)(void *);
+	void *liu_callback_arg;
 };
 
+
+
 /* Output a 16-bit quantity in the order that the amd7930 expects. */
 #define amd7930_out16(regs,v) ({ regs->dr = v & 0xFF; regs->dr = (v >> 8) & 0xFF; })
 
@@ -120,9 +178,8 @@
 #define NR_GER_COEFFS (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
 
 /* Enable amd7930 interrupts atomically. */
-static __inline__ void amd7930_enable_ints(struct sparcaudio_driver *drv)
+static __inline__ void amd7930_enable_ints(struct amd7930_info *info)
 {
-	struct amd7930_info *info = (struct amd7930_info *)drv->private;
 	register unsigned long flags;
 
 	if (info->ints_on)
@@ -137,9 +194,8 @@
 }
 
 /* Disable amd7930 interrupts atomically. */
-static __inline__ void amd7930_disable_ints(struct sparcaudio_driver *drv)
+static __inline__ void amd7930_disable_ints(struct amd7930_info *info)
 {
-	struct amd7930_info *info = (struct amd7930_info *)drv->private;
 	register unsigned long flags;
 
 	if (!info->ints_on)
@@ -153,8 +209,24 @@
 	info->ints_on = 0;
 }  
 
+/* Idle amd7930 (no interrupts, no audio, no data) */
+static __inline__ void amd7930_idle(struct amd7930_info *info)
+{
+	register unsigned long flags;
+
+	if (!info->ints_on)
+		return;
+
+	save_and_cli(flags);
+	info->regs->cr = AMR_INIT;
+	info->regs->dr = 0;
+	restore_flags(flags);
+
+	info->ints_on = 0;
+}  
+
 /* Commit the local copy of the MAP registers to the amd7930. */
-static void amd7930_commit_map(struct sparcaudio_driver *drv)
+static void amd7930_write_map(struct sparcaudio_driver *drv)
 {
 	struct amd7930_info *info = (struct amd7930_info *)drv->private;
 	struct amd7930      *regs = info->regs;
@@ -184,71 +256,257 @@
 	restore_flags(flags);
 }
 
-/* Interrupt handler (The chip takes only one byte per interrupt. Grrr!) */
-static void amd7930_interrupt(int irq, void *dev_id, struct pt_regs *intr_regs)
+/* Update the MAP registers with new settings. */
+static void amd7930_update_map(struct sparcaudio_driver *drv)
 {
-	struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
 	struct amd7930_info *info = (struct amd7930_info *)drv->private;
-	struct amd7930 *regs = info->regs;
+	struct amd7930_map  *map  = &info->map;
+	int level;
+
+	map->gx = gx_coeff[info->rgain];
+	map->stgr = gx_coeff[info->mgain];
+
+	level = (info->pgain * (256 + NR_GER_COEFFS)) >> 8;
+	if (level >= 256) {
+		map->ger = ger_coeff[level - 256];
+		map->gr = gx_coeff[255];
+	} else {
+		map->ger = ger_coeff[0];
+		map->gr = gx_coeff[level];
+	}
+
+	/* force output to speaker for now */
+	map->mmr2 |= AM_MAP_MMR2_LS;
+
+	/* input from external microphone */
+	map->mmr2 |= AM_MAP_MMR2_AINB;
+
+	amd7930_write_map(drv);
+}
+
+/* Bit of a hack here - if the HISAX ISDN driver has got INTSTAT debugging
+ * turned on, we send debugging characters to the ISDN driver:
+ *
+ *   i# - Interrupt received - number from 0 to 7 is low three bits of IR
+ *   >  - Loaded a single char into the Dchan xmit FIFO
+ *   +  - Finished loading an xmit packet into the Dchan xmit FIFO
+ *   <  - Read a single char from the Dchan recv FIFO
+ *   !  - Finished reading a packet from the Dchan recv FIFO
+ *
+ * This code needs to be removed if anything other than HISAX uses the ISDN
+ * driver, since D.output_callback_arg is assumed to be a certain struct ptr
+ */
+
+#include "../../isdn/hisax/hisax.h"
+#include "../../isdn/hisax/isdnl1.h"
+
+#ifdef L2FRAME_DEBUG
+
+inline void debug_info(struct amd7930_info *info, char c) {
+	struct IsdnCardState *cs;
+
+	if (!info || !info->D.output_callback_arg) return;
+
+	cs = (struct IsdnCardState *)info->D.output_callback_arg;
+
+	if (!cs || !cs->status_write) return;
+
+	if (cs->debug & L1_DEB_INTSTAT) {
+		*(cs->status_write++) = c;
+		if (cs->status_write > cs->status_end)
+			cs->status_write = cs->status_buf;
+	}
+}
+
+#else
+
+#define debug_info(info,c)
+
+#endif
+
+
+static void fill_D_xmit_fifo(struct amd7930_info *info)
+{
+	/* Send next byte(s) of outgoing data. */
+	while (info->D.output_ptr && info->D.output_count > 0 &&
+               (info->regs->dsr2 & AMR_DSR2_TBE)) {
+
+		/* Send the next byte and advance buffer pointer. */
+		info->regs->dctb = *(info->D.output_ptr);
+		info->D.output_ptr++;
+		info->D.output_count--;
+
+		debug_info(info, '>');
+        }
+}
+
+static void transceive_Dchannel(struct amd7930_info *info)
+{
 	__u8 dummy;
+	int lbrp=0;	/* Last Byte of Received Packet (LBRP) */
 
-	/* Clear the interrupt. */
-	dummy = regs->ir;
+#define D_XMIT_ERRORS (AMR_DER_COLLISION | AMR_DER_UNRN)
+#define D_RECV_ERRORS (AMR_DER_RABRT | AMR_DER_RFRAME | AMR_DER_FCS | \
+			AMR_DER_OVFL | AMR_DER_UNFL | AMR_DER_OVRN)
+
+	/* Transmit if we can */
+	fill_D_xmit_fifo(info);
+
+	/* Done with the xmit buffer? Notify the midlevel driver. */
+	if (info->D.output_ptr != NULL && info->D.output_count == 0) {
+		info->D.output_ptr = NULL;
+		info->D.output_count = 0;
+		debug_info(info, '+');
+		if (info->D.output_callback)
+			(*info->D.output_callback)
+				(info->D.output_callback_arg,
+				 info->regs->der);
+				 /* info->regs->der & D_XMIT_ERRORS); */
+	}
+
+	/* Read the next byte(s) of incoming data. */
+
+	while (info->regs->dsr2 & AMR_DSR2_RBA) {
+
+		if (info->D.input_ptr &&
+		    (info->D.input_count < info->D.input_limit)) {
+
+			/* Get the next byte and advance buffer pointer. */
 
+			*(info->D.input_ptr) = info->regs->dcrb;
+			info->D.input_ptr++;
+			info->D.input_count++;
+
+		} else {
+
+			/* Overflow - should be detected by chip via RBLR
+			 * so we'll just consume data until we see LBRP
+			 */
+
+			dummy = info->regs->dcrb;
+
+		}
+
+		debug_info(info, '<');
+
+		if (info->regs->dsr2 & AMR_DSR2_LBRP) {
+
+			/* End of recv packet? Notify the midlevel driver. */
+
+			__u8 der;
+
+			debug_info(info, '!');
+
+			info->D.input_ptr = NULL;
+
+			der = info->regs->der & D_RECV_ERRORS;
+
+			/* Read receive byte count - advances FIFOs */
+			info->regs->cr = AMR_DLC_DRCR;
+			dummy = info->regs->dr;
+			dummy = info->regs->dr;
+
+			if (info->D.input_callback)
+				(*info->D.input_callback)
+					(info->D.input_callback_arg, der,
+					 info->D.input_count);
+		}
+
+	}
+}
+
+long amd7930_xmit_idles=0;
+
+static void transceive_Bchannel(struct amd7930_channel *channel,
+	__volatile__ __u8 *io_reg)
+{
 	/* Send the next byte of outgoing data. */
-	if (info->output_ptr && info->output_count > 0) {
+	if (channel->output_ptr && channel->output_count > 0) {
+
 		/* Send the next byte and advance buffer pointer. */
-		regs->bbtb = *(info->output_ptr);
-		info->output_ptr++;
-		info->output_count--;
+		*io_reg = *(channel->output_ptr);
+		channel->output_ptr++;
+		channel->output_count--;
 
 		/* Done with the buffer? Notify the midlevel driver. */
-		if (info->output_count == 0) {
-			info->output_ptr = NULL;
-			info->output_count = 0;
-			sparcaudio_output_done(drv);
+		if (channel->output_count == 0) {
+			channel->output_ptr = NULL;
+			channel->output_count = 0;
+			if (channel->output_callback)
+				(*channel->output_callback)
+					(channel->output_callback_arg);
 		}
-	}
+	} else {
+		*io_reg = channel->xmit_idle_char;
+		amd7930_xmit_idles++;
+        }
 
 	/* Read the next byte of incoming data. */
-	if (info->input_ptr && info->input_count > 0) {
+	if (channel->input_ptr && channel->input_count > 0) {
+
 		/* Get the next byte and advance buffer pointer. */
-		*(info->input_ptr) = regs->bbrb;
-		info->input_ptr++;
-		info->input_count--;
+		*(channel->input_ptr) = *io_reg;
+		channel->input_ptr++;
+		channel->input_count--;
 
 		/* Done with the buffer? Notify the midlevel driver. */
-		if (info->input_count == 0) {
-			info->input_ptr = NULL;
-			info->input_count = 0;
-			sparcaudio_input_done(drv);
+		if (channel->input_count == 0) {
+			channel->input_ptr = NULL;
+			channel->input_count = 0;
+			if (channel->input_callback)
+				(*channel->input_callback)
+					(channel->input_callback_arg);
 		}
 	}
 }
 
-
-static int amd7930_open(struct inode * inode, struct file * file,
-			struct sparcaudio_driver *drv)
+/* Interrupt handler (The chip takes only one byte per interrupt. Grrr!) */
+static void amd7930_interrupt(int irq, void *dev_id, struct pt_regs *intr_regs)
 {
+	struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
 	struct amd7930_info *info = (struct amd7930_info *)drv->private;
-	int level;
+	struct amd7930 *regs = info->regs;
+	__u8 ir;
+	__u8 lsr;
 
-	/* Set the default audio parameters. */
-	info->map.gx = gx_coeff[128];
-	info->map.stgr = gx_coeff[0];
+	/* Clear the interrupt. */
+	ir = regs->ir;
 
-	level = (128 * (256 + NR_GER_COEFFS)) >> 8;
-	if (level >= 256) {
-		info->map.ger = ger_coeff[level-256];
-		info->map.gr = gx_coeff[255];
-	} else {
-		info->map.ger = ger_coeff[0];
-		info->map.gr = gx_coeff[level];
+	if (ir & AMR_IR_BBUF) {
+		if (info->Bb.channel_status == CHANNEL_INUSE)
+			transceive_Bchannel(&info->Bb, &info->regs->bbtb);
+		if (info->Bc.channel_status == CHANNEL_INUSE)
+			transceive_Bchannel(&info->Bc, &info->regs->bctb);
+	}
+
+	if (ir & (AMR_IR_DRTHRSH | AMR_IR_DTTHRSH | AMR_IR_DSRI)) {
+		debug_info(info, 'i');
+		debug_info(info, '0' + (ir&7));
+		transceive_Dchannel(info);
 	}
 
-	info->map.mmr2 |= AM_MAP_MMR2_LS;
+	if (ir & AMR_IR_LSRI) {
+		regs->cr = AMR_LIU_LSR;
+		lsr = regs->dr;
+
+                info->liu_state = (lsr&0x7) + 2;
+
+                if (info->liu_callback)
+			(*info->liu_callback)(info->liu_callback_arg);
+        }
+}
+
+
+static int amd7930_open(struct inode * inode, struct file * file,
+			struct sparcaudio_driver *drv)
+{
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
 
-	amd7930_commit_map(drv);
+	/* Set the default audio parameters. */
+	info->rgain = 128;
+	info->pgain = 200;
+	info->mgain = 0;
+	amd7930_update_map(drv);
 
 	MOD_INC_USE_COUNT;
 
@@ -258,29 +516,87 @@
 static void amd7930_release(struct inode * inode, struct file * file,
 			    struct sparcaudio_driver *drv)
 {
-	amd7930_disable_ints(drv);
+	/* amd7930_disable_ints(drv->private); */
 	MOD_DEC_USE_COUNT;
 }
 
+static void request_Baudio(struct amd7930_info *info)
+{
+	if (info->Bb.channel_status == CHANNEL_AVAILABLE) {
+
+		info->Bb.channel_status = CHANNEL_INUSE;
+		info->Baudio = &info->Bb;
+
+		/* Multiplexor map - audio (Ba) to Bb */
+		info->regs->cr = AMR_MUX_MCR1;
+		info->regs->dr = AM_MUX_CHANNEL_Ba | (AM_MUX_CHANNEL_Bb << 4);
+
+		/* Enable B channel interrupts */
+		info->regs->cr = AMR_MUX_MCR4;
+		info->regs->dr = AM_MUX_MCR4_ENABLE_INTS;
+
+	} else if (info->Bc.channel_status == CHANNEL_AVAILABLE) {
+
+		info->Bc.channel_status = CHANNEL_INUSE;
+		info->Baudio = &info->Bc;
+
+		/* Multiplexor map - audio (Ba) to Bc */
+		info->regs->cr = AMR_MUX_MCR1;
+		info->regs->dr = AM_MUX_CHANNEL_Ba | (AM_MUX_CHANNEL_Bc << 4);
+
+		/* Enable B channel interrupts */
+		info->regs->cr = AMR_MUX_MCR4;
+		info->regs->dr = AM_MUX_MCR4_ENABLE_INTS;
+
+	}
+}
+
+static void release_Baudio(struct amd7930_info *info)
+{
+	if (info->Baudio) {
+		info->Baudio->channel_status = CHANNEL_AVAILABLE;
+		info->regs->cr = AMR_MUX_MCR1;
+		info->regs->dr = 0;
+		info->Baudio = NULL;
+
+		if (info->Bb.channel_status == CHANNEL_AVAILABLE &&
+		    info->Bc.channel_status == CHANNEL_AVAILABLE) {
+
+			/* Disable B channel interrupts */
+			info->regs->cr = AMR_MUX_MCR4;
+			info->regs->dr = 0;
+		}
+	}
+}
+
 static void amd7930_start_output(struct sparcaudio_driver *drv,
 				 __u8 * buffer, unsigned long count)
 {
 	struct amd7930_info *info = (struct amd7930_info *)drv->private;
 
-	info->output_ptr = buffer;
-	info->output_count = count;
-	amd7930_enable_ints(drv);
+	if (! info->Baudio) {
+		request_Baudio(info);
+	}
+
+	if (info->Baudio) {
+		info->Baudio->output_ptr = buffer;
+		info->Baudio->output_count = count;
+        	info->Baudio->output_callback = (void *) &sparcaudio_output_done;
+	        info->Baudio->output_callback_arg = (void *) drv;
+		info->Baudio->xmit_idle_char = 0;
+	}
 }
 
 static void amd7930_stop_output(struct sparcaudio_driver *drv)
 {
 	struct amd7930_info *info = (struct amd7930_info *)drv->private;
 
-	info->output_ptr = NULL;
-	info->output_count = 0;
-
-	if (!info->input_ptr)
-		amd7930_disable_ints(drv);
+	if (info->Baudio) {
+		info->Baudio->output_ptr = NULL;
+		info->Baudio->output_count = 0;
+		if (! info->Baudio->input_ptr)
+			release_Baudio(info);
+	}
 }
 
 static void amd7930_start_input(struct sparcaudio_driver *drv,
@@ -288,20 +604,29 @@
 {
 	struct amd7930_info *info = (struct amd7930_info *)drv->private;
 
-	info->input_ptr = buffer;
-	info->input_count = count;
-	amd7930_enable_ints(drv);
+	if (! info->Baudio) {
+		request_Baudio(info);
+	}
+
+	if (info->Baudio) {
+		info->Baudio->input_ptr = buffer;
+		info->Baudio->input_count = count;
+		info->Baudio->input_callback = (void *) &sparcaudio_input_done;
+		info->Baudio->input_callback_arg = (void *) drv;
+	}
 }
 
 static void amd7930_stop_input(struct sparcaudio_driver *drv)
 {
 	struct amd7930_info *info = (struct amd7930_info *)drv->private;
 
-	info->input_ptr = NULL;
-	info->input_count = 0;
+	if (info->Baudio) {
+		info->Baudio->input_ptr = NULL;
+		info->Baudio->input_count = 0;
+		if (! info->Baudio->output_ptr)
+			release_Baudio(info);
+	}
 
-	if (!info->output_ptr)
-		amd7930_disable_ints(drv);
 }
 
 static void amd7930_sunaudio_getdev(struct sparcaudio_driver *drv,
@@ -312,23 +637,686 @@
 	strncpy(audinfo->config, "audio", sizeof(audinfo->config) - 1);
 }
 
+static int amd7930_sunaudio_getdev_sunos(struct sparcaudio_driver *drv)
+{
+	return AUDIO_DEV_AMD;
+}
+
+static int amd7930_set_output_volume(struct sparcaudio_driver *drv, int vol)
+{
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+	info->pgain = vol;
+	amd7930_update_map(drv);
+	return 0;
+}
+
+static int amd7930_get_output_volume(struct sparcaudio_driver *drv)
+{
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+	return info->pgain;
+}
+
+static int amd7930_set_input_volume(struct sparcaudio_driver *drv, int vol)
+{
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+	info->rgain = vol;
+	amd7930_update_map(drv);
+	return 0;
+}
+
+static int amd7930_get_input_volume(struct sparcaudio_driver *drv)
+{
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+	return info->rgain;
+}
+
+static int amd7930_set_monitor_volume(struct sparcaudio_driver *drv, int vol)
+{
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+	info->mgain = vol;
+	amd7930_update_map(drv);
+	return 0;
+}
+
+/* Cheats. The amd has the minimum capabilities we support */
+static int amd7930_get_output_balance(struct sparcaudio_driver *drv)
+{
+	return AUDIO_MID_BALANCE;
+}
+
+static int amd7930_get_input_balance(struct sparcaudio_driver *drv)
+{
+	return AUDIO_MID_BALANCE;
+}
+
+static int amd7930_get_output_channels(struct sparcaudio_driver *drv)
+{
+	return AUDIO_MIN_PLAY_CHANNELS;
+}
+
+static int amd7930_get_input_channels(struct sparcaudio_driver *drv)
+{
+	return AUDIO_MIN_REC_CHANNELS;
+}
+
+static int amd7930_get_output_precision(struct sparcaudio_driver *drv)
+{
+	return AUDIO_MIN_PLAY_PRECISION;
+}
+
+static int amd7930_get_input_precision(struct sparcaudio_driver *drv)
+{
+	return AUDIO_MIN_REC_PRECISION;
+}
+
+/* This should eventually be made to DTRT, whatever that ends up */
+static int amd7930_get_output_port(struct sparcaudio_driver *drv)
+{
+	return AUDIO_SPEAKER; /* some of these have only HEADPHONE */
+}
+
+/* Only a microphone here, so no troubles */
+static int amd7930_get_input_port(struct sparcaudio_driver *drv)
+{
+	return AUDIO_MICROPHONE;
+}
+
+/* This chip also supports AUDIO_ENCODING_ALAW, add support later */
+static int amd7930_get_output_encoding(struct sparcaudio_driver *drv)
+{
+	return AUDIO_ENCODING_ULAW;
+}
+
+static int amd7930_get_input_encoding(struct sparcaudio_driver *drv)
+{
+	return AUDIO_ENCODING_ULAW;
+}
+
+/* This is what you get. Take it or leave it */
+static int amd7930_get_output_rate(struct sparcaudio_driver *drv)
+{
+	return AMD7930_RATE;
+}
+
+static int amd7930_get_input_rate(struct sparcaudio_driver *drv)
+{
+	return AMD7930_RATE;
+}
+
+static int amd7930_get_output_muted(struct sparcaudio_driver *drv)
+{
+      return 0;
+}
+
+static int amd7930_get_output_ports(struct sparcaudio_driver *drv)
+{
+      return AUDIO_SPEAKER | AUDIO_HEADPHONE;
+}
+
+static int amd7930_get_input_ports(struct sparcaudio_driver *drv)
+{
+      return AUDIO_MICROPHONE;
+}
+
+static int amd7930_get_monitor_volume(struct sparcaudio_driver *drv)
+{
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+	return info->mgain;
+}
+
 
 /*
- *	Device detection and initialization.
+ *       ISDN operations
+ *
+ * Many of these routines take an "int dev" argument, which is simply
+ * an index into the drivers[] array.  Currently, we only support a
+ * single AMD 7930 chip, so the value should always be 0.  B channel
+ * operations require an "int chan", which should be 0 for channel B1
+ * and 1 for channel B2
+ *
+ * int amd7930_get_irqnum(int dev)
+ *
+ *   returns the interrupt number being used by the chip.  ISDN4linux
+ *   uses this number to watch the interrupt during initialization and
+ *   make sure something is happening.
+ *
+ * int amd7930_get_liu_state(int dev)
+ *
+ *   returns the current state of the ISDN Line Interface Unit (LIU)
+ *   as a number between 2 (state F2) and 7 (state F7).  0 may also be
+ *   returned if the chip doesn't exist or the LIU hasn't been
+ *   activated.  The meanings of the states are defined in I.430, ISDN
+ *   BRI Physical Layer Interface.  The most important two states are
+ *   F3 (shutdown) and F7 (syncronized).
+ *
+ * void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg)
+ *
+ *   initializes the LIU and optionally registers a callback to be
+ *   signaled upon a change of LIU state.  The callback will be called
+ *   with a single opaque callback_arg Once the callback has been
+ *   triggered, amd7930_get_liu_state can be used to determine the LIU
+ *   current state.
+ *
+ * void amd7930_liu_activate(int dev, int priority)
+ *
+ *   requests LIU activation at a given D-channel priority.
+ *   Successful activatation is achieved upon entering state F7, which
+ *   will trigger any callback previously registered with
+ *   amd7930_liu_init.
+ *
+ * void amd7930_liu_deactivate(int dev)
+ *
+ *   deactivates LIU.  Outstanding D and B channel transactions are
+ *   terminated rudely and without callback notification.  LIU change
+ *   of state callback will be triggered, however.
+ *
+ * void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count,
+ *               void (*callback)(void *, int), void *callback_arg)
+ *
+ *   transmits a packet - specified with buffer, count - over the D-channel
+ *   interface.  Buffer should begin with the LAPD address field and
+ *   end with the information field.  FCS and flag sequences should not
+ *   be included, nor is bit-stuffing required - all these functions are
+ *   performed by the chip.  The callback function will be called
+ *   DURING THE TOP HALF OF AN INTERRUPT HANDLER and will be passed
+ *   both the arbitrary callback_arg and an integer error indication:
+ *
+ *       0 - successful transmission; ready for next packet
+ *   non-0 - error value from chip's DER (D-Channel Error Register):
+ *       4 - collision detect
+ *     128 - underrun; irq routine didn't service chip fast enough
+ *
+ *   The callback routine should defer any time-consuming operations
+ *   to a bottom-half handler; however, amd7930_dxmit may be called
+ *   from within the callback to request back-to-back transmission of
+ *   a second packet (without repeating the priority/collision mechanism)
+ *
+ *   A comment about the "collision detect" error, which is signalled
+ *   whenever the echoed D-channel data didn't match the transmitted
+ *   data.  This is part of ISDN's normal multi-drop T-interface
+ *   operation, indicating that another device has attempted simultaneous
+ *   transmission, but can also result from line noise.  An immediate
+ *   requeue via amd7930_dxmit is suggested, but repeated collision
+ *   errors may indicate a more serious problem.
+ *
+ * void amd7930_drecv(int dev, __u8 *buffer, unsigned int size,
+ *               void (*callback)(void *, int, unsigned int),
+ *               void *callback_arg)
+ *
+ *   register a buffer - buffer, size - into which a D-channel packet
+ *   can be received.  The callback function will be called DURING
+ *   THE TOP HALF OF AN INTERRUPT HANDLER and will be passed an
+ *   arbitrary callback_arg, an integer error indication and the length
+ *   of the received packet, which will start with the address field,
+ *   end with the information field, and not contain flag or FCS
+ *   bytes.  Bit-stuffing will already have been corrected for.
+ *   Possible values of second callback argument "error":
+ *
+ *       0 - successful reception
+ *   non-0 - error value from chip's DER (D-Channel Error Register):
+ *       1 - recieved packet abort
+ *       2 - framing error; non-integer number of bytes received
+ *       8 - FCS error; CRC sequence indicated corrupted data
+ *      16 - overflow error; packet exceeded size of buffer
+ *      32 - underflow error; packet smaller than required five bytes
+ *      64 - overrun error; irq routine didn't service chip fast enough
+ *
+ * int amd7930_bopen(int dev, int chan, u_char xmit_idle_char)
+ *
+ *   This function should be called before any other operations on a B
+ *   channel.  In addition to arranging for interrupt handling and
+ *   channel multiplexing, it sets the xmit_idle_char which is
+ *   transmitted on the interface when no data buffer is available.
+ *   Suggested values are: 0 for ISDN audio; FF for HDLC mark idle; 7E
+ *   for HDLC flag idle.  Returns 0 on a successful open; -1 on error,
+ *   which is quite possible if audio and the other ISDN channel are
+ *   already in use, since the Am7930 can only send two of the three
+ *   channels to the processor
+ *
+ * void amd7930_bclose(int dev, int chan)
+ *
+ *   Shuts down a B channel when no longer in use.
+ *
+ * void amd7930_bxmit(int dev, int chan, __u8 *buffer, unsigned int count,
+ *               void (*callback)(void *), void *callback_arg)
+ *
+ *   transmits a raw data block - specified with buffer, count - over
+ *   the B channel interface specified by dev/chan.  The callback
+ *   function will be called DURING THE TOP HALF OF AN INTERRUPT
+ *   HANDLER and will be passed the arbitrary callback_arg
+ *
+ *   The callback routine should defer any time-consuming operations
+ *   to a bottom-half handler; however, amd7930_bxmit may be called
+ *   from within the callback to request back-to-back transmission of
+ *   another data block
+ *
+ * void amd7930_brecv(int dev, int chan, __u8 *buffer, unsigned int size,
+ *               void (*callback)(void *), void *callback_arg)
+ *
+ *   receive a raw data block - specified with buffer, size - over the
+ *   B channel interface specified by dev/chan.  The callback function
+ *   will be called DURING THE TOP HALF OF AN INTERRUPT HANDLER and
+ *   will be passed the arbitrary callback_arg
+ *
+ *   The callback routine should defer any time-consuming operations
+ *   to a bottom-half handler; however, amd7930_brecv may be called
+ *   from within the callback to register another buffer and ensure
+ *   continuous B channel reception without loss of data
+ *
  */
 
-static struct sparcaudio_driver drivers[MAX_DRIVERS];
-static int num_drivers;
+
+int amd7930_get_irqnum(int dev)
+{
+	struct amd7930_info *info;
+
+	if (dev > num_drivers) {
+		return(0);
+	}
+
+	info = (struct amd7930_info *) drivers[dev].private;
+
+	return info->irq;
+}
+
+int amd7930_get_liu_state(int dev)
+{
+	struct amd7930_info *info;
+
+	if (dev > num_drivers) {
+		return(0);
+	}
+
+	info = (struct amd7930_info *) drivers[dev].private;
+
+	return info->liu_state;
+}
+
+void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg)
+{
+	struct amd7930_info *info;
+	register unsigned long flags;
+
+	if (dev > num_drivers) {
+		return;
+	}
+
+	info = (struct amd7930_info *) drivers[dev].private;
+
+	save_and_cli(flags);
+
+	/* Set callback for LIU state change */
+        info->liu_callback = callback;
+	info->liu_callback_arg = callback_arg;
+
+	/* De-activate the ISDN Line Interface Unit (LIU) */
+	info->regs->cr = AMR_LIU_LMR1;
+	info->regs->dr = 0;
+
+	/* Request interrupt when LIU changes state from/to F3/F7/F8 */
+	info->regs->cr = AMR_LIU_LMR2;
+	info->regs->dr = AM_LIU_LMR2_EN_F3_INT |
+          AM_LIU_LMR2_EN_F7_INT | AM_LIU_LMR2_EN_F8_INT;
+
+	/* amd7930_enable_ints(info); */
+
+	/* Activate the ISDN Line Interface Unit (LIU) */
+	info->regs->cr = AMR_LIU_LMR1;
+	info->regs->dr = AM_LIU_LMR1_LIU_ENABL;
+
+	restore_flags(flags);
+}
+
+void amd7930_liu_activate(int dev, int priority)
+{
+	struct amd7930_info *info;
+	register unsigned long flags;
+
+	if (dev > num_drivers) {
+		return;
+	}
+
+	info = (struct amd7930_info *) drivers[dev].private;
+
+	save_and_cli(flags);
+
+        /* Set D-channel access priority
+         *
+         * I.430 defines a priority mechanism based on counting 1s
+         * in the echo channel before transmitting
+         *
+         * Priority 0 is eight 1s; priority 1 is ten 1s; etc
+         */
+
+        info->regs->cr = AMR_LIU_LPR;
+        info->regs->dr = priority & 0x0f;
+
+	/* request LIU activation */
+
+	info->regs->cr = AMR_LIU_LMR1;
+	info->regs->dr = AM_LIU_LMR1_LIU_ENABL | AM_LIU_LMR1_REQ_ACTIV;
+
+	restore_flags(flags);
+}
+
+void amd7930_liu_deactivate(int dev)
+{
+	struct amd7930_info *info;
+	register unsigned long flags;
+
+	if (dev > num_drivers) {
+		return;
+	}
+
+	info = (struct amd7930_info *) drivers[dev].private;
+
+	save_and_cli(flags);
+
+	/* deactivate LIU */
+
+	info->regs->cr = AMR_LIU_LMR1;
+	info->regs->dr = 0;
+
+	restore_flags(flags);
+}
+
+void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count,
+		   void (*callback)(void *, int), void *callback_arg)
+{
+	struct amd7930_info *info;
+	register unsigned long flags;
+	__u8 dmr1;
+
+	if (dev > num_drivers) {
+		return;
+	}
+
+	info = (struct amd7930_info *) drivers[dev].private;
+
+	save_and_cli(flags);
+
+	if (info->D.output_ptr) {
+		restore_flags(flags);
+		printk("amd7930_dxmit: transmitter in use\n");
+		return;
+	}
+
+	info->D.output_ptr = buffer;
+	info->D.output_count = count;
+	info->D.output_callback = callback;
+	info->D.output_callback_arg = callback_arg;
+
+	/* Enable D-channel Transmit Threshold interrupt; disable addressing */
+	info->regs->cr = AMR_DLC_DMR1;
+	dmr1 = info->regs->dr;
+	dmr1 |= AMR_DLC_DMR1_DTTHRSH_INT;
+	dmr1 &= ~AMR_DLC_DMR1_EN_ADDRS;
+	info->regs->dr = dmr1;
+
+	/* Begin xmit by setting D-channel Transmit Byte Count Reg (DTCR) */
+	info->regs->cr = AMR_DLC_DTCR;
+	info->regs->dr = count & 0xff;
+	info->regs->dr = (count >> 8) & 0xff;
+
+	/* Prime xmit FIFO */
+	/* fill_D_xmit_fifo(info); */
+	transceive_Dchannel(info);
+
+	restore_flags(flags);
+}
+
+void amd7930_drecv(int dev, __u8 *buffer, unsigned int size,
+		   void (*callback)(void *, int, unsigned int),
+		   void *callback_arg)
+{
+	struct amd7930_info *info;
+	register unsigned long flags;
+	__u8 dmr1;
+
+	if (dev > num_drivers) {
+		return;
+	}
+
+	info = (struct amd7930_info *) drivers[dev].private;
+
+	save_and_cli(flags);
+
+	if (info->D.input_ptr) {
+		restore_flags(flags);
+		printk("amd7930_drecv: receiver already has buffer!\n");
+		return;
+	}
+
+	info->D.input_ptr = buffer;
+	info->D.input_count = 0;
+	info->D.input_limit = size;
+	info->D.input_callback = callback;
+	info->D.input_callback_arg = callback_arg;
+
+	/* Enable D-channel Receive Threshold interrupt;
+	 * Enable D-channel End of Receive Packet interrupt;
+	 * Disable address recognition
+	 */
+	info->regs->cr = AMR_DLC_DMR1;
+	dmr1 = info->regs->dr;
+	dmr1 |= AMR_DLC_DMR1_DRTHRSH_INT | AMR_DLC_DMR1_EORP_INT;
+	dmr1 &= ~AMR_DLC_DMR1_EN_ADDRS;
+	info->regs->dr = dmr1;
+
+	/* Set D-channel Receive Byte Count Limit Register */
+	info->regs->cr = AMR_DLC_DRCR;
+	info->regs->dr = size & 0xff;
+	info->regs->dr = (size >> 8) & 0xff;
+
+	restore_flags(flags);
+}
+
+int amd7930_bopen(int dev, int chan, u_char xmit_idle_char)
+{
+	struct amd7930_info *info;
+	register unsigned long flags;
+
+	if (dev > num_drivers || chan<0 || chan>1) {
+		return -1;
+	}
+
+	info = (struct amd7930_info *) drivers[dev].private;
+
+	save_and_cli(flags);
+
+	if (info->Bb.channel_status == CHANNEL_AVAILABLE) {
+
+		info->Bb.channel_status = CHANNEL_INUSE;
+		info->Bb.xmit_idle_char = xmit_idle_char;
+		info->Bisdn[chan] = &info->Bb;
+
+		/* Multiplexor map - isdn (B1/2) to Bb */
+		info->regs->cr = AMR_MUX_MCR2 + chan;
+		info->regs->dr = (AM_MUX_CHANNEL_B1 + chan) |
+				 (AM_MUX_CHANNEL_Bb << 4);
+
+	} else if (info->Bc.channel_status == CHANNEL_AVAILABLE) {
+
+		info->Bc.channel_status = CHANNEL_INUSE;
+		info->Bc.xmit_idle_char = xmit_idle_char;
+		info->Bisdn[chan] = &info->Bc;
+
+		/* Multiplexor map - isdn (B1/2) to Bc */
+		info->regs->cr = AMR_MUX_MCR2 + chan;
+		info->regs->dr = (AM_MUX_CHANNEL_B1 + chan) |
+				 (AM_MUX_CHANNEL_Bc << 4);
+
+	} else {
+		restore_flags(flags);
+		return (-1);
+	}
+
+	/* Enable B channel transmit */
+	info->regs->cr = AMR_LIU_LMR1;
+	info->regs->dr |= AM_LIU_LMR1_B1_ENABL + chan;
+
+	/* Enable B channel interrupts */
+	info->regs->cr = AMR_MUX_MCR4;
+	info->regs->dr = AM_MUX_MCR4_ENABLE_INTS | AM_MUX_MCR4_REVERSE_Bb | AM_MUX_MCR4_REVERSE_Bc;
+
+	restore_flags(flags);
+	return 0;
+}
+
+void amd7930_bclose(int dev, int chan)
+{
+	struct amd7930_info *info;
+	register unsigned long flags;
+
+	if (dev > num_drivers || chan<0 || chan>1) {
+		return;
+	}
+
+	info = (struct amd7930_info *) drivers[dev].private;
+
+	save_and_cli(flags);
+
+	if (info->Bisdn[chan]) {
+		info->Bisdn[chan]->channel_status = CHANNEL_AVAILABLE;
+		info->regs->cr = AMR_MUX_MCR2 + chan;
+		info->regs->dr = 0;
+		info->Bisdn[chan] = NULL;
+
+		/* Disable B channel transmit */
+		info->regs->cr = AMR_LIU_LMR1;
+		info->regs->dr &= ~(AM_LIU_LMR1_B1_ENABL + chan);
+
+		if (info->Bb.channel_status == CHANNEL_AVAILABLE &&
+		    info->Bc.channel_status == CHANNEL_AVAILABLE) {
+
+			/* Disable B channel interrupts */
+			info->regs->cr = AMR_MUX_MCR4;
+			info->regs->dr = 0;
+		}
+	}
+
+	restore_flags(flags);
+}
+
+void amd7930_bxmit(int dev, int chan, __u8 * buffer, unsigned long count,
+		   void (*callback)(void *), void *callback_arg)
+{
+	struct amd7930_info *info;
+	struct amd7930_channel *Bchan;
+	register unsigned long flags;
+
+	if (dev > num_drivers) {
+		return;
+	}
+
+	info = (struct amd7930_info *) drivers[dev].private;
+	Bchan = info->Bisdn[chan];
+
+	if (Bchan) {
+		save_and_cli(flags);
+
+		Bchan->output_ptr = buffer;
+		Bchan->output_count = count;
+	        Bchan->output_callback = (void *) callback;
+        	Bchan->output_callback_arg = callback_arg;
+
+		restore_flags(flags);
+	}
+}
+
+void amd7930_brecv(int dev, int chan, __u8 * buffer, unsigned long size,
+		   void (*callback)(void *), void *callback_arg)
+{
+	struct amd7930_info *info;
+	struct amd7930_channel *Bchan;
+	register unsigned long flags;
+
+	if (dev > num_drivers) {
+		return;
+	}
+
+	info = (struct amd7930_info *) drivers[dev].private;
+	Bchan = info->Bisdn[chan];
+
+	if (Bchan) {
+		save_and_cli(flags);
+
+		Bchan->input_ptr = buffer;
+		Bchan->input_count = size;
+		Bchan->input_callback = (void *) callback;
+		Bchan->input_callback_arg = callback_arg;
+
+		restore_flags(flags);
+	}
+}
+
+EXPORT_SYMBOL(amd7930_get_irqnum);
+EXPORT_SYMBOL(amd7930_get_liu_state);
+EXPORT_SYMBOL(amd7930_liu_init);
+EXPORT_SYMBOL(amd7930_liu_activate);
+EXPORT_SYMBOL(amd7930_liu_deactivate);
+EXPORT_SYMBOL(amd7930_dxmit);
+EXPORT_SYMBOL(amd7930_drecv);
+EXPORT_SYMBOL(amd7930_bopen);
+EXPORT_SYMBOL(amd7930_bclose);
+EXPORT_SYMBOL(amd7930_bxmit);
+EXPORT_SYMBOL(amd7930_brecv);
+
+
+/*
+ *	Device detection and initialization.
+ */
 
 static struct sparcaudio_operations amd7930_ops = {
 	amd7930_open,
 	amd7930_release,
-	NULL,			/* amd7930_ioctl */
+	NULL,				/* amd7930_ioctl */
 	amd7930_start_output,
 	amd7930_stop_output,
 	amd7930_start_input,
 	amd7930_stop_input,
 	amd7930_sunaudio_getdev,
+	amd7930_set_output_volume,
+	amd7930_get_output_volume,
+	amd7930_set_input_volume,
+	amd7930_get_input_volume,
+	amd7930_set_monitor_volume,
+	amd7930_get_monitor_volume,
+	NULL,			/* amd7930_set_output_balance */
+	amd7930_get_output_balance,
+	NULL,			/* amd7930_set_input_balance */
+	amd7930_get_input_balance,
+	NULL,			/* amd7930_set_output_channels */
+	amd7930_get_output_channels,
+	NULL,			/* amd7930_set_input_channels */
+	amd7930_get_input_channels,
+	NULL,			/* amd7930_set_output_precision */
+	amd7930_get_output_precision,
+	NULL,			/* amd7930_set_input_precision */
+	amd7930_get_input_precision,
+	NULL,			/* amd7930_set_output_port */
+	amd7930_get_output_port,
+	NULL,			/* amd7930_set_input_port */
+	amd7930_get_input_port,
+	NULL,			/* amd7930_set_output_encoding */
+	amd7930_get_output_encoding,
+	NULL,			/* amd7930_set_input_encoding */
+	amd7930_get_input_encoding,
+	NULL,			/* amd7930_set_output_rate */
+	amd7930_get_output_rate,
+	NULL,			/* amd7930_set_input_rate */
+	amd7930_get_input_rate,
+	amd7930_sunaudio_getdev_sunos,
+	amd7930_get_output_ports,
+	amd7930_get_input_ports,
+	NULL,			/* amd7930_set_output_muted */
+	amd7930_get_output_muted,
 };
 
 /* Attach to an amd7930 chip given its PROM node. */
@@ -348,8 +1336,10 @@
 	/* Point at the information structure and initialize it. */
 	drv->ops = &amd7930_ops;
 	info = (struct amd7930_info *)drv->private;
-	info->output_ptr = info->input_ptr = NULL;
-	info->output_count = info->input_count = 0;
+	info->Bb.output_ptr = info->Bb.input_ptr = NULL;
+	info->Bb.output_count = info->Bb.input_count = 0;
+	info->Bc.output_ptr = info->Bc.input_ptr = NULL;
+	info->Bc.output_count = info->Bc.input_count = 0;
 	info->ints_on = 1; /* force disable below */
 
 	/* Map the registers into memory. */
@@ -365,15 +1355,14 @@
 		return -EIO;
 	}
 
-	/* Disable amd7930 interrupt generation. */
-	amd7930_disable_ints(drv);
+	/* Put amd7930 in idle mode (interrupts disabled) */
+	amd7930_idle(info);
 
-	/* Initialize the MUX unit to connect the MAP to the CPU. */
-	info->regs->cr = AMR_MUX_1_4;
-	info->regs->dr = (AM_MUX_CHANNEL_Bb << 4) | AM_MUX_CHANNEL_Ba;
-	info->regs->dr = 0;
-	info->regs->dr = 0;
-	info->regs->dr = AM_MUX_MCR4_ENABLE_INTS;
+	/* Enable extended FIFO operation on D-channel */
+	info->regs->cr = AMR_DLC_EFCR;
+	info->regs->dr = AMR_DLC_EFCR_EXTEND_FIFO;
+	info->regs->cr = AMR_DLC_DMR4;
+	info->regs->dr = /* AMR_DLC_DMR4_RCV_30 | */ AMR_DLC_DMR4_XMT_14;
 
 	/* Attach the interrupt handler to the audio interrupt. */
 	prom_getproperty(node, "intr", (char *)&irq, sizeof(irq));
@@ -381,6 +1370,7 @@
 	request_irq(info->irq, amd7930_interrupt,
 		    SA_INTERRUPT, "amd7930", drv);
 	enable_irq(info->irq);
+	amd7930_enable_ints(info);
 
 	/* Initalize the local copy of the MAP registers. */
 	memset(&info->map, 0, sizeof(info->map));
@@ -413,7 +1403,7 @@
 	struct amd7930_info *info = (struct amd7930_info *)drv->private;
 
 	unregister_sparcaudio_driver(drv);
-	amd7930_disable_ints(drv);
+	amd7930_idle(info);
 	disable_irq(info->irq);
 	free_irq(info->irq, drv);
 	sparc_free_io(info->regs, info->regs_size);
@@ -421,7 +1411,6 @@
 }
 #endif
 
-
 /* Probe for the amd7930 chip and then attach the driver. */
 #ifdef MODULE
 int init_module(void)
@@ -432,12 +1421,6 @@
 	struct linux_sbus *bus;
 	struct linux_sbus_device *sdev;
 	int node;
-
-#if 0
-#ifdef MODULE
-	register_symtab(0);
-#endif
-#endif
 
 	/* Try to find the sun4c "audio" node first. */
 	node = prom_getchild(prom_root_node);
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/audio/amd7930.h linux/drivers/sbus/audio/amd7930.h
--- v2.1.78/linux/drivers/sbus/audio/amd7930.h	Mon Mar 17 14:54:27 1997
+++ linux/drivers/sbus/audio/amd7930.h	Mon Jan 12 15:15:45 1998
@@ -15,6 +15,26 @@
 
 #include <linux/types.h>
 
+/* Exported ISDN functions */
+
+int amd7930_get_irqnum(int dev);
+int amd7930_get_liu_state(int dev);
+void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg);
+void amd7930_liu_activate(int dev, int priority);
+void amd7930_liu_deactivate(int dev);
+void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count,
+		   void (*callback)(void *, int), void *callback_arg);
+void amd7930_drecv(int dev, __u8 *buffer, unsigned int size,
+		   void (*callback)(void *, int, unsigned int),
+		   void *callback_arg);
+int amd7930_bopen(int dev, int chan, u_char xmit_idle_char);
+void amd7930_bclose(int dev, int chan);
+void amd7930_bxmit(int dev, int chan, __u8 * buffer, unsigned long count,
+		   void (*callback)(void *), void *callback_arg);
+void amd7930_brecv(int dev, int chan, __u8 * buffer, unsigned long size,
+		   void (*callback)(void *), void *callback_arg);
+
+
 /* Register interface presented to the CPU by the amd7930. */
 struct amd7930
 {
@@ -48,6 +68,20 @@
 };
 
 
+/* After an amd7930 interrupt, reading the Interrupt Register (ir)
+ * clears the interrupt and returns a bitmask indicated which
+ * interrupt source(s) require service
+ */
+
+#define AMR_IR_DTTHRSH			0x01 /* D-channel xmit threshold */
+#define AMR_IR_DRTHRSH			0x02 /* D-channel recv threshold */
+#define AMR_IR_DSRI			0x04 /* D-channel packet status */
+#define AMR_IR_DERI			0x08 /* D-channel error */
+#define AMR_IR_BBUF			0x10 /* B-channel data xfer */
+#define AMR_IR_LSRI			0x20 /* LIU status */
+#define AMR_IR_DSR2I			0x40 /* D-channel buffer status */
+#define AMR_IR_MLTFRMI			0x80 /* multiframe or PP */
+
 /* The amd7930 has "indirect registers" which are accessed by writing
  * the register number into the Command Register and then reading or
  * writing values from the Data Register as appropriate. We define the
@@ -67,9 +101,29 @@
 
 /* Line Interface Unit */
 #define	AMR_LIU_LSR			0xA1
+#define		AM_LIU_LSR_STATE		0x07
+#define		AM_LIU_LSR_F3			0x08
+#define		AM_LIU_LSR_F7			0x10
+#define		AM_LIU_LSR_F8			0x20
+#define		AM_LIU_LSR_HSW			0x40
+#define		AM_LIU_LSR_HSW_CHG		0x80
 #define	AMR_LIU_LPR			0xA2
 #define	AMR_LIU_LMR1			0xA3
+#define		AM_LIU_LMR1_B1_ENABL		0x01
+#define		AM_LIU_LMR1_B2_ENABL		0x02
+#define		AM_LIU_LMR1_F_DISABL		0x04
+#define		AM_LIU_LMR1_FA_DISABL		0x08
+#define		AM_LIU_LMR1_REQ_ACTIV		0x10
+#define		AM_LIU_LMR1_F8_F3		0x20
+#define		AM_LIU_LMR1_LIU_ENABL		0x40
 #define	AMR_LIU_LMR2			0xA4
+#define		AM_LIU_LMR2_DECHO		0x01
+#define		AM_LIU_LMR2_DLOOP		0x02
+#define		AM_LIU_LMR2_DBACKOFF		0x04
+#define		AM_LIU_LMR2_EN_F3_INT		0x08
+#define		AM_LIU_LMR2_EN_F8_INT		0x10
+#define		AM_LIU_LMR2_EN_HSW_INT		0x20
+#define		AM_LIU_LMR2_EN_F7_INT		0x40
 #define	AMR_LIU_2_4			0xA5
 #define	AMR_LIU_MF			0xA6
 #define	AMR_LIU_MFSB			0xA7
@@ -134,7 +188,24 @@
 #define	AMR_DLC_DRLR			0x84
 #define	AMR_DLC_DTCR			0x85
 #define	AMR_DLC_DMR1			0x86
+#define		AMR_DLC_DMR1_DTTHRSH_INT	0x01
+#define		AMR_DLC_DMR1_DRTHRSH_INT	0x02
+#define		AMR_DLC_DMR1_TAR_ENABL		0x04
+#define		AMR_DLC_DMR1_EORP_INT		0x08
+#define		AMR_DLC_DMR1_EN_ADDR1		0x10
+#define		AMR_DLC_DMR1_EN_ADDR2		0x20
+#define		AMR_DLC_DMR1_EN_ADDR3		0x40
+#define		AMR_DLC_DMR1_EN_ADDR4		0x80
+#define		AMR_DLC_DMR1_EN_ADDRS		0xf0
 #define	AMR_DLC_DMR2			0x87
+#define		AMR_DLC_DMR2_RABRT_INT		0x01
+#define		AMR_DLC_DMR2_RESID_INT		0x02
+#define		AMR_DLC_DMR2_COLL_INT		0x04
+#define		AMR_DLC_DMR2_FCS_INT		0x08
+#define		AMR_DLC_DMR2_OVFL_INT		0x10
+#define		AMR_DLC_DMR2_UNFL_INT		0x20
+#define		AMR_DLC_DMR2_OVRN_INT		0x40
+#define		AMR_DLC_DMR2_UNRN_INT		0x80
 #define	AMR_DLC_1_7			0x88
 #define	AMR_DLC_DRCR			0x89
 #define	AMR_DLC_RNGR1			0x8A
@@ -142,10 +213,66 @@
 #define	AMR_DLC_FRAR4			0x8C
 #define	AMR_DLC_SRAR4			0x8D
 #define	AMR_DLC_DMR3			0x8E
+#define		AMR_DLC_DMR3_VA_INT		0x01
+#define		AMR_DLC_DMR3_EOTP_INT		0x02
+#define		AMR_DLC_DMR3_LBRP_INT		0x04
+#define		AMR_DLC_DMR3_RBA_INT		0x08
+#define		AMR_DLC_DMR3_LBT_INT		0x10
+#define		AMR_DLC_DMR3_TBE_INT		0x20
+#define		AMR_DLC_DMR3_RPLOST_INT		0x40
+#define		AMR_DLC_DMR3_KEEP_FCS		0x80
 #define	AMR_DLC_DMR4			0x8F
+#define		AMR_DLC_DMR4_RCV_1		0x00
+#define		AMR_DLC_DMR4_RCV_2		0x01
+#define		AMR_DLC_DMR4_RCV_4		0x02
+#define		AMR_DLC_DMR4_RCV_8		0x03
+#define		AMR_DLC_DMR4_RCV_16		0x01
+#define		AMR_DLC_DMR4_RCV_24		0x02
+#define		AMR_DLC_DMR4_RCV_30		0x03
+#define		AMR_DLC_DMR4_XMT_1		0x00
+#define		AMR_DLC_DMR4_XMT_2		0x04
+#define		AMR_DLC_DMR4_XMT_4		0x08
+#define		AMR_DLC_DMR4_XMT_8		0x0c
+#define		AMR_DLC_DMR4_XMT_10		0x08
+#define		AMR_DLC_DMR4_XMT_14		0x0c
+#define		AMR_DLC_DMR4_IDLE_MARK		0x00
+#define		AMR_DLC_DMR4_IDLE_FLAG		0x10
+#define		AMR_DLC_DMR4_ADDR_BOTH		0x00
+#define		AMR_DLC_DMR4_ADDR_1ST		0x20
+#define		AMR_DLC_DMR4_ADDR_2ND		0xa0
+#define		AMR_DLC_DMR4_CR_ENABLE		0x40
 #define	AMR_DLC_12_15			0x90
 #define	AMR_DLC_ASR			0x91
 #define	AMR_DLC_EFCR			0x92
+#define		AMR_DLC_EFCR_EXTEND_FIFO	0x01
+#define		AMR_DLC_EFCR_SEC_PKT_INT	0x02
+
+#define AMR_DSR1_VADDR			0x01
+#define AMR_DSR1_EORP			0x02
+#define AMR_DSR1_PKT_IP			0x04
+#define AMR_DSR1_DECHO_ON		0x08
+#define AMR_DSR1_DLOOP_ON		0x10
+#define AMR_DSR1_DBACK_OFF		0x20
+#define AMR_DSR1_EOTP			0x40
+#define AMR_DSR1_CXMT_ABRT		0x80
+
+#define AMR_DSR2_LBRP			0x01
+#define AMR_DSR2_RBA			0x02
+#define AMR_DSR2_RPLOST			0x04
+#define AMR_DSR2_LAST_BYTE		0x08
+#define AMR_DSR2_TBE			0x10
+#define AMR_DSR2_MARK_IDLE		0x20
+#define AMR_DSR2_FLAG_IDLE		0x40
+#define AMR_DSR2_SECOND_PKT		0x80
+
+#define AMR_DER_RABRT			0x01
+#define AMR_DER_RFRAME			0x02
+#define AMR_DER_COLLISION		0x04
+#define AMR_DER_FCS			0x08
+#define AMR_DER_OVFL			0x10
+#define AMR_DER_UNFL			0x20
+#define AMR_DER_OVRN			0x40
+#define AMR_DER_UNRN			0x80
 
 /* Peripheral Port */
 #define	AMR_PP_PPCR1			0xC0
@@ -160,4 +287,6 @@
 #define	AMR_PP_PPCR2			0xC8
 #define	AMR_PP_PPCR3			0xC9
 
+/* Give this chip a "default" sample rate */
+#define AMD7930_RATE                    (8000)
 #endif
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/audio/audio.c linux/drivers/sbus/audio/audio.c
--- v2.1.78/linux/drivers/sbus/audio/audio.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/sbus/audio/audio.c	Mon Jan 12 15:15:45 1998
@@ -1,7 +1,7 @@
 /*
  * drivers/sbus/audio/audio.c
  *
- * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
+ * Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu)
  *
  * This is the audio midlayer that sits between the VFS character
  * devices and the low-level audio hardware device drivers.
@@ -19,6 +19,8 @@
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/soundcard.h>
+#include <asm/uaccess.h>
 
 #include "audio.h"
 
@@ -42,33 +44,47 @@
 	if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output)
 		return -EINVAL;
 
-	/* Setup the circular queue of output buffers. */
+	/* Setup the circular queues of output and input buffers
+	 *
+	 * Each buffer is a single page, but output buffers might
+	 * be partially filled (by a write with count < PAGE_SIZE),
+	 * so each output buffer also has a paired output size.
+	 *
+	 * Input buffers, on the other hand, always fill completely,
+	 * so we don't need input counts - each contains PAGE_SIZE
+	 * bytes of audio data.
+	 *
+	 * TODO: Make number of input/output buffers tunable parameters
+	 */
+
 	drv->num_output_buffers = 32;
 	drv->output_front = 0;
 	drv->output_rear = 0;
 	drv->output_count = 0;
 	drv->output_active = 0;
-	drv->output_buffers = kmalloc(32 * sizeof(__u8 *), GFP_KERNEL);
-	drv->output_sizes = kmalloc(32 * sizeof(size_t), GFP_KERNEL);
-	if (!drv->output_buffers || !drv->output_sizes) {
-		if (drv->output_buffers)
-			kfree(drv->output_buffers);
-		if (drv->output_sizes)
-			kfree(drv->output_sizes);
-		return -ENOMEM;
-	}
+	drv->output_buffers = kmalloc(drv->num_output_buffers * sizeof(__u8 *), GFP_KERNEL);
+	drv->output_sizes = kmalloc(drv->num_output_buffers * sizeof(size_t), GFP_KERNEL);
+	if (!drv->output_buffers || !drv->output_sizes) goto kmalloc_failed1;
 
 	/* Allocate the pages for each output buffer. */
 	for (i = 0; i < drv->num_output_buffers; i++) {
 		drv->output_buffers[i] = (void *) __get_free_page(GFP_KERNEL);
-		if (!drv->output_buffers[i]) {
-			int j;
-			for (j = 0; j < i; j++)
-				free_page((unsigned long) drv->output_buffers[j]);
-			kfree(drv->output_buffers);
-			kfree(drv->output_sizes);
-			return -ENOMEM;
-		}
+		if (!drv->output_buffers[i]) goto kmalloc_failed2;
+	}
+
+	/* Setup the circular queue of input buffers. */
+	drv->num_input_buffers = 32;
+	drv->input_front = 0;
+	drv->input_rear = 0;
+	drv->input_count = 0;
+	drv->input_active = 0;
+	drv->input_buffers = kmalloc(drv->num_input_buffers * sizeof(__u8 *), GFP_KERNEL);
+	if (!drv->input_buffers) goto kmalloc_failed3;
+
+	/* Allocate the pages for each input buffer. */
+	for (i = 0; i < drv->num_input_buffers; i++) {
+		drv->input_buffers[i] = (void *) __get_free_page(GFP_KERNEL);
+		if (!drv->input_buffers[i]) goto kmalloc_failed4;
 	}
 
 	/* Ensure that the driver is marked as not being open. */
@@ -78,6 +94,28 @@
 
 	driver = drv;
 	return 0;
+
+
+kmalloc_failed4:
+	for (i--; i >= 0; i--)
+		free_page((unsigned long) drv->input_buffers[i]);
+
+kmalloc_failed3:
+	if (drv->input_buffers)
+		kfree(drv->input_buffers);
+	i = drv->num_output_buffers;
+
+kmalloc_failed2:
+	for (i--; i >= 0; i--)
+		free_page((unsigned long) drv->output_buffers[i]);
+
+kmalloc_failed1:
+	if (drv->output_buffers)
+		kfree(drv->output_buffers);
+	if (drv->output_sizes)
+		kfree(drv->output_sizes);
+
+	return -ENOMEM;
 }
 
 int unregister_sparcaudio_driver(struct sparcaudio_driver *drv)
@@ -94,6 +132,11 @@
 	kfree(driver->output_buffers);
 	kfree(driver->output_sizes);
 
+        /* Deallocate the queue of input buffers. */
+        for (i = 0; i < driver->num_input_buffers; i++)
+                free_page((unsigned long) driver->input_buffers[i]);
+        kfree(driver->input_buffers);
+
 	MOD_DEC_USE_COUNT;
 
 	driver = NULL;
@@ -116,6 +159,7 @@
 void sparcaudio_output_done(struct sparcaudio_driver * drv)
 {
 	/* Point the queue after the "done" buffer. */
+	drv->output_size -= drv->output_sizes[drv->output_front];
 	drv->output_front = (drv->output_front + 1) % drv->num_output_buffers;
 	drv->output_count--;
 
@@ -146,7 +190,23 @@
 
 void sparcaudio_input_done(struct sparcaudio_driver * drv)
 {
-	/* XXX Implement! */
+	/* Point the queue after the "done" buffer. */
+	drv->input_front = (drv->input_front + 1) % drv->num_input_buffers;
+	drv->input_count++;
+
+	/* If the input queue is full, shutdown the driver. */
+	if (drv->input_count == drv->num_input_buffers) {
+		/* Stop the lowlevel driver from inputing. */
+		drv->ops->stop_input(drv);
+		drv->input_active = 0;
+	} else {
+		/* Otherwise, give the driver the next buffer. */
+		drv->ops->start_input(drv, drv->input_buffers[drv->input_front],
+				      PAGE_SIZE);
+	}
+
+	/* Wake up any tasks that are waiting. */
+	wake_up_interruptible(&drv->input_read_wait);
 }
 
 
@@ -155,34 +215,85 @@
  *	VFS layer interface
  */
 
-static int sparcaudio_lseek(struct inode * inode, struct file * file,
-			    off_t offset, int origin)
+static loff_t sparcaudio_llseek(struct file * file, loff_t offset, int origin)
 {
 	return -ESPIPE;
 }
 
-static int sparcaudio_read(struct inode * inode, struct file * file,
-			   char *buf, int count)
+static ssize_t sparcaudio_read(struct file * file,
+			       char *buf, size_t count, loff_t *ppos)
+{
+	int bytes_to_copy;
+
+	if (! file->f_mode & FMODE_READ)
+		return -EINVAL;
+
+	if (driver->input_count == 0) {
+		interruptible_sleep_on(&driver->input_read_wait);
+		if (signal_pending(current))
+			return -EINTR;
+	}
+
+	bytes_to_copy = PAGE_SIZE - driver->input_offset;
+	if (bytes_to_copy > count)
+		bytes_to_copy = count;
+
+	copy_to_user_ret(buf, driver->input_buffers[driver->input_rear]+driver->input_offset,
+			 bytes_to_copy, -EFAULT);
+	driver->input_offset += bytes_to_copy;
+
+	if (driver->input_offset >= PAGE_SIZE) {
+		driver->input_rear = (driver->input_rear + 1) % driver->num_input_buffers;
+		driver->input_count--;
+		driver->input_offset = 0;
+	}
+
+	return bytes_to_copy;
+}
+
+static void sparcaudio_reorganize_buffers(struct sparcaudio_driver * driver)
 {
-	/* XXX Implement me! */
-	return -EINVAL;
+  /* It may never matter but if it does this routine will pack */
+  /* buffers to free space for more data */
 }
 
-static int sparcaudio_write(struct inode * inode, struct file * file,
-			    const char *buf, int count)
+static void sparcaudio_sync_output(struct sparcaudio_driver * driver)
 {
 	unsigned long flags;
-	int bytes_written = 0, bytes_to_copy, err;
+
+	/* If the low-level driver is not active, activate it. */
+	save_and_cli(flags);
+	if (! driver->output_active) {
+		driver->ops->start_output(driver,
+				driver->output_buffers[driver->output_front],
+				driver->output_sizes[driver->output_front]);
+		driver->output_active = 1;
+	}
+	restore_flags(flags);
+}
+
+static ssize_t sparcaudio_write(struct file * file, const char *buf,
+				size_t count, loff_t *ppos)
+{
+	int bytes_written = 0, bytes_to_copy;
 
 	/* Ensure that we have something to write. */
-	if (count < 1)
+	if (count < 1) {
+		sparcaudio_sync_output(driver);
 		return 0;
+	}
 
 	/* Loop until all output is written to device. */
 	while (count > 0) {
 		/* Check to make sure that an output buffer is available. */
+		/* If not, make valiant attempt */
+		if (driver->output_count == driver->num_output_buffers)
+			sparcaudio_reorganize_buffers(driver);
+
 		if (driver->output_count == driver->num_output_buffers) {
-			interruptible_sleep_on(&driver->output_write_wait);
+ 			/* We need buffers, so... */
+			sparcaudio_sync_output(driver);
+ 			interruptible_sleep_on(&driver->output_write_wait);
 			if (signal_pending(current))
 				return bytes_written > 0 ? bytes_written : -EINTR;
 		}
@@ -202,15 +313,11 @@
 		driver->output_sizes[driver->output_rear] = bytes_to_copy;
 		driver->output_rear = (driver->output_rear + 1) % driver->num_output_buffers;
 		driver->output_count++;
+		driver->output_size += bytes_to_copy;
 
-		/* If the low-level driver is not active, activate it. */
-		save_and_cli(flags);
-		if (! driver->output_active) {
-			driver->ops->start_output(driver, driver->output_buffers[driver->output_front],
-						  driver->output_sizes[driver->output_front]);
-			driver->output_active = 1;
-		}
-		restore_flags(flags);
+		/* Activate the driver if more than page of data is waiting. */
+		if (driver->output_size > 4096)
+			sparcaudio_sync_output(driver);
 	}
 
 	/* Return the number of bytes written to the caller. */
@@ -221,8 +328,10 @@
 			    unsigned int cmd, unsigned long arg)
 {
 	int retval = 0;
+	struct audio_info ainfo;
 
 	switch (cmd) {
+	case SNDCTL_DSP_SYNC:
 	case AUDIO_DRAIN:
 		if (driver->output_count > 0) {
 			interruptible_sleep_on(&driver->output_drain_wait);
@@ -239,18 +348,381 @@
 			copy_to_user_ret((audio_device_t *)arg, &tmp, sizeof(tmp), -EFAULT);
 		} else
 			retval = -EINVAL;
+
+		printk(KERN_INFO "sparcaudio_ioctl: AUDIO_GETDEV\n");
+		break;
+
+	case AUDIO_GETDEV_SUNOS:
+		if (driver->ops->sunaudio_getdev_sunos) {
+			int tmp=driver->ops->sunaudio_getdev_sunos(driver);
+
+			copy_to_user_ret((int *)arg, &tmp, sizeof(tmp), -EFAULT);
+		} else
+			retval = -EINVAL;
+
+		printk(KERN_INFO "sparcaudio_ioctl: AUDIO_GETDEV_SUNOS\n");
+		break;
+
+	case AUDIO_GETINFO:
+
+		AUDIO_INITINFO(&ainfo);
+
+		if (driver->ops->get_input_rate)
+		  ainfo.record.sample_rate =
+		    driver->ops->get_input_rate(driver);
+		if (driver->ops->get_input_channels)
+		  ainfo.record.channels =
+		    driver->ops->get_input_channels(driver);
+		if (driver->ops->get_input_precision)
+		  ainfo.record.precision =
+		    driver->ops->get_input_precision(driver);
+		if (driver->ops->get_input_encoding)
+		  ainfo.record.encoding =
+		    driver->ops->get_input_encoding(driver);
+		if (driver->ops->get_input_volume)
+		  ainfo.record.gain =
+		    driver->ops->get_input_volume(driver);
+		if (driver->ops->get_input_port)
+		  ainfo.record.port =
+		    driver->ops->get_input_port(driver);
+		if (driver->ops->get_input_ports)
+		  ainfo.record.avail_ports =
+		    driver->ops->get_input_ports(driver);
+		ainfo.record.buffer_size = PAGE_SIZE;
+		ainfo.record.samples = 0;
+		ainfo.record.eof = 0;
+		ainfo.record.pause = 0;
+		ainfo.record.error = 0;
+		ainfo.record.waiting = 0;
+		if (driver->ops->get_input_balance)
+		  ainfo.record.balance =
+		    driver->ops->get_input_balance(driver);
+		ainfo.record.minordev = 4;
+		ainfo.record.open = 1;
+		ainfo.record.active = 0;
+
+		if (driver->ops->get_output_rate)
+		  ainfo.play.sample_rate =
+		    driver->ops->get_output_rate(driver);
+		if (driver->ops->get_output_channels)
+		  ainfo.play.channels =
+		    driver->ops->get_output_channels(driver);
+		if (driver->ops->get_output_precision)
+		  ainfo.play.precision =
+		    driver->ops->get_output_precision(driver);
+		if (driver->ops->get_output_encoding)
+		  ainfo.play.encoding =
+		    driver->ops->get_output_encoding(driver);
+		if (driver->ops->get_output_volume)
+		  ainfo.play.gain =
+		    driver->ops->get_output_volume(driver);
+		if (driver->ops->get_output_port)
+		  ainfo.play.port =
+		    driver->ops->get_output_port(driver);
+		if (driver->ops->get_output_ports)
+		  ainfo.play.avail_ports =
+		    driver->ops->get_output_ports(driver);
+		ainfo.play.buffer_size = PAGE_SIZE;
+		ainfo.play.samples = 0;
+		ainfo.play.eof = 0;
+		ainfo.play.pause = 0;
+		ainfo.play.error = 0;
+		ainfo.play.waiting = waitqueue_active(&driver->open_wait);
+		if (driver->ops->get_output_balance)
+		  ainfo.play.balance =
+		    driver->ops->get_output_balance(driver);
+		ainfo.play.minordev = 4;
+		ainfo.play.open = 1;
+		ainfo.play.active = driver->output_active;
+
+		if (driver->ops->get_monitor_volume)
+		  ainfo.monitor_gain =
+		    driver->ops->get_monitor_volume(driver);
+
+		if (driver->ops->get_output_muted)
+		  ainfo.output_muted =
+		    driver->ops->get_output_muted(driver);
+
+		printk("sparcaudio_ioctl: AUDIO_GETINFO\n");
+
+		copy_to_user_ret((struct audio_info *)arg, &ainfo,
+				 sizeof(ainfo), -EFAULT);
+
 		break;
 
+	case AUDIO_SETINFO:
+	  {
+	    audio_info_t curinfo;
+
+	    copy_from_user_ret(&ainfo, (audio_info_t *) arg, sizeof(audio_info_t), -EFAULT);
+
+	    /* Without these there's no point in trying */
+	    if (!driver->ops->get_input_precision ||
+		!driver->ops->get_input_channels ||
+		!driver->ops->get_input_rate ||
+		!driver->ops->get_input_encoding ||
+		!driver->ops->get_output_precision ||
+		!driver->ops->get_output_channels ||
+                !driver->ops->get_output_rate ||
+                !driver->ops->get_output_encoding) 
+	      {
+		retval = -EINVAL;
+		break;
+	      }
+
+	    /* Do bounds checking for things which always apply.
+	     * Follow with enforcement of basic tenets of certain
+	     * encodings. Everything over and above generic is
+	     * enforced by the driver, which can assume that
+	     * Martian cases are taken care of here. */
+	    if (Modify(ainfo.play.gain) && 
+		((ainfo.play.gain > AUDIO_MAX_GAIN) || 
+		 (ainfo.play.gain < AUDIO_MIN_GAIN))) {
+	      /* Need to differentiate this from e.g. the above error */
+	      retval = -EINVAL;
+	      break;
+	    }
+	    if (Modify(ainfo.record.gain) &&
+		((ainfo.record.gain > AUDIO_MAX_GAIN) ||
+		 (ainfo.record.gain < AUDIO_MIN_GAIN))) {
+	      retval = -EINVAL;
+	      break;
+	    }
+	    if (Modify(ainfo.monitor_gain) &&
+		((ainfo.monitor_gain > AUDIO_MAX_GAIN) ||
+		 (ainfo.monitor_gain < AUDIO_MIN_GAIN))) {
+	      retval = -EINVAL;
+	      break;
+	    }
+	    /* Don't need to check less than zero on these */
+	    if (Modifyc(ainfo.play.balance) &&
+		(ainfo.play.balance > AUDIO_RIGHT_BALANCE)) {
+	      retval = -EINVAL;
+	      break;
+	    }
+	    if (Modifyc(ainfo.record.balance) &&
+		(ainfo.record.balance > AUDIO_RIGHT_BALANCE)) {
+	      retval = -EINVAL;
+	      break;
+	    }
+	    
+	    /* If any of these changed, record them all, then make
+	     * changes atomically. If something fails, back it all out. */
+	    if (Modify(ainfo.record.precision) || 
+		Modify(ainfo.record.sample_rate) ||
+		Modify(ainfo.record.channels) ||
+		Modify(ainfo.record.encoding) || 
+		Modify(ainfo.play.precision) || 
+		Modify(ainfo.play.sample_rate) ||
+		Modify(ainfo.play.channels) ||
+		Modify(ainfo.play.encoding)) 
+	      {
+		/* If they're trying to change something we
+		 * have no routine for, they lose */
+		if ((!driver->ops->set_input_encoding && 
+		    Modify(ainfo.record.encoding)) ||
+		    (!driver->ops->set_input_rate && 
+		    Modify(ainfo.record.sample_rate)) ||
+		    (!driver->ops->set_input_precision && 
+		    Modify(ainfo.record.precision)) ||
+		    (!driver->ops->set_input_channels && 
+		    Modify(ainfo.record.channels))) {
+		  retval = -EINVAL;
+		  break;
+		}		  
+
+		curinfo.record.encoding = (Modify(ainfo.record.encoding) ? 
+					   ainfo.record.encoding :
+					   driver->ops->get_input_encoding(driver));	    
+		curinfo.record.sample_rate = (Modify(ainfo.record.sample_rate) ? 
+					   ainfo.record.sample_rate :
+					   driver->ops->get_input_rate(driver));	   
+		curinfo.record.precision = (Modify(ainfo.record.precision) ? 
+					   ainfo.record.precision :
+					   driver->ops->get_input_precision(driver));	   
+		curinfo.record.channels = (Modify(ainfo.record.channels) ? 
+					   ainfo.record.channels :
+					   driver->ops->get_input_channels(driver));	   
+		switch (curinfo.record.encoding) {
+		case AUDIO_ENCODING_ALAW:
+		case AUDIO_ENCODING_ULAW:
+		  if (Modify(ainfo.record.precision) && 
+		      ainfo.record.precision != 8) {
+		    retval = -EINVAL;
+		    break;
+		  }
+		  if (Modify(ainfo.record.channels) && 
+		      ainfo.record.channels != 1) {
+		    retval = -EINVAL;
+		    break;
+		  }
+		  break;
+		case AUDIO_ENCODING_LINEAR:
+		case AUDIO_ENCODING_LINEARLE:
+		  if (Modify(ainfo.record.precision) && 
+		      ainfo.record.precision != 16) {
+		    retval = -EINVAL;
+		    break;
+		  }
+		  if (Modify(ainfo.record.channels) && 
+		      (ainfo.record.channels != 1 && 
+		       ainfo.record.channels != 2)) 
+		    {
+		      retval = -EINVAL;
+		      break;
+		    }
+		  break;
+		case AUDIO_ENCODING_LINEAR8:
+		  if (Modify(ainfo.record.precision) && 
+		      ainfo.record.precision != 8) {
+		    retval = -EINVAL;
+		    break;
+		  }
+		  if (Modify(ainfo.record.channels) && 
+		      (ainfo.record.channels != 1 && 
+		       ainfo.record.channels != 2)) 
+		    {
+		      retval = -EINVAL;
+		      break;
+		    }
+		}
+
+		if (retval < 0)
+		  break;
+
+		/* If they're trying to change something we
+		 * have no routine for, they lose */
+		if ((!driver->ops->set_output_encoding && 
+		    Modify(ainfo.play.encoding)) ||
+		    (!driver->ops->set_output_rate && 
+		    Modify(ainfo.play.sample_rate)) ||
+		    (!driver->ops->set_output_precision && 
+		    Modify(ainfo.play.precision)) ||
+		    (!driver->ops->set_output_channels && 
+		    Modify(ainfo.play.channels))) {
+		  retval = -EINVAL;
+		  break;
+		}		  
+
+		curinfo.play.encoding = (Modify(ainfo.play.encoding) ? 
+					 ainfo.play.encoding : 
+					 driver->ops->get_output_encoding(driver));
+		curinfo.play.sample_rate = (Modify(ainfo.play.sample_rate) ? 
+					   ainfo.play.sample_rate :
+					   driver->ops->get_output_rate(driver));	   
+		curinfo.play.precision = (Modify(ainfo.play.precision) ? 
+					   ainfo.play.precision :
+					   driver->ops->get_output_precision(driver));	   
+		curinfo.play.channels = (Modify(ainfo.play.channels) ? 
+					   ainfo.play.channels :
+					   driver->ops->get_output_channels(driver));	   
+		switch (curinfo.play.encoding) {
+		case AUDIO_ENCODING_ALAW:
+		case AUDIO_ENCODING_ULAW:
+		  if (Modify(ainfo.play.precision) && 
+		      ainfo.play.precision != 8) {
+		    retval = -EINVAL;
+		    break;
+		  }
+		  if (Modify(ainfo.play.channels) && 
+		      ainfo.play.channels != 1) {
+		    retval = -EINVAL;
+		    break;
+		  }
+		  break;
+		case AUDIO_ENCODING_LINEAR:
+		case AUDIO_ENCODING_LINEARLE:
+		  if (Modify(ainfo.play.precision) && 
+		      ainfo.play.precision != 16) {
+		    retval = -EINVAL;
+		    break;
+		  }
+		  if (Modify(ainfo.play.channels) && 
+		      (ainfo.play.channels != 1 && 
+		       ainfo.play.channels != 2)) 
+		    {
+		      retval = -EINVAL;
+		      break;
+		    }
+		  break;
+		case AUDIO_ENCODING_LINEAR8:
+		  if (Modify(ainfo.play.precision) && 
+		      ainfo.play.precision != 8) {
+		    retval = -EINVAL;
+		    break;
+		  }
+		  if (Modify(ainfo.play.channels) && 
+		      (ainfo.play.channels != 1 && 
+		       ainfo.play.channels != 2)) 
+		    {
+		      retval = -EINVAL;
+		      break;
+		    }
+		}
+		
+		if (retval < 0)
+		  break;
+
+		/* If we got this far, we're at least sane with
+		 * respect to generics. Try the changes. */
+		if ((driver->ops->set_input_precision(driver, ainfo.record.precision) < 0) ||
+		    (driver->ops->set_output_precision(driver, ainfo.play.precision) < 0) ||
+		    (driver->ops->set_input_channels(driver, ainfo.record.channels) < 0) ||
+		    (driver->ops->set_output_channels(driver, ainfo.play.channels) < 0) ||
+		    (driver->ops->set_input_rate(driver, ainfo.record.sample_rate) < 0) ||
+		    (driver->ops->set_output_rate(driver, ainfo.play.sample_rate) < 0) ||
+		    (driver->ops->set_input_encoding(driver, ainfo.record.encoding) < 0) ||
+		    (driver->ops->set_output_encoding(driver, ainfo.play.encoding) < 0)) 
+		  {
+		    /* Pray we can set it all back. If not, uh... */
+		    driver->ops->set_input_precision(driver, curinfo.record.precision);
+		    driver->ops->set_output_precision(driver, curinfo.play.precision);
+		    driver->ops->set_input_channels(driver, curinfo.record.channels);
+		    driver->ops->set_output_channels(driver, curinfo.play.channels);
+		    driver->ops->set_input_rate(driver, curinfo.record.sample_rate);
+		    driver->ops->set_output_rate(driver, curinfo.play.sample_rate);
+		    driver->ops->set_input_encoding(driver, curinfo.record.encoding); 
+		    driver->ops->set_output_encoding(driver, curinfo.play.encoding);
+		  }
+
+	    }
+
+	    printk("sparcaudio_ioctl: AUDIO_SETINFO\n");
+	    break;
+	  }
+
 	default:
 		if (driver->ops->ioctl)
 			retval = driver->ops->ioctl(inode,file,cmd,arg,driver);
-		else
+		else {
 			retval = -EINVAL;
+
+			printk("sparcaudio_ioctl: 0x%x\n", cmd);
+		}
 	}
 
 	return retval;
 }
 
+static int sparcaudioctl_release(struct inode * inode, struct file * file)
+{
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static struct file_operations sparcaudioctl_fops = {
+	NULL,
+	NULL,
+	NULL,
+	NULL,			/* sparcaudio_readdir */
+	NULL,			/* sparcaudio_select */
+	sparcaudio_ioctl,
+	NULL,			/* sparcaudio_mmap */
+	NULL,
+	sparcaudioctl_release
+};
+
 static int sparcaudio_open(struct inode * inode, struct file * file)
 {
 	int err;
@@ -259,6 +731,15 @@
 	if (!driver)
 		return -ENODEV;
 
+	if (MINOR(inode->i_rdev) == 5) {
+
+		file->f_op = &sparcaudioctl_fops;
+
+		MOD_INC_USE_COUNT;
+
+		return 0;
+	}
+
 	/* We only support minor #4 (/dev/audio) right now. */
 	if (MINOR(inode->i_rdev) != 4)
 		return -ENXIO;
@@ -285,9 +766,18 @@
 	}
 
 	/* Mark the driver as locked for read and/or write. */
-	if (file->f_mode & FMODE_READ)
+	if (file->f_mode & FMODE_READ) {
+		driver->input_offset = 0;
+		driver->input_front = 0;
+		driver->input_rear = 0;
+		driver->input_count = 0;
+		driver->ops->start_input(driver, driver->input_buffers[driver->input_front],
+					 PAGE_SIZE);
+		driver->input_active = 1;
 		driver->flags |= SDF_OPEN_READ;
+	}
 	if (file->f_mode & FMODE_WRITE) {
+		driver->output_size = 0;
 		driver->output_front = 0;
 		driver->output_rear = 0;
 		driver->output_count = 0;
@@ -308,8 +798,15 @@
 	return 0;
 }
 
-static void sparcaudio_release(struct inode * inode, struct file * file)
+static int sparcaudio_release(struct inode * inode, struct file * file)
 {
+        /* Anything in the queue? */
+        sparcaudio_sync_output(driver);
+
+	/* Stop input */
+	driver->ops->stop_input(driver);
+	driver->input_active = 0;
+
 	/* Wait for any output still in the queue to be played. */
 	if (driver->output_count > 0)
 		interruptible_sleep_on(&driver->output_drain_wait);
@@ -331,10 +828,12 @@
 	MOD_DEC_USE_COUNT;
 
 	wake_up_interruptible(&driver->open_wait);
+
+	return 0;
 }
 
 static struct file_operations sparcaudio_fops = {
-	sparcaudio_lseek,
+	sparcaudio_llseek,
 	sparcaudio_read,
 	sparcaudio_write,
 	NULL,			/* sparcaudio_readdir */
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/audio/audio.h linux/drivers/sbus/audio/audio.h
--- v2.1.78/linux/drivers/sbus/audio/audio.h	Mon Mar 17 14:54:28 1997
+++ linux/drivers/sbus/audio/audio.h	Mon Jan 12 15:15:45 1998
@@ -82,12 +82,13 @@
 /*
  * Audio encoding types
  */
-#define	AUDIO_ENCODING_NONE	(0)	/* no encoding assigned	*/
-#define	AUDIO_ENCODING_ULAW	(1)	/* u-law encoding	*/
-#define	AUDIO_ENCODING_ALAW	(2)	/* A-law encoding	*/
-#define	AUDIO_ENCODING_LINEAR	(3)	/* Linear PCM encoding	*/
-#define	AUDIO_ENCODING_DVI	(104)	/* DVI ADPCM		*/
-#define	AUDIO_ENCODING_LINEAR8	(105)	/* 8 bit UNSIGNED	*/
+#define	AUDIO_ENCODING_NONE	(0)	/* no encoding assigned	  */
+#define	AUDIO_ENCODING_ULAW	(1)	/* u-law encoding	  */
+#define	AUDIO_ENCODING_ALAW	(2)	/* A-law encoding	  */
+#define	AUDIO_ENCODING_LINEAR	(3)	/* Linear PCM encoding	  */
+#define	AUDIO_ENCODING_DVI	(104)	/* DVI ADPCM		  */
+#define	AUDIO_ENCODING_LINEAR8	(105)	/* 8 bit UNSIGNED	  */
+#define	AUDIO_ENCODING_LINEARLE	(106)	/* Linear PCM LE encoding */
 
 /*
  * These ranges apply to record, play, and monitor gain values
@@ -136,6 +137,8 @@
 #define	AUDIO_LINE_IN		0x02	/* input from line in	 */
 #define	AUDIO_CD		0x04	/* input from on-board CD inputs */
 #define	AUDIO_INTERNAL_CD_IN	AUDIO_CD	/* input from internal CDROM */
+/* Supposedly an undocumented feature of the 4231 */
+#define AUDIO_ANALOG_LOOPBACK   0x40
 
 
 /*
@@ -152,6 +155,14 @@
 
 
 /*
+ * These allow testing for what the user wants to set 
+ */
+#define AUD_INITVALUE   (~0)
+#define Modify(X)       ((unsigned int)(X) != AUD_INITVALUE)
+#define Modifys(X)      ((X) != (unsigned short)AUD_INITVALUE)
+#define Modifyc(X)      ((X) != (unsigned char)AUD_INITVALUE)
+
+/*
  * Parameter for the AUDIO_GETDEV ioctl to determine current
  * audio devices.
  */
@@ -192,6 +203,16 @@
 #define	AUDIO_SETINFO	_IOWR('A', 2, audio_info_t)
 #define	AUDIO_DRAIN	_IO('A', 3)
 #define	AUDIO_GETDEV	_IOR('A', 4, audio_device_t)
+#define	AUDIO_GETDEV_SUNOS	_IOR('A', 4, int)
+
+/* Define possible audio hardware configurations for 
+ * old SunOS-style AUDIO_GETDEV ioctl */
+
+#define AUDIO_DEV_UNKNOWN       (0)     /* not defined */
+#define AUDIO_DEV_AMD           (1)     /* audioamd device */
+#define AUDIO_DEV_SPEAKERBOX    (2)     /* dbri device with speakerbox */
+#define AUDIO_DEV_CODEC         (3)     /* dbri device (internal speaker) */
+#define AUDIO_DEV_CS4231        (5)     /* cs4231 device */
 
 /*
  * The following ioctl sets the audio device into an internal loopback mode,
@@ -214,8 +235,6 @@
 };
 #endif
 
-
-
 /*
  *	Linux kernel internal implementation.
  */
@@ -245,10 +264,17 @@
 
 	/* Support for a circular queue of output buffers. */
 	__u8 **output_buffers;
-	size_t *output_sizes;
+	size_t *output_sizes, output_size;
 	int num_output_buffers, output_front, output_rear;
 	int output_count, output_active;
 	struct wait_queue *output_write_wait, *output_drain_wait;
+
+	/* Support for a circular queue of input buffers. */
+	__u8 **input_buffers;
+	int input_offset;
+	int num_input_buffers, input_front, input_rear;
+	int input_count, input_active;
+	struct wait_queue *input_read_wait;
 };
 
 struct sparcaudio_operations
@@ -272,6 +298,77 @@
 
 	/* Return driver name/version to caller. (/dev/audio specific) */
 	void (*sunaudio_getdev)(struct sparcaudio_driver *, audio_device_t *);
+
+        /* Get and set the output volume. (0-255) */
+        int (*set_output_volume)(struct sparcaudio_driver *, int);
+        int (*get_output_volume)(struct sparcaudio_driver *);
+
+        /* Get and set the input volume. (0-255) */
+        int (*set_input_volume)(struct sparcaudio_driver *, int);
+        int (*get_input_volume)(struct sparcaudio_driver *);
+
+        /* Get and set the monitor volume. (0-255) */
+        int (*set_monitor_volume)(struct sparcaudio_driver *, int);
+        int (*get_monitor_volume)(struct sparcaudio_driver *);
+
+        /* Get and set the output balance. (0-64) */
+        int (*set_output_balance)(struct sparcaudio_driver *, int);
+        int (*get_output_balance)(struct sparcaudio_driver *);
+
+        /* Get and set the input balance. (0-64) */
+        int (*set_input_balance)(struct sparcaudio_driver *, int);
+        int (*get_input_balance)(struct sparcaudio_driver *);
+
+        /* Get and set the output channels. (1-4) */
+        int (*set_output_channels)(struct sparcaudio_driver *, int);
+        int (*get_output_channels)(struct sparcaudio_driver *);
+
+        /* Get and set the input channels. (1-4) */
+        int (*set_input_channels)(struct sparcaudio_driver *, int);
+        int (*get_input_channels)(struct sparcaudio_driver *);
+
+        /* Get and set the output precision. (8-32) */
+        int (*set_output_precision)(struct sparcaudio_driver *, int);
+        int (*get_output_precision)(struct sparcaudio_driver *);
+
+        /* Get and set the input precision. (8-32) */
+        int (*set_input_precision)(struct sparcaudio_driver *, int);
+        int (*get_input_precision)(struct sparcaudio_driver *);
+
+        /* Get and set the output port. () */
+        int (*set_output_port)(struct sparcaudio_driver *, int);
+        int (*get_output_port)(struct sparcaudio_driver *);
+
+        /* Get and set the input port. () */
+        int (*set_input_port)(struct sparcaudio_driver *, int);
+        int (*get_input_port)(struct sparcaudio_driver *);
+
+        /* Get and set the output encoding. () */
+        int (*set_output_encoding)(struct sparcaudio_driver *, int);
+        int (*get_output_encoding)(struct sparcaudio_driver *);
+
+        /* Get and set the input encoding. () */
+        int (*set_input_encoding)(struct sparcaudio_driver *, int);
+        int (*get_input_encoding)(struct sparcaudio_driver *);
+
+        /* Get and set the output rate. () */
+        int (*set_output_rate)(struct sparcaudio_driver *, int);
+        int (*get_output_rate)(struct sparcaudio_driver *);
+
+        /* Get and set the input rate. () */
+        int (*set_input_rate)(struct sparcaudio_driver *, int);
+        int (*get_input_rate)(struct sparcaudio_driver *);
+
+	/* Return driver number to caller. (SunOS /dev/audio specific) */
+	int (*sunaudio_getdev_sunos)(struct sparcaudio_driver *);
+
+	/* Get available ports */
+	int (*get_output_ports)(struct sparcaudio_driver *);
+	int (*get_input_ports)(struct sparcaudio_driver *);
+
+	/* Get and set output mute */
+	int (*set_output_muted)(struct sparcaudio_driver *, int);
+	int (*get_output_muted)(struct sparcaudio_driver *);
 };
 
 extern int register_sparcaudio_driver(struct sparcaudio_driver *);
@@ -282,6 +379,6 @@
 extern int amd7930_init(void);
 extern int cs4231_init(void);
 
-#endif
+#endif  /* __KERNEL__ */
 
-#endif
+#endif  /* _AUDIO_H */
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/audio/cs4215.h linux/drivers/sbus/audio/cs4215.h
--- v2.1.78/linux/drivers/sbus/audio/cs4215.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/audio/cs4215.h	Mon Jan 12 15:15:45 1998
@@ -0,0 +1,120 @@
+/*
+ * drivers/sbus/audio/cs4215.h
+ *
+ * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
+ * Used with dbri.h
+ */
+
+#ifndef _CS4215_H_
+#define _CS4215_H_
+
+struct cs4215 {
+	__u8	data[4];	/* Data mode: Time slots 5-8 */
+	__u8	ctrl[4];	/* Ctrl mode: Time slots 1-4 */
+	__volatile__ struct dbri_mem td;
+	__volatile__ struct dbri_mem rd;
+	__u8	version;
+	__u8	onboard;
+};
+
+
+/*
+ * Control mode first 
+ */
+
+/* Time Slot 1, Status register */
+#define CS4215_CLB	(1<<2)	/* Control Latch Bit */
+#define CS4215_OLB	(1<<3)	/* 1: line: 2.0V, speaker 4V */
+				/* 0: line: 2.8V, speaker 8V */
+#define CS4215_MLB	(1<<4)	/* 1: Microphone: 20dB gain disabled */
+#define CS4215_RSRVD_1  (1<<5)
+
+
+/* Time Slot 2, Data Format Register */
+#define CS4215_DFR_LINEAR16	0
+#define CS4215_DFR_ULAW		1
+#define CS4215_DFR_ALAW		2
+#define CS4215_DFR_LINEAR8	3
+#define CS4215_DFR_STEREO	(1<<2)
+static struct {
+	unsigned short freq;
+	unsigned char  xtal;
+	unsigned char  csval;
+} CS4215_FREQ[] = {
+	{	 8000,	1,	(0<<3)	},
+	{	16000,	1,	(1<<3)	},
+	{	27429,	1,	(2<<3)	},	/* Actually 24428.57 */
+	{	32000,	1,	(3<<3)	},
+	/* {	 NA,	1,	(4<<3)	}, */
+	/* {	 NA,	1,	(5<<3)	}, */
+	{	48000,	1,	(6<<3)	},
+	{	 9600,	1,	(7<<3)	},
+	{	 5513,	2,	(0<<3)	},	/* Actually 5512.5 */
+	{	11025,	2,	(1<<3)	},
+	{	18900,	2,	(2<<3)	},
+	{	22050,	2,	(3<<3)	},
+	{	37800,	2,	(4<<3)	},
+	{	44100,	2,	(5<<3)	},
+	{	33075,	2,	(6<<3)	},
+	{	 6615,	2,	(7<<3)	},
+	{	    0,	0,	0	}
+};
+#define CS4215_HPF	(1<<7)	/* High Pass Filter, 1: Enabled */
+
+#define CS4215_12_MASK	0xfcbf	/* Mask off reseved bits in slot 1 & 2 */
+
+/* Time Slot 3, Serial Port Control register */
+#define CS4215_XEN	(1<<0)	/* 0: Enable serial output */
+#define CS4215_XCLK	(1<<1)	/* 1: Master mode: Generate SCLK */
+#define CS4215_BSEL_64	(0<<2)	/* Bitrate: 64 bits per frame */
+#define CS4215_BSEL_128	(1<<2)
+#define CS4215_BSEL_256	(2<<2)
+#define CS4215_MCK_MAST (0<<4)	/* Master clock */
+#define CS4215_MCK_XTL1 (1<<4)	/* 24.576 MHz clock source */
+#define CS4215_MCK_XTL2 (2<<4)	/* 16.9344 MHz clock source */
+#define CS4215_MCK_CLK1 (3<<4)	/* Clockin, 256 x Fs */
+#define CS4215_MCK_CLK2 (4<<4)	/* Clockin, see DFR */
+
+/* Time Slot 4, Test Register */
+#define CS4215_DAD	(1<<0)	/* 0:Digital-Dig loop, 1:Dig-Analog-Dig loop */
+#define CS4215_ENL	(1<<1)	/* Enable Loopback Testing */
+
+/* Time Slot 5, Parallel Port Register */
+/* Read only here and the same as the in data mode */
+
+/* Time Slot 6, Reserved  */
+
+/* Time Slot 7, Version Register  */
+#define CS4215_VERSION_MASK 0xf	/* Known versions 0/C, 1/D, 2/E */
+
+/* Time Slot 8, Reserved  */
+
+
+
+/*
+ * Data mode
+ */
+/* Time Slot 1-2: Left Channel Data, 2-3: Right Channel Data  */
+
+/* Time Slot 5, Output Setting  */
+#define CS4215_LO(v)	v	/* Left Output Attenuation 0x3f: -94.5 dB */
+#define CS4215_LE	(1<<6)	/* Line Out Enable */
+#define CS4215_HE	(1<<7)	/* Headphone Enable */
+
+/* Time Slot 6, Output Setting  */
+#define CS4215_RO(v)	v	/* Right Output Attenuation 0x3f: -94.5 dB */
+#define CS4215_SE	(1<<6)	/* Line Out Enable */
+#define CS4215_ADI	(1<<7)	/* A/D Data Invalid: Busy in calibration */
+
+/* Time Slot 7, Input Setting */
+#define CS4215_LG(v)	v	/* Left Gain Setting 0xf: 22.5 dB */
+#define CS4215_IS	(1<<4)	/* Input Select: 1=Microphone, 0=Line */
+#define CS4215_OVR	(1<<5)	/* 1: Overrange condition occured */
+#define CS4215_PIO0	(1<<6)	/* Parallel I/O 0 */
+#define CS4215_PIO1	(1<<7)
+
+/* Time Slot 8, Input Setting */
+#define CS4215_RG(v)	v	/* Right Gain Setting 0xf: 22.5 dB */
+#define CS4215_MA(v)	(v<<4)	/* Monitor Path Attenuation 0xf: mute */
+
+#endif
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/audio/cs4231.c linux/drivers/sbus/audio/cs4231.c
--- v2.1.78/linux/drivers/sbus/audio/cs4231.c	Tue May 13 22:41:12 1997
+++ linux/drivers/sbus/audio/cs4231.c	Mon Jan 12 15:15:45 1998
@@ -44,8 +44,6 @@
 static void cs4231_output_muted(struct sparcaudio_driver *drv, unsigned int value);
 static void cs4231_mute(struct sparcaudio_driver *drv);
 static void cs4231_pollinput(struct sparcaudio_driver *drv);
-static int cs4231_attach(struct sparcaudio_driver *drv, int node,
-                          struct linux_sbus *sbus);
 
 #define CHIP_BUG udelay(100); cs4231_ready(drv); udelay(1000);
 
@@ -623,56 +621,16 @@
 	cs4231_audio_getdev,
 };
 
-/* Probe for the cs4231 chip and then attach the driver. */
-#ifdef MODULE
-int init_module(void)
-#else
-__initfunc(int cs4231_init(void))
-#endif
-{
-  struct linux_sbus *bus;
-  struct linux_sbus_device *sdev;
-  int cs4231_node;
-  
-  /* Find the PROM CS4231 node. */
-  /* There's an easier way, and I should FIXME */
-  cs4231_node = prom_getchild(prom_root_node);
-  cs4231_node = prom_searchsiblings(cs4231_node,"iommu");
-  cs4231_node = prom_getchild(cs4231_node);
-  cs4231_node = prom_searchsiblings(cs4231_node,"sbus");
-  cs4231_node = prom_getchild(cs4231_node);
-  cs4231_node = prom_searchsiblings(cs4231_node,"SUNW,CS4231");
-  
-  if (cs4231_node && cs4231_attach(&drivers[0], cs4231_node, NULL) == 0)
-    num_drivers = 1;
-  else
-    num_drivers = 0;
-  
-  /* Probe each SBUS for cs4231 chips. */
-  for_all_sbusdev(sdev,bus) {
-    if (!strcmp(sdev->prom_name, "SUNW,CS4231")) {
-      /* Don't go over the max number of drivers. */
-      if (num_drivers >= MAX_DRIVERS)
-	continue;
-      
-      if (cs4231_attach(&drivers[num_drivers],
-			sdev->prom_node, sdev->my_bus) == 0)
-	num_drivers++;
-    }
-  }
-  
-  /* Only return success if we found some cs4231 chips. */
-  return (num_drivers > 0) ? 0 : -EIO;
-}
-
 /* Attach to an cs4231 chip given its PROM node. */
-static int cs4231_attach(struct sparcaudio_driver *drv, int node,
-                          struct linux_sbus *sbus)
+static inline int
+cs4231_attach(struct sparcaudio_driver *drv, struct linux_sbus_device *sdev)
 {
-  struct linux_prom_registers regs;
-  struct linux_prom_irqs irq;
   struct cs4231_chip *cs4231_chip;
   int err;
+  struct linux_sbus *sbus = sdev->my_bus;
+#ifdef __sparc_v9__
+  struct devid_cookie dcookie;
+#endif
 
   /* Allocate our private information structure. */
   drv->private = kmalloc(sizeof(struct cs4231_chip), GFP_KERNEL);
@@ -690,12 +648,11 @@
 #endif
 
   /* Map the registers into memory. */
-  prom_getproperty(node, "reg", (char *)&regs, sizeof(regs));
-  if (sbus)
-    prom_apply_sbus_ranges(sbus, &regs, 1);
-  cs4231_chip->regs_size = regs.reg_size;
-  cs4231_chip->pioregs = sparc_alloc_io(regs.phys_addr, 0, regs.reg_size,
-				      "cs4231", regs.which_io, 0);
+  prom_apply_sbus_ranges(sbus, sdev->reg_addrs, 1, sdev);
+  cs4231_chip->regs_size = sdev->reg_addrs[0].reg_size;
+  cs4231_chip->pioregs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, 
+  					sdev->reg_addrs[0].reg_size,
+				      "cs4231", sdev->reg_addrs[0].which_io, 0);
   if (!cs4231_chip->pioregs) {
     printk(KERN_ERR "cs4231: could not allocate registers\n");
     kfree(drv->private);
@@ -706,9 +663,18 @@
   cs4231_reset(drv);
 
   /* Attach the interrupt handler to the audio interrupt. */
-  prom_getproperty(node, "intr", (char *)&irq, sizeof(irq));
-  cs4231_chip->irq = irq.pri;
-  request_irq(cs4231_chip->irq, cs4231_interrupt, SA_INTERRUPT, "cs4231", NULL);
+  cs4231_chip->irq = sdev->irqs[0].pri;
+
+#ifndef __sparc_v9__
+  request_irq(cs4231_chip->irq, cs4231_interrupt, SA_SHIRQ, "cs4231", drv);
+#else
+  dcookie.real_dev_id = s;
+  dcookie.imap = dcookie.iclr = 0;
+  dcookie.pil = -1;
+  dcookie.bus_cookie = sdev->my_bus;
+  request_irq (cs4231_chip->irq, cs4231_interrupt, (SA_SHIRQ | SA_SBUS | SA_DCOOKIE), "cs4231", drv);
+  cs4231_chip->irq = dcookie.ret_ino;
+#endif
   enable_irq(cs4231_chip->irq);
 
   /* Register ourselves with the midlevel audio driver. */
@@ -728,6 +694,32 @@
   
   /* Success! */
   return 0;
+}
+
+/* Probe for the cs4231 chip and then attach the driver. */
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(int cs4231_init(void))
+#endif
+{
+  struct linux_sbus *bus;
+  struct linux_sbus_device *sdev;
+  
+  /* Probe each SBUS for cs4231 chips. */
+  for_all_sbusdev(sdev,bus) {
+    if (!strcmp(sdev->prom_name, "SUNW,CS4231")) {
+      /* Don't go over the max number of drivers. */
+      if (num_drivers >= MAX_DRIVERS)
+	continue;
+      
+      if (cs4231_attach(&drivers[num_drivers], sdev) == 0)
+	num_drivers++;
+    }
+  }
+  
+  /* Only return success if we found some cs4231 chips. */
+  return (num_drivers > 0) ? 0 : -EIO;
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/audio/dbri.c linux/drivers/sbus/audio/dbri.c
--- v2.1.78/linux/drivers/sbus/audio/dbri.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/audio/dbri.c	Mon Jan 12 15:15:45 1998
@@ -0,0 +1,715 @@
+/*
+ * drivers/sbus/audio/dbri.c
+ *
+ * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
+ * The SparcLinux interface was adopted from the CS4231 driver.
+ *
+ * This is the lowlevel driver for the DBRI & MMCODEC duo used for ISDN & AUDIO
+ * on Sun SPARCstation 10, 20, LX and Voyager models.
+ * NOTE: This driver only supports audio for now, there is NO SUPPORT for ISDN.
+ *
+ * - DBRI: AT&T T5900FX Dual Basic Rates ISDN Interface. It is a 32 channel
+ *   data time multiplexer with ISDN support (aka T7259)
+ *   Interfaces: SBus,ISDN NT & TE, CHI, 4 bits parallel.
+ *   CHI: (spelled ki) Concentration Highway Interface (AT&T or Intel bus ?).
+ *   Documentation:
+ *   - "STP 4000SBus Dual Basic Rate ISDN (DBRI) Tranceiver" from
+ *     Sparc Technology Business (courtesy of Sun Support)
+ *   - Data sheet of the T7903, a newer but very similar ISA bus equivalent
+ *     available from the Lucent (formarly AT&T microelectronics) home
+ *     page.
+ * - MMCODEC: Crystal Semiconductor CS4215 16 bit Multimedia Audio Codec
+ *   Interfaces: CHI, Audio In & Out, 2 bits parallel
+ *   Documentation: from the Crystal Semiconductor home page.
+ *
+ * The DBRI is a 32 pipe machine, each pipe can transfer some bits between
+ * memory and a serial device (long pipes, nr 0-15) or between two serial
+ * devices (short pipes, nr 16-31), or simply send a fixed data to a serial
+ * device (short pipes).
+ * A timeslot defines the bit-offset and nr of bits read from a serial device.
+ * The timeslots are linked to 6 circular lists, one for each direction for
+ * each serial device (NT,TE,CHI). A timeslot is associated to 1 or 2 pipes
+ * (the second one is a monitor/tee pipe, valid only for serial input).
+ *
+ * The mmcodec is connected via the CHI bus and needs the data & some
+ * parameters (volume, balance, output selection) timemultiplexed in 8 byte
+ * chunks. It also has a control mode, which serves for audio format setting.
+ *
+ * Looking at the CS4215 data sheet it is easy to set up 2 or 4 codecs on
+ * the same CHI bus, so I thought perhaps it is possible to use the onboard
+ * & the speakerbox codec simultanously, giving 2 (not very independent :-)
+ * audio devices. But the SUN HW group decided against it, at least on my
+ * LX the speakerbox connector has at least 1 pin missing and 1 wrongly
+ * connected.
+ */
+
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/sbus.h>
+
+#include "audio.h"
+#include "dbri.h"
+
+
+
+#define DBRI_DEBUG
+
+#ifdef DBRI_DEBUG
+
+#define dprintk(a, x) if(dbri_debug & a) printk x
+#define D_GEN	(1<<0)
+#define D_INT	(1<<1)
+#define D_CMD	(1<<2)
+#define D_MM	(1<<3)
+#define D_USR	(1<<4)
+
+static int dbri_debug = D_GEN|D_INT|D_CMD|D_MM|D_USR;
+static char *cmds[] = { 
+  "WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS",
+  "SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV"
+};
+
+/* Bit hunting */
+#define dumpcmd {int i; for(i=0; i<n; i++) printk("DBRI: %x\n", dbri->cmd[i]); }
+
+#else
+
+#define dprintk(a, x)
+#define dumpcmd
+
+#endif	/* DBRI_DEBUG */
+
+
+
+#define MAX_DRIVERS	2	/* Increase this if need more than 2 DBRI's */
+
+#define WAIT_INTR1	0xbe
+#define WAIT_INTR2	0xbf
+
+static struct sparcaudio_driver drivers[MAX_DRIVERS];
+static char drv_name[] = "DBRI/audio";
+static int num_drivers;
+static int dbri_cmdlocked = 0;
+
+
+/*
+ * Make sure, that we can send a command to the dbri
+ */
+static int dbri_cmdlock(struct dbri *dbri)
+{
+	unsigned long flags;
+	int was_sleeping = 0;
+
+	save_flags(flags);
+	cli();
+
+	if(dbri_cmdlocked) {
+		interruptible_sleep_on(&dbri->wait);
+		was_sleeping = 1;
+	}
+	if(dbri_cmdlocked)
+		return -EINTR;
+	dbri_cmdlocked = 1;
+
+	restore_flags(flags);
+
+	if(was_sleeping)
+		dprintk(D_INT, ("DBRI: Just woke up\n"));
+	return 0;
+}
+
+static void dummy()
+{
+}
+
+static struct sparcaudio_operations dbri_ops = {
+	dummy, /* dbri_open, */
+	dummy, /* dbri_release, */
+	dummy, /* dbri_ioctl, */
+	dummy, /* dbri_start_output, */
+	dummy, /* dbri_stop_output, */
+	dummy, /* dbri_start_input, */
+        dummy, /* dbri_stop_input, */
+	dummy, /* dbri_audio_getdev, */
+};
+
+static void dbri_reset(struct sparcaudio_driver *drv)
+{
+	struct dbri *dbri = (struct dbri *)drv->private;
+	int i;
+
+	dprintk(D_GEN, ("DBRI: reset 0:%x 2:%x 8:%x 9:%x\n",
+		dbri->regs->reg0, dbri->regs->reg2,
+		dbri->regs->reg8, dbri->regs->reg9)); 
+
+	dbri->regs->reg0 = D_R; /* Soft Reset */
+	for(i = 0; (dbri->regs->reg0 & D_R) && i < 10; i++)
+		udelay(10);
+}
+
+static void dbri_detach(struct sparcaudio_driver *drv)
+{
+        struct dbri *info = (struct dbri *)drv->private;
+
+	dbri_reset(drv);
+        unregister_sparcaudio_driver(drv);
+        free_irq(info->irq, drv);
+        sparc_free_io(info->regs, info->regs_size);
+        kfree(drv->private);
+}
+
+
+static void dbri_init(struct sparcaudio_driver *drv)
+{
+	struct dbri *dbri = (struct dbri *)drv->private;
+	int n;
+
+        dbri_reset(drv);
+	dbri->wait = NULL;
+
+	dprintk(D_GEN, ("DBRI: init: cmd: %x, int: %x\n",
+			(int)dbri->cmd, (int)dbri->intr));
+
+	/*
+	 * Initialize the interrupt ringbuffer.
+	 */
+	for(n = 0; n < DBRI_NO_INTS-1; n++)
+		dbri->intr[n * DBRI_INT_BLK] = 
+					(int)(&dbri->intr[(n+1)*DBRI_INT_BLK]);
+	dbri->intr[n * DBRI_INT_BLK] = (int)(dbri->intr);
+	dbri->dbri_irqp = 1;
+
+	dbri->regs->reg0 |= (D_G|D_S|D_E);
+
+	/*
+	 * Set up the interrupt queue
+	 */
+	(void)dbri_cmdlock(dbri);
+
+	n = 0;
+	dbri->cmd[n++] = DBRI_CMD(D_IIQ, 0, 0);
+	dbri->cmd[n++] = (int)(dbri->intr);
+	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, WAIT_INTR1);
+	dbri->regs->reg8 = (int)dbri->cmd;
+}
+
+
+
+/*
+ * Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr.
+ * So we have to reverse the bits. Note: only 1, 2 or 4 bytes are supported.
+ */
+static __u32 reverse_bytes(__u32 b, int len)
+{
+	switch(len) {
+		case 4: b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16);
+		case 2: b = ((b & 0xff00ff00) >>  8) | ((b & 0x00ff00ff) <<  8);
+		case 1: b = ((b & 0xf0f0f0f0) >>  4) | ((b & 0x0f0f0f0f) <<  4);
+			b = ((b & 0xcccccccc) >>  2) | ((b & 0x33333333) <<  2);
+			b = ((b & 0xaaaaaaaa) >>  1) | ((b & 0x55555555) <<  1);
+	}
+	return b;
+}
+
+
+
+static void mmcodec_default(struct cs4215 *mm)
+{
+	/*
+	 * No action, memory resetting only.
+	 *
+	 * Data Time Slot 5-8
+	 * Speaker,Line and Headphone enable. Gain set to the half.
+	 * Input is mike.
+	 */
+	mm->data[0] = CS4215_LO(0x20) | CS4215_HE|CS4215_LE;
+	mm->data[1] = CS4215_RO(0x20) | CS4215_SE;
+	mm->data[2] = CS4215_LG( 0x8) | CS4215_IS;
+	mm->data[3] = CS4215_RG( 0x8) | CS4215_MA(0xf);
+
+	/*
+	 * Control Time Slot 1-4
+	 * 0: Default I/O voltage scale
+	 * 1: 8 bit ulaw, 8kHz, mono, high pass filter disabled
+	 * 2: Serial enable, CHI master, 1 CHI device (64bit), clock 1
+	 * 3: Tests disabled
+	 */
+	mm->ctrl[0] = CS4215_RSRVD_1;
+	mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval;
+	mm->ctrl[2] = CS4215_XEN | CS4215_XCLK |
+			CS4215_BSEL_128 | CS4215_FREQ[0].xtal;
+	mm->ctrl[3] = 0;
+}
+
+static void mmcodec_init_data(struct dbri *dbri)
+{
+	int val, n = 0;
+
+	dbri_cmdlock(dbri);
+
+	/*
+	 * Data mode:
+	 * Pipe  4: Send timeslots 1-4 (audio data)
+	 * Pipe 17: Send timeslots 5-8 (part of ctrl data)
+	 * Pipe  6: Receive timeslots 1-4 (audio data)
+	 * Pipe 19: Receive timeslots 6-7. We can only receive 20 bits via
+	 *          interrupt, and the rest of the data (slot 5 and 8) is
+	 *	    not relevant for us (only for doublechecking).
+	 */
+	
+	/* Transmit & Receive Memory setup */
+	dbri->mm.td.flags = DBRI_TD_F|DBRI_TD_D|DBRI_TD_CNT(0);
+	dbri->mm.td.ba = 0;
+	dbri->mm.td.nda = (__u32)&dbri->mm.td;
+	dbri->mm.td.status = 0;
+
+	dbri->mm.td.flags = DBRI_RD_BCNT(0);
+	dbri->mm.td.ba = 0;
+	dbri->mm.td.nda = (__u32)&dbri->mm.rd;
+	dbri->mm.td.status = 0;
+
+	/* Pipe 4: SDP + DTS */
+	val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_C|D_SDP_MSB|D_PIPE(D_P_4);
+	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+	dbri->cmd[n++] = (__u32)&dbri->mm.td;
+
+	val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_4);
+	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+	dbri->cmd[n++] = 0;
+	dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_16);
+
+
+	/* Pipe 17: SDP + DTS + SSP */
+	val = D_SDP_FIXED|D_SDP_TO_SER|D_SDP_C|D_PIPE(D_P_17);
+	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+	dbri->cmd[n++] = 0;				/* Fixed data */
+
+	val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_4) | D_PIPE(D_P_17);
+	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+	dbri->cmd[n++] = 0;
+	dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(32) | D_TS_NONCONTIG |
+			 D_TS_MON(D_P_4) | D_TS_NEXT(D_P_16);
+
+	dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17));
+	dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.data, 4);
+
+
+	/* Pipe 6: SDP + DTS */
+	val=D_SDP_MEM|D_SDP_FROM_SER|D_SDP_C|D_SDP_MSB|D_PIPE(D_P_6);
+	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+	dbri->cmd[n++] = (__u32)&dbri->mm.rd;
+
+	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_6);
+	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+	dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_16);
+	dbri->cmd[n++] = 0;
+
+
+	/* Pipe 19: SDP + DTS */
+	val = D_SDP_FIXED|D_SDP_FROM_SER|D_SDP_P|D_SDP_C|D_PIPE(D_P_19);
+	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+	dbri->cmd[n++] = 0;				/* Fixed data */
+
+	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_6) | D_PIPE(D_P_19);
+	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+	dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(40) | D_TS_NONCONTIG |
+			 D_TS_MON(D_P_6) | D_TS_NEXT(D_P_16); 
+	dbri->cmd[n++] = 0;
+
+	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, WAIT_INTR1);
+
+	dbri->regs->reg8 = (int)dbri->cmd;
+}
+
+
+/*
+ * Send the control information (i.e. audio format)
+ */
+static void mmcodec_setctrl(struct dbri *dbri)
+{
+	int n = 0, val;
+
+	/*
+	 * Enable Command mode: Set PIO3 to 0, then wait
+	 * 12 cycles <= 12/(5512.5*64) sec = 34.01 usec
+	 */
+	val = D_ENPIO | D_PIO1 | (dbri->mm.onboard ? D_PIO0 : D_PIO2);
+	dbri->regs->reg2 = val;
+	udelay(34);	
+
+	dbri_cmdlock(dbri);
+
+	/*
+	 * Control mode:
+	 * Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly)
+	 * Pipe 18: Receive timeslot 1 (clb).
+	 * Pipe 19: Receive timeslot 7 (version). 
+	 */
+
+	/* Set CHI Anchor: Pipe 16. This should take care of the rest. */
+	val = D_DTS_VI | D_DTS_VO | D_DTS_INS |
+	      D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16);
+	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+	dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
+	dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
+
+
+	/* Setup the pipes first */
+	val = D_SDP_FIXED|D_SDP_TO_SER|D_SDP_P|D_SDP_C|D_PIPE(D_P_17);
+	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+	dbri->cmd[n++] = 0;
+
+	val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_P|D_SDP_C|D_PIPE(D_P_18);
+	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+	dbri->cmd[n++] = 0;
+
+	val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_P|D_SDP_C|D_PIPE(D_P_19);
+	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+	dbri->cmd[n++] = 0;
+
+	/* Fill in the data to send */
+	dbri->mm.ctrl[0] &= ~CS4215_CLB;
+	dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17));
+	dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4);
+
+	dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
+
+
+
+	/* Link the timeslots */
+	val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_17);
+	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+	dbri->cmd[n++] = 0;
+	dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(256) | D_TS_NEXT(D_P_16);
+
+	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_18);
+	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+	dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(0) | D_TS_NEXT(D_P_16);
+	dbri->cmd[n++] = 0;
+
+	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_18) | D_PIPE(D_P_19);
+	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+	dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(48) | D_TS_NEXT(D_P_16);
+	/*
+	 * According to the manual we should also specify 
+	 * D_TS_NONCONTIG | D_TS_MON(D_P_18), but the machine freezes
+	 * if we do that. Can somebody explain me why?
+	 */
+	dbri->cmd[n++] = 0;
+
+
+	/* Setup DBRI for CHI Master */
+	dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_REN);
+	dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(6) | D_CHI_FD |
+					D_CHI_IR | D_CHI_EN);
+	dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
+	dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+
+	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, 0);
+	dbri->regs->reg8 = (int)dbri->cmd;
+
+	/* Wait for the data from the CS4215 */
+	interruptible_sleep_on(&dbri->int_wait);
+printk("Woke up (1) reg2: %x\n", dbri->regs->reg2);
+
+
+	/* Now switch back to data mode */
+	n = 0;
+	/* CHI Anchor: Stop Send/Receive */
+	val = D_DTS_VI | D_DTS_VO | D_DTS_INS |
+	      D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16);
+	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+	dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
+	dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
+
+	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, 0x17);
+	dbri->regs->reg8 = (int)dbri->cmd;
+
+#if 0      
+	dbri->mm.ctrl[0] |= CS4215_CLB;
+	dbri->cmd[n++] = DBRI_CMD(D_SSP, 1, D_PIPE(D_P_17));
+	dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4);
+
+	/* Setup DBRI for CHI Slave */
+	dbri->cmd[n++] = DBRI_CMD(D_CDM, 1, D_CDM_XCE|D_CDM_REN);
+	dbri->cmd[n++] = DBRI_CMD(D_CHI, 1, D_CHI_CHICM(1) | D_CHI_FD |
+					D_CHI_IR | D_CHI_EN);
+	dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 1, 0x16);
+	dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+
+	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, 0x17);
+	dbri->regs->reg8 = (int)dbri->cmd;
+
+	dbri->regs->reg2 = D_ENPIO | D_PIO3 |
+				(dbri->mm.onboard ? D_PIO0 : D_PIO2);
+#endif
+
+	/* We are ready */
+	dbri_cmdlocked = 0;
+	wake_up(&dbri->wait);
+}
+
+static int mmcodec_init(struct sparcaudio_driver *drv)
+{
+	struct dbri *dbri = (struct dbri *)drv->private;
+	int reg2 = dbri->regs->reg2;
+
+
+	/* Look for the cs4215 chips */
+	if(reg2 & D_PIO2) {
+		dprintk(D_MM, ("DBRI: Onboard CS4215 detected\n"));
+		dbri->mm.onboard = 1;
+	}
+	if(reg2 & D_PIO0) {
+		dprintk(D_MM, ("DBRI: Speakerbox detected\n"));
+		dbri->mm.onboard = 0;
+	}
+	
+
+	/* Using the Speakerbox, if both are attached.  */
+	if((reg2 & D_PIO2) && (reg2 & D_PIO0)) {
+		printk("DBRI: Using speakerbox / ignoring onboard mmcodec.\n");
+		dbri->regs->reg2 = D_ENPIO2;
+		dbri->mm.onboard = 0;
+	}
+	if( !(reg2 & (D_PIO0|D_PIO2)) ) {
+		printk("DBRI: no mmcodec found.\n");
+		return -EIO;
+	}
+
+
+	/* Now talk to our baby */
+	dbri->regs->reg0 |= D_C;	/* Enable CHI */
+
+	mmcodec_default(&dbri->mm);
+
+	dbri->mm.version = 0xff;
+	mmcodec_setctrl(dbri);
+	if(dbri->mm.version == 0xff) 
+		return -EIO;
+
+	/*
+	mmcodec_init_data(dbri, &n);
+	*/
+
+	return 0;
+}
+
+void dbri_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
+	struct dbri *dbri = (struct dbri *)drv->private;
+	int x, val;
+	static int numint = 0;
+	
+	/*
+	 * Read it, so the interrupt goes away.
+	 */
+	x = dbri->regs->reg1;
+	if(numint++ > 20) {
+	    dbri->regs->reg0 = D_R; /* Soft Reset */
+	    numint = 0;
+	    printk("Soft reset\n");
+	}
+
+	if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) {
+		/*
+		 * What should I do here ?
+		 */
+		if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n");
+		if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n");
+		if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n");
+		if(x & D_MBE) printk("DBRI: Burst Error on SBus\n");
+	}
+
+	if (!(x & D_IR))	/* Not for us */
+		return;
+
+	x = dbri->intr[dbri->dbri_irqp];
+	while (x != 0) {
+		dbri->intr[dbri->dbri_irqp] = 0;
+
+		if(D_INTR_GETCHAN(x) == D_INTR_CMD) {
+			dprintk(D_INT,("DBRI: INTR: Command: %-5s  Value:%d\n",
+				cmds[D_INTR_GETCMD(x)], D_INTR_GETVAL(x)));
+		} else {
+			dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n",
+				D_INTR_GETCHAN(x), D_INTR_GETCODE(x),
+				D_INTR_GETRVAL(x)));
+		}
+
+		val = D_INTR_GETVAL(x);
+
+		switch(D_INTR_GETCHAN(x)) {
+			case D_INTR_CMD:
+				if(D_INTR_GETCMD(x) == D_WAIT)
+					if(val == WAIT_INTR1) {
+						dbri_cmdlocked = 0;
+						wake_up(&dbri->wait);
+					}
+					if(val == WAIT_INTR2)
+						wake_up(&dbri->int_wait);
+				break;
+			case D_P_18:
+				if(val != 0) {
+					x = reverse_bytes(val,2)&CS4215_12_MASK;
+printk("Comparing int: %x with hi(%x)\n", x, *(int *)dbri->mm.ctrl);
+					if(x == (*(int *)dbri->mm.ctrl >> 16))
+{
+printk("Comp ok\n");
+						wake_up(&dbri->int_wait);
+}
+				}
+				break;
+			case D_P_19:
+				if(val != 0) {
+					dbri->mm.version = 
+						reverse_bytes(val, 1) & 0xf;
+				}
+				break;
+		}
+
+		dbri->dbri_irqp++;
+		if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK))
+			dbri->dbri_irqp = 1;
+		else if ((dbri->dbri_irqp & (DBRI_INT_BLK-1)) == 0)
+			dbri->dbri_irqp++;
+		x = dbri->intr[dbri->dbri_irqp];
+	}
+}
+
+
+
+
+
+static int dbri_attach(struct sparcaudio_driver *drv, 
+			 struct linux_sbus_device *sdev)
+{
+	struct dbri *dbri;
+	struct linux_prom_irqs irq;
+	int err;
+
+	if (sdev->prom_name[9] < 'e') {
+		printk(KERN_ERR "DBRI: unsupported chip version %c found.\n",
+			sdev->prom_name[9]);
+		return -EIO;
+	}
+
+	drv->ops = &dbri_ops;
+	drv->private = kmalloc(sizeof(struct dbri), GFP_KERNEL);
+	if (!drv->private)
+		return -ENOMEM;
+	dbri = (struct dbri *)drv->private;
+
+        memset(dbri, 0, sizeof(*dbri));
+
+	dbri->dbri_version = sdev->prom_name[9];
+
+	/* Map the registers into memory. */
+	prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0], 
+		sdev->num_registers, sdev);
+	dbri->regs_size = sdev->reg_addrs[0].reg_size;
+	dbri->regs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
+		sdev->reg_addrs[0].reg_size, 
+		drv_name, sdev->reg_addrs[0].which_io, 0);
+	if (!dbri->regs) {
+		printk(KERN_ERR "DBRI: could not allocate registers\n");
+		kfree(drv->private);
+		return -EIO;
+	}
+
+	prom_getproperty(sdev->prom_node, "intr", (char *)&irq, sizeof(irq));
+	dbri->irq = irq.pri;
+
+	err = request_irq(dbri->irq, dbri_intr, SA_SHIRQ, "DBRI/audio", drv);
+	if (err) {
+		printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
+		sparc_free_io(dbri->regs, dbri->regs_size);
+		kfree(drv->private);
+		return err;
+	}
+
+	/* Register ourselves with the midlevel audio driver. */
+	err = register_sparcaudio_driver(drv);
+	if (err) {
+		printk(KERN_ERR "DBRI: unable to register audio\n");
+		free_irq(dbri->irq, drv);
+		sparc_free_io(dbri->regs, dbri->regs_size);
+		kfree(drv->private);
+		return err;
+	}
+
+	dbri_init(drv);
+	err = mmcodec_init(drv);
+	if(err) {
+		dbri_detach(drv);
+		return err;
+	}
+	  
+
+	dbri->perchip_info.play.active   = dbri->perchip_info.play.pause = 0;
+	dbri->perchip_info.record.active = dbri->perchip_info.record.pause = 0;
+
+	printk(KERN_INFO "audio%d at 0x%lx (irq %d) is DBRI(%c)+CS4215(%d)\n",
+	       num_drivers, (unsigned long)dbri->regs,
+	       dbri->irq, dbri->dbri_version, dbri->mm.version);
+	
+	return 0;
+}
+
+/* Probe for the dbri chip and then attach the driver. */
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(int dbri_init(void))
+#endif
+{
+	struct linux_sbus *bus;
+	struct linux_sbus_device *sdev;
+  
+	num_drivers = 0;
+  
+	/* Probe each SBUS for the DBRI chip(s). */
+	for_all_sbusdev(sdev,bus) {
+		/*
+		 * The version is coded in the last character
+		 */
+		if (!strncmp(sdev->prom_name, "SUNW,DBRI", 9)) {
+      			dprintk(D_GEN, ("DBRI: Found %s in SBUS slot %d\n",
+				sdev->prom_name, sdev->slot));
+			if (num_drivers >= MAX_DRIVERS) {
+				printk("DBRI: Ignoring slot %d\n", sdev->slot);
+				continue;
+			}
+	      
+			if (dbri_attach(&drivers[num_drivers], sdev) == 0)
+				num_drivers++;
+		}
+	}
+  
+	return (num_drivers > 0) ? 0 : -EIO;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+        register int i;
+
+        for (i = 0; i < num_drivers; i++) {
+                dbri_detach(&drivers[i]);
+                num_drivers--;
+        }
+}
+#endif
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/audio/dbri.h linux/drivers/sbus/audio/dbri.h
--- v2.1.78/linux/drivers/sbus/audio/dbri.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/audio/dbri.h	Mon Jan 12 15:15:45 1998
@@ -0,0 +1,293 @@
+/*
+ * drivers/sbus/audio/cs4231.h
+ *
+ * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
+ */
+
+#ifndef _DBRI_H_
+#define _DBRI_H_
+
+#include <linux/types.h>
+
+struct dbri_regs {
+  __volatile__ __u32 reg0;		/* Status & Control */
+  __volatile__ __u32 reg1;		/* Mode & Interrupt */
+  __volatile__ __u32 reg2;		/* Parallel IO */
+  __volatile__ __u32 reg3;		/* Test */
+  __volatile__ __u32 unused[4];
+  __volatile__ __u32 reg8;		/* Command Queue Pointer */
+  __volatile__ __u32 reg9;		/* Interrupt Queue Pointer */
+};
+
+#define DBRI_NO_CMDS	64
+#define DBRI_NO_INTS	2
+#define DBRI_INT_BLK	64
+
+#define DBRI_MM_ONB	1
+#define DBRI_MM_SB	2
+
+struct dbri_mem {
+	__u32	flags;
+	__u32	ba;			/* Transmit/Receive Buffer Address */
+	__u32	nda;			/* Next Descriptor Address */
+	__u32	status;
+};
+
+#include "cs4215.h"
+
+/* This structure holds the information for both chips (DBRI & CS4215) */
+struct dbri {
+  int regs_size, irq;				/* Needed for unload */
+
+  struct dbri_regs *regs;			/* dbri HW regs */
+  int dbri_version;				/* 'e' and up is OK */
+  int dbri_irqp;				/* intr queue pointer */
+  __volatile__ int cmd[DBRI_NO_CMDS];		/* Place for commands */
+  __volatile__ int intr[DBRI_NO_INTS * DBRI_INT_BLK];	/* Interrupt field */
+  
+  struct cs4215 mm;				/* mmcodec special info */
+ 
+  struct wait_queue *wait, *int_wait;		/* Where to sleep if busy */
+  struct audio_info perchip_info;
+};
+
+
+/* DBRI Reg0 - Status Control Register - defines. (Page 17) */
+#define D_P		(1<<15)	/* Program command & queue pointer valid */
+#define D_G		(1<<14)	/* Allow 4-Word SBus Burst */
+#define D_S		(1<<13)	/* Allow 16-Word SBus Burst */
+#define D_E		(1<<12)	/* Allow 8-Word SBus Burst */
+#define D_X		(1<<7)	/* Sanity Timer Disable */
+#define D_T		(1<<6)	/* Permit activation of the TE interface */
+#define D_N		(1<<5)	/* Permit activation of the NT interface */
+#define D_C		(1<<4)	/* Permit activation of the CHI interface */
+#define D_F		(1<<3)	/* Force Sanity Timer Time-Out */
+#define D_D		(1<<2)	/* Disable Master Mode */
+#define D_H		(1<<1)	/* Halt for Analysis */
+#define D_R		(1<<0)	/* Soft Reset */
+
+
+/* DBRI Reg1 - Mode and Interrupt Register - defines. (Page 18) */
+#define D_LITTLE_END	(1<<8)	/* Byte Order */
+#define D_BIG_END	(0<<8)	/* Byte Order */
+#define D_MRR		(1<<4)	/* Multiple Error Ack on SBus (readonly) */
+#define D_MLE		(1<<3)	/* Multiple Late Error on SBus (readonly) */
+#define D_LBG		(1<<2)	/* Lost Bus Grant on SBus (readonly) */
+#define D_MBE		(1<<1)	/* Burst Error on SBus (readonly) */
+#define D_IR		(1<<0)	/* Interrupt Indicator (readonly) */
+
+
+/* DBRI Reg2 - Parallel IO Register - defines. (Page 18) */
+#define D_ENPIO3	(1<<7)	/* Enable Pin 3 */
+#define D_ENPIO2	(1<<6)	/* Enable Pin 2 */
+#define D_ENPIO1	(1<<5)	/* Enable Pin 1 */
+#define D_ENPIO0	(1<<4)	/* Enable Pin 0 */
+#define D_ENPIO		(0xf0)	/* Enable all the pins */
+#define D_PIO3		(1<<3)	/* Pin 3: 1: Data mode, 0: Ctrl mode */
+#define D_PIO2		(1<<2)	/* Pin 2: 1: Onboard PDN */
+#define D_PIO1		(1<<1)	/* Pin 1: 0: Reset */
+#define D_PIO0		(1<<0)	/* Pin 0: 1: Speakerbox PDN */
+
+
+/* DBRI Commands (Page 20) */
+#define D_WAIT		0x0	/* Stop execution */
+#define D_PAUSE		0x1	/* Flush long pipes */
+#define D_JUMP		0x2	/* New command queue */
+#define D_IIQ		0x3	/* Initialize Interrupt Queue */
+#define D_REX		0x4	/* Report command execution via interrupt */
+#define D_SDP		0x5	/* Setup Data Pipe */
+#define D_CDP		0x6	/* Continue Data Pipe (reread NULL Pointer) */
+#define D_DTS		0x7	/* Define Time Slot */
+#define D_SSP		0x8	/* Set short Data Pipe */
+#define D_CHI		0x9	/* Set CHI Global Mode */
+#define D_NT		0xa	/* NT Command */
+#define D_TE		0xb	/* TE Command */
+#define D_CDEC		0xc	/* Codec setup */
+#define D_TEST		0xd	/* No comment */
+#define D_CDM		0xe	/* CHI Data mode command */
+
+#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (intr << 27) | value)
+
+
+/* Special bits for some commands */
+#define D_PIPE(v)	(v<<0)	/* Pipe Nr: 0-15 long, 16-21 short */
+
+/* Setup Data Pipe */
+/* IRM */
+#define D_SDP_2SAME	(1<<18) /* Report 2nd time in a row value rcvd*/
+#define D_SDP_CHANGE	(2<<18) /* Report any changes */
+#define D_SDP_EVERY	(3<<18) /* Report any changes */
+#define D_SDP_EOL	(1<<17) /* EOL interrupt enable */
+#define D_SDP_IDLE	(1<<16) /* HDLC idle interrupt enable */
+
+/* Pipe data MODE */
+#define D_SDP_MEM	(0<<13)	/* To/from memory */
+#define D_SDP_HDLC	(2<<13)
+#define D_SDP_HDLC_D	(3<<13)	/* D Channel (prio control)*/
+#define D_SDP_SER	(4<<13)	/* Serial to serial */
+#define D_SDP_FIXED	(6<<13)	/* Short only */
+
+#define D_SDP_TO_SER	(1<<12)	/* Direction */
+#define D_SDP_FROM_SER	(0<<12)	/* Direction */
+#define D_SDP_MSB	(1<<11)	/* Bit order within Byte */
+#define D_SDP_LSB	(0<<11)	/* Bit order within Byte */
+#define D_SDP_P		(1<<10)	/* Pointer Valid */
+#define D_SDP_A		(1<<8)	/* Abort */
+#define D_SDP_C		(1<<7)	/* Clear */
+
+/* Define Time Slot */
+#define D_DTS_VI	(1<<17) /* Valid Input Time-Slot Descriptor */
+#define D_DTS_VO	(1<<16) /* Valid Output Time-Slot Descriptor */
+#define D_DTS_INS	(1<<15) /* Insert Time Slot */
+#define D_DTS_DEL	(0<<15) /* Delete Time Slot */
+#define D_DTS_PRVIN(v)	(v<<10) /* Previous In Pipe */
+#define D_DTS_PRVOUT(v)	(v<<5)  /* Previous Out Pipe */
+
+/* Time Slot defines */
+#define D_TS_LEN(v)	(v<<24)	/* Number of bits in this time slot */
+#define D_TS_CYCLE(v)	(v<<14)	/* Bit Count at start of TS */
+#define D_TS_DI(v)	(1<<13)	/* Data Invert */
+#define D_TS_1CHANNEL	(0<<10)	/* Single Channel / Normal mode */
+#define D_TS_MONITOR	(2<<10)	/* Monitor pipe */
+#define D_TS_NONCONTIG	(3<<10) /* Non contiguous mode */
+#define D_TS_ANCHOR	(7<<10) /* Starting short pipes */
+#define D_TS_MON(v)	(v<<5)	/* Monitor Pipe */
+#define D_TS_NEXT(v)	(v<<0)	/* Pipe Nr: 0-15 long, 16-21 short */
+
+/* Concentration Highway Interface Modes */
+#define D_CHI_CHICM(v)	(v<<16)	/* Clock mode */
+#define D_CHI_IR	(1<<15) /* Immediate Interrupt Report */
+#define D_CHI_EN	(1<<14) /* CHIL Interrupt enabled */
+#define D_CHI_OD	(1<<13) /* Open Drain Enable */
+#define D_CHI_FE	(1<<12) /* Sample CHIFS on Rising Frame Edge */
+#define D_CHI_FD	(1<<11) /* Frame Drive */
+#define D_CHI_BPF(v)	(v<<0)	/* Bits per Frame */
+
+/* NT: These are here for completeness */
+#define D_NT_FBIT	(1<<17)	/* Frame Bit */
+#define D_NT_NBF	(1<<16)	/* Number of bad frames to loose framing */
+#define D_NT_IRM_IMM	(1<<15)	/* Interrupt Report & Mask: Immediate */
+#define D_NT_IRM_EN	(1<<14)	/* Interrupt Report & Mask: Enable */
+#define D_NT_ISNT	(1<<13)	/* Configfure interface as NT */
+#define D_NT_FT		(1<<12)	/* Fixed Timing */
+#define D_NT_EZ		(1<<11)	/* Echo Channel is Zeros */
+#define D_NT_IFA	(1<<10)	/* Inhibit Final Activation */
+#define D_NT_ACT	(1<<9)	/* Activate Interface */
+#define D_NT_MFE	(1<<8)	/* Multiframe Enable */
+#define D_NT_RLB(v)	(1<<5)	/* Remote Loopback */
+#define D_NT_LLB(v)	(1<<2)	/* Local Loopback */
+#define D_NT_FACT	(1<<1)	/* Force Activation */
+#define D_NT_ABV	(1<<0)	/* Activate Bipolar Violation */
+
+/* Codec Setup */
+#define D_CDEC_CK(v)	(v<<24)	/* Clock Select */
+#define D_CDEC_FED(v)	(v<<12)	/* FSCOD Falling Edge Delay */
+#define D_CDEC_RED(v)	(v<<0)	/* FSCOD Rising Edge Delay */
+
+/* Test */
+#define D_TEST_RAM(v)	(v<<16)	/* RAM Pointer */
+#define D_TEST_SIZE(v)	(v<<11)	/* */
+#define D_TEST_ROMONOFF	0x5	/* Toggle ROM opcode monitor on/off */
+#define D_TEST_PROC	0x6	/* MicroProcessor test */
+#define D_TEST_SER	0x7	/* Serial-Controller test */
+#define D_TEST_RAMREAD	0x8	/* Copy from Ram to system memory */
+#define D_TEST_RAMWRITE	0x9	/* Copy into Ram from system memory */
+#define D_TEST_RAMBIST	0xa	/* RAM Built-In Self Test */
+#define D_TEST_MCBIST	0xb	/* Microcontroller Built-In Self Test */
+#define D_TEST_DUMP	0xe	/* ROM Dump */
+
+/* CHI Data Mode */
+#define D_CDM_THI	(1<<8)	/* Transmit Data on CHIDR Pin */
+#define D_CDM_RHI	(1<<7)	/* Receive Data on CHIDX Pin */
+#define D_CDM_RCE	(1<<6)	/* Receive on Rising Edge of CHICK */
+#define D_CDM_XCE	(1<<2)	/* Transmit Data on Rising Edge of CHICK */
+#define D_CDM_XEN	(1<<1)	/* Transmit Highway Enable */
+#define D_CDM_REN	(1<<0)	/* Receive Highway Enable */
+
+/* The Interrupts */
+#define D_INTR_BRDY	1	/* Buffer Ready for processing */
+#define D_INTR_MINT	2	/* Marked Interrupt in RD/TD */
+#define D_INTR_IBEG	3	/* Flag to idle transition detected (HDLC) */
+#define D_INTR_IEND	4	/* Idle to flag transition detected (HDLC) */
+#define D_INTR_EOL	5	/* End of List */
+#define D_INTR_CMDI	6	/* Command has bean read */
+#define D_INTR_XCMP	8	/* Transmission of frame complete */
+#define D_INTR_SBRI	9	/* BRI status change info */
+#define D_INTR_FXDT	10	/* Fixed data change */
+#define D_INTR_CHIL	11	/* CHI lost frame sync (channel 36 only) */
+#define D_INTR_COLL	11	/* Unrecoverable D-Channel collision */
+#define D_INTR_DBYT	12	/* Dropped by frame slip */
+#define D_INTR_RBYT	13	/* Repeated by frame slip */
+#define D_INTR_LINT	14	/* Lost Interrupt */
+#define D_INTR_UNDR	15	/* DMA underrun */
+
+#define D_INTR_TE	32
+#define D_INTR_NT	34
+#define D_INTR_CHI	36
+#define D_INTR_CMD	38
+
+#define D_INTR_GETCHAN(v)	((v>>24) & 0x3f)
+#define D_INTR_GETCODE(v)	((v>>20) & 0xf)
+#define D_INTR_GETCMD(v)	((v>>16) & 0xf)
+#define D_INTR_GETVAL(v)	(v & 0xffff)
+#define D_INTR_GETRVAL(v)	(v & 0xfffff)
+
+#define D_P_0		0	/* TE receive anchor */
+#define D_P_1		1	/* TE transmit anchor */
+#define D_P_2		2	/* NT transmit anchor */
+#define D_P_3		3	/* NT receive anchor */
+#define D_P_4		4	/* CHI send data */
+#define D_P_5		5	/* CHI receive data */
+#define D_P_6		6	/* */
+#define D_P_7		7	/* */
+#define D_P_8		8	/* */
+#define D_P_9		9	/* */
+#define D_P_10		10	/* */
+#define D_P_11		11	/* */
+#define D_P_12		12	/* */
+#define D_P_13		13	/* */
+#define D_P_14		14	/* */
+#define D_P_15		15	/* */
+#define D_P_16		16	/* CHI anchor pipe */
+#define D_P_17		17	/* CHI send */
+#define D_P_18		18	/* CHI receive */
+#define D_P_19		19	/* CHI receive */
+#define D_P_20		20	/* CHI receive */
+#define D_P_21		21	/* */
+#define D_P_22		22	/* */
+#define D_P_23		23	/* */
+#define D_P_24		24	/* */
+#define D_P_25		25	/* */
+#define D_P_26		26	/* */
+#define D_P_27		27	/* */
+#define D_P_28		28	/* */
+#define D_P_29		29	/* */
+#define D_P_30		30	/* */
+#define D_P_31		31	/* */
+
+
+/* Transmit descriptor defines */
+#define DBRI_TD_F	(1<<31)	/* End of Frame */
+#define DBRI_TD_D	(1<<31)	/* Do not append CRC */
+#define DBRI_TD_CNT(v)	(v<<16)	/* Number of valid bytes in the buffer */
+#define DBRI_TD_B	(1<<15)	/* Final interrupt */
+#define DBRI_TD_M	(1<<14)	/* Marker interrupt */
+#define DBRI_TD_I	(1<<13)	/* Transmit Idle Characters */
+#define DBRI_TD_FCNT(v)	v	/* Flag Count */
+#define DBRI_TD_UNR	(1<<3)	/* Underrun: transmitter is out of data */
+#define DBRI_TD_ABT	(1<<2)	/* Abort: frame aborted */
+#define DBRI_TD_TBC	(1<<0)	/* Transmit buffer Complete */
+
+/* Receive descriptor defines */
+#define DBRI_RD_F	(1<<31)	/* End of Frame */
+#define DBRI_RD_C	(1<<30)	/* Completed buffer */
+#define DBRI_RD_B	(1<<15)	/* Final interrupt */
+#define DBRI_RD_M	(1<<14)	/* Marker interrupt */
+#define DBRI_RD_CNT(v)	(v<<16)	/* Number of valid bytes in the buffer */
+#define DBRI_RD_BCNT(v)	v	/* Buffer size */
+#define DBRI_RD_CRC	(1<<7)	/* 0: CRC is correct */
+#define DBRI_RD_BBC	(1<<6)	/* 1: Bad Byte recieved */
+#define DBRI_RD_ABT	(1<<5)	/* Abort: frame aborted */
+#define DBRI_RD_OVRN	(1<<3)	/* Overrun: data lost */
+
+#endif /* _DBRI_H_ */
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/Config.in linux/drivers/sbus/char/Config.in
--- v2.1.78/linux/drivers/sbus/char/Config.in	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/Config.in	Mon Jan 12 15:15:45 1998
@@ -19,6 +19,7 @@
 		fbs=$fbs$SUN_FB_CGFOURTEEN
 		fbs=$fbs$SUN_FB_LEO
 		fbs=$fbs$TADPOLE_FB_WEITEK
+		fbs=$fbs$SUN_FB_CREATOR
 		if [ "$fbs" = "nnnnnnnn" ]; then
 			echo "Warning: You have excluded ALL FB Support"
 			echo "Notice:  Enabling Generic AutoResolution"
@@ -41,7 +42,10 @@
 comment 'Misc Linux/SPARC drivers'
 tristate '/dev/openprom device support' CONFIG_SUN_OPENPROMIO
 tristate 'Mostek real time clock support' CONFIG_SUN_MOSTEK_RTC
-tristate 'Siemens SAB82532 serial support' CONFIG_SAB82532
+if [ "$ARCH" = "sparc64" ]; then
+	tristate 'Siemens SAB82532 serial support' CONFIG_SAB82532
+	tristate 'OBP Flash Device support' CONFIG_OBP_FLASH
+fi
 
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   tristate 'Bidirectional parallel port support (EXPERIMENTAL)' CONFIG_SUN_BPP
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/Makefile linux/drivers/sbus/char/Makefile
--- v2.1.78/linux/drivers/sbus/char/Makefile	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/Makefile	Mon Jan 12 15:15:45 1998
@@ -51,13 +51,35 @@
 
 O_TARGET := sunchar.o
 O_OBJ    := ${FB_OBJS} suncons.o sbuscons.o pcicons.o sunfb.o
-O_OBJS   := ${O_OBJ} sunkbd.o sunkeymap.o sunmouse.o sunserial.o zs.o
+O_OBJS   := ${O_OBJ} sunkbd.o sunkbdmap.o sunmouse.o sunserial.o zs.o
 M_OBJS   :=
 
 ifeq ($(ARCH),sparc64)
+
+ifeq ($(CONFIG_PCI),y)
+
 O_OBJS += su.o pcikbd.o
+
+ifeq ($(CONFIG_SAB82532),y)
+O_OBJS += sab82532.o
+else
+  ifeq ($(CONFIG_SAB82532),m)
+  M_OBJS += sab82532.o
+  endif
 endif
 
+endif # eq($(CONFIG_PCI,y)
+
+ifeq ($(CONFIG_OBP_FLASH),y)
+O_OBJS += flash.o
+else
+  ifeq ($(CONFIG_OBP_FLASH),m)
+  M_OBJS += flash.o
+  endif
+endif
+
+endif # eq($(ARCH),sparc64)
+
 ifeq ($(CONFIG_SUN_OPENPROMIO),y)
 O_OBJS += openprom.o
 else
@@ -90,14 +112,6 @@
   endif
 endif
 
-ifeq ($(CONFIG_SAB82532),y)
-O_OBJS += sab82532.o
-else
-  ifeq ($(CONFIG_SAB82532),m)
-  M_OBJS += sab82532.o
-  endif
-endif
-
 # Add PCI console/fb drivers here.
 #
 ifeq ($(CONFIG_PCI),y)
@@ -105,6 +119,8 @@
 endif
 
 include $(TOPDIR)/Rules.make
+
+sunkbdmap.o: sunkeymap.c
 
 vfc.o: vfc_dev.o vfc_i2c.o
 	$(LD) -r -o vfc.o vfc_dev.o vfc_i2c.o
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/creator.c linux/drivers/sbus/char/creator.c
--- v2.1.78/linux/drivers/sbus/char/creator.c	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/creator.c	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: creator.c,v 1.12 1997/08/25 07:50:27 jj Exp $
+/* $Id: creator.c,v 1.13 1997/10/17 04:14:40 davem Exp $
  * creator.c: Creator/Creator3D frame buffer driver
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -194,7 +194,7 @@
 	unsigned long map_offset = 0;
 	int i;
 	int alignment;
-	struct vm_area_struct *vmm;
+	struct vm_area_struct *vmm = NULL;
 
 	size = vma->vm_end - vma->vm_start;
         if (vma->vm_offset & ~PAGE_MASK)
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/flash.c linux/drivers/sbus/char/flash.c
--- v2.1.78/linux/drivers/sbus/char/flash.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/flash.c	Mon Jan 12 15:15:45 1998
@@ -0,0 +1,232 @@
+/* $Id: flash.c,v 1.5 1997/11/01 10:22:13 ecd Exp $
+ * flash.c: Allow mmap access to the OBP Flash, for OBP updates.
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/sbus.h>
+#include <asm/ebus.h>
+
+static struct {
+	unsigned long read_base;
+	unsigned long write_base;
+	unsigned long read_size;
+	unsigned long write_size;
+	unsigned long busy;
+} flash;
+
+#define FLASH_MINOR	152
+
+static int
+flash_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	unsigned long addr;
+	unsigned long size;
+
+	if (vma->vm_offset & ~(PAGE_MASK))
+		return -ENXIO;
+
+	if (flash.read_base == flash.write_base) {
+		addr = __pa(flash.read_base);
+		size = flash.read_size;
+	} else {
+		if ((vma->vm_flags & VM_READ) &&
+		    (vma->vm_flags & VM_WRITE))
+			return -EINVAL;
+
+		if (vma->vm_flags & VM_READ) {
+			addr = __pa(flash.read_base);
+			size = flash.read_size;
+		} else if (vma->vm_flags & VM_WRITE) {
+			addr = __pa(flash.write_base);
+			size = flash.write_size;
+		} else
+			return -ENXIO;
+	}
+
+	if (vma->vm_offset > size)
+		return -ENXIO;
+	addr += vma->vm_offset;
+
+	if (vma->vm_end - (vma->vm_start + vma->vm_offset) > size)
+		size = vma->vm_end - (vma->vm_start + vma->vm_offset);
+
+	pgprot_val(vma->vm_page_prot) &= ~(_PAGE_CACHE);
+	pgprot_val(vma->vm_page_prot) |= _PAGE_E;
+	vma->vm_flags |= (VM_SHM | VM_LOCKED);
+
+	if (remap_page_range(vma->vm_start, addr, size, vma->vm_page_prot))
+		return -EAGAIN;
+	vma->vm_dentry = dget(file->f_dentry);
+	return 0;
+}
+
+static long long
+flash_llseek(struct file *file, long long offset, int origin)
+{
+	switch (origin) {
+		case 0:
+			file->f_pos = offset;
+			break;
+		case 1:
+			file->f_pos += offset;
+			if (file->f_pos > flash.read_size)
+				file->f_pos = flash.read_size;
+			break;
+		case 2:
+			file->f_pos = flash.read_size;
+			break;
+		default:
+			return -EINVAL;
+	}
+	return file->f_pos;
+}
+
+static ssize_t
+flash_read(struct file * file, char * buf,
+	   size_t count, loff_t *ppos)
+{
+	unsigned long p = file->f_pos;
+	
+	if (count > flash.read_size - p)
+		count = flash.read_size - p;
+
+	if (copy_to_user(buf, flash.read_base + p, count) < 0)
+		return -EFAULT;
+
+	file->f_pos += count;
+	return count;
+}
+
+static int
+flash_open(struct inode *inode, struct file *file)
+{
+	if (test_and_set_bit(0, (void *)&flash.busy) != 0)
+		return -EBUSY;
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int
+flash_release(struct inode *inode, struct file *file)
+{
+	MOD_DEC_USE_COUNT;
+	flash.busy = 0;
+	return 0;
+}
+
+static struct file_operations flash_fops = {
+	flash_llseek,
+	flash_read,
+	NULL,		/* no write to the Flash, use mmap
+			 * and play flash dependant tricks.
+			 */
+	NULL,		/* readdir */
+	NULL,		/* poll */
+	NULL,		/* ioctl */
+	flash_mmap,
+	flash_open,
+	flash_release
+};
+
+static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops };
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(int flash_init(void))
+#endif
+{
+	struct linux_sbus *sbus;
+	struct linux_sbus_device *sdev = 0;
+	struct linux_ebus *ebus;
+	struct linux_ebus_device *edev = 0;
+	struct linux_prom_registers regs[2];
+	int len, err;
+
+	for_all_sbusdev(sdev, sbus) {
+		if (!strcmp(sdev->prom_name, "flashprom")) {
+			prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0],
+					       sdev->num_registers, sdev);
+			if (sdev->reg_addrs[0].phys_addr == sdev->reg_addrs[1].phys_addr) {
+				flash.read_base = (unsigned long)sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
+								 sdev->reg_addrs[0].reg_size, "flashprom",
+								 sdev->reg_addrs[0].which_io, 0);
+				flash.read_size = sdev->reg_addrs[0].reg_size;
+				flash.write_base = flash.read_base;
+				flash.write_size = flash.read_size;
+			} else {
+				flash.read_base = (unsigned long)sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
+								 sdev->reg_addrs[0].reg_size, "flashprom",
+								 sdev->reg_addrs[0].which_io, 0);
+				flash.read_size = sdev->reg_addrs[0].reg_size;
+				flash.write_base = (unsigned long)sparc_alloc_io(sdev->reg_addrs[1].phys_addr, 0,
+								 sdev->reg_addrs[1].reg_size, "flashprom",
+								 sdev->reg_addrs[1].which_io, 0);
+				flash.write_size = sdev->reg_addrs[1].reg_size;
+			}
+			flash.busy = 0;
+			break;
+		}
+	}
+	if (!sdev) {
+#ifdef CONFIG_PCI
+		for_all_ebusdev(edev, ebus)
+			if (!strcmp(edev->prom_name, "flashprom"))
+				break;
+		if (!edev)
+			return -ENODEV;
+
+		len = prom_getproperty(edev->prom_node, "reg", (void *)regs, sizeof(regs));
+		if (len != sizeof(regs)) {
+			printk("flash: Strange reg property size %d\n", len);
+			return -ENODEV;
+		}
+
+		flash.read_base = edev->base_address[0];
+		flash.read_size = regs[0].reg_size;
+		flash.write_base = edev->base_address[1];
+		flash.write_size = regs[1].reg_size;
+		flash.busy = 0;
+
+#else
+		return -ENODEV;
+#endif
+	}
+
+	printk("OBP Flash: RD %lx[%lx] WR %lx[%lx]\n",
+	       __pa(flash.read_base), flash.read_size,
+	       __pa(flash.write_base), flash.write_size);
+
+	err = misc_register(&flash_dev);
+	if (err) {
+		printk(KERN_ERR "flash: unable to get misc minor\n");
+		return err;
+	}
+
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	misc_deregister(&flash_dev);
+}
+#endif
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/mach64.c linux/drivers/sbus/char/mach64.c
--- v2.1.78/linux/drivers/sbus/char/mach64.c	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/mach64.c	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: mach64.c,v 1.8 1997/08/25 07:50:34 jj Exp $
+/* $Id: mach64.c,v 1.11 1997/10/17 04:13:35 davem Exp $
  * mach64.c: Ultra/PCI Mach64 console driver.
  *
  * Just about all of this is from the PPC/mac driver, see that for
@@ -22,13 +22,18 @@
 #include <asm/pbm.h>
 #include <asm/fbio.h>
 #include <asm/sbus.h>
+#include <asm/pgtable.h>
 
 #include "pcicons.h"
 #include "mach64.h"
 #include "fb.h"
 
+static unsigned int mach64_pci_membase;
+static unsigned int mach64_pci_iobase;
+
+#define MACH64_LE_FBOFF	0x000000
 #define MACH64_REGOFF	0x7ffc00
-#define MACH64_FBOFF	0x800000
+#define MACH64_BE_FBOFF	0x800000
 
 static inline void mach64_waitq(int entries)
 {
@@ -69,7 +74,35 @@
 mach64_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma,
 	    long base, fbinfo_t *fb)
 {
-	return -ENOSYS;
+	unsigned long addr, size;
+
+	size = vma->vm_end - vma->vm_start;
+	if (vma->vm_offset & ~PAGE_MASK)
+		return -ENXIO;
+
+	if (vma->vm_offset == mach64_pci_iobase) {
+		addr = __pa(pcivga_iobase);
+		size = PAGE_SIZE;
+	} else if (vma->vm_offset >= (mach64_pci_membase + 0x800000)) {
+		addr = __pa(pcivga_membase) - mach64_pci_membase
+			+ vma->vm_offset;
+		pgprot_val(vma->vm_page_prot) |= _PAGE_IE;
+	} else if (vma->vm_offset >= mach64_pci_membase) {
+		addr = __pa(pcivga_membase) - mach64_pci_membase
+			+ vma->vm_offset;
+	} else {
+		return -EINVAL;
+	}
+
+	pgprot_val(vma->vm_page_prot) &= ~(_PAGE_CACHE);
+	pgprot_val(vma->vm_page_prot) |= _PAGE_E;
+	vma->vm_flags |= (VM_SHM | VM_LOCKED);
+
+	if (remap_page_range(vma->vm_start, addr, size, vma->vm_page_prot))
+		return -EAGAIN;
+
+	vma->vm_dentry = dget(file->f_dentry);
+	return 0;
 }
 
 static void
@@ -113,11 +146,29 @@
 
 static struct mach64_info mach64;
 
+void mach64_test(fbinfo_t *fb)
+{
+	unsigned int x;
+	int i;
+
+	for (i = 0; i < mach64.total_vram; i += 4)
+		writel(i, pcivga_membase + i);
+
+	for (i = 0; i < mach64.total_vram; i += 4)
+		if ((x = readl(pcivga_membase + i)) != i) {
+			printk("vga mem read error @ %08x: exp %x, rd %x\n",
+			       i, i, x);
+			i = (i & ~(0xffff)) + 0x10000;
+		}
+}
+
 int mach64_init(fbinfo_t *fb)
 {
 	struct pci_dev *pdev;
 	struct pcidev_cookie *cookie;
+	struct linux_pbm_info *pbm;
 	unsigned long addr;
+	unsigned int tmp;
 
 	memset(&mach64, 0, sizeof(mach64));
 	for(pdev = pci_devices; pdev; pdev = pdev->next) {
@@ -148,48 +199,85 @@
 		prom_halt();
 	}
 
+	pcibios_read_config_dword(pdev->bus->number, pdev->devfn,
+				  PCI_BASE_ADDRESS_0, &mach64_pci_membase);
+	mach64_pci_membase &= PCI_BASE_ADDRESS_MEM_MASK;
+
+	pcibios_read_config_dword(pdev->bus->number, pdev->devfn,
+				  PCI_BASE_ADDRESS_1, &mach64_pci_iobase);
+	mach64_pci_iobase &= PCI_BASE_ADDRESS_IO_MASK;
+
 	printk("mach64_init: IOBASE[%016lx] MEMBASE[%016lx]\n",
 		pcivga_iobase, pcivga_membase);
 
-	cookie = (struct pcidev_cookie *)pdev->sysdata;
+	cookie = pdev->sysdata;
+	pbm = cookie->pbm;
+
 	fb->prom_node = cookie->prom_node;
-	fb->proc_entry.node = cookie->pbm->prom_node;
+	fb->proc_entry.node = pbm->prom_node;
 
 	fb->type.fb_type = FBTYPE_PCI_MACH64;
 	fb->type.fb_cmsize = 256;
 	fb->info.private = (void *)&mach64;
-	fb->base = pcivga_membase + MACH64_FBOFF;
-
-	switch(pcivga_readl(MACH64_REGOFF + MEM_CNTL) & MEM_SIZE_ALIAS) {
-	case MEM_SIZE_512K:
-		mach64.total_vram = 0x80000;
-		break;
-	case MEM_SIZE_1M:
-		mach64.total_vram = 0x100000;
-		break;
-	case MEM_SIZE_2M:
-		mach64.total_vram = 0x200000;
-		break;
-	case MEM_SIZE_4M:
-		mach64.total_vram = 0x400000;
-		break;
-	case MEM_SIZE_6M:
-		mach64.total_vram = 0x600000;
-		break;
-	case MEM_SIZE_8M:
-		mach64.total_vram = 0x800000;
-		break;
-	default:
-		mach64.total_vram = 0x80000;
-		break;
-	}
+	fb->base = pcivga_membase + MACH64_BE_FBOFF;
 
 	if ((pcivga_readl(MACH64_REGOFF + CONFIG_CHIP_ID)
 					& CFG_CHIP_TYPE) == MACH64_VT_ID)
 		mach64.flags |= MACH64_MASK_VT;
 
+	/*
+	 * Fix the PROM's idea of MEM_CNTL settings...
+	 */
+	tmp = pcivga_readl(MACH64_REGOFF + MEM_CNTL);
+	switch (tmp & 0xf) {
+		case 3:
+			tmp = (tmp & ~(0xf)) | 2;
+			break;
+		case 7:
+			tmp = (tmp & ~(0xf)) | 3;
+			break;
+		case 9:
+			tmp = (tmp & ~(0xf)) | 4;
+			break;
+		case 11:
+			tmp = (tmp & ~(0xf)) | 5;
+			break;
+		default:
+			break;
+	}
+	tmp &= ~(0x00f00000);
+	pcivga_writel(tmp, MACH64_REGOFF + MEM_CNTL);
+
+	switch(tmp & MEM_SIZE_ALIAS) {
+		case MEM_SIZE_512K:
+			mach64.total_vram = 0x80000;
+			break;
+		case MEM_SIZE_1M:
+			mach64.total_vram = 0x100000;
+			break;
+		case MEM_SIZE_2M:
+			mach64.total_vram = 0x200000;
+			break;
+		case MEM_SIZE_4M:
+			mach64.total_vram = 0x400000;
+			break;
+		case MEM_SIZE_6M:
+			mach64.total_vram = 0x600000;
+			break;
+		case MEM_SIZE_8M:
+			mach64.total_vram = 0x800000;
+			break;
+		default:
+			mach64.total_vram = 0x80000;
+			break;
+	}
+
 	printk("mach64_init: total_vram[%08x] is_vt_chip[%d]\n",
 		mach64.total_vram, mach64.flags & MACH64_MASK_VT ? 1 : 0);
+
+#if 0
+	mach64_test(fb);
+#endif
 
 	fb->mmap = mach64_mmap;
 	fb->loadcmap = mach64_loadcmap;
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/mach64.h linux/drivers/sbus/char/mach64.h
--- v2.1.78/linux/drivers/sbus/char/mach64.h	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/mach64.h	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: mach64.h,v 1.3 1997/08/24 12:13:07 ecd Exp $
+/* $Id: mach64.h,v 1.4 1997/10/04 08:51:30 ecd Exp $
  * mach64.h: Ultra/PCI mach64 driver constants etc.
  *
  * Copyright 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -42,6 +42,9 @@
 #define CRTC_FIFO               0x001e
 #define CRTC_EXT_DISP           0x001f
 
+#define SHARED_CNTL		0x0030  /* Dword offset 0C */
+#define SHARED_MEM_CONFIG	0x0034  /* Dword offset 0D */
+
 #define OVR_CLR                 0x0040  /* Dword offset 10 */
 #define OVR_WID_LEFT_RIGHT      0x0044  /* Dword offset 11 */
 #define OVR_WID_TOP_BOTTOM      0x0048  /* Dword offset 12 */
@@ -60,6 +63,7 @@
 
 #define BUS_CNTL                0x00A0  /* Dword offset 28 */
 
+#define EXT_MEM_CNTL		0x00AC	/* Dword offset 2B */
 #define MEM_CNTL                0x00B0  /* Dword offset 2C */
 
 #define MEM_VGA_WP_SEL          0x00B4  /* Dword offset 2D */
@@ -359,6 +363,8 @@
 #define MEM_SIZE_6M		0x00000004
 #define MEM_SIZE_8M		0x00000005
 #define MEM_SIZE_ALIAS_GTB	0x0000000F
+#define MEM_SIZE_512K_GTB	0x00000000
+#define MEM_SIZE_1M_GTB		0x00000001
 #define MEM_SIZE_2M_GTB		0x00000003
 #define MEM_SIZE_4M_GTB		0x00000007
 #define MEM_SIZE_6M_GTB		0x00000009
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/openprom.c linux/drivers/sbus/char/openprom.c
--- v2.1.78/linux/drivers/sbus/char/openprom.c	Mon Jul  7 08:18:55 1997
+++ linux/drivers/sbus/char/openprom.c	Mon Jan 12 15:15:45 1998
@@ -526,8 +526,7 @@
 	}
 }
 
-static long long openprom_lseek(struct inode * inode, struct file * file,
-			  long long offset, int origin)
+static long long openprom_lseek(struct file * file, long long offset, int origin)
 {
 	return -ESPIPE;
 }
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/pcicons.c linux/drivers/sbus/char/pcicons.c
--- v2.1.78/linux/drivers/sbus/char/pcicons.c	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/pcicons.c	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: pcicons.c,v 1.9 1997/08/28 02:23:24 ecd Exp $
+/* $Id: pcicons.c,v 1.10 1997/10/04 08:52:57 ecd Exp $
  * pcicons.c: PCI specific probing and console operations layer.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -37,14 +37,9 @@
 static int y_margin = 0;
 static int skip_bytes;
 
-static void pci_cursor_blink(unsigned long);
-static __u32 *cursor_screen_pos;
-static __u32 cursor_bits;
+static __u64 *cursor_screen_pos;
+static __u64 cursor_bits[2];
 static int cursor_pos = -1;
-static int cursor_off = 1;
-static struct timer_list pci_cursor_timer = {
-	NULL, NULL, 0, 0, pci_cursor_blink
-};
 
 extern int serial_console;
 
@@ -219,52 +214,24 @@
 static void pci_invert_cursor(int cpos)
 {
 	fbinfo_t *fb = &fbinfo[0];
-	unsigned char color;
-	__u32 *screen, mask;
-	int i;
-
-	del_timer(&pci_cursor_timer);
+	__u64 *screen;
 
 	if (cpos == -1) {
-		if (cursor_off)
-			return;
 		screen = cursor_screen_pos;
-		mask = cursor_bits;
-	} else {
-		screen = (__u32 *)(fb->base + fbuf_offset(cpos)
-					    + 14 * fb->linebytes);
-
-		color = CHARATTR_TO_SUNCOLOR(
-					vc_cons[fg_console].d->vc_color << 8);
-
-		mask = (color ^ (color >> 4)) & 0x0f;
-		mask |= mask << 8;
-		mask |= mask << 16;
-
-		cursor_screen_pos = screen;
-		cursor_bits = mask;
-
-		pci_cursor_timer.expires = jiffies + (HZ >> 2);
-		add_timer(&pci_cursor_timer);
+		*screen = cursor_bits[0];
+		screen = (__u64 *)((unsigned long)screen + fb->linebytes);
+		*screen = cursor_bits[1];
+		return;
 	}
 
-	for (i = 0; i < 2; i++) {
-		screen[0] ^= mask;
-		screen[1] ^= mask;
-		screen = (__u32 *)((unsigned long)screen + fb->linebytes);
-	}
-}
+	screen = (__u64 *)(fb->base + fbuf_offset(cpos) + 14 * fb->linebytes);
+	cursor_screen_pos = screen;
 
-static void pci_cursor_blink(unsigned long ignored)
-{
-	unsigned long flags;
-
-	save_flags(flags); cli();
-	if (cursor_pos != -1) {
-		pci_invert_cursor(cursor_pos);
-		cursor_off = 1 - cursor_off;
-	}
-	restore_flags(flags);
+	cursor_bits[0] = *screen;
+	*screen = 0x0000000000000000;
+	screen = (__u64 *)((unsigned long)screen + fb->linebytes);
+	cursor_bits[1] = *screen;
+	*screen = 0x0000000000000000;
 }
 
 static void pci_hide_cursor(void)
@@ -275,11 +242,8 @@
 		return;
 
 	save_flags(flags); cli();
-	if (cursor_pos != -1) {
+	if (cursor_pos != -1)
 		pci_invert_cursor(-1);
-		cursor_pos = -1;
-	}
-	cursor_off = 1;
 	restore_flags(flags);
 }
 
@@ -300,7 +264,6 @@
 		if (old_cursor != -1)
 			pci_invert_cursor(-1);
 		pci_invert_cursor(cursor_pos);
-		cursor_off = 0;
 	}
 	restore_flags(flags);
 }
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/pcikbd.c linux/drivers/sbus/char/pcikbd.c
--- v2.1.78/linux/drivers/sbus/char/pcikbd.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/sbus/char/pcikbd.c	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: pcikbd.c,v 1.4 1997/09/05 22:59:53 ecd Exp $
+/* $Id: pcikbd.c,v 1.12 1997/12/27 16:28:27 jj Exp $
  * pcikbd.c: Ultra/AX PC keyboard support.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -8,6 +8,7 @@
  * to the original authors.
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -19,6 +20,7 @@
 #include <linux/random.h>
 #include <linux/miscdevice.h>
 #include <linux/kbd_ll.h>
+#include <linux/delay.h>
 #include <linux/init.h>
 
 #include <asm/ebus.h>
@@ -26,7 +28,6 @@
 #include <asm/irq.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
-#include <asm/keyboard.h>
 
 #include "pcikbd.h"
 #include "sunserial.h"
@@ -35,6 +36,7 @@
 static int beep_node;
 
 static unsigned long pcikbd_iobase = 0;
+static unsigned long pcibeep_iobase = 0;
 static unsigned int pcikbd_irq;
 
 /* used only by send_data - set by keyboard_interrupt */
@@ -42,12 +44,31 @@
 static volatile unsigned char acknowledge = 0;
 static volatile unsigned char resend = 0;
 
+unsigned char pckbd_read_mask = KBD_STAT_OBF;
+
+extern int pcikbd_init(void);
+extern void pci_compute_shiftstate(void);
+extern int pci_setkeycode(unsigned int, unsigned int);
+extern int pci_getkeycode(unsigned int);
+extern void pci_setledstate(struct kbd_struct *, unsigned int);
+extern unsigned char pci_getledstate(void);
+
+static __inline__ unsigned char pcikbd_inb(unsigned long port)
+{
+	return inb(port);
+}
+
+static __inline__ void pcikbd_outb(unsigned char val, unsigned long port)
+{
+	outb(val, port);
+}
+
 static inline void kb_wait(void)
 {
 	unsigned long start = jiffies;
 
 	do {
-		if(!(inb(pcikbd_iobase + KBD_STATUS_REG) & KBD_STAT_IBF))
+		if(!(pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG) & KBD_STAT_IBF))
 			return;
 	} while (jiffies - start < KBC_TIMEOUT);
 }
@@ -167,6 +188,19 @@
   0, 0, 0, 0, 0, 0, 0, 0			      /* 0x78-0x7f */
 };
 
+/* Simple translation table for the SysRq keys */
+
+#ifdef CONFIG_MAGIC_SYSRQ
+unsigned char pcikbd_sysrq_xlate[128] =
+	"\000\0331234567890-=\177\t"			/* 0x00 - 0x0f */
+	"qwertyuiop[]\r\000as"				/* 0x10 - 0x1f */
+	"dfghjkl;'`\000\\zxcv"				/* 0x20 - 0x2f */
+	"bnm,./\000*\000 \000\201\202\203\204\205"	/* 0x30 - 0x3f */
+	"\206\207\210\211\212\000\000789-456+1"		/* 0x40 - 0x4f */
+	"230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */
+	"\r\000/";					/* 0x60 - 0x6f */
+#endif
+
 static unsigned int prev_scancode = 0;
 
 int pcikbd_setkeycode(unsigned int scancode, unsigned int keycode)
@@ -268,25 +302,17 @@
 {
 	unsigned char status;
 
-	/*
-	 * This IRQ might be shared with the 16550A serial chip,
-	 * so we check dev_id to see if it was for us.
-	 * (See also drivers/sbus/char/su.c).
-	 */
-	if (dev_id)
-		return;
-
-	/* kbd_pt_regs = regs; */
-	status = inb(pcikbd_iobase + KBD_STATUS_REG);
+	kbd_pt_regs = regs;
+	status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG);
 	do {
 		unsigned char scancode;
 
-		if(status & kbd_read_mask & KBD_STAT_MOUSE_OBF)
+		if(status & pckbd_read_mask & KBD_STAT_MOUSE_OBF)
 			break;
-		scancode = inb(pcikbd_iobase + KBD_DATA_REG);
+		scancode = pcikbd_inb(pcikbd_iobase + KBD_DATA_REG);
 		if((status & KBD_STAT_OBF) && do_acknowledge(scancode))
-			/* handle_scancode(scancode) */;
-		status = inb(pcikbd_iobase + KBD_STATUS_REG);
+			handle_scancode(scancode);
+		status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG);
 	} while(status & KBD_STAT_OBF);
 	mark_bh(KEYBOARD_BH);
 }
@@ -300,7 +326,7 @@
 		kb_wait();
 		acknowledge = resend = 0;
 		reply_expected = 1;
-		outb(data, pcikbd_iobase + KBD_DATA_REG);
+		pcikbd_outb(data, pcikbd_iobase + KBD_DATA_REG);
 		start = jiffies;
 		do {
 			if(acknowledge)
@@ -325,10 +351,10 @@
 	unsigned long start = jiffies;
 
 	do {
-		status = inb(pcikbd_iobase + KBD_STATUS_REG);
+		status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG);
 		if(!(status & KBD_STAT_OBF))
 			continue;
-		data = inb(pcikbd_iobase + KBD_DATA_REG);
+		data = pcikbd_inb(pcikbd_iobase + KBD_DATA_REG);
 		if(status & (KBD_STAT_GTO | KBD_STAT_PERR))
 			continue;
 		return (data & 0xff);
@@ -341,18 +367,55 @@
 	int status;
 
 	do {
-		status = inb(pcikbd_iobase + KBD_STATUS_REG);
+		status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG);
 	} while (status & KBD_STAT_IBF);
-	outb(data, pcikbd_iobase + address);
+	pcikbd_outb(data, pcikbd_iobase + address);
+}
+
+/* Timer routine to turn off the beep after the interval expires. */
+static void pcikbd_kd_nosound(unsigned long __unused)
+{
+	outl(0, pcibeep_iobase);
+}
+
+/*
+ * Initiate a keyboard beep. If the frequency is zero, then we stop
+ * the beep. Any other frequency will start a monotone beep. The beep
+ * will be stopped by a timer after "ticks" jiffies. If ticks is 0,
+ * then we do not start a timer.
+ */
+static void pcikbd_kd_mksound(unsigned int hz, unsigned int ticks)
+{
+	unsigned long flags;
+	static struct timer_list sound_timer = { NULL, NULL, 0, 0,
+						 pcikbd_kd_nosound };
+
+	save_flags(flags); cli();
+	del_timer(&sound_timer);
+	if (hz) {
+		outl(1, pcibeep_iobase);
+		if (ticks) {
+			sound_timer.expires = jiffies + ticks;
+			add_timer(&sound_timer);
+		}
+	} else
+		outl(0, pcibeep_iobase);
+	restore_flags(flags);
 }
 
-__initfunc(static char *do_pcikbd_hwinit(void))
+static void nop_kd_mksound(unsigned int hz, unsigned int ticks)
+{
+}
+
+extern void (*kd_mksound)(unsigned int hz, unsigned int ticks);
+
+__initfunc(static char *do_pcikbd_init_hw(void))
 {
 	while(pcikbd_wait_for_input() != -1)
 		;
 
 	pcikbd_write(KBD_CNTL_REG, KBD_CCMD_SELF_TEST);
-	if(pcikbd_wait_for_input() != 0xff)
+	if(pcikbd_wait_for_input() != 0x55)
 		return "Keyboard failed self test";
 
 	pcikbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_TEST);
@@ -388,23 +451,12 @@
 	return NULL; /* success */
 }
 
-__initfunc(void pcikbd_hwinit(void))
-{
-	char *msg;
-
-	disable_irq(pcikbd_irq);
-	msg = do_pcikbd_hwinit();
-	enable_irq(pcikbd_irq);
-
-	if(msg)
-		printk("8042: keyboard init failure [%s]\n", msg);
-}
-
-__initfunc(int pcikbd_probe(void))
+__initfunc(void pcikbd_init_hw(void))
 {
 	struct linux_ebus *ebus;
 	struct linux_ebus_device *edev;
 	struct linux_ebus_child *child;
+	char *msg;
 
 	for_all_ebusdev(edev, ebus) {
 		if(!strcmp(edev->prom_name, "8042")) {
@@ -415,29 +467,57 @@
 		}
 	}
 	printk("pcikbd_probe: no 8042 found\n");
-	return -ENODEV;
+	return;
 
 found:
 	pcikbd_iobase = child->base_address[0];
 	if (check_region(pcikbd_iobase, sizeof(unsigned long))) {
 		printk("8042: can't get region %lx, %d\n",
 		       pcikbd_iobase, (int)sizeof(unsigned long));
-		return -ENODEV;
+		return;
 	}
 	request_region(pcikbd_iobase, sizeof(unsigned long), "8042 controller");
 
 	pcikbd_irq = child->irqs[0];
 	if (request_irq(pcikbd_irq, &pcikbd_interrupt,
-			SA_SHIRQ, "keyboard", NULL)) {
+			SA_SHIRQ, "keyboard", (void *)pcikbd_iobase)) {
 		printk("8042: cannot register IRQ %x\n", pcikbd_irq);
-		return -ENODEV;
+		return;
 	}
 
 	printk("8042(kbd): iobase[%016lx] irq[%x]\n", pcikbd_iobase, pcikbd_irq);
 
-	/* pcikbd_init(); */
-	kbd_read_mask = KBD_STAT_OBF;
-	return 0;
+	kd_mksound = nop_kd_mksound;
+	for_all_ebusdev(edev, ebus) {
+		if(!strcmp(edev->prom_name, "beeper"))
+			break;
+	}
+
+	/*
+	 * XXX: my 3.1.3 PROM does not give me the beeper node for the audio
+	 *      auxio register, though I know it is there... (ecd)
+	 */
+	if (!edev)
+		pcibeep_iobase = (pcikbd_iobase & ~(0xffffff)) | 0x722000;
+	else
+		pcibeep_iobase = edev->base_address[0];
+
+	if (check_region(pcibeep_iobase, sizeof(unsigned int))) {
+		printk("8042: can't get region %lx, %d\n",
+		       pcibeep_iobase, (int)sizeof(unsigned int));
+	} else {
+		request_region(pcibeep_iobase, sizeof(unsigned int), "speaker");
+		kd_mksound = pcikbd_kd_mksound;
+		printk("8042(speaker): iobase[%016lx]%s\n", pcibeep_iobase,
+		       edev ? "" : " (forced)");
+	}
+
+	disable_irq(pcikbd_irq);
+	msg = do_pcikbd_init_hw();
+	enable_irq(pcikbd_irq);
+
+	if(msg)
+		printk("8042: keyboard init failure [%s]\n", msg);
 }
 
 
@@ -451,8 +531,6 @@
 static unsigned long pcimouse_iobase = 0;
 static unsigned int pcimouse_irq;
 
-#define PSMOUSE_MINOR      1		/* Minor device # for this mouse */
-
 #define AUX_BUF_SIZE	2048
 
 struct aux_queue {
@@ -468,6 +546,16 @@
 static int aux_count = 0;
 static int aux_present = 0;
 
+static __inline__ unsigned char pcimouse_inb(unsigned long port)
+{
+	return inb(port);
+}
+
+static __inline__ void pcimouse_outb(unsigned char val, unsigned long port)
+{
+	outb(val, port);
+}
+
 /*
  *	Shared subroutines
  */
@@ -491,11 +579,11 @@
 	return queue->head == queue->tail;
 }
 
-static int fasync_aux(struct inode *inode, struct file *filp, int on)
+static int aux_fasync(struct file *filp, int on)
 {
 	int retval;
 
-	retval = fasync_helper(inode, filp, on, &queue->fasync);
+	retval = fasync_helper(filp, on, &queue->fasync);
 	if (retval < 0)
 		return retval;
 	return 0;
@@ -521,11 +609,11 @@
 {
 	int retries=0;
 
-	while ((inb(pcimouse_iobase + KBD_STATUS_REG) &
+	while ((pcimouse_inb(pcimouse_iobase + KBD_STATUS_REG) &
 		(KBD_STAT_IBF | KBD_STAT_OBF)) && retries < MAX_RETRIES) {
- 		if ((inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF)
+ 		if ((pcimouse_inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF)
 		    == AUX_STAT_OBF)
-			inb(pcimouse_iobase + KBD_DATA_REG);
+			pcimouse_inb(pcimouse_iobase + KBD_DATA_REG);
 		current->state = TASK_INTERRUPTIBLE;
 		current->timeout = jiffies + (5*HZ + 99) / 100;
 		schedule();
@@ -541,9 +629,10 @@
 static void aux_write_dev(int val)
 {
 	poll_aux_status();
-	outb(KBD_CCMD_WRITE_MOUSE, pcimouse_iobase + KBD_CNTL_REG);/* Write magic cookie */
+	pcimouse_outb(KBD_CCMD_WRITE_MOUSE, pcimouse_iobase + KBD_CNTL_REG);/* Write magic cookie */
 	poll_aux_status();
-	outb_p(val, pcimouse_iobase + KBD_DATA_REG);		 /* Write data */
+	pcimouse_outb(val, pcimouse_iobase + KBD_DATA_REG);		 /* Write data */
+	udelay(1);
 }
 
 /*
@@ -555,8 +644,8 @@
 	aux_write_dev(val);
 	poll_aux_status();
 
-	if ((inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF)
-		return (inb(pcimouse_iobase + KBD_DATA_REG));
+	if ((pcimouse_inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF)
+		return (pcimouse_inb(pcimouse_iobase + KBD_DATA_REG));
 	return 0;
 }
 
@@ -567,9 +656,9 @@
 static void aux_write_cmd(int val)
 {
 	poll_aux_status();
-	outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG);
+	pcimouse_outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG);
 	poll_aux_status();
-	outb(val, pcimouse_iobase + KBD_DATA_REG);
+	pcimouse_outb(val, pcimouse_iobase + KBD_DATA_REG);
 }
 
 /*
@@ -607,18 +696,10 @@
 	int head = queue->head;
 	int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
 
-	/*
-	 * This IRQ might be shared with the 16550A serial chip,
-	 * so we check dev_id to see if it was for us.
-	 * (See also drivers/sbus/char/su.c).
-	 */
-	if (dev_id)
-		return;
-
-	if ((inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) != AUX_STAT_OBF)
+	if ((pcimouse_inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) != AUX_STAT_OBF)
 		return;
 
-	add_mouse_randomness(queue->buf[head] = inb(pcimouse_iobase + KBD_DATA_REG));
+	add_mouse_randomness(queue->buf[head] = pcimouse_inb(pcimouse_iobase + KBD_DATA_REG));
 	if (head != maxhead) {
 		head++;
 		head &= AUX_BUF_SIZE-1;
@@ -630,9 +711,9 @@
 	wake_up_interruptible(&queue->proc_list);
 }
 
-static int release_aux(struct inode * inode, struct file * file)
+static int aux_release(struct inode * inode, struct file * file)
 {
-	fasync_aux(inode, file, 0);
+	aux_fasync(file, 0);
 	if (--aux_count)
 		return 0;
 	aux_start_atomic();
@@ -642,7 +723,7 @@
 	poll_aux_status();
 
 	/* Disable Aux device */
-	outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG);
+	pcimouse_outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG);
 	poll_aux_status();
 	aux_end_atomic();
 
@@ -655,10 +736,11 @@
  * Enable auxiliary device.
  */
 
-static int open_aux(struct inode * inode, struct file * file)
+static int aux_open(struct inode * inode, struct file * file)
 {
 	if (!aux_present)
 		return -ENODEV;
+
 	aux_start_atomic();
 	if (aux_count++) {
 		aux_end_atomic();
@@ -674,7 +756,7 @@
 	MOD_INC_USE_COUNT;
 
 	poll_aux_status();
-	outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase+KBD_CNTL_REG);    /* Enable Aux */
+	pcimouse_outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase+KBD_CNTL_REG);    /* Enable Aux */
 	aux_write_dev(AUX_ENABLE_DEV);			    /* Enable aux device */
 	aux_write_cmd(AUX_INTS_ON);			    /* Enable controller ints */
 	poll_aux_status();
@@ -688,31 +770,31 @@
  * Write to the aux device.
  */
 
-static long write_aux(struct inode * inode, struct file * file,
-	const char * buffer, unsigned long count)
+static ssize_t aux_write(struct file * file, const char * buffer,
+			 size_t count, loff_t *ppos)
 {
-	int retval = 0;
+	ssize_t retval = 0;
 
 	if (count) {
-		int written = 0;
+		ssize_t written = 0;
 
 		aux_start_atomic();
 		do {
 			char c;
 			if (!poll_aux_status())
 				break;
-			outb(KBD_CCMD_WRITE_MOUSE, pcimouse_iobase + KBD_CNTL_REG);
+			pcimouse_outb(KBD_CCMD_WRITE_MOUSE, pcimouse_iobase + KBD_CNTL_REG);
 			if (!poll_aux_status())
 				break;
 			get_user(c, buffer++);
-			outb(c, pcimouse_iobase + KBD_DATA_REG);
+			pcimouse_outb(c, pcimouse_iobase + KBD_DATA_REG);
 			written++;
 		} while (--count);
 		aux_end_atomic();
 		retval = -EIO;
 		if (written) {
 			retval = written;
-			inode->i_mtime = CURRENT_TIME;
+			file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
 		}
 	}
 
@@ -727,11 +809,11 @@
  * Put bytes from input queue to buffer.
  */
 
-static long read_aux(struct inode * inode, struct file * file,
-	char * buffer, unsigned long count)
+static ssize_t aux_read(struct file * file, char * buffer,
+		        size_t count, loff_t *ppos)
 {
 	struct wait_queue wait = { current, NULL };
-	int i = count;
+	ssize_t i = count;
 	unsigned char c;
 
 	if (queue_empty()) {
@@ -754,7 +836,7 @@
 	}
 	aux_ready = !queue_empty();
 	if (count-i) {
-		inode->i_atime = CURRENT_TIME;
+		file->f_dentry->d_inode->i_atime = CURRENT_TIME;
 		return count-i;
 	}
 	if (signal_pending(current))
@@ -772,16 +854,16 @@
 
 struct file_operations psaux_fops = {
 	NULL,		/* seek */
-	read_aux,
-	write_aux,
+	aux_read,
+	aux_write,
 	NULL, 		/* readdir */
 	aux_poll,
 	NULL, 		/* ioctl */
 	NULL,		/* mmap */
-	open_aux,
-	release_aux,
+	aux_open,
+	aux_release,
 	NULL,
-	fasync_aux,
+	aux_fasync,
 };
 
 static struct miscdevice psaux_mouse = {
@@ -816,7 +898,7 @@
 
 	pcimouse_irq = child->irqs[0];
 	if (request_irq(pcimouse_irq, &pcimouse_interrupt,
-		        SA_SHIRQ, "mouse", NULL)) {
+		        SA_SHIRQ, "mouse", (void *)pcimouse_iobase)) {
 		printk("8042: Cannot register IRQ %x\n", pcimouse_irq);
 		return -ENODEV;
 	}
@@ -826,7 +908,7 @@
 
 	printk("8042: PS/2 auxiliary pointing device detected.\n");
 	aux_present = 1;
-	kbd_read_mask = AUX_STAT_OBF;
+	pckbd_read_mask = AUX_STAT_OBF;
 
 	misc_register(&psaux_mouse);
 	queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
@@ -834,18 +916,19 @@
 	queue->head = queue->tail = 0;
 	queue->proc_list = NULL;
 	aux_start_atomic();
-	outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase + KBD_CNTL_REG);
+	pcimouse_outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase + KBD_CNTL_REG);
+	aux_write_ack(AUX_RESET);
 	aux_write_ack(AUX_SET_SAMPLE);
 	aux_write_ack(100);
 	aux_write_ack(AUX_SET_RES);
 	aux_write_ack(3);
 	aux_write_ack(AUX_SET_SCALE21);
 	poll_aux_status();
-	outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG);
+	pcimouse_outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG);
 	poll_aux_status();
-	outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG);
+	pcimouse_outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG);
 	poll_aux_status();
-	outb(AUX_INTS_OFF, pcimouse_iobase + KBD_DATA_REG);
+	pcimouse_outb(AUX_INTS_OFF, pcimouse_iobase + KBD_DATA_REG);
 	poll_aux_status();
 	aux_end_atomic();
 
@@ -853,21 +936,6 @@
 }
 
 
-__initfunc(static int ps2_init(void))
-{
-	int err;
-
-	err = pcikbd_probe();
-	if (err)
-		return err;
-
-	err = pcimouse_init();
-	if (err)
-		return err;
-
-	return 0;
-}
-
 __initfunc(int ps2kbd_probe(unsigned long *memory_start))
 {
 	int pnode, enode, node, dnode;
@@ -959,6 +1027,12 @@
 	return -ENODEV;
 
 found:
-        sunserial_setinitfunc(memory_start, ps2_init);
+        sunkbd_setinitfunc(memory_start, pcimouse_init);
+        sunkbd_setinitfunc(memory_start, pcikbd_init);
+	kbd_ops.compute_shiftstate = pci_compute_shiftstate;
+	kbd_ops.setledstate = pci_setledstate;
+	kbd_ops.getledstate = pci_getledstate;
+	kbd_ops.setkeycode = pci_setkeycode;
+	kbd_ops.getkeycode = pci_getkeycode;
 	return 0;
 }
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/pcikbd.h linux/drivers/sbus/char/pcikbd.h
--- v2.1.78/linux/drivers/sbus/char/pcikbd.h	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/pcikbd.h	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: pcikbd.h,v 1.1 1997/08/24 02:53:25 davem Exp $
+/* $Id: pcikbd.h,v 1.2 1997/12/25 21:13:14 geert Exp $
  * pcikbd.h: PCI/PC 8042 keyboard/mouse driver stuff.  Mostly snarfed
  *           from the existing driver by Martin Mares.
  *
@@ -28,7 +28,7 @@
  *	Internal variables of the driver
  */
 
-extern unsigned char kbd_read_mask;
+extern unsigned char pckbd_read_mask;
 extern unsigned char aux_device_present;
 
 extern unsigned long pcikbd_iobase;
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/rtc.c linux/drivers/sbus/char/rtc.c
--- v2.1.78/linux/drivers/sbus/char/rtc.c	Mon Apr 14 16:28:13 1997
+++ linux/drivers/sbus/char/rtc.c	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: rtc.c,v 1.10 1997/04/03 08:47:55 davem Exp $
+/* $Id: rtc.c,v 1.11 1997/09/20 20:47:26 davem Exp $
  *
  * Linux/SPARC Real Time Clock Driver
  * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
@@ -70,8 +70,7 @@
 	restore_flags(flags);
 }
 
-static long long rtc_lseek(struct inode *inode, struct file *file,
-	long long offset, int origin)
+static long long rtc_lseek(struct file *file, long long offset, int origin)
 {
 	return -ESPIPE;
 }
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/sab82532.c linux/drivers/sbus/char/sab82532.c
--- v2.1.78/linux/drivers/sbus/char/sab82532.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/sbus/char/sab82532.c	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: sab82532.c,v 1.4 1997/09/03 17:04:21 ecd Exp $
+/* $Id: sab82532.c,v 1.13 1997/12/30 09:37:49 ecd Exp $
  * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -16,6 +16,7 @@
 #include <linux/tty_flip.h>
 #include <linux/serial.h>
 #include <linux/serial_reg.h>
+#include <linux/console.h>
 #include <linux/major.h>
 #include <linux/string.h>
 #include <linux/fcntl.h>
@@ -40,6 +41,9 @@
 /* number of characters left in xmit buffer before we ask for more */
 #define WAKEUP_CHARS 256
 
+#define SERIAL_PARANOIA_CHECK
+#define SERIAL_DO_RESTART
+
 /* Set of debugging defines */
 #undef SERIAL_DEBUG_OPEN
 #undef SERIAL_DEBUG_INTR
@@ -59,10 +63,23 @@
 static struct termios *sab82532_termios[NR_PORTS];
 static struct termios *sab82532_termios_locked[NR_PORTS];
 
+#ifdef CONFIG_SERIAL_CONSOLE
+extern int serial_console;
+static struct console sab82532_console;
+static int sab82532_console_init(void);
+#endif
+
 #ifndef MIN
 #define MIN(a,b)	((a) < (b) ? (a) : (b))
 #endif
 
+static char *sab82532_version[16] = {
+	"V1.0", "V2.0", "V3.2", "V(0x03)",
+	"V(0x04)", "V(0x05)", "V(0x06)", "V(0x07)",
+	"V(0x08)", "V(0x09)", "V(0x0a)", "V(0x0b)",
+	"V(0x0c)", "V(0x0d)", "V(0x0e)", "V(0x0f)"
+};
+
 /*
  * tmp_buf is used as a temporary buffer by sab82532_write.  We need to
  * lock it in case the copy_from_user blocks while swapping in a page,
@@ -178,8 +195,10 @@
 	restore_flags(flags);
 }
 
-static void batten_down_hatches(void)
+static void batten_down_hatches(struct sab82532 *info)
 {
+	unsigned char saved_rfc;
+
 	/* If we are doing kadb, we call the debugger
 	 * else we just drop into the boot monitor.
 	 * Note that we must flush the user windows
@@ -187,6 +206,16 @@
 	 */
 	printk("\n");
 	flush_user_windows();
+
+	/*
+	 * Set FIFO to single character mode.
+	 */
+	saved_rfc = info->regs->r.rfc;
+	info->regs->rw.rfc &= ~(SAB82532_RFC_RFDF);
+	if (info->regs->r.star & SAB82532_STAR_CEC)
+		udelay(1);
+	info->regs->w.cmdr = SAB82532_CMDR_RRES;
+
 #ifndef __sparc_v9__
 	if ((((unsigned long)linux_dbvec) >= DEBUG_FIRSTVADDR) &&
 	    (((unsigned long)linux_dbvec) <= DEBUG_LASTVADDR))
@@ -194,6 +223,14 @@
 	else
 #endif
 		prom_cmdline();
+
+	/*
+	 * Reset FIFO to character + status mode.
+	 */
+	info->regs->w.rfc = saved_rfc;
+	if (info->regs->r.star & SAB82532_STAR_CEC)
+		udelay(1);
+	info->regs->w.cmdr = SAB82532_CMDR_RRES;
 }
 
 /*
@@ -242,16 +279,11 @@
 		count = info->recv_fifo_size;
 		free_fifo++;
 	}
+
 	if (stat->sreg.isr0 & SAB82532_ISR0_TCD) {
 		count = info->regs->r.rbcl & (info->recv_fifo_size - 1);
 		free_fifo++;
 	}
-	if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
-#if 1
-		printk("sab82532: receive_chars: RFO");
-#endif
-		free_fifo++;
-	}
 
 	/* Issue a FIFO read command in case we where idle. */
 	if (stat->sreg.isr0 & SAB82532_ISR0_TIME) {
@@ -260,8 +292,15 @@
 		info->regs->w.cmdr = SAB82532_CMDR_RFRD;
 	}
 
+	if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
+#if 1
+		printk("sab82532: receive_chars: RFO");
+#endif
+		free_fifo++;
+	}
+
 	/* Read the FIFO. */
-	for (i = 0; i < (count << 1); i++)
+	for (i = 0; i < count; i++)
 		buf[i] = info->regs->r.rfifo[i];
 
 	/* Issue Receive Message Complete command. */
@@ -271,6 +310,11 @@
 		info->regs->w.cmdr = SAB82532_CMDR_RMC;
 	}
 
+	if (info->is_console)
+		wake_up(&keypress_wait);
+	if (!tty)
+		return;
+
 	for (i = 0; i < count; ) {
 		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
 #if 1
@@ -312,6 +356,12 @@
 {
 	int i;
 
+	if (!info->tty) {
+		info->interrupt_mask1 |= SAB82532_IMR1_XPR;
+		info->regs->w.imr1 = info->interrupt_mask1;
+		return;
+	}
+
 	if ((info->xmit_cnt <= 0) || info->tty->stopped ||
 	    info->tty->hw_stopped) {
 		if (stat->sreg.isr1 & SAB82532_ISR1_ALLS)
@@ -356,7 +406,7 @@
 
 	if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
 		if (info->is_console) {
-			batten_down_hatches();
+			batten_down_hatches(info);
 			return;
 		}
 		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
@@ -369,6 +419,9 @@
 		info->icount.brk++;
 	}
 
+	if (!tty)
+		return;
+
 	if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
 		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
 			info->icount.buf_overrun++;
@@ -380,9 +433,6 @@
 		info->icount.overrun++;
 	}
 
-	if (info->is_console)
-		return;
-
 check_modem:
 	if (stat->sreg.isr0 & SAB82532_ISR0_CDSC) {
 		info->dcd = (info->regs->r.vstr & SAB82532_VSTR_CD) ? 0 : 1;
@@ -581,38 +631,18 @@
 	tty_hangup(tty);
 }
 
-
-static int startup(struct sab82532 *info)
+static void
+sab82532_init_line(struct sab82532 *info)
 {
-	unsigned long flags;
-	unsigned long page;
 	unsigned char stat;
 
-	page = get_free_page(GFP_KERNEL);
-	if (!page)
-		return -ENOMEM;
-
-	save_flags(flags); cli();
-
-	if (info->flags & ASYNC_INITIALIZED) {
-		free_page(page);
-		goto errout;
-	}
-
-	if (!info->regs) {
-		if (info->tty)
-			set_bit(TTY_IO_ERROR, &info->tty->flags);
-		free_page(page);
-		goto errout;
-	}
-	if (info->xmit_buf)
-		free_page(page);
-	else
-		info->xmit_buf = (unsigned char *)page;
-
-#ifdef SERIAL_DEBUG_OPEN
-	printk("starting up serial port %d...", info->line);
-#endif
+	/*
+	 * Wait for any commands or immediate characters
+	 */
+	if (info->regs->r.star & SAB82532_STAR_CEC)
+		udelay(1);
+	while (info->regs->r.star & SAB82532_STAR_TEC)
+		udelay(1);
 
 	/*
 	 * Clear the FIFO buffers.
@@ -662,7 +692,46 @@
 			break;
 	}
 	info->regs->rw.ccr0 |= SAB82532_CCR0_PU;	/* power-up */
-	
+}
+
+static int startup(struct sab82532 *info)
+{
+	unsigned long flags;
+	unsigned long page;
+	int retval = 0;
+
+	page = get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	save_flags(flags); cli();
+
+	if (info->flags & ASYNC_INITIALIZED) {
+		free_page(page);
+		goto errout;
+	}
+
+	if (!info->regs) {
+		if (info->tty)
+			set_bit(TTY_IO_ERROR, &info->tty->flags);
+		free_page(page);
+		retval = -ENODEV;
+		goto errout;
+	}
+	if (info->xmit_buf)
+		free_page(page);
+	else
+		info->xmit_buf = (unsigned char *)page;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up serial port %d...", info->line);
+#endif
+
+	/*
+	 * Initialize the Hardware
+	 */
+	sab82532_init_line(info);
+
 	/*
 	 * Finally, enable interrupts
 	 */
@@ -689,7 +758,7 @@
 	
 errout:
 	restore_flags(flags);
-	return -ENODEV;
+	return retval;
 }
 
 /*
@@ -720,6 +789,22 @@
 		info->xmit_buf = 0;
 	}
 
+	if (info->is_console) {
+		info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+					SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
+		info->regs->w.imr0 = info->interrupt_mask0;
+		info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
+					SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
+					SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
+					SAB82532_IMR1_XPR;
+		info->regs->w.imr1 = info->interrupt_mask1;
+		if (info->tty)
+			set_bit(TTY_IO_ERROR, &info->tty->flags);
+		info->flags &= ~ASYNC_INITIALIZED;
+		restore_flags(flags);
+		return;
+	}
+
 	/* Disable Interrupts */
 	info->interrupt_mask0 = 0xff;
 	info->regs->w.imr0 = info->interrupt_mask0;
@@ -758,7 +843,7 @@
 	unsigned int	ebrg;
 	tcflag_t	cflag;
 	unsigned char	dafo;
-	int		i;
+	int		i, bits;
 
 	if (!info->tty || !info->tty->termios)
 		return;
@@ -766,19 +851,23 @@
 
 	/* Byte size and parity */
 	switch (cflag & CSIZE) {
-	      case CS5: dafo = SAB82532_DAFO_CHL5; break;
-	      case CS6: dafo = SAB82532_DAFO_CHL6; break;
-	      case CS7: dafo = SAB82532_DAFO_CHL7; break;
-	      case CS8: dafo = SAB82532_DAFO_CHL8; break;
+	      case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break;
+	      case CS6: dafo = SAB82532_DAFO_CHL6; bits = 8; break;
+	      case CS7: dafo = SAB82532_DAFO_CHL7; bits = 9; break;
+	      case CS8: dafo = SAB82532_DAFO_CHL8; bits = 10; break;
 	      /* Never happens, but GCC is too dumb to figure it out */
-	      default:  dafo = SAB82532_DAFO_CHL5; break;
+	      default:  dafo = SAB82532_DAFO_CHL5; bits = 7; break;
 	}
 
-	if (cflag & CSTOPB)
+	if (cflag & CSTOPB) {
 		dafo |= SAB82532_DAFO_STOP;
+		bits++;
+	}
 
-	if (cflag & PARENB)
+	if (cflag & PARENB) {
 		dafo |= SAB82532_DAFO_PARE;
+		bits++;
+	}
 
 	if (cflag & PARODD) {
 #ifdef CMSPAR
@@ -808,6 +897,12 @@
 	ebrg = ebrg_table[i].n;
 	ebrg |= (ebrg_table[i].m << 6);
 
+	if (ebrg_table[i].baud)
+		info->timeout = (info->xmit_fifo_size * HZ * bits) / ebrg_table[i].baud;
+	else
+		info->timeout = 0;
+	info->timeout += HZ / 50;		/* Add .02 seconds of slop */
+
 	/* CTS flow control flags */
 	if (cflag & CRTSCTS)
 		info->flags |= ASYNC_CTS_FLOW;
@@ -842,6 +937,10 @@
 					    SAB82532_ISR0_TIME;
 
 	save_flags(flags); cli();
+	if (info->regs->r.star & SAB82532_STAR_CEC)
+		udelay(1);
+	while (info->regs->r.star & SAB82532_STAR_TEC)
+		udelay(1);
 	info->regs->w.dafo = dafo;
 	info->regs->w.bgr = ebrg & 0xff;
 	info->regs->rw.ccr2 &= ~(0xc0);
@@ -980,7 +1079,7 @@
 static void sab82532_flush_buffer(struct tty_struct *tty)
 {
 	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-				
+
 	if (serial_paranoia_check(info, tty->device, "sab82532_flush_buffer"))
 		return;
 	cli();
@@ -1003,7 +1102,7 @@
 	if (serial_paranoia_check(info, tty->device, "sab82532_send_xchar"))
 		return;
 
-	if (info->regs->r.star & SAB82532_STAR_TEC)
+	while (info->regs->r.star & SAB82532_STAR_TEC)
 		udelay(1);
 	info->regs->w.tic = ch;
 }
@@ -1114,7 +1213,7 @@
 {
 	unsigned int result;
 
-	result = info->all_sent ? TIOCSER_TEMT : 0;
+	result = (!info->xmit_buf && info->all_sent) ? TIOCSER_TEMT : 0;
 	return put_user(result, value);
 }
 
@@ -1496,7 +1595,9 @@
 	 */
 	info->interrupt_mask0 |= SAB82532_IMR0_TCD;
 	info->regs->w.imr0 = info->interrupt_mask0;
+#if 0
 	info->regs->rw.mode &= ~(SAB82532_MODE_RAC);
+#endif
 	if (info->flags & ASYNC_INITIALIZED) {
 		/*
 		 * Before we drop DTR, make sure the UART transmitter
@@ -1554,16 +1655,23 @@
 		char_time = 1;
 	if (timeout)
 	  char_time = MIN(char_time, timeout);
-#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+#ifdef SERIAL_DEBUG_WAIT_UNTIL_SENT
 	printk("In sab82532_wait_until_sent(%d) check=%lu...", timeout, char_time);
 	printk("jiff=%lu...", jiffies);
 #endif
-
-	/* XXX: Implement this... */
-
+	while (info->xmit_cnt || !info->all_sent) {
+		current->state = TASK_INTERRUPTIBLE;
+		current->counter = 0;
+		current->timeout = jiffies + char_time;
+		schedule();
+		if (signal_pending(current))
+			break;
+		if (timeout && (orig_jiffies + timeout) < jiffies)
+			break;
+	}
 	current->state = TASK_RUNNING;
-#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
-	printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+#ifdef SERIAL_DEBUG_WAIT_UNTIL_SENT
+	printk("xmit_cnt = %d, alls = %d (jiff=%lu)...done\n", info->xmit_cnt, info->all_sent, jiffies);
 #endif
 }
 
@@ -1573,10 +1681,13 @@
 static void sab82532_hangup(struct tty_struct *tty)
 {
 	struct sab82532 * info = (struct sab82532 *)tty->driver_data;
-	
+
 	if (serial_paranoia_check(info, tty->device, "sab82532_hangup"))
 		return;
 
+	if (info->is_console)
+		return;
+
 	sab82532_flush_buffer(tty);
 	shutdown(info);
 	info->event = 0;
@@ -1754,7 +1865,6 @@
 		return -ENODEV;
 	}
 
-	info->count++;
 	if (serial_paranoia_check(info, tty->device, "sab82532_open"))
 		return -ENODEV;
 
@@ -1762,6 +1872,8 @@
 	printk("sab82532_open %s%d, count = %d\n", tty->driver.name, info->line,
 	       info->count);
 #endif
+
+	info->count++;
 	tty->driver_data = info;
 	info->tty = tty;
 
@@ -1774,7 +1886,22 @@
 		else
 			tmp_buf = (unsigned char *) page;
 	}
-	
+
+	/*
+	 * If the port is in the middle of closing, bail out now.
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ASYNC_CLOSING)) {
+		if (info->flags & ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+		return ((info->flags & ASYNC_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+#else
+		return -EAGAIN;
+#endif
+	}
+
 	/*
 	 * Start up serial port
 	 */
@@ -1801,6 +1928,14 @@
 		change_speed(info);
 	}
 
+#ifdef CONFIG_SERIAL_CONSOLE
+	if (sab82532_console.cflag && sab82532_console.index == line) {
+		tty->termios->c_cflag = sab82532_console.cflag;
+		sab82532_console.cflag = 0;
+		change_speed(info);
+	}
+#endif
+
 	info->session = current->session;
 	info->pgrp = current->pgrp;
 
@@ -1856,7 +1991,7 @@
 	int i, len = 0;
 	off_t	begin = 0;
 
-	len += sprintf(page, "serinfo:1.0 driver:%s\n", "$Revision: 1.4 $");
+	len += sprintf(page, "serinfo:1.0 driver:%s\n", "$Revision: 1.13 $");
 	for (i = 0; i < NR_PORTS && len < 4000; i++) {
 		len += line_info(page + len, sab82532_table[i]);
 		if (len+begin > off+count)
@@ -1881,7 +2016,7 @@
  * sab82532_init() is called at boot-time to initialize the serial driver.
  * ---------------------------------------------------------------------
  */
-__initfunc(static int get_sab82532(void))
+__initfunc(static int get_sab82532(unsigned long *memory_start))
 {
 	struct linux_ebus *ebus;
 	struct linux_ebus_device *edev;
@@ -1895,23 +2030,29 @@
 	if (!edev)
 		return -ENODEV;
 
-	printk("%s: SAB82532 at 0x%lx IRQ %x\n", __FUNCTION__,
-	       edev->base_address[0], edev->irqs[0]);
-
 	regs = edev->base_address[0];
 	offset = sizeof(union sab82532_async_regs);
 
 	for (i = 0; i < 2; i++) {
-		sab = (struct sab82532 *)kmalloc(sizeof(struct sab82532),
-						 GFP_KERNEL);
-		if (!sab) {
-			printk("sab82532: can't alloc sab struct\n");
-			break;
+		if (memory_start) {
+			*memory_start = (*memory_start + 7) & ~(7);
+			sab = (struct sab82532 *)*memory_start;
+			*memory_start += sizeof(struct sab82532);
+		} else {
+			sab = (struct sab82532 *)kmalloc(sizeof(struct sab82532),
+							 GFP_KERNEL);
+			if (!sab) {
+				printk("sab82532: can't alloc sab struct\n");
+				break;
+			}
 		}
 		memset(sab, 0, sizeof(struct sab82532));
 
 		sab->regs = (union sab82532_async_regs *)(regs + offset);
 		sab->irq = edev->irqs[0];
+		sab->line = 1 - i;
+		sab->xmit_fifo_size = 32;
+		sab->recv_fifo_size = 32;
 
 		if (check_region((unsigned long)sab->regs,
 				 sizeof(union sab82532_async_regs))) {
@@ -1932,20 +2073,8 @@
 	return 0;
 }
 
-/* Hooks for running a serial console. con_init() calls this if the
- * console is run over one of the ttya/ttyb serial ports.
- * 'chip' should be zero, as for now we only have one chip on board.
- * 'line' is decoded as 0=ttya, 1=ttyb.
- */
-void
-sab82532_cons_hook(int chip, int out, int line)
-{
-	prom_printf("sab82532: serial console is not implemented, yet\n");
-	prom_halt();
-}
-
-void
-sab82532_kgdb_hook(int line)
+__initfunc(static void
+sab82532_kgdb_hook(int line))
 {
 	prom_printf("sab82532: kgdb support is not implemented, yet\n");
 	prom_halt();
@@ -1953,7 +2082,7 @@
 
 __initfunc(static inline void show_serial_version(void))
 {
-	char *revision = "$Revision: 1.4 $";
+	char *revision = "$Revision: 1.13 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -1971,7 +2100,7 @@
 	int i;
 
 	if (!sab82532_chain)
-		get_sab82532();
+		get_sab82532(0);
 	if (!sab82532_chain)
 		return -ENODEV;
 
@@ -2035,9 +2164,6 @@
 
 	for (info = sab82532_chain, i = 0; info; info = info->next, i++) {
 		info->magic = SERIAL_MAGIC;
-		info->line = i;
-		info->tty = 0;
-		info->count = 0;
 
 		info->type = info->regs->r.vstr & 0x0f;
 		info->regs->w.pcr = ~((1 << 1) | (1 << 2) | (1 << 4));
@@ -2053,14 +2179,11 @@
 		info->regs->rw.mode |= SAB82532_MODE_FRTS;
 		info->regs->rw.mode |= SAB82532_MODE_RTS;
 
-		info->xmit_fifo_size = 32;
-		info->recv_fifo_size = 32;
 		info->custom_divisor = 16;
 		info->close_delay = 5*HZ/10;
 		info->closing_wait = 30*HZ;
 		info->x_char = 0;
 		info->event = 0;	
-		info->count = 0;
 		info->blocked_open = 0;
 		info->tqueue.routine = do_softint;
 		info->tqueue.data = info;
@@ -2086,9 +2209,9 @@
 			}
 		}
 	
-		printk(KERN_INFO "ttyS%02d at 0x%lx (irq = %x) is a %s\n",
+		printk(KERN_INFO "ttyS%02d at 0x%lx (irq = %x) is a SAB82532 %s\n",
 		       info->line, (unsigned long)info->regs, info->irq,
-		       "SAB82532");
+		       sab82532_version[info->type]);
 	}
 	return 0;
 }
@@ -2125,8 +2248,10 @@
 	return -ENODEV;
 
 found:
+#ifdef CONFIG_SERIAL_CONSOLE
+	sunserial_setinitfunc(memory_start, sab82532_console_init);
+#endif
 	sunserial_setinitfunc(memory_start, sab82532_init);
-	rs_ops.rs_cons_hook = sab82532_cons_hook;
 	rs_ops.rs_kgdb_hook = sab82532_kgdb_hook;
 	return 0;
 }
@@ -2134,7 +2259,7 @@
 #ifdef MODULE
 int init_module(void)
 {
-	if (get_sab82532())
+	if (get_sab82532(0))
 		return -ENODEV;
 
 	return sab82532_init();
@@ -2171,3 +2296,200 @@
 	}
 }
 #endif /* MODULE */
+
+#ifdef CONFIG_SERIAL_CONSOLE
+
+static void
+sab82532_console_putchar(struct sab82532 *info, char c)
+{
+	while (info->regs->r.star & SAB82532_STAR_TEC)
+		udelay(1);
+	info->regs->w.tic = c;
+}
+
+static void
+sab82532_console_write(struct console *con, const char *s, unsigned n)
+{
+	struct sab82532 *info;
+	int i;
+
+	info = sab82532_chain + con->index;
+
+	for (i = 0; i < n; i++) {
+		if (*s == '\n')
+			sab82532_console_putchar(info, '\r');
+		sab82532_console_putchar(info, *s++);
+	}
+	while (info->regs->r.star & SAB82532_STAR_TEC)
+		udelay(1);
+}
+
+static int
+sab82532_console_wait_key(struct console *con)
+{
+	sleep_on(&keypress_wait);
+	return 0;
+}
+
+static kdev_t
+sab82532_console_device(struct console *con)
+{
+	return MKDEV(TTY_MAJOR, 64 + con->index);
+}
+
+static int
+sab82532_console_setup(struct console *con, char *options)
+{
+	struct sab82532 *info;
+	unsigned int	ebrg;
+	tcflag_t	cflag;
+	unsigned char	dafo;
+	int		i, bits;
+	unsigned long	flags;
+
+	info = sab82532_chain + con->index;
+	info->is_console = 1;
+
+	/*
+	 * Initialize the hardware
+	 */
+	sab82532_init_line(info);
+
+	/*
+	 * Finally, enable interrupts
+	 */
+	info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+				SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
+	info->regs->w.imr0 = info->interrupt_mask0;
+	info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
+				SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
+				SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
+				SAB82532_IMR1_XPR;
+	info->regs->w.imr1 = info->interrupt_mask1;
+
+	printk("Console: ttyS%d (SAB82532)\n", info->line);
+
+	sunserial_console_termios(con);
+	cflag = con->cflag;
+
+	/* Byte size and parity */
+	switch (cflag & CSIZE) {
+	      case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break;
+	      case CS6: dafo = SAB82532_DAFO_CHL6; bits = 8; break;
+	      case CS7: dafo = SAB82532_DAFO_CHL7; bits = 9; break;
+	      case CS8: dafo = SAB82532_DAFO_CHL8; bits = 10; break;
+	      /* Never happens, but GCC is too dumb to figure it out */
+	      default:  dafo = SAB82532_DAFO_CHL5; bits = 7; break;
+	}
+
+	if (cflag & CSTOPB) {
+		dafo |= SAB82532_DAFO_STOP;
+		bits++;
+	}
+
+	if (cflag & PARENB) {
+		dafo |= SAB82532_DAFO_PARE;
+		bits++;
+	}
+
+	if (cflag & PARODD) {
+#ifdef CMSPAR
+		if (cflag & CMSPAR)
+			dafo |= SAB82532_DAFO_PAR_MARK;
+		else
+#endif
+			dafo |= SAB82532_DAFO_PAR_ODD;
+	} else {
+#ifdef CMSPAR
+		if (cflag & CMSPAR)
+			dafo |= SAB82532_DAFO_PAR_SPACE;
+		else
+#endif
+			dafo |= SAB82532_DAFO_PAR_EVEN;
+	}
+
+	/* Determine EBRG values based on baud rate */
+	i = cflag & CBAUD;
+	if (i & CBAUDEX) {
+		i &= ~(CBAUDEX);
+		if ((i < 1) || ((i + 15) >= NR_EBRG_VALUES))
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			i += 15;
+	}
+	ebrg = ebrg_table[i].n;
+	ebrg |= (ebrg_table[i].m << 6);
+
+	if (ebrg_table[i].baud)
+		info->timeout = (info->xmit_fifo_size * HZ * bits) / ebrg_table[i].baud;
+	else
+		info->timeout = 0;
+	info->timeout += HZ / 50;		/* Add .02 seconds of slop */
+
+	/* CTS flow control flags */
+	if (cflag & CRTSCTS)
+		info->flags |= ASYNC_CTS_FLOW;
+	else
+		info->flags &= ~(ASYNC_CTS_FLOW);
+
+	if (cflag & CLOCAL)
+		info->flags &= ~(ASYNC_CHECK_CD);
+	else
+		info->flags |= ASYNC_CHECK_CD;
+
+	save_flags(flags); cli();
+	if (info->regs->r.star & SAB82532_STAR_CEC)
+		udelay(1);
+	while (info->regs->r.star & SAB82532_STAR_TEC)
+		udelay(1);
+	info->regs->w.dafo = dafo;
+	info->regs->w.bgr = ebrg & 0xff;
+	info->regs->rw.ccr2 &= ~(0xc0);
+	info->regs->rw.ccr2 |= (ebrg >> 2) & 0xc0;
+	if (info->flags & ASYNC_CTS_FLOW) {
+		info->regs->rw.mode &= ~(SAB82532_MODE_RTS);
+		info->regs->rw.mode |= SAB82532_MODE_FRTS;
+		info->regs->rw.mode &= ~(SAB82532_MODE_FCTS);
+	} else {
+		info->regs->rw.mode |= SAB82532_MODE_RTS;
+		info->regs->rw.mode &= ~(SAB82532_MODE_FRTS);
+		info->regs->rw.mode |= SAB82532_MODE_FCTS;
+	}
+	info->regs->rw.mode |= SAB82532_MODE_RAC;
+	restore_flags(flags);
+
+	return 0;
+}
+
+static struct console sab82532_console = {
+	"ttyS",
+	sab82532_console_write,
+	NULL,
+	sab82532_console_device,
+	sab82532_console_wait_key,
+	NULL,
+	sab82532_console_setup,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+
+__initfunc(int sab82532_console_init(void))
+{
+	extern int con_is_present(void);
+
+	if (con_is_present())
+		return 0;
+
+	if (!sab82532_chain) {
+		prom_printf("sab82532_console_setup: can't get SAB82532 chain");
+		prom_halt();
+	}
+
+	sab82532_console.index = serial_console - 1;
+	register_console(&sab82532_console);
+	return 0;
+}
+
+#endif /* CONFIG_SERIAL_CONSOLE */
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/sbuscons.c linux/drivers/sbus/char/sbuscons.c
--- v2.1.78/linux/drivers/sbus/char/sbuscons.c	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/sbuscons.c	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: sbuscons.c,v 1.7 1997/08/28 09:30:07 davem Exp $
+/* $Id: sbuscons.c,v 1.10 1998/01/07 06:37:22 baccala Exp $
  * sbuscons.c: Routines specific to SBUS frame buffer consoles.
  *
  * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
@@ -1168,7 +1168,7 @@
 		const int cpl = chars_per_line;
 		/* The register assignment is important here, do not modify without touching the assembly code as well */
 		register unsigned int x1 __asm__("g4"), x2 __asm__("g5"), x3 __asm__("g2"), x4 __asm__("g3"), flags __asm__("g7");
-		register unsigned int *dst __asm__("g1");
+		register unsigned int *dst;
 #else		
 		const int ipl = ints_per_line;
 		unsigned int data2, data3, data4;
@@ -1182,9 +1182,9 @@
 		if (j == ' ') /* space is quite common, so we optimize a bit */ {
 #ifdef ASM_BLITC
 #define BLITC_SPACE \
-		"\n\t std	%%g4, [%%g1]" \
-		"\n\t std	%%g4, [%%g1 + %0]" \
-		"\n\t add	%%g1, %1, %%g1"
+		"\n\t std	%3, [%0]" \
+		"\n\t std	%3, [%0 + %1]" \
+		"\n\t add	%0, %2, %0"
 #define BLITC_SPC \
 		"\n\t std	%0, [%1]" \
 		"\n\t std	%0, [%1 + %2]"
@@ -1195,7 +1195,7 @@
 			x3 = cpl << 1;
 			
 			__asm__ __volatile__ (
-				"\n\t mov	%2, %3"
+				"\n\t mov	%3, %4"
 				BLITC_SPACE
 				BLITC_SPACE
 				BLITC_SPACE
@@ -1203,12 +1203,19 @@
 				BLITC_SPACE
 				BLITC_SPACE
 				BLITC_SPACE
-					: : "r" (cpl), "r" (x3), "r" (x1), "r" (x2));
+				: "=r" (dst)
+				: "r" (cpl), "r" (x3), "r" (x1), "r" (x2));
 			__save_and_cli (flags);
 			if (idx != cursor_pos)
-				__asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (dst), "r" (cpl));
+				__asm__ __volatile__ (
+					BLITC_SPC
+					: /* no outputs */
+					: "r" (x1), "r" (dst), "r" (cpl));
 			else
-				__asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (under_cursor), "i" (8));
+				__asm__ __volatile__ (BLITC_SPC
+					: /* no outputs */
+					: "r" (x1), "r" (under_cursor),
+					  "i" (8));
 			__restore_flags (flags);
 #else
 			bgmask = attrib >> 4;
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/su.c linux/drivers/sbus/char/su.c
--- v2.1.78/linux/drivers/sbus/char/su.c	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/su.c	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: su.c,v 1.3 1997/09/03 11:54:56 ecd Exp $
+/* $Id: su.c,v 1.4 1997/09/07 15:40:19 ecd Exp $
  * su.c: Small serial driver for keyboard/mouse interface on Ultra/AX
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -150,16 +150,6 @@
 	struct su_struct *info = (struct su_struct *)dev_id;
 	unsigned char status;
 
-	/*
-	 * We might share interrupts with ps2kbd/ms driver,
-	 * in case we want to use the 16550A as general serial
-	 * driver in the presence of ps2 devices, so do a
-	 * sanity check here, needs to be done in ps2kbd/ms
-	 * driver, too.
-	 */
-	if (!info || info->magic != SERIAL_MAGIC)
-		return;
-
 #ifdef SERIAL_DEBUG_INTR
 	printk("su_interrupt(%d)...", irq);
 #endif
@@ -644,12 +634,12 @@
 				 * Does it match?
 				 */
 				if (sunode == kbnode) {
-					info->kbd_node = kbnode;
+					info->kbd_node = sunode;
 					++info;
 					++devices;
 				}
 				if (sunode == msnode) {
-					info->ms_node = msnode;
+					info->ms_node = sunode;
 					++info;
 					++devices;
 				}
@@ -674,5 +664,15 @@
 found:
         sunserial_setinitfunc(memory_start, su_init);
         rs_ops.rs_change_mouse_baud = su_change_mouse_baud;
+	sunkbd_setinitfunc(memory_start, sun_kbd_init);
+	kbd_ops.compute_shiftstate = sun_compute_shiftstate;
+	kbd_ops.setledstate = sun_setledstate;
+	kbd_ops.getledstate = sun_getledstate;
+	kbd_ops.setkeycode = sun_setkeycode;
+	kbd_ops.getkeycode = sun_getkeycode;
+	sunkbd_install_keymaps(memory_start, sun_key_maps, sun_keymap_count,
+			       sun_func_buf, sun_func_table,
+			       sun_funcbufsize, sun_funcbufleft,
+			       sun_accent_table, sun_accent_table_size);
 	return 0;
 }
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/suncons.c linux/drivers/sbus/char/suncons.c
--- v2.1.78/linux/drivers/sbus/char/suncons.c	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/suncons.c	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: suncons.c,v 1.73 1997/08/25 07:50:33 jj Exp $
+/* $Id: suncons.c,v 1.77 1997/12/19 07:32:59 ecd Exp $
  * suncons.c: Sparc platform console generic layer.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -100,13 +100,11 @@
 static unsigned long nop_con_type_init(unsigned long mem_start,
 				       const char **display_desc)
 {
-	prom_printf("YIEEE: nop_con_type_init called!\n");
 	return mem_start;
 }
 
 static void nop_con_type_init_finish(void)
 {
-	prom_printf("YIEEE: nop_con_type_init_finish called!\n");
 }
 
 static void nop_vesa_blank(void)
@@ -323,12 +321,17 @@
 extern void pci_console_inithook(void);
 #endif
 
+__initfunc(int con_is_present(void))
+{
+	return serial_console ? 0 : 1;
+}
+
 __initfunc(unsigned long sun_console_init(unsigned long memory_start))
 {
 	int i;
 
 	/* Nothing to do in this case. */
-	if(serial_console)
+	if (!con_is_present())
 		return memory_start;
 
 	fbinfo = (fbinfo_t *)memory_start;
@@ -358,7 +361,7 @@
 __initfunc(unsigned long pci_console_init(unsigned long memory_start))
 {
 	/* Nothing to do in this case. */
-	if(serial_console)
+	if (!con_is_present())
 		return memory_start;
 
 	if(pci_console_probe()) {
@@ -369,8 +372,6 @@
 	memory_start = finish_console_init(memory_start);
 
 	con_type_init_finish();
-	register_console(&vt_console_driver);
-
 	return memory_start;
 }
 #endif
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/sunfb.c linux/drivers/sbus/char/sunfb.c
--- v2.1.78/linux/drivers/sbus/char/sunfb.c	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/sunfb.c	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: sunfb.c,v 1.28 1997/08/22 15:55:23 jj Exp $
+/* $Id: sunfb.c,v 1.29 1997/09/20 20:47:26 davem Exp $
  * sunfb.c: Sun generic frame buffer support.
  *
  * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -45,7 +45,7 @@
 extern void set_cursor (int);
 
 #define FB_SETUP(err) \
-	int minor = FB_DEV (inode->i_rdev); \
+	int minor = FB_DEV (file->f_dentry->d_inode->i_rdev); \
 \
 	if (minor >= fbinfos || \
 	    fbinfo [minor].type.fb_type == FBTYPE_NOTYPE) \
@@ -229,7 +229,7 @@
 }
 
 static int
-fb_close (struct inode * inode, struct file *filp)
+fb_close (struct inode * inode, struct file *file)
 {
 	fbinfo_t *fb;
 	struct fbcursor cursor;
@@ -255,7 +255,7 @@
 
 	if (fb->open)
 		fb->open = 0;
-	fb_ioctl (inode, filp, FBIOSCURPOS, (unsigned long) &cursor);
+	fb_ioctl (inode, file, FBIOSCURPOS, (unsigned long) &cursor);
 	set_other_palette (minor);
 	if (!minor) {
 		render_screen ();
@@ -266,7 +266,7 @@
 }
 
 static int
-fb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma)
+fb_mmap (struct file *file, struct vm_area_struct *vma)
 {
 	fbinfo_t *fb;
 	FB_SETUP(ENXIO)
@@ -276,7 +276,7 @@
 	if (fb->mmap){
 		int v;
 		
-		v = (*fb->mmap)(inode, file, vma, fb->base, fb);
+		v = (*fb->mmap)(file->f_dentry->d_inode, file, vma, fb->base, fb);
 		if (v)
 			return v;
 		vma->vm_flags |= VM_IO;
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/sunkbd.c linux/drivers/sbus/char/sunkbd.c
--- v2.1.78/linux/drivers/sbus/char/sunkbd.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/sbus/char/sunkbd.c	Mon Jan 12 15:15:45 1998
@@ -6,6 +6,7 @@
  * compatibility - Miguel (miguel@nuclecu.unam.mx)
  *
  * Added PCI 8042 controller support -DaveM
+ * Added Magic SysRq support -MJ
  */
 
 #include <linux/config.h>
@@ -22,6 +23,7 @@
 #include <linux/random.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/sysrq.h>
 
 #include <asm/kbio.h>
 #include <asm/vuid_event.h>
@@ -77,10 +79,10 @@
 extern void scrollfront(int);
 
 struct l1a_kbd_state l1a_state = { 0, 0 };
-unsigned char kbd_read_mask = 0x01;	  /* modified by psaux.c */
-unsigned char aux_device_present = 0x00;  /* To make kernel/ksyms.c happy */
 
+#ifndef CONFIG_PCI
 struct wait_queue * keypress_wait = NULL;
+#endif
 
 void keyboard_wait_for_keypress(void)
 {
@@ -96,7 +98,6 @@
 /* shift state counters.. */
 static unsigned char k_down[NR_SHIFT] = {0, };
 /* keyboard key bitmap */
-#define BITS_PER_LONG (8*sizeof(unsigned long))
 static unsigned long key_down[256/BITS_PER_LONG] = { 0, };
 
 void push_kbd (int scan);
@@ -108,7 +109,9 @@
  * the variable must be global, or a new procedure must be created to 
  * return the value. I chose the former way.
  */
+#ifndef CONFIG_PCI
 /*static*/ int shift_state = 0;
+#endif
 static int npadch = -1;			/* -1 or number assembled on pad */
 static unsigned char diacr = 0;
 static char rep = 0;			/* flag telling character repeat */
@@ -120,7 +123,7 @@
 static int kbd_delay_ticks = HZ / 5;
 static int kbd_rate_ticks = HZ / 20;
 
-extern void compute_shiftstate(void);
+void sun_compute_shiftstate(void);
 
 typedef void (*k_hand)(unsigned char value, char up_flag);
 typedef void (k_handfn)(unsigned char value, char up_flag);
@@ -151,13 +154,16 @@
 };
 
 /* maximum values each key_handler can handle */
+#ifndef CONFIG_PCI
 const int max_vals[] = {
 	255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1,
 	NR_DEAD - 1, 255, 3, NR_SHIFT - 1,
-	255, NR_ASCII - 1, NR_LOCK - 1, 255
+	255, NR_ASCII - 1, NR_LOCK - 1, 255,
+	NR_LOCK - 1
 };
 
 const int NR_TYPES = SIZE(max_vals);
+#endif
 
 static void put_queue(int);
 static unsigned char handle_diacr(unsigned char);
@@ -165,6 +171,18 @@
 /* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
 static struct pt_regs * pt_regs;
 
+#ifdef CONFIG_MAGIC_SYSRQ
+unsigned char sun_sysrq_xlate[128] =
+	"\0\0\0\0\0\201\202\212\203\213\204\214\205\0\206\0"	/* 0x00 - 0x0f */
+	"\207\210\211\0\0\0\0\0\0\0\0\0\0\03312"		/* 0x10 - 0x1f */
+	"34567890-=`\177\0=/*"					/* 0x20 - 0x2f */
+	"\0\0.\0\0\011qwertyuiop"				/* 0x30 - 0x3f */
+	"[]\177\000789-\0\0\0\0\0asd"				/* 0x40 - 0x4f */
+	"fghjkl;'\\\015\0154560\0"				/* 0x50 - 0x5f */
+	"\0\0\0\0zxcvbnm,./\0\012"				/* 0x60 - 0x6f */
+	"123\0\0\0\0\0\0 \0\0\0\0\0\0";				/* 0x70 - 0x7f */
+#endif
+
 volatile unsigned char sunkbd_layout;
 volatile unsigned char sunkbd_type;
 #define SUNKBD_TYPE2        0x02
@@ -222,7 +240,7 @@
 #define KEY_ALT         0x86
 #define KEY_L1          0x87
 
-/* Do to kbd_init() being called before rs_init(), and kbd_init() doing:
+/* Do to sun_kbd_init() being called before rs_init(), and sun_kbd_init() doing:
  *
  *	init_bh(KEYBOARD_BH, kbd_bh);
  *	mark_bh(KEYBOARD_BH);
@@ -394,7 +412,7 @@
 };
 
 
-int setkeycode(unsigned int scancode, unsigned int keycode)
+int sun_setkeycode(unsigned int scancode, unsigned int keycode)
 {
 	if (scancode < SC_LIM || scancode > 255 || keycode > 127)
 	  return -EINVAL;
@@ -405,7 +423,7 @@
 	return 0;
 }
 
-int getkeycode(unsigned int scancode)
+int sun_getkeycode(unsigned int scancode)
 {
 	return
 	  (scancode < SC_LIM || scancode > 255) ? -EINVAL :
@@ -468,7 +486,7 @@
 	} else if(ch == SKBD_ALLUP) {
 		del_timer (&auto_repeat_timer);
 		memset(key_down, 0, sizeof(key_down));
-		compute_shiftstate();
+		sun_compute_shiftstate();
 		goto out;
 	}
 #ifdef SKBD_DEBUG
@@ -520,6 +538,14 @@
 		rep = test_and_set_bit(keycode, key_down);
 	}
 
+#ifdef CONFIG_MAGIC_SYSRQ			/* Handle the SysRq hack */
+	if (l1a_state.l1_down) {
+		if (!up_flag)
+			handle_sysrq(sun_sysrq_xlate[keycode], pt_regs, kbd, tty);
+		goto out;
+	}
+#endif
+
 	if(raw_mode)
 		goto out;
 
@@ -548,7 +574,7 @@
 		u_char type;
 
 		/* the XOR below used to be an OR */
-		int shift_final = shift_state ^ kbd->lockstate;
+		int shift_final = shift_state ^ kbd->lockstate ^ kbd->slockstate;
 		ushort *key_map = key_maps[shift_final];
 
 		if (key_map != NULL) {
@@ -572,7 +598,7 @@
 		} else {
 			/* maybe beep? */
 			/* we have at least to update shift_state */
-			compute_shiftstate();
+			sun_compute_shiftstate();
 		}
 	}
 out:
@@ -774,7 +800,7 @@
 
 static void do_null()
 {
-	compute_shiftstate();
+	sun_compute_shiftstate();
 }
 
 static void do_spec(unsigned char value, char up_flag)
@@ -981,7 +1007,7 @@
 /* called after returning from RAW mode or when changing consoles -
    recompute k_down[] and shift_state from key_down[] */
 /* maybe called when keymap is undefined, so that shiftkey release is seen */
-void compute_shiftstate(void)
+void sun_compute_shiftstate(void)
 {
 	int i, j, k, sym, val;
 
@@ -1055,11 +1081,11 @@
 static unsigned char sunkbd_ledstate = 0xff; /* undefined */
 static unsigned char ledioctl;
 
-unsigned char getledstate(void) {
+unsigned char sun_getledstate(void) {
     return ledstate;
 }
 
-void setledstate(struct kbd_struct *kbd, unsigned int led) {
+void sun_setledstate(struct kbd_struct *kbd, unsigned int led) {
     if (!(led & ~7)) {
 	ledioctl = led;
 	kbd->ledmode = LED_SHOW_IOCTL;
@@ -1181,7 +1207,7 @@
 
 extern void (*kd_mksound)(unsigned int hz, unsigned int ticks);
 
-__initfunc(int kbd_init(void))
+__initfunc(int sun_kbd_init(void))
 {
 	int i, opt_node;
 	struct kbd_struct kbd0;
@@ -1190,6 +1216,7 @@
 	kbd0.ledflagstate = kbd0.default_ledflagstate = KBD_DEFLEDS;
 	kbd0.ledmode = LED_SHOW_FLAGS;
 	kbd0.lockstate = KBD_DEFLOCK;
+	kbd0.slockstate = 0;
 	kbd0.modeflags = KBD_DEFMODE;
 	kbd0.kbdmode = VC_XLATE;
  
@@ -1245,8 +1272,8 @@
 	wake_up_interruptible (&kbd_wait);
 }
 
-static long
-kbd_read (struct inode *inode, struct file *f, char *buffer, unsigned long count)
+static ssize_t
+kbd_read (struct file *f, char *buffer, size_t count, loff_t *ppos)
 {
 	struct wait_queue wait = { current, NULL };
 	char *end, *p;
@@ -1278,12 +1305,11 @@
 }
 
 /* Needed by X */
-static int
-kbd_fasync (struct inode *inode, struct file *filp, int on)
+static int kbd_fasync (struct file *filp, int on)
 {
 	int retval;
 
-	retval = fasync_helper (inode, filp, on, &kb_fasync);
+	retval = fasync_helper (filp, on, &kb_fasync);
 	if (retval < 0)
 		return retval;
 	return 0;
@@ -1353,7 +1379,7 @@
 		if (c & LED_NLOCK) leds |= (1 << VC_NUMLOCK);
 		if (c & LED_CLOCK) leds |= (1 << VC_CAPSLOCK);
 		compose_led_on = !!(c & LED_CMPOSE);
-		setledstate(kbd_table + fg_console, leds);
+		sun_setledstate(kbd_table + fg_console, leds);
 		break;
 	case KIOCGLED:
 		put_user_ret(vcleds_to_sunkbd(getleds()), (unsigned char *) arg, -EFAULT);
@@ -1433,7 +1459,7 @@
 	kbd_redirected = 0;
 	kbd_opened = 0;
 
-	kbd_fasync (i, f, 0);
+	kbd_fasync (f, 0);
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/sunkbd.h linux/drivers/sbus/char/sunkbd.h
--- v2.1.78/linux/drivers/sbus/char/sunkbd.h	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/sunkbd.h	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: sunkbd.h,v 1.1 1997/08/28 02:23:34 ecd Exp $
+/* $Id: sunkbd.h,v 1.3 1997/09/08 03:05:10 tdyas Exp $
  * sunkbd.h: Defines needed by SUN Keyboard drivers
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -7,6 +7,8 @@
 #ifndef _SPARC_SUNKBD_H
 #define _SPARC_SUNKBD_H 1
 
+#include <linux/config.h>
+
 /* Keyboard defines for L1-A processing... */
 #define SUNKBD_RESET		0xff
 #define SUNKBD_L1		0x01
@@ -23,5 +25,27 @@
 extern void keyboard_zsinit(void (*kbd_put_char)(unsigned char));
 extern void sunkbd_inchar(unsigned char, struct pt_regs *);
 extern void batten_down_hatches(void);
+
+extern int sun_kbd_init(void);
+extern void sun_compute_shiftstate(void);
+extern void sun_setledstate(struct kbd_struct *, unsigned int);
+extern unsigned char sun_getledstate(void);
+extern int sun_setkeycode(unsigned int, unsigned int);
+extern int sun_getkeycode(unsigned int);
+
+#ifdef CONFIG_PCI
+
+extern ushort *sun_key_maps[MAX_NR_KEYMAPS];
+extern unsigned int sun_keymap_count;
+
+extern char sun_func_buf[];
+extern char *sun_func_table[MAX_NR_FUNC];
+extern int sun_funcbufsize;
+extern int sun_funcbufleft;
+
+extern struct kbdiacr sun_accent_table[MAX_DIACR];
+extern unsigned int sun_accent_table_size;
+
+#endif /* CONFIG_PCI */
 
 #endif /* !(_SPARC_SUNKBD_H) */
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/sunkbdmap.c linux/drivers/sbus/char/sunkbdmap.c
--- v2.1.78/linux/drivers/sbus/char/sunkbdmap.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/sunkbdmap.c	Mon Jan 12 15:15:45 1998
@@ -0,0 +1,33 @@
+
+/* $Id: sunkbdmap.c,v 1.1 1997/09/07 15:40:27 ecd Exp $
+ * sunkbdmap.c: Wrapper around sunkeymap.c to change table names.
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_PCI
+
+#define func_buf			sun_func_buf
+#define func_table			sun_func_table
+#define funcbufsize			sun_funcbufsize
+#define funcbufleft			sun_funcbufleft
+#define funcbufptr			sun_funcbufptr
+#define accent_table			sun_accent_table
+#define accent_table_size		sun_accent_table_size
+
+#define key_maps			sun_key_maps
+#define keymap_count			sun_keymap_count
+
+#define plain_map			sun_plain_map
+#define shift_map			sun_shift_map
+#define ctrl_map			sun_ctrl_map
+#define alt_map				sun_alt_map
+#define altgr_map			sun_altgr_map
+#define shift_ctrl_map			sun_shift_ctrl_map
+#define ctrl_alt_map			sun_ctrl_alt_map
+
+#endif /* CONFIG_PCI */
+
+#include "sunkeymap.c"
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/sunmouse.c linux/drivers/sbus/char/sunmouse.c
--- v2.1.78/linux/drivers/sbus/char/sunmouse.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/sbus/char/sunmouse.c	Mon Jan 12 15:15:45 1998
@@ -329,12 +329,11 @@
 	return 0;
 }
 
-static int
-sun_mouse_fasync (struct inode *inode, struct file *filp, int on)
+static int sun_mouse_fasync (struct file *filp, int on)
 {
 	int retval;
 
-	retval = fasync_helper (inode, filp, on, &sunmouse.fasync);
+	retval = fasync_helper (filp, on, &sunmouse.fasync);
 	if (retval < 0)
 		return retval;
 	return 0;
@@ -343,23 +342,23 @@
 static int
 sun_mouse_close(struct inode *inode, struct file *file)
 {
-	sun_mouse_fasync (inode, file, 0);
+	sun_mouse_fasync (file, 0);
 	if (--sunmouse.active)
 		return 0;
 	sunmouse.ready = 0;
 	return 0;
 }
 
-static long
-sun_mouse_write(struct inode *inode, struct file *file, const char *buffer,
-		unsigned long count)
+static ssize_t
+sun_mouse_write(struct file *file, const char *buffer,
+		size_t count, loff_t *ppos)
 {
 	return -EINVAL;  /* foo on you */
 }
 
-static long
-sun_mouse_read(struct inode *inode, struct file *file, char *buffer,
-	       unsigned long count)
+static ssize_t
+sun_mouse_read(struct file *file, char *buffer,
+	       size_t count, loff_t *ppos)
 {
 	struct wait_queue wait = { current, NULL };
 
@@ -399,7 +398,7 @@
 			}
 		}
 		sunmouse.ready = !queue_empty ();
-		inode->i_atime = CURRENT_TIME;
+		file->f_dentry->d_inode->i_atime = CURRENT_TIME;
 		return p-buffer;
 	} else {
 		int c;
@@ -410,7 +409,7 @@
 			sunmouse.tail = (sunmouse.tail + 1) % STREAM_SIZE;
 		}
 		sunmouse.ready = !queue_empty();
-		inode->i_atime = CURRENT_TIME;
+		file->f_dentry->d_inode->i_atime = CURRENT_TIME;
 		return count-c;
 	}
 	/* Only called if nothing was sent */
@@ -484,8 +483,11 @@
 
 __initfunc(int sun_mouse_init(void))
 {
+	if (!sunmouse.present)
+		return -ENODEV;
+
 	printk("Sun Mouse-Systems mouse driver version 1.00\n");
-	sunmouse.present = 1;
+
 	sunmouse.ready = sunmouse.active = 0;
 	misc_register (&sun_mouse_mouse);
 	sunmouse.delta_x = sunmouse.delta_y = 0;
@@ -498,5 +500,5 @@
 void
 sun_mouse_zsinit(void)
 {
-	sunmouse.ready = 1;
+	sunmouse.present = 1;
 }
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/sunserial.c linux/drivers/sbus/char/sunserial.c
--- v2.1.78/linux/drivers/sbus/char/sunserial.c	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/sunserial.c	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: sunserial.c,v 1.50 1997/09/03 11:54:59 ecd Exp $
+/* $Id: sunserial.c,v 1.56 1997/12/19 07:33:07 ecd Exp $
  * serial.c: Serial port driver infrastructure for the Sparc.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -9,17 +9,19 @@
 #include <linux/errno.h>
 #include <linux/tty.h>
 #include <linux/serial.h>
+#include <linux/string.h>
+#include <linux/kbd_diacr.h>
+#include <linux/version.h>
+#include <linux/init.h>
 
 #include <asm/oplib.h>
 
 #include "sunserial.h"
 
-static void nop_rs_cons_hook(int chip, int out, int line)
-{
-	printk("Oops: %s called\n", __FUNCTION__);
-}
+int serial_console;
 
-static void nop_rs_kgdb_hook(int channel)
+__initfunc(static void
+nop_rs_kgdb_hook(int channel))
 {
 	printk("Oops: %s called\n", __FUNCTION__);
 }
@@ -36,10 +38,8 @@
 	return 0;
 }
 
-
 struct sunserial_operations rs_ops = {
 	0,
-	nop_rs_cons_hook,
 	nop_rs_kgdb_hook,
 	nop_rs_change_mouse_baud,
 	nop_rs_read_proc
@@ -47,25 +47,41 @@
 
 int rs_init(void)
 {
-	struct rs_initfunc *init;
+	struct initfunc *init;
 	int err = -ENODEV;
 
 	init = rs_ops.rs_init;
 	while (init) {
-		err = init->rs_init();
+		err = init->init();
 		init = init->next;
 	}
 	return err;
 }
 
-void rs_cons_hook(int chip, int out, int line)
+__initfunc(void
+rs_kgdb_hook(int channel))
 {
-	rs_ops.rs_cons_hook(chip, out, line);
+	rs_ops.rs_kgdb_hook(channel);
 }
 
-void rs_kgdb_hook(int channel)
+__initfunc(static void sun_serial_finish_init(void))
 {
-	rs_ops.rs_kgdb_hook(channel);
+        extern unsigned char *linux_serial_image;
+	extern int con_is_present(void);
+        char buffer[2048];
+
+	if (con_is_present())
+		return;
+
+        sprintf (buffer, linux_serial_image, UTS_RELEASE);
+	printk(buffer);
+}
+
+__initfunc(long
+serial_console_init(long kmem_start, long kmem_end))
+{
+	sun_serial_finish_init();
+	return kmem_start;
 }
 
 void rs_change_mouse_baud(int baud)
@@ -88,45 +104,299 @@
 {
 }
 
+
+static void nop_compute_shiftstate (void)
+{
+	printk("Oops: %s called\n", __FUNCTION__);
+}
+
+static void nop_setledstate (struct kbd_struct *kbd, unsigned int ledstate)
+{
+	printk("Oops: %s called\n", __FUNCTION__);
+}
+
+static unsigned char nop_getledstate (void)
+{
+	printk("Oops: %s called\n", __FUNCTION__);
+	return 0;
+}
+
+static int nop_setkeycode (unsigned int scancode, unsigned int keycode)
+{
+	printk("Oops: %s called\n", __FUNCTION__);
+	return -EINVAL;
+}
+
+static int nop_getkeycode (unsigned int scancode)
+{
+	printk("Oops: %s called\n", __FUNCTION__);
+	return -EINVAL;
+}
+
+struct sunkbd_operations kbd_ops = {
+	0,
+	nop_compute_shiftstate,
+	nop_setledstate,
+	nop_getledstate,
+	nop_setkeycode,
+	nop_getkeycode
+};
+
+int kbd_init(void)
+{
+	struct initfunc *init;
+	int err = -ENODEV;
+
+	init = kbd_ops.kbd_init;
+	while (init) {
+		err = init->init();
+		init = init->next;
+	}
+	return err;
+}
+
+void compute_shiftstate (void)
+{
+	kbd_ops.compute_shiftstate();
+}
+
+void setledstate (struct kbd_struct *kbd, unsigned int ledstate)
+{
+	kbd_ops.setledstate(kbd, ledstate);
+}
+
+unsigned char getledstate (void)
+{
+	return kbd_ops.getledstate();
+}
+
+int setkeycode (unsigned int scancode, unsigned int keycode)
+{
+	return kbd_ops.setkeycode(scancode, keycode);
+}
+
+int getkeycode (unsigned int scancode)
+{
+	return kbd_ops.getkeycode(scancode);
+}
+
+
 void
 sunserial_setinitfunc(unsigned long *memory_start, int (*init) (void))
 {
-	struct rs_initfunc *rs_init;
+	struct initfunc *rs_init;
 
 	*memory_start = (*memory_start + 7) & ~(7);
-	rs_init = (struct rs_initfunc *) *memory_start;
-	*memory_start += sizeof(struct rs_initfunc);
+	rs_init = (struct initfunc *) *memory_start;
+	*memory_start += sizeof(struct initfunc);
 
-	rs_init->rs_init = init;
+	rs_init->init = init;
 	rs_init->next = rs_ops.rs_init;
 	rs_ops.rs_init = rs_init;
 }
 
+void
+sunserial_console_termios(struct console *con)
+{
+	char mode[16], buf[16], *s;
+	char *mode_prop = "ttyX-mode";
+	char *cd_prop = "ttyX-ignore-cd";
+	char *dtr_prop = "ttyX-rts-dtr-off";
+	int baud, bits, stop, cflag;
+	char parity;
+	int carrier = 0;
+	int rtsdtr = 1;
+	int topnd, nd;
+
+	if (!serial_console)
+		return;
+
+	if (serial_console == 1) {
+		mode_prop[3] = 'a';
+		cd_prop[3] = 'a';
+		dtr_prop[3] = 'a';
+	} else {
+		mode_prop[3] = 'b';
+		cd_prop[3] = 'b';
+		dtr_prop[3] = 'b';
+	}
+
+	topnd = prom_getchild(prom_root_node);
+	nd = prom_searchsiblings(topnd, "options");
+	if (!nd) {
+		strcpy(mode, "9600,8,n,1,-");
+		goto no_options;
+	}
+
+	if (!prom_node_has_property(nd, mode_prop)) {
+		strcpy(mode, "9600,8,n,1,-");
+		goto no_options;
+	}
+
+	memset(mode, 0, sizeof(mode));
+	prom_getstring(nd, mode_prop, mode, sizeof(mode));
+
+	if (prom_node_has_property(nd, cd_prop)) {
+		memset(buf, 0, sizeof(buf));
+		prom_getstring(nd, cd_prop, buf, sizeof(buf));
+		if (!strcmp(buf, "false"))
+			carrier = 1;
+
+		/* XXX: this is unused below. */
+	}
+
+	if (prom_node_has_property(nd, cd_prop)) {
+		memset(buf, 0, sizeof(buf));
+		prom_getstring(nd, cd_prop, buf, sizeof(buf));
+		if (!strcmp(buf, "false"))
+			rtsdtr = 0;
+
+		/* XXX: this is unused below. */
+	}
+
+no_options:
+	cflag = CREAD | HUPCL | CLOCAL;
+
+	s = mode;
+	baud = simple_strtoul(s, 0, 0);
+	s = strchr(s, ',');
+	bits = simple_strtoul(++s, 0, 0);
+	s = strchr(s, ',');
+	parity = *(++s);
+	s = strchr(s, ',');
+	stop = simple_strtoul(++s, 0, 0);
+	s = strchr(s, ',');
+	/* XXX handshake is not handled here. */
+
+	switch (baud) {
+		case 150: cflag |= B150; break;
+		case 300: cflag |= B300; break;
+		case 600: cflag |= B600; break;
+		case 1200: cflag |= B1200; break;
+		case 2400: cflag |= B2400; break;
+		case 4800: cflag |= B4800; break;
+		case 9600: cflag |= B9600; break;
+		case 19200: cflag |= B19200; break;
+		case 38400: cflag |= B38400; break;
+		default: cflag |= B9600; break;
+	}
+
+	switch (bits) {
+		case 5: cflag |= CS5; break;
+		case 6: cflag |= CS6; break;
+		case 7: cflag |= CS7; break;
+		case 8: cflag |= CS8; break;
+		default: cflag |= CS8; break;
+	}
+
+	switch (parity) {
+		case 'o': cflag |= (PARENB | PARODD); break;
+		case 'e': cflag |= PARENB; break;
+		case 'n': default: break;
+	}
+
+	switch (stop) {
+		case 2: cflag |= CSTOPB; break;
+		case 1: default: break;
+	}
+
+	con->cflag = cflag;
+}
+
+void
+sunkbd_setinitfunc(unsigned long *memory_start, int (*init) (void))
+{
+	struct initfunc *kbd_init;
+
+	*memory_start = (*memory_start + 7) & ~(7);
+	kbd_init = (struct initfunc *) *memory_start;
+	*memory_start += sizeof(struct initfunc);
+
+	kbd_init->init = init;
+	kbd_init->next = kbd_ops.kbd_init;
+	kbd_ops.kbd_init = kbd_init;
+}
+
+#ifdef CONFIG_PCI
+void
+sunkbd_install_keymaps(unsigned long *memory_start,
+		       ushort **src_key_maps, unsigned int src_keymap_count,
+		       char *src_func_buf, char **src_func_table,
+		       int src_funcbufsize, int src_funcbufleft,
+		       struct kbdiacr *src_accent_table,
+		       unsigned int src_accent_table_size)
+{
+	extern unsigned int keymap_count;
+	int i, j;
+
+	for (i = 0; i < MAX_NR_KEYMAPS; i++) {
+		if (src_key_maps[i]) {
+			if (!key_maps[i]) {
+				key_maps[i] = (ushort *)*memory_start;
+				*memory_start += NR_KEYS * sizeof(ushort);
+			}
+			for (j = 0; j < NR_KEYS; j++)
+				key_maps[i][j] = src_key_maps[i][j];
+		}
+		key_maps[i] = src_key_maps[i];
+	}
+	keymap_count = src_keymap_count;
+
+	for (i = 0; i < MAX_NR_FUNC; i++)
+		func_table[i] = src_func_table[i];
+	funcbufptr = src_func_buf;
+	funcbufsize = src_funcbufsize;
+	funcbufleft = src_funcbufleft;
+
+	for (i = 0; i < MAX_DIACR; i++)
+		accent_table[i] = src_accent_table[i];
+	accent_table_size = src_accent_table_size;
+}
+#endif
+
 extern int zs_probe(unsigned long *);
 #ifdef CONFIG_SAB82532
 extern int sab82532_probe(unsigned long *);
 #endif
-#ifdef __sparc_v9__
+#ifdef CONFIG_PCI
 extern int ps2kbd_probe(unsigned long *);
 extern int su_probe(unsigned long *);
 #endif
 
-unsigned long
-sun_serial_setup(unsigned long memory_start)
+__initfunc(unsigned long
+sun_serial_setup(unsigned long memory_start))
 {
+	int ret = -ENODEV;
+
 	/* Probe for controllers. */
-	if (zs_probe(&memory_start) == 0)
+	ret = zs_probe(&memory_start);
+	if (!ret)
 		return memory_start;
 
 #ifdef CONFIG_SAB82532
-	sab82532_probe(&memory_start);
+	ret = sab82532_probe(&memory_start);
 #endif
-#ifdef __sparc_v9__
-	if (ps2kbd_probe(&memory_start) == 0)
-		return memory_start;
-	if (su_probe(&memory_start) == 0)
-		return memory_start;
+#ifdef CONFIG_PCI
+	/*
+	 * Keyboard serial devices.
+	 *
+	 * Well done, Sun, prom_devopen("/pci@1f,4000/ebus@1/su@14,3083f8")
+	 * hangs the machine if no keyboard is connected to the device...
+	 * All PCI PROMs seem to do this, I have seen this on the Ultra 450
+	 * with version 3.5 PROM, and on the Ultra/AX with 3.1.5 PROM.
+	 *
+	 * So be very careful not to probe for keyboards if we are on a
+	 * serial console.
+	 */
+	if (!serial_console) {
+		if (ps2kbd_probe(&memory_start) == 0)
+			return memory_start;
+		if (su_probe(&memory_start) == 0)
+			return memory_start;
+	}
 #endif
+	if (!ret)
+		return memory_start;
 
 	prom_printf("No serial devices found, bailing out.\n");
 	prom_halt();
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/sunserial.h linux/drivers/sbus/char/sunserial.h
--- v2.1.78/linux/drivers/sbus/char/sunserial.h	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/char/sunserial.h	Mon Jan 12 15:15:45 1998
@@ -1,5 +1,5 @@
-/* $Id: sunserial.h,v 1.13 1997/09/03 11:55:00 ecd Exp $
- * sunserial.h: SUN serial driver infrastructure.
+/* $Id: sunserial.h,v 1.17 1997/12/19 07:33:12 ecd Exp $
+ * sunserial.h: SUN serial driver infrastructure (including keyboards).
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
  */
@@ -7,42 +7,45 @@
 #ifndef _SPARC_SUNSERIAL_H
 #define _SPARC_SUNSERIAL_H 1
 
+#include <linux/config.h>
 #include <linux/tty.h>
-
-struct rs_initfunc {
-	int	(*rs_init) (void);
-	struct rs_initfunc *next;
+#include <linux/kd.h>
+#include <linux/kbd_kern.h>
+#include <linux/console.h>
+
+struct initfunc {
+	int		(*init) (void);
+	struct initfunc *next;
 };
 
 struct sunserial_operations {
-	struct rs_initfunc	*rs_init;
-	void	(*rs_cons_hook) (int, int, int);
-	void	(*rs_kgdb_hook) (int);
-	void	(*rs_change_mouse_baud) (int);
-	int	(*rs_read_proc) (char *, char **, off_t, int, int *, void *);
+	struct initfunc	*rs_init;
+	void		(*rs_kgdb_hook) (int);
+	void		(*rs_change_mouse_baud) (int);
+	int		(*rs_read_proc) (char *, char **, off_t, int, int *, void *);
 };
 
-/*
- * XXX: Work in progress, don't worry this will go away in a few days. (ecd)
- * 
- * To support multiple keyboards in one binary we have to take care
- * about (at least) the following:
- * 
- * int	shift_state;
- * 
- * char	*func_buf;
- * char	*func_bufptr;
- * int	 funcbufsize;
- * int	 funcbufleft;
- * char	**func_table;
- * 
- * XXX: keymaps need to be handled...
- * 
- * struct kbd_struct	*kbd_table;
- * int	(*kbd_init)(void);
- */
+struct sunkbd_operations {
+	struct initfunc	*kbd_init;
+	void		(*compute_shiftstate) (void);
+	void		(*setledstate) (struct kbd_struct *, unsigned int);
+	unsigned char	(*getledstate) (void);
+	int		(*setkeycode) (unsigned int, unsigned int);
+	int		(*getkeycode) (unsigned int);
+};
 
 extern struct sunserial_operations rs_ops;
+extern struct sunkbd_operations kbd_ops;
+
 extern void sunserial_setinitfunc(unsigned long *, int (*) (void));
+extern void sunkbd_setinitfunc(unsigned long *, int (*) (void));
+
+extern int serial_console;
+extern void sunserial_console_termios(struct console *);
+
+#ifdef CONFIG_PCI
+extern void sunkbd_install_keymaps(unsigned long *, ushort **, unsigned int, char *,
+				   char **, int, int, struct kbdiacr *, unsigned int);
+#endif
 
 #endif /* !(_SPARC_SUNSERIAL_H) */
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/zs.c linux/drivers/sbus/char/zs.c
--- v2.1.78/linux/drivers/sbus/char/zs.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/sbus/char/zs.c	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: zs.c,v 1.3 1997/09/04 14:57:34 jj Exp $
+/* $Id: zs.c,v 1.15 1997/12/22 16:09:34 jj Exp $
  * zs.c: Zilog serial port driver for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -22,8 +22,8 @@
 #include <linux/keyboard.h>
 #include <linux/console.h>
 #include <linux/delay.h>
-#include <linux/version.h>
 #include <linux/init.h>
+#include <linux/sysrq.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -36,6 +36,9 @@
 #include <asm/sbus.h>
 #ifdef __sparc_v9__
 #include <asm/fhc.h>
+#ifdef CONFIG_PCI
+#include <linux/bios32.h>
+#endif
 #endif
 
 #include "sunserial.h"
@@ -52,7 +55,6 @@
 
 struct sun_zslayout **zs_chips;
 struct sun_zschannel **zs_channels;
-struct sun_zschannel *zs_conschan;
 struct sun_zschannel *zs_mousechan;
 struct sun_zschannel *zs_kbdchan;
 struct sun_zschannel *zs_kgdbchan;
@@ -63,12 +65,12 @@
 int zilog_irq;
 
 struct tty_struct *zs_ttys;
-/** struct tty_struct *zs_constty; **/
 
 /* Console hooks... */
-static int zs_cons_chanout = 0;
-static int zs_cons_chanin = 0;
-struct sun_serial *zs_consinfo = 0;
+#ifdef CONFIG_SERIAL_CONSOLE
+static struct console zs_console;
+static int zs_console_init(void);
+#endif
 
 static unsigned char kgdb_regs[16] = {
 	0, 0, 0,                     /* write 0, 1, 2 */
@@ -114,6 +116,8 @@
 /* number of characters left in xmit buffer before we ask for more */
 #define WAKEUP_CHARS 256
 
+#define SERIAL_DO_RESTART
+
 /* Debugging... DEBUG_INTR is bad to use when one of the zs
  * lines is your console ;(
  */
@@ -127,8 +131,7 @@
 #define _INLINE_ inline
 
 int zs_init(void);
-void zs_cons_hook(int, int, int);
-void zs_kgdb_hook(int);
+static void zs_kgdb_hook(int);
 
 static void change_speed(struct sun_serial *info);
 
@@ -443,20 +446,29 @@
 			return;
 		}
 		if(info->is_cons) {
+#ifdef CONFIG_MAGIC_SYSRQ
+			static int serial_sysrq;
+
+			if (!ch) {
+				serial_sysrq = 1;
+				return;
+			} else if (serial_sysrq) {
+				if (ch == 'a' || ch == 'A')
+					/* whee, break-A received */
+					batten_down_hatches();
+				else
+					handle_sysrq(ch, regs, NULL, NULL);
+				serial_sysrq = 0;
+				return;
+			}
+#else
 			if(ch==0) {
 				/* whee, break received */
 				batten_down_hatches();
 				/* Continue execution... */
 				return;
-#if 0
-			} else if (ch == 1) {
-				show_state();
-				return;
-			} else if (ch == 2) {
-				show_buffers();
-				return;
-#endif
 			}
+#endif
 			/* It is a 'keyboard interrupt' ;-) */
 			wake_up(&keypress_wait);
 		}
@@ -996,21 +1008,6 @@
 	restore_flags(flags);
 }
 
-
-/* This is for console output over ttya/ttyb */
-static void zs_cons_put_char(char ch)
-{
-	struct sun_zschannel *chan = zs_conschan;
-	unsigned long flags;
-
-	if(!chan)
-		return;
-
-	save_flags(flags); cli();
-	zs_put_char(chan, ch);
-	restore_flags(flags);
-}
-
 /* These are for receiving and sending characters under the kgdb
  * source level kernel debugger.
  */
@@ -1032,70 +1029,6 @@
 	return chan->data;
 }
 
-/*
- * Fair output driver allows a process to speak.
- */
-static void zs_fair_output(void)
-{
-	int left;		/* Output no more than that */
-	unsigned long flags;
-	struct sun_serial *info = zs_consinfo;
-	char c;
-
-	if (info == 0) return;
-	if (info->xmit_buf == 0) return;
-
-	save_flags(flags);  cli();
-	left = info->xmit_cnt;
-	while (left != 0) {
-		c = info->xmit_buf[info->xmit_tail];
-		info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
-		info->xmit_cnt--;
-		restore_flags(flags);
-
-		zs_cons_put_char(c);
-
-		cli();
-		left = MIN(info->xmit_cnt, left-1);
-	}
-
-	/* Last character is being transmitted now (hopefully). */
-	zs_conschan->control = RES_Tx_P;
-	udelay(5);
-
-	restore_flags(flags);
-	return;
-}
-
-/*
- * zs_console_print is registered for printk.
- */
-static void zs_console_print(const char *s, unsigned count)
-{
-	int i;
-
-	for (i = 0; i < count; i++, s++) {
-		if(*s == '\n')
-			zs_cons_put_char('\r');
-		zs_cons_put_char(*s);
-	}
-
-	/* Comment this if you want to have a strict interrupt-driven output */
-	zs_fair_output();
-}
-
-static void zs_console_wait_key(void)
-{
-	sleep_on(&keypress_wait);
-}
-
-static int zs_console_device(void)
-{
-	extern int serial_console;
-	
-	return MKDEV(TTYAUX_MAJOR, 64 + serial_console - 1);
-}
-
 static void zs_flush_chars(struct tty_struct *tty)
 {
 	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
@@ -1190,7 +1123,7 @@
 {
 	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
 	int	ret;
-				
+
 	if (serial_paranoia_check(info, tty->device, "zs_write_room"))
 		return 0;
 	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
@@ -1202,7 +1135,7 @@
 static int zs_chars_in_buffer(struct tty_struct *tty)
 {
 	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-				
+
 	if (serial_paranoia_check(info, tty->device, "zs_chars_in_buffer"))
 		return 0;
 	return info->xmit_cnt;
@@ -1211,7 +1144,7 @@
 static void zs_flush_buffer(struct tty_struct *tty)
 {
 	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-				
+
 	if (serial_paranoia_check(info, tty->device, "zs_flush_buffer"))
 		return;
 	cli();
@@ -1641,10 +1574,13 @@
 void zs_hangup(struct tty_struct *tty)
 {
 	struct sun_serial * info = (struct sun_serial *)tty->driver_data;
-	
+
 	if (serial_paranoia_check(info, tty->device, "zs_hangup"))
 		return;
-	
+
+	if (info->is_cons)
+		return;
+
 #ifdef SERIAL_DEBUG_OPEN
 	printk("zs_hangup<%p: tty-%d, count = %d bye\n",
 		__builtin_return_address(0), info->line, info->count);
@@ -1862,6 +1798,14 @@
 		change_speed(info);
 	}
 
+#ifdef CONFIG_SERIAL_CONSOLE
+	if (zs_console.cflag && zs_console.index == line) {
+		tty->termios->c_cflag = zs_console.cflag;
+		zs_console.cflag = 0;
+		change_speed(info);
+	}
+#endif
+
 	info->session = current->session;
 	info->pgrp = current->pgrp;
 
@@ -1875,7 +1819,7 @@
 
 static void show_serial_version(void)
 {
-	char *revision = "$Revision: 1.3 $";
+	char *revision = "$Revision: 1.15 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -1895,7 +1839,8 @@
 #ifdef __sparc_v9__
 static struct devid_cookie zs_dcookie;
 static unsigned long zs_irq_flags;
-static struct sun_zslayout *get_zs(int chip)
+__initfunc(static struct sun_zslayout *
+get_zs(int chip))
 {
 	unsigned int vaddr[2] = { 0, 0 };
 	int busnode, seen, zsnode, sun4u_ino;
@@ -1968,13 +1913,16 @@
 	return (struct sun_zslayout *)(unsigned long) vaddr[0];
 }
 #else /* !(__sparc_v9__) */
-static struct sun_zslayout *get_zs(int chip)
+__initfunc(static struct sun_zslayout *
+get_zs(int chip))
 {
 	struct linux_prom_irqs tmp_irq[2];
 	unsigned int paddr = 0;
 	unsigned int vaddr[2] = { 0, 0 };
-	int zsnode, tmpnode, iospace, slave, len, seen, sun4u_irq;
+	int zsnode, tmpnode, iospace, slave, len;
+	int cpunode = 0, bbnode = 0;
 	static int irq = 0;
+	int chipid = chip;
 
 #if CONFIG_AP1000
         printk("No zs chip\n");
@@ -2014,7 +1962,10 @@
 				if (board == (chip >> 1)) {
 					node = prom_getchild(tmpnode);
 					if (node && (node = prom_searchsiblings(node, "bootbus"))) {
-						zsnode = node;
+						cpunode = tmpnode;
+						bbnode = node;
+						zsnode = prom_getchild(node);
+						chipid = (chip & 1);
 						break;
 					}
 				}
@@ -2022,10 +1973,6 @@
 			}
 			if (!tmpnode)
 				panic ("get_zs: couldn't find board%d's bootbus\n", chip >> 1);
-		} else if (sparc_cpu_model == sun4u) {
-			tmpnode = prom_searchsiblings(zsnode, "sbus");
-			if(tmpnode)
-				zsnode = prom_getchild(tmpnode);
 		} else {
 			tmpnode = prom_searchsiblings(zsnode, "obio");
 			if(tmpnode)
@@ -2033,41 +1980,46 @@
 		}
 		if(!zsnode)
 			panic("get_zs no zs serial prom node");
-		seen = 0;
 		while(zsnode) {
 			zsnode = prom_searchsiblings(zsnode, "zs");
 			slave = prom_getintdefault(zsnode, "slave", -1);
-			if((slave == chip) ||
-			   (sparc_cpu_model == sun4u && seen == chip)) {
+			if(slave == chipid) {
 				/* The one we want */
-				len = prom_getproperty(zsnode, "address",
-						       (void *) vaddr,
-						       sizeof(vaddr));
-        			if (len % sizeof(unsigned int)) {
-					prom_printf("WHOOPS:  proplen for %s "
-						"was %d, need multiple of "
-						"%d\n", "address", len,
-						sizeof(unsigned int));
-					panic("zilog: address property");
-				}
-				zs_nodes[chip] = zsnode;
-				if(sparc_cpu_model == sun4u) {
-					len = prom_getproperty(zsnode, "interrupts",
-							       (char *) &sun4u_irq,
-							       sizeof(tmp_irq));
-					tmp_irq[0].pri = sun4u_irq;
-				} else {
-					len = prom_getproperty(zsnode, "intr",
-							       (char *) tmp_irq,
-							       sizeof(tmp_irq));
-					if (len % sizeof(struct linux_prom_irqs)) {
-						prom_printf(
-						      "WHOOPS:  proplen for %s "
-						      "was %d, need multiple of "
-						      "%d\n", "address", len,
-						      sizeof(struct linux_prom_irqs));
+				if (sparc_cpu_model != sun4d) {
+					len = prom_getproperty(zsnode, "address",
+							       (void *) vaddr,
+							       sizeof(vaddr));
+        				if (len % sizeof(unsigned int)) {
+						prom_printf("WHOOPS:  proplen for %s "
+							"was %d, need multiple of "
+							"%d\n", "address", len,
+							sizeof(unsigned int));
 						panic("zilog: address property");
 					}
+				} else {
+					/* On sun4d don't have address property :( */
+					struct linux_prom_registers zsreg[4];
+					
+					if (prom_getproperty(zsnode, "reg", (char *)zsreg, sizeof(zsreg)) == -1) {
+						prom_printf ("Cannot map zs regs\n");
+						prom_halt();
+					}
+					prom_apply_generic_ranges(bbnode, cpunode, zsreg, 1);
+					vaddr[0] = (unsigned long)
+							sparc_alloc_io(zsreg[0].phys_addr, 0, 8,
+								       "Zilog Serial", zsreg[0].which_io, 0);
+				}
+				zs_nodes[chip] = zsnode;
+				len = prom_getproperty(zsnode, "intr",
+						       (char *) tmp_irq,
+						       sizeof(tmp_irq));
+				if (len % sizeof(struct linux_prom_irqs)) {
+					prom_printf(
+					      "WHOOPS:  proplen for %s "
+					      "was %d, need multiple of "
+					      "%d\n", "address", len,
+					      sizeof(struct linux_prom_irqs));
+					panic("zilog: address property");
 				}
 				if(!irq) {
 					irq = zilog_irq = tmp_irq[0].pri;
@@ -2078,7 +2030,6 @@
 				break;
 			}
 			zsnode = prom_getsibling(zsnode);
-			seen++;
 		}
 		if(!zsnode)
 			panic("get_zs whee chip not found");
@@ -2089,242 +2040,6 @@
 	return (struct sun_zslayout *)(unsigned long) vaddr[0];
 }
 #endif
-
-static inline void
-init_zscons_termios(struct termios *termios)
-{
-	char mode[16], buf[16];
-	char *mode_prop = "ttyX-mode";
-	char *cd_prop = "ttyX-ignore-cd";
-	char *dtr_prop = "ttyX-rts-dtr-off";
-	char *s;
-	int baud, bits, cflag;
-	char parity;
-	int topnd, nd;
-	int channel, stop;
-	int carrier = 0;
-	int rtsdtr = 1;
-	extern int serial_console;
-
-	if (!serial_console)
-		return;
-
-	if (serial_console == 1) {
-		mode_prop[3] = 'a';
-		cd_prop[3] = 'a';
-		dtr_prop[3] = 'a';
-	} else {
-		mode_prop[3] = 'b';
-		cd_prop[3] = 'b';
-		dtr_prop[3] = 'b';
-	}
-
-	topnd = prom_getchild(prom_root_node);
-	nd = prom_searchsiblings(topnd, "options");
-	if (!nd) {
-		strcpy(mode, "9600,8,n,1,-");
-		goto no_options;
-	}
-
-	if (!prom_node_has_property(nd, mode_prop)) {
-		strcpy(mode, "9600,8,n,1,-");
-		goto no_options;
-	}
-
-	memset(mode, 0, sizeof(mode));
-	prom_getstring(nd, mode_prop, mode, sizeof(mode));
-
-	if (prom_node_has_property(nd, cd_prop)) {
-		memset(buf, 0, sizeof(buf));
-		prom_getstring(nd, cd_prop, buf, sizeof(buf));
-		if (!strcmp(buf, "false"))
-			carrier = 1;
-
-		/* XXX this is unused below. */
-	}
-
-	if (prom_node_has_property(nd, cd_prop)) {
-		memset(buf, 0, sizeof(buf));
-		prom_getstring(nd, cd_prop, buf, sizeof(buf));
-		if (!strcmp(buf, "false"))
-			rtsdtr = 0;
-
-		/* XXX this is unused below. */
-	}
-
-no_options:
-	cflag = CREAD | HUPCL | CLOCAL;
-
-	s = mode;
-	baud = simple_strtoul(s, 0, 0);
-	s = strchr(s, ',');
-	bits = simple_strtoul(++s, 0, 0);
-	s = strchr(s, ',');
-	parity = *(++s);
-	s = strchr(s, ',');
-	stop = simple_strtoul(++s, 0, 0);
-	s = strchr(s, ',');
-	/* XXX handshake is not handled here. */
-
-	for (channel = 0; channel < NUM_CHANNELS; channel++)
-		if (zs_soft[channel].is_cons)
-			break;
-
-	switch (baud) {
-		case 150:
-			cflag |= B150;
-			break;
-		case 300:
-			cflag |= B300;
-			break;
-		case 600:
-			cflag |= B600;
-			break;
-		case 1200:
-			cflag |= B1200;
-			break;
-		case 2400:
-			cflag |= B2400;
-			break;
-		case 4800:
-			cflag |= B4800;
-			break;
-		default:
-			baud = 9600;
-		case 9600:
-			cflag |= B9600;
-			break;
-		case 19200:
-			cflag |= B19200;
-			break;
-		case 38400:
-			cflag |= B38400;
-			break;
-	}
-	zs_soft[channel].zs_baud = baud;
-
-	switch (bits) {
-		case 5:
-			zscons_regs[3] = Rx5 | RxENAB;
-			zscons_regs[5] = Tx5 | TxENAB;
-			zs_soft[channel].parity_mask = 0x1f;
-			cflag |= CS5;
-			break;
-		case 6:
-			zscons_regs[3] = Rx6 | RxENAB;
-			zscons_regs[5] = Tx6 | TxENAB;
-			zs_soft[channel].parity_mask = 0x3f;
-			cflag |= CS6;
-			break;
-		case 7:
-			zscons_regs[3] = Rx7 | RxENAB;
-			zscons_regs[5] = Tx7 | TxENAB;
-			zs_soft[channel].parity_mask = 0x7f;
-			cflag |= CS7;
-			break;
-		default:
-		case 8:
-			zscons_regs[3] = Rx8 | RxENAB;
-			zscons_regs[5] = Tx8 | TxENAB;
-			zs_soft[channel].parity_mask = 0xff;
-			cflag |= CS8;
-			break;
-	}
-	zscons_regs[5] |= DTR;
-
-	switch (parity) {
-		case 'o':
-			zscons_regs[4] |= PAR_ENAB;
-			cflag |= (PARENB | PARODD);
-			break;
-		case 'e':
-			zscons_regs[4] |= (PAR_ENAB | PAR_EVEN);
-			cflag |= PARENB;
-			break;
-		default:
-		case 'n':
-			break;
-	}
-
-	switch (stop) {
-		default:
-		case 1:
-			zscons_regs[4] |= SB1;
-			break;
-		case 2:
-			cflag |= CSTOPB;
-			zscons_regs[4] |= SB2;
-			break;
-	}
-
-	termios->c_cflag = cflag;
-}
-
-__initfunc(static void serial_finish_init(void (*printfunc)(const char *, unsigned)))
-{
-	extern unsigned char *linux_serial_image;
-	char buffer[2048];
-	
-	sprintf (buffer, linux_serial_image, UTS_RELEASE);
-	(*printfunc)(buffer, strlen(buffer));
-}
-
-static inline void
-zs_cons_check(struct sun_serial *ss, int channel)
-{
-	int i, o, io;
-	static int consout_registered = 0;
-	static int msg_printed = 0;
-	static struct console console = {
-		zs_console_print, 0,
-		zs_console_wait_key, zs_console_device };
-
-	i = o = io = 0;
-
-	/* Is this one of the serial console lines? */
-	if((zs_cons_chanout != channel) &&
-	   (zs_cons_chanin != channel))
-		return;
-	zs_conschan = ss->zs_channel;
-	zs_consinfo = ss;
-
-	/* Register the console output putchar, if necessary */
-	if((zs_cons_chanout == channel)) {
-		o = 1;
-		/* double whee.. */
-		if(!consout_registered) {
-			serial_finish_init (zs_console_print);
-			register_console(&console);
-			consout_registered = 1;
-		}
-	}
-
-	/* If this is console input, we handle the break received
-	 * status interrupt on this line to mean prom_halt().
-	 */
-	if(zs_cons_chanin == channel) {
-		ss->break_abort = 1;
-		i = 1;
-	}
-	if(o && i)
-		io = 1;
-
-	/* Set flag variable for this port so that it cannot be
-	 * opened for other uses by accident.
-	 */
-	ss->is_cons = 1;
-
-	if(io) {
-		if(!msg_printed) {
-			printk("zs%d: console I/O\n", ((channel>>1)&1));
-			msg_printed = 1;
-		}
-	} else {
-		printk("zs%d: console %s\n", ((channel>>1)&1),
-		       (i==1 ? "input" : (o==1 ? "output" : "WEIRD")));
-	}
-}
-
 /* This is for the auto baud rate detection in the mouse driver. */
 void zs_change_mouse_baud(int newbaud)
 {
@@ -2347,10 +2062,11 @@
 	if(sparc_cpu_model == sun4)
 		goto no_probe;
 
+	NUM_SERIAL = 0;
+	
 	node = prom_getchild(prom_root_node);
 	if (sparc_cpu_model == sun4d) {
 		node = prom_searchsiblings(node, "boards");
-		NUM_SERIAL = 0;
 		if (!node)
 			panic ("Cannot find out count of boards");
 		else
@@ -2360,22 +2076,38 @@
 			node = prom_getsibling(node);
 		}
 		goto no_probe;
-	} else if (sparc_cpu_model == sun4u) {
-		node = prom_searchsiblings(node, "sbus");
-		if(node)
+	}
+#ifdef __sparc_v9__
+	else if (sparc_cpu_model == sun4u) {
+		int central_node;
+
+		/* Central bus zilogs must be checked for first,
+		 * since Enterprise boxes have SBUS as well.
+		 */
+		central_node = prom_finddevice("/central");
+		if(central_node != 0 && central_node != -1)
+			node = prom_searchsiblings(prom_getchild(central_node), "fhc");
+		else
+			node = prom_searchsiblings(node, "sbus");
+		if(node != 0 && node != -1)
 			node = prom_getchild(node);
-		if(!node)
+		if(node == 0 || node == -1)
 			return -ENODEV;
-	} else {
+	}
+#endif /* __sparc_v9__ */
+	else {
 		node = prom_searchsiblings(node, "obio");
 		if(node)
 			node = prom_getchild(node);
+		NUM_SERIAL = 2;
 		goto no_probe;
 	}
 
 	node = prom_searchsiblings(node, "zs");
 	if (!node)
 		return -ENODEV;
+		
+	NUM_SERIAL = 2;
 
 no_probe:
 	p = (char *)((*memory_start + 7) & ~7);
@@ -2399,17 +2131,85 @@
 	*memory_start = (((unsigned long)p) + i + 7) & ~7;
 
 	/* Fill in rs_ops struct... */
+#ifdef CONFIG_SERIAL_CONSOLE
+	sunserial_setinitfunc(memory_start, zs_console_init);
+#endif
 	sunserial_setinitfunc(memory_start, zs_init);
-	rs_ops.rs_cons_hook = zs_cons_hook;
 	rs_ops.rs_kgdb_hook = zs_kgdb_hook;
 	rs_ops.rs_change_mouse_baud = zs_change_mouse_baud;
 
+	sunkbd_setinitfunc(memory_start, sun_kbd_init);
+	kbd_ops.compute_shiftstate = sun_compute_shiftstate;
+	kbd_ops.setledstate = sun_setledstate;
+	kbd_ops.getledstate = sun_getledstate;
+	kbd_ops.setkeycode = sun_setkeycode;
+	kbd_ops.getkeycode = sun_getkeycode;
+#ifdef CONFIG_PCI
+	sunkbd_install_keymaps(memory_start, sun_key_maps, sun_keymap_count,
+			       sun_func_buf, sun_func_table,
+			       sun_funcbufsize, sun_funcbufleft,
+			       sun_accent_table, sun_accent_table_size);
+#endif
 	return 0;
 }
 
+static inline void zs_prepare(void)
+{
+	int channel, chip;
+	unsigned long flags;
+
+	if (!NUM_SERIAL) return;
+	
+	save_and_cli(flags);
+	
+	/* Set up our interrupt linked list */
+	zs_chain = &zs_soft[0];
+	for(channel = 0; channel < NUM_CHANNELS - 1; channel++) {
+		zs_soft[channel].zs_next = &zs_soft[channel + 1];
+		zs_soft[channel].line = channel;
+	}
+	zs_soft[channel].zs_next = 0;
+
+	/* Initialize Softinfo */
+	for(chip = 0; chip < NUM_SERIAL; chip++) {
+		/* If we are doing kgdb over one of the channels on
+		 * chip zero, kgdb_channel will be set to 1 by the
+		 * zs_kgdb_hook() routine below.
+		 */
+		if(!zs_chips[chip]) {
+			zs_chips[chip] = get_zs(chip);
+			/* Two channels per chip */
+			zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
+			zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
+			zs_soft[(chip*2)].kgdb_channel = 0;
+			zs_soft[(chip*2)+1].kgdb_channel = 0;
+		}
+
+		/* First, set up channel A on this chip. */
+		channel = chip * 2;
+		zs_soft[channel].zs_channel = zs_channels[channel];
+		zs_soft[channel].change_needed = 0;
+		zs_soft[channel].clk_divisor = 16;
+		zs_soft[channel].cons_keyb = 0;
+		zs_soft[channel].cons_mouse = 0;
+		zs_soft[channel].channelA = 1;
+
+		/* Now, channel B */
+		channel++;
+		zs_soft[channel].zs_channel = zs_channels[channel];
+		zs_soft[channel].change_needed = 0;
+		zs_soft[channel].clk_divisor = 16;
+		zs_soft[channel].cons_keyb = 0;
+		zs_soft[channel].cons_mouse = 0;
+		zs_soft[channel].channelA = 0;
+	}
+	
+	restore_flags(flags);
+}
+
 __initfunc(int zs_init(void))
 {
-	int chip, channel, brg, i;
+	int channel, brg, i;
 	unsigned long flags;
 	struct sun_serial *info;
 	char dummy;
@@ -2420,7 +2220,7 @@
 #endif
 
 #ifdef CONFIG_PCI
-	if (prom_searchsiblings(prom_getchild(prom_root_node), "pci"))
+	if (pcibios_present())
 		return 0;
 #endif
 
@@ -2444,7 +2244,6 @@
 	serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
 	serial_driver.subtype = SERIAL_TYPE_NORMAL;
 	serial_driver.init_termios = tty_std_termios;
-
 	serial_driver.init_termios.c_cflag =
 		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
 	serial_driver.flags = TTY_DRIVER_REAL_RAW;
@@ -2472,8 +2271,6 @@
 	serial_driver.read_proc = 0;
 	serial_driver.proc_entry = 0;
 
-	init_zscons_termios(&serial_driver.init_termios);
-
 	/*
 	 * The callout device is just like normal device except for
 	 * major number and the subtype code.
@@ -2487,48 +2284,11 @@
 		panic("Couldn't register serial driver\n");
 	if (tty_register_driver(&callout_driver))
 		panic("Couldn't register callout driver\n");
-	
-	save_flags(flags); cli();
 
-	/* Set up our interrupt linked list */
-	zs_chain = &zs_soft[0];
-	for(channel = 0; channel < NUM_CHANNELS - 1; channel++)
-		zs_soft[channel].zs_next = &zs_soft[channel + 1];
-	zs_soft[channel + 1].zs_next = 0;
+	save_flags(flags); cli();
 
 	/* Initialize Softinfo */
-	for(chip = 0; chip < NUM_SERIAL; chip++) {
-		/* If we are doing kgdb over one of the channels on
-		 * chip zero, kgdb_channel will be set to 1 by the
-		 * zs_kgdb_hook() routine below.
-		 */
-		if(!zs_chips[chip]) {
-			zs_chips[chip] = get_zs(chip);
-			/* Two channels per chip */
-			zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
-			zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
-			zs_soft[(chip*2)].kgdb_channel = 0;
-			zs_soft[(chip*2)+1].kgdb_channel = 0;
-		}
-
-		/* First, set up channel A on this chip. */
-		channel = chip * 2;
-		zs_soft[channel].zs_channel = zs_channels[channel];
-		zs_soft[channel].change_needed = 0;
-		zs_soft[channel].clk_divisor = 16;
-		zs_soft[channel].cons_keyb = 0;
-		zs_soft[channel].cons_mouse = 0;
-		zs_soft[channel].channelA = 1;
-
-		/* Now, channel B */
-		channel++;
-		zs_soft[channel].zs_channel = zs_channels[channel];
-		zs_soft[channel].change_needed = 0;
-		zs_soft[channel].clk_divisor = 16;
-		zs_soft[channel].cons_keyb = 0;
-		zs_soft[channel].cons_mouse = 0;
-		zs_soft[channel].channelA = 0;
-	}
+	zs_prepare();
 
 	/* Initialize Hardware */
 	for(channel = 0; channel < NUM_CHANNELS; channel++) {
@@ -2719,50 +2479,13 @@
 	return 0;
 }
 
-/* Hooks for running a serial console.  con_init() calls this if the
- * console is being run over one of the ttya/ttyb serial ports.
- * 'chip' should be zero, as chip 1 drives the mouse/keyboard.
- * 'channel' is decoded as 0=TTYA 1=TTYB, note that the channels
- * are addressed backwards, channel B is first, then channel A.
- */
-void
-zs_cons_hook(int chip, int out, int line)
-{
-	int channel;
-
-#ifdef CONFIG_PCI
-	if (prom_searchsiblings(prom_getchild(prom_root_node), "pci"))
-		return;
-#endif
-
-	if(chip)
-		panic("zs_cons_hook called with chip not zero");
-	if(line != 1 && line != 2)
-		panic("zs_cons_hook called with line not ttya or ttyb");
-	channel = line - 1;
-	if(!zs_chips[chip]) {
-		zs_chips[chip] = get_zs(chip);
-		/* Two channels per chip */
-		zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
-		zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
-	}
-	zs_soft[channel].zs_channel = zs_channels[channel];
-	zs_soft[channel].change_needed = 0;
-	zs_soft[channel].clk_divisor = 16;
-	if(out)
-		zs_cons_chanout = ((chip * 2) + channel);
-	else
-		zs_cons_chanin = ((chip * 2) + channel);
-	zs_cons_check(&zs_soft[channel], channel);
-}
-
 /* This is called at boot time to prime the kgdb serial debugging
  * serial line.  The 'tty_num' argument is 0 for /dev/ttya and 1
  * for /dev/ttyb which is determined in setup_arch() from the
  * boot command line flags.
  */
-void
-zs_kgdb_hook(int tty_num)
+__initfunc(static void
+zs_kgdb_hook(int tty_num))
 {
 	int chip = 0;
 
@@ -2784,3 +2507,183 @@
         ZS_CLEARERR(zs_kgdbchan);
         ZS_CLEARFIFO(zs_kgdbchan);
 }
+
+#ifdef CONFIG_SERIAL_CONSOLE
+
+/* This is for console output over ttya/ttyb */
+static void
+zs_console_putchar(struct sun_serial *info, char ch)
+{
+	unsigned long flags;
+
+	if(!info->zs_channel)
+		return;
+
+	save_flags(flags); cli();
+	zs_put_char(info->zs_channel, ch);
+	restore_flags(flags);
+}
+
+/*
+ * Fair output driver allows a process to speak.
+ */
+static void zs_fair_output(struct sun_serial *info)
+{
+	int left;		/* Output no more than that */
+	unsigned long flags;
+	char c;
+
+	if (info == 0) return;
+	if (info->xmit_buf == 0) return;
+
+	save_flags(flags);  cli();
+	left = info->xmit_cnt;
+	while (left != 0) {
+		c = info->xmit_buf[info->xmit_tail];
+		info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
+		info->xmit_cnt--;
+		restore_flags(flags);
+
+		zs_console_putchar(info, c);
+
+		cli();
+		left = MIN(info->xmit_cnt, left-1);
+	}
+
+	/* Last character is being transmitted now (hopefully). */
+	info->zs_channel->control = RES_Tx_P;
+	udelay(5);
+
+	restore_flags(flags);
+	return;
+}
+
+/*
+ * zs_console_write is registered for printk.
+ */
+static void
+zs_console_write(struct console *con, const char *s, unsigned count)
+{
+	struct sun_serial *info;
+	int i;
+
+	info = zs_soft + con->index;
+
+	for (i = 0; i < count; i++, s++) {
+		if(*s == '\n')
+			zs_console_putchar(info, '\r');
+		zs_console_putchar(info, *s);
+	}
+
+	/* Comment this if you want to have a strict interrupt-driven output */
+	zs_fair_output(info);
+}
+
+static int
+zs_console_wait_key(struct console *con)
+{
+	sleep_on(&keypress_wait);
+	return 0;
+}
+
+static kdev_t zs_console_device(struct console *con)
+{
+	return MKDEV(TTY_MAJOR, 64 + con->index);
+}
+
+__initfunc(static int
+zs_console_setup(struct console *con, char *options))
+{
+	struct sun_serial *info;
+	int i, brg, baud;
+
+	info = zs_soft + con->index;
+	info->is_cons = 1;
+
+	printk("Console: ttyS%d (Zilog8530)\n", info->line);
+
+	sunserial_console_termios(con);
+
+	i = con->cflag & CBAUD;
+	if (con->cflag & CBAUDEX) {
+		i &= ~CBAUDEX;
+		con->cflag &= ~CBAUDEX;
+	}
+	baud = baud_table[i];
+	info->zs_baud = baud;
+
+	switch (con->cflag & CSIZE) {
+		case CS5:
+			zscons_regs[3] = Rx5 | RxENAB;
+			zscons_regs[5] = Tx5 | TxENAB;
+			info->parity_mask = 0x1f;
+			break;
+		case CS6:
+			zscons_regs[3] = Rx6 | RxENAB;
+			zscons_regs[5] = Tx6 | TxENAB;
+			info->parity_mask = 0x3f;
+			break;
+		case CS7:
+			zscons_regs[3] = Rx7 | RxENAB;
+			zscons_regs[5] = Tx7 | TxENAB;
+			info->parity_mask = 0x7f;
+			break;
+		default:
+		case CS8:
+			zscons_regs[3] = Rx8 | RxENAB;
+			zscons_regs[5] = Tx8 | TxENAB;
+			info->parity_mask = 0xff;
+			break;
+	}
+	zscons_regs[5] |= DTR;
+
+	if (con->cflag & PARENB)
+		zscons_regs[4] |= PAR_ENAB;
+	if (!(con->cflag & PARODD))
+		zscons_regs[4] |= PAR_EVEN;
+
+	if (con->cflag & CSTOPB)
+		zscons_regs[4] |= SB2;
+	else
+		zscons_regs[4] |= SB1;
+
+	brg = BPS_TO_BRG(baud, ZS_CLOCK / info->clk_divisor);
+	zscons_regs[12] = brg & 0xff;
+	zscons_regs[13] = (brg >> 8) & 0xff;
+
+	memcpy(info->curregs, zscons_regs, sizeof(zscons_regs));
+	load_zsregs(info, zscons_regs);
+
+	ZS_CLEARERR(info->zs_channel);
+	ZS_CLEARFIFO(info->zs_channel);
+	return 0;
+}
+
+static struct console zs_console = {
+	"ttyS",
+	zs_console_write,
+	NULL,
+	zs_console_device,
+	zs_console_wait_key,
+	NULL,
+	zs_console_setup,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+
+__initfunc(static int
+zs_console_init(void))
+{
+	extern int con_is_present(void);
+
+	if (con_is_present())
+		return 0;
+
+	zs_console.index = serial_console - 1;
+	register_console(&zs_console);
+	return 0;
+}
+
+#endif /* CONFIG_SERIAL_CONSOLE */
diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/sbus.c linux/drivers/sbus/sbus.c
--- v2.1.78/linux/drivers/sbus/sbus.c	Thu Sep  4 17:07:31 1997
+++ linux/drivers/sbus/sbus.c	Mon Jan 12 15:15:45 1998
@@ -65,13 +65,16 @@
 
 	base = (unsigned long) sbus_dev->reg_addrs[0].phys_addr;
 	if(base>=SUN_SBUS_BVADDR ||
-	   sparc_cpu_model == sun4m ||
-	   sparc_cpu_model == sun4u) {
+	   (sparc_cpu_model != sun4c &&
+	   sparc_cpu_model != sun4)) {
 		/* Ahh, we can determine the slot and offset */
 		if(sparc_cpu_model == sun4u) {
 			/* A bit tricky on the SYSIO. */
 			sbus_dev->slot = sbus_dev->reg_addrs[0].which_io;
 			sbus_dev->offset = sbus_dev_offset(base);
+		} else if (sparc_cpu_model == sun4d) {
+			sbus_dev->slot = sbus_dev->reg_addrs[0].which_io;
+			sbus_dev->offset = base;
 		} else {
 			sbus_dev->slot = sbus_dev_slot(base);
 			sbus_dev->offset = sbus_dev_offset(base);
@@ -181,19 +184,20 @@
 extern unsigned long sun_console_init(unsigned long);
 extern unsigned long iommu_init(int iommu_node, unsigned long memstart,
 				unsigned long memend, struct linux_sbus *sbus);
-extern void iommu_sun4d_init(int sbi_node, struct linux_sbus *sbus);
+extern unsigned long iounit_init(int sbi_node, int iounit_node, unsigned long memstart,
+				unsigned long memend, struct linux_sbus *sbus);
 #ifdef CONFIG_SUN_OPENPROMIO
 extern int openprom_init(void);
 #endif
 #ifdef CONFIG_SUN_MOSTEK_RTC
 extern int rtc_init(void);
 #endif
-#ifdef CONFIG_SPARCAUDIO
-extern int sparcaudio_init(void);
-#endif
 #ifdef CONFIG_SUN_AUXIO
 extern void auxio_probe(void);
 #endif
+#ifdef CONFIG_OBP_FLASH
+extern int flash_init(void);
+#endif
 
 __initfunc(static unsigned long 
 sbus_do_child_siblings(unsigned long memory_start, int start_node,
@@ -247,7 +251,7 @@
 	memory_start = ((memory_start + 7) & (~7));
 
 	topnd = prom_getchild(prom_root_node);
-
+	
 	/* Finding the first sbus is a special case... */
 	iommund = 0;
 	if(sparc_cpu_model == sun4u) {
@@ -287,19 +291,10 @@
 	sbus->next = 0;
 	this_sbus=nd;
 
-	if(sparc_cpu_model != sun4u)
-		/* Have IOMMU will travel.
-		 *
-		 * XXX This should be per sbus on sun4d...
-		 */
-		if(iommund) {
-			if (sparc_cpu_model == sun4d)
-				iommu_sun4d_init(this_sbus, sbus);
-			else
-				memory_start = iommu_init(iommund,
-							  memory_start, memory_end,
-							  sbus);
-	}
+	if(iommund && sparc_cpu_model != sun4u && sparc_cpu_model != sun4d)
+		memory_start = iommu_init(iommund,
+					  memory_start, memory_end,
+					  sbus);
 
 	/* Loop until we find no more SBUS's */
 	while(this_sbus) {
@@ -311,7 +306,12 @@
 			memory_start = iommu_init(this_sbus,
 						  memory_start, memory_end,
 						  sbus);
-
+#ifndef __sparc_v9__						  
+		else if (sparc_cpu_model == sun4d)
+			memory_start = iounit_init(this_sbus, iommund,
+						   memory_start, memory_end,
+						   sbus);
+#endif						   
 #ifdef E3000_DEBUG
 		prom_printf("1");
 #endif
@@ -323,9 +323,16 @@
 			      (((sbus_clock/1000)%1000) + 1000) : 0));
 
 		prom_getstring(this_sbus, "name", lbuf, sizeof(lbuf));
+		lbuf[sizeof(sbus->prom_name) - 1] = 0;
 		sbus->prom_node = this_sbus;
 		strcpy(sbus->prom_name, lbuf);
 		sbus->clock_freq = sbus_clock;
+#ifndef __sparc_v9__		
+		if (sparc_cpu_model == sun4d) {
+			sbus->devid = prom_getint(iommund, "device-id");
+			sbus->board = prom_getint(iommund, "board#");
+		}
+#endif
 		
 #ifdef E3000_DEBUG
 		prom_printf("psri()");
@@ -458,6 +465,12 @@
 			break;
 		}
 	} /* while(this_sbus) */
+	if (sparc_cpu_model == sun4d) {
+		extern unsigned long sun4d_init_sbi_irq(unsigned long);
+		
+		memory_start = sun4d_init_sbi_irq(memory_start);
+	}
+	
 #ifdef E3000_DEBUG
 	prom_printf("sbus_init: No more sbus's, calling sun_console_init()\n");
 #endif
@@ -471,15 +484,15 @@
 #ifdef CONFIG_SUN_MOSTEK_RTC
 	rtc_init();
 #endif
-#ifdef CONFIG_SPARCAUDIO
-	sparcaudio_init();
-#endif
 #ifdef CONFIG_SUN_BPP
 	bpp_init();
 #endif
 #ifdef CONFIG_SUN_AUXIO
 	if (sparc_cpu_model == sun4u)
 		auxio_probe ();
+#endif
+#ifdef CONFIG_OBP_FLASH
+	flash_init();
 #endif
 #ifdef __sparc_v9__
 	if (sparc_cpu_model == sun4u) {
diff -u --recursive --new-file v2.1.78/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx
--- v2.1.78/linux/drivers/scsi/ChangeLog.ncr53c8xx	Wed Sep 24 20:05:47 1997
+++ linux/drivers/scsi/ChangeLog.ncr53c8xx	Mon Jan 12 15:05:27 1998
@@ -1,3 +1,30 @@
+Fri Jan 2 18:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* Revision 2.5f
+	- Use FAST-5 instead of SLOW for slow scsi devices according to 
+	  new SPI-2 draft.
+	- Make some changes in order to accomodate with 875 rev <= 3 
+	  device errata listing 397. Minor consequences are:
+	  . Leave use of PCI Write and Invalidate under user control.
+	    Now, by default the driver does not enable PCI MWI and option
+	    'specf:y' is required in order to enable this feature.
+	  . Memory Read Line is not enabled for 875 and 875-like chips.
+	  . Programmed burst length set to 64 DWORDS (instead of 128).
+	    (Note: SYMBIOS uses 32 DWORDS for the SDMS BIOS)
+
+Sun Oct 26 12:00 1997 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.5e
+	- Add 'buschk' boot option.
+	  This option enables checking of SCSI BUS data lines after SCSI 
+	  RESET (set by default). (Submitted by Richard Waltham).
+	- Update the README file.
+
+Sat Oct 4 18:00 1997 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.5d
+	- Dispatch CONDITION MET and RESERVATION CONFLICT scsi status 
+	  as OK driver status.
+	- Update the README file and the Symbios NVRAM format definition 
+	  with removable media flags values (available with SDMS 4.09).
+
 Sat Sep 20 21:00 1997 Gerard Roudier (groudier@club-internet.fr)
 	* revision 2.5c
 	- Several PCI configuration registers fix-ups for powerpc.
diff -u --recursive --new-file v2.1.78/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx
--- v2.1.78/linux/drivers/scsi/README.ncr53c8xx	Wed Sep  3 20:52:43 1997
+++ linux/drivers/scsi/README.ncr53c8xx	Mon Jan 12 15:05:27 1998
@@ -4,7 +4,7 @@
 21 Rue Carnot
 95170 DEUIL LA BARRE - FRANCE
 
-23 August 1997
+2 January 1998
 ===============================================================================
 
 1.  Introduction
@@ -30,6 +30,7 @@
       10.3 Advised boot setup commands
       10.4 PCI configuration fix-up boot option
       10.5 Serial NVRAM support boot option
+      10.6 SCSI BUS checking boot option
 11. Some constants and flags of the ncr53c8xx.h header file
 12. Installation
       12.1 Provided files
@@ -38,6 +39,8 @@
 14. Known problems
       14.1 Tagged commands with Iomega Jaz device
       14.2 Device names change when another controller is added
+      14.3 Using only 8 bit devices with a WIDE SCSI controller.
+      14.4 Possible data corruption during a Memory Write and Invalidate
 15. SCSI problem troubleshooting
 16. Synchonous transfer negotiation tables
       16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
@@ -490,7 +493,9 @@
 
 10.1 Syntax
 
-Setup commands can be passed to the driver at boot time.
+Setup commands can be passed to the driver either at boot time or as a 
+string variable using 'insmod'.
+
 A boot setup command for the ncr53c8xx driver begins with the driver name 
 "ncr53c8xx=". The kernel syntax parser then expects an optionnal list of
 integers separated with comma followed by an optionnal list of  comma- 
@@ -502,7 +507,14 @@
 - set synchronous negotiation speed to 10 Mega-transfers / second.
 - set DEBUG_NEGO flag.
 
-For the moment, the integer list of arguments is disgarded by the driver. 
+Since comma seems not to be allowed when defining a string variable using  
+'insmod', the driver also accepts <space> as option separator. 
+The following command will install driver module with the same options as 
+above.
+
+insmod ncr53c8xx.o ncr53c8xx="tags:4 sync:10 debug:0x200"
+
+For the moment, the integer list of arguments is discarded by the driver. 
 It will be used in the future in order to allow a per controller setup.
 
 Each string argument must be specified as "keyword:value". Only lower-case 
@@ -525,8 +537,12 @@
 Special features
    Only apply to 810A, 825A, 860 and 875 controllers.
    Have no effect with normal 810 and 825.
-      specf:y    enabled
-      specf:n    disabled
+      specf:y    (or 1) enabled
+      specf:n    (or 0) disabled
+      specf:3           enabled except Memory Write And Invalidate
+   The default driver setup is 'specf:3'. As a consequence, option 'specf:y' 
+   must be specified in the boot setup command to enable Memory Write And 
+   Invalidate.
 
 Ultra SCSI support
    Only apply to 860 and 875 controllers.
@@ -628,6 +644,7 @@
     pcifix:<option bits>
 
     Available option bits:
+        0x0:   No attempt to fix PCI configuration space registers values.
         0x1:   Set PCI cache-line size register if not set.
         0x2:   Set write and invalidate bit in PCI command register.
         0x4:   Increase if necessary PCI latency timer according to burst max.
@@ -638,6 +655,14 @@
     nvram:n     do not look for serial NVRAM
     nvram:y     test controllers for onboard serial NVRAM
 
+Check SCSI BUS 
+    buschk:<option bits>
+
+    Available option bits:
+        0x0:   No check.
+        0x1:   Check and donnot attach the controller on error.  
+        0x2:   Check and just warn on error.
+
 Boot fail safe
     safe:y	load the following assumed fail safe initial setup
 
@@ -660,13 +685,14 @@
   settle time			10 seconds		settle:10
   differential support		from BIOS settings	diff:1
   irq mode			from BIOS settings	irqm:1
+  SCSI BUS check		donnot attach on error	buschk:1
 
 10.3 Advised boot setup commands
 
 If the driver has been configured with default options, the equivalent 
 boot setup is:
 
-   ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
+   ncr53c8xx=mpar:y,spar:y,disc:y,specf:3,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
              tags:0,sync:50,debug:0,burst:7,led:0,wide:1,settle:2,diff:0,irqm:0
 
 For an installation diskette or a safe but not fast system,
@@ -679,7 +705,7 @@
 
 My personnal system works flawlessly with the following equivalent setup:
 
-   ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
+   ncr53c8xx=mpar:y,spar:y,disc:y,specf:1,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
              tags:8,sync:12,debug:0,burst:7,led:1,wide:1,settle:2,diff:0,irqm:0
 
 The driver prints its actual setup when verbosity level is 2. You can try 
@@ -765,6 +791,21 @@
 use its setup for synchronous and wide negotiations.
 
 
+10.6 SCSI BUS checking boot option.
+
+When this option is set to a non-zero value, the driver checks SCSI lines 
+logic state, 100 micro-seconds after having asserted the SCSI RESET line.
+The driver just reads SCSI lines and checks all lines read FALSE except RESET.
+Since SCSI devices shall release the BUS at most 800 nano-seconds after SCSI 
+RESET has been asserted, any signal to TRUE may indicate a SCSI BUS problem.
+Unfortunately, the following common SCSI BUS problems are not detected:
+- Only 1 terminator installed.
+- Misplaced terminators.
+- Bad quality terminators.
+On the other hand, either bad cabling, broken devices, not conformant 
+devices, ... may cause a SCSI signal to be wrong when te driver reads it.
+
+
 11. Some constants and flags of the ncr53c8xx.h header file
 
 Some of these are defined from the configuration parameters.  To
@@ -952,6 +993,50 @@
 - Make appropriate changes in the fstab.
 - Use the 'scsidev' tool from Eric Youngdale.
 
+14.3 Using only 8 bit devices with a WIDE SCSI controller.
+
+When only 8 bit NARROW devices are connected to a 16 bit WIDE SCSI controller, 
+you must ensure that lines of the wide part of the SCSI BUS are pulled-up.
+This can be achieved by ENABLING the WIDE TERMINATOR portion of the SCSI 
+controller card.
+The TYAN 1365 documentation revision 1.2 is not correct about such settings.
+(page 10, figure 3.3).
+
+14.4 Possible data corruption during a Memory Write and Invalidate
+
+This problem is described in SYMBIOS DEL 397, Part Number 69-039241, ITEM 4.
+
+In some complex situations, 53C875 chips revision <= 3 may start a PCI 
+Write and Invalidate Command at a not cache-line-aligned 4 DWORDS boundary.
+This is only possible when Cache Line Size is 8 DWORDS or greater.
+Pentium systems use a 8 DWORDS cache line size and so are concerned by 
+this chip bug, unlike i486 systems that use a 4 DWORDS cache line size.
+
+When this situation occurs, the chip may complete the Write and Invalidate 
+command after having only filled part of the last cache line involved in 
+the transfer, leaving to data corruption the remainder of this cache line.
+
+Not using Write And Invalidate obviously gets rid of this chip bug, and so 
+it is now the default setting of the driver.
+However, for people like me who want to enable this feature, I have added 
+part of a work-around suggested by SYMBIOS. This work-around resets the 
+addressing logic when the DATA IN phase is entered and so prevents the bug 
+from being triggered for the first SCSI MOVE of the phase. This work-around 
+should be enough according to the following:
+
+The only driver internal data structure that is greater than 8 DWORDS  and 
+that is moved by the SCRIPTS processor is the 'CCB header' that contains 
+the context of the SCSI transfer. This data structure is aligned on 8 DWORDS 
+boundary (Pentium Cache Line Size), and so is immune to this chip bug, at 
+least on Pentium systems.
+But the conditions of this bug can be met when a SCSI read command is 
+performed using a buffer that is 4 DWORDS but not cache-line aligned.
+This cannot happen under Linux when scatter/gather lists are used since 
+they only refer to system buffers that are well aligned. So, a work around 
+may only be needed under Linux when a scatter/gather list is not used and 
+when the SCSI DATA IN phase is reentered after a phase mismatch.
+
+ 
 15. SCSI problem troubleshooting
 
 Most SCSI problems are due to a non conformant SCSI bus or to buggy
@@ -1216,8 +1301,14 @@
 controller set up
 
 00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00
-                   |     |                 |
-                   |     |                  -- host ID
+                   |     |           |     |
+                   |     |           |      -- host ID
+                   |     |           |
+                   |     |            --Removable Media Support
+                   |     |               0x00 = none
+                   |     |               0x01 = Bootable Device
+                   |     |               0x02 = All with Media
+                   |     |
                    |      --flag bits 2
                    |        0x00000001= scan order hi->low
                    |            (default 0x00 - scan low->hi)
@@ -1230,6 +1321,7 @@
 current set up for any of the controllers.
 
 default set up is identical for 53c810a and 53c875 NVRAM
+(Removable Media added Symbios BIOS version 4.09)
 -----------------------------------------------------------
 boot configuration
 
diff -u --recursive --new-file v2.1.78/linux/drivers/scsi/ide-scsi.c linux/drivers/scsi/ide-scsi.c
--- v2.1.78/linux/drivers/scsi/ide-scsi.c	Fri Dec 19 15:52:59 1997
+++ linux/drivers/scsi/ide-scsi.c	Sat Jan 10 10:42:55 1998
@@ -1,7 +1,7 @@
 /*
- * linux/drivers/scsi/ide-scsi.c	Version 0.4		Dec   7, 1997
+ * linux/drivers/scsi/ide-scsi.c	Version 0.5		Jan   2, 1998
  *
- * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
+ * Copyright (C) 1996 - 1998 Gadi Oxman <gadio@netvision.net.il>
  */
 
 /*
@@ -18,9 +18,11 @@
  *                       Added Scatter/Gather and DMA support.
  * Ver 0.4   Dec  7 97   Add support for ATAPI PD/CD drives.
  *                       Use variable timeout for each command.
+ * Ver 0.5   Jan  2 98   Fix previous PD/CD support.
+ *                       Allow disabling of SCSI-6 to SCSI-10 transformation.
  */
 
-#define IDESCSI_VERSION "0.4"
+#define IDESCSI_VERSION "0.5"
 
 #include <linux/module.h>
 #include <linux/types.h>
@@ -71,6 +73,7 @@
 	ide_drive_t *drive;
 	idescsi_pc_t *pc;			/* Current packet command */
 	unsigned int flags;			/* Status/Action flags */
+	int transform;				/* Transform SCSI-6 commands */
 } idescsi_scsi_t;
 
 /*
@@ -102,7 +105,7 @@
 }
 
 /*
- *	PIO data transfer routines using the scather gather table.
+ *	PIO data transfer routines using the scatter gather table.
  */
 static void idescsi_input_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount)
 {
@@ -110,7 +113,7 @@
 
 	while (bcount) {
 		if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) {
-			printk (KERN_ERR "ide-scsi: scather gather table too small, discarding data\n");
+			printk (KERN_ERR "ide-scsi: scatter gather table too small, discarding data\n");
 			idescsi_discard_data (drive, bcount);
 			return;
 		}
@@ -130,7 +133,7 @@
 
 	while (bcount) {
 		if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) {
-			printk (KERN_ERR "ide-scsi: scather gather table too small, padding with zeros\n");
+			printk (KERN_ERR "ide-scsi: scatter gather table too small, padding with zeros\n");
 			idescsi_output_zeros (drive, bcount);
 			return;
 		}
@@ -150,14 +153,17 @@
  */
 static inline void idescsi_transform_pc1 (ide_drive_t *drive, idescsi_pc_t *pc)
 {
+	idescsi_scsi_t *scsi = drive->driver_data;
 	u8 *c = pc->c, *buf = pc->buffer, *sc = pc->scsi_cmd->cmnd;
 	int i;
 
+	if (!scsi->transform)
+		return;
 	if (drive->media == ide_cdrom) {
 		if (c[0] == READ_6 || c[0] == WRITE_6) {
 			c[8] = c[4];		c[5] = c[3];		c[4] = c[2];
 			c[3] = c[1] & 0x1f;	c[2] = 0;		c[1] &= 0xe0;
-			c[0] = READ_10;
+			c[0] += (READ_10 - READ_6);
 		}
 		if (c[0] == MODE_SENSE || (c[0] == MODE_SELECT && buf[3] == 8)) {
 			pc->request_transfer -= 4;
@@ -172,9 +178,12 @@
 
 static inline void idescsi_transform_pc2 (ide_drive_t *drive, idescsi_pc_t *pc)
 {
+	idescsi_scsi_t *scsi = drive->driver_data;
 	u8 *buf = pc->buffer;
 	int i;
 
+	if (!scsi->transform)
+		return;
 	if (drive->media == ide_cdrom) {
 		if (pc->c[0] == MODE_SENSE_10 && pc->scsi_cmd->cmnd[0] == MODE_SENSE) {
 			buf[0] = buf[1];	buf[1] = buf[2];
@@ -182,6 +191,8 @@
 			for (i = pc->buffer_size - 1; i >= 12; i--) buf[i] = buf[i - 4];
 			for (i = 11; i >= 4; i--) buf[i] = 0;
 		}
+		if (pc->c[0] == INQUIRY)
+			buf[2] |= 2;
 	}
 }
 
@@ -400,6 +411,19 @@
 static ide_drive_t *idescsi_drives[MAX_HWIFS * MAX_DRIVES];
 static int idescsi_initialized = 0;
 
+static void idescsi_add_settings(ide_drive_t *drive)
+{
+	idescsi_scsi_t *scsi = drive->driver_data;
+
+/*
+ *			drive	setting name	read/write	ioctl	ioctl		data type	min	max	mul_factor	div_factor	data pointer		set function
+ */
+	ide_add_setting(drive,	"bios_cyl",	SETTING_RW,	-1,	-1,		TYPE_SHORT,	0,	1023,	1,		1,		&drive->bios_cyl,	NULL);
+	ide_add_setting(drive,	"bios_head",	SETTING_RW,	-1,	-1,		TYPE_BYTE,	0,	255,	1,		1,		&drive->bios_head,	NULL);
+	ide_add_setting(drive,	"bios_sect",	SETTING_RW,	-1,	-1,		TYPE_BYTE,	0,	63,	1,		1,		&drive->bios_sect,	NULL);
+	ide_add_setting(drive,	"transform",	SETTING_RW,	-1,	-1,		TYPE_INT,	0,	1,	1,		1,		&scsi->transform,	NULL);
+}
+
 /*
  *	Driver initialization.
  */
@@ -413,6 +437,8 @@
 	scsi->drive = drive;
 	if (drive->id && (drive->id->config & 0x0060) == 0x20)
 		set_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags);
+	scsi->transform = 1;
+	idescsi_add_settings(drive);
 }
 
 static int idescsi_cleanup (ide_drive_t *drive)
@@ -426,13 +452,6 @@
 	return 0;
 }
 
-int idescsi_init (void);
-static ide_module_t idescsi_module = {
-	IDE_DRIVER_MODULE,
-	idescsi_init,
-	NULL
-};
-
 /*
  *	IDE subdriver functions, registered with ide.c
  */
@@ -456,6 +475,14 @@
 	NULL			/* proc */
 };
 
+int idescsi_init (void);
+static ide_module_t idescsi_module = {
+	IDE_DRIVER_MODULE,
+	idescsi_init,
+	&idescsi_driver,
+	NULL
+};
+
 static struct proc_dir_entry idescsi_proc_dir = {PROC_SCSI_IDESCSI, 8, "ide-scsi", S_IFDIR | S_IRUGO | S_IXUGO, 2};
 
 /*
@@ -476,7 +503,7 @@
 	MOD_INC_USE_COUNT;
 	for (i = 0; media[i] != 255; i++) {
 		failed = 0;
-		while ((drive = ide_scan_devices (media[i], NULL, failed++)) != NULL) {
+		while ((drive = ide_scan_devices (media[i], idescsi_driver.name, NULL, failed++)) != NULL) {
 			if ((scsi = (idescsi_scsi_t *) kmalloc (sizeof (idescsi_scsi_t), GFP_KERNEL)) == NULL) {
 				printk (KERN_ERR "ide-scsi: %s: Can't allocate a scsi structure\n", drive->name);
 				continue;
@@ -664,10 +691,10 @@
 {
 	ide_drive_t *drive = idescsi_drives[disk->device->id];
 
-	if (drive->cyl && drive->head && drive->sect) {
-		parm[0] = drive->head;
-		parm[1] = drive->sect;
-		parm[2] = drive->cyl;
+	if (drive->bios_cyl && drive->bios_head && drive->bios_sect) {
+		parm[0] = drive->bios_head;
+		parm[1] = drive->bios_sect;
+		parm[2] = drive->bios_cyl;
 	}
 	return 0;
 }
@@ -692,7 +719,7 @@
 	scsi_unregister_module (MODULE_SCSI_HA, &idescsi_template);
 	for (i = 0; media[i] != 255; i++) {
 		failed = 0;
-		while ((drive = ide_scan_devices (media[i], &idescsi_driver, failed)) != NULL)
+		while ((drive = ide_scan_devices (media[i], idescsi_driver.name, &idescsi_driver, failed)) != NULL)
 			if (idescsi_cleanup (drive)) {
 				printk ("%s: cleanup_module() called while still busy\n", drive->name);
 				failed++;
diff -u --recursive --new-file v2.1.78/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.1.78/linux/drivers/scsi/ncr53c8xx.c	Tue Dec 23 16:30:59 1997
+++ linux/drivers/scsi/ncr53c8xx.c	Mon Jan 12 15:05:27 1998
@@ -67,7 +67,7 @@
 */
 
 /*
-**	20 September 1997, version 2.5c
+**	2 January 1998, version 2.5f
 **
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
@@ -504,7 +504,7 @@
 	unsigned master_parity	: 1;
 	unsigned scsi_parity	: 1;
 	unsigned disconnection	: 1;
-	unsigned special_features : 1;
+	unsigned special_features : 2;
 	unsigned ultra_scsi	: 2;
 	unsigned force_sync_nego: 1;
 	unsigned reverse_probe: 1;
@@ -520,6 +520,7 @@
 	u_char	settle_delay;
 	u_char	diff_support;
 	u_char	irqm;
+	u_char	bus_check;
 };
 
 static struct ncr_driver_setup
@@ -578,7 +579,8 @@
 	u_short	flags1;
 #define SYMBIOS_SCAN_HI_LO	(1)
 	u_short	word10;		/* 0x00 */
-	u_short	word12;		/* 0x00 */
+	u_short	flags3;		/* 0x00 */
+#define SYMBIOS_REMOVABLE_FLAGS	(3)		/* 0=none, 1=bootable, 2=all */
 	u_char	host_id;
 	u_char	byte15;		/* 0x04 */
 	u_short	word16;		/* 0x0410 */
@@ -693,7 +695,7 @@
 	ncr_slot  slot;
 	ncr_chip  chip;
 	ncr_nvram *nvram;
-	int attached;
+	int attach_done;
 } ncr_device;
 
 /*==========================================================
@@ -1883,7 +1885,7 @@
 	ncrcmd	prepare2	[ 24];
 	ncrcmd	setmsg		[  5];
 	ncrcmd  clrack		[  2];
-	ncrcmd  dispatch	[ 33];
+	ncrcmd  dispatch	[ 38];
 	ncrcmd	no_data		[ 17];
 	ncrcmd  checkatn	[ 10];
 	ncrcmd  command		[ 15];
@@ -1985,6 +1987,7 @@
 static	void	ncr_timeout	(ncb_p np);
 static  void    ncr_wakeup      (ncb_p np, u_long code);
 static	void	ncr_start_reset	(ncb_p np, int settle_delay);
+static	int	ncr_reset_scsi_bus (ncb_p np, int enab_int, int settle_delay);
 
 #ifdef SCSI_NCR_USER_COMMAND_SUPPORT
 static	void	ncr_usercmd	(ncb_p np);
@@ -2380,8 +2383,21 @@
 		0,
 	SCR_RETURN ^ IFTRUE (WHEN (SCR_DATA_OUT)),
 		0,
-	SCR_RETURN ^ IFTRUE (IF (SCR_DATA_IN)),
+	/*
+	**	DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 4.
+	**	Possible data corruption during Memory Write and Invalidate.
+	**	This work-around resets the addressing logic prior to the 
+	**	start of the first MOVE of a DATA IN phase.
+	**	(See README.ncr53c8xx for more information)
+	*/
+	SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)),
+		20,
+	SCR_COPY (4),
+		RADDR (scratcha),
+		RADDR (scratcha),
+	SCR_RETURN,
 		0,
+
 	SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)),
 		PADDR (msg_out),
 	SCR_JUMP ^ IFTRUE (IF (SCR_MSG_IN)),
@@ -4602,7 +4618,11 @@
 	**	Then enable disconnects.
 	*/
 	save_flags(flags); cli();
-	ncr_start_reset(np, driver_setup.settle_delay);
+	if (ncr_reset_scsi_bus(np, 0, driver_setup.settle_delay) != 0) {
+		printf("%s: FATAL ERROR: CHECK SCSI BUS - CABLES, TERMINATION, DEVICE POWER etc.!\n", ncr_name(np));
+		restore_flags(flags);
+		goto attach_error;
+	}
 	ncr_exception (np);
 	restore_flags(flags);
 
@@ -4670,6 +4690,16 @@
 #endif
 		release_region(np->port, 128);
 	}
+	if (np->irq) {
+#ifdef DEBUG_NCR53C8XX
+		printf("%s: freeing irq %d\n", ncr_name(np), np->irq);
+#endif
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
+		free_irq(np->irq, np);
+#else
+		free_irq(np->irq);
+#endif
+	}
 	scsi_unregister(instance);
 
         return -1;
@@ -5191,21 +5221,64 @@
 	save_flags(flags); cli();
 
 	if (!np->settle_time) {
-		if (bootverbose > 1)
-			printf("%s: resetting, command processing suspended for %d seconds\n",
-				ncr_name(np), settle_delay);
-		np->settle_time	= jiffies + settle_delay * HZ;
-		OUTB (nc_istat, SRST);
-		DELAY (1000);
-		OUTB (nc_istat, 0);
-		OUTW (nc_sien, RST);
-		OUTB (nc_scntl1, CRST);
-		DELAY (100);
+		(void) ncr_reset_scsi_bus(np, 1, settle_delay);
 	}
-
 	restore_flags(flags);
 }
 
+static int ncr_reset_scsi_bus(ncb_p np, int enab_int, int settle_delay)
+{
+	u_int32 term;
+	int retv = 0;
+
+	np->settle_time	= jiffies + settle_delay * HZ;
+
+	if (bootverbose > 1)
+		printf("%s: resetting, "
+			"command processing suspended for %d seconds\n",
+			ncr_name(np), settle_delay);
+
+	OUTB (nc_istat, SRST);
+	DELAY (1000);
+	OUTB (nc_istat, 0);
+	if (enab_int)
+		OUTW (nc_sien, RST);
+	OUTB (nc_scntl1, CRST);
+	DELAY (100);
+
+	if (!driver_setup.bus_check)
+		goto out;
+	/*
+	**	Check for no terminators or SCSI bus shorts to ground.
+	**	Read SCSI data bus, data parity bits and control signals.
+	**	We are expecting RESET to be TRUE and other signals to be 
+	**	FALSE.
+	*/
+	term =	INB(nc_sstat0);				/* rst, sdp0 */
+	term =	((term & 2) << 7) + ((term & 1) << 16);
+	term |= ((INB(nc_sstat2) & 0x01) << 25) |	/* sdp1 */
+		(INW(nc_sbdl) << 9) |			/* d15-0 */
+		INB(nc_sbcl);	/* req, ack, bsy, sel, atn, msg, cd, io */
+
+	if (!(np->features & FE_WIDE))
+		term &= 0x3ffff;
+
+	if (term != (2<<7)) {
+		printf("%s: suspicious SCSI data while resetting the BUS.\n",
+			ncr_name(np));
+		printf("%s: %sdp0,d7-0,rst,req,ack,bsy,sel,atn,msg,c/d,i/o = "
+			"0x%lx, expecting 0x%lx\n",
+			ncr_name(np),
+			(np->features & FE_WIDE) ? "dp1,d15-8," : "",
+			(u_long)term, (u_long)(2<<7));
+		if (driver_setup.bus_check == 1)
+			retv = 1;
+	}
+out:
+	OUTB (nc_scntl1, 0);
+	return retv;
+}
+
 /*==========================================================
 **
 **
@@ -5618,10 +5691,12 @@
 	**	Check the status.
 	*/
 	if (   (cp->host_status == HS_COMPLETE)
-		&& (cp->scsi_status == S_GOOD)) {
-
+		&& (cp->scsi_status == S_GOOD ||
+		    cp->scsi_status == S_COND_MET)) {
 		/*
-		**	All went well.
+		**	All went well (GOOD status).
+		**	CONDITION MET status is returned on 
+		**	`Pre-Fetch' or `Search data' success.
 		*/
 		cmd->result = ScsiResult(DID_OK, cp->scsi_status);
 
@@ -5716,7 +5791,8 @@
 		}
 
 	} else if ((cp->host_status == HS_COMPLETE)
-		&& (cp->scsi_status == S_BUSY)) {
+		&& (cp->scsi_status == S_BUSY ||
+		    cp->scsi_status == S_CONFLICT)) {
 
 		/*
 		**   Target is busy.
@@ -6250,7 +6326,7 @@
 		if	(tp->period < 500)	scsi = "FAST-40";
 		else if	(tp->period < 1000)	scsi = "FAST-20";
 		else if	(tp->period < 2000)	scsi = "FAST-10";
-		else				scsi = "SLOW";
+		else				scsi = "FAST-5";
 
 		printf ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi,
 			tp->widedone > 1 ? "WIDE " : "",
@@ -6929,9 +7005,16 @@
 			ncr_int_sir (np);
 			return;
 		}
-		if (!(sist & (SBMC|PAR)) && !(dstat & SSI))
-			printf("%s: unknown interrupt(s) ignored sist=%x dstat=%x\n",
-				ncr_name(np), sist, dstat);
+		/*
+		**  DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 2.
+		*/
+		if (!(sist & (SBMC|PAR)) && !(dstat & SSI)) {
+			printf(	"%s: unknown interrupt(s) ignored, "
+				"ISTAT=%x DSTAT=%x SIST=%x\n",
+				ncr_name(np), istat, dstat, sist);
+			return;
+		}
+
 		OUTONB (nc_dcntl, (STD|NOCOM));
 		return;
 	};
@@ -6958,6 +7041,11 @@
 
 	if ((sist & STO) &&
 		!(dstat & (MDPE|BF|ABRT))) {
+	/*
+	**	DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 1.
+	*/
+		OUTONB (nc_ctest3, CLF);
+
 		ncr_int_sto (np);
 		return;
 	};
@@ -8915,6 +9003,8 @@
 			driver_setup.irqm	= val;
 		else if	(!strncmp(cur, "pcifix:", 7))
 			driver_setup.pci_fix_up	= val;
+		else if	(!strncmp(cur, "buschk:", 7))
+			driver_setup.bus_check	= val;
 #ifdef SCSI_NCR_NVRAM_SUPPORT
 		else if	(!strncmp(cur, "nvram:", 6))
 			driver_setup.use_nvram	= val;
@@ -8958,9 +9048,9 @@
 )
 {
 #define YesNo(y)	y ? 'y' : 'n'
-	printk("ncr53c8xx: setup=disc:%c,specf:%c,ultra:%c,tags:%d,sync:%d,burst:%d,wide:%c,diff:%d\n",
+	printk("ncr53c8xx: setup=disc:%c,specf:%d,ultra:%c,tags:%d,sync:%d,burst:%d,wide:%c,diff:%d\n",
 			YesNo(driver_setup.disconnection),
-			YesNo(driver_setup.special_features),
+			driver_setup.special_features,
 			YesNo(driver_setup.ultra_scsi),
 			driver_setup.default_tags,
 			driver_setup.default_sync,
@@ -9059,10 +9149,10 @@
 			    h->device_id == devp->chip.device_id)
 				break;
 		}
-		if (j < count && !devp->attached &&
-		    !ncr_attach (tpnt, attach_count, devp)) {
-			attach_count++;
-			devp->attached = 1;
+		if (j < count && !devp->attach_done) {
+			if (!ncr_attach (tpnt, attach_count, devp))
+				attach_count++;
+			devp->attach_done = 1;
 		}
 	}
 
@@ -9171,8 +9261,6 @@
 #endif
 			printf(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
 				device[count].chip.name, msg);
-
-			device[count].attached = 0;
 			++count;
 		}
 	}
@@ -9185,9 +9273,9 @@
 	** so try to attach them here.
 	*/
 	for (i= 0; i < count; i++) {
-		if ((!device[i].attached) && (!ncr_attach (tpnt, attach_count, &device[i]))) {
+		if (!device[i].attach_done && 
+		    !ncr_attach (tpnt, attach_count, &device[i])) {
  			attach_count++;
-			device[i].attached = 1;
 		}
 	}
 
@@ -9281,10 +9369,10 @@
 		command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY;
 		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
 	}
-       if (io_port >= 0x10000000) {
+	if (io_port >= 0x10000000) {
 		io_port = (io_port & 0x00FFFFFF) | 0x01000000;
 		pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, io_port);
-       }
+	}
 	if (base >= 0x10000000) {
 		base = (base & 0x00FFFFFF) | 0x01000000;
 		pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, base);
@@ -9339,8 +9427,12 @@
 	/*
 	 * Fix some features according to driver setup.
 	 */
-	if (!driver_setup.special_features)
+	if (!(driver_setup.special_features & 1))
 		chip->features &= ~FE_SPECIAL_SET;
+	else {
+		if (driver_setup.special_features & 2)
+			chip->features &= ~FE_WRIE;
+	}
 	if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
 		chip->features |=  FE_ULTRA;
 		chip->features &= ~FE_ULTRA2;
@@ -9359,7 +9451,12 @@
 #if defined(__i386) && !defined(MODULE)
 	if ((driver_setup.pci_fix_up & 1) &&
 	    (chip->features & FE_CLSE) && cache_line_size == 0) {
-	    	switch(boot_cpu_data.x86) {
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
+		extern char x86;
+		switch(x86) {
+#else
+		switch(boot_cpu_data.x86) {
+#endif
 		case 4:	cache_line_size = 4; break;
 		case 5:	cache_line_size = 8; break;
 		}
@@ -9446,7 +9543,7 @@
 	device->slot.base_2	= base_2;
 	device->slot.io_port	= io_port;
 	device->slot.irq	= irq;
-	device->attached	= 0;
+	device->attach_done	= 0;
 #ifdef SCSI_NCR_NVRAM_SUPPORT
 	if (!nvram)
 		goto out;
diff -u --recursive --new-file v2.1.78/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h
--- v2.1.78/linux/drivers/scsi/ncr53c8xx.h	Sun Dec 21 22:36:14 1997
+++ linux/drivers/scsi/ncr53c8xx.h	Mon Jan 12 16:44:36 1998
@@ -45,7 +45,7 @@
 /*
 **	Name and revision of the driver
 */
-#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 2.5c"
+#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 2.5f"
 
 /*
 **	Check supported Linux versions
@@ -125,7 +125,7 @@
  * For Ultra2 SCSI support option, use special features and allow 40Mhz 
  * synchronous data transfers.
  */
-#define	SCSI_NCR_SETUP_SPECIAL_FEATURES		(1)
+#define	SCSI_NCR_SETUP_SPECIAL_FEATURES		(3)
 #define SCSI_NCR_SETUP_ULTRA_SCSI		(2)
 #define SCSI_NCR_MAX_SYNC			(40)
 
@@ -290,6 +290,8 @@
 #define ncr53c8xx_release NULL
 #endif
 
+#if	LINUX_VERSION_CODE >= LinuxVersionCode(2,1,75)
+
 #define NCR53C8XX {     name:           SCSI_NCR_DRIVER_NAME,	\
 			detect:         ncr53c8xx_detect,	\
 			release:        ncr53c8xx_release,	\
@@ -303,6 +305,30 @@
 			cmd_per_lun:    SCSI_NCR_CMD_PER_LUN,	\
 			use_clustering: DISABLE_CLUSTERING} 
   
+#elif	LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+
+#define NCR53C8XX {	NULL, NULL, NULL, NULL,				\
+			SCSI_NCR_DRIVER_NAME,	ncr53c8xx_detect,	\
+    			ncr53c8xx_release,	NULL,	NULL,		\
+			ncr53c8xx_queue_command,ncr53c8xx_abort,	\
+			ncr53c8xx_reset, NULL,	scsicam_bios_param,	\
+			SCSI_NCR_CAN_QUEUE,	7,			\
+			SCSI_NCR_SG_TABLESIZE,	SCSI_NCR_CMD_PER_LUN,	\
+			0,	0,	DISABLE_CLUSTERING} 
+
+#else
+
+#define NCR53C8XX {	NULL, NULL,					\
+			SCSI_NCR_DRIVER_NAME,	ncr53c8xx_detect,	\
+			ncr53c8xx_release,	NULL, 	NULL, 		\
+			ncr53c8xx_queue_command,ncr53c8xx_abort,	\
+			ncr53c8xx_reset, NULL,	scsicam_bios_param,	\
+			SCSI_NCR_CAN_QUEUE,	7,			\
+			SCSI_NCR_SG_TABLESIZE,	SCSI_NCR_CMD_PER_LUN,	\
+			0,	0,	DISABLE_CLUSTERING} 
+
+#endif /* LINUX_VERSION_CODE */
+
 #endif /* defined(HOSTS_C) || defined(MODULE) */ 
 
 
@@ -427,6 +453,19 @@
 #define FE_SPECIAL_SET	(FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM)
 } ncr_chip;
 
+/*
+**	DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 3.
+**	Memory Read transaction terminated by a retry followed by 
+**	Memory Read Line command.
+*/
+#define FE_CACHE0_SET	(FE_CACHE_SET & ~FE_ERL)
+
+/*
+**	DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 5.
+**	On paper, this errata is harmless. But it is a good reason for 
+**	using a shorter programmed burst length (64 DWORDS instead of 128).
+*/
+
 #define SCSI_NCR_CHIP_TABLE						\
 {									\
  {PCI_DEVICE_ID_NCR_53C810, 0x0f, "810",  4,  8, 4,			\
@@ -444,23 +483,23 @@
  {PCI_DEVICE_ID_NCR_53C825, 0x0f, "825",  4,  8, 4,			\
  FE_WIDE|FE_ERL|FE_BOF}							\
  ,									\
- {PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 7,  8, 4,			\
- FE_WIDE|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}		\
+ {PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 6,  8, 4,			\
+ FE_WIDE|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}		\
  ,									\
  {PCI_DEVICE_ID_NCR_53C860, 0xff, "860",  4,  8, 5,			\
  FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN}		\
  ,									\
- {PCI_DEVICE_ID_NCR_53C875, 0x01, "875",  7, 16, 5,			\
- FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ {PCI_DEVICE_ID_NCR_53C875, 0x01, "875",  6, 16, 5,			\
+ FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,									\
- {PCI_DEVICE_ID_NCR_53C875, 0xff, "875",  7, 16, 5,			\
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ {PCI_DEVICE_ID_NCR_53C875, 0xff, "875",  6, 16, 5,			\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,									\
- {PCI_DEVICE_ID_NCR_53C875J, 0xff, "875J",  7, 16, 5,			\
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ {PCI_DEVICE_ID_NCR_53C875J,0xff, "875J", 6, 16, 5,			\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,									\
- {PCI_DEVICE_ID_NCR_53C885, 0xff, "885",  7, 16, 5,			\
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ {PCI_DEVICE_ID_NCR_53C885, 0xff, "885",  6, 16, 5,			\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,									\
  {PCI_DEVICE_ID_NCR_53C895, 0xff, "895",  7, 31, 7,			\
  FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
@@ -510,7 +549,8 @@
 	1,					\
 	SCSI_NCR_SETUP_SETTLE_TIME,		\
 	SCSI_NCR_SETUP_DIFF_SUPPORT,		\
-	0					\
+	0,					\
+	1					\
 }
 
 /*
@@ -537,6 +577,7 @@
 	0,					\
 	0,					\
 	10,					\
+	1,					\
 	1,					\
 	1					\
 }
diff -u --recursive --new-file v2.1.78/linux/drivers/scsi/scsi_error.c linux/drivers/scsi/scsi_error.c
--- v2.1.78/linux/drivers/scsi/scsi_error.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/scsi/scsi_error.c	Thu Jan  8 20:35:43 1998
@@ -62,7 +62,7 @@
 #define HOST_RESET_SETTLE_TIME  10*HZ
 
 
-static const char RCSid[] = "$Header: /mnt/ide/home/eric/CVSROOT/linux/drivers/scsi/scsi_error.c,v 1.9 1997/12/07 23:38:23 eric Exp $";
+static const char RCSid[] = "$Header: /mnt/ide/home/eric/CVSROOT/linux/drivers/scsi/scsi_error.c,v 1.10 1997/12/08 04:50:35 eric Exp $";
 
 STATIC int         scsi_check_sense (Scsi_Cmnd * SCpnt);
 STATIC int         scsi_request_sense(Scsi_Cmnd *);
@@ -1088,10 +1088,12 @@
     case MEDIUM_ERROR:
 	return FAILED;
 
+    case ILLEGAL_REQUEST:
+	return SUCCESS;
+
     case BLANK_CHECK:
     case DATA_PROTECT:
     case HARDWARE_ERROR:
-    case ILLEGAL_REQUEST:
     default:
 	return FAILED;
     }
diff -u --recursive --new-file v2.1.78/linux/drivers/scsi/scsi_obsolete.c linux/drivers/scsi/scsi_obsolete.c
--- v2.1.78/linux/drivers/scsi/scsi_obsolete.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/scsi/scsi_obsolete.c	Thu Jan  8 20:35:43 1998
@@ -76,7 +76,7 @@
 #undef USE_STATIC_SCSI_MEMORY
 
 /*
-static const char RCSid[] = "$Header: /mnt/ide/home/eric/linux/drivers/scsi/RCS/scsi_obsolete.c,v 1.3 1997/04/29 04:25:08 eric Exp $";
+static const char RCSid[] = "$Header: /mnt/ide/home/eric/CVSROOT/linux/drivers/scsi/scsi_obsolete.c,v 1.1 1997/05/18 23:27:21 eric Exp $";
 */
 
 
diff -u --recursive --new-file v2.1.78/linux/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c
--- v2.1.78/linux/drivers/scsi/scsi_syms.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/scsi/scsi_syms.c	Thu Jan  8 20:35:43 1998
@@ -69,6 +69,8 @@
 EXPORT_SYMBOL(scsi_logging_level);
 #endif
 
+EXPORT_SYMBOL(scsi_sleep);
+
 #if defined(CONFIG_PROC_FS)
 EXPORT_SYMBOL(proc_print_scsidevice);
 #endif
diff -u --recursive --new-file v2.1.78/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v2.1.78/linux/drivers/scsi/sd.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/scsi/sd.c	Sat Jan 10 10:50:07 1998
@@ -1494,12 +1494,19 @@
 
 static void sd_finish()
 {
+    struct gendisk *gendisk;
     int i;
 
     blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
 
-    sd_gendisk.next = gendisk_head;
-    gendisk_head = &sd_gendisk;
+    for (gendisk = gendisk_head; gendisk != NULL; gendisk = gendisk->next)
+      if (gendisk == &sd_gendisk)
+	break;
+    if (gendisk == NULL)
+      {
+	sd_gendisk.next = gendisk_head;
+	gendisk_head = &sd_gendisk;
+      }
 
     for (i = 0; i < sd_template.dev_max; ++i)
 	if (!rscsi_disks[i].capacity &&
diff -u --recursive --new-file v2.1.78/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v2.1.78/linux/drivers/scsi/sr.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/scsi/sr.c	Thu Jan  8 20:35:43 1998
@@ -854,8 +854,6 @@
     SDp->scsi_request_fn = do_sr_request;
     scsi_CDs[i].device = SDp;
 
-    sr_vendor_init(i);
-
     sr_template.nr_dev++;
     if(sr_template.nr_dev > sr_template.dev_max)
 	panic ("scsi_devices corrupt (sr)");
@@ -986,13 +984,11 @@
     if (-EINVAL == rc) {
         /* failed, drive has'nt this mode page */
         scsi_CDs[i].cdi.speed      = 1;
-        scsi_CDs[i].cdi.capacity   = 1;
         /* disable speed select, drive probably can't do this either */
         scsi_CDs[i].cdi.mask      |= CDC_SELECT_SPEED;
     } else {
         n = buffer[3]+4;
         scsi_CDs[i].cdi.speed    = ((buffer[n+8] << 8) + buffer[n+9])/176;
-        scsi_CDs[i].cdi.capacity = 1;
       	scsi_CDs[i].readcd_known = 1;
         scsi_CDs[i].readcd_cdda  = buffer[n+5] & 0x01;
         /* print some capability bits */
@@ -1085,7 +1081,9 @@
 	scsi_CDs[i].cdi.handle     = &scsi_CDs[i];
 	scsi_CDs[i].cdi.dev        = MKDEV(MAJOR_NR,i);
 	scsi_CDs[i].cdi.mask       = 0;
+        scsi_CDs[i].cdi.capacity   = 1;
 	get_capabilities(i);
+	sr_vendor_init(i);
 
 	sprintf(name, "sr%d", i);
 	strcpy(scsi_CDs[i].cdi.name, name);
diff -u --recursive --new-file v2.1.78/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c
--- v2.1.78/linux/drivers/scsi/sr_ioctl.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/scsi/sr_ioctl.c	Thu Jan  8 20:35:43 1998
@@ -30,7 +30,7 @@
 #define IOCTL_RETRIES 3
 /* The CDROM is fairly slow, so we need a little extra time */
 /* In fact, it is very slow if it has to spin up first */
-#define IOCTL_TIMEOUT 3000
+#define IOCTL_TIMEOUT 30*HZ
 
 static void sr_ioctl_done(Scsi_Cmnd * SCpnt)
 {
@@ -51,11 +51,15 @@
 int sr_do_ioctl(int target, unsigned char * sr_cmd, void * buffer, unsigned buflength, int quiet)
 {
     Scsi_Cmnd * SCpnt;
+    Scsi_Device * SDev;
     int result, err = 0, retries = 0;
 
+    SDev  = scsi_CDs[target].device;
     SCpnt = scsi_allocate_device(NULL, scsi_CDs[target].device, 1);
 
 retry:
+    if( !scsi_block_when_processing_errors(SDev) )
+        return -ENODEV;
     {
 	struct semaphore sem = MUTEX_LOCKED;
 	SCpnt->request.sem = &sem;
@@ -69,7 +73,7 @@
     result = SCpnt->result;
     
     /* Minimal error checking.  Ignore cases we know about, and report the rest. */
-    if(driver_byte(result) != 0)
+    if(driver_byte(result) != 0) {
 	switch(SCpnt->sense_buffer[2] & 0xf) {
 	case UNIT_ATTENTION:
 	    scsi_CDs[target].device->changed = 1;
@@ -86,9 +90,7 @@
                     printk(KERN_INFO "sr%d: CDROM not ready yet.\n", target);
 		if (retries++ < 10) {
 		    /* sleep 2 sec and try again */
-                    current->state = TASK_INTERRUPTIBLE;
-                    current->timeout = jiffies + 200;
-                    schedule ();
+		    scsi_sleep(2*HZ);
                     goto retry;
 		} else {
 		    /* 20 secs are enouth? */
@@ -123,7 +125,8 @@
 	    print_command(sr_cmd);
 	    print_sense("sr", SCpnt);
             err = -EIO;
-	};
+	}
+    }
     
     result = SCpnt->result;
     /* Wake up a process waiting for device*/
@@ -279,23 +282,10 @@
 int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
 {
     u_char  sr_cmd[10];    
-    Scsi_Device * SDev;
     int result, target;
     
     target = MINOR(cdi->dev);
     
-    SDev = scsi_CDs[target].device;
-    /*
-     * If we are in the middle of error recovery, don't let anyone
-     * else try and use this device.  Also, if error recovery fails, it
-     * may try and take the device offline, in which case all further
-     * access to the device is prohibited.
-     */
-    if( !scsi_block_when_processing_errors(SDev) )
-      {
-        return -ENODEV;
-      }
-
     switch (cmd) 
     {
 	/* Sun-compatible */
@@ -718,22 +708,9 @@
                  unsigned int cmd, unsigned long arg)
 {
     int target, err;
-    Scsi_Device * SDev;
     
     target = MINOR(cdi->dev);
     
-    SDev = scsi_CDs[target].device;
-    /*
-     * If we are in the middle of error recovery, don't let anyone
-     * else try and use this device.  Also, if error recovery fails, it
-     * may try and take the device offline, in which case all further
-     * access to the device is prohibited.
-     */
-    if( !scsi_block_when_processing_errors(SDev) )
-      {
-        return -ENODEV;
-      }
-
     switch (cmd) {
     case CDROMREADMODE1:
     case CDROMREADMODE2:
diff -u --recursive --new-file v2.1.78/linux/drivers/scsi/sr_vendor.c linux/drivers/scsi/sr_vendor.c
--- v2.1.78/linux/drivers/scsi/sr_vendor.c	Tue Dec  2 16:45:19 1997
+++ linux/drivers/scsi/sr_vendor.c	Thu Jan  8 20:35:43 1998
@@ -56,8 +56,7 @@
 
 #define VENDOR_NEC             2
 #define VENDOR_TOSHIBA         3
-#define VENDOR_HP_4020         4   /* HP 4xxx writers, others too ?? */
-#define VENDOR_HP_6020         5   /* HP 6020 writers */
+#define VENDOR_WRITER          4   /* pre-scsi3 writers */
 
 #define VENDOR_ID (scsi_CDs[minor].vendor)
 
@@ -72,13 +71,12 @@
 
 	/* default */
 	VENDOR_ID = VENDOR_SCSI3;
+	if (scsi_CDs[minor].readcd_known)
+		/* this is true for scsi3/mmc drives - no more checks */
+		return;
 
-	if ((!strncmp(vendor,"HP",2) || !strncmp(vendor,"PHILIPS",7)) &&
-	    scsi_CDs[minor].device->type == TYPE_WORM) {
-		if (!strncmp(model,"CD-Writer 6020",14))
-                    VENDOR_ID = VENDOR_HP_6020;
-                else
-                    VENDOR_ID = VENDOR_HP_4020;
+	if (scsi_CDs[minor].device->type == TYPE_WORM) {
+		VENDOR_ID = VENDOR_WRITER;
 
 	} else if (!strncmp (vendor, "NEC", 3)) {
 		VENDOR_ID = VENDOR_NEC;
@@ -233,16 +231,13 @@
 			sector -= CD_MSF_OFFSET;
 		break;
 
-	case VENDOR_HP_4020:
-		/* Fallthrough */
-	case VENDOR_HP_6020:
+	case VENDOR_WRITER:
+		memset(cmd,0,12);
 		cmd[0] = READ_TOC;
 		cmd[1] = (scsi_CDs[minor].device->lun << 5);
-		cmd[8] = (VENDOR_ID == VENDOR_HP_4020) ?
-			0x04 : 0x0c;
+		cmd[8] = 0x04;
 		cmd[9] = 0x40;
-		rc = sr_do_ioctl(minor, cmd, buffer,
-		    (VENDOR_ID == VENDOR_HP_4020) ? 0x04 : 0x0c, 0);
+		rc = sr_do_ioctl(minor, cmd, buffer, 0x04, 0);
 		if (rc != 0) {
 			break;
 		}
@@ -252,16 +247,14 @@
 			break;
 		}
 
- 		if (VENDOR_ID == VENDOR_HP_4020) {
- 		    cmd[0] = READ_TOC; /* Read TOC */
- 		    cmd[1] = (scsi_CDs[minor].device->lun << 5);
- 		    cmd[6] = rc & 0x7f;  /* number of last session */
- 		    cmd[8] = 0x0c;
- 		    cmd[9] = 0x40;
- 		    rc = sr_do_ioctl(minor, cmd, buffer, 12, 0);	
- 		    if (rc != 0) {
- 			    break;
- 		    }
+		cmd[0] = READ_TOC; /* Read TOC */
+		cmd[1] = (scsi_CDs[minor].device->lun << 5);
+		cmd[6] = rc & 0x7f;  /* number of last session */
+		cmd[8] = 0x0c;
+		cmd[9] = 0x40;
+		rc = sr_do_ioctl(minor, cmd, buffer, 12, 0);	
+		if (rc != 0) {
+			break;
 		}
 
 		sector = buffer[11] + (buffer[10] << 8) +
diff -u --recursive --new-file v2.1.78/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.1.78/linux/drivers/sound/Config.in	Tue Jan  6 09:37:35 1998
+++ linux/drivers/sound/Config.in	Mon Jan 12 14:46:21 1998
@@ -149,7 +149,7 @@
 if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
    dep_tristate 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER $CONFIG_SOUND
    dep_tristate 'AWE32 synth' CONFIG_AWE32_SYNTH $CONFIG_SOUND
-   dep_tristate 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16
+   dep_tristate 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16 $CONFIG_SOUND
 
    if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then
       hex '  I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
diff -u --recursive --new-file v2.1.78/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v2.1.78/linux/drivers/sound/ad1848.c	Tue Jan  6 09:37:35 1998
+++ linux/drivers/sound/ad1848.c	Tue Jan  6 19:32:46 1998
@@ -40,24 +40,24 @@
 #include "ad1848_mixer.h"
 
 typedef struct
-  {
-	  int             base;
-	  int             irq;
-	  int             dma1, dma2;
-	  int             dual_dma;	/* 1, when two DMA channels allocated */
-	  unsigned char   MCE_bit;
-	  unsigned char   saved_regs[16];
-	  int             debug_flag;
-
-	  int             audio_flags;
-	  int             record_dev, playback_dev;
-
-	  int             xfer_count;
-	  int             audio_mode;
-	  int             open_mode;
-	  int             intr_active;
-	  char           *chip_name, *name;
-	  int             model;
+{
+	int             base;
+	int             irq;
+	int             dma1, dma2;
+	int             dual_dma;	/* 1, when two DMA channels allocated */
+	unsigned char   MCE_bit;
+	unsigned char   saved_regs[16];
+	int             debug_flag;
+
+	int             audio_flags;
+	int             record_dev, playback_dev;
+
+	int             xfer_count;
+	int             audio_mode;
+	int             open_mode;
+	int             intr_active;
+	char           *chip_name, *name;
+	int             model;
 #define MD_1848		1
 #define MD_4231		2
 #define MD_4231A	3
@@ -66,44 +66,47 @@
 #define MD_C930		6
 #define MD_IWAVE	7
 
-	  /* Mixer parameters */
-	  int             recmask;
-	  int             supported_devices, orig_devices;
-	  int             supported_rec_devices, orig_rec_devices;
-	  int            *levels;
-	  short           mixer_reroute[32];
-	  int             dev_no;
-	  volatile unsigned long timer_ticks;
-	  int             timer_running;
-	  int             irq_ok;
-	  mixer_ents     *mix_devices;
-	  int             mixer_output_port;
-	  int             c930_password_port;
-  }
-ad1848_info;
+	/* Mixer parameters */
+	int             recmask;
+	int             supported_devices, orig_devices;
+	int             supported_rec_devices, orig_rec_devices;
+	int            *levels;
+	short           mixer_reroute[32];
+	int             dev_no;
+	volatile unsigned long timer_ticks;
+	int             timer_running;
+	int             irq_ok;
+	mixer_ents     *mix_devices;
+	int             mixer_output_port;
+	int             c930_password_port;
+} ad1848_info;
 
 typedef struct ad1848_port_info
-  {
-	  int             open_mode;
-	  int             speed;
-	  unsigned char   speed_bits;
-	  int             channels;
-	  int             audio_format;
-	  unsigned char   format_bits;
-  }
+{
+	int             open_mode;
+	int             speed;
+	unsigned char   speed_bits;
+	int             channels;
+	int             audio_format;
+	unsigned char   format_bits;
+}
 ad1848_port_info;
 
-static int      nr_ad1848_devs = 0;
-static volatile char irq2dev[17] =
-{-1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1};
+static int nr_ad1848_devs = 0;
+int deskpro_xl = 0;
+
+static volatile char irq2dev[17] = {
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1
+};
 
 #if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
-static int      timer_installed = -1;
+
+static int timer_installed = -1;
 
 #endif
 
-static int      ad_format_mask[8 /*devc->model */ ] =
+static int ad_format_mask[8 /*devc->model */ ] =
 {
 	0,
 	AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,
@@ -134,13 +137,12 @@
 static void     ad1848_trigger(int dev, int bits);
 
 #if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE)
-static int      ad1848_tmr_install(int dev);
-static void     ad1848_tmr_reprogram(int dev);
+static int ad1848_tmr_install(int dev);
+static void ad1848_tmr_reprogram(int dev);
 
 #endif
 
-static int
-ad_read(ad1848_info * devc, int reg)
+static int ad_read(ad1848_info * devc, int reg)
 {
 	unsigned long   flags;
 	int             x;
@@ -159,8 +161,7 @@
 	return x;
 }
 
-static void
-ad_write(ad1848_info * devc, int reg, int data)
+static void ad_write(ad1848_info * devc, int reg, int data)
 {
 	unsigned long   flags;
 	int             timeout = 900000;
@@ -177,23 +178,22 @@
 	restore_flags(flags);
 }
 
-static void
-wait_for_calibration(ad1848_info * devc)
+static void wait_for_calibration(ad1848_info * devc)
 {
 	int             timeout = 0;
 
 	/*
-	   * Wait until the auto calibration process has finished.
-	   *
-	   * 1)       Wait until the chip becomes ready (reads don't return 0x80).
-	   * 2)       Wait until the ACI bit of I11 gets on and then off.
+	 * Wait until the auto calibration process has finished.
+	 *
+	 * 1)       Wait until the chip becomes ready (reads don't return 0x80).
+	 * 2)       Wait until the ACI bit of I11 gets on and then off.
 	 */
 
 	timeout = 100000;
 	while (timeout > 0 && inb(devc->base) == 0x80)
 		timeout--;
 	if (inb(devc->base) & 0x80)
-		printk("ad1848: Auto calibration timed out(1).\n");
+		printk(KERN_WARNING "ad1848: Auto calibration timed out(1).\n");
 
 	timeout = 100;
 	while (timeout > 0 && !(ad_read(devc, 11) & 0x20))
@@ -206,36 +206,34 @@
 		timeout--;
 	if (ad_read(devc, 11) & 0x20)
 		if (devc->model != MD_1845)
-			printk("ad1848: Auto calibration timed out(3).\n");
+			printk(KERN_WARNING "ad1848: Auto calibration timed out(3).\n");
 }
 
-static void
-ad_mute(ad1848_info * devc)
+static void ad_mute(ad1848_info * devc)
 {
-	int             i;
-	unsigned char   prev;
+	int i;
+	unsigned char prev;
 
 	/*
-	   * Save old register settings and mute output channels
+	 * Save old register settings and mute output channels
 	 */
+	 
 	for (i = 6; i < 8; i++)
-	  {
-		  prev = devc->saved_regs[i] = ad_read(devc, i);
-	  }
+	{
+		prev = devc->saved_regs[i] = ad_read(devc, i);
+	}
 
 }
 
-static void
-ad_unmute(ad1848_info * devc)
+static void ad_unmute(ad1848_info * devc)
 {
 }
 
-static void
-ad_enter_MCE(ad1848_info * devc)
+static void ad_enter_MCE(ad1848_info * devc)
 {
-	unsigned long   flags;
-	int             timeout = 1000;
-	unsigned short  prev;
+	unsigned long flags;
+	int timeout = 1000;
+	unsigned short prev;
 
 	while (timeout > 0 && inb(devc->base) == 0x80)	/*Are we initializing */
 		timeout--;
@@ -246,20 +244,19 @@
 	devc->MCE_bit = 0x40;
 	prev = inb(io_Index_Addr(devc));
 	if (prev & 0x40)
-	  {
-		  restore_flags(flags);
-		  return;
-	  }
+	{
+		restore_flags(flags);
+		return;
+	}
 	outb((devc->MCE_bit), io_Index_Addr(devc));
 	restore_flags(flags);
 }
 
-static void
-ad_leave_MCE(ad1848_info * devc)
+static void ad_leave_MCE(ad1848_info * devc)
 {
-	unsigned long   flags;
-	unsigned char   prev, acal;
-	int             timeout = 1000;
+	unsigned long flags;
+	unsigned char prev, acal;
+	int timeout = 1000;
 
 	while (timeout > 0 && inb(devc->base) == 0x80)	/*Are we initializing */
 		timeout--;
@@ -274,18 +271,17 @@
 	outb((0x00), io_Index_Addr(devc));	/* Clear the MCE bit */
 
 	if ((prev & 0x40) == 0)	/* Not in MCE mode */
-	  {
-		  restore_flags(flags);
-		  return;
-	  }
+	{
+		restore_flags(flags);
+		return;
+	}
 	outb((0x00), io_Index_Addr(devc));	/* Clear the MCE bit */
 	if (acal & 0x08)	/* Auto calibration is enabled */
 		wait_for_calibration(devc);
 	restore_flags(flags);
 }
 
-static int
-ad1848_set_recmask(ad1848_info * devc, int mask)
+static int ad1848_set_recmask(ad1848_info * devc, int mask)
 {
 	unsigned char   recdev;
 	int             i, n;
@@ -294,12 +290,17 @@
 
 	/* Rename the mixer bits if necessary */
 	for (i = 0; i < 32; i++)
+	{
 		if (devc->mixer_reroute[i] != i)
+		{
 			if (mask & (1 << i))
-			  {
-				  mask &= ~(1 << i);
-				  mask |= (1 << devc->mixer_reroute[i]);
-			  }
+			{
+				mask &= ~(1 << i);
+				mask |= (1 << devc->mixer_reroute[i]);
+			}
+		}
+	}
+	
 	n = 0;
 	for (i = 0; i < 32; i++)	/* Count selected device bits */
 		if (mask & (1 << i))
@@ -308,41 +309,41 @@
 	if (n == 0)
 		mask = SOUND_MASK_MIC;
 	else if (n != 1)	/* Too many devices selected */
-	  {
+	{
 		  mask &= ~devc->recmask;	/* Filter out active settings */
 
-		  n = 0;
-		  for (i = 0; i < 32; i++)	/* Count selected device bits */
-			  if (mask & (1 << i))
-				  n++;
-
-		  if (n != 1)
-			  mask = SOUND_MASK_MIC;
-	  }
+		n = 0;
+		for (i = 0; i < 32; i++)	/* Count selected device bits */
+			if (mask & (1 << i))
+				n++;
+
+		if (n != 1)
+			mask = SOUND_MASK_MIC;
+	}
 	switch (mask)
-	  {
-	  case SOUND_MASK_MIC:
-		  recdev = 2;
-		  break;
-
-	  case SOUND_MASK_LINE:
-	  case SOUND_MASK_LINE3:
-		  recdev = 0;
-		  break;
-
-	  case SOUND_MASK_CD:
-	  case SOUND_MASK_LINE1:
-		  recdev = 1;
-		  break;
-
-	  case SOUND_MASK_IMIX:
-		  recdev = 3;
-		  break;
-
-	  default:
-		  mask = SOUND_MASK_MIC;
-		  recdev = 2;
-	  }
+	{
+		case SOUND_MASK_MIC:
+			recdev = 2;
+			break;
+
+		case SOUND_MASK_LINE:
+		case SOUND_MASK_LINE3:
+			recdev = 0;
+			break;
+
+		case SOUND_MASK_CD:
+		case SOUND_MASK_LINE1:
+			recdev = 1;
+			break;
+
+		case SOUND_MASK_IMIX:
+			recdev = 3;
+			break;
+
+		default:
+			mask = SOUND_MASK_MIC;
+			recdev = 2;
+	}
 
 	recdev <<= 6;
 	ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev);
@@ -350,24 +351,27 @@
 
 	/* Rename the mixer bits back if necessary */
 	for (i = 0; i < 32; i++)
+	{
 		if (devc->mixer_reroute[i] != i)
+		{
 			if (mask & (1 << devc->mixer_reroute[i]))
-			  {
-				  mask &= ~(1 << devc->mixer_reroute[i]);
-				  mask |= (1 << i);
-			  }
+			{
+				mask &= ~(1 << devc->mixer_reroute[i]);
+				mask |= (1 << i);
+			}
+		}
+	}
 	devc->recmask = mask;
 	return mask;
 }
 
-static void
-change_bits(ad1848_info * devc, unsigned char *regval, int dev, int chn, int newval)
+static void change_bits(ad1848_info * devc, unsigned char *regval, int dev, int chn, int newval)
 {
-	unsigned char   mask;
-	int             shift;
-	int             mute;
-	int             mutemask;
-	int             set_mute_bit;
+	unsigned char mask;
+	int shift;
+	int mute;
+	int mutemask;
+	int set_mute_bit;
 
 	set_mute_bit = (newval == 0);
 
@@ -378,22 +382,22 @@
 	shift = devc->mix_devices[dev][chn].bitpos;
 
 	if (devc->mix_devices[dev][chn].mutepos == 8)
-	  {			/* if there is no mute bit */
-		  mute = 0;	/* No mute bit; do nothing special */
-		  mutemask = ~0;	/* No mute bit; do nothing special */
-	} else
-	  {
-		  mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos);
-		  mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos);
-	  }
+	{			/* if there is no mute bit */
+		mute = 0;	/* No mute bit; do nothing special */
+		mutemask = ~0;	/* No mute bit; do nothing special */
+	}
+	else
+	{
+		mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos);
+		mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos);
+	}
 
 	newval = (int) ((newval * mask) + 50) / 100;	/* Scale it */
 	*regval &= (~(mask << shift)) & (mutemask);	/* Clear bits */
 	*regval |= ((newval & mask) << shift) | mute;	/* Set new value */
 }
 
-static int
-ad1848_mixer_get(ad1848_info * devc, int dev)
+static int ad1848_mixer_get(ad1848_info * devc, int dev)
 {
 	if (!((1 << dev) & devc->supported_devices))
 		return -EINVAL;
@@ -403,15 +407,14 @@
 	return devc->levels[dev];
 }
 
-static int
-ad1848_mixer_set(ad1848_info * devc, int dev, int value)
+static int ad1848_mixer_set(ad1848_info * devc, int dev, int value)
 {
-	int             left = value & 0x000000ff;
-	int             right = (value & 0x0000ff00) >> 8;
-	int             retvol;
+	int left = value & 0x000000ff;
+	int right = (value & 0x0000ff00) >> 8;
+	int retvol;
 
-	int             regoffs;
-	unsigned char   val;
+	int regoffs;
+	unsigned char val;
 
 	if (dev > 31)
 		return -EINVAL;
@@ -445,7 +448,7 @@
 	devc->levels[dev] = retvol;
 
 	/*
-	   * Set the left channel
+	 * Set the left channel
 	 */
 
 	regoffs = devc->mix_devices[dev][LEFT_CHN].regno;
@@ -455,7 +458,7 @@
 	devc->saved_regs[regoffs] = val;
 
 	/*
-	   * Set the right channel
+	 * Set the right channel
 	 */
 
 	if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0)
@@ -470,11 +473,10 @@
 	return retvol;
 }
 
-static void
-ad1848_mixer_reset(ad1848_info * devc)
+static void ad1848_mixer_reset(ad1848_info * devc)
 {
-	int             i;
-	char            name[32];
+	int i;
+	char name[32];
 
 	devc->mix_devices = &(ad1848_mix_devices[0]);
 
@@ -484,30 +486,30 @@
 		devc->mixer_reroute[i] = i;
 
 	switch (devc->model)
-	  {
-	  case MD_4231:
-	  case MD_4231A:
-	  case MD_1845:
-		  devc->supported_devices = MODE2_MIXER_DEVICES;
-		  break;
-
-	  case MD_C930:
-		  devc->supported_devices = C930_MIXER_DEVICES;
-		  devc->mix_devices = &(c930_mix_devices[0]);
-		  break;
-
-	  case MD_IWAVE:
-		  devc->supported_devices = MODE3_MIXER_DEVICES;
-		  devc->mix_devices = &(iwave_mix_devices[0]);
-		  break;
-
-	  case MD_4232:
-		  devc->supported_devices = MODE3_MIXER_DEVICES;
-		  break;
-
-	  default:
-		  devc->supported_devices = MODE1_MIXER_DEVICES;
-	  }
+	{
+		case MD_4231:
+		case MD_4231A:
+		case MD_1845:
+			devc->supported_devices = MODE2_MIXER_DEVICES;
+			break;
+
+		case MD_C930:
+			devc->supported_devices = C930_MIXER_DEVICES;
+			devc->mix_devices = &(c930_mix_devices[0]);
+			break;
+
+		case MD_IWAVE:
+			devc->supported_devices = MODE3_MIXER_DEVICES;
+			devc->mix_devices = &(iwave_mix_devices[0]);
+			break;
+
+		case MD_4232:
+			devc->supported_devices = MODE3_MIXER_DEVICES;
+			break;
+
+		default:
+			devc->supported_devices = MODE1_MIXER_DEVICES;
+	}
 
 	devc->supported_rec_devices = MODE1_REC_DEVICES;
 	devc->orig_devices = devc->supported_devices;
@@ -516,9 +518,13 @@
 	devc->levels = load_mixer_volumes(name, default_mixer_levels, 1);
 
 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+	{
 		if (devc->supported_devices & (1 << i))
 			ad1848_mixer_set(devc, i, devc->levels[i]);
+	}
+	
 	ad1848_set_recmask(devc, SOUND_MASK_MIC);
+	
 	devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT;
 	if (devc->mixer_output_port & AUDIO_SPEAKER)
 		ad_write(devc, 26, ad_read(devc, 26) & ~0x40);	/* Unmute mono out */
@@ -531,11 +537,13 @@
 	ad1848_info *devc = mixer_devs[dev]->devc;
 	int val;
 
-	if (cmd == SOUND_MIXER_PRIVATE1) {
+	if (cmd == SOUND_MIXER_PRIVATE1) 
+	{
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
 
-		if (val != 0xffff) {
+		if (val != 0xffff) 
+		{
 			val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT);
 			devc->mixer_output_port = val;
 			val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT;	/* Always on */
@@ -548,75 +556,85 @@
 		val = devc->mixer_output_port;
 		return put_user(val, (int *)arg);
 	}
-	if (((cmd >> 8) & 0xff) == 'M') {
+	if (((cmd >> 8) & 0xff) == 'M') 
+	{
 		if (_SIOC_DIR(cmd) & _SIOC_WRITE)
-			switch (cmd & 0xff) {
-			case SOUND_MIXER_RECSRC:
-				if (get_user(val, (int *)arg))
-					return -EFAULT;
-				val = ad1848_set_recmask(devc, val);
-				return put_user(val, (int *)arg);
+		{
+			switch (cmd & 0xff) 
+			{
+				case SOUND_MIXER_RECSRC:
+					if (get_user(val, (int *)arg))
+						return -EFAULT;
+					val = ad1848_set_recmask(devc, val);
+					break;
 				
-			    default:
-				if (get_user(val, (int *)arg))
+				default:
+					if (get_user(val, (int *)arg))
 					return -EFAULT;
-				val = ad1848_mixer_set(devc, cmd & 0xff, val);
-				return put_user(val, (int *)arg);
+					val = ad1848_mixer_set(devc, cmd & 0xff, val);
+					break;
 			} 
+			return put_user(val, (int *)arg);
+		}
 		else
-			switch (cmd & 0xff) {
+		{
+			switch (cmd & 0xff) 
+			{
 				/*
 				 * Return parameters
 				 */
 			    
-			case SOUND_MIXER_RECSRC:
-				val = devc->recmask;
-				return put_user(val, (int *)arg);
+				case SOUND_MIXER_RECSRC:
+					val = devc->recmask;
+					break;
 				
-			case SOUND_MIXER_DEVMASK:
-				val = devc->supported_devices;
-				return put_user(val, (int *)arg);
+				case SOUND_MIXER_DEVMASK:
+					val = devc->supported_devices;
+					break;
 				
-			case SOUND_MIXER_STEREODEVS:
-				val = devc->supported_devices;
-				if (devc->model != MD_C930)
-					val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
-				return put_user(val, (int *)arg);
+				case SOUND_MIXER_STEREODEVS:
+					val = devc->supported_devices;
+					if (devc->model != MD_C930)
+						val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
+					break;
 				
-			case SOUND_MIXER_RECMASK:
-				val = devc->supported_rec_devices;
-				return put_user(val, (int *)arg);
-
-			case SOUND_MIXER_CAPS:
-				return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg);
-
-			default:
-				val = ad1848_mixer_get(devc, cmd & 0xff);
-				return put_user(val, (int *)arg);
+				case SOUND_MIXER_RECMASK:
+					val = devc->supported_rec_devices;
+					break;
+
+				case SOUND_MIXER_CAPS:
+					val=SOUND_CAP_EXCL_INPUT;
+					break;
+
+				default:
+					val = ad1848_mixer_get(devc, cmd & 0xff);
+					break;
 			}
-	} else
+			return put_user(val, (int *)arg);
+		}
+	}
+	else
 		return -EINVAL;
 }
 
-static int
-ad1848_set_speed(int dev, int arg)
+static int ad1848_set_speed(int dev, int arg)
 {
-	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+	ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
 	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
 	/*
-	   * The sampling speed is encoded in the least significant nibble of I8. The
-	   * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other
-	   * three bits select the divisor (indirectly):
-	   *
-	   * The available speeds are in the following table. Keep the speeds in
-	   * the increasing order.
+	 * The sampling speed is encoded in the least significant nibble of I8. The
+	 * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other
+	 * three bits select the divisor (indirectly):
+	 *
+	 * The available speeds are in the following table. Keep the speeds in
+	 * the increasing order.
 	 */
 	typedef struct
-	  {
-		  int             speed;
-		  unsigned char   bits;
-	  }
+	{
+		int             speed;
+		unsigned char   bits;
+	}
 	speed_struct;
 
 	static speed_struct speed_table[] =
@@ -638,7 +656,7 @@
 		{48000, (6 << 1) | 0}
 	};
 
-	int             i, n, selected = -1;
+	int i, n, selected = -1;
 
 	n = sizeof(speed_table) / sizeof(speed_struct);
 
@@ -646,48 +664,49 @@
 		return portc->speed;
 
 	if (devc->model == MD_1845)	/* AD1845 has different timer than others */
-	  {
-		  if (arg < 4000)
-			  arg = 4000;
-		  if (arg > 50000)
-			  arg = 50000;
-
-		  portc->speed = arg;
-		  portc->speed_bits = speed_table[3].bits;
-		  return portc->speed;
-	  }
+	{
+		if (arg < 4000)
+			arg = 4000;
+		if (arg > 50000)
+			arg = 50000;
+
+		portc->speed = arg;
+		portc->speed_bits = speed_table[3].bits;
+		return portc->speed;
+	}
 	if (arg < speed_table[0].speed)
 		selected = 0;
 	if (arg > speed_table[n - 1].speed)
 		selected = n - 1;
 
 	for (i = 1 /*really */ ; selected == -1 && i < n; i++)
+	{
 		if (speed_table[i].speed == arg)
 			selected = i;
 		else if (speed_table[i].speed > arg)
-		  {
-			  int             diff1, diff2;
+		{
+			int diff1, diff2;
 
-			  diff1 = arg - speed_table[i - 1].speed;
-			  diff2 = speed_table[i].speed - arg;
+			diff1 = arg - speed_table[i - 1].speed;
+			diff2 = speed_table[i].speed - arg;
 
-			  if (diff1 < diff2)
-				  selected = i - 1;
-			  else
-				  selected = i;
-		  }
+			if (diff1 < diff2)
+				selected = i - 1;
+			else
+				selected = i;
+		}
+	}
 	if (selected == -1)
-	  {
-		  printk("ad1848: Can't find speed???\n");
-		  selected = 3;
-	  }
+	{
+		printk(KERN_WARNING "ad1848: Can't find speed???\n");
+		selected = 3;
+	}
 	portc->speed = speed_table[selected].speed;
 	portc->speed_bits = speed_table[selected].bits;
 	return portc->speed;
 }
 
-static short
-ad1848_set_channels(int dev, short arg)
+static short ad1848_set_channels(int dev, short arg)
 {
 	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
@@ -698,17 +717,16 @@
 	return arg;
 }
 
-static unsigned int
-ad1848_set_bits(int dev, unsigned int arg)
+static unsigned int ad1848_set_bits(int dev, unsigned int arg)
 {
 	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
 	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
 	static struct format_tbl
-	  {
+	{
 		  int             format;
 		  unsigned char   bits;
-	  }
+	}
 	format2bits[] =
 	{
 		{
@@ -751,7 +769,7 @@
 			AFMT_U16_BE, 0
 		}
 	};
-	int             i, n = sizeof(format2bits) / sizeof(struct format_tbl);
+	int i, n = sizeof(format2bits) / sizeof(struct format_tbl);
 
 	if (arg == 0)
 		return portc->audio_format;
@@ -763,12 +781,12 @@
 
 	for (i = 0; i < n; i++)
 		if (format2bits[i].format == arg)
-		  {
-			  if ((portc->format_bits = format2bits[i].bits) == 0)
-				  return portc->audio_format = AFMT_U8;		/* Was not supported */
+		{
+			if ((portc->format_bits = format2bits[i].bits) == 0)
+				return portc->audio_format = AFMT_U8;		/* Was not supported */
 
-			  return arg;
-		  }
+			return arg;
+		}
 	/* Still hanging here. Something must be terribly wrong */
 	portc->format_bits = 0;
 	return portc->audio_format = AFMT_U8;
@@ -801,8 +819,7 @@
 	ad1848_mixer_ioctl
 };
 
-static int
-ad1848_open(int dev, int mode)
+static int ad1848_open(int dev, int mode)
 {
 	ad1848_info    *devc = NULL;
 	ad1848_port_info *portc;
@@ -817,16 +834,16 @@
 	save_flags(flags);
 	cli();
 	if (portc->open_mode || (devc->open_mode & mode))
-	  {
-		  restore_flags(flags);
-		  return -EBUSY;
-	  }
+	{
+		restore_flags(flags);
+		return -EBUSY;
+	}
 	devc->dual_dma = 0;
 
 	if (audio_devs[dev]->flags & DMA_DUPLEX)
-	  {
-		  devc->dual_dma = 1;
-	  }
+	{
+		devc->dual_dma = 1;
+	}
 	devc->intr_active = 0;
 	devc->audio_mode = 0;
 	devc->open_mode |= mode;
@@ -846,8 +863,7 @@
 	return 0;
 }
 
-static void
-ad1848_close(int dev)
+static void ad1848_close(int dev)
 {
 	unsigned long   flags;
 	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
@@ -869,8 +885,7 @@
 	restore_flags(flags);
 }
 
-static void
-ad1848_output_block(int dev, unsigned long buf, int count, int intrflag)
+static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag)
 {
 	unsigned long   flags, cnt;
 	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
@@ -879,13 +894,14 @@
 	cnt = count;
 
 	if (portc->audio_format == AFMT_IMA_ADPCM)
-	  {
-		  cnt /= 4;
-	} else
-	  {
-		  if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
-			  cnt >>= 1;
-	  }
+	{
+		cnt /= 4;
+	}
+	else
+	{
+		if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
+			cnt >>= 1;
+	}
 	if (portc->channels > 1)
 		cnt >>= 1;
 	cnt--;
@@ -893,13 +909,13 @@
 	if (devc->audio_mode & PCM_ENABLE_OUTPUT && audio_devs[dev]->flags & DMA_AUTOMODE &&
 	    intrflag &&
 	    cnt == devc->xfer_count)
-	  {
-		  devc->audio_mode |= PCM_ENABLE_OUTPUT;
-		  devc->intr_active = 1;
-		  return;	/*
-				 * Auto DMA mode on. No need to react
-				 */
-	  }
+	{
+		devc->audio_mode |= PCM_ENABLE_OUTPUT;
+		devc->intr_active = 1;
+		return;	/*
+			 * Auto DMA mode on. No need to react
+			 */
+	}
 	save_flags(flags);
 	cli();
 
@@ -912,8 +928,7 @@
 	restore_flags(flags);
 }
 
-static void
-ad1848_start_input(int dev, unsigned long buf, int count, int intrflag)
+static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag)
 {
 	unsigned long   flags, cnt;
 	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
@@ -921,39 +936,41 @@
 
 	cnt = count;
 	if (portc->audio_format == AFMT_IMA_ADPCM)
-	  {
-		  cnt /= 4;
-	} else
-	  {
-		  if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
-			  cnt >>= 1;
-	  }
+	{
+		cnt /= 4;
+	}
+	else
+	{
+		if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
+			cnt >>= 1;
+	}
 	if (portc->channels > 1)
 		cnt >>= 1;
 	cnt--;
 
 	if (devc->audio_mode & PCM_ENABLE_INPUT && audio_devs[dev]->flags & DMA_AUTOMODE &&
-	    intrflag &&
-	    cnt == devc->xfer_count)
-	  {
-		  devc->audio_mode |= PCM_ENABLE_INPUT;
-		  devc->intr_active = 1;
-		  return;	/*
-				 * Auto DMA mode on. No need to react
-				 */
-	  }
+		intrflag &&
+		cnt == devc->xfer_count)
+	{
+		devc->audio_mode |= PCM_ENABLE_INPUT;
+		devc->intr_active = 1;
+		return;	/*
+			 * Auto DMA mode on. No need to react
+			 */
+	}
 	save_flags(flags);
 	cli();
 
 	if (devc->model == MD_1848)
-	  {
+	{
 		  ad_write(devc, 15, (unsigned char) (cnt & 0xff));
 		  ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
-	} else
-	  {
+	}
+	else
+	{
 		  ad_write(devc, 31, (unsigned char) (cnt & 0xff));
 		  ad_write(devc, 30, (unsigned char) ((cnt >> 8) & 0xff));
-	  }
+	}
 
 	ad_unmute(devc);
 
@@ -963,8 +980,7 @@
 	restore_flags(flags);
 }
 
-static int
-ad1848_prepare_for_output(int dev, int bsize, int bcount)
+static int ad1848_prepare_for_output(int dev, int bsize, int bcount)
 {
 	int             timeout;
 	unsigned char   fs, old_fs, tmp = 0;
@@ -984,26 +1000,28 @@
 	ad_enter_MCE(devc);	/* Enables changes to the format select reg */
 
 	if (devc->model == MD_1845)	/* Use alternate speed select registers */
-	  {
-		  fs &= 0xf0;	/* Mask off the rate select bits */
+	{
+		fs &= 0xf0;	/* Mask off the rate select bits */
 
-		  ad_write(devc, 22, (portc->speed >> 8) & 0xff);	/* Speed MSB */
-		  ad_write(devc, 23, portc->speed & 0xff);	/* Speed LSB */
-	  }
+		ad_write(devc, 22, (portc->speed >> 8) & 0xff);	/* Speed MSB */
+		ad_write(devc, 23, portc->speed & 0xff);	/* Speed LSB */
+	}
 	old_fs = ad_read(devc, 8);
 
 	if (devc->model == MD_4232)
-	  {
-		  tmp = ad_read(devc, 16);
-		  ad_write(devc, 16, tmp | 0x30);
-	  }
+	{
+		tmp = ad_read(devc, 16);
+		ad_write(devc, 16, tmp | 0x30);
+	}
 	if (devc->model == MD_IWAVE)
 		ad_write(devc, 17, 0xc2);	/* Disable variable frequency select */
 
 	ad_write(devc, 8, fs);
+
 	/*
 	 * Write to I8 starts resynchronization. Wait until it completes.
 	 */
+
 	timeout = 0;
 	while (timeout < 100 && inb(devc->base) != 0x80)
 		timeout++;
@@ -1023,21 +1041,20 @@
 #if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE)
 	if (dev == timer_installed && devc->timer_running)
 		if ((fs & 0x01) != (old_fs & 0x01))
-		  {
-			  ad1848_tmr_reprogram(dev);
-		  }
+		{
+			ad1848_tmr_reprogram(dev);
+		}
 #endif
 	ad1848_halt_output(dev);
 	return 0;
 }
 
-static int
-ad1848_prepare_for_input(int dev, int bsize, int bcount)
+static int ad1848_prepare_for_input(int dev, int bsize, int bcount)
 {
-	int             timeout;
-	unsigned char   fs, old_fs, tmp = 0;
-	unsigned long   flags;
-	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+	int timeout;
+	unsigned char fs, old_fs, tmp = 0;
+	unsigned long flags;
+	ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
 	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
 	if (devc->audio_mode)
@@ -1053,75 +1070,78 @@
 	ad_enter_MCE(devc);	/* Enables changes to the format select reg */
 
 	if (devc->model == MD_1845)	/* Use alternate speed select registers */
-	  {
-		  fs &= 0xf0;	/* Mask off the rate select bits */
+	{
+		fs &= 0xf0;	/* Mask off the rate select bits */
 
-		  ad_write(devc, 22, (portc->speed >> 8) & 0xff);	/* Speed MSB */
-		  ad_write(devc, 23, portc->speed & 0xff);	/* Speed LSB */
-	  }
+		ad_write(devc, 22, (portc->speed >> 8) & 0xff);	/* Speed MSB */
+		ad_write(devc, 23, portc->speed & 0xff);	/* Speed LSB */
+	}
 	if (devc->model == MD_4232)
-	  {
-		  tmp = ad_read(devc, 16);
-		  ad_write(devc, 16, tmp | 0x30);
-	  }
+	{
+		tmp = ad_read(devc, 16);
+		ad_write(devc, 16, tmp | 0x30);
+	}
 	if (devc->model == MD_IWAVE)
 		ad_write(devc, 17, 0xc2);	/* Disable variable frequency select */
 
 	/*
 	 * If mode >= 2 (CS4231), set I28. It's the capture format register.
 	 */
+	
 	if (devc->model != MD_1848)
-	  {
-		  old_fs = ad_read(devc, 28);
-		  ad_write(devc, 28, fs);
-
-		  /*
-		   * Write to I28 starts resynchronization. Wait until it completes.
-		   */
-		  timeout = 0;
-		  while (timeout < 100 && inb(devc->base) != 0x80)
-			  timeout++;
-
-		  timeout = 0;
-		  while (timeout < 10000 && inb(devc->base) == 0x80)
-			  timeout++;
-
-		  if (devc->model != MD_1848 && devc->model != MD_1845)
-		    {
-			    /*
-			     * CS4231 compatible devices don't have separate sampling rate selection
-			     * register for recording an playback. The I8 register is shared so we have to
-			     * set the speed encoding bits of it too.
-			     */
-			    unsigned char   tmp = portc->speed_bits | (ad_read(devc, 8) & 0xf0);
-
-			    ad_write(devc, 8, tmp);
-			    /*
-			     * Write to I8 starts resynchronization. Wait until it completes.
-			     */
-			    timeout = 0;
-			    while (timeout < 100 && inb(devc->base) != 0x80)
-				    timeout++;
-
-			    timeout = 0;
-			    while (timeout < 10000 && inb(devc->base) == 0x80)
-				    timeout++;
-		    }
-	} else
-	  {			/* For AD1848 set I8. */
+	{
+		old_fs = ad_read(devc, 28);
+		ad_write(devc, 28, fs);
+
+		/*
+		 * Write to I28 starts resynchronization. Wait until it completes.
+		 */
+		
+		timeout = 0;
+		while (timeout < 100 && inb(devc->base) != 0x80)
+			timeout++;
+
+		timeout = 0;
+		while (timeout < 10000 && inb(devc->base) == 0x80)
+			timeout++;
 
-		  old_fs = ad_read(devc, 8);
-		  ad_write(devc, 8, fs);
-		  /*
-		   * Write to I8 starts resynchronization. Wait until it completes.
-		   */
-		  timeout = 0;
-		  while (timeout < 100 && inb(devc->base) != 0x80)
-			  timeout++;
-		  timeout = 0;
-		  while (timeout < 10000 && inb(devc->base) == 0x80)
-			  timeout++;
-	  }
+		if (devc->model != MD_1848 && devc->model != MD_1845)
+		{
+			/*
+			 * CS4231 compatible devices don't have separate sampling rate selection
+			 * register for recording an playback. The I8 register is shared so we have to
+			 * set the speed encoding bits of it too.
+			 */
+			unsigned char   tmp = portc->speed_bits | (ad_read(devc, 8) & 0xf0);
+
+			ad_write(devc, 8, tmp);
+			/*
+			 * Write to I8 starts resynchronization. Wait until it completes.
+			 */
+			timeout = 0;
+			while (timeout < 100 && inb(devc->base) != 0x80)
+				timeout++;
+
+			timeout = 0;
+			while (timeout < 10000 && inb(devc->base) == 0x80)
+				timeout++;
+		}
+	}
+	else
+	{			/* For AD1848 set I8. */
+
+		old_fs = ad_read(devc, 8);
+		ad_write(devc, 8, fs);
+		/*
+		 * Write to I8 starts resynchronization. Wait until it completes.
+		 */
+		timeout = 0;
+		while (timeout < 100 && inb(devc->base) != 0x80)
+			timeout++;
+		timeout = 0;
+		while (timeout < 10000 && inb(devc->base) == 0x80)
+			timeout++;
+	}
 
 	if (devc->model == MD_4232)
 		ad_write(devc, 16, tmp & ~0x30);
@@ -1134,19 +1154,20 @@
 
 #if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
 	if (dev == timer_installed && devc->timer_running)
+	{
 		if ((fs & 0x01) != (old_fs & 0x01))
-		  {
-			  ad1848_tmr_reprogram(dev);
-		  }
+		{
+			ad1848_tmr_reprogram(dev);
+		}
+	}
 #endif
 	ad1848_halt_input(dev);
 	return 0;
 }
 
-static void
-ad1848_halt(int dev)
+static void ad1848_halt(int dev)
 {
-	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+	ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
 	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
 	unsigned char   bits = ad_read(devc, 9);
@@ -1159,8 +1180,7 @@
 	devc->audio_mode = 0;
 }
 
-static void
-ad1848_halt_input(int dev)
+static void ad1848_halt_input(int dev)
 {
 	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
 	unsigned long   flags;
@@ -1187,19 +1207,18 @@
 		devc->audio_mode &= ~PCM_ENABLE_INPUT;
 	}
 
-	outb((0), io_Status(devc));	/* Clear interrupt status */
-	outb((0), io_Status(devc));	/* Clear interrupt status */
+	outb(0, io_Status(devc));	/* Clear interrupt status */
+	outb(0, io_Status(devc));	/* Clear interrupt status */
 
 	devc->audio_mode &= ~PCM_ENABLE_INPUT;
 
 	restore_flags(flags);
 }
 
-static void
-ad1848_halt_output(int dev)
+static void ad1848_halt_output(int dev)
 {
-	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
-	unsigned long   flags;
+	ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+	unsigned long flags;
 
 	if (!(ad_read(devc, 9) & 0x01))
 		return;		/* Playback not enabled */
@@ -1230,8 +1249,7 @@
 	restore_flags(flags);
 }
 
-static void
-ad1848_trigger(int dev, int state)
+static void ad1848_trigger(int dev, int state)
 {
 	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
 	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
@@ -1245,32 +1263,31 @@
 	tmp = old = ad_read(devc, 9);
 
 	if (portc->open_mode & OPEN_READ)
-	  {
+	{
 		  if (state & PCM_ENABLE_INPUT)
 			  tmp |= 0x02;
 		  else
 			  tmp &= ~0x02;
-	  }
+	}
 	if (portc->open_mode & OPEN_WRITE)
-	  {
-		  if (state & PCM_ENABLE_OUTPUT)
-			  tmp |= 0x01;
-		  else
-			  tmp &= ~0x01;
-	  }
+	{
+		if (state & PCM_ENABLE_OUTPUT)
+			tmp |= 0x01;
+		else
+			tmp &= ~0x01;
+	}
 	/* ad_mute(devc); */
 	if (tmp != old)
-	  {
+	{
 		  ad_write(devc, 9, tmp);
 		  ad_unmute(devc);
-	  }
+	}
 	restore_flags(flags);
 }
 
-static void
-ad1848_init_hw(ad1848_info * devc)
+static void ad1848_init_hw(ad1848_info * devc)
 {
-	int             i;
+	int i;
 
 	/*
 	 * Initial values for the indirect registers of CS4248/AD1848.
@@ -1294,40 +1311,40 @@
 	ad_unmute(devc);	/* Leave it unmuted now */
 
 	if (devc->model > MD_1848)
-	  {
-		  ad_write(devc, 12, ad_read(devc, 12) | 0x40);		/* Mode2 = enabled */
-
-		  if (devc->model == MD_IWAVE)
-			  ad_write(devc, 12, 0x6c);	/* Select codec mode 3 */
+	{
+		ad_write(devc, 12, ad_read(devc, 12) | 0x40);		/* Mode2 = enabled */
 
-		  for (i = 16; i < 32; i++)
-			  ad_write(devc, i, init_values[i]);
+		if (devc->model == MD_IWAVE)
+			ad_write(devc, 12, 0x6c);	/* Select codec mode 3 */
 
-		  if (devc->model == MD_IWAVE)
-			  ad_write(devc, 16, 0x30);	/* Playback and capture counters enabled */
+		for (i = 16; i < 32; i++)
+			ad_write(devc, i, init_values[i]);
 
-	  }
+		if (devc->model == MD_IWAVE)
+			ad_write(devc, 16, 0x30);	/* Playback and capture counters enabled */
+	}
 	if (devc->model > MD_1848)
-	  {
-		  if (devc->audio_flags & DMA_DUPLEX)
-			  ad_write(devc, 9, ad_read(devc, 9) & ~0x04);	/* Dual DMA mode */
-		  else
-			  ad_write(devc, 9, ad_read(devc, 9) | 0x04);	/* Single DMA mode */
+	{
+		if (devc->audio_flags & DMA_DUPLEX)
+			ad_write(devc, 9, ad_read(devc, 9) & ~0x04);	/* Dual DMA mode */
+		else
+			ad_write(devc, 9, ad_read(devc, 9) | 0x04);	/* Single DMA mode */
 
-		  if (devc->model == MD_1845)
-			  ad_write(devc, 27, ad_read(devc, 27) | 0x08);		/* Alternate freq select enabled */
+		if (devc->model == MD_1845)
+			ad_write(devc, 27, ad_read(devc, 27) | 0x08);		/* Alternate freq select enabled */
 
-		  if (devc->model == MD_IWAVE)
-		    {		/* Some magic Interwave specific initialization */
-			    ad_write(devc, 12, 0x6c);	/* Select codec mode 3 */
-			    ad_write(devc, 16, 0x30);	/* Playback and capture counters enabled */
-			    ad_write(devc, 17, 0xc2);	/* Alternate feature enable */
-		    }
-	} else
-	  {
+		if (devc->model == MD_IWAVE)
+		{		/* Some magic Interwave specific initialization */
+			ad_write(devc, 12, 0x6c);	/* Select codec mode 3 */
+			ad_write(devc, 16, 0x30);	/* Playback and capture counters enabled */
+			ad_write(devc, 17, 0xc2);	/* Alternate feature enable */
+		}
+	}
+	else
+	{
 		  devc->audio_flags &= ~DMA_DUPLEX;
 		  ad_write(devc, 9, ad_read(devc, 9) | 0x04);	/* Single DMA mode */
-	  }
+	}
 
 	outb((0), io_Status(devc));	/* Clear pending interrupts */
 
@@ -1341,45 +1358,43 @@
 	ad1848_mixer_reset(devc);
 }
 
-int
-ad1848_detect(int io_base, int *ad_flags, int *osp)
+int ad1848_detect(int io_base, int *ad_flags, int *osp)
 {
+	unsigned char tmp;
+	ad1848_info *devc = &adev_info[nr_ad1848_devs];
+	unsigned char tmp1 = 0xff, tmp2 = 0xff;
+	int optiC930 = 0;	/* OPTi 82C930 flag */
+	int interwave = 0;
+	int ad1847_flag = 0;
+	int cs4248_flag = 0;
 
-	unsigned char   tmp;
-	ad1848_info    *devc = &adev_info[nr_ad1848_devs];
-	unsigned char   tmp1 = 0xff, tmp2 = 0xff;
-	int             optiC930 = 0;	/* OPTi 82C930 flag */
-	int             interwave = 0;
-	int             ad1847_flag = 0;
-	int             cs4248_flag = 0;
-
-	int             i;
+	int i;
 
 	DDB(printk("ad1848_detect(%x)\n", io_base));
 
 	if (ad_flags)
-	  {
-		  if (*ad_flags == 0x12345678)
-		    {
-			    interwave = 1;
-			    *ad_flags = 0;
-		    }
-		  if (*ad_flags == 0x12345677)
-		    {
-			    cs4248_flag = 1;
-			    *ad_flags = 0;
-		    }
-	  }
+	{
+		if (*ad_flags == 0x12345678)
+		{
+			interwave = 1;
+			*ad_flags = 0;
+		}
+		if (*ad_flags == 0x12345677)
+		{
+		    cs4248_flag = 1;
+		    *ad_flags = 0;
+		}
+	}
 	if (nr_ad1848_devs >= MAX_AUDIO_DEV)
-	  {
-		  printk("ad1848 - Too many audio devices\n");
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "ad1848 - Too many audio devices\n");
+		return 0;
+	}
 	if (check_region(io_base, 4))
-	  {
-		  printk("ad1848.c: Port %x not free.\n", io_base);
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "ad1848.c: Port %x not free.\n", io_base);
+		return 0;
+	}
 	devc->base = io_base;
 	devc->irq_ok = 0;
 	devc->timer_running = 0;
@@ -1393,31 +1408,33 @@
 	devc->debug_flag = 0;
 
 	/*
-	   * Check that the I/O address is in use.
-	   *
-	   * The bit 0x80 of the base I/O port is known to be 0 after the
-	   * chip has performed its power on initialization. Just assume
-	   * this has happened before the OS is starting.
-	   *
-	   * If the I/O address is unused, it typically returns 0xff.
+	 * Check that the I/O address is in use.
+	 *
+	 * The bit 0x80 of the base I/O port is known to be 0 after the
+	 * chip has performed its power on initialization. Just assume
+	 * this has happened before the OS is starting.
+	 *
+	 * If the I/O address is unused, it typically returns 0xff.
 	 */
 
 	if (inb(devc->base) == 0xff)
-	  {
-		  DDB(printk("ad1848_detect: The base I/O address appears to be dead\n"));
-	  }
-/*
- * Wait for the device to stop initialization
- */
+	{
+		DDB(printk("ad1848_detect: The base I/O address appears to be dead\n"));
+	}
+
+	/*
+	 * Wait for the device to stop initialization
+	 */
+	
 	DDB(printk("ad1848_detect() - step 0\n"));
 
 	for (i = 0; i < 10000000; i++)
-	  {
-		  unsigned char   x = inb(devc->base);
+	{
+		unsigned char   x = inb(devc->base);
 
-		  if (x == 0xff || !(x & 0x80))
-			  break;
-	  }
+		if (x == 0xff || !(x & 0x80))
+			break;
+	}
 
 	DDB(printk("ad1848_detect() - step A\n"));
 
@@ -1425,14 +1442,15 @@
 		ad_leave_MCE(devc);
 
 	if ((inb(devc->base) & 0x80) != 0x00)	/* Not a AD1848 */
-	  {
-		  DDB(printk("ad1848 detect error - step A (%02x)\n", (int) inb(devc->base)));
-		  return 0;
-	  }
+	{
+		DDB(printk("ad1848 detect error - step A (%02x)\n", (int) inb(devc->base)));
+		return 0;
+	}
+	
 	/*
-	   * Test if it's possible to change contents of the indirect registers.
-	   * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only
-	   * so try to avoid using it.
+	 * Test if it's possible to change contents of the indirect registers.
+	 * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only
+	 * so try to avoid using it.
 	 */
 
 	DDB(printk("ad1848_detect() - step B\n"));
@@ -1440,28 +1458,33 @@
 	ad_write(devc, 1, 0x45);	/* 0x55 with bit 0x10 clear */
 
 	if ((tmp1 = ad_read(devc, 0)) != 0xaa || (tmp2 = ad_read(devc, 1)) != 0x45)
+	{
 		if (tmp2 == 0x65)	/* AD1847 has couple of bits hardcoded to 1 */
 			ad1847_flag = 1;
 		else
-		  {
-			  DDB(printk("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2));
-			  return 0;
-		  }
+		{
+			DDB(printk("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2));
+			return 0;
+		}
+	}
 	DDB(printk("ad1848_detect() - step C\n"));
 	ad_write(devc, 0, 0x45);
 	ad_write(devc, 1, 0xaa);
 
 	if ((tmp1 = ad_read(devc, 0)) != 0x45 || (tmp2 = ad_read(devc, 1)) != 0xaa)
+	{
 		if (tmp2 == 0x8a)	/* AD1847 has few bits hardcoded to 1 */
 			ad1847_flag = 1;
 		else
-		  {
-			  DDB(printk("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2));
-			  return 0;
-		  }
+		{
+			DDB(printk("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2));
+			return 0;
+		}
+	}
+
 	/*
-	   * The indirect register I12 has some read only bits. Lets
-	   * try to change them.
+	 * The indirect register I12 has some read only bits. Lets
+	 * try to change them.
 	 */
 
 	DDB(printk("ad1848_detect() - step D\n"));
@@ -1469,43 +1492,47 @@
 	ad_write(devc, 12, (~tmp) & 0x0f);
 
 	if ((tmp & 0x0f) != ((tmp1 = ad_read(devc, 12)) & 0x0f))
-	  {
-		  DDB(printk("ad1848 detect error - step D (%x)\n", tmp1));
-		  return 0;
-	  }
+	{
+		DDB(printk("ad1848 detect error - step D (%x)\n", tmp1));
+		return 0;
+	}
+	
 	/*
-	   * NOTE! Last 4 bits of the reg I12 tell the chip revision.
-	   *   0x01=RevB and 0x0A=RevC.
+	 * NOTE! Last 4 bits of the reg I12 tell the chip revision.
+	 *   0x01=RevB and 0x0A=RevC.
 	 */
 
 	/*
-	   * The original AD1848/CS4248 has just 15 indirect registers. This means
-	   * that I0 and I16 should return the same value (etc.).
-	   * However this doesn't work with CS4248. Actually it seems to be impossible
-	   * to detect if the chip is a CS4231 or CS4248.
-	   * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails
-	   * with CS4231.
+	 * The original AD1848/CS4248 has just 15 indirect registers. This means
+	 * that I0 and I16 should return the same value (etc.).
+	 * However this doesn't work with CS4248. Actually it seems to be impossible
+	 * to detect if the chip is a CS4231 or CS4248.
+	 * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails
+	 * with CS4231.
 	 */
 
-/*
- * OPTi 82C930 has mode2 control bit in another place. This test will fail
- * with it. Accept this situation as a possible indication of this chip.
- */
+	/*
+	 * OPTi 82C930 has mode2 control bit in another place. This test will fail
+	 * with it. Accept this situation as a possible indication of this chip.
+	 */
 
 	DDB(printk("ad1848_detect() - step F\n"));
 	ad_write(devc, 12, 0);	/* Mode2=disabled */
 
 	for (i = 0; i < 16; i++)
+	{
 		if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16)))
-		  {
-			  DDB(printk("ad1848 detect step F(%d/%x/%x) - OPTi chip???\n", i, tmp1, tmp2));
-			  if (!ad1847_flag)
-				  optiC930 = 1;
-			  break;
-		  }
+		{
+			DDB(printk("ad1848 detect step F(%d/%x/%x) - OPTi chip???\n", i, tmp1, tmp2));
+			if (!ad1847_flag)
+				optiC930 = 1;
+			break;
+		}
+	}
+
 	/*
-	   * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40).
-	   * The bit 0x80 is always 1 in CS4248 and CS4231.
+	 * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40).
+	 * The bit 0x80 is always 1 in CS4248 and CS4231.
 	 */
 
 	DDB(printk("ad1848_detect() - step G\n"));
@@ -1521,66 +1548,67 @@
 
 	tmp1 = ad_read(devc, 12);
 	if (tmp1 & 0x80)
-	  {
-		  if (ad_flags)
-			  *ad_flags |= AD_F_CS4248;
+	{
+		if (ad_flags)
+			*ad_flags |= AD_F_CS4248;
 
-		  devc->chip_name = "CS4248";	/* Our best knowledge just now */
-	  }
+		devc->chip_name = "CS4248";	/* Our best knowledge just now */
+	}
 	if (optiC930 || (tmp1 & 0xc0) == (0x80 | 0x40))
-	  {
-		  /*
-		     *      CS4231 detected - is it?
-		     *
-		     *      Verify that setting I0 doesn't change I16.
-		   */
-		  DDB(printk("ad1848_detect() - step H\n"));
-		  ad_write(devc, 16, 0);	/* Set I16 to known value */
-
-		  ad_write(devc, 0, 0x45);
-		  if ((tmp1 = ad_read(devc, 16)) != 0x45)	/* No change -> CS4231? */
-		    {
-
-			    ad_write(devc, 0, 0xaa);
-			    if ((tmp1 = ad_read(devc, 16)) == 0xaa)	/* Rotten bits? */
-			      {
-				      DDB(printk("ad1848 detect error - step H(%x)\n", tmp1));
-				      return 0;
-			      }
-			    /*
-			       * Verify that some bits of I25 are read only.
-			     */
-
-			    DDB(printk("ad1848_detect() - step I\n"));
-			    tmp1 = ad_read(devc, 25);	/* Original bits */
-			    ad_write(devc, 25, ~tmp1);	/* Invert all bits */
-			    if ((ad_read(devc, 25) & 0xe7) == (tmp1 & 0xe7))
-			      {
-				      int             id, full_id;
-
-				      /*
-				       *      It's at least CS4231
-				       */
-				      devc->chip_name = "CS4231";
-
-				      devc->model = MD_4231;
-
-				      /*
-				       * It could be an AD1845 or CS4231A as well.
-				       * CS4231 and AD1845 report the same revision info in I25
-				       * while the CS4231A reports different.
-				       */
-
-				      id = ad_read(devc, 25) & 0xe7;
-				      full_id = ad_read(devc, 25);
-				      if (id == 0x80)	/* Device busy??? */
-					      id = ad_read(devc, 25) & 0xe7;
-				      if (id == 0x80)	/* Device still busy??? */
-					      id = ad_read(devc, 25) & 0xe7;
-				      DDB(printk("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read(devc, 25)));
+	{
+		/*
+		 *      CS4231 detected - is it?
+		 *
+		 *      Verify that setting I0 doesn't change I16.
+		 */
+		
+		DDB(printk("ad1848_detect() - step H\n"));
+		ad_write(devc, 16, 0);	/* Set I16 to known value */
+
+		ad_write(devc, 0, 0x45);
+		if ((tmp1 = ad_read(devc, 16)) != 0x45)	/* No change -> CS4231? */
+		{
+			ad_write(devc, 0, 0xaa);
+			if ((tmp1 = ad_read(devc, 16)) == 0xaa)	/* Rotten bits? */
+			{
+				DDB(printk("ad1848 detect error - step H(%x)\n", tmp1));
+				return 0;
+			}
+			
+			/*
+			 * Verify that some bits of I25 are read only.
+			 */
+
+			DDB(printk("ad1848_detect() - step I\n"));
+			tmp1 = ad_read(devc, 25);	/* Original bits */
+			ad_write(devc, 25, ~tmp1);	/* Invert all bits */
+			if ((ad_read(devc, 25) & 0xe7) == (tmp1 & 0xe7))
+			{
+				int id, full_id;
 
-				      switch (id)
-					{
+				/*
+				 *      It's at least CS4231
+				 */
+				
+				devc->chip_name = "CS4231";
+				devc->model = MD_4231;
+
+				/*
+				 * It could be an AD1845 or CS4231A as well.
+				 * CS4231 and AD1845 report the same revision info in I25
+				 * while the CS4231A reports different.
+				 */
+
+				id = ad_read(devc, 25) & 0xe7;
+				full_id = ad_read(devc, 25);
+				if (id == 0x80)	/* Device busy??? */
+					id = ad_read(devc, 25) & 0xe7;
+				if (id == 0x80)	/* Device still busy??? */
+					id = ad_read(devc, 25) & 0xe7;
+				DDB(printk("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read(devc, 25)));
+
+				switch (id)
+				{
 
 					case 0xa0:
 						devc->chip_name = "CS4231A";
@@ -1618,25 +1646,26 @@
 							 */
 
 							unsigned char   tmp = ad_read(devc, 23);
-
 							ad_write(devc, 23, ~tmp);
+
 							if (interwave)
-							  {
-								  devc->model = MD_IWAVE;
-								  devc->chip_name = "IWave";
-							} else if (ad_read(devc, 23) != tmp)	/* AD1845 ? */
-							  {
-								  devc->chip_name = "AD1845";
-								  devc->model = MD_1845;
-							} else if (cs4248_flag)
-							  {
-								  if (ad_flags)
+							{
+								devc->model = MD_IWAVE;
+								devc->chip_name = "IWave";
+							}
+							else if (ad_read(devc, 23) != tmp)	/* AD1845 ? */
+							{
+								devc->chip_name = "AD1845";
+								devc->model = MD_1845;
+							}
+							else if (cs4248_flag)
+							{
+								if (ad_flags)
 									  *ad_flags |= AD_F_CS4248;
-
-								  devc->chip_name = "CS4248";
-								  devc->model = MD_1848;
-								  ad_write(devc, 12, ad_read(devc, 12) & ~0x40);	/* Mode2 off */
-							  }
+								devc->chip_name = "CS4248";
+								devc->model = MD_1848;
+								ad_write(devc, 12, ad_read(devc, 12) & ~0x40);	/* Mode2 off */
+							}
 							ad_write(devc, 23, tmp);	/* Restore */
 						}
 						break;
@@ -1644,27 +1673,27 @@
 					default:	/* Assume CS4231 or OPTi 82C930 */
 						DDB(printk("ad1848: I25 = %02x/%02x\n", ad_read(devc, 25), ad_read(devc, 25) & 0xe7));
 						if (optiC930)
-						  {
-							  devc->chip_name = "82C930";
-							  devc->model = MD_C930;
-						} else
-						  {
-							  devc->model = MD_4231;
-						  }
-
-					}
-			      }
-			    ad_write(devc, 25, tmp1);	/* Restore bits */
-
-			    DDB(printk("ad1848_detect() - step K\n"));
-		    }
-	  }
+						{
+							devc->chip_name = "82C930";
+							devc->model = MD_C930;
+						}
+						else
+						{
+							devc->model = MD_4231;
+						}
+				}
+			}
+			ad_write(devc, 25, tmp1);	/* Restore bits */
+
+			DDB(printk("ad1848_detect() - step K\n"));
+		}
+	}
 	DDB(printk("ad1848_detect() - step L\n"));
 	if (ad_flags)
-	  {
+	{
 		  if (devc->model != MD_1848)
 			  *ad_flags |= AD_F_CS4231;
-	  }
+	}
 	DDB(printk("ad1848_detect() - Detected OK\n"));
 
 	if (devc->model == MD_1848 && ad1847_flag)
@@ -1674,19 +1703,18 @@
 	return 1;
 }
 
-int
-ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capture, int share_dma, int *osp)
+int ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capture, int share_dma, int *osp)
 {
 	/*
-	   * NOTE! If irq < 0, there is another driver which has allocated the IRQ
-	   *   so that this driver doesn't need to allocate/deallocate it.
-	   *   The actually used IRQ is ABS(irq).
+	 * NOTE! If irq < 0, there is another driver which has allocated the IRQ
+	 *   so that this driver doesn't need to allocate/deallocate it.
+	 *   The actually used IRQ is ABS(irq).
 	 */
 
 
-	int             my_dev;
-	char            dev_name[100];
-	int             e;
+	int my_dev;
+	char dev_name[100];
+	int e;
 
 	ad1848_info    *devc = &adev_info[nr_ad1848_devs];
 
@@ -1711,19 +1739,19 @@
 
 	request_region(devc->base, 4, devc->name);
 
-	conf_printf2(dev_name,
-		     devc->base, devc->irq, dma_playback, dma_capture);
+	conf_printf2(dev_name, devc->base, devc->irq, dma_playback, dma_capture);
 
 	if (devc->model == MD_1848 || devc->model == MD_C930)
 		devc->audio_flags |= DMA_HARDSTOP;
 
 	if (devc->model > MD_1848)
-	  {
-		  if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1)
-			  devc->audio_flags &= ~DMA_DUPLEX;
-		  else
-			  devc->audio_flags |= DMA_DUPLEX;
-	  }
+	{
+		if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1)
+			devc->audio_flags &= ~DMA_DUPLEX;
+		else
+			devc->audio_flags |= DMA_DUPLEX;
+	}
+	
 	if ((my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
 					     dev_name,
 					     &ad1848_audio_driver,
@@ -1733,9 +1761,9 @@
 					     devc,
 					     dma_playback,
 					     dma_capture)) < 0)
-	  {
-		  return -1;
-	  }
+	{
+		return -1;
+	}
 	portc = (ad1848_port_info *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(ad1848_port_info)));
 	sound_mem_sizes[sound_nblocks] = sizeof(ad1848_port_info);
 	if (sound_nblocks < 1024)
@@ -1748,37 +1776,38 @@
 	ad1848_init_hw(devc);
 
 	if (irq > 0)
-	  {
-		  irq2dev[irq] = devc->dev_no = my_dev;
-		  if (snd_set_irq_handler(devc->irq, adintr,
+	{
+		irq2dev[irq] = devc->dev_no = my_dev;
+		if (snd_set_irq_handler(devc->irq, adintr,
 					  devc->name,
 					  NULL) < 0)
-		    {
-			    printk(KERN_WARNING "ad1848: IRQ in use\n");
-		    }
-		  if (devc->model != MD_1848 && devc->model != MD_C930)
-		    {
-			    int             x;
-			    unsigned char   tmp = ad_read(devc, 16);
-
-			    devc->timer_ticks = 0;
-
-			    ad_write(devc, 21, 0x00);	/* Timer MSB */
-			    ad_write(devc, 20, 0x10);	/* Timer LSB */
-
-			    ad_write(devc, 16, tmp | 0x40);	/* Enable timer */
-			    for (x = 0; x < 100000 && devc->timer_ticks == 0; x++);
-			    ad_write(devc, 16, tmp & ~0x40);	/* Disable timer */
-
-			    if (devc->timer_ticks == 0)
-				    printk(KERN_WARNING "ad1848: Interrupt test failed (IRQ%d)\n", devc->irq);
-			    else
-			      {
-				      DDB(printk("Interrupt test OK\n"));
-				      devc->irq_ok = 1;
-			      }
-		  } else
-			  devc->irq_ok = 1;	/* Couldn't test. assume it's OK */
+		{
+			printk(KERN_WARNING "ad1848: IRQ in use\n");
+		}
+		if (devc->model != MD_1848 && devc->model != MD_C930)
+		{
+			int             x;
+			unsigned char   tmp = ad_read(devc, 16);
+
+			devc->timer_ticks = 0;
+
+			ad_write(devc, 21, 0x00);	/* Timer MSB */
+			ad_write(devc, 20, 0x10);	/* Timer LSB */
+
+			ad_write(devc, 16, tmp | 0x40);	/* Enable timer */
+			for (x = 0; x < 100000 && devc->timer_ticks == 0; x++);
+			ad_write(devc, 16, tmp & ~0x40);	/* Disable timer */
+
+			if (devc->timer_ticks == 0)
+				printk(KERN_WARNING "ad1848: Interrupt test failed (IRQ%d)\n", devc->irq);
+			else
+			{
+				DDB(printk("Interrupt test OK\n"));
+				devc->irq_ok = 1;
+			}
+		}
+		else
+			devc->irq_ok = 1;	/* Couldn't test. assume it's OK */
 	} else if (irq < 0)
 		irq2dev[-irq] = devc->dev_no = my_dev;
 
@@ -1789,22 +1818,22 @@
 #endif
 
 	if (!share_dma)
-	  {
-		  if (sound_alloc_dma(dma_playback, devc->name))
-			  printk("ad1848.c: Can't allocate DMA%d\n", dma_playback);
-
-		  if (dma_capture != dma_playback)
-			  if (sound_alloc_dma(dma_capture, devc->name))
-				  printk("ad1848.c: Can't allocate DMA%d\n", dma_capture);
-	  }
+	{
+		if (sound_alloc_dma(dma_playback, devc->name))
+			printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_playback);
+
+		if (dma_capture != dma_playback)
+			if (sound_alloc_dma(dma_capture, devc->name))
+				printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_capture);
+	}
 	if ((e = sound_install_mixer(MIXER_DRIVER_VERSION,
 				     dev_name,
 				     &ad1848_mixer_operations,
 				     sizeof(struct mixer_operations),
 				     devc)) >= 0)
-	  {
-		  audio_devs[my_dev]->mixer_dev = e;
-	  }
+	{
+		audio_devs[my_dev]->mixer_dev = e;
+	}
 	MOD_INC_USE_COUNT;
 	return my_dev;
 }
@@ -1819,76 +1848,79 @@
 	devc = &adev_info[nr_ad1848_devs - 1];
 
 	switch (cmd)
-	  {
-	  case AD1848_SET_XTAL:	/* Change clock frequency of AD1845 (only ) */
-		  if (devc->model != MD_1845)
-			  return;
-		  ad_enter_MCE(devc);
-		  ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5));
-		  ad_leave_MCE(devc);
-		  break;
-
-	  case AD1848_MIXER_REROUTE:
-		  {
-			  int             o = (arg >> 8) & 0xff;
-			  int             n = arg & 0xff;
-
-			  if (n == SOUND_MIXER_NONE)
-			    {	/* Just hide this control */
-				    ad1848_mixer_set(devc, o, 0);	/* Shut up it */
-				    devc->supported_devices &= ~(1 << o);
-				    devc->supported_rec_devices &= ~(1 << o);
-				    return;
-			    }
-			  /* Make the mixer control identified by o to appear as n */
-
-			  if (o < 0 || o > SOUND_MIXER_NRDEVICES)
-				  return;
-			  if (n < 0 || n > SOUND_MIXER_NRDEVICES)
-				  return;
-			  if (!(devc->supported_devices & (1 << o)))
-				  return;	/* Not supported */
-
-			  devc->mixer_reroute[n] = o;	/* Rename the control */
-			  devc->supported_devices &= ~(1 << o);
-			  devc->supported_devices |= (1 << n);
-			  if (devc->supported_rec_devices & (1 << o))
-				  devc->supported_rec_devices |= (1 << n);
-			  devc->supported_rec_devices &= ~(1 << o);
-		  }
-		  break;
-	  }
+	{
+		case AD1848_SET_XTAL:	/* Change clock frequency of AD1845 (only ) */
+		if (devc->model != MD_1845)
+				return;
+			ad_enter_MCE(devc);
+			ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5));
+			ad_leave_MCE(devc);
+			break;
+
+		case AD1848_MIXER_REROUTE:
+		{
+			int             o = (arg >> 8) & 0xff;
+			int             n = arg & 0xff;
+
+			if (n == SOUND_MIXER_NONE)
+			{	/* Just hide this control */
+				ad1848_mixer_set(devc, o, 0);	/* Shut up it */
+				devc->supported_devices &= ~(1 << o);
+				devc->supported_rec_devices &= ~(1 << o);
+				return;
+			}
+			/* Make the mixer control identified by o to appear as n */
+
+			if (o < 0 || o > SOUND_MIXER_NRDEVICES)
+				return;
+			if (n < 0 || n > SOUND_MIXER_NRDEVICES)
+				return;
+			if (!(devc->supported_devices & (1 << o)))
+				return;	/* Not supported */
+
+			devc->mixer_reroute[n] = o;	/* Rename the control */
+			devc->supported_devices &= ~(1 << o);
+			devc->supported_devices |= (1 << n);
+			if (devc->supported_rec_devices & (1 << o))
+				devc->supported_rec_devices |= (1 << n);
+			devc->supported_rec_devices &= ~(1 << o);
+		}
+		break;
+	}
 	return;
 }
 
-void
-ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int share_dma)
+void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int share_dma)
 {
-	int             i, dev = 0;
-	ad1848_info    *devc = NULL;
+	int i, dev = 0;
+	ad1848_info *devc = NULL;
 
 	for (i = 0; devc == NULL && i < nr_ad1848_devs; i++)
+	{
 		if (adev_info[i].base == io_base)
-		  {
-			  devc = &adev_info[i];
-			  dev = devc->dev_no;
-		  }
+		{
+			devc = &adev_info[i];
+			dev = devc->dev_no;
+		}
+	}
+		
 	if (devc != NULL)
-	  {
-		  release_region(devc->base, 4);
+	{
+		release_region(devc->base, 4);
 
-		  if (!share_dma)
-		    {
-			    if (irq > 0)
-				    snd_release_irq(devc->irq);
-
-			    sound_free_dma(audio_devs[dev]->dmap_out->dma);
-
-			    if (audio_devs[dev]->dmap_in->dma != audio_devs[dev]->dmap_out->dma)
-				    sound_free_dma(audio_devs[dev]->dmap_in->dma);
-		    }
-	} else
-		printk("ad1848: Can't find device to be unloaded. Base=%x\n", io_base);
+		if (!share_dma)
+		{
+			if (irq > 0)
+				snd_release_irq(devc->irq);
+
+			sound_free_dma(audio_devs[dev]->dmap_out->dma);
+
+			if (audio_devs[dev]->dmap_in->dma != audio_devs[dev]->dmap_out->dma)
+				sound_free_dma(audio_devs[dev]->dmap_in->dma);
+		}
+	}
+	else
+		printk(KERN_ERR "ad1848: Can't find device to be unloaded. Base=%x\n", io_base);
 	MOD_DEC_USE_COUNT;
 }
 
@@ -1902,94 +1934,96 @@
 	int             cnt = 0;
 
 	if (irq < 0 || irq > 15)
-	  {
-		  dev = -1;
-	} else
+	{
+		dev = -1;
+	}
+	else
 		dev = irq2dev[irq];
 
 	if (dev < 0 || dev >= num_audiodevs)
-	  {
-		  for (irq = 0; irq < 17; irq++)
-			  if (irq2dev[irq] != -1)
-				  break;
-
-		  if (irq > 15)
-		    {
-			    /* printk("ad1848.c: Bogus interrupt %d\n", irq); */
-			    return;
-		    }
-		  dev = irq2dev[irq];
-		  devc = (ad1848_info *) audio_devs[dev]->devc;
+	{
+		for (irq = 0; irq < 17; irq++)
+			if (irq2dev[irq] != -1)
+				break;
+
+		if (irq > 15)
+		{
+			/* printk("ad1848.c: Bogus interrupt %d\n", irq); */
+			return;
+		}
+		dev = irq2dev[irq];
+		devc = (ad1848_info *) audio_devs[dev]->devc;
 	} else
 		devc = (ad1848_info *) audio_devs[dev]->devc;
 
-      interrupt_again:		/* Jump back here if int status doesn't reset */
+interrupt_again:		/* Jump back here if int status doesn't reset */
 
 	status = inb(io_Status(devc));
 
 	if (status == 0x80)
-		printk("adintr: Why?\n");
+		printk(KERN_DEBUG "adintr: Why?\n");
 	if (devc->model == MD_1848)
 		outb((0), io_Status(devc));	/* Clear interrupt status */
 
 	if (status & 0x01)
-	  {
-		  if (devc->model == MD_C930)
-		    {		/* 82C930 has interrupt status register in MAD16 register MC11 */
-			    unsigned long   flags;
-
-			    save_flags(flags);
-			    cli();
-
-			    alt_stat = 0;
-
-			    if (devc->c930_password_port)
-				    outb((0xe4), devc->c930_password_port);	/* Password */
-			    outb((11), 0xe0e);
-			    c930_stat = inb(0xe0f);
-
-			    if (c930_stat & 0x04)
-				    alt_stat |= 0x10;	/* Playback intr */
-			    if (c930_stat & 0x08)
-				    alt_stat |= 0x20;	/* Playback intr */
-			    restore_flags(flags);
-		  } else if (devc->model != MD_1848)
-			  alt_stat = ad_read(devc, 24);
-
-		  /* Acknowledge the intr before proceeding */
-		  if (devc->model == MD_C930)
-		    {		/* 82C930 has interrupt status register in MAD16 register MC11 */
-			    unsigned long   flags;
-
-			    save_flags(flags);
-			    cli();
-
-			    if (devc->c930_password_port)
-				    outb((0xe4), devc->c930_password_port);	/* Password */
-			    outb((11), 0xe0e);
-			    outb((~c930_stat), 0xe0f);
-			    restore_flags(flags);
-		  } else if (devc->model != MD_1848)
-			  ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat);	/* Selective ack */
-
-		  if (devc->open_mode & OPEN_READ && devc->audio_mode & PCM_ENABLE_INPUT && alt_stat & 0x20)
-		    {
-			    DMAbuf_inputintr(devc->record_dev);
-		    }
-		  if (devc->open_mode & OPEN_WRITE && devc->audio_mode & PCM_ENABLE_OUTPUT &&
+	{
+		if (devc->model == MD_C930)
+		{		/* 82C930 has interrupt status register in MAD16 register MC11 */
+			unsigned long   flags;
+
+			save_flags(flags);
+			cli();
+
+			alt_stat = 0;
+
+			if (devc->c930_password_port)
+				outb((0xe4), devc->c930_password_port);	/* Password */
+			outb(11, 0xe0e);
+			c930_stat = inb(0xe0f);
+
+			if (c930_stat & 0x04)
+				alt_stat |= 0x10;	/* Playback intr */
+			if (c930_stat & 0x08)
+				alt_stat |= 0x20;	/* Playback intr */
+			restore_flags(flags);
+		} else if (devc->model != MD_1848)
+			alt_stat = ad_read(devc, 24);
+
+		/* Acknowledge the intr before proceeding */
+		if (devc->model == MD_C930)
+		{		/* 82C930 has interrupt status register in MAD16 register MC11 */
+			unsigned long   flags;
+
+			save_flags(flags);
+			cli();
+
+			if (devc->c930_password_port)
+				outb((0xe4), devc->c930_password_port);	/* Password */
+			outb((11), 0xe0e);
+			outb((~c930_stat), 0xe0f);
+			restore_flags(flags);
+		}
+		else if (devc->model != MD_1848)
+			ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat);	/* Selective ack */
+
+		if (devc->open_mode & OPEN_READ && devc->audio_mode & PCM_ENABLE_INPUT && alt_stat & 0x20)
+		{
+			DMAbuf_inputintr(devc->record_dev);
+		}
+		if (devc->open_mode & OPEN_WRITE && devc->audio_mode & PCM_ENABLE_OUTPUT &&
 		      alt_stat & 0x10)
-		    {
-			    DMAbuf_outputintr(devc->playback_dev, 1);
-		    }
-		  if (devc->model != MD_1848 && alt_stat & 0x40)	/* Timer interrupt */
-		    {
-			    devc->timer_ticks++;
+		{
+			DMAbuf_outputintr(devc->playback_dev, 1);
+		}
+		if (devc->model != MD_1848 && alt_stat & 0x40)	/* Timer interrupt */
+		{
+			devc->timer_ticks++;
 #if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE)
-			    if (timer_installed == dev && devc->timer_running)
-				    sound_timer_interrupt();
+			if (timer_installed == dev && devc->timer_running)
+				sound_timer_interrupt();
 #endif
-		    }
-	  }
+		}
+	}
 /*
  * Sometimes playback or capture interrupts occur while a timer interrupt
  * is being handled. The interrupt will not be retriggered if we don't
@@ -1997,63 +2031,61 @@
  * the handler in this case.
  */
 	if (inb(io_Status(devc)) & 0x01 && cnt++ < 4)
-	  {
+	{
 		  goto interrupt_again;
-	  }
+	}
 }
 
-#ifdef DESKPROXL
 /*
- * Very experimental initialization sequence for the integrated sound system
- * of Compaq Deskpro XL. Will be moved somewhere else in future.
+ *	Experimental initialization sequence for the integrated sound system
+ *	of Compaq Deskpro XL.
  */
 
-static int
-init_deskpro(struct address_info *hw_config)
+static int init_deskpro(struct address_info *hw_config)
 {
 	unsigned char   tmp;
 
 	if ((tmp = inb(0xc44)) == 0xff)
-	  {
-		  DDB(printk("init_deskpro: Dead port 0xc44\n"));
-		  return 0;
-	  }
+	{
+		DDB(printk("init_deskpro: Dead port 0xc44\n"));
+		return 0;
+	}
 	outb((tmp | 0x04), 0xc44);	/* Select bank 1 */
 	if (inb(0xc44) != 0x04)
-	  {
-		  DDB(printk("init_deskpro: Invalid bank1 signature in port 0xc44\n"));
-		  return 0;
-	  }
-/*
- * OK. It looks like a Deskpro so let's proceed.
- */
+	{
+		DDB(printk("init_deskpro: Invalid bank1 signature in port 0xc44\n"));
+		return 0;
+	}
+	/*
+	 * OK. It looks like a Deskpro so let's proceed.
+	 */
 
-/*
- * I/O port 0xc44 Audio configuration register.
- *
- * bits 0xc0:   Audio revision bits
- *              0x00 = Compaq Business Audio
- *              0x40 = MS Sound System Compatible (reset default)
- *              0x80 = Reserved
- *              0xc0 = Reserved
- * bit 0x20:    No Wait State Enable
- *              0x00 = Disabled (reset default, DMA mode)
- *              0x20 = Enabled (programmed I/O mode)
- * bit 0x10:    MS Sound System Decode Enable
- *              0x00 = Decoding disabled (reset default)
- *              0x10 = Decoding enabled
- * bit 0x08:    FM Synthesis Decode Enable
- *              0x00 = Decoding Disabled (reset default)
- *              0x08 = Decoding enabled
- * bit 0x04     Bank select
- *              0x00 = Bank 0
- *              0x04 = Bank 1
- * bits 0x03    MSS Base address
- *              0x00 = 0x530 (reset default)
- *              0x01 = 0x604
- *              0x02 = 0xf40
- *              0x03 = 0xe80
- */
+	/*
+	 * I/O port 0xc44 Audio configuration register.
+	 *
+	 * bits 0xc0:   Audio revision bits
+	 *              0x00 = Compaq Business Audio
+	 *              0x40 = MS Sound System Compatible (reset default)
+	 *              0x80 = Reserved
+	 *              0xc0 = Reserved
+	 * bit 0x20:    No Wait State Enable
+	 *              0x00 = Disabled (reset default, DMA mode)
+	 *              0x20 = Enabled (programmed I/O mode)
+	 * bit 0x10:    MS Sound System Decode Enable
+	 *              0x00 = Decoding disabled (reset default)
+	 *              0x10 = Decoding enabled
+	 * bit 0x08:    FM Synthesis Decode Enable
+	 *              0x00 = Decoding Disabled (reset default)
+	 *              0x08 = Decoding enabled
+	 * bit 0x04     Bank select
+	 *              0x00 = Bank 0
+	 *              0x04 = Bank 1
+	 * bits 0x03    MSS Base address
+	 *              0x00 = 0x530 (reset default)
+	 *              0x01 = 0x604
+	 *              0x02 = 0xf40
+	 *              0x03 = 0xe80
+	 */
 
 #ifdef DEBUGXL
 	/* Debug printing */
@@ -2068,23 +2100,23 @@
 	tmp = 0x58;		/* MSS Mode, MSS&FM decode enabled */
 
 	switch (hw_config->io_base)
-	  {
-	  case 0x530:
-		  tmp |= 0x00;
-		  break;
-	  case 0x604:
-		  tmp |= 0x01;
-		  break;
-	  case 0xf40:
-		  tmp |= 0x02;
-		  break;
-	  case 0xe80:
-		  tmp |= 0x03;
-		  break;
-	  default:
-		  DDB(printk("init_deskpro: Invalid MSS port %x\n", hw_config->io_base));
-		  return 0;
-	  }
+	{
+		case 0x530:
+			tmp |= 0x00;
+			break;
+		case 0x604:
+			tmp |= 0x01;
+			break;
+		case 0xf40:
+			tmp |= 0x02;
+			break;
+		case 0xe80:
+			tmp |= 0x03;
+			break;
+		default:
+			DDB(printk("init_deskpro: Invalid MSS port %x\n", hw_config->io_base));
+			return 0;
+	}
 	outb((tmp & ~0x04), 0xc44);	/* Write to bank=0 */
 
 #ifdef DEBUGXL
@@ -2096,15 +2128,15 @@
 	printk("%02x\n", inb(0xc44));
 #endif
 
-/*
- * I/O port 0xc45 FM Address Decode/MSS ID Register.
- *
- * bank=0, bits 0xfe:   FM synthesis Decode Compare bits 7:1 (default=0x88)
- * bank=0, bit 0x01:    SBIC Power Control Bit
- *                      0x00 = Powered up
- *                      0x01 = Powered down
- * bank=1, bits 0xfc:   MSS ID (default=0x40)
- */
+	/*
+	 * I/O port 0xc45 FM Address Decode/MSS ID Register.
+	 *
+	 * bank=0, bits 0xfe:   FM synthesis Decode Compare bits 7:1 (default=0x88)
+	 * bank=0, bit 0x01:    SBIC Power Control Bit
+	 *                      0x00 = Powered up
+	 *                      0x01 = Powered down
+	 * bank=1, bits 0xfc:   MSS ID (default=0x40)
+	 */
 
 #ifdef DEBUGXL
 	/* Debug printing */
@@ -2130,12 +2162,12 @@
 #endif
 
 
-/*
- * I/O port 0xc46 FM Address Decode/Address ASIC Revision Register.
- *
- * bank=0, bits 0xff:   FM synthesis Decode Compare bits 15:8 (default=0x03)
- * bank=1, bits 0xff:   Audio addressing ASIC id
- */
+	/*
+	 * I/O port 0xc46 FM Address Decode/Address ASIC Revision Register.
+	 *
+	 * bank=0, bits 0xff:   FM synthesis Decode Compare bits 15:8 (default=0x03)
+	 * bank=1, bits 0xff:   Audio addressing ASIC id
+	 */
 
 #ifdef DEBUGXL
 	/* Debug printing */
@@ -2160,12 +2192,12 @@
 	printk("%02x\n", inb(0xc46));
 #endif
 
-/*
- * I/O port 0xc47 FM Address Decode Register.
- *
- * bank=0, bits 0xff:   Decode enable selection for various FM address bits
- * bank=1, bits 0xff:   Reserved
- */
+	/*
+	 * I/O port 0xc47 FM Address Decode Register.
+	 *
+	 * bank=0, bits 0xff:   Decode enable selection for various FM address bits
+	 * bank=1, bits 0xff:   Reserved
+	 */
 
 #ifdef DEBUGXL
 	/* Debug printing */
@@ -2190,9 +2222,9 @@
 	printk("%02x\n", inb(0xc47));
 #endif
 
-/*
- * I/O port 0xc6f = Audio Disable Function Register
- */
+	/*
+	 * I/O port 0xc6f = Audio Disable Function Register
+	 */
 
 #ifdef DEBUGXL
 	printk("Port 0xc6f (before) = %02x\n", inb(0xc6f));
@@ -2206,32 +2238,29 @@
 
 	return 1;
 }
-#endif
 
-int
-probe_ms_sound(struct address_info *hw_config)
+int probe_ms_sound(struct address_info *hw_config)
 {
 	unsigned char   tmp;
 
 	DDB(printk("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype));
 
 	if (check_region(hw_config->io_base, 8))
-	  {
-		  printk("MSS: I/O port conflict\n");
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "MSS: I/O port conflict\n");
+		return 0;
+	}
 	if (hw_config->card_subtype == 1)	/* Has no IRQ/DMA registers */
-	  {
-		  /* check_opl3(0x388, hw_config); */
-		  return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp);
-	  }
-#ifdef DESKPROXL
-	if (hw_config->card_subtype == 2)	/* Compaq Deskpro XL */
-	  {
-		  if (!init_deskpro(hw_config))
-			  return 0;
-	  }
-#endif
+	{
+		/* check_opl3(0x388, hw_config); */
+		return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp);
+	}
+
+	if (deskpro_xl && hw_config->card_subtype == 2)	/* Compaq Deskpro XL */
+	{
+		if (!init_deskpro(hw_config))
+			return 0;
+	}
 
 	/*
 	   * Check if the IO port returns valid signature. The original MS Sound
@@ -2240,58 +2269,57 @@
 	 */
 
 	if ((tmp = inb(hw_config->io_base + 3)) == 0xff)	/* Bus float */
-	  {
+	{
 		  int             ret;
 
 		  DDB(printk("I/O address is inactive (%x)\n", tmp));
 		  if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp)))
 			  return 0;
 		  return 1;
-	  }
+	}
 	DDB(printk("MSS signature = %x\n", tmp & 0x3f));
 	if ((tmp & 0x3f) != 0x04 &&
 	    (tmp & 0x3f) != 0x0f &&
 	    (tmp & 0x3f) != 0x00)
-	  {
-		  int             ret;
+	{
+		int ret;
 
-		  MDB(printk("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb(hw_config->io_base + 3)));
-		  DDB(printk("Trying to detect codec anyway but IRQ/DMA may not work\n"));
-		  if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp)))
-			  return 0;
+		MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb(hw_config->io_base + 3)));
+		DDB(printk("Trying to detect codec anyway but IRQ/DMA may not work\n"));
+		if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp)))
+			return 0;
 
-		  hw_config->card_subtype = 1;
-		  return 1;
-	  }
+		hw_config->card_subtype = 1;
+		return 1;
+	}
 	if (hw_config->irq > 11)
-	  {
-		  printk("MSS: Bad IRQ %d\n", hw_config->irq);
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq);
+		return 0;
+	}
 	if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
-	  {
-		  printk("MSS: Bad DMA %d\n", hw_config->dma);
+	{
+		  printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma);
 		  return 0;
-	  }
+	}
 	/*
-	   * Check that DMA0 is not in use with a 8 bit board.
+	 * Check that DMA0 is not in use with a 8 bit board.
 	 */
 
 	if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80)
-	  {
-		  printk("MSS: Can't use DMA0 with a 8 bit card/slot\n");
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "MSS: Can't use DMA0 with a 8 bit card/slot\n");
+		return 0;
+	}
 	if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80)
-	  {
-		  printk("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq);
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq);
+		return 0;
+	}
 	return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp);
 }
 
-void
-attach_ms_sound(struct address_info *hw_config)
+void attach_ms_sound(struct address_info *hw_config)
 {
 	static char     interrupt_bits[12] =
 	{
@@ -2304,63 +2332,65 @@
 		1, 2, 0, 3
 	};
 
-	int             config_port = hw_config->io_base + 0;
-	int             version_port = hw_config->io_base + 3;
-	int             dma = hw_config->dma;
-	int             dma2 = hw_config->dma2;
+	int config_port = hw_config->io_base + 0;
+	int version_port = hw_config->io_base + 3;
+	int dma = hw_config->dma;
+	int dma2 = hw_config->dma2;
 
 	if (hw_config->card_subtype == 1)	/* Has no IRQ/DMA registers */
-	  {
-		  hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4,
+	{
+		hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4,
 						    hw_config->irq,
 						    hw_config->dma,
 				     hw_config->dma2, 0, hw_config->osp);
-		  request_region(hw_config->io_base, 4, "WSS config");
-		  return;
-	  }
+		request_region(hw_config->io_base, 4, "WSS config");
+		return;
+	}
 	/*
-	   * Set the IRQ and DMA addresses.
+	 * Set the IRQ and DMA addresses.
 	 */
 
 	bits = interrupt_bits[hw_config->irq];
 	if (bits == -1)
-	  {
-		  printk("MSS: Bad IRQ %d\n", hw_config->irq);
-		  return;
-	  }
+	{
+		printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq);
+		return;
+	}
 	outb((bits | 0x40), config_port);
 	if ((inb(version_port) & 0x40) == 0)
-		printk("[MSS: IRQ Conflict?]");
+		printk(KERN_ERR "[MSS: IRQ Conflict?]\n");
 
 /*
  * Handle the capture DMA channel
  */
 
 	if (dma2 != -1 && dma2 != dma)
-	  {
-		  if (!((dma == 0 && dma2 == 1) ||
+	{
+		if (!((dma == 0 && dma2 == 1) ||
 			(dma == 1 && dma2 == 0) ||
 			(dma == 3 && dma2 == 0)))
-		    {		/* Unsupported combination. Try to swap channels */
-			    int             tmp = dma;
+		{	/* Unsupported combination. Try to swap channels */
+			int tmp = dma;
 
-			    dma = dma2;
-			    dma2 = tmp;
-		    }
-		  if ((dma == 0 && dma2 == 1) ||
-		      (dma == 1 && dma2 == 0) ||
-		      (dma == 3 && dma2 == 0))
-		    {
-			    dma2_bit = 0x04;	/* Enable capture DMA */
-		  } else
-		    {
-			    printk("MSS: Invalid capture DMA\n");
-			    dma2 = dma;
-		    }
-	} else
-	  {
-		  dma2 = dma;
-	  }
+			dma = dma2;
+			dma2 = tmp;
+		}
+		if ((dma == 0 && dma2 == 1) ||
+			(dma == 1 && dma2 == 0) ||
+			(dma == 3 && dma2 == 0))
+		{
+			dma2_bit = 0x04;	/* Enable capture DMA */
+		}
+		else
+		{
+			printk(KERN_WARNING "MSS: Invalid capture DMA\n");
+			dma2 = dma;
+		}
+	}
+	else
+	{
+		dma2 = dma;
+	}
 
 	hw_config->dma = dma;
 	hw_config->dma2 = dma2;
@@ -2375,8 +2405,7 @@
 	request_region(hw_config->io_base, 4, "WSS config");
 }
 
-void
-unload_ms_sound(struct address_info *hw_config)
+void unload_ms_sound(struct address_info *hw_config)
 {
 	int mixer = audio_devs[hw_config->slots[0]]->mixer_dev;
 	ad1848_unload(hw_config->io_base + 4,
@@ -2387,18 +2416,17 @@
 		sound_unload_mixerdev(mixer);
 	sound_unload_audiodev(hw_config->slots[0]);
 	release_region(hw_config->io_base, 4);
-
 }
 
 #if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE)
+
 /*
  * Timer stuff (for /dev/music).
  */
 
 static unsigned int current_interval = 0;
 
-static unsigned int
-ad1848_tmr_start(int dev, unsigned int usecs)
+static unsigned int ad1848_tmr_start(int dev, unsigned int usecs)
 {
 	unsigned long   flags;
 	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
@@ -2408,16 +2436,16 @@
 	save_flags(flags);
 	cli();
 
-/*
- * Length of the timer interval (in nanoseconds) depends on the
- * selected crystal oscillator. Check this from bit 0x01 of I8.
- *
- * AD1845 has just one oscillator which has cycle time of 10.050 us
- * (when a 24.576 MHz xtal oscillator is used).
- *
- * Convert requested interval to nanoseconds before computing
- * the timer divider.
- */
+	/*
+	 * Length of the timer interval (in nanoseconds) depends on the
+	 * selected crystal oscillator. Check this from bit 0x01 of I8.
+	 *
+	 * AD1845 has just one oscillator which has cycle time of 10.050 us
+	 * (when a 24.576 MHz xtal oscillator is used).
+	 *
+	 * Convert requested interval to nanoseconds before computing
+	 * the timer divider.
+	 */
 
 	if (devc->model == MD_1845)
 		xtal_nsecs = 10050;
@@ -2443,20 +2471,18 @@
 	return current_interval = (divider * xtal_nsecs + 500) / 1000;
 }
 
-static void
-ad1848_tmr_reprogram(int dev)
+static void ad1848_tmr_reprogram(int dev)
 {
-/*
- *    Audio driver has changed sampling rate so that a different xtal
- *      oscillator was selected. We have to reprogram the timer rate.
- */
+	/*
+	 *    Audio driver has changed sampling rate so that a different xtal
+	 *      oscillator was selected. We have to reprogram the timer rate.
+	 */
 
 	ad1848_tmr_start(dev, current_interval);
 	sound_timer_syncinterval(current_interval);
 }
 
-static void
-ad1848_tmr_disable(int dev)
+static void ad1848_tmr_disable(int dev)
 {
 	unsigned long   flags;
 	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
@@ -2468,8 +2494,7 @@
 	restore_flags(flags);
 }
 
-static void
-ad1848_tmr_restart(int dev)
+static void ad1848_tmr_restart(int dev)
 {
 	unsigned long   flags;
 	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
@@ -2493,10 +2518,8 @@
 	ad1848_tmr_restart
 };
 
-static int
-ad1848_tmr_install(int dev)
+static int ad1848_tmr_install(int dev)
 {
-
 	if (timer_installed != -1)
 		return 0;	/* Don't install another timer */
 
@@ -2524,6 +2547,7 @@
 MODULE_PARM(dma, "i");
 MODULE_PARM(dma2, "i");
 MODULE_PARM(type, "i");
+MODULE_PARM(deskpro_xl, "i");
 
 int io = -1;
 int irq = -1;
@@ -2538,7 +2562,7 @@
 
 int init_module(void)
 {
-	printk("ad1848/cs4248 codec driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+	printk(KERN_INFO "ad1848/cs4248 codec driver Copyright (C) by Hannu Savolainen 1993-1996\n");
 	if(io!=-1)
 	{
 		if(irq == -1 || dma == -1)
@@ -2560,8 +2584,7 @@
 	return 0;
 }
 
-void
-cleanup_module(void)
+void cleanup_module(void)
 {
 	SOUND_LOCK_END;
 	if(loaded)
@@ -2570,8 +2593,7 @@
 
 #else
 
-void
-export_ad1848_syms(void)
+void export_ad1848_syms(void)
 {
 }
 
diff -u --recursive --new-file v2.1.78/linux/drivers/sound/pas2_card.c linux/drivers/sound/pas2_card.c
--- v2.1.78/linux/drivers/sound/pas2_card.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/pas2_card.c	Tue Jan  6 19:29:20 1998
@@ -11,14 +11,22 @@
 
 #if defined(CONFIG_PAS) || defined(MODULE)
 
-static unsigned char dma_bits[] =
-{4, 1, 2, 3, 0, 5, 6, 7};
-static unsigned char irq_bits[] =
-{0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11};
-static unsigned char sb_irq_bits[] =
-{0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0};
-static unsigned char sb_dma_bits[] =
-{0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0};
+static unsigned char dma_bits[] = {
+	4, 1, 2, 3, 0, 5, 6, 7
+};
+
+static unsigned char irq_bits[] = {
+	0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11
+};
+
+static unsigned char sb_irq_bits[] = {
+	0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 
+	0x00, 0x08, 0x28, 0x30, 0x38, 0, 0
+};
+
+static unsigned char sb_dma_bits[] = {
+	0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0
+};
 
 /*
  * The Address Translation code is used to convert I/O register addresses to
@@ -32,32 +40,35 @@
 
 
 char            pas_model = 0;
-static char    *pas_model_names[] =
-{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16", "Pro AudioSpectrum 16D"};
+static char    *pas_model_names[] = {
+	"", 
+	"Pro AudioSpectrum+", 
+	"CDPC", 
+	"Pro AudioSpectrum 16", 
+	"Pro AudioSpectrum 16D"
+};
 
 /*
  * pas_read() and pas_write() are equivalents of inb and outb 
  * These routines perform the I/O address translation required
  * to support other than the default base address
  */
+
 extern void     mix_write(unsigned char data, int ioaddr);
 
-unsigned char
-pas_read(int ioaddr)
+unsigned charpas_read(int ioaddr)
 {
 	return inb(ioaddr ^ translate_code);
 }
 
-void
-pas_write(unsigned char data, int ioaddr)
+void pas_write(unsigned char data, int ioaddr)
 {
 	outb((data), ioaddr ^ translate_code);
 }
 
 /******************* Begin of the Interrupt Handler ********************/
 
-static void
-pasintr(int irq, void *dev_id, struct pt_regs *dummy)
+static void pasintr(int irq, void *dev_id, struct pt_regs *dummy)
 {
 	int             status;
 
@@ -65,23 +76,22 @@
 	pas_write(status, 0x0B89);	/* Clear interrupt */
 
 	if (status & 0x08)
-	  {
+	{
 #ifdef CONFIG_AUDIO
 		  pas_pcm_interrupt(status, 1);
 #endif
 		  status &= ~0x08;
-	  }
+	}
 	if (status & 0x10)
-	  {
+	{
 #if defined(CONFIG_MIDI)
 		  pas_midi_interrupt();
 #endif
 		  status &= ~0x10;
-	  }
+	}
 }
 
-int
-pas_set_intr(int mask)
+int pas_set_intr(int mask)
 {
 	if (!mask)
 		return 0;
@@ -92,8 +102,7 @@
 	return 0;
 }
 
-int
-pas_remove_intr(int mask)
+int pas_remove_intr(int mask)
 {
 	if (!mask)
 		return 0;
@@ -110,8 +119,7 @@
 
 extern struct address_info sbhw_config;
 
-static int
-config_pas_hw(struct address_info *hw_config)
+static int config_pas_hw(struct address_info *hw_config)
 {
 	char            ok = 1;
 	unsigned        int_ptrs;	/* scsi/sound interrupt pointers */
@@ -129,8 +137,8 @@
 	pas_write(0x80 | 0x40 | 0x20 | 1, 0x0B8A);
 	pas_write(0x80 | 0x20 | 0x10 | 0x08 | 0x01, 0xF8A);
 	pas_write(0x01 | 0x02 | 0x04 | 0x10	/*
-						   * |
-						   * 0x80
+						 * |
+						 * 0x80
 						 */ , 0xB88);
 
 	pas_write(0x80
@@ -140,49 +148,53 @@
 		  ,0xF388);
 
 	if (pas_irq < 0 || pas_irq > 15)
-	  {
-		  printk("PAS16: Invalid IRQ %d", pas_irq);
-		  ok = 0;
-	} else
-	  {
-		  int_ptrs = pas_read(0xF38A);
-		  int_ptrs |= irq_bits[pas_irq] & 0xf;
-		  pas_write(int_ptrs, 0xF38A);
-		  if (!irq_bits[pas_irq])
-		    {
-			    printk("PAS16: Invalid IRQ %d", pas_irq);
-			    ok = 0;
-		  } else
-		    {
-			    if (snd_set_irq_handler(pas_irq, pasintr, "PAS16", hw_config->osp) < 0)
-				    ok = 0;
-		    }
-	  }
+	{
+		printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
+		ok = 0;
+	}
+	else
+	{
+		int_ptrs = pas_read(0xF38A);
+		int_ptrs |= irq_bits[pas_irq] & 0xf;
+		pas_write(int_ptrs, 0xF38A);
+		if (!irq_bits[pas_irq])
+		{
+			printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
+			ok = 0;
+		}
+		else
+		{
+			if (snd_set_irq_handler(pas_irq, pasintr, "PAS16", hw_config->osp) < 0)
+				ok = 0;
+		}
+	}
 
 	if (hw_config->dma < 0 || hw_config->dma > 7)
-	  {
-		  printk("PAS16: Invalid DMA selection %d", hw_config->dma);
-		  ok = 0;
-	} else
-	  {
-		  pas_write(dma_bits[hw_config->dma], 0xF389);
-		  if (!dma_bits[hw_config->dma])
-		    {
-			    printk("PAS16: Invalid DMA selection %d", hw_config->dma);
-			    ok = 0;
-		  } else
-		    {
-			    if (sound_alloc_dma(hw_config->dma, "PAS16"))
-			      {
-				      printk("pas2_card.c: Can't allocate DMA channel\n");
-				      ok = 0;
-			      }
-		    }
-	  }
+	{
+		printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
+		ok = 0;
+	}
+	else
+	{
+		pas_write(dma_bits[hw_config->dma], 0xF389);
+		if (!dma_bits[hw_config->dma])
+		{
+			printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
+			ok = 0;
+		}
+		else
+		{
+			if (sound_alloc_dma(hw_config->dma, "PAS16"))
+			{
+				printk(KERN_ERR "pas2_card.c: Can't allocate DMA channel\n");
+				ok = 0;
+			}
+		}
+	}
 
 	/*
-	   * This fixes the timing problems of the PAS due to the Symphony chipset
-	   * as per Media Vision.  Only define this if your PAS doesn't work correctly.
+	 * This fixes the timing problems of the PAS due to the Symphony chipset
+	 * as per Media Vision.  Only define this if your PAS doesn't work correctly.
 	 */
 #ifdef SYMPHONY_PAS
 	outb((0x05), 0xa8);
@@ -215,33 +227,36 @@
 		sb_config = &sbhw_config;
 		if (sb_config->io_base)
 #endif
-		  {
-			  unsigned char   irq_dma;
-
-			  /*
-			   * Turn on Sound Blaster compatibility
-			   * bit 1 = SB emulation
-			   * bit 0 = MPU401 emulation (CDPC only :-( )
-			   */
-			  pas_write(0x02, 0xF788);
-
-			  /*
-			   * "Emulation address"
-			   */
-			  pas_write((sb_config->io_base >> 4) & 0x0f, 0xF789);
-			  pas_sb_base = sb_config->io_base;
-
-			  if (!sb_dma_bits[sb_config->dma])
-				  printk("\n\nPAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma);
-
-			  if (!sb_irq_bits[sb_config->irq])
-				  printk("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq);
-
-			  irq_dma = sb_dma_bits[sb_config->dma] |
-			      sb_irq_bits[sb_config->irq];
+		{
+			unsigned char   irq_dma;
 
-			  pas_write(irq_dma, 0xFB8A);
-		} else
+			/*
+			 * Turn on Sound Blaster compatibility
+			 * bit 1 = SB emulation
+			 * bit 0 = MPU401 emulation (CDPC only :-( )
+			 */
+			
+			pas_write(0x02, 0xF788);
+
+			/*
+			 * "Emulation address"
+			 */
+			
+			pas_write((sb_config->io_base >> 4) & 0x0f, 0xF789);
+			pas_sb_base = sb_config->io_base;
+
+			if (!sb_dma_bits[sb_config->dma])
+				printk(KERN_ERR "PAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma);
+
+			if (!sb_irq_bits[sb_config->irq])
+				printk(KERN_ERR "PAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq);
+
+			irq_dma = sb_dma_bits[sb_config->dma] |
+				sb_irq_bits[sb_config->irq];
+
+			pas_write(irq_dma, 0xFB8A);
+		}
+		else
 			pas_write(0x00, 0xF788);
 	}
 #else
@@ -249,13 +264,12 @@
 #endif
 
 	if (!ok)
-		printk("PAS16: Driver not enabled\n");
+		printk(KERN_WARNING "PAS16: Driver not enabled\n");
 
 	return ok;
 }
 
-static int
-detect_pas_hw(struct address_info *hw_config)
+static int detect_pas_hw(struct address_info *hw_config)
 {
 	unsigned char   board_id, foo;
 
@@ -296,40 +310,39 @@
 	return pas_model;
 }
 
-void
-attach_pas_card(struct address_info *hw_config)
+void attach_pas_card(struct address_info *hw_config)
 {
 	pas_irq = hw_config->irq;
 
 	if (detect_pas_hw(hw_config))
-	  {
+	{
 
-		  if ((pas_model = pas_read(0xFF88)))
-		    {
-			    char            temp[100];
+		if ((pas_model = pas_read(0xFF88)))
+		{
+			char temp[100];
 
-			    sprintf(temp,
+			sprintf(temp,
 			    "%s rev %d", pas_model_names[(int) pas_model],
 				    pas_read(0x2789));
-			    conf_printf(temp, hw_config);
-		    }
-		  if (config_pas_hw(hw_config))
-		    {
+			conf_printf(temp, hw_config);
+		}
+		if (config_pas_hw(hw_config))
+		{
 #ifdef CONFIG_AUDIO
-			    pas_pcm_init(hw_config);
+			pas_pcm_init(hw_config);
 #endif
 
 #if !defined(DISABLE_SB_EMULATION) && (defined(CONFIG_SB) || defined(CONFIG_SB_MODULE))
 
-			    sb_dsp_disable_midi(pas_sb_base);	/* No MIDI capability */
+			sb_dsp_disable_midi(pas_sb_base);	/* No MIDI capability */
 #endif
 
 #if defined(CONFIG_MIDI)
-			    pas_midi_init();
+			pas_midi_init();
 #endif
-			    pas_init_mixer();
-		    }
-	  }
+			pas_init_mixer();
+		}
+	}
 }
 
 int
@@ -357,18 +370,28 @@
 int             sb_dma = -1;
 int             sb_dma16 = -1;
 
+MODULE_PARM(io,"i");
+MODULE_PARM(irq,"i");
+MODULE_PARM(dma,"i");
+MODULE_PARM(dma16,"i");
+
+MODULE_PARM(sb_io,"i");
+MODULE_PARM(sb_irq,"i");
+MODULE_PARM(sb_dma,"i");
+MODULE_PARM(sb_dma16,"i");
+
 struct address_info config;
 struct address_info sbhw_config;
 
 int init_module(void)
 {
-	printk("MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+	printk(KERN_INFO "MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
 
 	if (io == -1 || dma == -1 || irq == -1)
-	  {
-		  printk("I/O, IRQ, DMA and type are mandatory\n");
+	{
+		  printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n");
 		  return -EINVAL;
-	  }
+	}
 	config.io_base = io;
 	config.irq = irq;
 	config.dma = dma;
@@ -386,8 +409,7 @@
 	return 0;
 }
 
-void 
-cleanup_module(void)
+void cleanup_module(void)
 {
 	unload_pas(&config);
 	SOUND_LOCK_END;
diff -u --recursive --new-file v2.1.78/linux/fs/affs/Changes linux/fs/affs/Changes
--- v2.1.78/linux/fs/affs/Changes	Thu Dec  4 14:53:56 1997
+++ linux/fs/affs/Changes	Tue Jan  6 13:33:29 1998
@@ -21,10 +21,31 @@
   might leave a trashed file system with the
   bitmap flag set valid.
 
-- The blocks from deleted directories are 
-  sometimes reclaimed only at umount time.
+- When a file is truncated to a size that is not
+  a multiple of the blocksize, the rest of the
+  last allocated block is not cleared. Well,
+  this fs never claimed to be Posix conformant.
 
 Please direct bug reports to: hjw@zvw.de
+
+Version 3.7
+-----------
+
+- Added dentry callbacks to allow the dcache to
+  operate case insensitive and length ignorant
+  like the affs itself.
+
+- getblock() didn't update the lastblock field in the
+  inode if the fs was not an OFS. This bug only shows
+  up if a file was enlarged via truncate() and there
+  was not enough space.
+
+- Remove some more superfluous code left over from
+  the old link days ...
+
+- Fixed some oversights which were in patch 2.1.78.
+
+- Fixed a few typos.
 
 Version 3.6
 -----------
diff -u --recursive --new-file v2.1.78/linux/fs/affs/file.c linux/fs/affs/file.c
--- v2.1.78/linux/fs/affs/file.c	Thu Dec  4 14:53:56 1997
+++ linux/fs/affs/file.c	Tue Jan  6 13:33:29 1998
@@ -12,6 +12,7 @@
  *  affs regular file handling primitives
  */
 
+#define DEBUG 0
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <linux/sched.h>
@@ -417,6 +418,7 @@
 			nkey = affs_new_data(inode);
 			if (!nkey)
 				break;
+			inode->u.affs_i.i_lastblock++;
 			lock_super(inode->i_sb);
 			if (AFFS_BLOCK(bh->b_data,inode,j)) {
 				unlock_super(inode->i_sb);
@@ -436,7 +438,6 @@
 					AFFS_BLOCK(bh->b_data,inode,j) = 0;
 					break;
 				}
-				inode->u.affs_i.i_lastblock++;
 				DATA_FRONT(ebh)->primary_type    = cpu_to_be32(T_DATA);
 				DATA_FRONT(ebh)->header_key      = cpu_to_be32(inode->i_ino);
 				DATA_FRONT(ebh)->sequence_number = cpu_to_be32(inode->u.affs_i.i_lastblock + 1);
@@ -750,7 +751,7 @@
 	int	 rem;
 	int	 ext;
 
-	pr_debug("AFFS: file_truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
+	pr_debug("AFFS: truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
 
 	blocksize = AFFS_I2BSIZE(inode) - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0);
 	first = (inode->i_size + blocksize - 1) / blocksize;
@@ -777,7 +778,7 @@
 			unlock_super(inode->i_sb);
 		}
 		if (!bh) {
-			affs_error(inode->i_sb,"truncate","Cannot extend file");
+			affs_warning(inode->i_sb,"truncate","Cannot extend file");
 			inode->i_size = blocksize * (inode->u.affs_i.i_lastblock + 1);
 		} else if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) {
 			rem = inode->i_size % blocksize;
@@ -798,12 +799,6 @@
 		}
 		ptype = be32_to_cpu(((struct file_front *)bh->b_data)->primary_type);
 		stype = be32_to_cpu(FILE_END(bh->b_data,inode)->secondary_type);
-		if (ekey == inode->i_ino && ptype == T_SHORT && stype == ST_LINKFILE &&
-		    LINK_END(bh->b_data,inode)->original == 0) {
-			pr_debug("AFFS: truncate(): dumping link\n");
-			affs_brelse(bh);
-			break;
-		}
 		if (stype != ST_FILE || (ptype != T_SHORT && ptype != T_LIST)) {
 			affs_error(inode->i_sb,"truncate","Bad block (ptype=%d, stype=%d)",
 			           ptype,stype);
diff -u --recursive --new-file v2.1.78/linux/fs/affs/namei.c linux/fs/affs/namei.c
--- v2.1.78/linux/fs/affs/namei.c	Thu Dec  4 14:53:56 1997
+++ linux/fs/affs/namei.c	Tue Jan  6 13:33:29 1998
@@ -21,7 +21,7 @@
 
 #include <linux/errno.h>
 
-/* Simple toupper() for DOS\1 */
+/* Simple toupper()/tolower() for DOS\1 */
 
 static inline unsigned int
 affs_toupper(unsigned int ch)
@@ -29,7 +29,13 @@
 	return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
 }
 
-/* International toupper() for DOS\3 */
+static inline unsigned int
+affs_tolower(unsigned int ch)
+{
+	return ch >= 'A' && ch <= 'Z' ? ch + ('a' - 'A') : ch;
+}
+
+/* International toupper()/tolower() for DOS\3 ("international") */
 
 static inline unsigned int
 affs_intl_toupper(unsigned int ch)
@@ -39,6 +45,112 @@
 		ch - ('a' - 'A') : ch;
 }
 
+static inline unsigned int
+affs_intl_tolower(unsigned int ch)
+{
+	return (ch >= 'A' && ch <= 'Z') || (ch >= 0xC0
+		&& ch <= 0xDE && ch != 0xD7) ?
+		ch + ('a' - 'A') : ch;
+}
+
+/* We need 2 sets of dentry operations, since we cannot
+ * determine the fs flavour in the callback routines.
+ */
+
+static int	 affs_hash_dentry(struct dentry *, struct qstr *);
+static int       affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+static int	 affs_hash_dentry_intl(struct dentry *, struct qstr *);
+static int       affs_compare_dentry_intl(struct dentry *, struct qstr *, struct qstr *);
+
+struct dentry_operations affs_dentry_operations = {
+	NULL,			/* d_validate	*/
+	affs_hash_dentry,	/* d_hash	*/
+	affs_compare_dentry,	/* d_compare	*/
+	NULL			/* d_delete	*/
+};
+
+struct dentry_operations affs_dentry_operations_intl = {
+	NULL,				/* d_validate	*/
+	affs_hash_dentry_intl,		/* d_hash	*/
+	affs_compare_dentry_intl,	/* d_compare	*/
+	NULL				/* d_delete	*/
+};
+
+static int
+affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+	unsigned long	 hash;
+	int		 i;
+
+	if ((i = affs_check_name(qstr->name,qstr->len)))
+		return i;
+	hash = init_name_hash();
+	for (i = 0; i < qstr->len && i < 30; i++)
+		hash = partial_name_hash(affs_tolower(qstr->name[i]),hash);
+	qstr->hash = end_name_hash(hash);
+
+	return 0;
+}
+
+static int
+affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+	int	 i;
+
+	/* 'a' is the qstr of an already existing dentry, so the name
+	 * must be valid. 'b' must be validated first.
+	 */
+	
+	if (affs_check_name(b->name,b->len))
+		return 1;
+
+	/* If the names are longer than the allowed 30 chars,
+	 * the excess is ignored, so their length may differ.
+	 */
+
+	if ((a->len < 30 || b->len < 30) && a->len != b->len)
+		return 1;
+
+	for (i = 0; i < a->len && i < 30; i++)
+		if (affs_tolower(a->name[i]) != affs_tolower(b->name[i]))
+			return 1;
+	
+	return 0;
+}
+
+static int
+affs_hash_dentry_intl(struct dentry *dentry, struct qstr *qstr)
+{
+	unsigned long	 hash;
+	int		 i;
+
+	if ((i = affs_check_name(qstr->name,qstr->len)))
+		return i;
+	hash = init_name_hash();
+	for (i = 0; i < qstr->len && i < 30; i++)
+		hash = partial_name_hash(affs_intl_tolower(qstr->name[i]),hash);
+	qstr->hash = end_name_hash(hash);
+
+	return 0;
+}
+
+static int
+affs_compare_dentry_intl(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+	int	 i;
+
+	if (affs_check_name(b->name,b->len))
+		return 1;
+	if ((a->len < 30 || b->len < 30) && a->len != b->len)
+		return 1;
+
+	for (i = 0; i < a->len && i < 30; i++)
+		if (affs_intl_tolower(a->name[i]) != affs_intl_tolower(b->name[i]))
+			return 1;
+	
+	return 0;
+}
+
 /*
  * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
  */
@@ -162,6 +274,8 @@
 		if (!inode)
 			return -EACCES;
 	}
+	dentry->d_op = AFFS_I2FSTYPE(dir) ? &affs_dentry_operations_intl
+					  : &affs_dentry_operations;
 	d_add(dentry,inode);
 	return 0;
 }
@@ -422,8 +536,9 @@
 }
 
 int
-affs_link(struct inode *oldinode, struct inode *dir, struct dentry *dentry)
+affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
 {
+	struct inode		*oldinode = old_dentry->d_inode;
 	struct inode		*inode;
 	struct buffer_head	*bh;
 	unsigned long		 i;
diff -u --recursive --new-file v2.1.78/linux/fs/affs/super.c linux/fs/affs/super.c
--- v2.1.78/linux/fs/affs/super.c	Thu Dec  4 14:53:56 1997
+++ linux/fs/affs/super.c	Tue Jan  6 13:33:29 1998
@@ -494,7 +494,7 @@
 			if (bb) {
 				if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) &&
 				    !(s->s_flags & MS_RDONLY)) {
-					printk(KERN_WARNING "AFFS: Bitmap (%d,key=%lu) invalid - "
+					printk(KERN_WARNING "AFFS: Bitmap (%d,key=%u) invalid - "
 					       "mounting %s read only.\n",mapidx,be32_to_cpu(bm[i]),
 						kdevname(dev));
 					s->s_flags |= MS_RDONLY;
@@ -572,6 +572,8 @@
 		MOD_DEC_USE_COUNT;
 		return NULL;
 	}
+	s->s_root->d_op = (s->u.affs_sb.s_flags & SF_INTL) ? &affs_dentry_operations_intl
+							   : &affs_dentry_operations;
 
 	/* Record date of last change if the bitmap was truncated and
 	 * create data zones if the volume is writable.
diff -u --recursive --new-file v2.1.78/linux/fs/affs/symlink.c linux/fs/affs/symlink.c
--- v2.1.78/linux/fs/affs/symlink.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/affs/symlink.c	Mon Jan 12 14:46:24 1998
@@ -44,7 +44,7 @@
 };
 
 static int
-affs_readlink(struct inode *dentry, char *buffer, int buflen)
+affs_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
 	struct inode *inode = dentry->d_inode;
 	struct buffer_head	*bh;
diff -u --recursive --new-file v2.1.78/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v2.1.78/linux/fs/binfmt_elf.c	Sun Dec 21 22:36:15 1997
+++ linux/fs/binfmt_elf.c	Mon Jan 12 14:52:46 1998
@@ -34,7 +34,7 @@
 
 #include <linux/config.h>
 
-#define DLINFO_ITEMS 12
+#define DLINFO_ITEMS 13
 
 #include <linux/elf.h>
 
@@ -112,21 +112,40 @@
 	elf_caddr_t *argv;
 	elf_caddr_t *envp;
 	elf_addr_t *sp, *csp;
+	char *k_platform, *u_platform;
+	long hwcap;
+	size_t platform_len = 0;
+
+	/*
+	 * Get hold of platform and hardware capabilities masks for
+	 * the machine we are running on.  In some cases (Sparc), 
+	 * this info is impossible to get, in others (i386) it is
+	 * merely difficult.
+	 */
+
+	hwcap = ELF_HWCAP;
+	k_platform = ELF_PLATFORM;
+
+	if (k_platform) {
+		platform_len = strlen(k_platform) + 1;
+		u_platform = p - platform_len;
+		__copy_to_user(u_platform, k_platform, platform_len);
+	} else
+		u_platform = p;
 
 	/*
 	 * Force 16 byte _final_ alignment here for generality.
 	 * Leave an extra 16 bytes free so that on the PowerPC we
 	 * can move the aux table up to start on a 16-byte boundary.
 	 */
-	sp = (elf_addr_t *) ((~15UL & (unsigned long) p) - 16UL);
+	sp = (elf_addr_t *)((~15UL & (unsigned long)(u_platform)) - 16UL);
 	csp = sp;
-	csp -= exec ? DLINFO_ITEMS*2 : 2;
+	csp -= ((exec ? DLINFO_ITEMS*2 : 4) + (k_platform ? 2 : 0));
 	csp -= envc+1;
 	csp -= argc+1;
 	csp -= (!ibcs ? 3 : 1);	/* argc itself */
-	if ((unsigned long)csp & 15UL) {
+	if ((unsigned long)csp & 15UL)
 		sp -= ((unsigned long)csp & 15UL) / sizeof(*sp);
-	}
 
 	/*
 	 * Put the ELF interpreter info on the stack
@@ -137,21 +156,27 @@
 
 	sp -= 2;
 	NEW_AUX_ENT(0, AT_NULL, 0);
+	if (k_platform) {
+		sp -= 2;
+		NEW_AUX_ENT(0, AT_PLATFORM, (elf_addr_t)(unsigned long) u_platform);
+	}
+	sp -= 2;
+	NEW_AUX_ENT(0, AT_HWCAP, hwcap);
 
 	if (exec) {
 		sp -= 11*2;
 
-		NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff);
-		NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr));
-		NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum);
-		NEW_AUX_ENT (3, AT_PAGESZ, ELF_EXEC_PAGESIZE);
-		NEW_AUX_ENT (4, AT_BASE, interp_load_addr);
-		NEW_AUX_ENT (5, AT_FLAGS, 0);
-		NEW_AUX_ENT (6, AT_ENTRY, (elf_addr_t) exec->e_entry);
-		NEW_AUX_ENT (7, AT_UID, (elf_addr_t) current->uid);
-		NEW_AUX_ENT (8, AT_EUID, (elf_addr_t) current->euid);
-		NEW_AUX_ENT (9, AT_GID, (elf_addr_t) current->gid);
-		NEW_AUX_ENT (10, AT_EGID, (elf_addr_t) current->egid);
+		NEW_AUX_ENT(0, AT_PHDR, load_addr + exec->e_phoff);
+		NEW_AUX_ENT(1, AT_PHENT, sizeof (struct elf_phdr));
+		NEW_AUX_ENT(2, AT_PHNUM, exec->e_phnum);
+		NEW_AUX_ENT(3, AT_PAGESZ, ELF_EXEC_PAGESIZE);
+		NEW_AUX_ENT(4, AT_BASE, interp_load_addr);
+		NEW_AUX_ENT(5, AT_FLAGS, 0);
+		NEW_AUX_ENT(6, AT_ENTRY, (elf_addr_t) exec->e_entry);
+		NEW_AUX_ENT(7, AT_UID, (elf_addr_t) current->uid);
+		NEW_AUX_ENT(8, AT_EUID, (elf_addr_t) current->euid);
+		NEW_AUX_ENT(9, AT_GID, (elf_addr_t) current->gid);
+		NEW_AUX_ENT(10, AT_EGID, (elf_addr_t) current->egid);
 	}
 #undef NEW_AUX_ENT
 
@@ -592,7 +617,6 @@
 	/* OK, This is the point of no return */
 	current->mm->end_data = 0;
 	current->mm->end_code = 0;
-	current->mm->start_mmap = ELF_START_MMAP;
 	current->mm->mmap = NULL;
 	elf_entry = (unsigned long) elf_ex.e_entry;
 
@@ -699,7 +723,7 @@
 	kfree(elf_phdata);
 
 	if (interpreter_type != INTERPRETER_AOUT) sys_close(elf_exec_fileno);
-	current->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX);
+	SET_PERSONALITY(ibcs2_interpreter);
 
 	if (current->exec_domain && current->exec_domain->module)
 		__MOD_DEC_USE_COUNT(current->exec_domain->module);
diff -u --recursive --new-file v2.1.78/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.78/linux/fs/buffer.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/buffer.c	Mon Jan 12 14:33:20 1998
@@ -1319,8 +1319,13 @@
 {
 	if (test_and_clear_bit(PG_decr_after, &page->flags))
 		atomic_dec(&nr_async_pages);
-	if (test_and_clear_bit(PG_swap_unlock_after, &page->flags))
-		swap_after_unlock_page(page->pg_swap_entry);
+	if (test_and_clear_bit(PG_swap_unlock_after, &page->flags)) {
+		/*
+		 * We're doing a swap, so check that this page is
+		 * swap-cached and do the necessary cleanup. 
+		 */
+		swap_after_unlock_page(page->offset);
+	}
 	if (test_and_clear_bit(PG_free_after, &page->flags))
 		__free_page(page);
 }
diff -u --recursive --new-file v2.1.78/linux/fs/coda/cache.c linux/fs/coda/cache.c
--- v2.1.78/linux/fs/coda/cache.c	Tue Dec 23 16:31:00 1997
+++ linux/fs/coda/cache.c	Tue Jan  6 10:00:21 1998
@@ -236,7 +236,7 @@
 		if ( cnp ) 
 			CHECK_CNODE(cnp);
 	} else {
-		printk("No inode for dentry_delete!\n");
+		CDEBUG(D_CACHE, "No inode for dentry_delete!\n");
 		return;
 	}
 
@@ -254,6 +254,14 @@
 	return;
 }
 
+static void coda_zap_cnode(struct cnode *cnp, int flags)
+{
+	cnp->c_flags |= flags;
+	coda_cache_clear_cnp(cnp);
+}
+
+
+
 /* the dache will notice the flags and drop entries (possibly with
    children) the moment they are no longer in use  */
 void coda_zapfid(struct ViceFid *fid, struct super_block *sb, int flag)
@@ -273,9 +281,22 @@
 		return;
 	}
 
+	if ( coda_fid_is_volroot(fid) ) {
+		struct list_head *lh, *le;
+		struct coda_sb_info *sbi = coda_sbp(sb);
+		le = lh = &sbi->sbi_volroothead;
+		while ( (le = le->next) != lh ) {
+			cnp = list_entry(le, struct cnode, c_volrootlist);
+			if ( cnp->c_fid.Volume == fid->Volume) 
+				coda_zap_cnode(cnp, flag);
+		}
+		return;
+	}
+
+
 	inode = coda_fid_to_inode(fid, sb);
 	if ( !inode ) {
-		printk("coda_zapfid: no inode!\n");
+		CDEBUG(D_CACHE, "coda_zapfid: no inode!\n");
 		return;
 	}
 	cnp = ITOC(inode);
@@ -284,8 +305,7 @@
 		printk("coda_zapfid: no cnode!\n");
 		return;
 	}
-	cnp->c_flags |= flag;
-	coda_cache_clear_cnp(cnp);
+	coda_zap_cnode(cnp, flag);
 }
 		
 
diff -u --recursive --new-file v2.1.78/linux/fs/coda/cnode.c linux/fs/coda/cnode.c
--- v2.1.78/linux/fs/coda/cnode.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/coda/cnode.c	Tue Jan  6 10:00:21 1998
@@ -7,8 +7,8 @@
 
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
-#include <linux/coda_psdev.h>
 #include <linux/coda_cnode.h>
+#include <linux/coda_psdev.h>
 
 extern int coda_debug;
 extern int coda_print_entry;
@@ -29,6 +29,7 @@
 
         memset(result, 0, (int) sizeof(struct cnode));
         INIT_LIST_HEAD(&(result->c_cnhead));
+	INIT_LIST_HEAD(&(result->c_volrootlist));
 	return result;
 }
 
@@ -70,6 +71,7 @@
 int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb)
 {
         struct cnode *cnp;
+	struct coda_sb_info *sbi= coda_sbp(sb);
         struct coda_vattr attr;
         int error;
 	ino_t ino;
@@ -119,6 +121,9 @@
 	CHECK_CNODE(cnp);
 
 	/* fill in the inode attributes */
+	if ( coda_fid_is_volroot(fid) ) 
+		list_add(&cnp->c_volrootlist, &sbi->sbi_volroothead);
+
         coda_fill_inode(*inode, &attr);
 	CDEBUG(D_CNODE, "Done linking: ino %ld,  at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x\n", (*inode)->i_ino, (int) (*inode), (int) cnp, (int)cnp->c_vnode);
 
@@ -148,6 +153,7 @@
 ENTRY;
 
 	CDEBUG(D_INODE, "%s\n", coda_f2s(fid, str));
+
 	nr = coda_f2i(fid);
 	inode = iget(sb, nr);
 
diff -u --recursive --new-file v2.1.78/linux/fs/coda/coda_linux.c linux/fs/coda/coda_linux.c
--- v2.1.78/linux/fs/coda/coda_linux.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/coda/coda_linux.c	Tue Jan  6 10:00:21 1998
@@ -97,6 +97,11 @@
 }
 
 
+int coda_fid_is_volroot(struct ViceFid *fid)
+{
+	return ( (fid->Vnode == 1) && (fid->Unique == 1 ) );
+}
+
 /* utility functions below */
 void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr)
 {
@@ -155,7 +160,6 @@
 
 void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr)
 {
-        umode_t mode;
         unsigned int valid;
 
         /* clean out */        
@@ -170,15 +174,16 @@
         vattr->va_mtime.tv_nsec = (time_t) -1;
 	vattr->va_ctime.tv_nsec = (time_t) -1;
         vattr->va_type = C_VNON;
-	vattr->va_fileid = (long)-1;
-	vattr->va_gen = (long)-1;
-	vattr->va_bytes = (long)-1;
-	vattr->va_nlink = (short)-1;
-	vattr->va_blocksize = (long)-1;
-	vattr->va_rdev = (dev_t)-1;
+	vattr->va_fileid = -1;
+	vattr->va_gen = -1;
+	vattr->va_bytes = -1;
+	vattr->va_nlink = -1;
+	vattr->va_blocksize = -1;
+	vattr->va_rdev = -1;
         vattr->va_flags = 0;
 
         /* determine the type */
+#if 0
         mode = iattr->ia_mode;
                 if ( S_ISDIR(mode) ) {
                 vattr->va_type = C_VDIR; 
@@ -190,6 +195,7 @@
                 /* don't do others */
                 vattr->va_type = C_VNON;
         }
+#endif 
 
         /* set those vattrs that need change */
         valid = iattr->ia_valid;
@@ -219,7 +225,6 @@
 	}
         
 }
-  
 
 void print_vattr(struct coda_vattr *attr)
 {
diff -u --recursive --new-file v2.1.78/linux/fs/coda/dir.c linux/fs/coda/dir.c
--- v2.1.78/linux/fs/coda/dir.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/coda/dir.c	Tue Jan  6 10:00:21 1998
@@ -27,7 +27,7 @@
 /* dir inode-ops */
 static int coda_create(struct inode *dir, struct dentry *new, int mode);
 static int coda_lookup(struct inode *dir, struct dentry *target);
-static int coda_link(struct inode *old_inode, struct inode *dir_inode, 
+static int coda_link(struct dentry *old_dentry, struct inode *dir_inode, 
 		     struct dentry *entry);
 static int coda_unlink(struct inode *dir_inode, struct dentry *entry);
 static int coda_symlink(struct inode *dir_inode, struct dentry *entry,
@@ -243,19 +243,19 @@
 		char str[50];
 		CDEBUG(D_INODE, "create: %s, result %d\n",
 		       coda_f2s(&newfid, str), error); 
+		d_drop(de);
 		return error;
 	}
 
 	error = coda_cnode_make(&result, &newfid, dir->i_sb);
 	if ( error ) {
+		d_drop(de);
 		result = NULL;
 		return error;
 	}
 
 	/* invalidate the directory cnode's attributes */
 	dircnp->c_flags &= ~C_VATTR;
-/* 	cfsnc_zapfid(&(dircnp->c_fid)); */
-
 	d_instantiate(de, result);
         return 0;
 }			     
@@ -276,8 +276,10 @@
 		printk("coda_mkdir: inode is NULL or not a directory\n");
 		return -ENOENT;
 	}
+
         if ( len > CFS_MAXNAMLEN )
                 return -ENAMETOOLONG;
+
 	if (coda_isroot(dir) && coda_iscontrol(name, len))
 		return -EPERM;
 
@@ -294,6 +296,7 @@
         if ( error ) {
 	        CDEBUG(D_INODE, "mkdir error: %s result %d\n", 
 		       coda_f2s(&newfid, fidstr), error); 
+		d_drop(de);
                 return error;
         }
          
@@ -301,21 +304,23 @@
 	       coda_f2s(&newfid, fidstr)); 
 
 	error = coda_cnode_make(&inode, &newfid, dir->i_sb);
-	if ( error ) 
-	       return error;
+	if ( error ) {
+		d_drop(de);
+		return error;
+	}
 	
 	/* invalidate the directory cnode's attributes */
 	dircnp->c_flags &= ~C_VATTR;
-/* 	cfsnc_zapfid(&(dircnp->c_fid)); */
-
 	dir->i_nlink++;
 	d_instantiate(de, inode);
         return 0;
 }
 
-static int coda_link(struct inode *inode, struct inode *dir_inode, 
+/* try to make de an entry in dir_inodde linked to source_de */ 
+static int coda_link(struct dentry *source_de, struct inode *dir_inode, 
 	  struct dentry *de)
 {
+	struct inode *inode = source_de->d_inode;
         const char * name = de->d_name.name;
 	int len = de->d_name.len;
         struct cnode *dir_cnp, *cnp;
@@ -340,19 +345,17 @@
                 return -ENAMETOOLONG;
         }
 
-        /* Check for link to/from control object. */
-
         error = venus_link(dir_inode->i_sb,&(cnp->c_fid), &(dir_cnp->c_fid), 
 			   (const char *)name, len);
 
 	if (  ! error ) { 
-	      dir_cnp->c_flags &= ~C_VATTR;
-/* 	      cfsnc_zapfid(&(dir_cnp->c_fid)); */
-/* 	      cfsnc_zapfid(&(cnp->c_fid)); */
-
-	      inode->i_nlink++;
-	      d_instantiate(de, inode);
+		dir_cnp->c_flags &= ~C_VATTR;
+		inode->i_nlink++;
+		d_instantiate(de, inode);
+	} else {
+		d_drop(de);
 	}
+
         CDEBUG(D_INODE, "link result %d\n",error);
 	EXIT;
         return(error);
@@ -382,11 +385,18 @@
 
         CDEBUG(D_INODE, "symname: %s, length: %d\n", symname, symlen);
 
+	/*
+	 * This entry is now negative. Since we do not create
+	 * an inode for the entry we have to drop it. 
+	 */
+	d_drop(de);
+
 	error = venus_symlink(dir_inode->i_sb, &(dir_cnp->c_fid), name, len, 
 			      symname, symlen);
 
-	if ( !error )
-		d_drop(de);
+	if ( !error ) {
+		dir_cnp->c_flags |= C_VATTR;
+	}
 
         CDEBUG(D_INODE, "in symlink result %d\n",error);
         EXIT;
@@ -412,7 +422,6 @@
 	       coda_f2s(&(dircnp->c_fid), fidstr), dir->i_ino);
 
         /* this file should no longer be in the namecache! */
-/*         cfsnc_zapfile(dircnp, (const char *)name, len); */
 
         error = venus_remove(dir->i_sb, &(dircnp->c_fid), name, len);
 
@@ -423,7 +432,6 @@
 
         /* cache management */
 	dircnp->c_flags &= ~C_VATTR;
-/* 	cfsnc_zapfid(&(dircnp->c_fid)); */
 	
 	de->d_inode->i_nlink--;
 	d_delete(de);
@@ -436,7 +444,7 @@
         struct cnode *dircnp;
 	const char *name = de->d_name.name;
 	int len = de->d_name.len;
-        int error;
+        int error, rehash = 0;
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("coda_rmdir: inode is NULL or not a directory\n");
@@ -456,7 +464,16 @@
 			return error;
 	}
 	/* Drop the dentry to force a new lookup */
-	d_drop(de);
+	if (!list_empty(&de->d_hash)) {
+		d_drop(de);
+		rehash = 1;
+	}
+
+	/* update i_nlink and free the inode before unlinking;
+	   if rmdir fails a new lookup set i_nlink right.*/
+	if (de->d_inode->i_nlink)
+		de->d_inode->i_nlink --;
+	d_delete(de);
 
 	error = venus_rmdir(dir->i_sb, &(dircnp->c_fid), name, len);
 
@@ -465,8 +482,9 @@
                 return error;
         }
 
-	dir->i_nlink--;
-	d_delete(de);
+	if (rehash)
+		d_add(de, NULL);
+	/* XXX how can mtime be set? */
 
         return 0;
 }
diff -u --recursive --new-file v2.1.78/linux/fs/coda/file.c linux/fs/coda/file.c
--- v2.1.78/linux/fs/coda/file.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/coda/file.c	Tue Jan  6 10:00:21 1998
@@ -25,10 +25,10 @@
 #include <linux/coda_cache.h>
 
 /* file operations */
-static int coda_readpage(struct dentry *, struct page *);
-static ssize_t coda_file_read(struct file *, char *, size_t, loff_t *);
-static ssize_t coda_file_write(struct file *, const char *, size_t, loff_t *);
-static int coda_file_mmap(struct file *, struct vm_area_struct *);
+static int coda_readpage(struct dentry * dentry, struct page * page);
+static ssize_t coda_file_read(struct file *f, char *buf, size_t count, loff_t *off);
+static ssize_t coda_file_write(struct file *f, const char *buf, size_t count, loff_t *off);
+static int coda_file_mmap(struct file * file, struct vm_area_struct * vma);
 
 /* exported from this file */
 int coda_fsync(struct file *, struct dentry *dentry);
@@ -74,9 +74,11 @@
 };
 
 /*  File file operations */
-static int coda_readpage(struct dentry *dentry, struct page * page)
+static int coda_readpage(struct dentry *de, struct page * page)
 {
-        struct inode *open_inode, *inode = dentry->d_inode;
+	struct inode *inode = de->d_inode;
+	struct dentry cont_dentry;
+        struct inode *cont_inode;
         struct cnode *cnp;
 
         ENTRY;
@@ -89,12 +91,12 @@
                 return -ENXIO;
         }
 
-        open_inode = cnp->c_ovp;
+        cont_inode = cnp->c_ovp;
+	cont_dentry.d_inode = cont_inode;
 
-        CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", inode->i_ino, open_inode->i_ino, page->offset);
+        CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", inode->i_ino, cont_inode->i_ino, page->offset);
 
-	/* N.B. This needs the dentry for open_inode */
-        generic_readpage(open_inode, page);
+        generic_readpage(&cont_dentry, page);
         EXIT;
         return 0;
 }
diff -u --recursive --new-file v2.1.78/linux/fs/coda/inode.c linux/fs/coda/inode.c
--- v2.1.78/linux/fs/coda/inode.c	Tue Dec  2 16:45:19 1997
+++ linux/fs/coda/inode.c	Wed Dec 31 16:00:00 1969
@@ -1,34 +0,0 @@
-/*
- * Inode operations for Coda filesystem
- * Original version: (C) 1996 P. Braam and M. Callahan
- * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
- * 
- * Carnegie Mellon encourages users to contribute improvements to
- * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/stat.h>
-#include <linux/errno.h>
-#include <linux/locks.h>
-#include <asm/segment.h>
-#include <asm/uaccess.h>
-#include <linux/string.h>
-
-#include <linux/coda.h>
-#include <linux/coda_linux.h>
-#include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
-#include <linux/coda_namecache.h>
-
-/* prototypes */
-static int coda_readpage(struct inode *inode, struct page *page);
-static int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
-
-
-
-
-
diff -u --recursive --new-file v2.1.78/linux/fs/coda/psdev.c linux/fs/coda/psdev.c
--- v2.1.78/linux/fs/coda/psdev.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/coda/psdev.c	Tue Jan  6 10:00:21 1998
@@ -40,8 +40,8 @@
 
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
-#include <linux/coda_psdev.h>
 #include <linux/coda_cnode.h>
+#include <linux/coda_psdev.h>
 #include <linux/coda_cache.h>
 #include <linux/coda_sysctl.h>
 
diff -u --recursive --new-file v2.1.78/linux/fs/coda/super.c linux/fs/coda/super.c
--- v2.1.78/linux/fs/coda/super.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/coda/super.c	Tue Jan  6 10:00:21 1998
@@ -41,7 +41,7 @@
 /* VFS super_block ops */
 static struct super_block *coda_read_super(struct super_block *, void *, int);
 static void coda_read_inode(struct inode *);
-static int  coda_notify_change(struct dentry *, struct iattr *);
+static int  coda_notify_change(struct dentry *dentry, struct iattr *attr);
 static void coda_put_inode(struct inode *);
 static void coda_delete_inode(struct inode *);
 static void coda_put_super(struct super_block *);
@@ -101,6 +101,7 @@
         sbi->sbi_psdev = psdev;
 	sbi->sbi_vcomm = vc;
 	INIT_LIST_HEAD(&(sbi->sbi_cchead));
+	INIT_LIST_HEAD(&(sbi->sbi_volroothead));
 
         lock_super(sb);
         sb->u.generic_sbp = sbi;
@@ -133,7 +134,6 @@
 	printk("coda_read_super: rootinode is %ld dev %d\n", 
 	       root->i_ino, root->i_dev);
 	sbi->sbi_root = root;
-	/* N.B. check this for failure */
 	sb->s_root = d_alloc_root(root, NULL);
 	unlock_super(sb);
 	EXIT;  
@@ -141,6 +141,7 @@
 
 error:
 EXIT;  
+	MOD_DEC_USE_COUNT;
 	if (sbi) {
 		sbi->sbi_vcomm = NULL;
 		sbi->sbi_root = NULL;
@@ -154,7 +155,6 @@
                 coda_cnode_free(ITOC(root));
         }
         sb->s_dev = 0;
-	MOD_DEC_USE_COUNT;
         return NULL;
 }
 
@@ -209,6 +209,10 @@
 	}
 
         cnp = ITOC(inode);
+
+	if ( coda_fid_is_volroot(&cnp->c_fid) )
+		list_del(&cnp->c_volrootlist);
+
         open_inode = cnp->c_ovp;
         if ( open_inode ) {
                 CDEBUG(D_SUPER, "DELINO cached file: ino %ld count %d.\n",  
@@ -225,9 +229,9 @@
 	EXIT;
 }
 
-static int  coda_notify_change(struct dentry *dentry, struct iattr *iattr)
+static int  coda_notify_change(struct dentry *de, struct iattr *iattr)
 {
-	struct inode *inode = dentry->d_inode;
+	struct inode *inode = de->d_inode;
         struct cnode *cnp;
         struct coda_vattr vattr;
         int error;
diff -u --recursive --new-file v2.1.78/linux/fs/coda/symlink.c linux/fs/coda/symlink.c
--- v2.1.78/linux/fs/coda/symlink.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/coda/symlink.c	Tue Jan  6 10:00:21 1998
@@ -24,7 +24,7 @@
 #include <linux/coda_cnode.h>
 #include <linux/coda_cache.h>
 
-static int coda_readlink(struct dentry *dentry, char *buffer, int length);
+static int coda_readlink(struct dentry *de, char *buffer, int length);
 static struct dentry *coda_follow_link(struct dentry *, struct dentry *);
 
 struct inode_operations coda_symlink_inode_operations = {
@@ -39,41 +39,41 @@
 	NULL,			/* mknod */
 	NULL,			/* rename */
 	coda_readlink,		/* readlink */
-	coda_follow_link,     	/* follow_link */
+	coda_follow_link,	/* follow_link */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
-	NULL,            	/* permission */
-	NULL,                   /* smap */
-	NULL,                   /* update page */
-        NULL                    /* revalidate */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL,			/* update page */
+	NULL			/* revalidate */
 };
 
-static int coda_readlink(struct inode *dentry, char *buffer, int length)
+static int coda_readlink(struct dentry *de, char *buffer, int length)
 {
-	struct inode *inode = dentry->d_inode;
-        int len;
+	struct inode *inode = de->d_inode;
+	int len;
 	int error;
-        char *buf;
+	char *buf;
 	struct cnode *cp;
-        ENTRY;
+	ENTRY;
 
-        cp = ITOC(inode);
-        CHECK_CNODE(cp);
+	cp = ITOC(inode);
+	CHECK_CNODE(cp);
 
-        /* the maximum length we receive is len */
-        if ( length > CFS_MAXPATHLEN ) 
-	        len = CFS_MAXPATHLEN;
+	/* the maximum length we receive is len */
+	if ( length > CFS_MAXPATHLEN ) 
+		len = CFS_MAXPATHLEN;
 	else
-	        len = length;
+		len = length;
 	CODA_ALLOC(buf, char *, len);
 	if ( !buf ) 
-	        return -ENOMEM;
+		return -ENOMEM;
 	
 	error = venus_readlink(inode->i_sb, &(cp->c_fid), buf, &len);
 
-        CDEBUG(D_INODE, "result %s\n", buf);
+	CDEBUG(D_INODE, "result %s\n", buf);
 	if (! error) {
 		copy_to_user(buffer, buf, len);
 		put_user('\0', buffer + len);
@@ -84,20 +84,20 @@
 	return error;
 }
 
-static struct dentry *coda_follow_link(struct dentry *dentry, 
-					struct dentry *base)
+static struct dentry *coda_follow_link(struct dentry *de, 
+				       struct dentry *base)
 {
-	struct inode *inode = dentry->d_inode;
+	struct inode *inode = de->d_inode;
 	int error;
 	struct cnode *cnp;
 	unsigned int len;
+	char mem[CFS_MAXPATHLEN];
 	char *path;
-	char mem[CFS_MAXPATHLEN]; /* N.B. too big for the stack? */
 ENTRY;
 	CDEBUG(D_INODE, "(%x/%ld)\n", inode->i_dev, inode->i_ino);
 	
-        cnp = ITOC(inode);
-        CHECK_CNODE(cnp);
+	cnp = ITOC(inode);
+	CHECK_CNODE(cnp);
 
 	len = CFS_MAXPATHLEN;
 	error = venus_readlink(inode->i_sb, &(cnp->c_fid), mem, &len);
diff -u --recursive --new-file v2.1.78/linux/fs/dcache.c linux/fs/dcache.c
--- v2.1.78/linux/fs/dcache.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/dcache.c	Mon Jan 12 14:38:40 1998
@@ -59,6 +59,22 @@
 }
 
 /*
+ * Release the dentry's inode, using the fileystem
+ * d_iput() operation if defined.
+ */
+static inline void dentry_iput(struct dentry * dentry)
+{
+	struct inode *inode = dentry->d_inode;
+	if (inode) {
+		dentry->d_inode = NULL;
+		if (dentry->d_op && dentry->d_op->d_iput)
+			dentry->d_op->d_iput(dentry, inode);
+		else
+			iput(inode);
+	}
+}
+
+/*
  * dput()
  *
  * This is complicated by the fact that we do not want to put
@@ -104,13 +120,10 @@
 		list_del(&dentry->d_lru);
 	}
 	if (list_empty(&dentry->d_hash)) {
-		struct inode *inode = dentry->d_inode;
 		struct dentry * parent;
+
 		list_del(&dentry->d_child);
-		if (inode) {
-			dentry->d_inode = NULL;
-			iput(inode);
-		}
+		dentry_iput(dentry);
 		parent = dentry->d_parent;
 		d_free(dentry);
 		if (dentry == parent)
@@ -146,8 +159,9 @@
 int d_invalidate(struct dentry * dentry)
 {
 	/* Check whether to do a partial shrink_dcache */
-	if (dentry->d_count > 1 && !list_empty(&dentry->d_subdirs))
+	if (!list_empty(&dentry->d_subdirs))
 		shrink_dcache_parent(dentry);
+
 	if (dentry->d_count != 1)
 		return -EBUSY;
 
@@ -259,12 +273,7 @@
 
 	list_del(&dentry->d_hash);
 	list_del(&dentry->d_child);
-	if (dentry->d_inode) {
-		struct inode * inode = dentry->d_inode;
-
-		dentry->d_inode = NULL;
-		iput(inode);
-	}
+	dentry_iput(dentry);
 	parent = dentry->d_parent;
 	d_free(dentry);
 	dput(parent);
@@ -636,11 +645,7 @@
 	 * Are we the only user?
 	 */
 	if (dentry->d_count == 1) {
-		struct inode * inode = dentry->d_inode;
-		if (inode) {
-			dentry->d_inode = NULL;
-			iput(inode);
-		}
+		dentry_iput(dentry);
 		return;
 	}
 
diff -u --recursive --new-file v2.1.78/linux/fs/dquot.c linux/fs/dquot.c
--- v2.1.78/linux/fs/dquot.c	Tue Dec  2 09:49:39 1997
+++ linux/fs/dquot.c	Mon Jan 12 14:46:24 1998
@@ -23,6 +23,12 @@
  *          Nick Kralevich <nickkral@cal.alumni.berkeley.edu>, 21 Jul 97
  *          Fixed a condition where user and group quotas could get mixed up.
  *
+ *          Chris Rankin <rankinc@bellsouth.net>, 31 Dec 97, 2-4 Jan 98
+ *          Fixed kernel API so that the user can get the quota for any
+ *          group s/he belongs to. Also return useful error codes when
+ *          turning quotas off, and fixed sync_dquot() so that all devices
+ *          are synced when dev==NODEV. 
+ *
  * (C) Copyright 1994, 1995 Marco van Wieringen 
  *
  */
@@ -273,7 +279,7 @@
 
 	dqstats.syncs++;
 	for (i = 0; i < nr_dquots * 2; i++, dquot = dquot->dq_next) {
-		if (dev == NODEV || dquot->dq_count == 0 || dquot->dq_dev != dev)
+		if ((dev != NODEV && dquot->dq_dev != dev) || dquot->dq_count == 0 )
 			continue;
 		if (type != -1 && dquot->dq_type != type)
 			continue;
@@ -592,13 +598,13 @@
 	}
 	if ((dquot = dqget(dev, id, type)) != NODQUOT) {
 		lock_dquot(dquot);
-		if (id > 0 && ((flags & SET_QUOTA) || (flags & SET_QLIMIT))) {
+		if (id > 0 && (flags & (SET_QUOTA|SET_QLIMIT))) {
 			dquot->dq_bhardlimit = dq_dqblk.dqb_bhardlimit;
 			dquot->dq_bsoftlimit = dq_dqblk.dqb_bsoftlimit;
 			dquot->dq_ihardlimit = dq_dqblk.dqb_ihardlimit;
 			dquot->dq_isoftlimit = dq_dqblk.dqb_isoftlimit;
 		}
-		if ((flags & SET_QUOTA) || (flags & SET_USE)) {
+		if (flags & (SET_QUOTA|SET_USE)) {
 			if (dquot->dq_isoftlimit &&
 			    dquot->dq_curinodes < dquot->dq_isoftlimit &&
 			    dq_dqblk.dqb_curinodes >= dquot->dq_isoftlimit)
@@ -917,12 +923,18 @@
 	struct vfsmount *vfsmnt;
 	short cnt;
 
+	if ( !(vfsmnt = lookup_vfsmnt(dev)) )
+		return -ENODEV;
+
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		if (type != -1 && cnt != type)
 			continue;
-		if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL ||
-	     	     vfsmnt->mnt_quotas[cnt] == (struct file *)NULL)
-			continue;
+		if (vfsmnt->mnt_quotas[cnt] == (struct file *)NULL)
+		{
+			if(type == -1)
+				continue;
+			return -ESRCH;
+		}
 		vfsmnt->mnt_sb->dq_op = (struct dquot_operations *)NULL;
 		reset_dquot_ptrs(dev, cnt);
 		invalidate_dquots(dev, cnt);
@@ -1027,7 +1039,7 @@
 			break;
 		case Q_GETQUOTA:
 			if (((type == USRQUOTA && current->uid != id) ||
-			     (type == GRPQUOTA && current->gid != id)) && !fsuser())
+			     (type == GRPQUOTA && in_group_p(id))) && !fsuser())
 				goto out;
 			break;
 		default:
@@ -1036,7 +1048,7 @@
 	}
 
 	ret = -EINVAL;
-	dev = 0;
+	dev = NODEV;
 	if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) {
 		mode_t mode;
 		struct dentry * dentry;
diff -u --recursive --new-file v2.1.78/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
--- v2.1.78/linux/fs/ext2/inode.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/ext2/inode.c	Mon Jan 12 14:22:54 1998
@@ -209,18 +209,30 @@
 repeat:
 	tmp = *p;
 	if (tmp) {
-		result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+		struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
 		if (tmp == *p)
 			return result;
 		brelse (result);
 		goto repeat;
 	}
-	if (!create || new_block >= 
-	    (current->rlim[RLIMIT_FSIZE].rlim_cur >>
-	     EXT2_BLOCK_SIZE_BITS(inode->i_sb))) {
-		*err = -EFBIG;
-		return NULL;
+	*err = -EFBIG;
+	if (!create)
+		goto dont_create;
+
+	/* Check file limits.. */
+	{
+		unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+		if (limit < RLIM_INFINITY) {
+			limit >>= EXT2_BLOCK_SIZE_BITS(inode->i_sb);
+			if (new_block >= limit) {
+				send_sig(SIGXFSZ, current, 0);
+dont_create:
+				*err = -EFBIG;
+				return NULL;
+			}
+		}
 	}
+
 	if (inode->u.ext2_i.i_next_alloc_block == new_block)
 		goal = inode->u.ext2_i.i_next_alloc_goal;
 
diff -u --recursive --new-file v2.1.78/linux/fs/fat/Makefile linux/fs/fat/Makefile
--- v2.1.78/linux/fs/fat/Makefile	Tue Feb  6 23:39:27 1996
+++ linux/fs/fat/Makefile	Thu Jan  8 14:02:41 1998
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := fat.o
-O_OBJS   := buffer.o cache.o dir.o file.o inode.o misc.o mmap.o tables.o
+O_OBJS   := buffer.o cache.o dir.o file.o inode.o misc.o mmap.o tables.o cvf.o
 OX_OBJS  := fatfs_syms.o
 M_OBJS   := $(O_TARGET)
 
diff -u --recursive --new-file v2.1.78/linux/fs/fat/buffer.c linux/fs/fat/buffer.c
--- v2.1.78/linux/fs/fat/buffer.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/fat/buffer.c	Thu Jan  8 14:02:41 1998
@@ -9,6 +9,7 @@
 #include <linux/string.h>
 #include <linux/fs.h>
 #include <linux/msdos_fs.h>
+#include <linux/fat_cvf.h>
 
 #if 0
 #  define PRINTK(x) printk x
@@ -28,6 +29,11 @@
 	 * is always of size 1024 (or 2048). Doing readahead may be
 	 * counterproductive or just plain wrong.
 	 */
+
+	if(MSDOS_SB(sb)->cvf_format)
+	  if(MSDOS_SB(sb)->cvf_format->cvf_bread)
+	    return MSDOS_SB(sb)->cvf_format->cvf_bread(sb,block);
+
 	if (sb->s_blocksize == 512) {
 		ret = bread (sb->s_dev,block,512);
 	} else {
@@ -92,6 +98,11 @@
 {
 	struct buffer_head *ret = NULL;
 	PRINTK(("fat_getblk: block=0x%x\n", block));
+
+	if(MSDOS_SB(sb)->cvf_format)
+	  if(MSDOS_SB(sb)->cvf_format->cvf_getblk)
+	    return MSDOS_SB(sb)->cvf_format->cvf_getblk(sb,block);
+
 	if (sb->s_blocksize == 512){
 		ret = getblk (sb->s_dev,block,512);
 	}else{
@@ -111,6 +122,10 @@
 	struct buffer_head *bh)
 {
 	if (bh != NULL){
+		if(MSDOS_SB(sb)->cvf_format)
+          	  if(MSDOS_SB(sb)->cvf_format->cvf_brelse)
+            	    return MSDOS_SB(sb)->cvf_format->cvf_brelse(sb,bh);
+
 		if (sb->s_blocksize == 512){
 			brelse (bh);
 		}else{
@@ -128,6 +143,12 @@
 	struct buffer_head *bh,
 	int dirty_val)
 {
+	if(MSDOS_SB(sb)->cvf_format)
+          if(MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty)
+          { MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty(sb,bh,dirty_val);
+            return;
+          }
+ 
 	if (sb->s_blocksize != 512){
 		bh = bh->b_next;
 	}
@@ -139,6 +160,12 @@
 	struct buffer_head *bh,
 	int val)
 {
+	if(MSDOS_SB(sb)->cvf_format)
+          if(MSDOS_SB(sb)->cvf_format->cvf_set_uptodate)
+          { MSDOS_SB(sb)->cvf_format->cvf_set_uptodate(sb,bh,val);
+            return;
+          }
+ 
 	if (sb->s_blocksize != 512){
 		bh = bh->b_next;
 	}
@@ -148,6 +175,10 @@
 	struct super_block *sb,
 	struct buffer_head *bh)
 {
+	if(MSDOS_SB(sb)->cvf_format)
+          if(MSDOS_SB(sb)->cvf_format->cvf_is_uptodate)
+            return MSDOS_SB(sb)->cvf_format->cvf_is_uptodate(sb,bh);
+ 
 	if (sb->s_blocksize != 512){
 		bh = bh->b_next;
 	}
@@ -160,6 +191,12 @@
 	int nbreq,
 	struct buffer_head *bh[32])
 {
+        if(MSDOS_SB(sb)->cvf_format)
+          if(MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block)
+          { MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block(sb,opr,nbreq,bh);
+            return;
+          }
+ 
 	if (sb->s_blocksize == 512){
 		ll_rw_block(opr,nbreq,bh);
 	}else{
diff -u --recursive --new-file v2.1.78/linux/fs/fat/cache.c linux/fs/fat/cache.c
--- v2.1.78/linux/fs/fat/cache.c	Wed Nov 12 13:34:27 1997
+++ linux/fs/fat/cache.c	Thu Jan  8 14:02:41 1998
@@ -9,6 +9,7 @@
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/stat.h>
+#include <linux/fat_cvf.h>
 
 #include "msbuffer.h"
 
@@ -29,6 +30,10 @@
 	unsigned char *p_first,*p_last;
 	int copy,first,last,next,b;
 
+	if(MSDOS_SB(sb)->cvf_format)
+	  if(MSDOS_SB(sb)->cvf_format->fat_access)
+	    return MSDOS_SB(sb)->cvf_format->fat_access(sb,nr,new_value);
+
 	if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
 		return 0;
 	if (MSDOS_SB(sb)->fat_bits == 32) {
@@ -240,7 +245,7 @@
 }
 
 
-int get_cluster(struct inode *inode,int cluster)
+int fat_get_cluster(struct inode *inode,int cluster)
 {
 	int nr,count;
 
@@ -262,6 +267,9 @@
 	int cluster,offset;
 
 	sb = MSDOS_SB(inode->i_sb);
+	if(sb->cvf_format)
+	  if(sb->cvf_format->cvf_smap)
+	    return sb->cvf_format->cvf_smap(inode,sector);
 	if ((sb->fat_bits != 32) &&
 	    (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
 	     !MSDOS_I(inode)->i_start))) {
@@ -271,7 +279,7 @@
 	}
 	cluster = sector/sb->cluster_size;
 	offset = sector % sb->cluster_size;
-	if (!(cluster = get_cluster(inode,cluster))) return 0;
+	if (!(cluster = fat_get_cluster(inode,cluster))) return 0;
 	return (cluster-2)*sb->cluster_size+sb->data_start+offset;
 }
 
diff -u --recursive --new-file v2.1.78/linux/fs/fat/cvf.c linux/fs/fat/cvf.c
--- v2.1.78/linux/fs/fat/cvf.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/fat/cvf.c	Thu Jan  8 14:02:41 1998
@@ -0,0 +1,120 @@
+/*
+ * CVF extensions for fat-based filesystems
+ *
+ * written 1997,1998 by Frank Gockel <gockel@sent13.uni-duisburg.de>
+ *
+ */
+ 
+#include<linux/sched.h>
+#include<linux/fs.h>
+#include<linux/msdos_fs.h>
+#include<linux/msdos_fs_sb.h>
+#include<linux/string.h>
+#include<linux/fat_cvf.h>
+
+#define MAX_CVF_FORMATS 3
+
+struct cvf_format *cvf_formats[MAX_CVF_FORMATS]={NULL,NULL,NULL};
+int cvf_format_use_count[MAX_CVF_FORMATS]={0,0,0};
+
+int register_cvf_format(struct cvf_format*cvf_format)
+{ int i,j;
+
+  for(i=0;i<MAX_CVF_FORMATS;++i)
+  { if(cvf_formats[i]==NULL)
+    { /* free slot found, now check version */
+      for(j=0;j<MAX_CVF_FORMATS;++j)
+      { if(cvf_formats[j])
+        { if(cvf_formats[j]->cvf_version==cvf_format->cvf_version)
+          { printk("register_cvf_format: version %d already registered\n",
+                   cvf_format->cvf_version);
+            return -1;
+          }
+        }
+      }
+      cvf_formats[i]=cvf_format;
+      cvf_format_use_count[i]=0;
+      printk("CVF format %s (version id %d) successfully registered.\n",
+             cvf_format->cvf_version_text,cvf_format->cvf_version);
+      return 0;
+    }
+  }
+  
+  printk("register_cvf_format: too many formats\n");
+  return -1;
+}
+
+int unregister_cvf_format(struct cvf_format*cvf_format)
+{ int i;
+
+  for(i=0;i<MAX_CVF_FORMATS;++i)
+  { if(cvf_formats[i])
+    { if(cvf_formats[i]->cvf_version==cvf_format->cvf_version)
+      { if(cvf_format_use_count[i])
+        { printk("unregister_cvf_format: format %d in use, cannot remove!\n",
+          cvf_formats[i]->cvf_version);
+          return -1;
+        }
+      
+        printk("CVF format %s (version id %d) successfully unregistered.\n",
+        cvf_formats[i]->cvf_version_text,cvf_formats[i]->cvf_version);
+        cvf_formats[i]=NULL;
+        return 0;
+      }
+    }
+  }
+  
+  printk("unregister_cvf_format: format %d is not registered\n",
+         cvf_format->cvf_version);
+  return -1;
+}
+
+void dec_cvf_format_use_count_by_version(int version)
+{ int i;
+
+  for(i=0;i<MAX_CVF_FORMATS;++i)
+  { if(cvf_formats[i])
+    { if(cvf_formats[i]->cvf_version==version)
+      { --cvf_format_use_count[i];
+        if(cvf_format_use_count[i]<0)
+        { cvf_format_use_count[i]=0;
+          printk(KERN_EMERG "FAT FS/CVF: This is a bug in cvf_version_use_count\n");
+        }
+        return;
+      }
+    }
+  }
+  
+  printk("dec_cvf_format_use_count_by_version: version %d not found ???\n",
+         version);
+}
+
+int detect_cvf(struct super_block*sb,char*force)
+{ int i;
+  int found=0;
+  int found_i=-1;
+
+  if(force)
+  { if(*force)
+    { for(i=0;i<MAX_CVF_FORMATS;++i)
+      { if(cvf_formats[i])
+        { if(!strcmp(cvf_formats[i]->cvf_version_text,force))
+            return i;
+        }
+      }
+    }
+  }
+
+  for(i=0;i<MAX_CVF_FORMATS;++i)
+  { if(cvf_formats[i])
+    { if(cvf_formats[i]->detect_cvf(sb))
+      { ++found;
+        found_i=i;
+      }
+    }
+  }
+  
+  if(found==1)return found_i;
+  if(found>1)printk("CVF detection ambiguous, use cvf_format=xxx option\n"); 
+  return -1;
+}
diff -u --recursive --new-file v2.1.78/linux/fs/fat/dir.c linux/fs/fat/dir.c
--- v2.1.78/linux/fs/fat/dir.c	Wed Nov 12 13:34:27 1997
+++ linux/fs/fat/dir.c	Thu Jan  8 14:02:41 1998
@@ -434,6 +434,11 @@
 				    vfat_ioctl_fill, NULL, 1, 0, 1);
 	}
 	default:
+		/* forward ioctl to CVF extension */
+	       if(MSDOS_SB(inode->i_sb)->cvf_format
+	          &&MSDOS_SB(inode->i_sb)->cvf_format->cvf_dir_ioctl)
+	              return MSDOS_SB(inode->i_sb)->cvf_format->
+	                        cvf_dir_ioctl(inode,filp,cmd,arg);
 		return -EINVAL;
 	}
 
diff -u --recursive --new-file v2.1.78/linux/fs/fat/fatfs_syms.c linux/fs/fat/fatfs_syms.c
--- v2.1.78/linux/fs/fat/fatfs_syms.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/fat/fatfs_syms.c	Thu Jan  8 14:02:41 1998
@@ -11,6 +11,7 @@
 
 #include <linux/mm.h>
 #include <linux/msdos_fs.h>
+#include <linux/fat_cvf.h>
 
 #include "msbuffer.h"
 #include "tables.h"
@@ -47,6 +48,11 @@
 EXPORT_SYMBOL(fat_uni2esc);
 EXPORT_SYMBOL(fat_unlock_creation);
 EXPORT_SYMBOL(fat_write_inode);
+EXPORT_SYMBOL(register_cvf_format);
+EXPORT_SYMBOL(unregister_cvf_format);
+EXPORT_SYMBOL(fat_get_cluster);
+EXPORT_SYMBOL(lock_fat);
+EXPORT_SYMBOL(unlock_fat);
 
 int init_fat_fs(void)
 {
diff -u --recursive --new-file v2.1.78/linux/fs/fat/file.c linux/fs/fat/file.c
--- v2.1.78/linux/fs/fat/file.c	Thu Dec  4 14:53:56 1997
+++ linux/fs/fat/file.c	Thu Jan  8 14:02:41 1998
@@ -17,6 +17,7 @@
 #include <linux/stat.h>
 #include <linux/string.h>
 #include <linux/pagemap.h>
+#include <linux/fat_cvf.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -112,6 +113,40 @@
 	NULL			/* smap */
 };
 
+static struct file_operations fat_file_operations_readpage = {
+	NULL,			/* lseek - default */
+	fat_file_read,		/* read */
+	fat_file_write,		/* write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select v2.0.x/poll v2.1.x - default */
+	NULL,			/* ioctl - default */
+	generic_file_mmap,	/* mmap */
+	NULL,			/* no special open is needed */
+	NULL,			/* release */
+	file_fsync		/* fsync */
+};
+
+struct inode_operations fat_file_inode_operations_readpage = {
+	&fat_file_operations_readpage,	/* default file operations */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	fat_readpage,		/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	fat_truncate,		/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
+};
+
 #define MSDOS_PREFETCH	32
 struct fat_pre {
 	int file_sector;/* Next sector to read in the prefetch table */
@@ -277,6 +312,10 @@
 	loff_t *ppos)
 {
 	struct inode *inode = filp->f_dentry->d_inode;
+	if(MSDOS_SB(inode->i_sb)->cvf_format)
+          if(MSDOS_SB(inode->i_sb)->cvf_format->cvf_file_read)
+            return MSDOS_SB(inode->i_sb)->cvf_format->cvf_file_read(filp,buf,count,ppos);
+
 	if (!MSDOS_I(inode)->i_binary)
 		return fat_file_read_text(filp, buf, count, ppos);
 	return generic_file_read(filp, buf, count, ppos);
@@ -303,6 +342,10 @@
 		printk("fat_file_write: inode = NULL\n");
 		return -EINVAL;
 	}
+        if(MSDOS_SB(sb)->cvf_format)
+          if(MSDOS_SB(sb)->cvf_format->cvf_file_write)
+            return MSDOS_SB(sb)->cvf_format->cvf_file_write(filp,buf,count,ppos);
+
 	/* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
 	if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
 		printk("fat_file_write: mode = %07o\n",inode->i_mode);
diff -u --recursive --new-file v2.1.78/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v2.1.78/linux/fs/fat/inode.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/fat/inode.c	Thu Jan  8 14:02:41 1998
@@ -20,6 +20,7 @@
 #include <linux/fs.h>
 #include <linux/stat.h>
 #include <linux/locks.h>
+#include <linux/fat_cvf.h>
 #include <linux/malloc.h>
 
 #include "msbuffer.h"
@@ -139,6 +140,10 @@
 void fat_put_super(struct super_block *sb)
 {
 	lock_super(sb);
+	if(MSDOS_SB(sb)->cvf_format)
+	{ MSDOS_SB(sb)->cvf_format->unmount_cvf(sb);
+	  dec_cvf_format_use_count_by_version(MSDOS_SB(sb)->cvf_format->cvf_version);
+	}
 	if (MSDOS_SB(sb)->fat_bits == 32) {
 		fat_clusters_flush(sb);
 	}
@@ -169,7 +174,8 @@
 
 
 static int parse_options(char *options,int *fat, int *blksize, int *debug,
-			 struct fat_mount_options *opts)
+			 struct fat_mount_options *opts,
+			 char* cvf_format, char*cvf_options)
 {
 	char *this_char,*value,save,*savep;
 	char *p;
@@ -306,6 +312,16 @@
 					ret = 0;
 			}
 		}
+                else if (!strcmp(this_char,"cvf_format")) {
+                        if (!value)
+                                return 0;
+                        strncpy(cvf_format,value,20);
+                }
+                else if (!strcmp(this_char,"cvf_options")) {
+                        if (!value)
+                                return 0;
+                        strncpy(cvf_options,value,100);
+                }
 
 		if (this_char != options) *(this_char-1) = ',';
 		if (value) *savep = save;
@@ -335,6 +351,14 @@
 	int fat32;
 	struct fat_mount_options opts;
 	char buf[50];
+	int i;
+	char cvf_format[21];
+	char cvf_options[101];
+
+	cvf_format[0]='\0';
+	cvf_options[0]='\0';
+	MSDOS_SB(sb)->cvf_format=NULL;
+	MSDOS_SB(sb)->private_data=NULL;
 
 	MOD_INC_USE_COUNT;
 	if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
@@ -342,10 +366,12 @@
 		if (blksize != 512){
 			printk ("MSDOS: Hardware sector size is %d\n",blksize);
 		}
+
 	}
 
 	opts.isvfat = MSDOS_SB(sb)->options.isvfat;
-	if (!parse_options((char *) data, &fat, &blksize, &debug, &opts)
+	if (!parse_options((char *) data, &fat, &blksize, &debug, &opts, 
+	                   cvf_format, cvf_options)
 	    || (blksize != 512 && blksize != 1024 && blksize != 2048))
 		goto out_fail;
 	/* N.B. we should parse directly into the sb structure */
@@ -458,6 +484,9 @@
 				/* because clusters (DOS) are often aligned */
 				/* on odd sectors. */
 	sb->s_blocksize_bits = blksize == 512 ? 9 : (blksize == 1024 ? 10 : 11);
+	if(!strcmp(cvf_format,"none"))i=-1;
+	else i=detect_cvf(sb,cvf_format);
+	if(i>=0)error=cvf_formats[i]->mount_cvf(sb,cvf_options);
 	if (error || debug) {
 		/* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
 		printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
@@ -477,7 +506,7 @@
 		       MSDOS_SB(sb)->root_cluster,MSDOS_SB(sb)->free_clusters);
 		printk ("Transaction block size = %d\n",blksize);
 	}
-	if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
+	if (i<0) if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
 		MSDOS_SB(sb)->clusters = fat_clusters-2;
 	if (error)
 		goto out_invalid;
@@ -517,6 +546,10 @@
 	sb->s_root = d_alloc_root(root_inode, NULL);
 	if (!sb->s_root)
 		goto out_no_root;
+	if(i>=0)
+	{ MSDOS_SB(sb)->cvf_format=cvf_formats[i];
+	  ++cvf_format_use_count[i];
+	}
 	return sb;
 
 out_no_root:
@@ -540,6 +573,9 @@
 		kfree(opts.iocharset);
 	}
 	sb->s_dev = 0;
+	if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
+	MSDOS_SB(sb)->private_data=NULL;
+ 
 	MOD_DEC_USE_COUNT;
 	return NULL;
 }
@@ -548,7 +584,11 @@
 {
 	int free,nr;
 	struct statfs tmp;
-
+       
+        if(MSDOS_SB(sb)->cvf_format)
+          if(MSDOS_SB(sb)->cvf_format->cvf_statfs)
+            return MSDOS_SB(sb)->cvf_format->cvf_statfs(sb,buf,bufsiz);
+          
 	lock_fat(sb);
 	if (MSDOS_SB(sb)->free_clusters != -1)
 		free = MSDOS_SB(sb)->free_clusters;
@@ -577,12 +617,15 @@
 	int cluster,offset;
 
 	sb = MSDOS_SB(inode->i_sb);
+	if(sb->cvf_format)
+	  if(sb->cvf_format->cvf_bmap)
+	    return sb->cvf_format->cvf_bmap(inode,block);
 	if ((inode->i_ino == MSDOS_ROOT_INO) && (sb->fat_bits != 32)) {
 		return sb->dir_start + block;
 	}
 	cluster = block/sb->cluster_size;
 	offset = block % sb->cluster_size;
-	if (!(cluster = get_cluster(inode,cluster))) return 0;
+	if (!(cluster = fat_get_cluster(inode,cluster))) return 0;
 	return (cluster-2)*sb->cluster_size+sb->data_start+offset;
 }
 
@@ -689,7 +732,12 @@
 		       !is_exec(raw_entry->ext)))
 		    	? S_IRUGO|S_IWUGO : S_IRWXUGO)
 		    & ~MSDOS_SB(sb)->options.fs_umask) | S_IFREG;
-		inode->i_op = (sb->s_blocksize == 1024 || sb->s_blocksize == 2048)
+		if(MSDOS_SB(sb)->cvf_format)
+		  inode->i_op = (MSDOS_SB(sb)->cvf_format->flags&CVF_USE_READPAGE)
+			? &fat_file_inode_operations_readpage
+			: &fat_file_inode_operations_1024;
+		else
+		  inode->i_op = (sb->s_blocksize == 1024 || sb->s_blocksize == 2048)
 			? &fat_file_inode_operations_1024
 			: &fat_file_inode_operations;
 		MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
diff -u --recursive --new-file v2.1.78/linux/fs/fat/misc.c linux/fs/fat/misc.c
--- v2.1.78/linux/fs/fat/misc.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/fat/misc.c	Thu Jan  8 14:02:41 1998
@@ -221,6 +221,10 @@
 #endif
 	sector = MSDOS_SB(sb)->data_start+(nr-2)*cluster_size;
 	last_sector = sector + cluster_size;
+	if(MSDOS_SB(sb)->cvf_format&&
+	  MSDOS_SB(sb)->cvf_format->zero_out_cluster)
+	    MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode,nr);
+	else
 	for ( ; sector < last_sector; sector++) {
 		#ifdef DEBUG
 			printk("zeroing sector %d\n",sector);
@@ -279,9 +283,7 @@
 	    month < 2 ? 1 : 0)+3653);
 			/* days since 1.1.70 plus 80's leap day */
 	secs += sys_tz.tz_minuteswest*60;
-	if (sys_tz.tz_dsttime) {
-	    secs -= 3600;
-	}
+	if (sys_tz.tz_dsttime) secs -= 3600;
 	return secs;
 }
 
@@ -293,9 +295,6 @@
 {
 	int day,year,nl_day,month;
 
-	if (sys_tz.tz_dsttime) {
-		unix_date += 3600;
-	}
 	unix_date -= sys_tz.tz_minuteswest*60;
 	if (sys_tz.tz_dsttime) unix_date += 3600;
 
diff -u --recursive --new-file v2.1.78/linux/fs/fat/mmap.c linux/fs/fat/mmap.c
--- v2.1.78/linux/fs/fat/mmap.c	Tue Dec  2 09:49:40 1997
+++ linux/fs/fat/mmap.c	Thu Jan  8 14:02:41 1998
@@ -97,7 +97,10 @@
 int fat_mmap(struct file * file, struct vm_area_struct * vma)
 {
 	struct inode *inode = file->f_dentry->d_inode;
-	
+	if(MSDOS_SB(inode->i_sb)->cvf_format)
+	  if(MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap)
+	    return MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap(file,vma);
+
 	if (vma->vm_flags & VM_SHARED)	/* only PAGE_COW or read-only supported now */
 		return -EINVAL;
 	if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
@@ -114,4 +117,14 @@
 	return 0;
 }
 
+
+int fat_readpage(struct inode * inode, struct page * page)
+{
+	if(MSDOS_SB(inode->i_sb)->cvf_format)
+	  if(MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage)
+	    return MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage(inode,page);
+	    
+	printk("fat_readpage called with no handler (shouldn't happen)\n");
+	return -1;
+}
 
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/ChangeLog linux/fs/hfs/ChangeLog
--- v2.1.78/linux/fs/hfs/ChangeLog	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/ChangeLog	Mon Jan 12 14:46:24 1998
@@ -1,3 +1,39 @@
+Wed Jan  7 19:33:33 1998  a sun  <asun@zoology.washington.edu>
+
+	* inode.c
+	don't hfs_cat_put in hfs_iget. that's a bad idea and results
+	in screwed up entry counts.
+
+	* catalog.c
+	modified hfs_cat_put to undirty deleted entries without trying to 
+	write them out.
+
+Tue Jan  6 14:38:24 1998  a sun  <asun@zoology.washington.edu>
+
+	* version.c
+	changed it to 0.95+asun2
+
+	* sysdep.c
+	altered catalog entry pruning to make sure that an iput
+	gets done. for some reason, shrink_dcache_parent wasn't
+	doing it.
+
+	* catalog.c
+	added a global dirty list to check for pruning.
+	
+Tue Jan  6 12:29:52 1998  a sun  <asun@zoology.washington.edu>
+
+	* catalog.c
+	re-wrote it to be similar to 2.1.x inode.c. this should
+ 	at least make catalog.c SMP safe. 
+
+	* hfs.h, linux/hfs_fs.h
+	moved dentry operations into hfs.h. these probably should
+	be moved somewhere else.
+
+	* super.c, dir_cap.c, dir_nat.c, dir_dbl.c, sysdep.c
+	added dentry ops to hash everything to lowercase.
+
 Sun Dec 28 22:48:53 1997  a sun  <asun@zoology.washington.edu>
 
 	* sysdep.c, catalog.c, hfs.h
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/catalog.c linux/fs/hfs/catalog.c
--- v2.1.78/linux/fs/hfs/catalog.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/catalog.c	Mon Jan 12 14:46:24 1998
@@ -33,7 +33,7 @@
 #define CCACHE_MAX 1024
 
 /* Number of entries to fit in a single page on an i386 */
-#define CCACHE_INC ((4080-sizeof(void *))/sizeof(struct hfs_cat_entry))
+#define CCACHE_INC ((PAGE_SIZE - sizeof(void *))/sizeof(struct hfs_cat_entry))
 
 /*================ File-local data types ================*/
 
@@ -94,7 +94,6 @@
 	} u;
 };
 
-typedef struct hfs_cat_entry *hfs_cat_entry_ptr;
 
 struct allocation_unit {
 	struct allocation_unit *next;
@@ -103,11 +102,17 @@
 
 /*================ File-local variables ================*/
  
-static hfs_cat_entry_ptr hash_table[CCACHE_NR] = {NULL, };
-
-static struct hfs_cat_entry *first_entry = NULL;
-static hfs_wait_queue entry_wait;
-static int nr_entries = 0, nr_free_entries = 0;
+static LIST_HEAD(entry_in_use);
+static LIST_HEAD(entry_dirty); /* all the dirty entries */
+static LIST_HEAD(entry_unused);
+static struct list_head hash_table[CCACHE_NR];
+
+spinlock_t entry_lock = SPIN_LOCK_UNLOCKED;
+
+static struct {
+        int nr_entries;
+        int nr_free_entries;
+} entries_stat;
 
 static struct allocation_unit *allocation = NULL;
 
@@ -147,140 +152,22 @@
  * hash an (struct mdb *) and a (struct hfs_cat_key *)
  * to a pointer to a slot in the hash table.
  */
-static inline struct hfs_cat_entry **hash(struct hfs_mdb *mdb,
-				          const struct hfs_cat_key *key)
+static inline struct list_head *hash(struct hfs_mdb *mdb,
+				     const struct hfs_cat_key *key)
 {
 	return hash_table + hashfn(mdb, key);
 }
 
-/*
- * insert_free()
- *
- * Add an entry to the front of the free list.
- */
-static inline void insert_free(struct hfs_cat_entry *entry)
-{
-	struct hfs_cat_entry * prev, * next = first_entry;
-
-	first_entry = entry;
-	prev = next->prev;
-	entry->next = next;
-	entry->prev = prev;
-	prev->next = entry;
-	next->prev = entry;
-}
-
-/*
- * remove_free()
- *
- * Remove an entry from the free list.
- */
-static inline void remove_free(struct hfs_cat_entry *entry)
-{
-	if (first_entry == entry) {
-		first_entry = first_entry->next;
-	}
-	if (entry->next) {
-		entry->next->prev = entry->prev;
-	}
-	if (entry->prev) {
-		entry->prev->next = entry->next;
-	}
-	entry->next = entry->prev = NULL;
-}
-
-/*
- * insert_hash()
- *
- * Add an entry to the front of the appropriate hash list
- */
-static void insert_hash(struct hfs_cat_entry *entry)
+static inline void insert_hash(struct hfs_cat_entry *entry)
 {
-	struct hfs_cat_entry **h;
-	h = hash(entry->mdb, &entry->key);
-
-	entry->hash_next = *h;
-	entry->hash_prev = NULL;
-	if (entry->hash_next) {
-		entry->hash_next->hash_prev = entry;
-	}
-	*h = entry;
+	struct list_head *head = hash(entry->mdb, &entry->key);
+	list_add(&entry->hash, head);
 }
 
-/*
- * remove_hash
- *
- * Remove an entry from its hash list (if any).
- */
-static void remove_hash(struct hfs_cat_entry *entry)
+static inline void remove_hash(struct hfs_cat_entry *entry)
 {
-	struct hfs_cat_entry **h;
-
-	if (entry->mdb) {
-		h = hash(entry->mdb, &entry->key);
-
-		if (*h == entry) {
-			*h = entry->hash_next;
-		}
-		if (entry->hash_next) {
-			entry->hash_next->hash_prev = entry->hash_prev;
-		}
-		if (entry->hash_prev) {
-			entry->hash_prev->hash_next = entry->hash_next;
-		}
-		entry->hash_prev = entry->hash_next = NULL;
-	}
-}
-
-/*
- * put_last_free()
- *
- * Move an entry to the end of the free list.
- */
-static inline void put_last_free(struct hfs_cat_entry *entry)
-{
-	remove_free(entry);
-	entry->prev = first_entry->prev;
-	entry->prev->next = entry;
-	entry->next = first_entry;
-	entry->next->prev = entry;
-}
-
-/*
- * grow_entries()
- *
- * Try to allocate more entries, adding them to the free list.
- */
-static int grow_entries(void)
-{
-	struct allocation_unit *tmp;
-	struct hfs_cat_entry * entry;
-	int i;
-
-	if (!HFS_NEW(tmp)) {
-		return -ENOMEM;
-	}
-
-	memset(tmp, 0, sizeof(*tmp));
-
-	tmp->next = allocation;
-	allocation = tmp;
-	entry = tmp->entries;
-
-	i = CCACHE_INC;
-
-	nr_entries += i;
-	nr_free_entries += i;
-
-	if (!first_entry) {
-		entry->next = entry->prev = first_entry = entry++;
-		--i;
-	}
-
-	for ( i = CCACHE_INC - 1; i ; --i ) {
-		insert_free(entry++);
-	}
-	return 0;
+	list_del(&entry->hash);
+	INIT_LIST_HEAD(&entry->hash);
 }
 
 /*
@@ -290,7 +177,7 @@
  */
 static inline void wait_on_entry(struct hfs_cat_entry * entry)
 {
-	while (entry->lock) {
+	while ((entry->state & HFS_LOCK)) {
 		hfs_sleep_on(&entry->wait);
 	}
 }
@@ -303,7 +190,9 @@
 static void lock_entry(struct hfs_cat_entry * entry)
 {
 	wait_on_entry(entry);
-	entry->lock = 1;
+	spin_lock(&entry_lock);
+	entry->state |= HFS_LOCK;
+	spin_unlock(&entry_lock);
 }
 
 /*
@@ -313,7 +202,9 @@
  */
 static void unlock_entry(struct hfs_cat_entry * entry)
 {
-	entry->lock = 0;
+	spin_lock(&entry_lock);
+	entry->state &= ~HFS_LOCK;
+	spin_unlock(&entry_lock);
 	hfs_wake_up(&entry->wait);
 }
 
@@ -325,15 +216,171 @@
 static void clear_entry(struct hfs_cat_entry * entry)
 {
 	wait_on_entry(entry);
-	remove_hash(entry);
-	remove_free(entry);
-	if (entry->count) {
-		nr_free_entries++;
-	}
 	/* zero all but the wait queue */
-	memset(&entry->next, 0,
-	       sizeof(*entry) - offsetof(struct hfs_cat_entry, next));
-	insert_free(entry);
+	memset(&entry->wait, 0,
+	       sizeof(*entry) - offsetof(struct hfs_cat_entry, wait));
+	INIT_LIST_HEAD(&entry->hash);
+	INIT_LIST_HEAD(&entry->list);
+	INIT_LIST_HEAD(&entry->dirty);
+}
+
+/* put entry on mdb dirty list. this only does it if it's on the hash
+ * list. we also add it to the global dirty list as well. */
+void hfs_cat_mark_dirty(struct hfs_cat_entry *entry)
+{
+        struct hfs_mdb *mdb = entry->mdb;
+
+	spin_lock(&entry_lock);
+	if (!(entry->state & HFS_DIRTY)) {
+	        entry->state |= HFS_DIRTY;
+
+		/* Only add valid (ie hashed) entries to the
+		 * dirty list */
+		if (!list_empty(&entry->hash)) {
+		        list_del(&entry->list);
+			list_add(&entry->list, &mdb->entry_dirty);
+			INIT_LIST_HEAD(&entry->dirty);
+			list_add(&entry->dirty, &entry_dirty);
+		}
+	}
+	spin_unlock(&entry_lock);
+}
+
+/* prune all entries */
+static void dispose_list(struct list_head *head)
+{
+        struct list_head *next;
+	int count = 0;
+
+	next = head->next;
+	for (;;) {
+		struct list_head * tmp = next;
+
+		next = next->next;
+		if (tmp == head)
+			break;
+		hfs_cat_prune(list_entry(tmp, struct hfs_cat_entry, list));
+		count++;
+	}
+}
+
+/*
+ * try_to_free_entries works by getting the underlying 
+ * cache system to release entries. it gets called with the entry lock
+ * held. 
+ *
+ * count can be up to 2 due to both a resource and data fork being
+ * listed. we can unuse dirty entries as well.
+ */
+#define CAN_UNUSE(tmp) (((tmp)->count < 3) && ((tmp)->state <= HFS_DIRTY))
+static int try_to_free_entries(const int goal) 
+{
+        struct list_head *tmp, *head = &entry_in_use;
+	LIST_HEAD(freeable);
+	int found = 0, depth = goal << 1;
+
+	/* try freeing from entry_in_use */
+	while ((tmp = head->prev) != head && depth--) {
+	        struct hfs_cat_entry *entry = 
+		  list_entry(tmp, struct hfs_cat_entry, list);
+		list_del(tmp);
+		if (CAN_UNUSE(entry)) {
+		        list_del(&entry->hash);
+			INIT_LIST_HEAD(&entry->hash);
+			list_add(tmp, &freeable);
+			if (++found < goal)
+			       continue;
+			break;
+		}
+		list_add(tmp, head);
+	}
+
+	if (found < goal) { /* try freeing from global dirty list */
+	        head = &entry_dirty;
+		depth = goal << 1;
+		while ((tmp = head->prev) != head && depth--) {
+		        struct hfs_cat_entry *entry = 
+			  list_entry(tmp, struct hfs_cat_entry, dirty);
+			list_del(tmp);
+			if (CAN_UNUSE(entry)) {
+			        list_del(&entry->hash);
+				INIT_LIST_HEAD(&entry->hash);
+				list_del(&entry->list);
+				INIT_LIST_HEAD(&entry->list);
+				list_add(&entry->list, &freeable);
+				if (++found < goal)
+				  continue;
+				break;
+			}
+			list_add(tmp, head);
+		}
+	}
+		
+	if (found) {
+	        spin_unlock(&entry_lock);
+		dispose_list(&freeable);
+		spin_lock(&entry_lock);
+	}
+
+	return found;
+}
+  
+/* init_once */
+static inline void init_once(struct hfs_cat_entry *entry)
+{
+	init_waitqueue(&entry->wait);
+	INIT_LIST_HEAD(&entry->hash);
+	INIT_LIST_HEAD(&entry->list);
+	INIT_LIST_HEAD(&entry->dirty);
+}
+
+/*
+ * grow_entries()
+ *
+ * Try to allocate more entries, adding them to the free list. this returns
+ * with the spinlock held if successful 
+ */
+static struct hfs_cat_entry *grow_entries(struct hfs_mdb *mdb)
+{
+	struct allocation_unit *tmp;
+	struct hfs_cat_entry * entry;
+	int i;
+
+	spin_unlock(&entry_lock);
+	if ((entries_stat.nr_entries < CCACHE_MAX) &&
+	    HFS_NEW(tmp)) {
+	        spin_lock(&entry_lock);
+		memset(tmp, 0, sizeof(*tmp));
+		tmp->next = allocation;
+		allocation = tmp;
+		entry = tmp->entries;
+		for (i = 1; i < CCACHE_INC; i++) {
+		        entry++;
+			init_once(entry);
+		        list_add(&entry->list, &entry_unused);
+		}
+		init_once(tmp->entries);
+
+		entries_stat.nr_entries += CCACHE_INC;
+		entries_stat.nr_free_entries += CCACHE_INC - 1;
+		return tmp->entries;
+	}
+
+	/* allocation failed. do some pruning and try again */
+	spin_lock(&entry_lock);
+	try_to_free_entries(entries_stat.nr_entries >> 2);
+	{
+		struct list_head *tmp = entry_unused.next;
+		if (tmp != &entry_unused) {
+			entries_stat.nr_free_entries--;
+			list_del(tmp);
+			entry = list_entry(tmp, struct hfs_cat_entry, list);
+			return entry;
+		}
+	}
+	spin_unlock(&entry_lock);
+
+	return NULL;
 }
 
 /*
@@ -497,18 +544,13 @@
 	struct hfs_brec brec;
 	int error;
 
-	wait_on_entry(entry);
-	if (!entry->dirt) {
-		return;
-	}
-	if (!entry->deleted) {
-		entry->lock = 1;	
+	if (!(entry->state & HFS_DELETED)) {
 		error = hfs_bfind(&brec, entry->mdb->cat_tree,
 				  HFS_BKEY(&entry->key), HFS_BFIND_WRITE);
 		if (!error) {
-			if (entry->key_dirt) {
+			if ((entry->state & HFS_KEYDIRTY)) {
 				/* key may have changed case due to a rename */
-				entry->key_dirt = 0;
+				entry->state &= ~HFS_KEYDIRTY;
 				if (brec.key->KeyLen != entry->key.KeyLen) {
 					hfs_warn("hfs_write_entry: key length "
 						 "changed!\n");
@@ -531,90 +573,111 @@
 			hfs_warn("hfs_write_entry: unable to write "
 				 "entry %08x\n", entry->cnid);
 		}
-		unlock_entry(entry);
 	}
-	entry->dirt = 0;
 }
 
-/*
- */
-#define CAN_UNUSE(tmp) ((tmp)->count < 3 && !((tmp)->lock || (tmp)->dirt))
-static inline int try_to_free_entries(const int goal)
+
+static struct hfs_cat_entry *find_entry(struct hfs_mdb *mdb,
+					const struct hfs_cat_key *key)
 {
-	hfs_prune_entry(first_entry);
-	return 1;
+	struct list_head *tmp, *head = hash(mdb, key);
+	struct hfs_cat_entry * entry;
+
+	tmp = head;
+	for (;;) {
+		tmp = tmp->next;
+		entry = NULL;
+		if (tmp == head)
+			break;
+		entry = list_entry(tmp, struct hfs_cat_entry, hash);
+		if (entry->mdb != mdb)
+			continue;
+		if (hfs_cat_compare(&entry->key, key))
+			continue;
+		entry->count++;
+		break;
+	}
+
+	return entry;
 }
 
 
-/*
- * get_empty_entry()
- *
- * Allocate an unused entry.
- */
-static inline struct hfs_cat_entry *get_empty_entry(void)
+/* be careful. this gets called with the spinlock held. */
+static struct hfs_cat_entry *get_new_entry(struct hfs_mdb *mdb,
+					   const struct hfs_cat_key *key,
+					   const int read)
 {
-	struct hfs_cat_entry * entry, * best;
-	int i, try = 0;
+	struct hfs_cat_entry *entry;
+	struct list_head *head = hash(mdb, key);
+	struct list_head *tmp = entry_unused.next;
 
-	if ((nr_entries < CCACHE_MAX) &&
-	    (nr_free_entries <= (nr_entries >> 1))) {
-		grow_entries();
-	}
-repeat:
-	entry = first_entry;
-	best = NULL;
-	for (i = nr_entries/2; i > 0; i--,entry = entry->next) {
-		if (!entry->count) {
-			if (!best) {
-				best = entry;
-			}
-			if (!entry->dirt && !entry->lock) {
-				best = entry;
-				break;
-			}
+	if (tmp != &entry_unused) {
+		list_del(tmp);
+		entries_stat.nr_free_entries--;
+		entry = list_entry(tmp, struct hfs_cat_entry, list);
+add_new_entry:
+		list_add(&entry->list, &entry_in_use);
+		list_add(&entry->hash, head);
+		entry->mdb = mdb;
+		entry->count = 1;
+		memcpy(&entry->key, key, sizeof(*key));
+		entry->state = HFS_LOCK;
+		spin_unlock(&entry_lock);
 
-		}
-	}
-	if (!best || best->dirt || best->lock) {
-		if (nr_entries < CCACHE_MAX) {
-			if (grow_entries() == 0) {
-				goto repeat;
-			}
-		}
-	}
-	entry = best;
-	if (!entry) {
-	        if (try++ < 4 && try_to_free_entries(NUM_FREE_ENTRIES)) {
-		        goto repeat;
+		if (read) {
+		   struct hfs_brec brec;
+
+		   if (hfs_bfind(&brec, mdb->cat_tree,
+				 HFS_BKEY(key), HFS_BFIND_READ_EQ)) {
+		        /* uh oh. we failed to read the record */
+		        entry->state |= HFS_DELETED;
+		        goto read_fail;
+		   }
+
+		   read_entry(entry, &brec);
+		   
+		   /* error */
+		   if (!entry->cnid) {
+		        goto read_fail;
+		   }
+
+		   /* we don't have to acquire a spinlock here or
+		    * below for the unlocking bits as we're the first
+		    * user of this entry. */
+		   entry->state &= ~HFS_LOCK;
+		   hfs_wake_up(&entry->wait);
 		}
 
-		hfs_warn("hfs_cat_get: No free entries\n");
-		hfs_sleep_on(&entry_wait);
-		goto repeat;
-	}
-	if (entry->lock) {
-		wait_on_entry(entry);
-		goto repeat;
-	}
-	if (entry->dirt) {
-		write_entry(entry);
-		goto repeat;
-	}
-	if (entry->count) {
-		goto repeat;
+		return entry;
 	}
 
-	clear_entry(entry);
-	entry->count = 1;
-	entry->dirt = 0;
-	entry->deleted = 1;	/* so it gets cleared if discarded */
-
-	nr_free_entries--;
-	if (nr_free_entries < 0) {
-		hfs_warn ("hfs_get_empty_entry: bad free entry count.\n");
-		nr_free_entries = 0;
+	/*
+	 * Uhhuh.. We need to expand. Note that "grow_entries()" will
+	 * release the spinlock, but will return with the lock held
+	 * again if the allocation succeeded.
+	 */
+	entry = grow_entries(mdb);
+	if (entry) {
+		/* We released the lock, so.. */
+		struct hfs_cat_entry * old = find_entry(mdb, key);
+		if (!old)
+			goto add_new_entry;
+		list_add(&entry->list, &entry_unused);
+		entries_stat.nr_free_entries++;
+		spin_unlock(&entry_lock);
+		wait_on_entry(old);
+		return old;
 	}
+
 	return entry;
+
+
+read_fail:
+	remove_hash(entry);
+	entry->state &= ~HFS_LOCK;
+	hfs_wake_up(&entry->wait);
+	hfs_cat_put(entry);
+	return NULL;
 }
 
 /*
@@ -625,81 +688,28 @@
  * and a locked, but uninitialized, entry is returned.
  */
 static struct hfs_cat_entry *get_entry(struct hfs_mdb *mdb,
-				       const struct hfs_cat_key *key, int read)
+				       const struct hfs_cat_key *key,
+				       const int read)
 {
-	struct hfs_cat_entry **h;
 	struct hfs_cat_entry * entry;
-	struct hfs_cat_entry * empty = NULL;
-	struct hfs_brec brec;
 
-	h = hash(mdb, key);
-repeat:
-	for (entry = *h; entry ; entry = entry->hash_next) {
-		if (entry->mdb == mdb && !hfs_cat_compare(&entry->key, key)) {
-			goto found_it;
-		}
-	}
-	if (!empty) {
-		empty = get_empty_entry();
-		if (empty && read &&
-		    hfs_bfind(&brec, mdb->cat_tree,
-			      HFS_BKEY(key), HFS_BFIND_READ_EQ)) {
-			/* If we get here then:
-				1) We got an empty entry.
-				2) We want to read the record.
-				3) We failed to find the record. */
-			hfs_cat_put(empty);
-			empty = NULL;
-		}
-		if (empty) {
-			goto repeat;
-		}
-		return NULL;
-	}
-	entry = empty;
-	entry->deleted = 0;
-	entry->mdb = mdb;
-	memcpy(&entry->key, key, sizeof(*key));
-	put_last_free(entry);
-	insert_hash(entry);
+	spin_lock(&entry_lock);
+	if (!entries_stat.nr_free_entries &&
+	    (entries_stat.nr_entries >= CCACHE_MAX))
+		goto restock;
 
-	entry->lock = 1;
-	if (!read) {
-		/* Return a locked but incomplete entry.  Note that the
-		   caller can tell it is incomplete since entry->cnid = 0. */
-		return entry;
-	}
-	read_entry(entry, &brec);
-	unlock_entry(entry);
-
-	goto return_it;
-
-found_it:
-	if (!entry->count) {
-		nr_free_entries--;
-	}
-	entry->count++;
-	if (empty) {
-		if (read) {
-			hfs_brec_relse(&brec, NULL);
-		}
-		hfs_cat_put(empty);
+search:
+	entry = find_entry(mdb, key);
+	if (!entry) {
+	        return get_new_entry(mdb, key, read);
 	}
+	spin_unlock(&entry_lock);
 	wait_on_entry(entry);
-	if (entry->deleted) {
-		/* The entry was deleted while we slept */
-		hfs_cat_put(entry);
-		hfs_relinquish();
-		goto repeat;
-	}
-
-return_it:
-	if (!entry->cnid) {
-		/* There was an error reading the entry */
-		hfs_cat_put(entry);
-		entry = NULL;
-	}
 	return entry;
+
+restock:
+	try_to_free_entries(8);
+	goto search;
 }
 
 /* 
@@ -738,7 +748,7 @@
 	
 	/* update times and dirt */
 	dir->modify_date = hfs_time();
-	dir->dirt = 1;
+	hfs_cat_mark_dirty(dir);
 }
 
 /*
@@ -813,6 +823,7 @@
 		error = -EIO;
 		goto done;
 	}
+
 	if (entry->cnid) {
 		/* The (unlocked) entry exists in the cache */
 		error = -EEXIST;
@@ -869,8 +880,7 @@
 	goto done;
 
 bail1:
-	entry->deleted = 1;
-	remove_hash(entry);
+	entry->state |= HFS_DELETED;
 	unlock_entry(entry);
 bail2:
 	hfs_cat_put(entry);
@@ -893,64 +903,66 @@
  */
 void hfs_cat_put(struct hfs_cat_entry * entry)
 {
-	if (!entry) {
-		return;
-	}
-	wait_on_entry(entry);
-	if (!entry->count) {
-		hfs_warn("hfs_cat_put: trying to free free entry\n");
-		return;
-	}
-repeat:
-	if (entry->count > 1) {
-		entry->count--;
-		return;
-	}
+	if (entry) {
+	        wait_on_entry(entry);
 
-	if (!entry->cnid) {
-		clear_entry(entry);
-		return;
-	}
-
-	if (entry->type == HFS_CDR_FIL) {
-		if (entry->deleted) {
-			/* free all extents */
-			entry->u.file.data_fork.lsize = 0;
-			hfs_extent_adj(&entry->u.file.data_fork);
-			entry->u.file.rsrc_fork.lsize = 0;
-			hfs_extent_adj(&entry->u.file.rsrc_fork);
-		} else {
-			/* clear out any cached extents */
-			if (entry->u.file.data_fork.first.next) {
-				hfs_extent_free(&entry->u.file.data_fork);
-				wait_on_entry(entry);
-				goto repeat;
+		if (!entry->count) {/* just in case */
+		  hfs_warn("hfs_cat_put: trying to free free entry: %p\n",
+			   entry);
+		  return;
+		}
+
+		spin_lock(&entry_lock);
+		if (!--entry->count) {
+repeat:		  
+		        if ((entry->state & HFS_DELETED)) {
+				if (entry->type == HFS_CDR_FIL) {
+				  /* free all extents */
+				  entry->u.file.data_fork.lsize = 0;
+				  hfs_extent_adj(&entry->u.file.data_fork);
+				  entry->u.file.rsrc_fork.lsize = 0;
+				  hfs_extent_adj(&entry->u.file.rsrc_fork);
+				}
+				entry->state = 0;
+			} else if (entry->type == HFS_CDR_FIL) {
+		                /* clear out any cached extents */
+			        if (entry->u.file.data_fork.first.next) {
+				  hfs_extent_free(&entry->u.file.data_fork);
+				  spin_unlock(&entry_lock);
+				  wait_on_entry(entry);
+				  spin_lock(&entry_lock);
+				  goto repeat;
+				}
+				if (entry->u.file.rsrc_fork.first.next) {
+				  hfs_extent_free(&entry->u.file.rsrc_fork);
+				  spin_unlock(&entry_lock);
+				  wait_on_entry(entry);
+				  spin_lock(&entry_lock);
+				  goto repeat;
+				}
 			}
-			if (entry->u.file.rsrc_fork.first.next) {
-				hfs_extent_free(&entry->u.file.rsrc_fork);
-				wait_on_entry(entry);
+
+			/* if we put a dirty entry, write it out. */
+			if ((entry->state & HFS_DIRTY)) {
+				list_del(&entry->dirty);
+				INIT_LIST_HEAD(&entry->dirty);
+				spin_unlock(&entry_lock);
+				write_entry(entry);
+				spin_lock(&entry_lock);
+				entry->state &= ~HFS_DIRTY;
 				goto repeat;
 			}
-		}
-	}
-
-	if (entry->deleted) {
-		clear_entry(entry);
-		return;
-	}
 
-	if (entry->dirt) {
-		write_entry(entry);
-		goto repeat;
+			list_del(&entry->hash);
+			list_del(&entry->list);
+			spin_unlock(&entry_lock);
+			clear_entry(entry);
+			spin_lock(&entry_lock);
+			list_add(&entry->list, &entry_unused);
+			entries_stat.nr_free_entries++;
+		}
+		spin_unlock(&entry_lock);
 	}
-
-	entry->count--;
-	nr_free_entries++;
-
-	/* get_empty_entry() could be blocked waiting for more entries */
-	hfs_wake_up(&entry_wait);
-
-	return;
 }
 
 /* 
@@ -965,6 +977,40 @@
 	return get_entry(mdb, key, 1);
 }
 
+/* invalidate all entries for a device */
+static void invalidate_list(struct list_head *head, struct hfs_mdb *mdb,
+			    struct list_head *dispose)
+{
+        struct list_head *next;
+
+	next = head->next;
+	for (;;) {
+	        struct list_head *tmp = next;
+		struct hfs_cat_entry * entry;
+		
+		next = next->next;
+		if (tmp == head)
+		        break;
+		entry = list_entry(tmp, struct hfs_cat_entry, list);
+		if (entry->mdb != mdb) {
+			continue;
+		}
+		if (!entry->count) {
+		        list_del(&entry->hash);
+			INIT_LIST_HEAD(&entry->hash);
+			list_del(&entry->dirty);
+			INIT_LIST_HEAD(&entry->dirty);
+			list_del(&entry->list);
+			list_add(&entry->list, dispose);
+			continue;
+		}
+		hfs_warn("hfs_fs: entry %p(%u:%lu) busy on removed device %s.\n",
+			 entry, entry->count, entry->state,
+			 hfs_mdb_name(entry->mdb->sys_mdb));
+	}
+
+}
+
 /* 
  * hfs_cat_invalidate()
  *
@@ -973,23 +1019,14 @@
  */
 void hfs_cat_invalidate(struct hfs_mdb *mdb)
 {
-	struct hfs_cat_entry * entry, * next;
-	int i;
+	LIST_HEAD(throw_away);
 
-	next = first_entry;
-	for (i = nr_entries ; i > 0 ; i--) {
-		entry = next;
-		next = entry->next;	/* clear_entry() changes the queues.. */
-		if (entry->mdb != mdb) {
-			continue;
-		}
-		if (entry->count || entry->dirt || entry->lock) {
-			hfs_warn("hfs_fs: entry busy on removed device %s.\n",
-			         hfs_mdb_name(entry->mdb->sys_mdb));
-			continue;
-		}
-		clear_entry(entry);
-	}
+	spin_lock(&entry_lock);
+	invalidate_list(&entry_in_use, mdb, &throw_away);
+	invalidate_list(&mdb->entry_dirty, mdb, &throw_away);
+	spin_unlock(&entry_lock);
+
+	dispose_list(&throw_away); 
 }
 
 /*
@@ -999,22 +1036,40 @@
  */
 void hfs_cat_commit(struct hfs_mdb *mdb)
 {
-	int i;
+        struct list_head *tmp, *head = &mdb->entry_dirty;
 	struct hfs_cat_entry * entry;
 
-	entry = first_entry;
-	for(i = 0; i < nr_entries*2; i++, entry = entry->next) {
-		if (mdb && entry->mdb != mdb) {
-			continue;
-		}
-		if (!entry->cnid || entry->deleted) {
-			continue;
-		}
-		wait_on_entry(entry);
-		if (entry->dirt) {
-			write_entry(entry);
+	spin_lock(&entry_lock);
+	while ((tmp = head->prev) != head) {
+	        entry = list_entry(tmp, struct hfs_cat_entry, list);
+		  
+		if ((entry->state & HFS_LOCK)) {
+		        spin_unlock(&entry_lock);
+			wait_on_entry(entry);
+			spin_lock(&entry_lock);
+		} else {
+		       struct list_head *insert = &entry_in_use;
+
+		       if (!entry->count)
+			        insert = entry_in_use.prev;
+		       /* remove from global dirty list */
+		       list_del(&entry->dirty); 
+		       INIT_LIST_HEAD(&entry->dirty);
+
+		       /* add to in_use list */
+		       list_del(&entry->list);
+		       list_add(&entry->list, insert);
+
+		       /* reset DIRTY, set LOCK */
+		       entry->state ^= HFS_DIRTY | HFS_LOCK;
+		       spin_unlock(&entry_lock);
+		       write_entry(entry);
+		       spin_lock(&entry_lock);
+		       entry->state &= ~HFS_LOCK;
+		       hfs_wake_up(&entry->wait);
 		}
 	}
+	spin_unlock(&entry_lock);
 }
 
 /*
@@ -1191,7 +1246,7 @@
 	int error;
 
 	lock_entry(entry);
-	if (!entry->deleted) {
+	if (!(entry->state & HFS_DELETED)) {
 		hfs_cat_build_key(hfs_get_nl(entry->key.ParID), NULL, &key);
 		error = hfs_bfind(&brec, mdb->cat_tree,
 				  HFS_BKEY(&key), HFS_BFIND_READ_EQ);
@@ -1303,8 +1358,8 @@
 
 	/* try to delete the file or directory */
 	if (!error) {
-		lock_entry(entry);
-		if (entry->deleted) {
+	        lock_entry(entry);
+		if ((entry->state & HFS_DELETED)) {
 			/* somebody beat us to it */
 			error = -ENOENT;
 		} else {
@@ -1316,7 +1371,7 @@
 
 	if (!error) {
 		/* Mark the entry deleted and remove it from the cache */
-		entry->deleted = 1;
+		entry->state |= HFS_DELETED;
 		remove_hash(entry);
 
 		/* try to delete the thread entry if it exists */
@@ -1446,8 +1501,7 @@
 				    &new_record, is_dir ? 2 + sizeof(DIR_REC) :
 							  2 + sizeof(FIL_REC));
 		if (error == -EEXIST) {
-			dest->deleted = 1;
-			remove_hash(dest);
+			dest->state |= HFS_DELETED;
 			unlock_entry(dest);
 			hfs_cat_put(dest);
 			goto restart;
@@ -1461,8 +1515,8 @@
 have_distinct:
 		/* The destination exists and is not same as source */
 		lock_entry(dest);
-		if (dest->deleted) {
-			unlock_entry(dest);
+		if ((dest->state & HFS_DELETED)) {
+		        unlock_entry(dest);
 			hfs_cat_put(dest);
 			goto restart;
 		}
@@ -1480,14 +1534,13 @@
 		}
 	} else {
 		/* The destination exists but is same as source */
-	  /*--entry->count;*/
-		hfs_cat_put(dest);
+	        --entry->count;
 		dest = NULL;
 	}
 
 	/* lock the entry */
 	lock_entry(entry);
-	if (entry->deleted) {
+	if ((entry->state & HFS_DELETED)) {
 		error = -ENOENT;
 		goto bail1;
 	}
@@ -1519,7 +1572,7 @@
 			} else {
 				/* We were lied to! */
 				entry->u.file.flags &= ~HFS_FIL_THD;
-				entry->dirt = 1;
+				hfs_cat_mark_dirty(entry);
 			}
 		}
 		if (!error) {
@@ -1537,7 +1590,7 @@
 			/* Something went seriously wrong.
 			   The dir/file has been deleted. */
 			/* XXX try some recovery? */
-			entry->deleted = 1;
+			entry->state |= HFS_DELETED;
 			remove_hash(entry);
 			goto bail1;
 		}
@@ -1554,20 +1607,20 @@
 
 	/* update directories */
 	new_dir->modify_date = hfs_time();
-	new_dir->dirt = 1;
+	hfs_cat_mark_dirty(new_dir);
 
 	/* update key */
 	remove_hash(entry);
 	memcpy(&entry->key, new_key, sizeof(*new_key));
-	entry->key_dirt = 1; /* Since case might differ */
-	entry->dirt = 1;
+	/* KEYDIRTY as case might differ */
+	entry->state |= HFS_KEYDIRTY;
 	insert_hash(entry);
+	hfs_cat_mark_dirty(entry);
 	unlock_entry(entry);
 
 	/* delete any pre-existing or place-holder entry */
 	if (dest) {
-		dest->deleted = 1;
-		remove_hash(dest);
+		dest->state |= HFS_DELETED;
 		unlock_entry(dest);
 		if (removed && dest->cnid) {
 			*removed = dest;
@@ -1586,8 +1639,7 @@
 			(void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(new_key));
 			update_dir(mdb, new_dir, is_dir, -1);
 bail3:
-			dest->deleted = 1;
-			remove_hash(dest);
+			dest->state |= HFS_DELETED;
 		}
 		unlock_entry(dest);
 		hfs_cat_put(dest);
@@ -1602,3 +1654,21 @@
 
 	return error;
 }
+
+/*
+ * Initialize the hash tables
+ */
+void hfs_cat_init(void)
+{
+	int i;
+	struct list_head *head = hash_table;
+
+        i = CCACHE_NR;
+        do {
+                INIT_LIST_HEAD(head);
+                head++;
+                i--;
+        } while (i);
+}
+
+
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/dir_cap.c linux/fs/hfs/dir_cap.c
--- v2.1.78/linux/fs/hfs/dir_cap.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/dir_cap.c	Mon Jan 12 14:46:24 1998
@@ -1,5 +1,4 @@
-/*
- * linux/fs/hfs/dir_cap.c
+/* linux/fs/hfs/dir_cap.c
  *
  * Copyright (C) 1995-1997  Paul H. Hargrove
  * This file may be distributed under the terms of the GNU Public License.
@@ -222,6 +221,7 @@
 	}
 
 done:
+	dentry->d_op = &hfs_dentry_operations;
 	d_add(dentry, inode);
 	return 0;
 }
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/dir_dbl.c linux/fs/hfs/dir_dbl.c
--- v2.1.78/linux/fs/hfs/dir_dbl.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/dir_dbl.c	Mon Jan 12 14:46:24 1998
@@ -180,6 +180,7 @@
 	}
 	
 done:
+	dentry->d_op = &hfs_dentry_operations;
 	d_add(dentry, inode);
 	return 0;
 }
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/dir_nat.c linux/fs/hfs/dir_nat.c
--- v2.1.78/linux/fs/hfs/dir_nat.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/dir_nat.c	Mon Jan 12 14:46:24 1998
@@ -205,6 +205,7 @@
 	}
 
 done:
+	dentry->d_op = &hfs_dentry_operations;
 	d_add(dentry, inode);
 	return 0;
 }
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/extent.c linux/fs/hfs/extent.c
--- v2.1.78/linux/fs/hfs/extent.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/extent.c	Mon Jan 12 14:46:24 1998
@@ -693,10 +693,8 @@
 				fork->lsize =
 					fork->psize >> HFS_SECTOR_SIZE_BITS;
 			}
-			fork->entry->dirt = 1;
 		} else if (blks < fork->psize) {
 			shrink_fork(fork, ablocks);
-			fork->entry->dirt = 1;
 		}
 	}
 }
@@ -738,7 +736,6 @@
 	if (block >= fork->psize) {
 		if (create) {
 			grow_fork(fork, ablock + 1);
-			fork->entry->dirt = 1;
 		} else {
 			return 0;
 		}
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/file.c linux/fs/hfs/file.c
--- v2.1.78/linux/fs/hfs/file.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/file.c	Mon Jan 12 14:46:24 1998
@@ -97,7 +97,12 @@
 		/* If writing the block, then we have exclusive access
 		   to the file until we return, so it can't have moved.
 		*/
-		return tmp ? getblk(dev, tmp, HFS_SECTOR_SIZE) : NULL;
+	       if (tmp) {
+	               hfs_cat_mark_dirty(fork->entry);
+		       return getblk(dev, tmp, HFS_SECTOR_SIZE);
+	       }
+	       return NULL;
+
 	} else {
 		/* If reading the block, then retry since the
 		   location on disk could have changed while
@@ -235,6 +240,7 @@
 
 	fork->lsize = inode->i_size;
 	hfs_extent_adj(fork);
+	hfs_cat_mark_dirty(HFS_I(inode)->entry);
 
 	inode->i_size = fork->lsize;
 	inode->i_blocks = fork->psize;
@@ -491,7 +497,7 @@
 			fork->lsize = pos;
 		}
 		entry->modify_date = hfs_u_to_mtime(CURRENT_TIME);
-		entry->dirt = 1;
+		hfs_cat_mark_dirty(entry);
 	}
 	return written;
 }
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/file_cap.c linux/fs/hfs/file_cap.c
--- v2.1.78/linux/fs/hfs/file_cap.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/file_cap.c	Mon Jan 12 14:46:24 1998
@@ -241,7 +241,7 @@
 		/* Update finder attributes if changed */
 		if (OVERLAPS(pos, end, struct hfs_cap_info, fi_fndr)) {
 			memcpy(&entry->info, meta.fi_fndr, 32);
-			entry->dirt = 1;
+			hfs_cat_mark_dirty(entry);
 		}
 
 		/* Update file flags if changed */
@@ -259,7 +259,7 @@
 
 			if (new_flags != entry->u.file.flags) {
 				entry->u.file.flags = new_flags;
-				entry->dirt = 1;
+				hfs_cat_mark_dirty(entry);
 				hfs_file_fix_mode(entry);
 			}
 		}
@@ -268,14 +268,14 @@
 		if (OVERLAPS(pos, end, struct hfs_cap_info, fi_ctime)) {
 			entry->create_date =
 				hfs_h_to_mtime(hfs_get_nl(meta.fi_ctime));
-			entry->dirt = 1;
+			hfs_cat_mark_dirty(entry);
 		}
 
 		/* Update MdDat if changed */
 		if (OVERLAPS(pos, end, struct hfs_cap_info, fi_mtime)) {
 			entry->modify_date =
 				hfs_h_to_mtime(hfs_get_nl(meta.fi_mtime));
-			entry->dirt = 1;
+			hfs_cat_mark_dirty(entry);
 		}
 	}
 
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/file_hdr.c linux/fs/hfs/file_hdr.c
--- v2.1.78/linux/fs/hfs/file_hdr.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/file_hdr.c	Mon Jan 12 14:46:24 1998
@@ -288,10 +288,12 @@
 		    (descr->length != entry->u.file.data_fork.lsize)) {
 			entry->u.file.data_fork.lsize = descr->length;
 			hfs_extent_adj(&entry->u.file.data_fork);
+			hfs_cat_mark_dirty(entry);
 		} else if ((descr->id == HFS_HDR_RSRC) &&
 			   (descr->length != entry->u.file.rsrc_fork.lsize)) {
 			entry->u.file.rsrc_fork.lsize = descr->length;
 			hfs_extent_adj(&entry->u.file.rsrc_fork);
+			hfs_cat_mark_dirty(entry);
 		}
 	}
 }
@@ -326,37 +328,37 @@
 		tmp = hfs_u_to_mtime(ntohl(dates[0]));
 		if (entry->create_date != tmp) {
 			entry->create_date = tmp;
-			entry->dirt = 1;
+			hfs_cat_mark_dirty(entry);
 		}
 		tmp = hfs_u_to_mtime(ntohl(dates[1]));
 		if (entry->modify_date != tmp) {
 			entry->modify_date = tmp;
 			inode->i_ctime = inode->i_atime = inode->i_mtime =
 				ntohl(dates[1]);
-			entry->dirt = 1;
+			hfs_cat_mark_dirty(entry);
 		}
 		tmp = hfs_u_to_mtime(ntohl(dates[2]));
 		if (entry->backup_date != tmp) {
 			entry->backup_date = tmp;
-			entry->dirt = 1;
+			hfs_cat_mark_dirty(entry);
 		}
 	} else {
 		tmp = hfs_h_to_mtime(dates[0]);
 		if (entry->create_date != tmp) {
 			entry->create_date = tmp;
-			entry->dirt = 1;
+			hfs_cat_mark_dirty(entry);
 		}
 		tmp = hfs_h_to_mtime(dates[1]);
 		if (entry->modify_date != tmp) {
 			entry->modify_date = tmp;
 			inode->i_ctime = inode->i_atime = inode->i_mtime = 
 				hfs_h_to_utime(dates[1]);
-			entry->dirt = 1;
+			hfs_cat_mark_dirty(entry);
 		}
 		tmp = hfs_h_to_mtime(dates[2]);
 		if (entry->backup_date != tmp) {
 			entry->backup_date = tmp;
-			entry->dirt = 1;
+			hfs_cat_mark_dirty(entry);
 		}
 	}
 }
@@ -800,7 +802,7 @@
 
 				if (new_flags != entry->u.file.flags) {
 					entry->u.file.flags = new_flags;
-					entry->dirt = 1;
+					hfs_cat_mark_dirty(entry);
 					hfs_file_fix_mode(entry);
 				}
 			}
@@ -811,7 +813,7 @@
 			break;
 
 		case HFS_HDR_FINFO:
-			entry->dirt = 1;
+			hfs_cat_mark_dirty(entry);
 			break;
 
 		case HFS_HDR_MACI:
@@ -820,7 +822,7 @@
 
 				if (entry->u.dir.flags != new_flags) {
 					entry->u.dir.flags = new_flags;
-					entry->dirt = 1;
+					hfs_cat_mark_dirty(entry);
 				}
 			} else {
 				hfs_u8 new_flags = tmp[3];
@@ -828,7 +830,7 @@
 				
 				if (changed) {
 					entry->u.file.flags = new_flags;
-					entry->dirt = 1;
+					hfs_cat_mark_dirty(entry);
 					if (changed & HFS_FIL_LOCK) {
 						hfs_file_fix_mode(entry);
 					}
@@ -932,6 +934,7 @@
 		if (fork->lsize != descr->length) {
 			fork->lsize = descr->length;
 			hfs_extent_adj(fork);
+			hfs_cat_mark_dirty(HFS_I(inode)->entry);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/hfs.h linux/fs/hfs/hfs.h
--- v2.1.78/linux/fs/hfs/hfs.h	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/hfs.h	Mon Jan 12 14:46:24 1998
@@ -11,6 +11,7 @@
 #define _HFS_H
 
 #include <linux/hfs_sysdep.h>
+#include <linux/hfs_fs.h>
 
 #define HFS_NEW(X)	((X) = hfs_malloc(sizeof(*(X))))
 #define HFS_DELETE(X)	{ hfs_free((X), sizeof(*(X))); (X) = NULL; }
@@ -255,6 +256,7 @@
 	int			rename_lock;
 	hfs_wait_queue		bitmap_wait;
 	int			bitmap_lock;
+        struct list_head        entry_dirty;
 };
 
 /*
@@ -339,10 +341,9 @@
  */
 struct hfs_cat_entry {
 	hfs_wait_queue		wait;
-	struct hfs_cat_entry	*next;
-	struct hfs_cat_entry	*prev;
-	struct hfs_cat_entry	*hash_next;
-	struct hfs_cat_entry	*hash_prev;
+        struct list_head        hash;
+        struct list_head        list;
+        struct list_head        dirty;
 	struct hfs_mdb		*mdb;
 	hfs_sysentry		sys_entry;
 	struct hfs_cat_key	key;
@@ -352,10 +353,7 @@
 	hfs_u32			modify_date;	/* In network byte-order */
 	hfs_u32			backup_date;	/* In network byte-order */
 	unsigned short		count;
-	unsigned char		lock;
-	unsigned char		dirt;
-	unsigned char		key_dirt;
-	unsigned char		deleted;
+        unsigned long           state;
 	hfs_u8			type;
 	union {
 		struct hfs_dir	dir;
@@ -363,6 +361,13 @@
 	} u;
 };
 
+/* hfs entry state bits */
+#define HFS_DIRTY        1
+#define HFS_KEYDIRTY     2
+#define HFS_LOCK         4
+#define HFS_DELETED      8
+#define HFS_SUPERBLK    16
+
 /* 
  * struct hfs_bnode_ref
  *
@@ -433,7 +438,9 @@
 extern void hfs_btree_commit(struct hfs_btree *, hfs_byte_t *, hfs_lword_t);
 
 /* catalog.c */
+extern void hfs_cat_init(void);
 extern void hfs_cat_put(struct hfs_cat_entry *);
+extern void hfs_cat_mark_dirty(struct hfs_cat_entry *);
 extern struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *,
 					 const struct hfs_cat_key *);
 
@@ -485,6 +492,41 @@
 extern void hfs_tolower(unsigned char *, int);
 
 /* sysdep.c */
-extern int hfs_prune_entry(struct hfs_cat_entry *);
+extern void hfs_cat_prune(struct hfs_cat_entry *);
+
+extern __inline__ struct dentry 
+*hfs_lookup_dentry(const char *name, const int len, 
+		   struct dentry *base)
+{
+  struct qstr this;
+
+  this.name = name;
+  this.len = len;
+  this.hash = full_name_hash(name, len);
+
+  return d_lookup(base, &this);
+}
+
+/* drop a dentry for one of the special subdirectories */
+extern __inline__ void hfs_drop_special(const struct hfs_name *name,
+					struct dentry *base,
+					struct dentry *dentry)
+{
+  struct dentry *dparent, *de;
+  
+  dparent = hfs_lookup_dentry(name->Name, name->Len, base);
+  if (dparent) {
+    de = hfs_lookup_dentry(dentry->d_name.name, dentry->d_name.len,
+			   dparent);
+    dput(dparent);
+
+    if (de) {
+      if (!de->d_inode)
+	d_drop(de);
+      dput(de);
+    }
+  }
+}
 
+extern struct dentry_operations hfs_dentry_operations;
 #endif
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/inode.c linux/fs/hfs/inode.c
--- v2.1.78/linux/fs/hfs/inode.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/inode.c	Mon Jan 12 14:46:24 1998
@@ -103,8 +103,9 @@
  *     to permissions must be applied to all other in-core inodes which 
  *     correspond to the same HFS file.
  */
-int hfs_notify_change(struct inode * inode, struct iattr * attr)
+int hfs_notify_change(struct dentry *dentry, struct iattr * attr)
 {
+	struct inode *inode = dentry->d_inode;
 	struct hfs_cat_entry *entry = HFS_I(inode)->entry;
 	struct dentry **de = entry->sys_entry;
 	struct hfs_sb_info *hsb = HFS_SB(inode->i_sb);
@@ -151,7 +152,7 @@
 
 	/* We must change all in-core inodes corresponding to this file. */
 	for (i = 0; i < 4; ++i) {
-	  if (de[i] && (de[i]->d_inode != inode)) {
+	  if (de[i] && (de[i] != dentry)) {
 			inode_setattr(de[i]->d_inode, attr);
 	  }
 	}
@@ -159,7 +160,7 @@
 	/* Change the catalog entry if needed */
 	if (attr->ia_valid & ATTR_MTIME) {
 		entry->modify_date = hfs_u_to_mtime(inode->i_mtime);
-		entry->dirt = 1;
+		hfs_cat_mark_dirty(entry);
 	}
 	if (attr->ia_valid & ATTR_MODE) {
 		hfs_u8 new_flags;
@@ -172,7 +173,7 @@
 
 		if (new_flags != entry->u.file.flags) {
 			entry->u.file.flags = new_flags;
-			entry->dirt = 1;
+			hfs_cat_mark_dirty(entry);
 		}
 	}
 	/* size changes handled in hfs_extent_adj() */
@@ -211,6 +212,8 @@
  * XXX: Both this function and NFS's corresponding nfs_fhget() would
  * benefit from a way to pass an additional (void *) through iget() to
  * the VFS read_inode() function.
+ *
+ * hfs_iget no longer touches hfs_cat_entries.
  */
 struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type,
 		       struct dentry *dentry)
@@ -239,20 +242,21 @@
 	if (*sys_entry && (inode = (*sys_entry)->d_inode)) {
 		/* There is an existing inode for this file/dir.  Use it. */
 		++inode->i_count;
-		hfs_cat_put(entry);
-	} else if (!(inode = iget(sb, ntohl(entry->cnid) | type)) ||
-		   (inode->i_dev != sb->s_dev)) {
-		/* Something went wrong */
-		hfs_cat_put(entry);
+		return inode;
+	}
+
+	if (!(inode = iget(sb, ntohl(entry->cnid) | type)))
+	        return NULL;
+
+	if (inode->i_dev != sb->s_dev) {
+	        iput(inode);
+		inode = NULL;
 	} else if (inode->i_mode) {
 		/* The inode has been initialized by another process.
 		   Note that if hfs_put_inode() is sleeping in hfs_cat_put()
 		   then we still need to attach it to the entry. */
-		if (*sys_entry) {
-			hfs_cat_put(entry);
-		} else {
+		if (!(*sys_entry))
 			*sys_entry = dentry; /* cache dentry */
-		}
 	} else {
 		/* Initialize the inode */
 		struct hfs_sb_info *hsb = HFS_SB(sb);
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/super.c linux/fs/hfs/super.c
--- v2.1.78/linux/fs/hfs/super.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/super.c	Mon Jan 12 14:46:24 1998
@@ -149,6 +149,8 @@
  * This is the statfs() entry in the super_operations structure for
  * HFS filesystems.  The purpose is to return various data about the
  * filesystem.
+ *
+ * XXX: changed f_files/f_ffree to reflect the fs_ablock/free_ablocks.
  */
 static int hfs_statfs(struct super_block *sb, struct statfs *buf, int len)
 {
@@ -160,8 +162,8 @@
 	tmp.f_blocks = mdb->alloc_blksz * mdb->fs_ablocks;
 	tmp.f_bfree = mdb->alloc_blksz * mdb->free_ablocks;
 	tmp.f_bavail = tmp.f_bfree;
-	tmp.f_files = -1; /* According to the statfs manual page, -1 is the  */
-	tmp.f_ffree = -1; /* correct value when the meaning is undefined.    */
+	tmp.f_files = mdb->fs_ablocks;   /* According to the statfs manual page, -1 is the  */
+	tmp.f_ffree = mdb->free_ablocks; /* correct value when the meaning is undefined. */ 
 	tmp.f_namelen = HFS_NAMELEN;
 
 	return copy_to_user(buf, &tmp, len) ? -EFAULT : 0;
@@ -436,6 +438,7 @@
 		goto bail2;
 	}
 	HFS_SB(s)->s_mdb = mdb;
+	INIT_LIST_HEAD(&mdb->entry_dirty);
 
 	if (HFS_ITYPE(mdb->next_id) != 0) {
 		hfs_warn("hfs_fs: too many files.\n");
@@ -462,6 +465,11 @@
 	if (!s->s_root) 
 		goto bail_no_root;
 
+	/* HFS_SUPERBLK prevents the root inode from being flushed 
+	 * inadvertantly. */
+	HFS_I(root_inode)->entry->state = HFS_SUPERBLK;
+	s->s_root->d_op = &hfs_dentry_operations;
+
 	/* everything's okay */
 	unlock_super(s);
 	return s;
@@ -482,6 +490,7 @@
 
 __initfunc(int init_hfs_fs(void))
 {
+        hfs_cat_init();
 	return register_filesystem(&hfs_fs);
 }
 
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/sysdep.c linux/fs/hfs/sysdep.c
--- v2.1.78/linux/fs/hfs/sysdep.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/sysdep.c	Mon Jan 12 14:46:24 1998
@@ -18,6 +18,16 @@
 #include <linux/hfs_fs_i.h>
 #include <linux/hfs_fs.h>
 
+static int hfs_hash_dentry(struct dentry *, struct qstr *);
+static int hfs_compare_dentry (struct dentry *, struct qstr *, struct qstr *);
+struct dentry_operations hfs_dentry_operations =
+{
+	NULL,	                /* d_validate(struct dentry *) */
+	hfs_hash_dentry,	/* d_hash */
+	hfs_compare_dentry,    	/* d_compare */
+	NULL	                /* d_delete(struct dentry *) */
+};
+
 /*
  * hfs_buffer_get()
  *
@@ -43,10 +53,51 @@
 	return tmp;
 }
 
-/* catalog.c needs a re-write. as a kludge, we throw away all of 
- * the dcache if we need more entries. */
-int hfs_prune_entry(struct hfs_cat_entry *entry)
+/* dentry case-handling: just lowercase everything */
+
+/* should we use hfs_strhash? if so, it probably needs to be beefed 
+ * up a little. */
+static int hfs_hash_dentry(struct dentry *dentry, struct qstr *this)
+{
+        unsigned char name[HFS_NAMELEN];
+	int len = this->len;
+	
+	if (len > HFS_NAMELEN)
+	        return 0;
+  
+	strncpy(name, this->name, len);
+	hfs_tolower(name, len);
+	this->hash = full_name_hash(name, len);
+	return 0;
+}
+
+static int hfs_compare_dentry(struct dentry *dentry, struct qstr *a, 
+			      struct qstr *b)
 {
-        shrink_dcache_sb(entry->mdb->sys_mdb);
-	return 1;
+	struct hfs_name s1, s2;
+
+	if (a->len != b->len) return 1;
+
+	if ((s1.Len = s2.Len = a->len) > HFS_NAMELEN)
+	  return 1;
+
+	strncpy(s1.Name, a->name, s1.Len);
+	strncpy(s2.Name, b->name, s2.Len);
+	return hfs_streq(&s1, &s2);
+}
+
+
+/* toss a catalog entry. this does it by dropping the dentry. */
+void hfs_cat_prune(struct hfs_cat_entry *entry)
+{
+        int i;
+
+	for (i = 0; i < 4; i++) {
+	       struct dentry *de = entry->sys_entry[i];
+	       if (de) {
+			dget(de);
+		 	d_drop(de);
+		 	dput(de);
+	       }
+	}
 }
diff -u --recursive --new-file v2.1.78/linux/fs/hfs/version.c linux/fs/hfs/version.c
--- v2.1.78/linux/fs/hfs/version.c	Tue Jan  6 09:37:36 1998
+++ linux/fs/hfs/version.c	Mon Jan 12 14:46:24 1998
@@ -7,4 +7,4 @@
  * This file contains the version string for this release.
  */
 
-const char hfs_version[]="0.96";
+const char hfs_version[]="0.95+asun2";
diff -u --recursive --new-file v2.1.78/linux/fs/minix/namei.c linux/fs/minix/namei.c
--- v2.1.78/linux/fs/minix/namei.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/minix/namei.c	Tue Jan  6 12:26:59 1998
@@ -562,10 +562,11 @@
 	return 0;
 }
 
-int minix_link(struct inode * inode, struct inode * dir,
+int minix_link(struct dentry * old_dentry, struct inode * dir,
 	       struct dentry *dentry)
 {
 	int error;
+	struct inode *inode = old_dentry->d_inode;
 	struct minix_dir_entry * de;
 	struct buffer_head * bh;
 
diff -u --recursive --new-file v2.1.78/linux/fs/namei.c linux/fs/namei.c
--- v2.1.78/linux/fs/namei.c	Tue Jan  6 09:37:37 1998
+++ linux/fs/namei.c	Mon Jan 12 15:03:28 1998
@@ -247,20 +247,13 @@
 
 /*
  * Internal lookup() using the new generic dcache.
- *
- * Note the revalidation: we have to drop the dcache
- * lock when we revalidate, so we need to update the
- * counts around it.
  */
 static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name)
 {
 	struct dentry * dentry = d_lookup(parent, name);
 
 	if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
-		int validated, (*revalidate)(struct dentry *) = dentry->d_op->d_revalidate;
-
-		validated = revalidate(dentry) || d_invalidate(dentry);
-		if (!validated) {
+		if (!dentry->d_op->d_revalidate(dentry) && !d_invalidate(dentry)) {
 			dput(dentry);
 			dentry = NULL;
 		}
@@ -365,7 +358,7 @@
 
 	/* At this point we know we have a real path component. */
 	for(;;) {
-		int len, err;
+		int err;
 		unsigned long hash;
 		struct qstr this;
 		struct inode *inode;
@@ -386,16 +379,14 @@
 			break;
 
 		this.name = name;
-		len = 0;
 		c = *name;
 
 		hash = init_name_hash();
 		do {
-			len++; name++;
 			hash = partial_name_hash(c, hash);
-			c = *name;
+			c = *++name;
 		} while (c && (c != '/'));
-		this.len = len;
+		this.len = name - (const char *) this.name;
 		this.hash = end_name_hash(hash);
 
 		/* remove trailing slashes? */
@@ -840,7 +831,7 @@
 
 	/* Disallow removals of mountpoints. */
 	error = -EBUSY;
-	if (dentry == dir)
+	if (dentry->d_mounts != dentry->d_covers)
 		goto exit_lock;
 
 	error = -EPERM;
diff -u --recursive --new-file v2.1.78/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.1.78/linux/fs/nfs/dir.c	Tue Jan  6 09:37:37 1998
+++ linux/fs/nfs/dir.c	Mon Jan 12 14:38:40 1998
@@ -35,6 +35,7 @@
 extern void nfs_renew_times(struct dentry *);
 
 #define NFS_PARANOIA 1
+/* #define NFS_DEBUG_VERBOSE 1 */
 
 /*
  * Head for a dircache entry. Currently still very simple; when
@@ -458,6 +459,28 @@
 }
 
 /*
+ * Called to free the inode from the dentry. We must flush
+ * any pending writes for this dentry before freeing the inode.
+ */
+static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
+{
+	if (NFS_WRITEBACK(inode)) {
+#ifdef NFS_PARANOIA
+printk("nfs_dentry_iput: pending writes for %s/%s, i_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count);
+#endif
+		while (nfs_find_dentry_request(inode, dentry)) {
+#ifdef NFS_PARANOIA
+printk("nfs_dentry_iput: flushing %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+			nfs_flush_dirty_pages(inode, 0, 0, 0);
+		}
+	}
+	iput(inode);
+}
+
+/*
  * Called when the dentry is being freed to release private memory.
  */
 static void nfs_dentry_release(struct dentry *dentry)
@@ -471,9 +494,53 @@
 	NULL,			/* d_hash */
 	NULL,			/* d_compare */
 	nfs_dentry_delete,	/* d_delete(struct dentry *) */
+	nfs_dentry_iput,	/* d_iput(struct dentry *, struct inode *) */
 	nfs_dentry_release	/* d_release(struct dentry *) */
 };
 
+#ifdef NFS_PARANOIA
+/*
+ * Display all dentries holding the specified inode.
+ */
+static void show_dentry(struct inode * inode)
+{
+	struct dentry *parent = inode->i_sb->s_root;
+	struct dentry *this_parent = parent;
+	struct list_head *next;
+
+repeat:
+	next = this_parent->d_subdirs.next;
+resume:
+	while (next != &this_parent->d_subdirs) {
+		struct list_head *tmp = next;
+		struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
+		next = tmp->next;
+		if (dentry->d_inode == inode) {
+			int unhashed = list_empty(&dentry->d_hash);
+			printk("show_dentry: %s/%s, d_count=%d%s\n",
+				dentry->d_parent->d_name.name,
+				dentry->d_name.name, dentry->d_count,
+				unhashed ? "(unhashed)" : "");
+		}
+		/*
+		 * Descend a level if the d_subdirs list is non-empty.
+		 */
+		if (!list_empty(&dentry->d_subdirs)) {
+			this_parent = dentry;
+			goto repeat;
+		}
+	}
+	/*
+	 * All done at this level ... ascend and resume the search.
+	 */
+	if (this_parent != parent) {
+		next = this_parent->d_child.next; 
+		this_parent = this_parent->d_parent;
+		goto resume;
+	}
+}
+#endif
+
 /*
  * Whenever a lookup succeeds, we know the parent directories
  * are all valid, so we want to update the dentry timestamps.
@@ -531,10 +598,12 @@
 		inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr);
 		if (inode) {
 #ifdef NFS_PARANOIA
-if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink))
+if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink)) {
 printk("nfs_lookup: %s/%s ino=%ld in use, count=%d, nlink=%d\n",
 dentry->d_parent->d_name.name, dentry->d_name.name,
 inode->i_ino, inode->i_count, inode->i_nlink);
+show_dentry(inode);
+}
 #endif
 	    no_entry:
 			d_add(dentry, inode);
@@ -564,10 +633,12 @@
 	inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
 	if (inode) {
 #ifdef NFS_PARANOIA
-if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink))
+if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink)) {
 printk("nfs_instantiate: %s/%s ino=%ld in use, count=%d, nlink=%d\n",
 dentry->d_parent->d_name.name, dentry->d_name.name,
 inode->i_ino, inode->i_count, inode->i_nlink);
+show_dentry(inode);
+}
 #endif
 		d_instantiate(dentry, inode);
 		nfs_renew_times(dentry);
@@ -682,10 +753,6 @@
 	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
-				dentry->d_name.name, &sattr, &fhandle, &fattr);
-
 	/*
 	 * Always drop the dentry, we can't always depend on
 	 * the fattr returned by the server (AIX seems to be
@@ -693,6 +760,9 @@
 	 * depending on potentially bogus information.
 	 */
 	d_drop(dentry);
+	nfs_invalidate_dircache(dir);
+	error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent),
+				dentry->d_name.name, &sattr, &fhandle, &fattr);
 out:
 	return error;
 }
diff -u --recursive --new-file v2.1.78/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v2.1.78/linux/fs/nfs/inode.c	Tue Jan  6 09:37:37 1998
+++ linux/fs/nfs/inode.c	Mon Jan 12 14:38:40 1998
@@ -138,6 +138,7 @@
 	 */
 	nfs_invalidate_dircache_sb(sb);
 
+	kfree(server->hostname);
 	sb->s_dev = 0;
 	unlock_super(sb);
 	MOD_DEC_USE_COUNT;
@@ -222,6 +223,10 @@
 	server->acregmax = data->acregmax*HZ;
 	server->acdirmin = data->acdirmin*HZ;
 	server->acdirmax = data->acdirmax*HZ;
+
+	server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL);
+	if (!server->hostname)
+		goto out_unlock;
 	strcpy(server->hostname, data->hostname);
 
 	/* Which protocol do we use? */
@@ -308,6 +313,7 @@
 
 out_no_xprt:
 	printk("NFS: cannot create RPC transport.\n");
+	kfree(server->hostname);
 out_unlock:
 	unlock_super(sb);
 	goto out_fail;
diff -u --recursive --new-file v2.1.78/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v2.1.78/linux/fs/nfs/nfsroot.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/nfs/nfsroot.c	Fri Jan  9 09:28:06 1998
@@ -58,6 +58,7 @@
  *	Martin Mares	:	Default path now contains host name instead of
  *				host IP address (but host name defaults to IP
  *				address anyway).
+ *	Martin Mares	:	Use root_server_addr appropriately during setup.
  */
 
 #include <linux/types.h>
@@ -86,6 +87,7 @@
 
 /* Parameters passed from the kernel command line */
 static char nfs_root_name[NFS_ROOT_NAME_LEN] __initdata = "default";
+static int nfs_params_parsed = 0;
 
 /* Address of NFS server */
 static __u32 servaddr __initdata = 0;
@@ -157,6 +159,9 @@
 	char *cp, *cq, *options, *val;
 	int octets = 0;
 
+	if (nfs_params_parsed)
+		return nfs_params_parsed;
+
 	/* It is possible to override the server IP number here */
 	cp = cq = name;
 	while (octets < 4) {
@@ -173,17 +178,12 @@
 	if (octets == 4 && (*cp == ':' || *cp == '\0')) {
 		if (*cp == ':')
 			*cp++ = '\0';
-		servaddr = in_aton(name);
+		root_server_addr = in_aton(name);
 		name = cp;
-	} else if ((servaddr = root_server_addr) == INADDR_NONE) {
-		printk(KERN_ERR "Root-NFS: No NFS server available, giving up.\n");
-		return -1;
 	}
 
 	/* Clear the nfs_data structure and setup the server hostname */
 	memset(&nfs_data, 0, sizeof(nfs_data));
-	strncpy(nfs_data.hostname, in_ntoa(servaddr), sizeof(nfs_data.hostname)-1);
-	nfs_data.namlen = strlen(nfs_data.hostname);
 
 	/* Set the name of the directory to mount */
 	if (root_server_path[0] && !strcmp(name, "default"))
@@ -239,11 +239,26 @@
 			cp = strtok(NULL, ",");
 		}
 	}
-	return 0;
+	return 1;
 }
 
 
 /*
+ *  Get NFS server address.
+ */
+__initfunc(static int root_nfs_addr(void))
+{
+	if ((servaddr = root_server_addr) == INADDR_NONE) {
+		printk(KERN_ERR "Root-NFS: No NFS server available, giving up.\n");
+		return -1;
+	}
+
+	strncpy(nfs_data.hostname, in_ntoa(servaddr), sizeof(nfs_data.hostname)-1);
+	nfs_data.namlen = strlen(nfs_data.hostname);
+	return 0;
+}
+
+/*
  *  Tell the user what's going on.
  */
 #ifdef NFSROOT_DEBUG
@@ -274,7 +289,8 @@
 	 * be able to use the client IP address for the remote root
 	 * directory (necessary for pure RARP booting).
 	 */
-	if (root_nfs_name(nfs_root_name) < 0)
+	if (root_nfs_name(nfs_root_name) < 0 ||
+	    root_nfs_addr() < 0)
 		return -1;
 
 #ifdef NFSROOT_DEBUG
@@ -301,6 +317,7 @@
 			line[sizeof(nfs_root_name) - strlen(NFS_ROOT) - 1] = '\0';
 		sprintf(nfs_root_name, NFS_ROOT, line);
 	}
+	nfs_params_parsed = root_nfs_name(nfs_root_name);
 }
 
 
diff -u --recursive --new-file v2.1.78/linux/fs/nfs/proc.c linux/fs/nfs/proc.c
--- v2.1.78/linux/fs/nfs/proc.c	Wed Oct 15 16:04:23 1997
+++ linux/fs/nfs/proc.c	Fri Jan  9 13:52:01 1998
@@ -45,8 +45,8 @@
 
 /*
  * If NFS_DEBUG is defined, you can toggle NFS debugging by causing
- * a lookup of "xyzzy". Just cd to an NFS-mounted filesystem and type
- * 'ls xyzzy' to turn on debugging.
+ * a lookup of "__xyzzy__". Just cd to an NFS-mounted filesystem and type
+ * 'ls __xyzzy__' to turn on debugging.
  */
 #ifdef NFS_DEBUG
 # define NFSDBG_FACILITY	NFSDBG_PROC
@@ -91,7 +91,7 @@
 
 	dprintk("NFS call  lookup %s\n", name);
 #ifdef RPC_DEBUG
-	if (!strcmp(name, "xyzzy"))
+	if (!strcmp(name, "__xyzzy__"))
 		nfs_debug = ~nfs_debug;
 #endif
 	status = rpc_call(server->client, NFSPROC_LOOKUP, &arg, &res, 0);
diff -u --recursive --new-file v2.1.78/linux/fs/nfs/write.c linux/fs/nfs/write.c
--- v2.1.78/linux/fs/nfs/write.c	Tue Jan  6 09:37:37 1998
+++ linux/fs/nfs/write.c	Mon Jan 12 14:38:40 1998
@@ -52,6 +52,7 @@
 #include <linux/malloc.h>
 #include <linux/swap.h>
 #include <linux/pagemap.h>
+
 #include <linux/sunrpc/clnt.h>
 #include <linux/nfs_fs.h>
 #include <asm/uaccess.h>
@@ -66,52 +67,13 @@
  */
 #define IS_SOFT 0
 
+#define NFS_PARANOIA 1
 #define NFSDBG_FACILITY		NFSDBG_PAGECACHE
 
 static void			nfs_wback_lock(struct rpc_task *task);
 static void			nfs_wback_result(struct rpc_task *task);
 
 /*
- * This struct describes a file region to be written.
- * It's kind of a pity we have to keep all these lists ourselves, rather
- * than sticking an extra pointer into struct page.
- */
-struct nfs_wreq {
-	struct rpc_listitem	wb_list;	/* linked list of req's */
-	struct rpc_task		wb_task;	/* RPC task */
-	struct dentry *		wb_dentry;	/* dentry referenced */
-	struct inode *		wb_inode;	/* inode referenced */
-	struct page *		wb_page;	/* page to be written */
-	unsigned int		wb_offset;	/* offset within page */
-	unsigned int		wb_bytes;	/* dirty range */
-	pid_t			wb_pid;		/* owner process */
-	unsigned short		wb_flags;	/* status flags */
-
-	struct nfs_writeargs *	wb_args;	/* NFS RPC stuff */
-	struct nfs_fattr *	wb_fattr;	/* file attributes */
-};
-#define wb_status		wb_task.tk_status
-
-#define WB_NEXT(req)		((struct nfs_wreq *) ((req)->wb_list.next))
-
-/*
- * Various flags for wb_flags
- */
-#define NFS_WRITE_WANTLOCK	0x0001	/* needs to lock page */
-#define NFS_WRITE_LOCKED	0x0002	/* holds lock on page */
-#define NFS_WRITE_CANCELLED	0x0004	/* has been cancelled */
-#define NFS_WRITE_UNCOMMITTED	0x0008	/* written but uncommitted (NFSv3) */
-#define NFS_WRITE_INVALIDATE	0x0010	/* invalidate after write */
-#define NFS_WRITE_INPROGRESS	0x0020	/* RPC call in progress */
-
-#define WB_INPROGRESS(req)	((req)->wb_flags & NFS_WRITE_INPROGRESS)
-#define WB_WANTLOCK(req)	((req)->wb_flags & NFS_WRITE_WANTLOCK)
-#define WB_HAVELOCK(req)	((req)->wb_flags & NFS_WRITE_LOCKED)
-#define WB_CANCELLED(req)	((req)->wb_flags & NFS_WRITE_CANCELLED)
-#define WB_UNCOMMITTED(req)	((req)->wb_flags & NFS_WRITE_UNCOMMITTED)
-#define WB_INVALIDATE(req)	((req)->wb_flags & NFS_WRITE_INVALIDATE)
-
-/*
  * Cache parameters
  */
 #define NFS_WRITEBACK_DELAY	(10 * HZ)
@@ -144,8 +106,13 @@
 	/* async swap-out support */
 	if (test_and_clear_bit(PG_decr_after, &page->flags))
 		atomic_dec(&page->count);
-	if (test_and_clear_bit(PG_swap_unlock_after, &page->flags))
-		swap_after_unlock_page(page->pg_swap_entry);
+	if (test_and_clear_bit(PG_swap_unlock_after, &page->flags)) {
+		/*
+		 * We're doing a swap, so check that this page is
+		 * swap-cached and do the necessary cleanup. 
+		 */
+		swap_after_unlock_page(page->offset);
+	}
 #endif
 }
 
@@ -282,6 +249,27 @@
 }
 
 /*
+ * Find any requests for the specified dentry.
+ */
+int
+nfs_find_dentry_request(struct inode *inode, struct dentry *dentry)
+{
+	struct nfs_wreq	*head, *req;
+	int found = 0;
+
+	req = head = NFS_WRITEBACK(inode);
+	while (req != NULL) {
+		if (req->wb_dentry == dentry) {
+			found = 1;
+			break;
+		}
+		if ((req = WB_NEXT(req)) == head)
+			break;
+	}
+	return found;
+}
+
+/*
  * Find a failed write request by pid
  */
 static struct nfs_wreq *
@@ -400,7 +388,7 @@
 	memset(wreq, 0, sizeof(*wreq));
 
 	task = &wreq->wb_task;
-	rpc_init_task(task, clnt, nfs_wback_result, 0);
+	rpc_init_task(task, clnt, nfs_wback_result, RPC_TASK_NFSWRITE);
 	task->tk_calldata = wreq;
 	task->tk_action = nfs_wback_lock;
 
@@ -612,14 +600,18 @@
 /*
  * Flush out a dirty page.
  */ 
-static inline void
+static void
 nfs_flush_request(struct nfs_wreq *req)
 {
 	struct page	*page = req->wb_page;
 
-	dprintk("NFS:      nfs_flush_request(%x/%ld, @%ld)\n",
-				page->inode->i_dev, page->inode->i_ino,
-				page->offset);
+#ifdef NFS_DEBUG_VERBOSE
+if (req->wb_inode != page->inode)
+printk("NFS: inode %ld no longer has page %p\n", req->wb_inode->i_ino, page);
+#endif
+	dprintk("NFS:      nfs_flush_request(%s/%s, @%ld)\n",
+		req->wb_dentry->d_parent->d_name.name,
+		req->wb_dentry->d_name.name, page->offset);
 
 	req->wb_flags |= NFS_WRITE_WANTLOCK;
 	if (!test_and_set_bit(PG_locked, &page->flags)) {
@@ -656,7 +648,7 @@
 			if (rqoffset < end && offset < rqend
 			 && (pid == 0 || req->wb_pid == pid)) {
 				if (!WB_HAVELOCK(req)) {
-#ifdef NFS_PARANOIA
+#ifdef NFS_DEBUG_VERBOSE
 printk("nfs_flush: flushing inode=%ld, %d @ %lu\n",
 req->wb_inode->i_ino, req->wb_bytes, rqoffset);
 #endif
@@ -664,11 +656,6 @@
 				}
 				last = req;
 			}
-		} else {
-#ifdef NFS_PARANOIA
-printk("nfs_flush_pages: in progress inode=%ld, %d @ %lu\n",
-req->wb_inode->i_ino, req->wb_bytes, rqoffset);
-#endif
 		}
 		if (invalidate)
 			req->wb_flags |= NFS_WRITE_INVALIDATE;
diff -u --recursive --new-file v2.1.78/linux/fs/nfsd/nfsctl.c linux/fs/nfsd/nfsctl.c
--- v2.1.78/linux/fs/nfsd/nfsctl.c	Tue Nov 18 17:22:08 1997
+++ linux/fs/nfsd/nfsctl.c	Mon Jan 12 14:39:48 1998
@@ -222,6 +222,21 @@
 extern int (*do_nfsservctl)(int, void *, void *);
 
 /*
+ * This is called as the fill_inode function when an inode
+ * is going into (fill = 1) or out of service (fill = 0).
+ *
+ * We use it here to make sure the module can't be unloaded
+ * while a /proc inode is in use.
+ */
+void nfsd_modcount(struct inode *inode, int fill)
+{
+	if (fill)
+		MOD_INC_USE_COUNT;
+	else
+		MOD_DEC_USE_COUNT;
+}
+
+/*
  * Initialize the module
  */
 int
diff -u --recursive --new-file v2.1.78/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- v2.1.78/linux/fs/nfsd/nfsfh.c	Sat Nov 29 11:25:12 1997
+++ linux/fs/nfsd/nfsfh.c	Mon Jan 12 14:39:48 1998
@@ -1089,8 +1089,10 @@
 {
 	struct inode * inode = dentry->d_inode;
 
-	dprintk("nfsd: fh_compose(exp %x/%ld dentry %p)\n",
-			exp->ex_dev, exp->ex_ino, dentry);
+	dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n",
+		exp->ex_dev, exp->ex_ino,
+		dentry->d_parent->d_name.name, dentry->d_name.name,
+		(inode ? inode->i_ino : 0));
 
 	/*
 	 * N.B. We shouldn't need to init the fh -- the call to fh_compose
diff -u --recursive --new-file v2.1.78/linux/fs/nfsd/nfsproc.c linux/fs/nfsd/nfsproc.c
--- v2.1.78/linux/fs/nfsd/nfsproc.c	Tue Nov 18 17:22:08 1997
+++ linux/fs/nfsd/nfsproc.c	Mon Jan 12 14:39:48 1998
@@ -56,7 +56,8 @@
 nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle  *argp,
 					  struct nfsd_attrstat *resp)
 {
-	dprintk("nfsd: GETATTR  %p\n", SVCFH_DENTRY(&argp->fh));
+	dprintk("nfsd: GETATTR  %d/%ld\n",
+		SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh));
 
 	fh_copy(&resp->fh, &argp->fh);
 	RETURN(fh_verify(rqstp, &resp->fh, 0, MAY_NOP));
@@ -70,7 +71,9 @@
 nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
 					  struct nfsd_attrstat  *resp)
 {
-	dprintk("nfsd: SETATTR  %p\n", SVCFH_DENTRY(&argp->fh));
+	dprintk("nfsd: SETATTR  %d/%ld, valid=%x, size=%ld\n",
+		SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
+		argp->attrs.ia_valid, (long) argp->attrs.ia_size);
 
 	fh_copy(&resp->fh, &argp->fh);
 	RETURN(nfsd_setattr(rqstp, &resp->fh, &argp->attrs));
@@ -88,7 +91,8 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: LOOKUP   %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
+	dprintk("nfsd: LOOKUP   %d/%ld %s\n",
+		SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh), argp->name);
 
 	nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len,
 				 &resp->fh);
@@ -131,8 +135,8 @@
 	u32 *	buffer;
 	int	nfserr, avail;
 
-	dprintk("nfsd: READ %p %d bytes at %d\n",
-		SVCFH_DENTRY(&argp->fh),
+	dprintk("nfsd: READ    %d/%ld %d bytes at %d\n",
+		SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
 		argp->count, argp->offset);
 
 	/* Obtain buffer pointer for payload. 19 is 1 word for
@@ -168,8 +172,8 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: WRITE    %p %d bytes at %d\n",
-		SVCFH_DENTRY(&argp->fh),
+	dprintk("nfsd: WRITE    %d/%ld %d bytes at %d\n",
+		SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
 		argp->len, argp->offset);
 
 	nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
@@ -192,24 +196,21 @@
 nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
 					 struct nfsd_diropres   *resp)
 {
-	struct inode	*dirp, *inode = NULL;
-	struct iattr	*attr;
-	svc_fh		*dirfhp, *newfhp;
+	svc_fh		*dirfhp = &argp->fh;
+	svc_fh		*newfhp = &resp->fh;
+	struct iattr	*attr = &argp->attrs;
+	struct inode	*inode = NULL;
 	int		nfserr, type, mode;
 	int		rdonly = 0, exists;
 	dev_t		rdev = NODEV;
 
-	dprintk("nfsd: CREATE   %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
-
-	dirfhp = &argp->fh;
-	newfhp = &resp->fh;
-	attr = &argp->attrs;
+	dprintk("nfsd: CREATE   %d/%ld %s\n",
+		SVCFH_DEV(dirfhp), SVCFH_INO(dirfhp), argp->name);
 
 	/* Get the directory inode */
 	nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
 	if (nfserr)
 		goto done; /* must fh_put dirfhp even on error */
-	dirp = dirfhp->fh_dentry->d_inode;
 
 	/* Check for MAY_WRITE separately. */
 	nfserr = nfsd_permission(dirfhp->fh_export, dirfhp->fh_dentry,
@@ -247,10 +248,9 @@
 	}
 
 	/* This is for "echo > /dev/null" a la SunOS. Argh. */
-	if (rdonly && (!exists || type == S_IFREG)) {
-		nfserr = nfserr_rofs;
+	nfserr = nfserr_rofs;
+	if (rdonly && (!exists || type == S_IFREG))
 		goto done;
-	}
 
 	attr->ia_valid |= ATTR_MODE;
 	attr->ia_mode = type | mode;
@@ -292,11 +292,19 @@
 		nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len,
 					attr, type, rdev, newfhp);
 	} else if (type == S_IFREG) {
+		dprintk("nfsd:   existing %s, valid=%x, size=%ld\n",
+			argp->name, attr->ia_valid, (long) attr->ia_size);
 		/* File already exists. We ignore all attributes except
 		 * size, so that creat() behaves exactly like
 		 * open(..., O_CREAT|O_TRUNC|O_WRONLY).
 		 */
+#if 0
+		/* N.B. What is this doing? ignores size?? */
 		if ((attr->ia_valid &= ~(ATTR_SIZE)) != 0)
+			nfserr = nfsd_setattr(rqstp, newfhp, attr);
+#endif
+		attr->ia_valid &= ATTR_SIZE;
+		if (attr->ia_valid)
 			nfserr = nfsd_setattr(rqstp, newfhp, attr);
 	}
 
diff -u --recursive --new-file v2.1.78/linux/fs/nfsd/stats.c linux/fs/nfsd/stats.c
--- v2.1.78/linux/fs/nfsd/stats.c	Sat Nov 29 11:25:12 1997
+++ linux/fs/nfsd/stats.c	Mon Jan 12 14:39:48 1998
@@ -75,8 +75,12 @@
 {
 	struct proc_dir_entry	*ent;
 
-	if ((ent = svc_proc_register(&nfsd_svcstats)) != 0)
+	if ((ent = svc_proc_register(&nfsd_svcstats)) != 0) {
 		ent->read_proc = nfsd_proc_read;
+#ifdef MODULE
+		ent->fill_inode = nfsd_modcount;
+#endif
+	}
 }
 
 void
diff -u --recursive --new-file v2.1.78/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- v2.1.78/linux/fs/nfsd/vfs.c	Tue Jan  6 09:37:37 1998
+++ linux/fs/nfsd/vfs.c	Mon Jan 12 14:39:48 1998
@@ -190,20 +190,24 @@
 	inode = dentry->d_inode;
 
 	/* The size case is special... */
-	if ((iap->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) {
+	if (iap->ia_valid & ATTR_SIZE) {
+if (!S_ISREG(inode->i_mode))
+printk("nfsd_setattr: size change??\n");
 		if (iap->ia_size < inode->i_size) {
 			err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC);
 			if (err != 0)
 				goto out;
 		}
-		if ((err = get_write_access(inode)) != 0)
-			return nfserrno(-err);
+		err = get_write_access(inode);
+		if (err)
+			goto out_nfserr;
+		/* N.B. Should we update the inode cache here? */
 		inode->i_size = iap->ia_size;
 		if (inode->i_op && inode->i_op->truncate)
 			inode->i_op->truncate(inode);
 		mark_inode_dirty(inode);
 		put_write_access(inode);
-		iap->ia_valid &= ATTR_SIZE;
+		iap->ia_valid &= ~ATTR_SIZE;
 		iap->ia_valid |= ATTR_MTIME;
 		iap->ia_mtime = CURRENT_TIME;
 	}
@@ -232,13 +236,17 @@
 		iap->ia_ctime = CURRENT_TIME;
 		err = notify_change(dentry, iap);
 		if (err)
-			return nfserrno(-err);
+			goto out_nfserr;
 		if (EX_ISSYNC(fhp->fh_export))
 			write_inode_now(inode);
 	}
 	err = 0;
 out:
 	return err;
+
+out_nfserr:
+	err = nfserrno(-err);
+	goto out;
 }
 
 /*
diff -u --recursive --new-file v2.1.78/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.1.78/linux/fs/proc/array.c	Fri Jan  2 14:37:02 1998
+++ linux/fs/proc/array.c	Mon Jan 12 14:46:24 1998
@@ -28,6 +28,13 @@
  *
  * Yves Arrouye      :  remove removal of trailing spaces in get_array.
  *			<Yves.Arrouye@marin.fdn.fr>
+
+ * Jerome Forissier  :  added per-cpu time information to /proc/stat
+ *                      and /proc/<pid>/cpu extension
+ *                      <forissier@isia.cma.fr>
+ *			- Incorporation and non-SMP safe operation
+ *			of forissier patch in 2.1.78 by 
+ *			Hans Marcus <crowbar@concepts.nl>
  */
 
 #include <linux/types.h>
@@ -222,6 +229,32 @@
 	ticks = jiffies * smp_num_cpus;
 	for (i = 0 ; i < NR_IRQS ; i++)
 		sum += kstat.interrupts[i];
+#ifdef __SMP__
+	len = sprintf(buffer,
+		"cpu  %u %u %u %lu\n",
+		kstat.cpu_user,
+		kstat.cpu_nice,
+		kstat.cpu_system,
+		jiffies*smp_num_cpus - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system));
+	for (i = 0 ; i < smp_num_cpus; i++)
+		len += sprintf(buffer + len, "cpu%d %u %u %u %lu\n",
+			i,
+			kstat.per_cpu_user[cpu_logical_map[i]],
+			kstat.per_cpu_nice[cpu_logical_map[i]],
+			kstat.per_cpu_system[cpu_logical_map[i]],
+			jiffies - (  kstat.per_cpu_user[cpu_logical_map[i]] \
+			           + kstat.per_cpu_nice[cpu_logical_map[i]] \
+			           + kstat.per_cpu_system[cpu_logical_map[i]]));
+	len += sprintf(buffer + len,
+		"disk %u %u %u %u\n"
+		"disk_rio %u %u %u %u\n"
+		"disk_wio %u %u %u %u\n"
+		"disk_rblk %u %u %u %u\n"
+		"disk_wblk %u %u %u %u\n"
+		"page %u %u\n"
+		"swap %u %u\n"
+		"intr %u",
+#else
 	len = sprintf(buffer,
 		"cpu  %u %u %u %lu\n"
 		"disk %u %u %u %u\n"
@@ -236,6 +269,7 @@
 		kstat.cpu_nice,
 		kstat.cpu_system,
 		ticks - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system),
+#endif
 		kstat.dk_drive[0], kstat.dk_drive[1],
 		kstat.dk_drive[2], kstat.dk_drive[3],
 		kstat.dk_drive_rio[0], kstat.dk_drive_rio[1],
@@ -1077,6 +1111,33 @@
 	return retval;
 }
 
+#ifdef __SMP__
+static int get_pidcpu(int pid, char * buffer)
+{
+	struct task_struct * tsk = current ;
+	int i, len;
+	
+	if (pid != tsk->pid)
+		tsk = find_task_by_pid(pid);
+
+	if (tsk == NULL)
+		return 0;
+
+	len = sprintf(buffer,
+		"cpu  %lu %lu\n",
+		tsk->times.tms_utime,
+		tsk->times.tms_stime);
+		
+	for (i = 0 ; i < smp_num_cpus; i++)
+		len += sprintf(buffer + len, "cpu%d %lu %lu\n",
+			i,
+			tsk->per_cpu_utime[cpu_logical_map[i]],
+			tsk->per_cpu_stime[cpu_logical_map[i]]);
+
+	return len;
+}
+#endif
+
 #ifdef CONFIG_MODULES
 extern int get_module_list(char *);
 extern int get_ksyms_list(char *, char **, off_t, int);
@@ -1206,6 +1267,10 @@
 			return get_stat(pid, page);
 		case PROC_PID_STATM:
 			return get_statm(pid, page);
+#ifdef __SMP__
+		case PROC_PID_CPU:
+			return get_pidcpu(pid, page);
+#endif
 	}
 	return -EBADF;
 }
diff -u --recursive --new-file v2.1.78/linux/fs/proc/base.c linux/fs/proc/base.c
--- v2.1.78/linux/fs/proc/base.c	Wed Oct 15 16:04:23 1997
+++ linux/fs/proc/base.c	Mon Jan 12 14:46:24 1998
@@ -158,6 +158,15 @@
 };
 #endif
 
+#ifdef __SMP__
+static struct proc_dir_entry proc_pid_cpu = {
+	PROC_PID_CPU, 3, "cpu",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations,
+	NULL, proc_pid_fill_inode,
+};
+#endif
+
 void proc_base_init(void)
 {
 #if CONFIG_AP1000
@@ -174,4 +183,17 @@
 	proc_register(&proc_pid, &proc_pid_stat);
 	proc_register(&proc_pid, &proc_pid_statm);
 	proc_register(&proc_pid, &proc_pid_maps);
+#ifdef __SMP__	
+	proc_register(&proc_pid, &proc_pid_cpu);
+#endif	
 };
+
+
+
+
+
+
+
+
+
+
diff -u --recursive --new-file v2.1.78/linux/fs/proc/link.c linux/fs/proc/link.c
--- v2.1.78/linux/fs/proc/link.c	Tue Jan  6 09:37:37 1998
+++ linux/fs/proc/link.c	Tue Jan  6 12:33:11 1998
@@ -146,9 +146,9 @@
 	pattern = NULL;
 	inode = dentry->d_inode;
 	if (inode && dentry->d_parent == dentry) {
-		if (inode->i_sock)
+		if (S_ISSOCK(inode->i_mode))
 			pattern = "socket:[%lu]";
-		if (inode->i_pipe)
+		if (S_ISFIFO(inode->i_mode))
 			pattern = "pipe:[%lu]";
 	}
 	
diff -u --recursive --new-file v2.1.78/linux/fs/proc/root.c linux/fs/proc/root.c
--- v2.1.78/linux/fs/proc/root.c	Tue Jan  6 09:37:37 1998
+++ linux/fs/proc/root.c	Sat Jan 10 10:43:43 1998
@@ -50,6 +50,9 @@
 	NULL			/* can't fsync */
 };
 
+int proc_readlink(struct dentry * dentry, char * buffer, int buflen);
+struct dentry * proc_follow_link(struct dentry *dentry, struct dentry *base);
+
 /*
  * proc directories can do almost nothing..
  */
@@ -332,6 +335,9 @@
 		if (dp->ops == NULL)
 			dp->ops = &proc_dir_inode_operations;
 		dir->nlink++;
+	} else if (S_ISLNK(dp->mode)) {
+		if (dp->ops == NULL)
+			dp->ops = &proc_link_inode_operations;
 	} else {
 		if (dp->ops == NULL)
 			dp->ops = &proc_file_inode_operations;
@@ -389,6 +395,50 @@
 	return lookup_dentry(tmp, base, 1);
 }	
 
+int proc_readlink(struct dentry * dentry, char * buffer, int buflen)
+{
+	struct inode *inode = dentry->d_inode;
+	struct proc_dir_entry * de;
+	char 	*page;
+	int len = 0;
+
+	de = (struct proc_dir_entry *) inode->u.generic_ip;
+	if (!de)
+		return -ENOENT;
+	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+
+	if (de->readlink_proc)
+		len = de->readlink_proc(de, page);
+
+	if (len > buflen)
+		len = buflen;
+
+	copy_to_user(buffer, page, len);
+	free_page((unsigned long) page);
+	return len;
+}
+
+struct dentry * proc_follow_link(struct dentry * dentry, struct dentry *base)
+{
+	struct inode *inode = dentry->d_inode;
+	struct proc_dir_entry * de;
+	char 	*page;
+	struct dentry *d;
+	int len = 0;
+
+	de = (struct proc_dir_entry *) inode->u.generic_ip;
+	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+		return NULL;
+
+	if (de->readlink_proc)
+		len = de->readlink_proc(de, page);
+
+	d = lookup_dentry(page, base, 1);
+	free_page((unsigned long) page);
+	return d;
+}
+
 static struct inode_operations proc_self_inode_operations = {
 	NULL,			/* no file-ops */
 	NULL,			/* create */
@@ -402,6 +452,26 @@
 	NULL,			/* rename */
 	proc_self_readlink,	/* readlink */
 	proc_self_follow_link,	/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
+
+static struct inode_operations proc_link_inode_operations = {
+	NULL,			/* no file-ops */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	proc_readlink,		/* readlink */
+	proc_follow_link,	/* follow_link */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
 	NULL,			/* bmap */
diff -u --recursive --new-file v2.1.78/linux/fs/super.c linux/fs/super.c
--- v2.1.78/linux/fs/super.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/super.c	Mon Jan 12 15:12:00 1998
@@ -1064,7 +1064,11 @@
 		ROOT_DEV = 0;
 		if ((fs_type = get_fs_type("nfs"))) {
 			if ((vfsmnt = add_vfsmnt(ROOT_DEV, "/dev/root", "/"))) {
-				sb = vfsmnt->mnt_sb;
+
+				sb = &super_blocks[0];
+				while (sb->s_dev) sb++;
+				vfsmnt->mnt_sb = sb;
+
 				sb->s_dev = get_unnamed_dev();
 				sb->s_flags = root_mountflags & ~MS_RDONLY;
 				if (nfs_root_mount(sb) >= 0) {
diff -u --recursive --new-file v2.1.78/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v2.1.78/linux/fs/sysv/inode.c	Tue Jan  6 09:37:38 1998
+++ linux/fs/sysv/inode.c	Tue Jan  6 09:48:03 1998
@@ -873,7 +873,7 @@
 }
 
 /* To avoid inconsistencies between inodes in memory and inodes on disk. */
-extern int sysv_notify_change(struct dentry *dentry, struct iattr *attr)
+int sysv_notify_change(struct dentry *dentry, struct iattr *attr)
 {
 	struct inode *inode = dentry->d_inode;
 	int error;
diff -u --recursive --new-file v2.1.78/linux/fs/sysv/namei.c linux/fs/sysv/namei.c
--- v2.1.78/linux/fs/sysv/namei.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/sysv/namei.c	Tue Jan  6 09:48:04 1998
@@ -571,9 +571,10 @@
 	return 0;
 }
 
-int sysv_link(struct inode * oldinode, struct inode * dir, 
+int sysv_link(struct dentry * old_dentry, struct inode * dir, 
 	      struct dentry * dentry)
 {
+	struct inode *oldinode = old_dentry->d_inode;
 	int error;
 	struct sysv_dir_entry * de;
 	struct buffer_head * bh;
diff -u --recursive --new-file v2.1.78/linux/include/asm-alpha/apecs.h linux/include/asm-alpha/apecs.h
--- v2.1.78/linux/include/asm-alpha/apecs.h	Mon Jul  8 22:08:18 1996
+++ linux/include/asm-alpha/apecs.h	Mon Jan 12 14:51:14 1998
@@ -31,7 +31,7 @@
    BIOS ROMs. So we must put the windows high enough to avoid these areas.
 
    We put window 1 at BUS 64Mb for 64Mb, mapping physical 0 to 64Mb-1,
-   and window 2 at BUS 512Mb for 512Mb, mapping physical 0 to 512Mb-1.
+   and window 2 at BUS 1Gb for 1Gb, mapping physical 0 to 1Gb-1.
    Yes, this does map 0 to 64Mb-1 twice, but only window 1 will actually
    be used for that range (via virt_to_bus()).
 
@@ -53,12 +53,14 @@
    DMAable memory; they count on being able to DMA to any memory they
    get from kmalloc()/get_free_pages(). They will also use window 1 for
    any physical memory accesses below 64Mb; the rest will be handled by
-   window 2, maxing out at 512Mb of memory. I trust this is enough... :-)
+   window 2, maxing out at 1Gb of memory. I trust this is enough... :-)
 
-   Finally, the reason we make window 2 start at 512Mb for 512Mb, is so that
-   we can allocate PCI bus devices' memory starting at 1Gb and up, to ensure
-   that no conflicts occur and bookkeeping is simplified (ie we don't
-   try to fill the gap between the two windows, we just go above the top).
+   We hope that the area before the first window is large enough so that
+   there will be no overlap at the top end (64Mb). We *must* locate the
+   PCI cards' memory just below window 1, so that there's still the
+   possibility of being able to access it via SPARSE space. This is
+   important for cards such as the Matrox Millennium, whose Xserver
+   wants to access memory-mapped registers in byte and short lengths.
 
    Note that the XL is treated differently from the AVANTI, even though
    for most other things they are identical. It didn't seem reasonable to
@@ -69,8 +71,8 @@
 #define APECS_XL_DMA_WIN1_BASE		(64*1024*1024)
 #define APECS_XL_DMA_WIN1_SIZE		(64*1024*1024)
 #define APECS_XL_DMA_WIN1_SIZE_PARANOID	(48*1024*1024)
-#define APECS_XL_DMA_WIN2_BASE		(512*1024*1024)
-#define APECS_XL_DMA_WIN2_SIZE		(512*1024*1024)
+#define APECS_XL_DMA_WIN2_BASE		(1024*1024*1024)
+#define APECS_XL_DMA_WIN2_SIZE		(1024*1024*1024)
 
 #else /* CONFIG_ALPHA_XL */
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-alpha/cache.h linux/include/asm-alpha/cache.h
--- v2.1.78/linux/include/asm-alpha/cache.h	Sun Jan 26 02:07:46 1997
+++ linux/include/asm-alpha/cache.h	Mon Jan 12 14:51:14 1998
@@ -4,9 +4,9 @@
 #ifndef __ARCH_ALPHA_CACHE_H
 #define __ARCH_ALPHA_CACHE_H
 
-/* 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))
+/* Bytes per L1 (data) cache line.  Both EV4 and EV5 are write-through,
+   read-allocate, direct-mapped, physical. */
+#define L1_CACHE_BYTES     32
+#define L1_CACHE_ALIGN(x)  (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1))
 
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-alpha/elf.h linux/include/asm-alpha/elf.h
--- v2.1.78/linux/include/asm-alpha/elf.h	Wed Sep 24 20:05:48 1997
+++ linux/include/asm-alpha/elf.h	Mon Jan 12 14:52:46 1998
@@ -98,4 +98,36 @@
 	_dest[32] = _dump.regs[EF_PS];				\
 }
 
+/* This yields a mask that user programs can use to figure out what
+   instruction set this cpu supports.  This is trivial on Alpha, 
+   but not so on other machines. */
+
+#define ELF_HWCAP							\
+({									\
+	/* Sadly, most folks don't yet have assemblers that know about	\
+	   amask.  This is "amask v0, v0" */				\
+	register long _v0 __asm("$0") = -1;				\
+	__asm(".long 0x47e00c20" : "=r"(_v0) : "0"(_v0));		\
+	~_v0;								\
+})
+
+/* This yields a string that ld.so will use to load implementation
+   specific libraries for optimization.  This is more specific in
+   intent than poking at uname or /proc/cpuinfo.  
+
+   This might do with checking bwx simultaneously...  */
+
+#define ELF_PLATFORM				\
+({						\
+	/* Or "implver v0" ... */		\
+	register long _v0 __asm("$0");		\
+	__asm(".long 0x47e03d80" : "=r"(_v0));	\
+	_v0 == 0 ? "ev4" : "ev5";		\
+})
+
+#ifdef __KERNEL__
+#define SET_PERSONALITY(ibcs2) \
+	current->personality = (ibcs2 ? PER_SVR4 : PER_LINUX)
+#endif
+
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-alpha/fpu.h linux/include/asm-alpha/fpu.h
--- v2.1.78/linux/include/asm-alpha/fpu.h	Mon Jun 16 16:35:59 1997
+++ linux/include/asm-alpha/fpu.h	Mon Jan 12 14:49:36 1998
@@ -81,4 +81,7 @@
 	return fp;
 }
 
+extern unsigned long rdfpcr(void);
+extern void wrfpcr(unsigned long);
+
 #endif /* __ASM_ALPHA_FPU_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-alpha/hwrpb.h linux/include/asm-alpha/hwrpb.h
--- v2.1.78/linux/include/asm-alpha/hwrpb.h	Thu Apr  4 04:56:47 1996
+++ linux/include/asm-alpha/hwrpb.h	Mon Jan 12 14:51:14 1998
@@ -40,6 +40,7 @@
 #define ST_DEC_EB66P		-19	/* EB66 systype		*/
 #define ST_DEC_EBPC64		-20	/* Cabriolet (AlphaPC64) systype */
 #define ST_DEC_EB164		 26	/* EB164 systype	*/
+#define ST_DEC_MIATA		 30	/* MIATA systype        */
 
 struct pcb_struct {
 	unsigned long ksp;
diff -u --recursive --new-file v2.1.78/linux/include/asm-alpha/io.h linux/include/asm-alpha/io.h
--- v2.1.78/linux/include/asm-alpha/io.h	Tue May 13 22:41:15 1997
+++ linux/include/asm-alpha/io.h	Mon Jan 12 14:51:14 1998
@@ -80,6 +80,10 @@
 # include <asm/apecs.h>		/* get chip-specific definitions */
 #elif defined(CONFIG_ALPHA_CIA)
 # include <asm/cia.h>		/* get chip-specific definitions */
+#elif defined(CONFIG_ALPHA_T2)
+# include <asm/t2.h>		/* get chip-specific definitions */
+#elif defined(CONFIG_ALPHA_PYXIS)
+# include <asm/pyxis.h>		/* get chip-specific definitions */
 #else
 # include <asm/jensen.h>
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-alpha/irq.h linux/include/asm-alpha/irq.h
--- v2.1.78/linux/include/asm-alpha/irq.h	Wed Apr 16 14:15:00 1997
+++ linux/include/asm-alpha/irq.h	Mon Jan 12 14:51:14 1998
@@ -14,8 +14,10 @@
 # define NR_IRQS	33
 #elif defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P) || defined(CONFIG_ALPHA_MIKASA)
 # define NR_IRQS	32
-#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
+#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) || defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_NORITAKE)
 # define NR_IRQS	48
+#elif defined(CONFIG_ALPHA_SABLE)
+# define NR_IRQS	40
 #else
 # define NR_IRQS	16
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-alpha/lca.h linux/include/asm-alpha/lca.h
--- v2.1.78/linux/include/asm-alpha/lca.h	Mon Oct  7 02:40:22 1996
+++ linux/include/asm-alpha/lca.h	Mon Jan 12 14:51:14 1998
@@ -130,6 +130,37 @@
 
 #define HAE_ADDRESS	LCA_IOC_HAE
 
+/* LCA PMR Power Management register defines */
+#define LCA_PMR_ADDR	(IDENT_ADDR + 0x120000098UL)
+#define LCA_PMR_PDIV    0x7                     /* Primary clock divisor */
+#define LCA_PMR_ODIV    0x38                    /* Override clock divisor */
+#define LCA_PMR_INTO    0x40                    /* Interrupt override */
+#define LCA_PMR_DMAO    0x80                    /* DMA override */
+#define LCA_PMR_OCCEB   0xffff0000L             /* Override cycle counter - even
+ bits */
+#define LCA_PMR_OCCOB   0xffff000000000000L     /* Override cycle counter - even
+ bits */
+#define LCA_PMR_PRIMARY_MASK    0xfffffffffffffff8
+/* LCA PMR Macros */
+
+#define READ_PMR        (*(volatile unsigned long *)LCA_PMR_ADDR)
+#define WRITE_PMR(d)    (*((volatile unsigned long *)LCA_PMR_ADDR) = (d))
+
+#define GET_PRIMARY(r)  ((r) & LCA_PMR_PDIV)
+#define GET_OVERRIDE(r) (((r) >> 3) & LCA_PMR_PDIV)
+#define SET_PRIMARY_CLOCK(r, c) ((r) = (((r) & LCA_PMR_PRIMARY_MASK) | (c)))
+
+/* LCA PMR Divisor values */
+#define DIV_1   0x0
+#define DIV_1_5 0x1
+#define DIV_2   0x2
+#define DIV_4   0x3
+#define DIV_8   0x4
+#define DIV_16  0x5
+#define DIV_MIN DIV_1
+#define DIV_MAX DIV_16
+
+
 #ifdef __KERNEL__
 
 /*
diff -u --recursive --new-file v2.1.78/linux/include/asm-alpha/pal.h linux/include/asm-alpha/pal.h
--- v2.1.78/linux/include/asm-alpha/pal.h	Mon Jun 26 01:06:53 1995
+++ linux/include/asm-alpha/pal.h	Mon Jan 12 14:51:14 1998
@@ -7,7 +7,6 @@
 #define PAL_halt	  0
 #define PAL_cflush	  1
 #define PAL_draina	  2
-#define PAL_cobratt	  9
 #define PAL_bpt		128
 #define PAL_bugchk	129
 #define PAL_chmk	131
@@ -27,6 +26,8 @@
 /*
  * OSF specific PAL-code
  */
+#define PAL_cserve	 9
+#define PAL_wripir	13
 #define PAL_rdmces	16
 #define PAL_wrmces	17
 #define PAL_wrfen	43
diff -u --recursive --new-file v2.1.78/linux/include/asm-alpha/pyxis.h linux/include/asm-alpha/pyxis.h
--- v2.1.78/linux/include/asm-alpha/pyxis.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-alpha/pyxis.h	Mon Jan 12 14:51:14 1998
@@ -0,0 +1,420 @@
+#ifndef __ALPHA_PYXIS__H__
+#define __ALPHA_PYXIS__H__
+
+#include <linux/types.h>
+
+/*
+ * PYXIS is the internal name for a cor logic chipset which provides
+ * memory controller and PCI access for the 21164A chip based systems.
+ *
+ * This file is based on:
+ *
+ * Pyxis Chipset Spec
+ * 14-Jun-96
+ * Rev. X2.0
+ *
+ */
+
+/*------------------------------------------------------------------------**
+**                                                                        **
+**  I/O procedures                                                   **
+**                                                                        **
+**      inport[b|w|t|l], outport[b|w|t|l] 8:16:24:32 IO xfers             **
+**	inportbxt: 8 bits only                                            **
+**      inport:    alias of inportw                                       **
+**      outport:   alias of outportw                                      **
+**                                                                        **
+**      inmem[b|w|t|l], outmem[b|w|t|l] 8:16:24:32 ISA memory xfers       **
+**	inmembxt: 8 bits only                                             **
+**      inmem:    alias of inmemw                                         **
+**      outmem:   alias of outmemw                                        **
+**                                                                        **
+**------------------------------------------------------------------------*/
+
+
+/* CIA ADDRESS BIT DEFINITIONS
+ *
+ *  3 3 3 3|3 3 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 
+ *  9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |1| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |0|0|0|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
+ *  |                                                                        \_/ \_/
+ *  |                                                                         |   |
+ *  +-- IO space, not cached.                                   Byte Enable --+   |
+ *                                                              Transfer Length --+
+ *
+ *
+ *
+ *   Byte      Transfer
+ *   Enable    Length    Transfer  Byte    Address
+ *   adr<6:5>  adr<4:3>  Length    Enable  Adder
+ *   ---------------------------------------------
+ *      00        00      Byte      1110   0x000
+ *      01        00      Byte      1101   0x020
+ *      10        00      Byte      1011   0x040
+ *      11        00      Byte      0111   0x060
+ *
+ *      00        01      Word      1100   0x008
+ *      01        01      Word      1001   0x028 <= Not supported in this code.
+ *      10        01      Word      0011   0x048
+ *
+ *      00        10      Tribyte   1000   0x010
+ *      01        10      Tribyte   0001   0x030
+ *
+ *      10        11      Longword  0000   0x058
+ *
+ *      Note that byte enables are asserted low.
+ *
+ */
+
+#define BYTE_ENABLE_SHIFT 5
+#define TRANSFER_LENGTH_SHIFT 3
+#define MEM_SP1_MASK 0x1fffffff  /* Mem sparse space 1 mask is 29 bits */
+
+
+#define PYXIS_DMA_WIN_BASE	(1024UL*1024UL*1024UL)
+#define PYXIS_DMA_WIN_SIZE	(1024*1024*1024)
+
+/*
+ *  General Registers
+ */
+#define PYXIS_REV			(IDENT_ADDR + 0x8740000080UL)
+#define PYXIS_PCI_LAT			(IDENT_ADDR + 0x87400000C0UL)
+#define PYXIS_CTRL			(IDENT_ADDR + 0x8740000100UL)
+#define PYXIS_CTRL1			(IDENT_ADDR + 0x8740000140UL)
+#define PYXIS_FLASH_CTRL		(IDENT_ADDR + 0x8740000200UL)
+
+#define PYXIS_HAE_MEM			(IDENT_ADDR + 0x8740000400UL)
+#define PYXIS_HAE_IO			(IDENT_ADDR + 0x8740000440UL)
+#define PYXIS_CFG			(IDENT_ADDR + 0x8740000480UL)
+
+/*
+ * Diagnostic Registers
+ */
+#define PYXIS_DIAG			(IDENT_ADDR + 0x8740002000UL)
+#define PYXIS_DIAG_CHECK		(IDENT_ADDR + 0x8740003000UL)
+
+/*
+ * Performance Monitor registers (p4-3)
+ */
+#define PYXIS_PERF_MONITOR		(IDENT_ADDR + 0x8740004000UL)
+#define PYXIS_PERF_CONTROL		(IDENT_ADDR + 0x8740004040UL)
+
+/*
+ * 21171-CA Error registers (p4-3)
+ */
+#define PYXIS_ERR			(IDENT_ADDR + 0x8740008200UL)
+#define PYXIS_STAT			(IDENT_ADDR + 0x8740008240UL)
+#define PYXIS_ERR_MASK			(IDENT_ADDR + 0x8740008280UL)
+#define PYXIS_SYN			(IDENT_ADDR + 0x8740008300UL)
+#define PYXIS_ERR_DATA			(IDENT_ADDR + 0x8740008308UL)
+
+#define PYXIS_MEAR			(IDENT_ADDR + 0x8740008400UL)
+#define PYXIS_MESR			(IDENT_ADDR + 0x8740008440UL)
+#define PYXIS_PCI_ERR0			(IDENT_ADDR + 0x8740008800UL)
+#define PYXIS_PCI_ERR1			(IDENT_ADDR + 0x8740008840UL)
+#define PYXIS_PCI_ERR2			(IDENT_ADDR + 0x8740008880UL)
+
+/*
+ *  PCI Address Translation Registers.   I've only defined
+ * the first window fully as that's the only one that we're currently using.
+ * The other window bases are needed to disable the windows.
+ */
+#define PYXIS_TBIA			(IDENT_ADDR + 0x8760000100UL)
+#define PYXIS_W0_BASE			(IDENT_ADDR + 0x8760000400UL)
+#define PYXIS_W0_MASK			(IDENT_ADDR + 0x8760000440UL)
+#define PYXIS_T0_BASE			(IDENT_ADDR + 0x8760000480UL)
+
+#define PYXIS_W1_BASE			(IDENT_ADDR + 0x8760000500UL)
+#define PYXIS_W2_BASE			(IDENT_ADDR + 0x8760000600UL)
+#define PYXIS_W3_BASE			(IDENT_ADDR + 0x8760000700UL)
+
+/*
+ * Memory Control registers
+ */
+#define PYXIS_MCR			(IDENT_ADDR + 0x8750000000UL)
+
+/*
+ * Memory spaces:
+ */
+#define PYXIS_IACK_SC		        (IDENT_ADDR + 0x8720000000UL)
+#define PYXIS_CONF		        (IDENT_ADDR + 0x8700000000UL)
+#define PYXIS_IO			(IDENT_ADDR + 0x8580000000UL)
+#define PYXIS_SPARSE_MEM		(IDENT_ADDR + 0x8000000000UL)
+#define PYXIS_DENSE_MEM		        (IDENT_ADDR + 0x8600000000UL)
+
+/*
+ * Interrupt Control registers
+ */
+#define PYXIS_INT_REQ			(IDENT_ADDR + 0x87A0000000UL)
+#define PYXIS_INT_MASK			(IDENT_ADDR + 0x87A0000040UL)
+#define PYXIS_INT_HILO			(IDENT_ADDR + 0x87A00000C0UL)
+#define PYXIS_INT_ROUTE			(IDENT_ADDR + 0x87A0000140UL)
+#define PYXIS_GPO			(IDENT_ADDR + 0x87A0000180UL)
+#define PYXIS_INT_CNFG			(IDENT_ADDR + 0x87A00001C0UL)
+#define PYXIS_RT_COUNT			(IDENT_ADDR + 0x87A0000200UL)
+#define PYXIS_INT_TIME			(IDENT_ADDR + 0x87A0000240UL)
+#define PYXIS_IIC_CTRL			(IDENT_ADDR + 0x87A00002C0UL)
+
+/*
+ * Bit definitions for I/O Controller status register 0:
+ */
+#define PYXIS_STAT0_CMD			0xf
+#define PYXIS_STAT0_ERR			(1<<4)
+#define PYXIS_STAT0_LOST		(1<<5)
+#define PYXIS_STAT0_THIT		(1<<6)
+#define PYXIS_STAT0_TREF		(1<<7)
+#define PYXIS_STAT0_CODE_SHIFT		8
+#define PYXIS_STAT0_CODE_MASK		0x7
+#define PYXIS_STAT0_P_NBR_SHIFT		13
+#define PYXIS_STAT0_P_NBR_MASK		0x7ffff
+
+#define HAE_ADDRESS	                PYXIS_HAE_MEM
+
+#ifdef __KERNEL__
+
+/*
+ * Translate physical memory address as seen on (PCI) bus into
+ * a kernel virtual address and vv.
+ */
+extern inline unsigned long virt_to_bus(void * address)
+{
+	return virt_to_phys(address) + PYXIS_DMA_WIN_BASE;
+}
+
+extern inline void * bus_to_virt(unsigned long address)
+{
+	return phys_to_virt(address - PYXIS_DMA_WIN_BASE);
+}
+
+/*
+ * I/O functions:
+ *
+ * PYXIS (the 2117x PCI/memory support chipset for the EV5 (21164)
+ * series of processors uses a sparse address mapping scheme to
+ * get at PCI memory and I/O.
+ */
+
+#define vuip	volatile unsigned int *
+
+extern inline unsigned int __inb(unsigned long addr)
+{
+	long result = *(vuip) ((addr << 5) + PYXIS_IO + 0x00);
+	result >>= (addr & 3) * 8;
+	return 0xffUL & result;
+}
+
+extern inline void __outb(unsigned char b, unsigned long addr)
+{
+	unsigned int w;
+
+	asm ("insbl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	*(vuip) ((addr << 5) + PYXIS_IO + 0x00) = w;
+	mb();
+}
+
+extern inline unsigned int __inw(unsigned long addr)
+{
+	long result = *(vuip) ((addr << 5) + PYXIS_IO + 0x08);
+	result >>= (addr & 3) * 8;
+	return 0xffffUL & result;
+}
+
+extern inline void __outw(unsigned short b, unsigned long addr)
+{
+	unsigned int w;
+
+	asm ("inswl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	*(vuip) ((addr << 5) + PYXIS_IO + 0x08) = w;
+	mb();
+}
+
+extern inline unsigned int __inl(unsigned long addr)
+{
+	return *(vuip) ((addr << 5) + PYXIS_IO + 0x18);
+}
+
+extern inline void __outl(unsigned int b, unsigned long addr)
+{
+	*(vuip) ((addr << 5) + PYXIS_IO + 0x18) = b;
+	mb();
+}
+
+
+/*
+ * Memory functions.  64-bit and 32-bit accesses are done through
+ * dense memory space, everything else through sparse space.
+ * 
+ * For reading and writing 8 and 16 bit quantities we need to 
+ * go through one of the three sparse address mapping regions
+ * and use the HAE_MEM CSR to provide some bits of the address.
+ * The following few routines use only sparse address region 1
+ * which gives 1Gbyte of accessible space which relates exactly
+ * to the amount of PCI memory mapping *into* system address space.
+ * See p 6-17 of the specification but it looks something like this:
+ *
+ * 21164 Address:
+ * 
+ *          3         2         1                                                               
+ * 9876543210987654321098765432109876543210
+ * 1ZZZZ0.PCI.QW.Address............BBLL                 
+ *
+ * ZZ = SBZ
+ * BB = Byte offset
+ * LL = Transfer length
+ *
+ * PCI Address:
+ *
+ * 3         2         1                                                               
+ * 10987654321098765432109876543210
+ * HHH....PCI.QW.Address........ 00
+ *
+ * HHH = 31:29 HAE_MEM CSR
+ * 
+ */
+
+extern inline void pyxis_set_hae(unsigned long new_hae)
+{
+        unsigned long ipl = swpipl(7);
+        hae.cache = new_hae;
+        *hae.reg = new_hae;
+        mb();
+	new_hae = *hae.reg; /* read it to be sure it got out */
+        setipl(ipl);
+}
+
+extern inline unsigned long __readb(unsigned long addr)
+{
+	unsigned long result, shift, msb, work, temp;
+
+	shift = (addr & 0x3) << 3;
+	msb = addr & 0xE0000000UL;
+	temp = addr & MEM_SP1_MASK ;
+	if (msb != hae.cache) {
+	  pyxis_set_hae(msb);
+	}
+	work = ((temp << 5) + PYXIS_SPARSE_MEM + 0x00);
+	result = *(vuip) work;
+	result >>= shift;
+	return 0x0ffUL & result;
+}
+
+extern inline unsigned long __readw(unsigned long addr)
+{
+	unsigned long result, shift, msb, work, temp;
+
+	shift = (addr & 0x3) << 3;
+	msb = addr & 0xE0000000UL;
+	temp = addr & MEM_SP1_MASK ;
+	if (msb != hae.cache) {
+	  pyxis_set_hae(msb);
+	}
+	work = ((temp << 5) + PYXIS_SPARSE_MEM + 0x08);
+	result = *(vuip) work;
+	result >>= shift;
+	return 0x0ffffUL & result;
+}
+
+extern inline unsigned long __readl(unsigned long addr)
+{
+	return *(vuip) (addr + PYXIS_DENSE_MEM);
+}
+
+extern inline void __writeb(unsigned char b, unsigned long addr)
+{
+        unsigned long msb ; 
+
+	msb = addr & 0xE0000000 ;
+	addr &= MEM_SP1_MASK ;
+	if (msb != hae.cache) {
+	  pyxis_set_hae(msb);
+	}
+	*(vuip) ((addr << 5) + PYXIS_SPARSE_MEM + 0x00) = b * 0x01010101;
+}
+
+extern inline void __writew(unsigned short b, unsigned long addr)
+{
+        unsigned long msb ; 
+
+	msb = addr & 0xE0000000 ;
+	addr &= MEM_SP1_MASK ;
+	if (msb != hae.cache) {
+	  pyxis_set_hae(msb);
+	}
+	*(vuip) ((addr << 5) + PYXIS_SPARSE_MEM + 0x08) = b * 0x00010001;
+}
+
+extern inline void __writel(unsigned int b, unsigned long addr)
+{
+	*(vuip) (addr + PYXIS_DENSE_MEM) = b;
+}
+
+#define inb(port) \
+(__builtin_constant_p((port))?__inb(port):_inb(port))
+
+#define outb(x, port) \
+(__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port)))
+
+#define readl(a)	__readl((unsigned long)(a))
+#define writel(v,a)	__writel((v),(unsigned long)(a))
+
+#undef vuip
+
+extern unsigned long pyxis_init (unsigned long mem_start,
+				 unsigned long mem_end);
+
+#endif /* __KERNEL__ */
+
+/*
+ * Data structure for handling PYXIS machine checks:
+ */
+struct el_PYXIS_sysdata_mcheck {
+    u_long      coma_gcr;                       
+    u_long      coma_edsr;                      
+    u_long      coma_ter;                       
+    u_long      coma_elar;                      
+    u_long      coma_ehar;                      
+    u_long      coma_ldlr;                      
+    u_long      coma_ldhr;                      
+    u_long      coma_base0;                     
+    u_long      coma_base1;                     
+    u_long      coma_base2;                     
+    u_long      coma_cnfg0;                     
+    u_long      coma_cnfg1;                     
+    u_long      coma_cnfg2;                     
+    u_long      epic_dcsr;                      
+    u_long      epic_pear;                      
+    u_long      epic_sear;                      
+    u_long      epic_tbr1;                      
+    u_long      epic_tbr2;                      
+    u_long      epic_pbr1;                      
+    u_long      epic_pbr2;                      
+    u_long      epic_pmr1;                      
+    u_long      epic_pmr2;                      
+    u_long      epic_harx1;                     
+    u_long      epic_harx2;                     
+    u_long      epic_pmlt;                      
+    u_long      epic_tag0;                      
+    u_long      epic_tag1;                      
+    u_long      epic_tag2;                      
+    u_long      epic_tag3;                      
+    u_long      epic_tag4;                      
+    u_long      epic_tag5;                      
+    u_long      epic_tag6;                      
+    u_long      epic_tag7;                      
+    u_long      epic_data0;                     
+    u_long      epic_data1;                     
+    u_long      epic_data2;                     
+    u_long      epic_data3;                     
+    u_long      epic_data4;                     
+    u_long      epic_data5;                     
+    u_long      epic_data6;                     
+    u_long      epic_data7;                     
+};
+
+#define RTC_PORT(x)	(0x70 + (x))
+#define RTC_ADDR(x)	(0x80 | (x))
+#define RTC_ALWAYS_BCD	0
+
+#endif /* __ALPHA_PYXIS__H__ */
diff -u --recursive --new-file v2.1.78/linux/include/asm-alpha/t2.h linux/include/asm-alpha/t2.h
--- v2.1.78/linux/include/asm-alpha/t2.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-alpha/t2.h	Mon Jan 12 14:51:14 1998
@@ -0,0 +1,508 @@
+#ifndef __ALPHA_T2__H__
+#define __ALPHA_T2__H__
+
+#include <linux/types.h>
+
+/*
+ * T2 is the internal name for the core logic chipset which provides
+ * memory controller and PCI access for the SABLE-based systems.
+ *
+ * This file is based on:
+ *
+ * SABLE I/O Specification
+ * Revision/Update Information: 1.3
+ *
+ * jestabro@amt.tay1.dec.com Initial Version.
+ *
+ */
+
+#define BYTE_ENABLE_SHIFT 5
+#define TRANSFER_LENGTH_SHIFT 3
+#define MEM_SP1_MASK 0x1fffffff  /* Mem sparse space 1 mask is 29 bits */
+
+
+#define T2_DMA_WIN_BASE	(1024UL*1024UL*1024UL)
+#define T2_DMA_WIN_SIZE	(1024*1024*1024)
+
+/*
+ * Memory spaces:
+ */
+#define T2_CONF			        (IDENT_ADDR + 0x390000000UL)
+#define T2_IO				(IDENT_ADDR + 0x3a0000000UL)
+#define T2_SPARSE_MEM			(IDENT_ADDR + 0x200000000UL)
+#define T2_DENSE_MEM		        (IDENT_ADDR + 0x3c0000000UL)
+
+#define T2_IOCSR			(IDENT_ADDR + 0x38e000000UL)
+#define T2_CERR1			(IDENT_ADDR + 0x38e000020UL)
+#define T2_CERR2			(IDENT_ADDR + 0x38e000040UL)
+#define T2_CERR3			(IDENT_ADDR + 0x38e000060UL)
+#define T2_PERR1			(IDENT_ADDR + 0x38e000080UL)
+#define T2_PERR2			(IDENT_ADDR + 0x38e0000a0UL)
+#define T2_PSCR				(IDENT_ADDR + 0x38e0000c0UL)
+#define T2_HAE_1			(IDENT_ADDR + 0x38e0000e0UL)
+#define T2_HAE_2			(IDENT_ADDR + 0x38e000100UL)
+#define T2_HBASE			(IDENT_ADDR + 0x38e000120UL)
+#define T2_WBASE1			(IDENT_ADDR + 0x38e000140UL)
+#define T2_WMASK1			(IDENT_ADDR + 0x38e000160UL)
+#define T2_TBASE1			(IDENT_ADDR + 0x38e000180UL)
+#define T2_WBASE2			(IDENT_ADDR + 0x38e0001a0UL)
+#define T2_WMASK2			(IDENT_ADDR + 0x38e0001c0UL)
+#define T2_TBASE2			(IDENT_ADDR + 0x38e0001e0UL)
+#define T2_TLBBR			(IDENT_ADDR + 0x38e000200UL)
+
+#define T2_HAE_3			(IDENT_ADDR + 0x38e000240UL)
+#define T2_HAE_4			(IDENT_ADDR + 0x38e000260UL)
+
+#define HAE_ADDRESS	                T2_HAE_1
+
+/*  T2 CSRs are in the non-cachable primary IO space from 3.8000.0000 to
+ 3.8fff.ffff
+ *
+ *  +--------------+ 3 8000 0000
+ *  | CPU 0 CSRs   |            
+ *  +--------------+ 3 8100 0000
+ *  | CPU 1 CSRs   |            
+ *  +--------------+ 3 8200 0000
+ *  | CPU 2 CSRs   |            
+ *  +--------------+ 3 8300 0000
+ *  | CPU 3 CSRs   |            
+ *  +--------------+ 3 8400 0000
+ *  | CPU Reserved |            
+ *  +--------------+ 3 8700 0000
+ *  | Mem Reserved |            
+ *  +--------------+ 3 8800 0000
+ *  | Mem 0 CSRs   |            
+ *  +--------------+ 3 8900 0000
+ *  | Mem 1 CSRs   |            
+ *  +--------------+ 3 8a00 0000
+ *  | Mem 2 CSRs   |            
+ *  +--------------+ 3 8b00 0000
+ *  | Mem 3 CSRs   |            
+ *  +--------------+ 3 8c00 0000           
+ *  | Mem Reserved |            
+ *  +--------------+ 3 8e00 0000           
+ *  | PCI Bridge   |            
+ *  +--------------+ 3 8f00 0000           
+ *  | Expansion IO |            
+ *  +--------------+ 3 9000 0000           
+ *                                              
+ *
+ */
+#define CPU0_BASE               (IDENT_ADDR + 0x380000000L)
+#define CPU1_BASE               (IDENT_ADDR + 0x381000000L)
+#define CPU2_BASE               (IDENT_ADDR + 0x382000000L)
+#define CPU3_BASE               (IDENT_ADDR + 0x383000000L)
+#define MEM0_BASE               (IDENT_ADDR + 0x388000000L)
+#define MEM1_BASE               (IDENT_ADDR + 0x389000000L)
+#define MEM2_BASE               (IDENT_ADDR + 0x38a000000L)
+#define MEM3_BASE               (IDENT_ADDR + 0x38b000000L)
+
+#ifdef __KERNEL__
+
+/*
+ * Translate physical memory address as seen on (PCI) bus into
+ * a kernel virtual address and vv.
+ */
+extern inline unsigned long virt_to_bus(void * address)
+{
+	return virt_to_phys(address) + T2_DMA_WIN_BASE;
+}
+
+extern inline void * bus_to_virt(unsigned long address)
+{
+	return phys_to_virt(address - T2_DMA_WIN_BASE);
+}
+
+/*
+ * I/O functions:
+ *
+ * T2 (the core logic PCI/memory support chipset for the SABLE
+ * series of processors uses a sparse address mapping scheme to
+ * get at PCI memory and I/O.
+ */
+
+#define vuip	volatile unsigned int *
+
+extern inline unsigned int __inb(unsigned long addr)
+{
+	long result = *(vuip) ((addr << 5) + T2_IO + 0x00);
+	result >>= (addr & 3) * 8;
+	return 0xffUL & result;
+}
+
+extern inline void __outb(unsigned char b, unsigned long addr)
+{
+	unsigned int w;
+
+	asm ("insbl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	*(vuip) ((addr << 5) + T2_IO + 0x00) = w;
+	mb();
+}
+
+extern inline unsigned int __inw(unsigned long addr)
+{
+	long result = *(vuip) ((addr << 5) + T2_IO + 0x08);
+	result >>= (addr & 3) * 8;
+	return 0xffffUL & result;
+}
+
+extern inline void __outw(unsigned short b, unsigned long addr)
+{
+	unsigned int w;
+
+	asm ("inswl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	*(vuip) ((addr << 5) + T2_IO + 0x08) = w;
+	mb();
+}
+
+extern inline unsigned int __inl(unsigned long addr)
+{
+	return *(vuip) ((addr << 5) + T2_IO + 0x18);
+}
+
+extern inline void __outl(unsigned int b, unsigned long addr)
+{
+	*(vuip) ((addr << 5) + T2_IO + 0x18) = b;
+	mb();
+}
+
+
+/*
+ * Memory functions.  64-bit and 32-bit accesses are done through
+ * dense memory space, everything else through sparse space.
+ * 
+ * For reading and writing 8 and 16 bit quantities we need to 
+ * go through one of the three sparse address mapping regions
+ * and use the HAE_MEM CSR to provide some bits of the address.
+ * The following few routines use only sparse address region 1
+ * which gives 1Gbyte of accessible space which relates exactly
+ * to the amount of PCI memory mapping *into* system address space.
+ * See p 6-17 of the specification but it looks something like this:
+ *
+ * 21164 Address:
+ * 
+ *          3         2         1                                                               
+ * 9876543210987654321098765432109876543210
+ * 1ZZZZ0.PCI.QW.Address............BBLL                 
+ *
+ * ZZ = SBZ
+ * BB = Byte offset
+ * LL = Transfer length
+ *
+ * PCI Address:
+ *
+ * 3         2         1                                                               
+ * 10987654321098765432109876543210
+ * HHH....PCI.QW.Address........ 00
+ *
+ * HHH = 31:29 HAE_MEM CSR
+ * 
+ */
+
+extern inline unsigned long __readb(unsigned long addr)
+{
+	unsigned long result, shift, msb;
+
+	shift = (addr & 0x3) * 8 ;
+	msb = addr & 0xE0000000 ;
+	addr &= MEM_SP1_MASK ;
+	if (msb != hae.cache) {
+	  set_hae(msb);
+	}
+	result = *(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x00) ;
+	result >>= shift;
+	return 0xffUL & result;
+}
+
+extern inline unsigned long __readw(unsigned long addr)
+{
+	unsigned long result, shift, msb;
+
+	shift = (addr & 0x3) * 8;
+	msb = addr & 0xE0000000 ;
+	addr &= MEM_SP1_MASK ;
+	if (msb != hae.cache) {
+	  set_hae(msb);
+	}
+	result = *(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x08);
+	result >>= shift;
+	return 0xffffUL & result;
+}
+
+/* on SABLE with T2, we must use SPARSE memory even for 32-bit access */
+extern inline unsigned long __readl(unsigned long addr)
+{
+	unsigned long result, msb;
+
+	msb = addr & 0xE0000000 ;
+	addr &= MEM_SP1_MASK ;
+	if (msb != hae.cache) {
+	  set_hae(msb);
+	}
+	result = *(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x18);
+	return 0xffffffffUL & result;
+}
+
+extern inline void __writeb(unsigned char b, unsigned long addr)
+{
+        unsigned long msb ; 
+
+	msb = addr & 0xE0000000 ;
+	addr &= MEM_SP1_MASK ;
+	if (msb != hae.cache) {
+	  set_hae(msb);
+	}
+	*(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x00) = b * 0x01010101;
+}
+
+extern inline void __writew(unsigned short b, unsigned long addr)
+{
+        unsigned long msb ; 
+
+	msb = addr & 0xE0000000 ;
+	addr &= MEM_SP1_MASK ;
+	if (msb != hae.cache) {
+	  set_hae(msb);
+	}
+	*(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x08) = b * 0x00010001;
+}
+
+/* on SABLE with T2, we must use SPARSE memory even for 32-bit access */
+extern inline void __writel(unsigned int b, unsigned long addr)
+{
+        unsigned long msb ; 
+
+	msb = addr & 0xE0000000 ;
+	addr &= MEM_SP1_MASK ;
+	if (msb != hae.cache) {
+	  set_hae(msb);
+	}
+	*(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x18) = b;
+}
+
+#define inb(port) \
+(__builtin_constant_p((port))?__inb(port):_inb(port))
+
+#define outb(x, port) \
+(__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port)))
+
+#define readl(a)	__readl((unsigned long)(a))
+#define writel(v,a)	__writel((v),(unsigned long)(a))
+
+#undef vuip
+
+extern unsigned long t2_init (unsigned long mem_start,
+				 unsigned long mem_end);
+
+#endif /* __KERNEL__ */
+
+/*
+ * Sable CPU Module CSRS
+ *
+ * These are CSRs for hardware other than the CPU chip on the CPU module.
+ * The CPU module has Backup Cache control logic, Cbus control logic, and
+ * interrupt control logic on it.  There is a duplicate tag store to speed
+ * up maintaining cache coherency.
+ */
+
+struct sable_cpu_csr {
+unsigned long bcc;     long fill_00[3]; /* Backup Cache Control */
+unsigned long bcce;    long fill_01[3]; /* Backup Cache Correctable Error */
+unsigned long bccea;   long fill_02[3]; /* B-Cache Corr Err Address Latch */
+unsigned long bcue;    long fill_03[3]; /* B-Cache Uncorrectable Error */
+unsigned long bcuea;   long fill_04[3]; /* B-Cache Uncorr Err Addr Latch */
+unsigned long dter;    long fill_05[3]; /* Duplicate Tag Error */
+unsigned long cbctl;   long fill_06[3]; /* CBus Control */
+unsigned long cbe;     long fill_07[3]; /* CBus Error */
+unsigned long cbeal;   long fill_08[3]; /* CBus Error Addr Latch low */
+unsigned long cbeah;   long fill_09[3]; /* CBus Error Addr Latch high */
+unsigned long pmbx;    long fill_10[3]; /* Processor Mailbox */
+unsigned long ipir;    long fill_11[3]; /* Inter-Processor Int Request */
+unsigned long sic;     long fill_12[3]; /* System Interrupt Clear */
+unsigned long adlk;    long fill_13[3]; /* Address Lock (LDxL/STxC) */
+unsigned long madrl;   long fill_14[3]; /* CBus Miss Address */
+unsigned long rev;     long fill_15[3]; /* CMIC Revision */
+};
+
+/*
+ * Data structure for handling T2 machine checks:
+ */
+struct el_t2_frame_header {
+	unsigned int	elcf_fid;	/* Frame ID (from above) */
+	unsigned int	elcf_size;	/* Size of frame in bytes */
+};
+
+struct el_t2_procdata_mcheck {
+	unsigned long	elfmc_paltemp[32];	/* PAL TEMP REGS. */
+	/* EV4-specific fields */
+	unsigned long	elfmc_exc_addr;	/* Addr of excepting insn. */
+	unsigned long	elfmc_exc_sum;	/* Summary of arith traps. */
+	unsigned long	elfmc_exc_mask;	/* Exception mask (from exc_sum). */
+	unsigned long	elfmc_iccsr;	/* IBox hardware enables. */
+	unsigned long	elfmc_pal_base;	/* Base address for PALcode. */
+	unsigned long	elfmc_hier;	/* Hardware Interrupt Enable. */
+	unsigned long	elfmc_hirr;	/* Hardware Interrupt Request. */
+	unsigned long	elfmc_mm_csr;	/* D-stream fault info. */
+	unsigned long	elfmc_dc_stat;	/* D-cache status (ECC/Parity Err). */
+	unsigned long	elfmc_dc_addr;	/* EV3 Phys Addr for ECC/DPERR. */
+	unsigned long	elfmc_abox_ctl;	/* ABox Control Register. */
+	unsigned long	elfmc_biu_stat;	/* BIU Status. */
+	unsigned long	elfmc_biu_addr;	/* BUI Address. */
+	unsigned long	elfmc_biu_ctl;	/* BIU Control. */
+	unsigned long	elfmc_fill_syndrome;	/* For correcting ECC errors. */
+	unsigned long	elfmc_fill_addr;/* Cache block which was being read. */
+	unsigned long	elfmc_va;	/* Effective VA of fault or miss. */
+	unsigned long	elfmc_bc_tag;	/* Backup Cache Tag Probe Results. */
+};
+
+/* 
+ * Sable processor specific Machine Check Data segment.
+ */
+
+struct el_t2_logout_header {
+	unsigned int	elfl_size;	/* size in bytes of logout area. */
+	int	elfl_sbz1:31;		/* Should be zero. */
+	char	elfl_retry:1;		/* Retry flag. */
+        unsigned int	elfl_procoffset;	/* Processor-specific offset. */
+	unsigned int	elfl_sysoffset;		/* Offset of system-specific. */
+	unsigned int	elfl_error_type;	/* PAL error type code. */
+	unsigned int	elfl_frame_rev;		/* PAL Frame revision. */
+};
+struct el_t2_sysdata_mcheck {
+	unsigned long    elcmc_bcc;	      /* CSR 0 */
+	unsigned long    elcmc_bcce;	      /* CSR 1 */
+	unsigned long    elcmc_bccea;      /* CSR 2 */
+	unsigned long    elcmc_bcue;	      /* CSR 3 */
+	unsigned long    elcmc_bcuea;      /* CSR 4 */
+	unsigned long    elcmc_dter;	      /* CSR 5 */
+	unsigned long    elcmc_cbctl;      /* CSR 6 */
+	unsigned long    elcmc_cbe;	      /* CSR 7 */
+	unsigned long    elcmc_cbeal;      /* CSR 8 */
+	unsigned long    elcmc_cbeah;      /* CSR 9 */
+	unsigned long    elcmc_pmbx;	      /* CSR 10 */
+	unsigned long    elcmc_ipir;	      /* CSR 11 */
+	unsigned long    elcmc_sic;	      /* CSR 12 */
+	unsigned long    elcmc_adlk;	      /* CSR 13 */
+	unsigned long    elcmc_madrl;      /* CSR 14 */
+	unsigned long    elcmc_crrev4;     /* CSR 15 */
+};
+
+/*
+ * Sable memory error frame - sable pfms section 3.42
+ */
+struct el_t2_data_memory {
+	struct	el_t2_frame_header elcm_hdr;	/* ID$MEM-FERR = 0x08 */
+	unsigned int  elcm_module;	/* Module id. */
+	unsigned int  elcm_res04;	/* Reserved. */
+	unsigned long elcm_merr;	/* CSR0: Error Reg 1. */
+	unsigned long elcm_mcmd1;	/* CSR1: Command Trap 1. */
+	unsigned long elcm_mcmd2;	/* CSR2: Command Trap 2. */
+	unsigned long elcm_mconf;	/* CSR3: Configuration. */
+	unsigned long elcm_medc1;	/* CSR4: EDC Status 1. */
+	unsigned long elcm_medc2;	/* CSR5: EDC Status 2. */
+	unsigned long elcm_medcc;	/* CSR6: EDC Control. */
+	unsigned long elcm_msctl;	/* CSR7: Stream Buffer Control. */
+	unsigned long elcm_mref;	/* CSR8: Refresh Control. */
+	unsigned long elcm_filter;	/* CSR9: CRD Filter Control. */
+};
+
+
+/*
+ * Sable other cpu error frame - sable pfms section 3.43
+ */
+struct el_t2_data_other_cpu {
+	short	      elco_cpuid;	/* CPU ID */
+	short	      elco_res02[3];	
+	unsigned long elco_bcc;	/* CSR 0 */
+	unsigned long elco_bcce;	/* CSR 1 */
+	unsigned long elco_bccea;	/* CSR 2 */
+	unsigned long elco_bcue;	/* CSR 3 */
+	unsigned long elco_bcuea;	/* CSR 4 */
+	unsigned long elco_dter;	/* CSR 5 */
+	unsigned long elco_cbctl;	/* CSR 6 */
+	unsigned long elco_cbe;	/* CSR 7 */
+	unsigned long elco_cbeal;	/* CSR 8 */
+	unsigned long elco_cbeah;	/* CSR 9 */
+	unsigned long elco_pmbx;	/* CSR 10 */
+	unsigned long elco_ipir;	/* CSR 11 */
+	unsigned long elco_sic;	/* CSR 12 */
+	unsigned long elco_adlk;	/* CSR 13 */
+	unsigned long elco_madrl;	/* CSR 14 */
+	unsigned long elco_crrev4;	/* CSR 15 */
+};
+
+/*
+ * Sable other cpu error frame - sable pfms section 3.44
+ */
+struct el_t2_data_t2{
+        struct el_t2_frame_header elct_hdr;	/* ID$T2-FRAME */
+	unsigned long elct_iocsr;	/* IO Control and Status Register */
+	unsigned long elct_cerr1;	/* Cbus Error Register 1 */
+	unsigned long elct_cerr2;	/* Cbus Error Register 2 */
+	unsigned long elct_cerr3;	/* Cbus Error Register 3 */
+	unsigned long elct_perr1;	/* PCI Error Register 1 */
+	unsigned long elct_perr2;	/* PCI Error Register 2 */
+	unsigned long elct_hae0_1;	/* High Address Extension Register 1 */
+	unsigned long elct_hae0_2;	/* High Address Extension Register 2 */
+	unsigned long elct_hbase;	/* High Base Register */
+	unsigned long elct_wbase1;	/* Window Base Register 1 */
+	unsigned long elct_wmask1;	/* Window Mask Register 1 */
+	unsigned long elct_tbase1;	/* Translated Base Register 1 */
+	unsigned long elct_wbase2;	/* Window Base Register 2 */
+	unsigned long elct_wmask2;	/* Window Mask Register 2 */
+	unsigned long elct_tbase2;	/* Translated Base Register 2 */
+	unsigned long elct_tdr0;	/* TLB Data Register 0 */
+	unsigned long elct_tdr1;	/* TLB Data Register 1 */
+	unsigned long elct_tdr2;	/* TLB Data Register 2 */
+	unsigned long elct_tdr3;	/* TLB Data Register 3 */
+	unsigned long elct_tdr4;	/* TLB Data Register 4 */
+	unsigned long elct_tdr5;	/* TLB Data Register 5 */
+	unsigned long elct_tdr6;	/* TLB Data Register 6 */
+	unsigned long elct_tdr7;	/* TLB Data Register 7 */
+};
+
+/*
+ * Sable error log data structure - sable pfms section 3.40
+ */
+struct el_t2_data_corrected {
+	unsigned long elcpb_biu_stat;
+	unsigned long elcpb_biu_addr;
+	unsigned long elcpb_biu_ctl;
+	unsigned long elcpb_fill_syndrome;
+	unsigned long elcpb_fill_addr;
+	unsigned long elcpb_bc_tag;
+};
+
+/* 
+ * Sable error log data structure
+ * Note there are 4 memory slots on sable (see t2.h)
+ */
+struct el_t2_frame_mcheck {
+        struct el_t2_frame_header elfmc_header;	/* ID$P-FRAME_MCHECK */
+	struct el_t2_logout_header elfmc_hdr;
+	struct el_t2_procdata_mcheck elfmc_procdata;
+	struct el_t2_sysdata_mcheck elfmc_sysdata;
+	struct el_t2_data_t2 elfmc_t2data;
+	struct el_t2_data_memory elfmc_memdata[4]; 
+        struct el_t2_frame_header elfmc_footer;	/* empty */
+};
+
+
+/* 
+ * Sable error log data structures on memory errors
+ */
+struct el_t2_frame_corrected {
+        struct el_t2_frame_header elfcc_header;	/* ID$P-BC-COR */
+	struct el_t2_logout_header elfcc_hdr;
+	struct el_t2_data_corrected elfcc_procdata; 
+/*	struct el_t2_data_t2 elfcc_t2data;		*/
+/*	struct el_t2_data_memory elfcc_memdata[4];	*/
+        struct el_t2_frame_header elfcc_footer;	/* empty */
+};
+
+
+#define RTC_PORT(x)	(0x70 + (x))
+#define RTC_ADDR(x)	(0x80 | (x))
+#define RTC_ALWAYS_BCD	0
+
+#endif /* __ALPHA_T2__H__ */
diff -u --recursive --new-file v2.1.78/linux/include/asm-i386/elf.h linux/include/asm-i386/elf.h
--- v2.1.78/linux/include/asm-i386/elf.h	Wed Sep 24 20:05:48 1997
+++ linux/include/asm-i386/elf.h	Mon Jan 12 14:52:46 1998
@@ -70,4 +70,24 @@
 	pr_reg[15] = regs->esp;				\
 	pr_reg[16] = regs->xss;
 
+/* This yields a mask that user programs can use to figure out what
+   instruction set this cpu supports.  This could be done in userspace,
+   but it's not easy, and we've already done it here.  */
+
+#define ELF_HWCAP	(boot_cpu_data.x86_capability)
+
+/* This yields a string that ld.so will use to load implementation
+   specific libraries for optimization.  This is more specific in
+   intent than poking at uname or /proc/cpuinfo.
+
+   For the moment, we have only optimizations for the Intel generations,
+   but that could change... */
+
+#define ELF_PLATFORM  ("i386\0i486\0i586\0i686"+((boot_cpu_data.x86-3)*5))
+
+#ifdef __KERNEL__
+#define SET_PERSONALITY(ibcs2) \
+	current->personality = (ibcs2 ? PER_SVR4 : PER_LINUX)
+#endif
+
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-i386/floppy.h linux/include/asm-i386/floppy.h
--- v2.1.78/linux/include/asm-i386/floppy.h	Sun Sep  7 13:10:43 1997
+++ linux/include/asm-i386/floppy.h	Mon Jan 12 16:43:41 1998
@@ -12,30 +12,42 @@
 
 #include <linux/vmalloc.h>
 
+
+/*
+ * The DMA channel used by the floppy controller cannot access data at
+ * addresses >= 16MB
+ *
+ * Went back to the 1MB limit, as some people had problems with the floppy
+ * driver otherwise. It doesn't matter much for performance anyway, as most
+ * floppy accesses go through the track buffer.
+ */
+#define _CROSS_64KB(a,s,vdma) \
+(!vdma && ((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64))
+
+#define CROSS_64KB(a,s) _CROSS_64KB(a,s,use_virtual_dma & 1)
+
+
 #define SW fd_routine[use_virtual_dma&1]
+#define CSW fd_routine[can_use_virtual_dma & 1]
+#define NCSW fd_routine[(can_use_virtual_dma >> 1)& 1]
 
 
 #define fd_inb(port)			inb_p(port)
 #define fd_outb(port,value)		outb_p(port,value)
 
-#define fd_enable_dma()         SW._enable_dma(FLOPPY_DMA)
-#define fd_disable_dma()        SW._disable_dma(FLOPPY_DMA)
-#define fd_request_dma()        SW._request_dma(FLOPPY_DMA,"floppy")
-#define fd_free_dma()           SW._free_dma(FLOPPY_DMA)
-#define fd_clear_dma_ff()       SW._clear_dma_ff(FLOPPY_DMA)
-#define fd_set_dma_mode(mode)   SW._set_dma_mode(FLOPPY_DMA,mode)
-#define fd_set_dma_addr(addr)   SW._set_dma_addr(FLOPPY_DMA,addr)
-#define fd_set_dma_count(count) SW._set_dma_count(FLOPPY_DMA,count)
+#define fd_request_dma()        CSW._request_dma(FLOPPY_DMA,"floppy")
+#define fd_free_dma()           CSW._free_dma(FLOPPY_DMA)
 #define fd_enable_irq()         enable_irq(FLOPPY_IRQ)
 #define fd_disable_irq()        disable_irq(FLOPPY_IRQ)
-#define fd_cacheflush(addr,size) /* nothing */
-#define fd_request_irq()        SW._request_irq(FLOPPY_IRQ, floppy_interrupt, \
-					       SA_INTERRUPT|SA_SAMPLE_RANDOM, \
-					       "floppy", NULL)
+#define fd_request_irq()        NCSW._request_irq(FLOPPY_IRQ, floppy_interrupt,\
+						 SA_INTERRUPT|SA_SAMPLE_RANDOM,\
+						 "floppy", NULL)
 #define fd_free_irq()		free_irq(FLOPPY_IRQ, NULL)
 #define fd_get_dma_residue()    SW._get_dma_residue(FLOPPY_DMA)
 #define fd_dma_mem_alloc(size)	SW._dma_mem_alloc(size)
-#define fd_dma_mem_free(addr,size)	SW._dma_mem_free(addr,size)
+#define fd_dma_setup(addr, size, mode, io) SW._dma_setup(addr, size, mode, io)
+
+#define FLOPPY_CAN_FALLBACK_ON_NODMA
 
 static int virtual_dma_count=0;
 static int virtual_dma_residue=0;
@@ -143,16 +155,13 @@
 #endif
 }
 
-static void vdma_enable_dma(unsigned int dummy)
-{
-	doing_pdma = 1;
-}
-
-static void vdma_disable_dma(unsigned int dummy)
+static void fd_disable_dma(void)
 {
+	if(! (can_use_virtual_dma & 1))
+		disable_dma(FLOPPY_DMA);
 	doing_pdma = 0;
 	virtual_dma_residue += virtual_dma_count;
-	virtual_dma_count=0;		
+	virtual_dma_count=0;
 }
 
 static int vdma_request_dma(unsigned int dmanr, const char * device_id)
@@ -164,26 +173,6 @@
 {
 }
 
-static void vdma_set_dma_mode(unsigned int dummy,char mode)
-{
-	virtual_dma_mode = (mode  == DMA_MODE_WRITE);
-}
-
-static void hset_dma_addr(unsigned int no, char *addr)
-{
-	set_dma_addr(no, virt_to_bus(addr));
-}
-
-static void vdma_set_dma_addr(unsigned int dummy, char *addr)
-{
-	virtual_dma_addr = addr;
-}
-
-static void vdma_set_dma_count(unsigned int dummy,unsigned int count)
-{
-	virtual_dma_count = count;
-	virtual_dma_residue = 0;
-}
 
 static int vdma_get_dma_residue(unsigned int dummy)
 {
@@ -197,7 +186,8 @@
 			    const char *device,
 			    void *dev_id)
 {
-	return request_irq(irq, floppy_hardint,SA_INTERRUPT,device, dev_id);
+	return request_irq(irq, floppy_hardint,SA_INTERRUPT,device, 
+			   dev_id);
 
 }
 
@@ -206,30 +196,74 @@
 	return __get_dma_pages(GFP_KERNEL,__get_order(size));
 }
 
-static void dma_mem_free(unsigned long addr, unsigned long size)
-{
-	free_pages(addr, __get_order(size));
-}
 
 static unsigned long vdma_mem_alloc(unsigned long size)
 {
 	return (unsigned long) vmalloc(size);
+
 }
 
-static void vdma_mem_free(unsigned long addr, unsigned long size)
+#define nodma_mem_alloc(size) vdma_mem_alloc(size)
+
+static void _fd_dma_mem_free(unsigned long addr, unsigned long size)
 {
-	return vfree((void *)addr);
+	if((unsigned int) addr >= (unsigned int) high_memory)
+		return vfree((void *)addr);
+	else
+		free_pages(addr, __get_order(size));		
+}
+
+#define fd_dma_mem_free(addr, size)  _fd_dma_mem_free(addr, size) 
+
+static void _fd_chose_dma_mode(char *addr, unsigned long size)
+{
+	if(can_use_virtual_dma == 2) {
+		if((unsigned int) addr >= (unsigned int) high_memory ||
+		   virt_to_bus(addr) >= 0x1000000 ||
+		   _CROSS_64KB(addr, size, 0))
+			use_virtual_dma = 1;
+		else
+			use_virtual_dma = 1;
+	} else {
+		use_virtual_dma = can_use_virtual_dma & 1;
+	}
+}
+
+#define fd_chose_dma_mode(addr, size) _fd_chose_dma_mode(addr, size)
+
+
+static int vdma_dma_setup(char *addr, unsigned long size, int mode, int io)
+{
+	doing_pdma = 1;
+	virtual_dma_port = io;
+	virtual_dma_mode = (mode  == DMA_MODE_WRITE);
+	virtual_dma_addr = addr;
+	virtual_dma_count = size;
+	virtual_dma_residue = 0;
+	return 0;
+}
+
+static int hard_dma_setup(char *addr, unsigned long size, int mode, int io)
+{
+#ifdef FLOPPY_SANITY_CHECK
+	if (CROSS_64KB(addr, size)) {
+		printk("DMA crossing 64-K boundary %p-%p\n", addr, addr+size);
+		return -1;
+	}
+#endif
+	/* actual, physical DMA */
+	doing_pdma = 0;
+	clear_dma_ff(FLOPPY_DMA);
+	set_dma_mode(FLOPPY_DMA,mode);
+	set_dma_addr(FLOPPY_DMA,virt_to_bus(addr));
+	set_dma_count(FLOPPY_DMA,size);
+	enable_dma(FLOPPY_DMA);
+	return 0;
 }
 
 struct fd_routine_l {
-	void (*_enable_dma)(unsigned int dummy);
-	void (*_disable_dma)(unsigned int dummy);
 	int (*_request_dma)(unsigned int dmanr, const char * device_id);
 	void (*_free_dma)(unsigned int dmanr);
-	void (*_clear_dma_ff)(unsigned int dummy);
-	void (*_set_dma_mode)(unsigned int dummy, char mode);
-	void (*_set_dma_addr)(unsigned int dummy, char *addr);
-	void (*_set_dma_count)(unsigned int dummy, unsigned int count);
 	int (*_get_dma_residue)(unsigned int dummy);
 	int (*_request_irq)(unsigned int irq,
 			   void (*handler)(int, void *, struct pt_regs *),
@@ -237,42 +271,26 @@
 			   const char *device,
 			   void *dev_id);
 	unsigned long (*_dma_mem_alloc) (unsigned long size);
-	void (*_dma_mem_free)(unsigned long addr, unsigned long size);
+	int (*_dma_setup)(char *addr, unsigned long size, int mode, int io);
 } fd_routine[] = {
 	{
-		enable_dma,
-		disable_dma,
 		request_dma,
 		free_dma,
-		clear_dma_ff,
-		set_dma_mode,
-		hset_dma_addr,
-		set_dma_count,
 		get_dma_residue,
 		request_irq,
 		dma_mem_alloc,
-		dma_mem_free
+		hard_dma_setup
 	},
 	{
-		vdma_enable_dma,
-		vdma_disable_dma,
 		vdma_request_dma,
 		vdma_nop,
-		vdma_nop,
-		vdma_set_dma_mode,
-		vdma_set_dma_addr,
-		vdma_set_dma_count,
 		vdma_get_dma_residue,
 		vdma_request_irq,
 		vdma_mem_alloc,
-		vdma_mem_free
+		vdma_dma_setup
 	}
 };
 
-__inline__ void virtual_dma_init(void)
-{
-	/* Nothing to do on an i386 */
-}
 
 static int FDC1 = 0x3f0;
 static int FDC2 = -1;
@@ -285,14 +303,7 @@
 
 #define FLOPPY_MOTOR_MASK 0xf0
 
-/*
- * The DMA channel used by the floppy controller cannot access data at
- * addresses >= 16MB
- *
- * Went back to the 1MB limit, as some people had problems with the floppy
- * driver otherwise. It doesn't matter much for performance anyway, as most
- * floppy accesses go through the track buffer.
- */
-#define CROSS_64KB(a,s) (((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64) && ! (use_virtual_dma & 1))
+#define AUTO_DMA
+
 
 #endif /* __ASM_I386_FLOPPY_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- v2.1.78/linux/include/asm-i386/processor.h	Fri Jan  2 14:37:02 1998
+++ linux/include/asm-i386/processor.h	Mon Jan 12 19:49:54 1998
@@ -18,16 +18,16 @@
  */
 
 struct cpuinfo_x86 {
-	u8	x86;		/* CPU family */
-	u8	x86_vendor;	/* CPU vendor */
-	u8	x86_model;
-	u8	x86_mask;
+	__u8	x86;		/* CPU family */
+	__u8	x86_vendor;	/* CPU vendor */
+	__u8	x86_model;
+	__u8	x86_mask;
 	char	wp_works_ok;	/* It doesn't on 386's */
 	char	hlt_works_ok;	/* Problems on some 486Dx4's and old 386's */
 	char	hard_math;
 	char	rfu;
 	int	cpuid_level;	/* Maximum supported CPUID level, -1=no CPUID */
-	u32	x86_capability;
+	__u32	x86_capability;
 	char	x86_vendor_id[16];
 	char	x86_model_id[64];
 	int	fdiv_bug;
diff -u --recursive --new-file v2.1.78/linux/include/asm-i386/spinlock.h linux/include/asm-i386/spinlock.h
--- v2.1.78/linux/include/asm-i386/spinlock.h	Tue May 13 22:41:17 1997
+++ linux/include/asm-i386/spinlock.h	Mon Jan 12 16:43:09 1998
@@ -64,12 +64,11 @@
 
 typedef struct {
 	volatile unsigned int lock;
-	unsigned long previous;
 } spinlock_t;
 
-#define SPIN_LOCK_UNLOCKED { 0, 0 }
+#define SPIN_LOCK_UNLOCKED { 0 }
 
-#define spin_lock_init(x)	do { (x)->lock = 0; (x)->previous = 0; } while(0)
+#define spin_lock_init(x)	do { (x)->lock = 0; } while(0)
 #define spin_unlock_wait(x)	do { barrier(); } while(((volatile spinlock_t *)(x))->lock)
 
 typedef struct { unsigned long a[100]; } __dummy_lock_t;
diff -u --recursive --new-file v2.1.78/linux/include/asm-i386/uaccess.h linux/include/asm-i386/uaccess.h
--- v2.1.78/linux/include/asm-i386/uaccess.h	Fri Jan  2 14:37:02 1998
+++ linux/include/asm-i386/uaccess.h	Mon Jan 12 16:43:17 1998
@@ -97,135 +97,66 @@
  * with a separate "access_ok()" call (this is used when we do multiple
  * accesses to the same area of user memory).
  */
-#define get_user(x,ptr) \
-  __get_user_check((x),(ptr),sizeof(*(ptr)))
-#define put_user(x,ptr) \
-  __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
-
-#define __get_user(x,ptr) \
-  __get_user_nocheck((x),(ptr),sizeof(*(ptr)))
-#define __put_user(x,ptr) \
-  __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
 
-/*
- * The "xxx_ret" versions return constant specified in third argument, if
- * something bad happens. These macros can be optimized for the
- * case of just returning from the function xxx_ret is used.
- */
-
-#define put_user_ret(x,ptr,ret) ({ \
-if (put_user(x,ptr)) return ret; })
-
-#define get_user_ret(x,ptr,ret) ({ \
-if (get_user(x,ptr)) return ret; })
-
-#define __put_user_ret(x,ptr,ret) ({ \
-if (__put_user(x,ptr)) return ret; })
-
-#define __get_user_ret(x,ptr,ret) ({ \
-if (__get_user(x,ptr)) return ret; })
-
-
-
-extern long __put_user_bad(void);
-
-#define __put_user_nocheck(x,ptr,size)			\
-({							\
-	long __pu_err;					\
-	__put_user_size((x),(ptr),(size),__pu_err);	\
-	__pu_err;					\
-})
-
-#define __put_user_check(x,ptr,size)				\
-({								\
-	long __pu_err = -EFAULT;				\
-	__typeof__(*(ptr)) *__pu_addr = (ptr);			\
-	if (access_ok(VERIFY_WRITE,__pu_addr,size))		\
-		__put_user_size((x),__pu_addr,(size),__pu_err);	\
-	__pu_err;						\
+extern void __get_user_1(void);
+extern void __get_user_2(void);
+extern void __get_user_4(void);
+
+#define __get_user_x(size,ret,x,ptr) \
+	__asm__ __volatile__("call __get_user_" #size \
+		:"=a" (ret),"=d" (x) \
+		:"0" (ptr))
+
+/* Careful: we have to cast the result to the type of the pointer for sign reasons */
+#define get_user(x,ptr)							\
+({	int __ret_gu,__val_gu;						\
+	switch(sizeof (*(ptr))) {					\
+	case 1:  __get_user_x(1,__ret_gu,__val_gu,ptr); break;		\
+	case 2:  __get_user_x(2,__ret_gu,__val_gu,ptr); break;		\
+	case 4:  __get_user_x(4,__ret_gu,__val_gu,ptr); break;		\
+	default: __get_user_x(X,__ret_gu,__val_gu,ptr); break;		\
+	}								\
+	(x) = (__typeof__(*(ptr)))__val_gu;				\
+	__ret_gu;							\
 })
 
-#define __put_user_size(x,ptr,size,retval)				\
-do {									\
-	retval = 0;							\
-	switch (size) {							\
-	  case 1: __put_user_asm(x,ptr,retval,"b","b","iq"); break;	\
-	  case 2: __put_user_asm(x,ptr,retval,"w","w","ir"); break;	\
-	  case 4: __put_user_asm(x,ptr,retval,"l","","ir"); break;	\
-	  default: __put_user_bad();					\
+extern void __put_user_1(void);
+extern void __put_user_2(void);
+extern void __put_user_4(void);
+
+#define __put_user_x(size,ret,x,ptr)					\
+	__asm__ __volatile__("call __put_user_" #size			\
+		:"=a" (ret)						\
+		:"0" (ptr),"d" (x)					\
+		:"cx")
+
+#define put_user(x,ptr)							\
+({	int __ret_pu;							\
+	switch(sizeof (*(ptr))) {					\
+	case 1:  __put_user_x(1,__ret_pu,(char)(x),ptr); break;		\
+	case 2:  __put_user_x(2,__ret_pu,(short)(x),ptr); break;	\
+	case 4:  __put_user_x(4,__ret_pu,(int)(x),ptr); break;		\
+	default: __put_user_x(X,__ret_pu,x,ptr); break;			\
 	}								\
-} while (0)
+	__ret_pu;							\
+})
 
-struct __large_struct { unsigned long buf[100]; };
-#define __m(x) (*(struct __large_struct *)(x))
+#define __get_user(x,ptr) get_user(x,ptr)
+#define __put_user(x,ptr) put_user(x,ptr)
 
 /*
- * Tell gcc we read from memory instead of writing: this is because
- * we do not write to any memory gcc knows about, so there are no
- * aliasing issues.
+ * The "xxx_ret" versions return constant specified in third argument, if
+ * something bad happens. These macros can be optimized for the
+ * case of just returning from the function xxx_ret is used.
  */
-#define __put_user_asm(x, addr, err, itype, rtype, ltype)	\
-	__asm__ __volatile__(					\
-		"1:	mov"itype" %"rtype"1,%2\n"		\
-		"2:\n"						\
-		".section .fixup,\"ax\"\n"			\
-		"3:	movl %3,%0\n"				\
-		"	jmp 2b\n"				\
-		".previous\n"					\
-		".section __ex_table,\"a\"\n"			\
-		"	.align 4\n"				\
-		"	.long 1b,3b\n"				\
-		".previous"					\
-		: "=r"(err)					\
-		: ltype (x), "m"(__m(addr)), "i"(-EFAULT), "0"(err))
-
-
-#define __get_user_nocheck(x,ptr,size)				\
-({								\
-	long __gu_err, __gu_val;				\
-	__get_user_size(__gu_val,(ptr),(size),__gu_err);	\
-	(x) = (__typeof__(*(ptr)))__gu_val;			\
-	__gu_err;						\
-})
 
-#define __get_user_check(x,ptr,size)					\
-({									\
-	long __gu_err = -EFAULT, __gu_val = 0;				\
-	const __typeof__(*(ptr)) *__gu_addr = (ptr);			\
-	if (access_ok(VERIFY_READ,__gu_addr,size))			\
-		__get_user_size(__gu_val,__gu_addr,(size),__gu_err);	\
-	(x) = (__typeof__(*(ptr)))__gu_val;				\
-	__gu_err;							\
-})
+#define put_user_ret(x,ptr,ret) ({ if (put_user(x,ptr)) return ret; })
 
-extern long __get_user_bad(void);
+#define get_user_ret(x,ptr,ret) ({ if (get_user(x,ptr)) return ret; })
 
-#define __get_user_size(x,ptr,size,retval)				\
-do {									\
-	retval = 0;							\
-	switch (size) {							\
-	  case 1: __get_user_asm(x,ptr,retval,"b","b","=q"); break;	\
-	  case 2: __get_user_asm(x,ptr,retval,"w","w","=r"); break;	\
-	  case 4: __get_user_asm(x,ptr,retval,"l","","=r"); break;	\
-	  default: (x) = __get_user_bad();				\
-	}								\
-} while (0)
+#define __put_user_ret(x,ptr,ret) ({ if (__put_user(x,ptr)) return ret; })
 
-#define __get_user_asm(x, addr, err, itype, rtype, ltype)	\
-	__asm__ __volatile__(					\
-		"1:	mov"itype" %2,%"rtype"1\n"		\
-		"2:\n"						\
-		".section .fixup,\"ax\"\n"			\
-		"3:	movl %3,%0\n"				\
-		"	xor"itype" %"rtype"1,%"rtype"1\n"	\
-		"	jmp 2b\n"				\
-		".previous\n"					\
-		".section __ex_table,\"a\"\n"			\
-		"	.align 4\n"				\
-		"	.long 1b,3b\n"				\
-		".previous"					\
-		: "=r"(err), ltype (x)				\
-		: "m"(__m(addr)), "i"(-EFAULT), "0"(err))
+#define __get_user_ret(x,ptr,ret) ({ if (__get_user(x,ptr)) return ret; })
 
 
 /*
@@ -252,6 +183,24 @@
 		: "r"(size & 3), "0"(size / 4), "D"(to), "S"(from)	\
 		: "di", "si", "memory")
 
+/* We let the __ versions of copy_from/to_user inline, because they're often
+ * used in fast paths and have only a small space overhead.
+ */
+static inline unsigned long
+__generic_copy_from_user_nocheck(void *to, const void *from, unsigned long n)
+{
+	__copy_user(to,from,n);
+	return n;
+}
+
+static inline unsigned long
+__generic_copy_to_user_nocheck(void *to, const void *from, unsigned long n)
+{
+	__copy_user(to,from,n);
+	return n;
+}
+
+
 /* Optimize just a little bit when we know the size of the move. */
 #define __constant_copy_user(to, from, size)			\
 do {								\
@@ -335,13 +284,8 @@
 	}							\
 } while (0)
 
-static inline unsigned long
-__generic_copy_to_user(void *to, const void *from, unsigned long n)
-{
-	if (access_ok(VERIFY_WRITE, to, n))
-		__copy_user(to,from,n);
-	return n;
-}
+unsigned long __generic_copy_to_user(void *, const void *, unsigned long);
+unsigned long __generic_copy_from_user(void *, const void *, unsigned long);
 
 static inline unsigned long
 __constant_copy_to_user(void *to, const void *from, unsigned long n)
@@ -352,14 +296,6 @@
 }
 
 static inline unsigned long
-__generic_copy_from_user(void *to, const void *from, unsigned long n)
-{
-	if (access_ok(VERIFY_READ, from, n))
-		__copy_user(to,from,n);
-	return n;
-}
-
-static inline unsigned long
 __constant_copy_from_user(void *to, const void *from, unsigned long n)
 {
 	if (access_ok(VERIFY_READ, from, n))
@@ -368,13 +304,6 @@
 }
 
 static inline unsigned long
-__generic_copy_to_user_nocheck(void *to, const void *from, unsigned long n)
-{
-	__copy_user(to,from,n);
-	return n;
-}
-
-static inline unsigned long
 __constant_copy_to_user_nocheck(void *to, const void *from, unsigned long n)
 {
 	__constant_copy_user(to,from,n);
@@ -382,13 +311,6 @@
 }
 
 static inline unsigned long
-__generic_copy_from_user_nocheck(void *to, const void *from, unsigned long n)
-{
-	__copy_user(to,from,n);
-	return n;
-}
-
-static inline unsigned long
 __constant_copy_from_user_nocheck(void *to, const void *from, unsigned long n)
 {
 	__constant_copy_user(to,from,n);
@@ -405,15 +327,9 @@
 	 __constant_copy_from_user((to),(from),(n)) :	\
 	 __generic_copy_from_user((to),(from),(n)))
 
-#define copy_to_user_ret(to,from,n,retval) ({ \
-if (copy_to_user(to,from,n)) \
-	return retval; \
-})
+#define copy_to_user_ret(to,from,n,retval) ({ if (copy_to_user(to,from,n)) return retval; })
 
-#define copy_from_user_ret(to,from,n,retval) ({ \
-if (copy_from_user(to,from,n)) \
-	return retval; \
-})
+#define copy_from_user_ret(to,from,n,retval) ({ if (copy_from_user(to,from,n)) return retval; })
 
 #define __copy_to_user(to,from,n)			\
 	(__builtin_constant_p(n) ?			\
@@ -425,116 +341,10 @@
 	 __constant_copy_from_user_nocheck((to),(from),(n)) :	\
 	 __generic_copy_from_user_nocheck((to),(from),(n)))
 
-
-/*
- * Zero Userspace
- */
-
-#define __do_clear_user(addr,size)						\
-	__asm__ __volatile__(						\
-		"0:	rep; stosl\n"					\
-		"	movl %1,%0\n"					\
-		"1:	rep; stosb\n"					\
-		"2:\n"							\
-		".section .fixup,\"ax\"\n"				\
-		"3:	lea 0(%1,%0,4),%0\n"				\
-		"	jmp 2b\n"					\
-		".previous\n"						\
-		".section __ex_table,\"a\"\n"				\
-		"	.align 4\n"					\
-		"	.long 0b,3b\n"					\
-		"	.long 1b,2b\n"					\
-		".previous"						\
-		: "=c"(size)						\
-		: "r"(size & 3), "0"(size / 4), "D"(addr), "a"(0)	\
-		: "di")
-
-static inline unsigned long
-clear_user(void *to, unsigned long n)
-{
-	if (access_ok(VERIFY_WRITE, to, n))
-		__do_clear_user(to, n);
-	return n;
-}
-
-static inline unsigned long
-__clear_user(void *to, unsigned long n)
-{
-	__do_clear_user(to, n);
-	return n;
-}
-
-
-/*
- * Copy a null terminated string from userspace.
- */
-
-#define __do_strncpy_from_user(dst,src,count,res)			   \
-	__asm__ __volatile__(						   \
-		"	testl %1,%1\n"					   \
-		"	jz 2f\n"					   \
-		"0:	lodsb\n"					   \
-		"	stosb\n"					   \
-		"	testb %%al,%%al\n"				   \
-		"	jz 1f\n"					   \
-		"	decl %1\n"					   \
-		"	jnz 0b\n"					   \
-		"1:	subl %1,%0\n"					   \
-		"2:\n"							   \
-		".section .fixup,\"ax\"\n"				   \
-		"3:	movl %2,%0\n"					   \
-		"	jmp 2b\n"					   \
-		".previous\n"						   \
-		".section __ex_table,\"a\"\n"				   \
-		"	.align 4\n"					   \
-		"	.long 0b,3b\n"					   \
-		".previous"						   \
-		: "=d"(res), "=c"(count)				   \
-		: "i"(-EFAULT), "0"(count), "1"(count), "S"(src), "D"(dst) \
-		: "si", "di", "ax", "memory")
-
-static inline long
-__strncpy_from_user(char *dst, const char *src, long count)
-{
-	long res;
-	__do_strncpy_from_user(dst, src, count, res);
-	return res;
-}
-
-static inline long
-strncpy_from_user(char *dst, const char *src, long count)
-{
-	long res = -EFAULT;
-	if (access_ok(VERIFY_READ, src, 1))
-		__do_strncpy_from_user(dst, src, count, res);
-	return res;
-}
-
-/*
- * Return the size of a string (including the ending 0)
- *
- * Return 0 for error
- */
-
-extern inline long strlen_user(const char *s)
-{
-	unsigned long res;
-
-	__asm__ __volatile__(
-		"0:	repne; scasb\n"
-		"	notl %0\n"
-		"1:\n"
-		".section .fixup,\"ax\"\n"
-		"2:	xorl %0,%0\n"
-		"	jmp 1b\n"
-		".previous\n"
-		".section __ex_table,\"a\"\n"
-		"	.align 4\n"
-		"	.long 0b,2b\n"
-		".previous"
-		:"=c" (res), "=D" (s)
-		:"1" (s), "a" (0), "0" (-__addr_ok(s)));
-	return res & -__addr_ok(s);
-}
+long strncpy_from_user(char *dst, const char *src, long count);
+long __strncpy_from_user(char *dst, const char *src, long count);
+long strlen_user(const char *str);
+unsigned long clear_user(void *mem, unsigned long len);
+unsigned long __clear_user(void *mem, unsigned long len);
 
 #endif /* __i386_UACCESS_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-m68k/elf.h linux/include/asm-m68k/elf.h
--- v2.1.78/linux/include/asm-m68k/elf.h	Wed Sep 24 20:05:48 1997
+++ linux/include/asm-m68k/elf.h	Mon Jan 12 14:52:46 1998
@@ -40,7 +40,7 @@
    the loader.  We need to make sure that it is out of the way of the program
    that it will "exec", and that there is sufficient room for the brk.  */
 
-#define ELF_ET_DYN_BASE         (2 * TASK_SIZE / 3)
+#define ELF_ET_DYN_BASE         0xD0000000UL
 
 #define ELF_CORE_COPY_REGS(pr_reg, regs)				\
 	/* Bleech. */							\
@@ -67,5 +67,21 @@
 	  pr_reg[12] = sw->a5;						\
 	  pr_reg[13] = sw->a6;						\
 	}
+
+/* This yields a mask that user programs can use to figure out what
+   instruction set this cpu supports.  */
+
+#define ELF_HWCAP	(0)
+
+/* This yields a string that ld.so will use to load implementation
+   specific libraries for optimization.  This is more specific in
+   intent than poking at uname or /proc/cpuinfo.  */
+
+#define ELF_PLATFORM  (NULL)
+
+#ifdef __KERNEL__
+#define SET_PERSONALITY(ibcs2) \
+	current->personality = (ibcs2 ? PER_SVR4 : PER_LINUX)
+#endif
 
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/adb.h linux/include/asm-ppc/adb.h
--- v2.1.78/linux/include/asm-ppc/adb.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/adb.h	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,57 @@
+/*
+ * Definitions for ADB (Apple Desktop Bus) support.
+ */
+#ifndef __PPC_ADB_H
+#define __PPC_ADB_H
+
+/* ADB commands */
+#define ADB_BUSRESET		0
+#define ADB_FLUSH(id)		(1 + ((id) << 4))
+#define ADB_WRITEREG(id, reg)	(8 + (reg) + ((id) << 4))
+#define ADB_READREG(id, reg)	(0xc + (reg) + ((id) << 4))
+
+/* ADB default device IDs (upper 4 bits of ADB command byte) */
+#define ADB_DONGLE	1	/* "software execution control" devices */
+#define ADB_KEYBOARD	2
+#define ADB_MOUSE	3
+#define ADB_TABLET	4
+#define ADB_MODEM	5
+#define ADB_MISC	7	/* maybe a monitor */
+
+
+#ifdef __KERNEL__
+
+struct adb_request {
+    unsigned char data[16];
+    int nbytes;
+    unsigned char reply[16];
+    int reply_len;
+    unsigned char reply_expected;
+    unsigned char sent;
+    unsigned char complete;
+    void (*done)(struct adb_request *);
+    void *arg;
+    struct adb_request *next;
+};
+
+extern enum adb_hw {
+	ADB_NONE, ADB_VIACUDA, ADB_VIAPMU, ADB_MACIO
+} adb_hardware;
+
+extern int (*adb_send_request)(struct adb_request *req, int sync);
+extern int (*adb_autopoll)(int on);
+
+/* Values for adb_request flags */
+#define ADBREQ_REPLY	1	/* expect reply */
+#define ADBREQ_SYNC	2	/* poll until done */
+
+void adb_init(void);
+int adb_request(struct adb_request *req, void (*done)(struct adb_request *),
+		int flags, int nbytes, ...);
+int adb_register(int default_id,
+		 void (*handler)(unsigned char *, int, struct pt_regs *, int));
+void adb_input(unsigned char *, int, struct pt_regs *, int);
+
+#endif /* __KERNEL__ */
+
+#endif /* __PPC_ADB_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/adb_mouse.h linux/include/asm-ppc/adb_mouse.h
--- v2.1.78/linux/include/asm-ppc/adb_mouse.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/adb_mouse.h	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,23 @@
+#ifndef _LINUX_ADB_MOUSE_H
+#define _LINUX_ADB_MOUSE_H
+
+/*
+ * linux/include/linux/mac_mouse.h
+ * header file for Macintosh ADB mouse driver
+ * 27-10-97 Michael Schmitz
+ * copied from:
+ * header file for Atari Mouse driver
+ * by Robert de Vries (robert@and.nl) on 19Jul93
+ */
+
+struct mouse_status {
+	char		buttons;
+	short		dx;
+	short		dy;
+	int		ready;
+	int		active;
+	struct wait_queue *wait;
+	struct fasync_struct *fasyncptr;
+};
+
+#endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/atomic.h linux/include/asm-ppc/atomic.h
--- v2.1.78/linux/include/asm-ppc/atomic.h	Mon Aug  4 16:25:39 1997
+++ linux/include/asm-ppc/atomic.h	Mon Jan 12 15:18:13 1998
@@ -17,6 +17,7 @@
 #define atomic_set(v,i)		(((v)->counter) = (i))
 
 extern void atomic_add(int a, atomic_t *v);
+extern int  atomic_add_return(int a, atomic_t *v);
 extern void atomic_sub(int a, atomic_t *v);
 extern void atomic_inc(atomic_t *v);
 extern int  atomic_inc_return(atomic_t *v);
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/cache.h linux/include/asm-ppc/cache.h
--- v2.1.78/linux/include/asm-ppc/cache.h	Mon Aug  4 16:25:39 1997
+++ linux/include/asm-ppc/cache.h	Mon Jan 12 15:18:13 1998
@@ -5,9 +5,8 @@
 #define __ARCH_PPC_CACHE_H
 
 /* bytes per L1 cache line */
-/* a guess */ /* a correct one -- Cort */
 #define        L1_CACHE_BYTES  32      
-
 #define        L1_CACHE_ALIGN(x)       (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1))
+#define L1_CACHE_PAGES		8
 
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/cuda.h linux/include/asm-ppc/cuda.h
--- v2.1.78/linux/include/asm-ppc/cuda.h	Mon Aug 18 18:19:46 1997
+++ linux/include/asm-ppc/cuda.h	Mon Jan 12 15:18:13 1998
@@ -13,20 +13,6 @@
 #define POWER_PACKET	4
 #define MACIIC_PACKET	5
 
-/* ADB commands (2nd byte) */
-#define ADB_BUSRESET		0
-#define ADB_FLUSH(id)		(1 + ((id) << 4))
-#define ADB_WRITEREG(id, reg)	(8 + (reg) + ((id) << 4))
-#define ADB_READREG(id, reg)	(0xc + (reg) + ((id) << 4))
-
-/* ADB default device IDs (upper 4 bits of 2nd byte) */
-#define ADB_DONGLE	1	/* "software execution control" devices */
-#define ADB_KEYBOARD	2
-#define ADB_MOUSE	3
-#define ADB_TABLET	4
-#define ADB_MODEM	5
-#define ADB_MISC	7	/* maybe a monitor */
-
 /* CUDA commands (2nd byte) */
 #define CUDA_WARM_START		0
 #define CUDA_AUTOPOLL		1
@@ -50,25 +36,10 @@
 
 #ifdef __KERNEL__
 
-struct cuda_request {
-    unsigned char data[16];
-    int nbytes;
-    unsigned char reply[16];
-    int reply_len;
-    unsigned char reply_expected;
-    unsigned char sent;
-    unsigned char got_reply;
-    void (*done)(struct cuda_request *);
-    void *arg;
-    struct cuda_request *next;
-};
-
 void via_cuda_init(void);
-int cuda_request(struct cuda_request *req,
-		 void (*done)(struct cuda_request *), int nbytes, ...);
-int cuda_send_request(struct cuda_request *req);
+int cuda_request(struct adb_request *req,
+		 void (*done)(struct adb_request *), int nbytes, ...);
+int cuda_send_request(struct adb_request *req);
 void cuda_poll(void);
-int adb_register(int default_id,
-		 void (*handler)(unsigned char *, int, struct pt_regs *));
 
 #endif	/* __KERNEL */
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/delay.h linux/include/asm-ppc/delay.h
--- v2.1.78/linux/include/asm-ppc/delay.h	Mon Aug  4 16:25:39 1997
+++ linux/include/asm-ppc/delay.h	Mon Jan 12 15:18:13 1998
@@ -29,4 +29,3 @@
 }
 
 #endif /* defined(_PPC_DELAY_H) */
-
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/dma.h linux/include/asm-ppc/dma.h
--- v2.1.78/linux/include/asm-ppc/dma.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/dma.h	Mon Jan 12 15:18:13 1998
@@ -3,9 +3,11 @@
  * Written by Hennus Bergman, 1992.
  * High DMA channel support & info by Hannu Savolainen
  * and John Boyd, Nov. 1992.
+ * Changes for ppc sound by Christoph Nadig
  */
 
 #include <linux/config.h>
+#include <asm/io.h>
 
 /*
  * Note: Adapted for PowerPC by Gary Thomas
@@ -31,8 +33,31 @@
 /* Doesn't really apply... */
 #define MAX_DMA_ADDRESS      0xFFFFFFFF
 
-#if defined(CONFIG_PREP) || defined(CONFIG_CHRP)
-#include <asm/io.h>		/* need byte IO */
+#if defined(CONFIG_MACH_SPECIFIC)
+
+#if defined(CONFIG_PREP)
+#define DMA_MODE_READ 0x44
+#define DMA_MODE_WRITE 0x48
+#define ISA_DMA_THRESHOLD 0x00ffffff
+#endif /* CONFIG_PREP */
+
+#if defined(CONFIG_CHRP)
+#define DMA_MODE_READ 0x44
+#define DMA_MODE_WRITE 0x48
+#define ISA_DMA_THRESHOLD ~0L
+#endif /* CONFIG_CHRP */
+
+#ifdef CONFIG_PMAC
+#define DMA_MODE_READ 1
+#define DMA_MODE_WRITE 2
+#define ISA_DMA_THRESHOLD ~0L
+#endif /* CONFIG_PMAC */
+
+#else
+/* in arch/ppc/kernel/setup.c -- Cort */
+extern unsigned long DMA_MODE_WRITE, DMA_MODE_READ;
+extern unsigned long ISA_DMA_THRESHOLD;
+#endif
 
 
 #ifdef HAVE_REALLY_SLOW_DMA_CONTROLLER
@@ -92,8 +117,20 @@
  *
  */
 
-#define POWERSTACK_SND_DMA 6
-#define POWERSTACK_SND_DMA2 7
+/* used in nasty hack for sound - see prep_setup_arch() -- Cort */
+extern long ppc_cs4232_dma, ppc_cs4232_dma2;
+#ifdef CONFIG_CS4232
+#define SND_DMA1 ppc_cs4232_dma
+#define SND_DMA2 ppc_cs4232_dma2
+#else
+#ifdef CONFIG_MSS
+#define SND_DMA1 MSS_DMA
+#define SND_DMA2 MSS_DMA2
+#else
+#define SND_DMA1 -1
+#define SND_DMA2 -1
+#endif
+#endif
 
 /* 8237 DMA controllers */
 #define IO_DMA1_BASE	0x00	/* 8 bit slave DMA, channels 0..3 */
@@ -159,8 +196,6 @@
 #define DMA1_EXT_REG               0x40B
 #define DMA2_EXT_REG               0x4D6
 
-#define DMA_MODE_READ	0x44	/* I/O to memory, no autoinit, increment, single mode */
-#define DMA_MODE_WRITE	0x48	/* memory to I/O, no autoinit, increment, single mode */
 #define DMA_MODE_CASCADE 0xC0   /* pass thru DREQ->HRQ, DACK<-HLDA only */
 
 /* enable/disable a specific DMA channel */
@@ -235,20 +270,24 @@
 			break;
 		case 3:
 			dma_outb(pagenr, DMA_LO_PAGE_3);
+			dma_outb(pagenr>>8, DMA_HI_PAGE_3); 
 			break;
 	        case 5:
-			dma_outb(pagenr & 0xfe, DMA_LO_PAGE_5);
+		        if (SND_DMA1 == 5 || SND_DMA2 == 5)
+				dma_outb(pagenr, DMA_LO_PAGE_5);
+			else
+				dma_outb(pagenr & 0xfe, DMA_LO_PAGE_5);
                         dma_outb(pagenr>>8, DMA_HI_PAGE_5);
 			break;
 		case 6:
-		        if (POWERSTACK_SND_DMA == 6 || POWERSTACK_SND_DMA2 == 6)
+		        if (SND_DMA1 == 6 || SND_DMA2 == 6)
 				dma_outb(pagenr, DMA_LO_PAGE_6);
 			else
 				dma_outb(pagenr & 0xfe, DMA_LO_PAGE_6);
 			dma_outb(pagenr>>8, DMA_HI_PAGE_6);
 			break;
 		case 7:
-			if (POWERSTACK_SND_DMA == 7 || POWERSTACK_SND_DMA2 == 7)
+			if (SND_DMA1 == 7 || SND_DMA2 == 7)
 				dma_outb(pagenr, DMA_LO_PAGE_7);
 			else
 				dma_outb(pagenr & 0xfe, DMA_LO_PAGE_7);
@@ -267,7 +306,7 @@
 	    dma_outb( phys & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
             dma_outb( (phys>>8) & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
 	}  else  {
-	  if (dmanr == POWERSTACK_SND_DMA || dmanr == POWERSTACK_SND_DMA2) {
+	  if (dmanr == SND_DMA1 || dmanr == SND_DMA2) {
 	    dma_outb( phys  & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
 	    dma_outb( (phys>>8)  & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
 	    dma_outb( (dmanr&3), DMA2_EXT_REG);
@@ -295,7 +334,7 @@
 	    dma_outb( count & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
 	    dma_outb( (count>>8) & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
         } else {
-	  if (dmanr == POWERSTACK_SND_DMA || dmanr == POWERSTACK_SND_DMA2) {
+	  if (dmanr == SND_DMA1 || dmanr == SND_DMA2) {
 	    dma_outb( count & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
 	    dma_outb( (count>>8) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
 	  } else {
@@ -325,17 +364,9 @@
 	count = 1 + dma_inb(io_port);
 	count += dma_inb(io_port) << 8;
 	
-	return (dmanr<=3)? count : (count<<1);
+	return (dmanr <= 3 || dmanr == SND_DMA1 || dmanr == SND_DMA2)
+	  ? count : (count<<1);
 }
-
-#endif /* CONFIG_PREP || CONFIG_CHRP */
-
-#ifdef CONFIG_PMAC
-
-#define DMA_MODE_READ	1
-#define DMA_MODE_WRITE	2
-
-#endif /* CONFIG_PMAC */
 
 /* These are in kernel/dma.c: */
 extern void free_dma(unsigned int dmanr);	/* release it again */
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/elf.h linux/include/asm-ppc/elf.h
--- v2.1.78/linux/include/asm-ppc/elf.h	Wed Sep 24 20:05:48 1997
+++ linux/include/asm-ppc/elf.h	Mon Jan 12 15:18:13 1998
@@ -42,4 +42,41 @@
 	       sizeof(struct pt_regs) < sizeof(elf_gregset_t)? \
 	       sizeof(struct pt_regs): sizeof(elf_gregset_t));
 
+
+/* This yields a mask that user programs can use to figure out what
+   instruction set this cpu supports.  This could be done in userspace,
+   but it's not easy, and we've already done it here.  */
+
+#define ELF_HWCAP	(0)
+
+/* This yields a string that ld.so will use to load implementation
+   specific libraries for optimization.  This is more specific in
+   intent than poking at uname or /proc/cpuinfo.
+
+   For the moment, we have only optimizations for the Intel generations,
+   but that could change... */
+
+#define ELF_PLATFORM	(NULL)
+
+#ifdef __KERNEL__
+#define SET_PERSONALITY(ibcs2) current->personality = (ibcs2 ? PER_SVR4 : PER_LINUX)
+#endif
+
+
+/* This yields a mask that user programs can use to figure out what
+   instruction set this cpu supports.  */
+
+#define ELF_HWCAP	(0)
+
+/* This yields a string that ld.so will use to load implementation
+   specific libraries for optimization.  This is more specific in
+   intent than poking at uname or /proc/cpuinfo.  */
+
+#define ELF_PLATFORM	(NULL)
+
+#ifdef __KERNEL__
+#define SET_PERSONALITY(ibcs2) \
+	current->personality = (ibcs2 ? PER_SVR4 : PER_LINUX)
+#endif
+
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/gg2.h linux/include/asm-ppc/gg2.h
--- v2.1.78/linux/include/asm-ppc/gg2.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/gg2.h	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,59 @@
+/*
+ *  asm-ppc/gg2.h -- VLSI VAS96011/12 `Golden Gate 2' register definitions
+ *
+ *  Copyright (C) 1997 Geert Uytterhoeven
+ *
+ *  This file is based on the following documentation:
+ *
+ *	The VAS96011/12 Chipset, Data Book, Edition 1.0
+ *	VLSI Technology, Inc.
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive
+ *  for more details.
+ */
+
+#ifndef _ASMPPC_GG2_H
+#define _ASMPPC_GG2_H
+
+    /*
+     *  Memory Map (CHRP mode)
+     */
+
+#define GG2_PCI_MEM_BASE	0xc0000000	/* Peripheral memory space */
+#define GG2_ISA_MEM_BASE	0xf7000000	/* Peripheral memory alias */
+#define GG2_ISA_IO_BASE		0xf8000000	/* Peripheral I/O space */
+#define GG2_PCI_CONFIG_BASE	0xfec00000	/* PCI configuration space */
+#define GG2_INT_ACK_SPECIAL	0xfec80000	/* Interrupt acknowledge and */
+						/* special PCI cycles */
+#define GG2_ROM_BASE0		0xff000000	/* ROM bank 0 */
+#define GG2_ROM_BASE1		0xff800000	/* ROM bank 1 */
+
+
+    /*
+     *  GG2 specific PCI Registers
+     */
+
+#define GG2_PCI_BUSNO		0x40	/* Bus number */
+#define GG2_PCI_SUBBUSNO	0x41	/* Subordinate bus number */
+#define GG2_PCI_DISCCTR		0x42	/* Disconnect counter */
+#define GG2_PCI_PPC_CTRL	0x50	/* PowerPC interface control register */
+#define GG2_PCI_ADDR_MAP	0x5c	/* Address map */
+#define GG2_PCI_PCI_CTRL	0x60	/* PCI interface control register */
+#define GG2_PCI_ROM_CTRL	0x70	/* ROM interface control register */
+#define GG2_PCI_ROM_TIME	0x74	/* ROM timing */
+#define GG2_PCI_CC_CTRL		0x80	/* Cache controller control register */
+#define GG2_PCI_DRAM_BANK0	0x90	/* Control register for DRAM bank #0 */
+#define GG2_PCI_DRAM_BANK1	0x94	/* Control register for DRAM bank #1 */
+#define GG2_PCI_DRAM_BANK2	0x98	/* Control register for DRAM bank #2 */
+#define GG2_PCI_DRAM_BANK3	0x9c	/* Control register for DRAM bank #3 */
+#define GG2_PCI_DRAM_BANK4	0xa0	/* Control register for DRAM bank #4 */
+#define GG2_PCI_DRAM_BANK5	0xa4	/* Control register for DRAM bank #5 */
+#define GG2_PCI_DRAM_TIME0	0xb0	/* Timing parameters set #0 */
+#define GG2_PCI_DRAM_TIME1	0xb4	/* Timing parameters set #1 */
+#define GG2_PCI_DRAM_CTRL	0xc0	/* DRAM control */
+#define GG2_PCI_ERR_CTRL	0xd0	/* Error control register */
+#define GG2_PCI_ERR_STATUS	0xd4	/* Error status register */
+					/* Cleared when read */
+
+#endif /* _ASMPPC_GG2_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/hardirq.h linux/include/asm-ppc/hardirq.h
--- v2.1.78/linux/include/asm-ppc/hardirq.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/hardirq.h	Mon Jan 12 15:18:13 1998
@@ -21,7 +21,8 @@
 extern atomic_t global_irq_count;
 
 #define release_irqlock(cpu)				\
-do {	if(global_irq_holder == (unsigned char) cpu) {	\
+do {							\
+	if(global_irq_holder == (unsigned char) cpu) {	\
 		global_irq_holder = NO_PROC_ID;		\
 		spin_unlock(&global_irq_lock);		\
 	}						\
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/hydra.h linux/include/asm-ppc/hydra.h
--- v2.1.78/linux/include/asm-ppc/hydra.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/hydra.h	Mon Jan 12 15:18:13 1998
@@ -1,12 +1,8 @@
 /*
- *  linux/hydra.h -- Mac I/O `Hydra' definitions
+ *  asm-ppc/hydra.h -- Mac I/O `Hydra' definitions
  *
  *  Copyright (C) 1997 Geert Uytterhoeven
  *
- *  This file is subject to the terms and conditions of the GNU General Public
- *  License.  See the file COPYING in the main directory of this archive
- *  for more details.
- *
  *  This file is based on the following documentation:
  *
  *	Macintosh Technology in the Common Hardware Reference Platform
@@ -98,8 +94,9 @@
 #define HYDRA_INT_EXT7		18
 #define HYDRA_INT_SPARE		19
 
+extern int hydra_init(void);
+extern void macio_adb_init(void);
+
 #endif /* __KERNEL__ */
-volatile struct Hydra *find_hydra(void);
-void hydra_post_openpic_init(void);
 
 #endif /* _ASMPPC_HYDRA_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/ide.h linux/include/asm-ppc/ide.h
--- v2.1.78/linux/include/asm-ppc/ide.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/ide.h	Mon Jan 12 15:18:13 1998
@@ -13,22 +13,32 @@
 
 #ifdef __KERNEL__
 
-#include <linux/config.h>
+#include <linux/ioport.h>
+#include <asm/io.h>		/* so we can redefine insw/outsw */
 
 #ifndef MAX_HWIFS
 #define MAX_HWIFS	4
 #endif
 
+#undef	SUPPORT_SLOW_DATA_PORTS
+#define	SUPPORT_SLOW_DATA_PORTS	0
+#undef	SUPPORT_VLB_SYNC
+#define SUPPORT_VLB_SYNC	0
+
+
 #define ide_sti()	sti()
 
-typedef unsigned short ide_ioreg_t;
+typedef unsigned long ide_ioreg_t;
 void ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
 void prep_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
 void pmac_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
+void ide_insw(ide_ioreg_t port, void *buf, int ns);
+void ide_outsw(ide_ioreg_t port, void *buf, int ns);
 
-#if defined(CONFIG_PREP) || defined(CONFIG_CHRP)
 static __inline__ int ide_default_irq(ide_ioreg_t base)
 {
+	if ( _machine == _MACH_Pmac )
+		return 0;
 	switch (base) {
 		case 0x1f0: return 13;
 		case 0x170: return 13;
@@ -41,6 +51,8 @@
 
 static __inline__ ide_ioreg_t ide_default_io_base(int index)
 {
+	if ( _machine == _MACH_Pmac )
+		return index;
 	switch (index) {
 		case 0:	return 0x1f0;
 		case 1:	return 0x170;
@@ -51,53 +63,52 @@
 	}
 }
 
-typedef union {
-	unsigned all			: 8;	/* all of the bits together */
-	struct {
-		unsigned head		: 4;	/* always zeros here */
-		unsigned unit		: 1;	/* drive select number, 0 or 1 */
-		unsigned bit5		: 1;	/* always 1 */
-		unsigned lba		: 1;	/* using LBA instead of CHS */
-		unsigned bit7		: 1;	/* always 1 */
-	} b;
-} select_t;
-
 static __inline__ int ide_check_region (ide_ioreg_t from, unsigned int extent)
 {
+	if ( _machine == _MACH_Pmac )
+		return 0;
 	return check_region(from, extent);
 }
 
 static __inline__ void ide_request_region (ide_ioreg_t from, unsigned int extent, const char *name)
 {
+	if ( _machine == _MACH_Pmac )
+		return;
 	request_region(from, extent, name);
 }
 
 static __inline__ void ide_release_region (ide_ioreg_t from, unsigned int extent)
 {
+	if ( _machine == _MACH_Pmac )
+		return;
 	release_region(from, extent);
 }
 
-#define ide_fix_driveid(id)		do {} while (0)
-
-#endif /* CONFIG_CHRP || CONFIG_PREP */
-
-
-#ifdef CONFIG_PMAC
-
-#include <asm/io.h>		/* so we can redefine insw/outsw */
-
-typedef unsigned long ide_ioreg_t;
+#define ide_fix_driveid(id)	do {			\
+	int nh;						\
+	unsigned short *p = (unsigned short *) id;	\
+	if ( _machine == _MACH_Pmac )			\
+		for (nh = SECTOR_WORDS * 2; nh != 0; --nh, ++p)	\
+			*p = (*p << 8) + (*p >> 8);	\
+} while (0)
 
-static __inline__ int ide_default_irq(ide_ioreg_t base)
-{
-	return 0;
-}
-
-extern __inline__ ide_ioreg_t ide_default_io_base(int index)
-{
-	return index;
-}
 
+#undef insw
+#define insw(port, buf, ns) 	do {			\
+	if ( _machine != _MACH_Pmac )			\
+		/* this must be the same as insw in io.h!! */	\
+		_insw((unsigned short *)((port)+_IO_BASE), (buf), (ns)); \
+	else						\
+		ide_insw((port), (buf), (ns));		\
+} while (0)
+#undef outsw
+#define outsw(port, buf, ns) 	do {			\
+	if ( _machine != _MACH_Pmac )			\
+		/* this must be the same as outsw in io.h!! */	\
+		_outsw((unsigned short *)((port)+_IO_BASE), (buf), (ns)); \
+	else						\
+		ide_outsw((port), (buf), (ns));		\
+} while (0)
 
 typedef union {
 	unsigned all			: 8;	/* all of the bits together */
@@ -109,41 +120,6 @@
 		unsigned head		: 4;	/* always zeros here */
 	} b;
 } select_t;
-
-#undef	SUPPORT_SLOW_DATA_PORTS
-#define	SUPPORT_SLOW_DATA_PORTS	0
-#undef	SUPPORT_VLB_SYNC
-#define SUPPORT_VLB_SYNC	0
-
-static __inline__ int ide_check_region (ide_ioreg_t from, unsigned int extent)
-{
-	return 0;
-}
-
-static __inline__ void ide_request_region (ide_ioreg_t from, unsigned int extent, const char *name)
-{
-}
-
-static __inline__ void ide_release_region (ide_ioreg_t from, unsigned int extent)
-{
-}
-
-#undef insw
-#undef outsw
-#define insw(port, buf, ns)	ide_insw((port), (buf), (ns))
-#define outsw(port, buf, ns)	ide_outsw((port), (buf), (ns))
-
-void ide_insw(ide_ioreg_t port, void *buf, int ns);
-void ide_outsw(ide_ioreg_t port, void *buf, int ns);
-
-#define ide_fix_driveid(id)	do {			\
-	int nh;						\
-	unsigned short *p = (unsigned short *) id;	\
-	for (nh = SECTOR_WORDS * 2; nh != 0; --nh, ++p)	\
-		*p = (*p << 8) + (*p >> 8);		\
-} while (0)
-
-#endif
 
 static __inline__ int ide_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
 			unsigned long flags, const char *device, void *dev_id)
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/io.h linux/include/asm-ppc/io.h
--- v2.1.78/linux/include/asm-ppc/io.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/io.h	Mon Jan 12 15:18:13 1998
@@ -16,14 +16,47 @@
 #define IBM_L2_INVALIDATE 0x814
 #define IBM_SYS_CTL       0x81c
 
-extern unsigned long io_base;
 #define SLOW_DOWN_IO
-#define _IO_BASE io_base
 
-extern unsigned long pci_dram_offset;
+#define PMAC_ISA_IO_BASE 	0
+#define PMAC_ISA_MEM_BASE 	0
+#define PMAC_PCI_DRAM_OFFSET 	0
+#define CHRP_ISA_IO_BASE 	0xf8000000
+#define CHRP_ISA_MEM_BASE 	0xf7000000
+#define CHRP_PCI_DRAM_OFFSET 	0
+#define PREP_ISA_IO_BASE 	0x80000000
+#define PREP_ISA_MEM_BASE 	0xd0000000
+/*#define PREP_ISA_MEM_BASE 	0xc0000000*/
+#define PREP_PCI_DRAM_OFFSET 	0x80000000
+
+#if defined(CONFIG_MACH_SPECIFIC)
+#ifdef CONFIG_PREP
+#define _IO_BASE	PREP_ISA_IO_BASE
+#define _ISA_MEM_BASE	PREP_ISA_MEM_BASE
+#define PCI_DRAM_OFFSET PREP_PCI_DRAM_OFFSET
+#endif /* CONFIG_PREP */
+
+#ifdef CONFIG_CHRP
+#define _IO_BASE	CHRP_ISA_IO_BASE
+#define _ISA_MEM_BASE	CHRP_ISA_MEM_BASE
+#define PCI_DRAM_OFFSET CHRP_PCI_DRAM_OFFSET
+#endif /* CONFIG_CHRP */
+
+#ifdef CONFIG_PMAC
+#define _IO_BASE	PMAC_ISA_IO_BASE
+#define _ISA_MEM_BASE	PMAC_ISA_MEM_BASE
+#define PCI_DRAM_OFFSET PMAC_PCI_DRAM_OFFSET
+#endif /* CONFIG_PMAC */
+
+#else /* CONFIG_MACH_SPECIFIC */
+extern unsigned long isa_io_base;
+#define _IO_BASE isa_io_base
+extern unsigned long isa_mem_base;
+#define _ISA_MEM_BASE isa_mem_base
 #undef PCI_DRAM_OFFSET
 #define PCI_DRAM_OFFSET  pci_dram_offset
-
+extern unsigned long pci_dram_offset;
+#endif /* CONFIG_MACH_SPECIFIC */
 #define readb(addr) (*(volatile unsigned char *) (addr))
 #define readw(addr) ld_le16((volatile unsigned short *)(addr))
 #define readl(addr) ld_le32((volatile unsigned *)addr)
@@ -58,6 +91,10 @@
 extern void _outsw(volatile unsigned short *port, const void *buf, int ns);
 extern void _insl(volatile unsigned long *port, void *buf, int nl);
 extern void _outsl(volatile unsigned long *port, const void *buf, int nl);
+
+#define memset_io(a,b,c)	memset((a),(b),(c))
+#define memcpy_fromio(a,b,c)	memcpy((a),(b),(c))
+#define memcpy_toio(a,b,c)	memcpy((a),(b),(c))
 
 #ifdef __KERNEL__
 /*
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/irq.h linux/include/asm-ppc/irq.h
--- v2.1.78/linux/include/asm-ppc/irq.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/irq.h	Mon Jan 12 15:18:13 1998
@@ -1,8 +1,6 @@
 #ifndef _ASM_IRQ_H
 #define _ASM_IRQ_H
 
-#include <linux/config.h>
-
 /*
  * this is the # irq's for all ppc arch's (pmac/chrp/prep)
  * so it is the max of them all - which happens to be chrp
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/linux_logo.h linux/include/asm-ppc/linux_logo.h
--- v2.1.78/linux/include/asm-ppc/linux_logo.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/linux_logo.h	Mon Jan 12 15:18:13 1998
@@ -13,10 +13,13 @@
  
 #include <linux/init.h>
 
+#define linux_logo_banner "Linux/PPC version " UTS_RELEASE
+
 #define LINUX_LOGO_HEIGHT	80
 #define LINUX_LOGO_WIDTH	80
 #define LINUX_LOGO_COLORS	221
 
+#ifdef INCLUDE_LINUX_LOGO_DATA
 unsigned char linux_logo_red[] __initdata = {
   0xF3, 0xF6, 0xF8, 0xF7, 0xEF, 0xE7, 0xE5, 0xE3,
   0xCA, 0xD4, 0xDD, 0xC8, 0xC7, 0xC4, 0xC2, 0xE5,
@@ -912,3 +915,562 @@
   0xD0, 0x5E, 0xD0, 0xD1, 0xCB, 0x58, 0x4C, 0xCF,
   0xCF, 0xCE, 0xCE, 0x57, 0x63, 0xCC, 0xCD, 0x57,
 };
+
+unsigned char linux_logo_bw[] __initdata = {
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x3F,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F,
+  0xFE, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFE, 0x3F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xC7, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xC3,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF,
+  0xFB, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFD, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xF1,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF,
+  0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xF9, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xF9, 0xCF, 0xC3, 0xF8, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x87, 0x81, 0xF9,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xA7,
+  0x99, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xF9, 0xF3, 0xBC, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xF9, 0xE3, 0xBC, 0xF9, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xB0, 0x3C, 0xF9,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xB0,
+  0x19, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xF9, 0xC0, 0x03, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xF9, 0x80, 0x01, 0xF8, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x80, 0x01, 0xF8,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x80,
+  0x01, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xF9, 0xC0, 0x21, 0xD8, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xF9, 0xB1, 0x80, 0xEC, 0xC0, 0x1F,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x90, 0x00, 0xE4,
+  0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x8C,
+  0xC0, 0x7C, 0x04, 0x81, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xE3, 0x80, 0x00, 0x7C, 0x40, 0x11, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xE3, 0x80, 0x00, 0x7F, 0xD2, 0x29,
+  0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x00, 0x00, 0x3F,
+  0x80, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x0E, 0x00,
+  0x00, 0x3F, 0x80, 0x19, 0xFF, 0xFF, 0xFF, 0xFF,
+  0x1E, 0x00, 0x00, 0x1F, 0x80, 0x19, 0xFF, 0xFF,
+  0xFF, 0xFE, 0x1C, 0x00, 0x00, 0x1E, 0x80, 0x19,
+  0xFF, 0xFF, 0xFF, 0xFE, 0x3C, 0x00, 0x00, 0x1E,
+  0x80, 0x11, 0xFF, 0xFF, 0xFF, 0xFC, 0x7C, 0x00,
+  0x00, 0x0F, 0x80, 0x11, 0xFF, 0xFF, 0xFF, 0xFC,
+  0xF8, 0x00, 0x00, 0x0E, 0x80, 0x11, 0xFF, 0xFF,
+  0xFF, 0xFC, 0xF8, 0x00, 0x00, 0x06, 0x00, 0x11,
+  0xFF, 0xFF, 0xFF, 0xF8, 0xF8, 0x00, 0x00, 0x06,
+  0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xF9, 0xF0, 0x00,
+  0x00, 0x02, 0x00, 0x09, 0xFF, 0xFF, 0xFF, 0xF1,
+  0xF0, 0x00, 0x00, 0x02, 0x80, 0x10, 0xFF, 0xFF,
+  0xFF, 0xF1, 0xE0, 0x00, 0x00, 0x00, 0x97, 0x10,
+  0xFF, 0xFF, 0xFF, 0xE3, 0xE0, 0x00, 0x00, 0x00,
+  0xDF, 0xF0, 0xFF, 0xFF, 0xFF, 0xE3, 0xC0, 0x00,
+  0x00, 0x00, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xC7,
+  0xC0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0xFF, 0xFF,
+  0xFF, 0xC7, 0x80, 0x00, 0x00, 0x01, 0xFF, 0xF8,
+  0xFF, 0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x00, 0x01,
+  0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0x8F, 0x80, 0x00,
+  0x00, 0x01, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0x9F,
+  0x80, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0xFF, 0xFF,
+  0xFF, 0x9F, 0x80, 0x00, 0x00, 0x01, 0x80, 0x18,
+  0xFF, 0xFF, 0xFF, 0x9E, 0x80, 0x00, 0x00, 0x03,
+  0xA8, 0x11, 0xFF, 0xFF, 0xFF, 0x9F, 0x80, 0x00,
+  0x00, 0x02, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x99,
+  0x80, 0x00, 0x00, 0x00, 0x00, 0x09, 0xFF, 0xFF,
+  0xFF, 0x00, 0x80, 0x00, 0x00, 0x01, 0xC0, 0x01,
+  0xFF, 0xFF, 0xFE, 0x20, 0x60, 0x00, 0x00, 0x00,
+  0xFF, 0xC3, 0xFF, 0xFF, 0xF8, 0x00, 0x30, 0x00,
+  0x00, 0x00, 0xFF, 0x0F, 0xFF, 0xFF, 0xC0, 0x40,
+  0x38, 0x00, 0x00, 0x00, 0xFE, 0x47, 0xFF, 0xFF,
+  0x81, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xFC, 0x23,
+  0xFF, 0xFF, 0x90, 0x00, 0x1E, 0x00, 0x00, 0x00,
+  0x78, 0x11, 0xFF, 0xFF, 0x80, 0x00, 0x0F, 0x80,
+  0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00,
+  0x07, 0xC0, 0x00, 0x00, 0x00, 0x08, 0xFF, 0xFF,
+  0xC0, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x04,
+  0x7F, 0xFF, 0x80, 0x00, 0x03, 0xC0, 0x00, 0x10,
+  0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x01, 0x80,
+  0x00, 0x30, 0x00, 0x00, 0x0F, 0xFF, 0x80, 0x00,
+  0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x4F, 0xFF,
+  0x80, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00,
+  0x0F, 0xFF, 0xC0, 0x00, 0x00, 0x80, 0x03, 0xF0,
+  0x00, 0x00, 0x8F, 0xFF, 0x80, 0x00, 0x00, 0x40,
+  0x0F, 0xF0, 0x00, 0x04, 0x1F, 0xFF, 0x80, 0x00,
+  0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x10, 0x1F, 0xFF,
+  0xC0, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x40,
+  0xFF, 0xFF, 0x98, 0x00, 0x00, 0xFF, 0xFF, 0xF0,
+  0x00, 0x83, 0xFF, 0xFF, 0x81, 0xE0, 0x01, 0xFF,
+  0xFF, 0xF8, 0x02, 0x07, 0xFF, 0xFF, 0x80, 0x3F,
+  0x07, 0xE0, 0x00, 0x1C, 0x0C, 0x1F, 0xFF, 0xFF,
+  0xF8, 0x03, 0xFF, 0x80, 0x00, 0x1F, 0x78, 0x1F,
+  0xFF, 0xFF, 0xFF, 0x80, 0x7F, 0x00, 0x07, 0x0F,
+  0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFE, 0x0C, 0x07,
+  0xFF, 0x83, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0x00, 0x1F, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+unsigned char linux_logo16_red[] __initdata = {
+    0x00, 0x90, 0xb0, 0x9c, 0xf7, 0x35, 0x83, 0xa5,
+    0x65, 0x8f, 0x98, 0xc9, 0xdb, 0xe1, 0xe7, 0xf8
+};
+
+unsigned char linux_logo16_green[] __initdata = {
+    0x00, 0x90, 0xb0, 0x9c, 0xf7, 0x2e, 0x83, 0xa5,
+    0x65, 0x6e, 0x98, 0x89, 0xbf, 0xac, 0xda, 0xf8
+};
+
+unsigned char linux_logo16_blue[] __initdata = {
+    0x00, 0x90, 0xaf, 0x9c, 0xf7, 0x2b, 0x82, 0xa5,
+    0x65, 0x41, 0x97, 0x1e, 0x60, 0x29, 0xa5, 0xf8
+};
+
+unsigned char linux_logo16[] __initdata = {
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa1, 0x11, 0x11,
+    0x61, 0x16, 0x66, 0x66, 0x11, 0x11, 0x11, 0x11,
+    0x11, 0x11, 0x1a, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0xa8, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x87, 0x77, 0x77, 0x77, 0x77,
+    0x77, 0x77, 0x73, 0x33, 0x33, 0x3a, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xa3, 0x33, 0x33, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0x77,
+    0x77, 0x27, 0x77, 0x77, 0x77, 0x33, 0x3a, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xa3, 0x33, 0x33, 0x30, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x55, 0x50, 0x08, 0x33, 0x77, 0x77,
+    0x77, 0x72, 0x72, 0x27, 0x77, 0x77, 0x33, 0x33,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xa3, 0x33, 0x33, 0x77, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x58, 0x85, 0x00, 0x11, 0x11, 0xaa,
+    0xa3, 0x37, 0x77, 0x72, 0x22, 0x22, 0x77, 0x73,
+    0x33, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa3,
+    0x33, 0x37, 0x77, 0x33, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x56, 0x85, 0x00, 0x06, 0x66, 0x11,
+    0x11, 0x1a, 0xa3, 0x37, 0x77, 0x72, 0x22, 0x77,
+    0x73, 0x33, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33,
+    0x33, 0x33, 0x33, 0x30, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x55, 0x00, 0x00, 0x06, 0x66, 0x66,
+    0x66, 0x66, 0x11, 0x1a, 0xa3, 0x77, 0x72, 0x22,
+    0x77, 0x73, 0x3a, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x33,
+    0x33, 0x33, 0x33, 0xa0, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,
+    0x66, 0x66, 0x66, 0x66, 0x11, 0xa3, 0x77, 0x22,
+    0x22, 0x77, 0x33, 0x33, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x33, 0x33,
+    0x33, 0x3a, 0xa1, 0x10, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x33,
+    0xaa, 0x11, 0x16, 0x66, 0x66, 0x61, 0x1a, 0x37,
+    0x22, 0x22, 0x77, 0x33, 0x3a, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xa3, 0x33, 0x33, 0x33,
+    0x3a, 0xa1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x22,
+    0x22, 0x77, 0x3a, 0x11, 0x66, 0x66, 0x66, 0x1a,
+    0x37, 0x22, 0x22, 0x77, 0x33, 0x3a, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x33, 0x33, 0x3a,
+    0xa1, 0x11, 0x11, 0x10, 0x00, 0x00, 0x50, 0x00,
+    0x00, 0x05, 0x80, 0x50, 0x00, 0x00, 0x07, 0x72,
+    0x22, 0x22, 0x22, 0x73, 0xa1, 0x66, 0x66, 0x61,
+    0x1a, 0x77, 0x22, 0x27, 0x73, 0x33, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x33, 0x3a, 0xaa,
+    0x11, 0x11, 0x1a, 0xa0, 0x08, 0x71, 0x05, 0x00,
+    0x00, 0x12, 0x22, 0x50, 0x00, 0x00, 0x07, 0x77,
+    0x77, 0x72, 0x22, 0x22, 0x27, 0x31, 0x16, 0x66,
+    0x61, 0x13, 0x77, 0x22, 0x77, 0x33, 0x3a, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xa3, 0x33, 0x33, 0xaa, 0xa1,
+    0x11, 0x1a, 0x33, 0x70, 0x07, 0x2e, 0x70, 0x00,
+    0x01, 0x44, 0x42, 0x60, 0x00, 0x00, 0x02, 0x22,
+    0x22, 0x22, 0x22, 0x22, 0x22, 0x27, 0x31, 0x66,
+    0x66, 0x61, 0xa3, 0x72, 0x22, 0x77, 0x33, 0xaa,
+    0xaa, 0xaa, 0xa3, 0x33, 0x33, 0xaa, 0xaa, 0x11,
+    0x1a, 0x33, 0x77, 0x30, 0x04, 0x82, 0x40, 0x00,
+    0x54, 0x48, 0x54, 0x40, 0x00, 0x00, 0x01, 0xaa,
+    0x32, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x31,
+    0x66, 0x66, 0x11, 0x37, 0x22, 0x27, 0x73, 0x3a,
+    0xaa, 0xaa, 0xa3, 0x33, 0x3a, 0xaa, 0xaa, 0xaa,
+    0xa3, 0x77, 0xaa, 0x10, 0x50, 0x08, 0x46, 0x05,
+    0x54, 0x80, 0x50, 0x42, 0x00, 0x00, 0x08, 0x66,
+    0x66, 0x1a, 0x32, 0x22, 0x22, 0x22, 0x22, 0x27,
+    0x31, 0x66, 0x66, 0x13, 0x72, 0x22, 0x77, 0x33,
+    0xaa, 0xaa, 0xaa, 0x33, 0xaa, 0xa1, 0xaa, 0xa3,
+    0x37, 0xa1, 0x1a, 0x30, 0x50, 0x06, 0x26, 0x00,
+    0x54, 0x00, 0x00, 0x44, 0x00, 0x00, 0x08, 0xe2,
+    0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x22, 0x22,
+    0x27, 0xa6, 0x66, 0x61, 0xa7, 0x72, 0x27, 0x73,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33,
+    0x31, 0x11, 0x37, 0x70, 0x02, 0x00, 0xab, 0xbb,
+    0xb6, 0x00, 0x00, 0xf4, 0x00, 0x00, 0xee, 0xee,
+    0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x22,
+    0x22, 0x23, 0x16, 0x66, 0x1a, 0x37, 0x22, 0x77,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa3, 0x3a,
+    0x11, 0xa7, 0x33, 0x10, 0x04, 0x09, 0xbd, 0xdd,
+    0xbd, 0xd0, 0x04, 0x45, 0x00, 0x0e, 0xee, 0xee,
+    0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x22,
+    0x22, 0x22, 0x71, 0x66, 0x66, 0x13, 0x72, 0x27,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x11,
+    0xa3, 0x73, 0xa1, 0x60, 0x08, 0xbd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdb, 0x90, 0x00, 0x02, 0xec, 0xee,
+    0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xce, 0x22,
+    0x22, 0x22, 0x27, 0xa6, 0x66, 0x61, 0x37, 0x27,
+    0x1a, 0xaa, 0xaa, 0xaa, 0xaa, 0xa3, 0xa1, 0x1a,
+    0x33, 0xa1, 0x16, 0x60, 0x0b, 0xbd, 0xdd, 0xdd,
+    0xcd, 0xdd, 0xdd, 0xd9, 0x00, 0x00, 0xec, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0xa2,
+    0x22, 0x22, 0x22, 0x7a, 0x66, 0x66, 0x13, 0x77,
+    0x1a, 0xaa, 0xaa, 0xaa, 0xaa, 0x3a, 0x11, 0x33,
+    0xaa, 0x11, 0x66, 0x60, 0x9b, 0xdd, 0xdd, 0xdd,
+    0xcd, 0xdd, 0xdb, 0xb9, 0x00, 0x00, 0xec, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xee, 0x61,
+    0x72, 0x22, 0x22, 0x22, 0xa1, 0x66, 0x61, 0x37,
+    0x1a, 0xaa, 0xaa, 0xaa, 0xa3, 0xa1, 0x13, 0x3a,
+    0x11, 0x11, 0x11, 0x10, 0x5b, 0xdd, 0xdd, 0xdc,
+    0xdd, 0xdd, 0xbd, 0xd9, 0x00, 0x00, 0xec, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xee, 0x86,
+    0x17, 0x22, 0x22, 0x22, 0x23, 0x16, 0x66, 0xaa,
+    0xaa, 0xa3, 0x3a, 0xaa, 0xaa, 0x1a, 0x3a, 0xa1,
+    0x11, 0x11, 0x1a, 0x70, 0x05, 0xbd, 0xdd, 0xdd,
+    0xdb, 0x5b, 0xdd, 0xb0, 0x00, 0x60, 0x2e, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xe6, 0x88,
+    0x66, 0x32, 0x22, 0x22, 0x22, 0x36, 0x66, 0x11,
+    0x33, 0x33, 0x3a, 0xaa, 0x11, 0xaa, 0xaa, 0xa1,
+    0x11, 0x1a, 0x3a, 0x60, 0x02, 0x99, 0xbb, 0xb9,
+    0x9b, 0xbb, 0xbc, 0x22, 0x00, 0x86, 0x5e, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xe1, 0x68,
+    0x86, 0x63, 0x22, 0x22, 0x22, 0x2a, 0x66, 0x66,
+    0x33, 0x33, 0xaa, 0xaa, 0x1a, 0xaa, 0xaa, 0x11,
+    0x1a, 0xa7, 0x68, 0x80, 0x02, 0x2b, 0xbd, 0xbb,
+    0xbb, 0xb9, 0x22, 0x22, 0x00, 0x06, 0x6e, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xc7, 0xa6,
+    0x88, 0x86, 0x32, 0x22, 0x22, 0x27, 0xa6, 0x66,
+    0x33, 0x3a, 0xaa, 0xa1, 0xaa, 0xaa, 0xa1, 0x11,
+    0xa3, 0xa6, 0x88, 0x80, 0x02, 0x22, 0x9b, 0xbb,
+    0xbb, 0x22, 0x24, 0xf4, 0x60, 0x00, 0x0c, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xc2, 0x21,
+    0x68, 0x88, 0x63, 0x22, 0x22, 0x22, 0x71, 0x66,
+    0x33, 0x3a, 0x11, 0x11, 0xaa, 0xaa, 0x11, 0xaa,
+    0x71, 0x88, 0x88, 0x00, 0x02, 0xe2, 0x26, 0x99,
+    0x22, 0x22, 0x4f, 0xf4, 0x40, 0x00, 0x0c, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x22, 0x22,
+    0x16, 0x88, 0x86, 0xa2, 0x22, 0x22, 0x27, 0x11,
+    0x33, 0xa1, 0x11, 0x11, 0xaa, 0x31, 0x1a, 0xa3,
+    0x68, 0x88, 0x81, 0x00, 0x54, 0x42, 0x22, 0x22,
+    0x22, 0x44, 0xff, 0xff, 0x48, 0x00, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x22, 0x22,
+    0x21, 0x88, 0x88, 0x6a, 0x22, 0x22, 0x22, 0x31,
+    0x3a, 0xa1, 0x11, 0x1a, 0xa3, 0x11, 0x33, 0x36,
+    0x88, 0x86, 0x30, 0x00, 0x4f, 0x44, 0x22, 0x22,
+    0x24, 0xff, 0xff, 0xff, 0x44, 0x00, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x95, 0x22, 0x72,
+    0x22, 0x18, 0x88, 0x86, 0x32, 0x22, 0x22, 0x27,
+    0xaa, 0x11, 0x11, 0x1a, 0x31, 0x13, 0x33, 0x68,
+    0x88, 0x6a, 0x00, 0x02, 0x4f, 0x4f, 0x42, 0x24,
+    0x4f, 0xff, 0xff, 0xff, 0xf4, 0x50, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x22, 0x73,
+    0x72, 0x26, 0x88, 0x88, 0x63, 0x22, 0x22, 0x22,
+    0x11, 0x11, 0x11, 0xa3, 0xa1, 0x73, 0xa6, 0x88,
+    0x81, 0xa5, 0x00, 0x04, 0x4f, 0x4f, 0x44, 0x4f,
+    0xff, 0xff, 0xff, 0xff, 0xf4, 0x40, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x12, 0x27,
+    0xaa, 0x22, 0x68, 0x55, 0x86, 0x72, 0x22, 0x22,
+    0x11, 0x11, 0x1a, 0x33, 0x13, 0x3a, 0x18, 0x88,
+    0x1a, 0x10, 0x00, 0x44, 0x4f, 0x4f, 0xff, 0x4f,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x61, 0x22,
+    0x3a, 0xa2, 0x26, 0x85, 0x58, 0x67, 0x22, 0x22,
+    0x61, 0x61, 0x1a, 0x7a, 0x37, 0x31, 0x88, 0x81,
+    0x11, 0x00, 0x05, 0xe4, 0x44, 0xff, 0xff, 0xff,
+    0x4f, 0xf4, 0x44, 0xff, 0xff, 0xf5, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x88, 0x12,
+    0x2a, 0xaa, 0x72, 0x68, 0x55, 0x81, 0x22, 0x22,
+    0x66, 0x61, 0xa3, 0x33, 0x73, 0x16, 0x88, 0x11,
+    0x10, 0x00, 0x08, 0x74, 0x44, 0x4f, 0x44, 0x44,
+    0xf4, 0xf4, 0x44, 0x44, 0xe2, 0x44, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x88, 0x81,
+    0x22, 0xaa, 0xa7, 0x26, 0x85, 0x88, 0x12, 0x22,
+    0x66, 0x61, 0x37, 0xa7, 0x3a, 0x66, 0x66, 0x11,
+    0x80, 0x00, 0x0a, 0x72, 0x44, 0x4f, 0x44, 0x4f,
+    0xff, 0x44, 0x44, 0x22, 0x22, 0x24, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x85, 0x88,
+    0x12, 0x2a, 0xaa, 0x22, 0x68, 0x58, 0x63, 0x22,
+    0x66, 0x1a, 0x73, 0x77, 0x31, 0x66, 0x61, 0x11,
+    0x00, 0x00, 0x07, 0x44, 0xff, 0x4f, 0xf4, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0x42, 0x22, 0x40, 0x9b,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb9, 0x85, 0x55,
+    0x81, 0x27, 0xaa, 0xa2, 0x78, 0x88, 0x86, 0x72,
+    0x66, 0x13, 0x77, 0x73, 0x11, 0x66, 0x61, 0x76,
+    0x00, 0x50, 0x84, 0xf4, 0xff, 0x4f, 0xf4, 0xff,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x42, 0x40, 0x9b,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb9, 0x68, 0x55,
+    0x58, 0x12, 0x3a, 0xaa, 0x23, 0x88, 0x88, 0xa7,
+    0x66, 0xa7, 0x77, 0x7a, 0x16, 0x66, 0x1a, 0x15,
+    0x05, 0x00, 0x4f, 0xf4, 0xff, 0x4f, 0xf4, 0xff,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x44, 0x24, 0x9b,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb9, 0x26, 0x55,
+    0x55, 0x81, 0x23, 0xaa, 0x32, 0x18, 0x88, 0x6a,
+    0x61, 0x37, 0x77, 0x31, 0x66, 0x66, 0x17, 0x60,
+    0x05, 0x08, 0x4f, 0xf4, 0xff, 0x4f, 0xf4, 0xff,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x4e, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0xa2, 0x65,
+    0x55, 0x58, 0xa2, 0x7a, 0xa2, 0x26, 0x88, 0x61,
+    0x61, 0x32, 0x27, 0xa1, 0x66, 0x61, 0x31, 0x60,
+    0x00, 0x04, 0x4f, 0xf4, 0xff, 0x44, 0x44, 0xff,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x44, 0xf4, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x9b, 0xaa, 0x26,
+    0x55, 0x55, 0x87, 0x27, 0x33, 0x27, 0x68, 0x61,
+    0x1a, 0x72, 0x27, 0xa6, 0x66, 0x6a, 0x71, 0x00,
+    0x80, 0x84, 0xff, 0xf4, 0xff, 0x44, 0x44, 0xff,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x44, 0xf4, 0x99,
+    0x9b, 0x9b, 0x99, 0xb9, 0xb9, 0x99, 0xaa, 0xa2,
+    0x85, 0x55, 0x56, 0x22, 0x27, 0x22, 0x36, 0x66,
+    0x13, 0x22, 0x23, 0x16, 0x86, 0x63, 0x73, 0x00,
+    0x00, 0x44, 0xf4, 0xf4, 0xff, 0x44, 0x44, 0xff,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x4f, 0x99,
+    0x9b, 0x99, 0x99, 0x99, 0xb9, 0x99, 0xaa, 0xaa,
+    0x28, 0x55, 0x58, 0x12, 0x22, 0x22, 0x21, 0x11,
+    0xa3, 0x27, 0x7a, 0x66, 0x86, 0x17, 0x75, 0x05,
+    0x05, 0xff, 0xf4, 0xf4, 0xff, 0x44, 0x44, 0xff,
+    0xff, 0x4f, 0x44, 0x4f, 0x4f, 0x44, 0x4f, 0x99,
+    0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x3a, 0xaa,
+    0xa2, 0x85, 0x58, 0x67, 0x72, 0x22, 0x27, 0xa1,
+    0x37, 0x27, 0x7a, 0x68, 0x86, 0xa2, 0x70, 0x00,
+    0x02, 0xff, 0xf4, 0xf4, 0xff, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0xf4, 0xf4, 0xf4, 0x99,
+    0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x23, 0xaa,
+    0xa7, 0x78, 0x88, 0x81, 0x77, 0x22, 0x27, 0x3a,
+    0x72, 0x73, 0x71, 0x68, 0x66, 0x32, 0x50, 0x00,
+    0x04, 0x4f, 0xf4, 0xf4, 0xff, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0xf4, 0xf4, 0x44, 0x95,
+    0x99, 0x99, 0x99, 0x99, 0x99, 0x55, 0x12, 0x3a,
+    0xaa, 0x21, 0x88, 0x81, 0x77, 0x27, 0x73, 0x73,
+    0x72, 0x33, 0x36, 0x86, 0x61, 0x72, 0x00, 0x00,
+    0x04, 0x44, 0xf4, 0xf4, 0xf4, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x44, 0x55,
+    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x8a, 0x27,
+    0xaa, 0x77, 0x68, 0x61, 0x23, 0x71, 0x11, 0x3a,
+    0x27, 0xa3, 0x36, 0x86, 0x61, 0x20, 0x00, 0x00,
+    0x04, 0xf4, 0xf4, 0xf4, 0xf4, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x41, 0x59,
+    0x99, 0x99, 0x99, 0x99, 0x99, 0x95, 0x58, 0x77,
+    0x27, 0x32, 0x36, 0x63, 0x23, 0x71, 0x66, 0x11,
+    0x27, 0x13, 0xa6, 0x86, 0x6a, 0x20, 0x00, 0x50,
+    0x04, 0x4f, 0x4f, 0x4f, 0x4f, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x41, 0x99,
+    0x9b, 0xbb, 0xbb, 0xbb, 0xb9, 0x99, 0x68, 0x13,
+    0x32, 0x22, 0x73, 0xa7, 0x2a, 0x31, 0x88, 0x66,
+    0x7a, 0x13, 0x18, 0x66, 0x63, 0x20, 0x00, 0x06,
+    0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x49, 0x95,
+    0xa9, 0xa9, 0x99, 0x97, 0x92, 0x99, 0x65, 0x6a,
+    0x17, 0x22, 0x23, 0x72, 0x27, 0xaa, 0x88, 0x88,
+    0xa1, 0x17, 0x68, 0x66, 0x67, 0x70, 0x00, 0x05,
+    0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0xf4, 0xf4, 0x49, 0x9c,
+    0x2e, 0xee, 0xee, 0xee, 0xee, 0xa9, 0x65, 0x8a,
+    0x1a, 0xaa, 0x37, 0x72, 0x27, 0x37, 0x88, 0x88,
+    0x11, 0x17, 0x68, 0x66, 0x67, 0x10, 0x9d, 0xd0,
+    0x84, 0x44, 0xff, 0x4f, 0x4f, 0x44, 0xf4, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0xf4, 0xf4, 0x4f, 0x69,
+    0xcc, 0xee, 0xee, 0xee, 0xec, 0x99, 0x88, 0x63,
+    0x61, 0x68, 0x61, 0x72, 0x22, 0x7a, 0x68, 0x88,
+    0x11, 0x17, 0x88, 0x66, 0x12, 0x1b, 0xdd, 0xdd,
+    0x02, 0x44, 0x4f, 0x4f, 0x4f, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0xff, 0x4f, 0x4c, 0xc5,
+    0x0c, 0xc1, 0x11, 0x1c, 0xc0, 0x26, 0x66, 0x17,
+    0x66, 0x88, 0x88, 0x12, 0x22, 0x23, 0xa8, 0x88,
+    0x11, 0x13, 0x88, 0x66, 0x17, 0xbb, 0xdd, 0xdd,
+    0xd0, 0x8f, 0xff, 0xf4, 0xf4, 0x44, 0xf4, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0x4f, 0x44, 0xdd, 0xdd,
+    0x00, 0x00, 0x00, 0x05, 0x9d, 0x21, 0x66, 0x27,
+    0xa6, 0x65, 0x58, 0x67, 0x22, 0x27, 0x28, 0x88,
+    0x11, 0xaa, 0x86, 0x68, 0x1a, 0xbb, 0xdd, 0xdd,
+    0xdb, 0x05, 0xf4, 0xf4, 0xf4, 0xf4, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0xf4, 0xf4, 0xdd, 0xdb,
+    0x00, 0x00, 0x00, 0x00, 0xdd, 0xda, 0x66, 0x22,
+    0x71, 0x15, 0x55, 0x81, 0x22, 0x22, 0x76, 0x88,
+    0x11, 0x31, 0x88, 0x88, 0xab, 0xbd, 0xdd, 0xdd,
+    0xdd, 0x00, 0x04, 0x44, 0xff, 0xff, 0x4f, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0xf4, 0x44, 0xdd, 0xdb,
+    0x00, 0x00, 0x00, 0x0b, 0xdd, 0xda, 0x11, 0x22,
+    0x23, 0x68, 0x55, 0x86, 0x22, 0x22, 0x7a, 0x88,
+    0x1a, 0x71, 0x88, 0x89, 0xbb, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xd0, 0x00, 0x4f, 0x44, 0xff, 0x4f, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0xff, 0xe2, 0xdd, 0xdb,
+    0x90, 0x00, 0x05, 0xbd, 0xdd, 0xb8, 0x63, 0x22,
+    0x27, 0xa6, 0x55, 0x88, 0x77, 0x22, 0x22, 0x88,
+    0x1a, 0x28, 0xbd, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdb, 0x00, 0x07, 0x44, 0x4f, 0x4f, 0x4f,
+    0xff, 0x4f, 0x44, 0x4f, 0x4f, 0x22, 0xdd, 0xdb,
+    0xbb, 0x9b, 0xbb, 0xbd, 0xdd, 0xd5, 0x86, 0x22,
+    0x22, 0x77, 0x85, 0x88, 0x17, 0x22, 0x22, 0x88,
+    0xaa, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0x00, 0x00, 0x54, 0x4f, 0x4f, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0x44, 0x22, 0xbd, 0xdd,
+    0xbb, 0xbb, 0xbb, 0xdd, 0xdd, 0xdd, 0x88, 0x72,
+    0x27, 0x22, 0x88, 0x88, 0x67, 0x72, 0x22, 0x18,
+    0x33, 0x2d, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xd0, 0x00, 0x05, 0x4f, 0x4f, 0x4f,
+    0xff, 0x4f, 0x44, 0x44, 0x4f, 0x22, 0xbd, 0xdd,
+    0xdb, 0xbb, 0xdd, 0xdd, 0xdd, 0xdd, 0x88, 0x17,
+    0x27, 0x72, 0x68, 0x88, 0x87, 0x32, 0x22, 0x36,
+    0x37, 0x2d, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xd5, 0x00, 0x00, 0x4f, 0x4f, 0x4f,
+    0xff, 0xf4, 0xf4, 0xf4, 0xf4, 0x22, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd8, 0x67,
+    0x72, 0x77, 0x38, 0x88, 0x83, 0x37, 0x22, 0x26,
+    0x72, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0x00, 0x00, 0x4f, 0x4f, 0x4f,
+    0xff, 0xf4, 0xf4, 0xf4, 0x44, 0x25, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd3,
+    0x32, 0x73, 0x76, 0x88, 0x81, 0x33, 0x22, 0x2a,
+    0x22, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xb0, 0x54, 0x4f, 0x4f, 0x4f,
+    0xff, 0xf4, 0xf4, 0xff, 0x44, 0x00, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xa7, 0x73, 0x26, 0x88, 0x86, 0x7a, 0x72, 0x27,
+    0x22, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdb, 0x44, 0xff, 0x4f, 0x4f,
+    0xff, 0xf4, 0xf4, 0x44, 0x40, 0x05, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0x13, 0x23, 0x21, 0x68, 0x86, 0x17, 0x72, 0x22,
+    0x22, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdb, 0x44, 0x4f, 0x4f, 0x4f,
+    0xff, 0xff, 0x44, 0x42, 0x00, 0x05, 0xbd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0x87, 0x27, 0x27, 0x16, 0x66, 0x67, 0x22, 0x22,
+    0x72, 0x7b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0x94, 0x44, 0x44, 0x44,
+    0x44, 0x44, 0x44, 0x00, 0x00, 0x05, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xb8,
+    0x86, 0x22, 0x22, 0x7a, 0x68, 0x81, 0x22, 0x22,
+    0x37, 0x7b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdb, 0xb5, 0x44, 0x44, 0x44,
+    0x44, 0x47, 0x00, 0x00, 0x00, 0x05, 0xbd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd8, 0x68,
+    0x58, 0x72, 0x22, 0x27, 0x18, 0x86, 0x72, 0x22,
+    0x1a, 0xbb, 0xbd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdb, 0xb5, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xb9, 0x18, 0x85,
+    0x58, 0x12, 0x22, 0x36, 0x18, 0x88, 0x32, 0x22,
+    0x61, 0x3b, 0xbb, 0xbb, 0xbd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdb, 0xb9, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xb9, 0x7a, 0x68, 0x85,
+    0x88, 0x62, 0x27, 0x16, 0x18, 0x88, 0x12, 0x27,
+    0x86, 0x18, 0x9b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbd,
+    0xdd, 0xdd, 0xdd, 0xbb, 0xb5, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xbb, 0xbd,
+    0xdd, 0xdd, 0xdb, 0xbb, 0x87, 0x31, 0x68, 0x65,
+    0x88, 0x82, 0x23, 0x16, 0x18, 0x88, 0x12, 0x23,
+    0x88, 0x67, 0x27, 0xa8, 0x9b, 0xbb, 0xbb, 0xbb,
+    0xbd, 0xdd, 0xbb, 0xbb, 0x95, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x9b, 0xbb,
+    0xbb, 0xbb, 0xbb, 0x96, 0x87, 0x16, 0x68, 0x18,
+    0x88, 0x62, 0x31, 0x66, 0x18, 0x88, 0x62, 0x73,
+    0x88, 0x63, 0x27, 0x33, 0x65, 0x55, 0x99, 0x9b,
+    0xbb, 0xbb, 0xbb, 0x99, 0x55, 0x0a, 0xa1, 0x86,
+    0x81, 0x68, 0x88, 0x55, 0x58, 0x85, 0x9b, 0xbb,
+    0xbb, 0xbb, 0x95, 0x88, 0x83, 0x66, 0x66, 0x18,
+    0x66, 0x82, 0xa1, 0x66, 0x18, 0x88, 0x62, 0x33,
+    0x88, 0x81, 0x27, 0x7a, 0x18, 0x58, 0x86, 0x85,
+    0x99, 0x99, 0x99, 0x95, 0x53, 0x2a, 0xaa, 0x88,
+    0x67, 0x31, 0x68, 0x55, 0x58, 0x85, 0x59, 0xbb,
+    0xbb, 0xb9, 0x58, 0x68, 0x83, 0x66, 0x61, 0x16,
+    0x66, 0x62, 0x16, 0x66, 0x68, 0x88, 0x62, 0xaa,
+    0x88, 0x86, 0x27, 0x77, 0x78, 0x55, 0x88, 0x22,
+    0x25, 0x55, 0x95, 0x55, 0x6a, 0xa2, 0x2a, 0x88,
+    0x62, 0x27, 0x37, 0x38, 0x88, 0x87, 0x55, 0x59,
+    0x95, 0x58, 0x16, 0x88, 0x8a, 0x66, 0x63, 0x68,
+    0x86, 0x67, 0x66, 0x66, 0x68, 0x88, 0x12, 0x11,
+    0x88, 0x88, 0x72, 0x77, 0x78, 0x85, 0x58, 0x17,
+    0x23, 0x32, 0x55, 0x55, 0x81, 0x13, 0x73, 0x66,
+    0x62, 0x7a, 0xaa, 0x38, 0x88, 0x58, 0x27, 0x55,
+    0x58, 0x32, 0x38, 0x88, 0x81, 0x66, 0xa2, 0x88,
+    0x86, 0x61, 0x66, 0x61, 0x66, 0x68, 0x13, 0x11,
+    0x88, 0x88, 0x12, 0x22, 0x71, 0x85, 0x58, 0x62,
+    0x23, 0xa2, 0x68, 0x88, 0x81, 0x66, 0x88, 0x88,
+    0x63, 0x2a, 0xaa, 0x28, 0x88, 0x55, 0x86, 0x61,
+    0x66, 0x66, 0x68, 0x88, 0x66, 0x66, 0x77, 0x88,
+    0x68, 0x16, 0x66, 0x62, 0x66, 0x68, 0xa1, 0x61,
+    0x88, 0x88, 0x62, 0x22, 0x22, 0x85, 0x55, 0x83,
+    0x72, 0x37, 0xa8, 0x88, 0x61, 0x66, 0x85, 0x55,
+    0x86, 0x23, 0xaa, 0x71, 0x88, 0x85, 0x88, 0x66,
+    0x88, 0x86, 0x88, 0x88, 0x16, 0x61, 0x21, 0x88,
+    0x66, 0xa6, 0x86, 0x17, 0x66, 0x66, 0x31, 0x61,
+    0x88, 0x88, 0x87, 0x72, 0x22, 0x68, 0x55, 0x86,
+    0x77, 0x77, 0x36, 0x88, 0x13, 0x68, 0x85, 0x55,
+    0x58, 0x12, 0x73, 0x72, 0x76, 0x88, 0x88, 0x68,
+    0x88, 0x88, 0x88, 0x66, 0x36, 0x63, 0x26, 0x86,
+    0x86, 0x36, 0x86, 0x11, 0x66, 0x66, 0x76, 0x61,
+    0x88, 0x88, 0x81, 0x22, 0x22, 0x38, 0x85, 0x58,
+    0x37, 0x22, 0x21, 0x68, 0xa2, 0x31, 0x68, 0x55,
+    0x55, 0x81, 0x22, 0x22, 0xa8, 0x88, 0x88, 0x68,
+    0x86, 0x88, 0x68, 0x81, 0x36, 0x17, 0x21, 0x68,
+    0x86, 0x16, 0x66, 0x26, 0x66, 0x61, 0x36, 0x66,
+    0x68, 0x88, 0x86, 0x27, 0x22, 0x28, 0x88, 0x88,
+    0x17, 0x72, 0x2a, 0x66, 0xa2, 0x22, 0x36, 0x55,
+    0x55, 0x58, 0x37, 0x3a, 0x16, 0x66, 0x66, 0x66,
+    0x66, 0x18, 0x88, 0x67, 0x16, 0x12, 0x71, 0x68,
+    0x81, 0x68, 0x61, 0x76, 0x66, 0x6a, 0x16, 0x66,
+    0x88, 0x88, 0x86, 0x77, 0x22, 0x26, 0x88, 0x88,
+    0x13, 0x37, 0x71, 0x66, 0xa2, 0x33, 0x2a, 0x85,
+    0x55, 0x55, 0x17, 0x73, 0x16, 0x66, 0x66, 0x68,
+    0x63, 0x88, 0x88, 0xa2, 0x66, 0xa2, 0xa6, 0x88,
+    0x61, 0x68, 0x6a, 0x76, 0x66, 0x6a, 0x66, 0x6a
+};
+
+/* Painted by Johnny Stenback <jst@uwasa.fi> */
+
+unsigned char *linux_serial_image __initdata = "\n"
+"         .u$e.\n"
+"       .$$$$$:S\n"
+"       $\"*$/\"*$$\n"
+"       $.`$ . ^F\n"
+"       4k+#+T.$F\n"
+"       4P+++\"$\"$\n"
+"       :R\"+  t$$B\n"
+"    ___#       $$$\n"
+"    |  |       R$$k\n"
+"   dd. | Linux  $!$\n"
+"   ddd |  PPC   $9$F\n"
+" '!!!!!$       !!#!`\n"
+"  !!!!!*     .!!!!!`\n"
+"'!!!!!!!W..e$$!!!!!!`    %s\n"
+" \"~^^~         ^~~^\n"
+"\n";
+
+#else
+
+/* prototypes only */
+extern unsigned char linux_logo_red[];
+extern unsigned char linux_logo_green[];
+extern unsigned char linux_logo_blue[];
+extern unsigned char linux_logo[];
+extern unsigned char linux_logo_bw[];
+extern unsigned char linux_logo16_red[];
+extern unsigned char linux_logo16_green[];
+extern unsigned char linux_logo16_blue[];
+extern unsigned char linux_logo16[];
+extern unsigned char *linux_serial_image;
+
+extern int (*console_show_logo)(void);
+
+#endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/md.h linux/include/asm-ppc/md.h
--- v2.1.78/linux/include/asm-ppc/md.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/md.h	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,13 @@
+/* $Id: md.h,v 1.1 1997/12/15 15:12:15 jj Exp $
+ * md.h: High speed xor_block operation for RAID4/5 
+ *
+ */
+ 
+#ifndef __ASM_MD_H
+#define __ASM_MD_H
+
+/* #define HAVE_ARCH_XORBLOCK */
+
+#define MD_XORBLOCK_ALIGNMENT	sizeof(long)
+
+#endif /* __ASM_MD_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/mmu.h linux/include/asm-ppc/mmu.h
--- v2.1.78/linux/include/asm-ppc/mmu.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/mmu.h	Mon Jan 12 15:18:13 1998
@@ -6,23 +6,22 @@
 #define _PPC_MMU_H_
 
 /* Hardware Page Table Entry */
-typedef struct _PTE
-   {
-      unsigned long v:1;	/* Entry is valid */
-      unsigned long vsid:24;	/* Virtual segment identifier */
-      unsigned long h:1;	/* Hash algorithm indicator */
-      unsigned long api:6;	/* Abbreviated page index */
-      unsigned long rpn:20;	/* Real (physical) page number */
-      unsigned long    :3;	/* Unused */
-      unsigned long r:1;	/* Referenced */
-      unsigned long c:1;	/* Changed */
-      unsigned long w:1;	/* Write-thru cache mode */
-      unsigned long i:1;	/* Cache inhibited */
-      unsigned long m:1;	/* Memory coherence */
-      unsigned long g:1;	/* Guarded */
-      unsigned long    :1;	/* Unused */
-      unsigned long pp:2;	/* Page protection */
-   } PTE; 
+typedef struct _PTE {
+	unsigned long v:1;	/* Entry is valid */
+	unsigned long vsid:24;	/* Virtual segment identifier */
+	unsigned long h:1;	/* Hash algorithm indicator */
+	unsigned long api:6;	/* Abbreviated page index */
+	unsigned long rpn:20;	/* Real (physical) page number */
+	unsigned long    :3;	/* Unused */
+	unsigned long r:1;	/* Referenced */
+	unsigned long c:1;	/* Changed */
+	unsigned long w:1;	/* Write-thru cache mode */
+	unsigned long i:1;	/* Cache inhibited */
+	unsigned long m:1;	/* Memory coherence */
+	unsigned long g:1;	/* Guarded */
+	unsigned long  :1;	/* Unused */
+	unsigned long pp:2;	/* Page protection */
+} PTE; 
 
 /* Values for PP (assumes Ks=0, Kp=1) */
 #define PP_RWXX	0	/* Supervisor read/write, User none */
@@ -31,68 +30,61 @@
 #define PP_RXRX 3	/* Supervisor read,       User read */
 
 /* Segment Register */
-typedef struct _SEGREG
-{
-  unsigned long t:1;	/* Normal or I/O  type */
-  unsigned long ks:1;	/* Supervisor 'key' (normally 0) */
-  unsigned long kp:1;	/* User 'key' (normally 1) */
-  unsigned long n:1;	/* No-execute */
-  unsigned long :4;		/* Unused */
-  unsigned long vsid:24;	/* Virtual Segment Identifier */
+typedef struct _SEGREG {
+	unsigned long t:1;	/* Normal or I/O  type */
+	unsigned long ks:1;	/* Supervisor 'key' (normally 0) */
+	unsigned long kp:1;	/* User 'key' (normally 1) */
+	unsigned long n:1;	/* No-execute */
+	unsigned long :4;	/* Unused */
+	unsigned long vsid:24;	/* Virtual Segment Identifier */
 } SEGREG;
 
 /* Block Address Translation (BAT) Registers */
-typedef struct _P601_BATU
-{
-  unsigned long bepi:15;	/* Effective page index (virtual address) */
-  unsigned long :8;		/* unused */
-  unsigned long w:1;
-  unsigned long i:1;		/* Cache inhibit */
-  unsigned long m:1;		/* Memory coherence */
-  unsigned long vs:1;	/* Supervisor valid */
-  unsigned long vp:1;	/* User valid */
-  unsigned long pp:2;	/* Page access protections */
+typedef struct _P601_BATU {	/* Upper part of BAT for 601 processor */
+	unsigned long bepi:15;	/* Effective page index (virtual address) */
+	unsigned long :8;	/* unused */
+	unsigned long w:1;
+	unsigned long i:1;	/* Cache inhibit */
+	unsigned long m:1;	/* Memory coherence */
+	unsigned long ks:1;	/* Supervisor key (normally 0) */
+	unsigned long kp:1;	/* User key (normally 1) */
+	unsigned long pp:2;	/* Page access protections */
 } P601_BATU;
 
-typedef struct _BATU		/* Upper part of BAT */
-{
-  unsigned long bepi:15;	/* Effective page index (virtual address) */
-  unsigned long :4;		/* Unused */
-  unsigned long bl:11;	/* Block size mask */
-  unsigned long vs:1;	/* Supervisor valid */
-  unsigned long vp:1;	/* User valid */
+typedef struct _BATU {		/* Upper part of BAT (all except 601) */
+	unsigned long bepi:15;	/* Effective page index (virtual address) */
+	unsigned long :4;	/* Unused */
+	unsigned long bl:11;	/* Block size mask */
+	unsigned long vs:1;	/* Supervisor valid */
+	unsigned long vp:1;	/* User valid */
 } BATU;   
 
-typedef struct _P601_BATL
-{
-  unsigned long brpn:15;	/* Real page index (physical address) */
-  unsigned long :10;		/* Unused */
-  unsigned long v:1;		/* valid/invalid */
-  unsigned long bl:6;		/* Block size mask */
+typedef struct _P601_BATL {	/* Lower part of BAT for 601 processor */
+	unsigned long brpn:15;	/* Real page index (physical address) */
+	unsigned long :10;	/* Unused */
+	unsigned long v:1;	/* Valid bit */
+	unsigned long bl:6;	/* Block size mask */
 } P601_BATL;
 
-typedef struct _BATL		/* Lower part of BAT */
-{
-  unsigned long brpn:15;	/* Real page index (physical address) */
-  unsigned long :10;		/* Unused */
-  unsigned long w:1;	/* Write-thru cache */
-  unsigned long i:1;	/* Cache inhibit */
-  unsigned long m:1;	/* Memory coherence */
-  unsigned long g:1;	/* Guarded (MBZ) */
-  unsigned long :1;		/* Unused */
-  unsigned long pp:2;	/* Page access protections */
+typedef struct _BATL {		/* Lower part of BAT (all except 601) */
+	unsigned long brpn:15;	/* Real page index (physical address) */
+	unsigned long :10;	/* Unused */
+	unsigned long w:1;	/* Write-thru cache */
+	unsigned long i:1;	/* Cache inhibit */
+	unsigned long m:1;	/* Memory coherence */
+	unsigned long g:1;	/* Guarded (MBZ in IBAT) */
+	unsigned long :1;	/* Unused */
+	unsigned long pp:2;	/* Page access protections */
 } BATL;
 
-typedef struct _BAT
-{
-  BATU batu;		/* Upper register */
-  BATL batl;		/* Lower register */
+typedef struct _BAT {
+	BATU batu;		/* Upper register */
+	BATL batl;		/* Lower register */
 } BAT;
 
-typedef struct _P601_BAT
-{
-  P601_BATU batu;		/* Upper register */
-  P601_BATL batl;		/* Lower register */
+typedef struct _P601_BAT {
+	P601_BATU batu;		/* Upper register */
+	P601_BATL batl;		/* Lower register */
 } P601_BAT;
 
 /* Block size masks */
@@ -123,11 +115,10 @@
  * platforms, notably the 80x86 and 680x0.
  */
 
-typedef struct _pte
-   {
+typedef struct _pte {
    	unsigned long page_num:20;
-   	unsigned long flags:12;		/* Page flags (with some unused bits) */
-   } pte;
+   	unsigned long flags:12;		/* Page flags (some unused bits) */
+} pte;
 
 #define PD_SHIFT (10+12)		/* Page directory */
 #define PD_MASK  0x02FF
@@ -138,11 +129,10 @@
 
 /* MMU context */
 
-typedef struct _MMU_context
-   {
-      SEGREG	segs[16];	/* Segment registers */
-      pte	**pmap;		/* Two-level page-map structure */
-   } MMU_context;
+typedef struct _MMU_context {
+	SEGREG	segs[16];	/* Segment registers */
+	pte	**pmap;		/* Two-level page-map structure */
+} MMU_context;
 
 /* Used to set up SDR1 register */
 #define HASH_TABLE_SIZE_64K	0x00010000
@@ -167,5 +157,4 @@
 }
 
 extern void _tlbia(void);		/* invalidate all TLB entries */
-
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/page.h linux/include/asm-ppc/page.h
--- v2.1.78/linux/include/asm-ppc/page.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/page.h	Mon Jan 12 15:18:13 1998
@@ -1,28 +1,12 @@
 #ifndef _PPC_PAGE_H
 #define _PPC_PAGE_H
 
-#include <linux/config.h>
-
 /* PAGE_SHIFT determines the page size */
 #define PAGE_SHIFT	12
 #define PAGE_SIZE	(1UL << PAGE_SHIFT)
 #define PAGE_MASK	(~(PAGE_SIZE-1))
 
-/* This handles the memory map.. */
-
-/*
- * these virtual mappings for prep and pmac
- * on the prep machine the io areas are at different physical locations
- * than their virtual address.  On the pmac and chrp the io areas
- * are mapped 1-1 virtual/physical.
- * -- Cort
- */
-#if defined(CONFIG_PREP) || defined(CONFIG_CHRP)
-#define KERNELBASE	0x90000000
-#endif
-#ifdef CONFIG_PMAC
-#define KERNELBASE	0xc0000000
-#endif
+/* KERNELBASE comes from arch/ppc/Makefile */
 #define PAGE_OFFSET	KERNELBASE
 
 
@@ -85,7 +69,7 @@
 #define __pa(x)			((unsigned long)(x)-PAGE_OFFSET)
 #define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))
 
-#define MAP_NR(addr)		(__pa(addr) >> PAGE_SHIFT)
+#define MAP_NR(addr)		(((unsigned long)addr-PAGE_OFFSET) >> PAGE_SHIFT)
 #define MAP_PAGE_RESERVED	(1<<15)
 
 extern unsigned long get_prezerod_page(void);
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/pgtable.h linux/include/asm-ppc/pgtable.h
--- v2.1.78/linux/include/asm-ppc/pgtable.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/pgtable.h	Mon Jan 12 15:18:13 1998
@@ -3,31 +3,39 @@
 
 #include <linux/mm.h>
 
-extern void flush_tlb_all(void);
-extern void flush_tlb_mm(struct mm_struct *mm);
-extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
-extern void flush_tlb_range(struct mm_struct *mm, unsigned long start,
+extern void local_flush_tlb_all(void);
+extern void local_flush_tlb_mm(struct mm_struct *mm);
+extern void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
+extern void local_flush_tlb_range(struct mm_struct *mm, unsigned long start,
 			    unsigned long end);
 
+#ifndef __SMP__
+#define flush_tlb_all local_flush_tlb_all
+#define flush_tlb_mm local_flush_tlb_mm
+#define flush_tlb_page local_flush_tlb_page
+#define flush_tlb_range local_flush_tlb_range
+#else /* __SMP__ */
+#define flush_tlb_all local_flush_tlb_all
+#define flush_tlb_mm local_flush_tlb_mm
+#define flush_tlb_page local_flush_tlb_page
+#define flush_tlb_range local_flush_tlb_range
+#endif /* __SMP__ */
+
 /*
  * No cache flushing is required when address mappings are
  * changed, because the caches on PowerPCs are physically
  * addressed.
+ * Also, when SMP we use the coherency (M) bit of the
+ * BATs and PTEs.  -- Cort
  */
 #define flush_cache_all()		do { } while (0)
 #define flush_cache_mm(mm)		do { } while (0)
 #define flush_cache_range(mm, a, b)	do { } while (0)
 #define flush_cache_page(vma, p)	do { } while (0)
-extern void flush_icache_range(unsigned long, unsigned long);
 
-/*
- * For the page specified, write modified lines in the data cache
- * out to memory, and invalidate lines in the instruction cache.
- */
+extern void flush_icache_range(unsigned long, unsigned long);
 extern void flush_page_to_ram(unsigned long);
 
-extern unsigned long va_to_phys(unsigned long address);
-
 /*
  * The PowerPC MMU uses a hash table containing PTEs, together with
  * a set of 16 segment registers (on 32-bit implementations), to define
@@ -61,13 +69,19 @@
 #define PTRS_PER_PGD	1024
 
 /* Just any arbitrary offset to the start of the vmalloc VM area: the
- * current 8MB value just means that there will be a 8MB "hole" after the
+ * current 64MB value just means that there will be a 64MB "hole" after the
  * physical memory until the kernel virtual memory starts.  That means that
  * any out-of-bounds memory accesses will hopefully be caught.
  * The vmalloc() routines leaves a hole of 4kB between each vmalloced
  * area for the same reason. ;)
+ *
+ * The vmalloc_offset MUST be larger than the gap between the bat2 mapping
+ * and the size of physical ram.  Since the bat2 mapping can be larger than
+ * the amount of ram we have vmalloc_offset must ensure that we don't try
+ * to allocate areas that don't exist! This value of 64M will only cause
+ * problems when we have >128M -- Cort
  */
-#define VMALLOC_OFFSET	(0x2000000) /* 32M */
+#define VMALLOC_OFFSET	(0x4000000) /* 64M */
 #define VMALLOC_START ((((long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)))
 #define VMALLOC_VMADDR(x) ((unsigned long)(x))
 
@@ -150,7 +164,8 @@
 
 /* to set the page-dir */
 /* tsk is a task_struct and pgdir is a pte_t */
-#define SET_PAGE_DIR(tsk,pgdir) 
+#define SET_PAGE_DIR(tsk,pgdir)  \
+	((tsk)->tss.pg_tables = (unsigned long *)(pgdir))
 
 extern inline int pte_none(pte_t pte)		{ return !pte_val(pte); }
 extern inline int pte_present(pte_t pte)	{ return pte_val(pte) & _PAGE_PRESENT; }
@@ -253,7 +268,7 @@
 { pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); return pte; }
 
 extern inline unsigned long pte_page(pte_t pte)
-{ return (pte_val(pte) & PAGE_MASK) + KERNELBASE; }
+{ return (unsigned long) __va(pte_val(pte) & PAGE_MASK); }
 
 extern inline unsigned long pmd_page(pmd_t pmd)
 { return pmd_val(pmd); }
@@ -394,12 +409,6 @@
 extern void flush_hash_segments(unsigned low_vsid, unsigned high_vsid);
 extern void flush_hash_page(unsigned context, unsigned long va);
 
-extern inline void
-flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
-{
-	if (vmaddr < TASK_SIZE)
-		flush_hash_page(vma->vm_mm->context, vmaddr);
-}
 
 #define SWP_TYPE(entry) (((entry) >> 1) & 0x7f)
 #define SWP_OFFSET(entry) ((entry) >> 8)
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/processor.h linux/include/asm-ppc/processor.h
--- v2.1.78/linux/include/asm-ppc/processor.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/processor.h	Mon Jan 12 15:18:13 1998
@@ -1,6 +1,8 @@
 #ifndef __ASM_PPC_PROCESSOR_H
 #define __ASM_PPC_PROCESSOR_H
 
+#include <linux/config.h>
+
 #include <asm/ptrace.h>
 #include <asm/residual.h>
 
@@ -47,33 +49,128 @@
 #define HID0_DCI	(1<<10)		/* Data Cache Invalidate */
 #define HID0_SIED	(1<<7)		/* Serial Instruction Execution [Disable] */
 #define HID0_BHTE	(1<<2)		/* Branch History Table Enable */
+#define HID0_BTCD	(1<<1)		/* Branch target cache disable */
 
 /* fpscr settings */
 #define FPSCR_FX        (1<<31)
 #define FPSCR_FEX       (1<<30)
 
-#define _MACH_Motorola 1 /* motorola prep */
-#define _MACH_IBM      2 /* ibm prep */
-#define _MACH_Pmac     4 /* pmac or pmac clone (non-chrp) */
-#define _MACH_chrp     8 /* chrp machine */
+#define _MACH_prep     1
+#define _MACH_Pmac     2 /* pmac or pmac clone (non-chrp) */
+#define _MACH_chrp     4 /* chrp machine */
+
+/* see residual.h for these */
+#define _PREP_Motorola 0x01  /* motorola prep */
+#define _PREP_Firm     0x02  /* firmworks prep */
+#define _PREP_IBM      0x00  /* ibm prep */
+#define _PREP_Bull     0x03  /* bull prep */
+
+#define _GLOBAL(n)\
+	.globl n;\
+n:
+
+#define	TBRU	269	/* Time base Upper/Lower (Reading) */
+#define	TBRL	268
+#define TBWU	284	/* Time base Upper/Lower (Writing) */
+#define TBWL	285
+#define	XER	1
+#define LR	8
+#define CTR	9
+#define HID0	1008	/* Hardware Implementation */
+#define PVR	287	/* Processor Version */
+#define IBAT0U	528	/* Instruction BAT #0 Upper/Lower */
+#define IBAT0L	529
+#define IBAT1U	530	/* Instruction BAT #1 Upper/Lower */
+#define IBAT1L	531
+#define IBAT2U	532	/* Instruction BAT #2 Upper/Lower */
+#define IBAT2L	533
+#define IBAT3U	534	/* Instruction BAT #3 Upper/Lower */
+#define IBAT3L	535
+#define DBAT0U	536	/* Data BAT #0 Upper/Lower */
+#define DBAT0L	537
+#define DBAT1U	538	/* Data BAT #1 Upper/Lower */
+#define DBAT1L	539
+#define DBAT2U	540	/* Data BAT #2 Upper/Lower */
+#define DBAT2L	541
+#define DBAT3U	542	/* Data BAT #3 Upper/Lower */
+#define DBAT3L	543
+#define DMISS	976	/* TLB Lookup/Refresh registers */
+#define DCMP	977
+#define HASH1	978
+#define HASH2	979
+#define IMISS	980
+#define ICMP	981
+#define RPA	982
+#define SDR1	25	/* MMU hash base register */
+#define DAR	19	/* Data Address Register */
+#define SPR0	272	/* Supervisor Private Registers */
+#define SPRG0   272
+#define SPR1	273
+#define SPRG1   273
+#define SPR2	274
+#define SPRG2   274
+#define SPR3	275
+#define SPRG3   275
+#define DSISR	18
+#define SRR0	26	/* Saved Registers (exception) */
+#define SRR1	27
+#define IABR	1010	/* Instruction Address Breakpoint */
+#define DEC	22	/* Decrementer */
+#define EAR	282	/* External Address Register */
+
+/* Segment Registers */
+#define SR0	0
+#define SR1	1
+#define SR2	2
+#define SR3	3
+#define SR4	4
+#define SR5	5
+#define SR6	6
+#define SR7	7
+#define SR8	8
+#define SR9	9
+#define SR10	10
+#define SR11	11
+#define SR12	12
+#define SR13	13
+#define SR14	14
+#define SR15	15
 
 #ifndef __ASSEMBLY__
+/*
+ * If we've configured for a specific machine set things
+ * up so the compiler can optimize away the other parts.
+ * -- Cort
+ */
+#ifdef CONFIG_MACH_SPECIFIC
+#ifdef CONFIG_PREP
+#define _machine (_MACH_prep)
+#define is_prep (1)
+#endif /* CONFIG_PREP */
+
+#ifdef CONFIG_CHRP
+#define _machine (_MACH_chrp)
+#define is_prep (0)
+#endif /* CONFIG_CHRP */
+
+#ifdef CONFIG_PMAC
+#define _machine (_MACH_Pmac)
+#define is_prep (0)
+#endif /* CONFIG_PMAC */
+
+#else /* CONFIG_MACH_SPECIFIC */
+
 extern int _machine;
 
 /* if we're a prep machine */
-#define is_prep (_machine & (_MACH_Motorola|_MACH_IBM))
-/*
- * if we have openfirmware - pmac/chrp have it implicitly
- * but we have to check residual data to know on prep
- */
-extern __inline__ int have_of(void)
-{
-	if ( (_machine & (_MACH_Pmac|_MACH_chrp)) /*||
-	     ( is_prep && (res.VitalProductData.FirmwareSupplier & OpenFirmware))*/)
-		return 1; 
-	else 
-		return 0;
-}
+#define is_prep (_machine == _MACH_prep)
+
+#endif /* CONFIG_MACH_SPECIFIC */
+
+/* if we have openfirmware */
+extern unsigned long have_of;
+/* what kind of prep workstation we are */
+extern int _prep_type;
 
 struct task_struct;
 void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp);
@@ -100,11 +197,21 @@
  */
 #define TASK_UNMAPPED_BASE     (TASK_SIZE / 8 * 3)
 
+#define COPY_TASK_STRUCT(dst, src) 	\
+do {					\
+	*dst = *src;			\
+} while (0)
+
+typedef struct {
+	unsigned long seg;
+} mm_segment_t;
+
 struct thread_struct {
 	unsigned long	ksp;		/* Kernel stack pointer */
+	unsigned long	*pg_tables;	/* Base of page-table tree */
 	unsigned long	wchan;		/* Event task is sleeping on */
 	struct pt_regs	*regs;		/* Pointer to saved register state */
-	unsigned long   fs;		/* for get_fs() validation */
+	mm_segment_t	fs;		/* for get_fs() validation */
 	signed long     last_syscall;
 	double		fpr[32];	/* Complete floating point set */
 	unsigned long	fpscr_pad;	/* fpr ... fpscr must be contiguous */
@@ -115,6 +222,7 @@
 
 #define INIT_TSS  { \
 	INIT_SP, /* ksp */ \
+	(unsigned long *) swapper_pg_dir, /* pg_tables */ \
 	0, /* wchan */ \
 	(struct pt_regs *)INIT_SP - 1, /* regs */ \
 	KERNEL_DS, /*fs*/ \
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/prom.h linux/include/asm-ppc/prom.h
--- v2.1.78/linux/include/asm-ppc/prom.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/prom.h	Mon Jan 12 15:18:13 1998
@@ -5,13 +5,22 @@
  * Copyright (C) 1996 Paul Mackerras.
  */
 
+#include <linux/config.h>
+
 typedef void *phandle;
 typedef void *ihandle;
 
-extern ihandle prom_stdout;
-extern ihandle prom_chosen;
-extern phandle cpu_node;
-extern char prom_display_path[];
+#ifndef FB_MAX
+#define FB_MAX	8	/* I don't want to include the whole console stuff */
+#endif
+extern char *prom_display_paths[FB_MAX];
+extern unsigned int prom_num_displays;
+
+struct address_range {
+	unsigned int space;
+	unsigned int address;
+	unsigned int size;
+};
 
 struct reg_property {
 	unsigned int address;
@@ -37,7 +46,7 @@
 	char	*type;
 	phandle	node;
 	int	n_addrs;
-	struct	reg_property *addrs;
+	struct	address_range *addrs;
 	int	n_intrs;
 	int	*intrs;
 	char	*full_name;
@@ -49,17 +58,21 @@
 	struct	device_node *allnext;	/* next in list of all nodes */
 };
 
+struct prom_args;
+typedef void (*prom_entry)(struct prom_args *);
+
 /* Prototypes */
 void abort(void);
-void prom_exit(void);
-void *call_prom(const char *service, int nargs, int nret, ...);
-void prom_print(const char *msg);
-void prom_init(char *params, int unused, void (*)(void *));
-void set_prom_callback(void);
-unsigned long copy_device_tree(unsigned long, unsigned long);
+void prom_init(int, int, prom_entry);
+void finish_device_tree(void);
 struct device_node *find_devices(const char *name);
 struct device_node *find_type_devices(const char *type);
 struct device_node *find_path_device(const char *path);
+struct device_node *find_compatible_devices(const char *type,
+					    const char *compat);
+struct device_node *find_phandle(phandle);
 unsigned char *get_property(struct device_node *node, const char *name,
 	int *lenp);
 void print_properties(struct device_node *node);
+int call_rtas(const char *service, int nargs, int nret,
+	      unsigned long *outputs, ...);
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/scatterlist.h linux/include/asm-ppc/scatterlist.h
--- v2.1.78/linux/include/asm-ppc/scatterlist.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/scatterlist.h	Mon Jan 12 15:18:13 1998
@@ -1,7 +1,7 @@
 #ifndef _PPC_SCATTERLIST_H
 #define _PPC_SCATTERLIST_H
 
-#include <linux/config.h>
+#include <asm/dma.h>
 
 struct scatterlist {
     char *  address;    /* Location data is to be transferred to */
@@ -10,23 +10,5 @@
     unsigned int length;
 };
 
-#ifdef CONFIG_PMAC
-/*
- * This is used in the scsi code to decide if bounce buffers are needed.
- * Fortunately the dma controllers on the PowerMac are a bit better
- * than on PCs...
- */
-#define ISA_DMA_THRESHOLD (~0UL)
-#endif
-
-#ifdef CONFIG_PREP
-/* PReP systems are like PCs */
-#define ISA_DMA_THRESHOLD (0x00ffffff)
-#endif
-
-#ifdef CONFIG_CHRP
-/* The W83C553F supports 32-bit DMA on ISA */
-#define ISA_DMA_THRESHOLD (~0UL)
-#endif
 
 #endif /* !(_PPC_SCATTERLIST_H) */
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/siginfo.h linux/include/asm-ppc/siginfo.h
--- v2.1.78/linux/include/asm-ppc/siginfo.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/siginfo.h	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,195 @@
+#ifndef _PPC_SIGINFO_H
+#define _PPC_SIGINFO_H
+
+/* Copied from i386 from alpha. */
+
+#include <linux/types.h>
+
+typedef union sigval {
+	int sival_int;
+	void *sival_ptr;
+} sigval_t;
+
+#define SI_MAX_SIZE	128
+#define SI_PAD_SIZE	((SI_MAX_SIZE/sizeof(int)) - 3)
+
+typedef struct siginfo {
+	int si_signo;
+	int si_errno;
+	int si_code;
+
+	union {
+		int _pad[SI_PAD_SIZE];
+
+		/* kill() */
+		struct {
+			pid_t _pid;		/* sender's pid */
+			uid_t _uid;		/* sender's uid */
+		} _kill;
+
+		/* POSIX.1b timers */
+		struct {
+			unsigned int _timer1;
+			unsigned int _timer2;
+		} _timer;
+
+		/* POSIX.1b signals */
+		struct {
+			pid_t _pid;		/* sender's pid */
+			uid_t _uid;		/* sender's uid */
+			sigval_t _sigval;
+		} _rt;
+
+		/* SIGCHLD */
+		struct {
+			pid_t _pid;		/* which child */
+			int _status;		/* exit code */
+			clock_t _utime;
+			clock_t _stime;
+		} _sigchld;
+
+		/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+		struct {
+			void *_addr; /* faulting insn/memory ref. */
+		} _sigfault;
+
+		/* SIGPOLL */
+		struct {
+			int _band;	/* POLL_IN, POLL_OUT, POLL_MSG */
+			int _fd;
+		} _sigpoll;
+	} _sifields;
+} siginfo_t;
+
+/*
+ * How these fields are to be accessed.
+ */
+#define si_pid		_sifields._kill._pid
+#define si_uid		_sifields._kill._uid
+#define si_status	_sifields._sigchld._status
+#define si_utime	_sifields._sigchld._utime
+#define si_stime	_sifields._sigchld._stime
+#define si_value	_sifields._rt._sigval
+#define si_int		_sifields._rt._sigval.sival_int
+#define si_ptr		_sifields._rt._sigval.sival_ptr
+#define si_addr		_sifields._sigfault._addr
+#define si_band		_sifields._sigpoll._band
+#define si_fd		_sifields._sigpoll._fd
+
+/*
+ * si_code values
+ * Digital reserves positive values for kernel-generated signals.
+ */
+#define SI_USER		0	/* sent by kill, sigsend, raise */
+#define SI_KERNEL	0x80	/* sent by the kernel from somewhere */
+#define SI_QUEUE	-1	/* sent by sigqueue */
+#define SI_TIMER	-2	/* sent by timer expiration */
+#define SI_MESGQ	-3	/* sent by real time mesq state change */
+#define SI_ASYNCIO	-4	/* sent by AIO completion */
+
+#define SI_FROMUSER(siptr)	((siptr)->si_code <= 0)
+#define SI_FROMKERNEL(siptr)	((siptr)->si_code > 0)
+
+/*
+ * SIGILL si_codes
+ */
+#define ILL_ILLOPC	1	/* illegal opcode */
+#define ILL_ILLOPN	2	/* illegal operand */
+#define ILL_ILLADR	3	/* illegal addressing mode */
+#define ILL_ILLTRP	4	/* illegal trap */
+#define ILL_PRVOPC	5	/* privileged opcode */
+#define ILL_PRVREG	6	/* privileged register */
+#define ILL_COPROC	7	/* coprocessor error */
+#define ILL_BADSTK	8	/* internal stack error */
+#define NSIGILL		8
+
+/*
+ * SIGFPE si_codes
+ */
+#define FPE_INTDIV	1	/* integer divide by zero */
+#define FPE_INTOVF	2	/* integer overflow */
+#define FPE_FLTDIV	3	/* floating point divide by zero */
+#define FPE_FLTOVF	4	/* floating point overflow */
+#define FPE_FLTUND	5	/* floating point underflow */
+#define FPE_FLTRES	6	/* floating point inexact result */
+#define FPE_FLTINV	7	/* floating point invalid operation */
+#define FPE_FLTSUB	8	/* subscript out of range */
+#define NSIGFPE		8
+
+/*
+ * SIGSEGV si_codes
+ */
+#define SEGV_MAPERR	1	/* address not mapped to object */
+#define SRGV_ACCERR	2	/* invalid permissions for mapped object */
+#define NSIGSEGV	2
+
+/*
+ * SIGBUS si_codes
+ */
+#define BUS_ADRALN	1	/* invalid address alignment */
+#define BUS_ADRERR	2	/* non-existant physical address */
+#define BUS_OBJERR	3	/* object specific hardware error */
+#define NSIGBUS		3
+
+/*
+ * SIGTRAP si_codes
+ */
+#define TRAP_BRKPT	1	/* process breakpoint */
+#define TRAP_TRACE	2	/* process trace trap */
+#define NSIGTRAP
+
+/*
+ * SIGCHLD si_codes
+ */
+#define CLD_EXITED	1	/* child has exited */
+#define CLD_KILLED	2	/* child was killed */
+#define CLD_DUMPED	3	/* child terminated abnormally */
+#define CLD_TRAPPED	4	/* traced child has trapped */
+#define CLD_STOPPED	5	/* child has stopped */
+#define CLD_CONTINUED	6	/* stopped child has continued */
+#define NSIGCHLD
+
+/*
+ * SIGPOLL si_codes
+ */
+#define POLL_IN		1	/* data input available */
+#define POLL_OUT	2	/* output buffers available */
+#define POLL_MSG	3	/* input message available */
+#define POLL_ERR	4	/* i/o error */
+#define POLL_PRI	5	/* high priority input available */
+#define POLL_HUP	6	/* device disconnected */
+#define NSIGPOLL	6
+
+/*
+ * sigevent definitions
+ * 
+ * It seems likely that SIGEV_THREAD will have to be handled from 
+ * userspace, libpthread transmuting it to SIGEV_SIGNAL, which the
+ * thread manager then catches and does the appropriate nonsense.
+ * However, everything is written out here so as to not get lost.
+ */
+#define SIGEV_SIGNAL	0	/* notify via signal */
+#define SIGEV_NONE	1	/* other notification: meaningless */
+#define SIGEV_THREAD	2	/* deliver via thread creation */
+
+#define SIGEV_MAX_SIZE	64
+#define SIGEV_PAD_SIZE	((SIGEV_MAX_SIZE/sizeof(int)) - 3)
+
+typedef struct sigevent {
+	sigval_t sigev_value;
+	int sigev_signo;
+	int sigev_notify;
+	union {
+		int _pad[SIGEV_PAD_SIZE];
+
+		struct {
+			void (*_function)(sigval_t);
+			void *_attribute;	/* really pthread_attr_t */
+		} _sigev_thread;
+	} _sigev_un;
+} sigevent_t;
+
+#define sigev_notify_function	_sigev_un._sigev_thread._function
+#define sigev_notify_attributes	_sigev_un._sigev_thread._attribute
+
+#endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/signal.h linux/include/asm-ppc/signal.h
--- v2.1.78/linux/include/asm-ppc/signal.h	Mon May 27 02:00:59 1996
+++ linux/include/asm-ppc/signal.h	Mon Jan 12 15:18:13 1998
@@ -1,10 +1,23 @@
 #ifndef _ASMPPC_SIGNAL_H
 #define _ASMPPC_SIGNAL_H
 
-typedef unsigned long sigset_t;		/* at least 32 bits */
+#include <linux/types.h>
 
-#define _NSIG             32
-#define NSIG		_NSIG
+/* Avoid too many header ordering problems.  */
+struct siginfo;
+
+/* Most things should be clean enough to redefine this at will, if care
+   is taken to make libc match.  */
+
+#define _NSIG		64
+#define _NSIG_BPW	32
+#define _NSIG_WORDS	(_NSIG / _NSIG_BPW)
+
+typedef unsigned long old_sigset_t;		/* at least 32 bits */
+
+typedef struct {
+	unsigned long sig[_NSIG_WORDS];
+} sigset_t;
 
 #define SIGHUP		 1
 #define SIGINT		 2
@@ -43,32 +56,50 @@
 #define SIGPWR		30
 #define	SIGUNUSED	31
 
+/* These should not be considered constants from userland.  */
+#define SIGRTMIN	32
+#define SIGRTMAX	(_NSIG-1)
+
 /*
- * sa_flags values: SA_STACK is not currently supported, but will allow the
- * usage of signal stacks by using the (now obsolete) sa_restorer field in
- * the sigaction structure as a stack pointer. This is now possible due to
- * the changes in signal handling. LBT 010493.
+ * SA_FLAGS values:
+ *
+ * SA_ONSTACK is not currently supported, but will allow sigaltstack(2).
  * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the
  * SA_RESTART flag to get restarting signals (which were the default long ago)
- * SA_SHIRQ flag is for shared interrupt support on PCI and EISA.
+ * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop.
+ * SA_RESETHAND clears the handler when the signal is delivered.
+ * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies.
+ * SA_NODEFER prevents the current signal from being masked in the handler.
+ *
+ * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single
+ * Unix names RESETHAND and NODEFER respectively.
  */
-#define SA_NOCLDSTOP	1
-#define SA_SHIRQ	0x04000000
-#define SA_STACK	0x08000000
+#define SA_NOCLDSTOP	0x00000001
+#define SA_NOCLDWAIT	0x00000002 /* not supported yet */
+#define SA_SIGINFO	0x00000004
+#define SA_ONSTACK	0x08000000
 #define SA_RESTART	0x10000000
-#define SA_INTERRUPT	0x20000000
-#define SA_NOMASK	0x40000000
-#define SA_ONESHOT	0x80000000
+#define SA_NODEFER	0x40000000
+#define SA_RESETHAND	0x80000000
+
+#define SA_NOMASK	SA_NODEFER
+#define SA_ONESHOT	SA_RESETHAND
+#define SA_INTERRUPT	0x20000000 /* dummy -- ignored */
+
+#define SA_RESTORER	0x04000000
 
 #ifdef __KERNEL__
+
 /*
  * These values of sa_flags are used only by the kernel as part of the
  * irq handling routines.
  *
  * SA_INTERRUPT is also used by the irq handling routines.
+ * SA_SHIRQ is for shared interrupt support on PCI and EISA.
  */
-#define SA_PROBE SA_ONESHOT
-#define SA_SAMPLE_RANDOM SA_RESTART
+#define SA_PROBE		SA_ONESHOT
+#define SA_SAMPLE_RANDOM	SA_RESTART
+#define SA_SHIRQ		0x04000000
 #endif
 
 #define SIG_BLOCK          0	/* for blocking signals */
@@ -82,17 +113,33 @@
 #define SIG_IGN	((__sighandler_t)1)	/* ignore signal */
 #define SIG_ERR	((__sighandler_t)-1)	/* error return from signal */
 
+struct old_sigaction {
+	__sighandler_t sa_handler;
+	old_sigset_t sa_mask;
+	unsigned long sa_flags;
+	void (*sa_restorer)(void);
+};
+
 struct sigaction {
 	__sighandler_t sa_handler;
-	sigset_t sa_mask;
 	unsigned long sa_flags;
 	void (*sa_restorer)(void);
+	sigset_t sa_mask;		/* mask last for extensibility */
+};
+
+struct k_sigaction {
+	struct sigaction sa;
 };
 
+typedef struct sigaltstack {
+	void *ss_sp;
+	int ss_flags;
+	size_t ss_size;
+} stack_t;
+
 #ifdef __KERNEL__
 #include <asm/sigcontext.h>
-#endif
 
 #endif
 
-
+#endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/smp.h linux/include/asm-ppc/smp.h
--- v2.1.78/linux/include/asm-ppc/smp.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/smp.h	Mon Jan 12 15:18:13 1998
@@ -1,12 +1,13 @@
 /* smp.h: PPC specific SMP stuff.
  *
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Taken from asm-sparc/smp.h
  */
 
 #ifndef _PPC_SMP_H
 #define _PPC_SMP_H
 
 #include <linux/kernel.h> /* for panic */
+#include <linux/tasks.h> /* for NR_CPUS */
 
 #ifdef __SMP__
 
@@ -16,7 +17,8 @@
 
 /* per processor PPC parameters we need. */
 struct cpuinfo_PPC {
-	unsigned long udelay_val;
+	unsigned long loops_per_sec;
+	unsigned long pvr;
 };
 
 extern struct cpuinfo_PPC cpu_data[NR_CPUS];
@@ -31,7 +33,7 @@
 #define KLOCK_HELD       0xff
 #define KLOCK_CLEAR      0x00
 
-#define PROC_CHANGE_PENALTY     20
+#define PROC_CHANGE_PENALTY     1000 /* don't change cpu's for now */
 
 extern __volatile__ int cpu_number_map[NR_CPUS];
 extern __volatile__ int cpu_logical_map[NR_CPUS];
@@ -39,21 +41,19 @@
 
 extern __inline__ int hard_smp_processor_id(void)
 {
-	int cpuid;
-	if ( ! have_of() ) /* assume prep */
-		panic("hard_smp_processor_id()\n");
-	else
-		panic("hard_smp_processor_id()\n");
-
+	int cpuid = 0;
+	/* assume cpu # 0 for now */
 	return cpuid;
 }
 
-#define smp_processor_id() hard_smp_processor_id()
+#define smp_processor_id() (current->processor)
 
 #endif /* __ASSEMBLY__ */
 
 #endif /* !(__SMP__) */
 
 #define NO_PROC_ID               0xFF            /* No processor magic marker */
+
+extern void smp_store_cpu_info(int id);
 
 #endif /* !(_PPC_SMP_H) */
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/smp_lock.h linux/include/asm-ppc/smp_lock.h
--- v2.1.78/linux/include/asm-ppc/smp_lock.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/smp_lock.h	Mon Jan 12 15:18:13 1998
@@ -1,7 +1,6 @@
 #ifndef __PPC_SMPLOCK_H
 #define __PPC_SMPLOCK_H
 
-#include <linux/kernel.h> /* for panic */
 #ifndef __SMP__
 
 #define lock_kernel()		do { } while (0)
@@ -9,7 +8,7 @@
 #define release_kernel_lock(task, cpu, depth)	((depth) = 1)
 #define reacquire_kernel_lock(task, cpu, depth)	do { } while(0)
 
-#else
+#else /* __SMP__ */
 
 /* Release global kernel lock and global interrupt lock */
 #define release_kernel_lock(task, cpu, depth)		\
@@ -19,39 +18,30 @@
 		(task)->lock_depth = 0;			\
 		klock_info.akp = NO_PROC_ID;		\
 		klock_info.kernel_flag = 0;		\
-	}						\
+	} 						\
 	release_irqlock(cpu);				\
 	__sti();					\
 } while(0)
 
-/* Re-acquire the kernel lock */
-#define reacquire_kernel_lock(task, cpu, depth) \
-do { if (depth) \
-	{ __cli(); \
-	  __asm__ __volatile__( \
-	  "blr __lock_kernel\n\t" \
-	  "stw %2,%0\n\t" \
-	  : "=m" (task->lock_depth) \
-	  : "d" (cpu), "c" (depth)); \
-	  __sti(); \
-       } \
-} while (0)
+extern void reacquire_kernel_lock(struct task_struct *, int,int);
 
 /* The following acquire and release the master kernel global lock,
  * the idea is that the usage of this mechanmism becomes less and less
  * as time goes on, to the point where they are no longer needed at all
  * and can thus disappear.
  */
+extern void __lock_kernel(struct task_struct *);
+extern void __unlock_kernel(struct task_struct *);
 
 extern __inline__ void lock_kernel(void)
 {
-	panic("lock_kernel()\n");
+	__lock_kernel(current);
 }
 
 /* Release kernel global lock. */
 extern __inline__ void unlock_kernel(void)
 {
-	panic("unlock_kernel()\n");
+	__unlock_kernel(current);
 }
 
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/socket.h linux/include/asm-ppc/socket.h
--- v2.1.78/linux/include/asm-ppc/socket.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/socket.h	Mon Jan 12 15:18:13 1998
@@ -39,4 +39,6 @@
 #define SO_SECURITY_ENCRYPTION_TRANSPORT	23
 #define SO_SECURITY_ENCRYPTION_NETWORK		24
 
+#define SO_BINDTODEVICE	25
+
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/softirq.h linux/include/asm-ppc/softirq.h
--- v2.1.78/linux/include/asm-ppc/softirq.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/softirq.h	Mon Jan 12 15:18:13 1998
@@ -74,12 +74,12 @@
 
 #else /* __SMP__ */
 
-extern atomic_t __sparc_bh_counter;
+extern atomic_t __ppc_bh_counter;
 
 #define start_bh_atomic() \
-	do { atomic_inc(&__sparc_bh_counter); synchronize_irq(); } while(0)
+	do { atomic_inc(&__ppc_bh_counter); synchronize_irq(); } while(0)
 
-#define end_bh_atomic()		atomic_dec(&__sparc_bh_counter)
+#define end_bh_atomic()		atomic_dec(&__ppc_bh_counter)
 
 #include <asm/spinlock.h>
 
@@ -132,13 +132,13 @@
 #define softirq_trylock()					\
 ({								\
 	int ret = 1;						\
-	if(atomic_add_return(1, &__sparc_bh_counter) != 1) {	\
-		atomic_dec(&__sparc_bh_counter);		\
+	if(atomic_add_return(1, &__ppc_bh_counter) != 1) {	\
+		atomic_dec(&__ppc_bh_counter);		\
 		ret = 0;					\
 	}							\
 	ret;							\
 })
-#define softirq_endlock()	atomic_dec(&__sparc_bh_counter)
+#define softirq_endlock()	atomic_dec(&__ppc_bh_counter)
 #define clear_active_bhs(mask)				\
 do {	unsigned long flags;				\
 	spin_lock_irqsave(&global_bh_lock, flags);	\
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/spinlock.h linux/include/asm-ppc/spinlock.h
--- v2.1.78/linux/include/asm-ppc/spinlock.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/spinlock.h	Mon Jan 12 15:18:13 1998
@@ -71,71 +71,37 @@
 #define spin_unlock_wait(lp)	do { barrier(); } while((lp)->lock)
 
 extern void _spin_lock(spinlock_t *lock);
-extern int _spin_trylock(spinlock_t *lock);
 extern void _spin_unlock(spinlock_t *lock);
-extern void _spin_lock_irq(spinlock_t *lock);
-extern void _spin_unlock_irq(spinlock_t *lock);
-extern void _spin_lock_irqsave(spinlock_t *lock);
-extern void _spin_unlock_irqrestore(spinlock_t *lock);
 
 #define spin_lock(lp)			_spin_lock(lp)
-#define spin_trylock(lp)		_spin_trylock(lp)
 #define spin_unlock(lp)			_spin_unlock(lp)
-#define spin_lock_irq(lp)		_spin_lock_irq(lp)
-#define spin_unlock_irq(lp)		_spin_unlock_irq(lp)
-#define spin_lock_irqsave(lp, flags)	do { __save_and_cli(flags); \
-					     _spin_lock_irqsave(lp); } while (0)
-#define spin_unlock_irqrestore(lp, flags) do { _spin_unlock_irqrestore(lp); \
-					       __restore_flags(flags); } while(0)
-#if 0
-extern __inline__ void spin_unlock(spinlock_t *lock)
-{
-	__asm__ __volatile__("stw 0,%0" : : "m" (lock) : "memory");
-}
-
-static inline void spin_lock(spinlock_t * lock)
-{
-	int stuck = 10000000;
-	int tmp, val;
-
-	__asm__ __volatile__(
-		"	mtctr %2\n"
-		"1:	lwarx %0,0,%3\n"
-		"	andi. %1,%0,1\n\t"
-		"	ori %0,%0,1\n\t"
-		"	bne- 2f\n\t"
-		"	stwcx. %0,0,%3\n\t"
-		"2:	bdnzf- 2,1b"
-		: "=r" (tmp), "=r" (val)
-		: "r" (stuck), "r" (lock)
-		: "ctr");
-	if (!val)
-	{
-		unsigned long __nip;
-		asm("mfnip %0\n": "=r" (__nip));
-		printk("spinlock stuck at %08lx\n", __nip);
-	}
-}
-#define spin_trylock(lock) (!set_bit(0,(lock)))
+
+#define spin_trylock(l) (!test_and_set_bit(0, &((l)->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
 
-struct _rwlock_debug {
-	volatile unsigned int lock;
-	unsigned long owner_pc;
-};
-typedef struct _rwlock_debug rwlock_t;
+/*
+ * Read-write spinlocks, allowing multiple readers
+ * but only one writer.
+ *
+ * NOTE! it is quite common to have readers in interrupts
+ * but no interrupt writers. For those circumstances we
+ * can "mix" irq-safe locks - any writer needs to get a
+ * irq-safe write-lock, but readers can get non-irqsafe
+ * read-locks.
+ */
+typedef struct {
+	volatile unsigned long lock;
+	volatile unsigned long owner_pc;
+} rwlock_t;
 
 #define RW_LOCK_UNLOCKED { 0, 0 }
 
@@ -143,36 +109,25 @@
 extern void _read_unlock(rwlock_t *rw);
 extern void _write_lock(rwlock_t *rw);
 extern void _write_unlock(rwlock_t *rw);
-extern void _read_lock_irq(rwlock_t *rw);
-extern void _read_unlock_irq(rwlock_t *rw);
-extern void _write_lock_irq(rwlock_t *rw);
-extern void _write_unlock_irq(rwlock_t *rw);
-extern void _read_lock_irqsave(rwlock_t *rw);
-extern void _read_unlock_irqrestore(rwlock_t *rw);
-extern void _write_lock_irqsave(rwlock_t *rw);
-extern void _write_unlock_irqrestore(rwlock_t *rw);
 
 #define read_lock(rw)		_read_lock(rw)
-#define read_unlock(rw)		_read_unlock(rw)
 #define write_lock(rw)		_write_lock(rw)
 #define write_unlock(rw)	_write_unlock(rw)
-#define read_lock_irq(rw)	_read_lock_irq(rw)
-#define read_unlock_irq(rw)	_read_unlock_irq(rw)
-#define write_lock_irq(rw)	_write_lock_irq(rw)
-#define write_unlock_irq(rw)	_write_unlock_irq(rw)
-
-#define read_lock_irqsave(rw, flags) \
-do { __save_and_cli(flags); _read_lock_irqsave(rw); } while (0)
-
-#define read_unlock_irqrestore(rw, flags) do { _read_unlock_irqrestore(rw); \
-					       __restore_flags(flags); } while(0)
-
-#define write_lock_irqsave(rw, flags) \
-do { __save_and_cli(flags); _write_lock_irqsave(rw); } while(0)
-
-#define write_unlock_irqrestore(rw, flags) do { _write_unlock_irqrestore(rw); \
-					        __restore_flags(flags); } while(0)
+#define read_unlock(rw)		_read_unlock(rw)
 
+#define read_lock_irq(lock)	do { __cli(); read_lock(lock); } while (0)
+#define read_unlock_irq(lock)	do { read_unlock(lock); __sti(); } while (0)
+#define write_lock_irq(lock)	do { __cli(); write_lock(lock); } while (0)
+#define write_unlock_irq(lock)	do { write_unlock(lock); __sti(); } while (0)
+
+#define read_lock_irqsave(lock, flags)	\
+	do { __save_flags(flags); __cli(); read_lock(lock); } while (0)
+#define read_unlock_irqrestore(lock, flags) \
+	do { read_unlock(lock); __restore_flags(flags); } while (0)
+#define write_lock_irqsave(lock, flags)	\
+	do { __save_flags(flags); __cli(); write_lock(lock); } while (0)
+#define write_unlock_irqrestore(lock, flags) \
+	do { write_unlock(lock); __restore_flags(flags); } while (0)
 
 #endif /* SMP */
 #endif /* __ASM_SPINLOCK_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/system.h linux/include/asm-ppc/system.h
--- v2.1.78/linux/include/asm-ppc/system.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-ppc/system.h	Mon Jan 12 15:18:13 1998
@@ -10,6 +10,22 @@
 	__asm__ __volatile__ ("mfmsr %0" : "=r" ((flags)) : : "memory"); })
 #define __save_and_cli(flags)	({__save_flags(flags);__cli();})
 
+extern __inline__ void dcbf(void *line)
+{
+	asm("dcbf %0,%1\n\t"
+	    "sync \n\t"
+	    "isync \n\t"
+	    :: "r" (line), "r" (0));
+}
+
+extern __inline__ void dcbi(void *line)
+{
+	asm("dcbi %0,%1\n\t"
+	    "sync \n\t"
+	    "isync \n\t"
+	    :: "r" (line), "r" (0));
+}
+     
 extern __inline__ void __restore_flags(unsigned long flags)
 {
         extern unsigned lost_interrupts;
@@ -78,7 +94,6 @@
 		    unsigned long context);
 
 struct pt_regs;
-extern int do_signal(unsigned long oldmask, struct pt_regs *regs);
 extern void dump_regs(struct pt_regs *);
 
 #ifndef __SMP__
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/types.h linux/include/asm-ppc/types.h
--- v2.1.78/linux/include/asm-ppc/types.h	Mon May 27 02:00:59 1996
+++ linux/include/asm-ppc/types.h	Mon Jan 12 15:18:13 1998
@@ -39,6 +39,8 @@
 typedef signed long long s64;
 typedef unsigned long long u64;
 
+#define BITS_PER_LONG 32
+
 #endif /* __KERNEL__ */
 
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/uaccess.h linux/include/asm-ppc/uaccess.h
--- v2.1.78/linux/include/asm-ppc/uaccess.h	Mon Aug  4 16:25:39 1997
+++ linux/include/asm-ppc/uaccess.h	Mon Jan 12 15:18:13 1998
@@ -4,6 +4,7 @@
 #ifndef __ASSEMBLY__
 #include <linux/sched.h>
 #include <linux/errno.h>
+#include <asm/processor.h>
 
 #define VERIFY_READ	0
 #define VERIFY_WRITE	1
@@ -16,15 +17,17 @@
  * For historical reasons, these macros are grossly misnamed.
  */
 
-#define KERNEL_DS	(0)
-#define USER_DS		(1)
+#define KERNEL_DS	((mm_segment_t) { 0 })
+#define USER_DS		((mm_segment_t) { 1 })
 
-#define get_fs()	(current->tss.fs)
 #define get_ds()	(KERNEL_DS)
+#define get_fs()	(current->tss.fs)
 #define set_fs(val)	(current->tss.fs = (val))
 
+#define segment_eq(a,b)	((a).seg == (b).seg)
+
+#define __kernel_ok (segment_eq(get_fs(), KERNEL_DS))
 #define __user_ok(addr,size) (((size) <= 0x80000000)&&((addr) <= 0x80000000-(size)))
-#define __kernel_ok	(get_fs() == KERNEL_DS)
 #define __access_ok(addr,size) (__kernel_ok || __user_ok((addr),(size)))
 #define access_ok(type,addr,size) __access_ok((unsigned long)(addr),(size))
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/ucontext.h linux/include/asm-ppc/ucontext.h
--- v2.1.78/linux/include/asm-ppc/ucontext.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/ucontext.h	Mon Jan 12 15:18:13 1998
@@ -0,0 +1,13 @@
+#ifndef _ASMPPC_UCONTEXT_H
+#define _ASMPPC_UCONTEXT_H
+
+/* Copied from i386. */
+
+struct ucontext {
+	unsigned long	  uc_flags;
+	struct ucontext  *uc_link;
+	stack_t		  uc_stack;
+	sigset_t	  uc_sigmask;	/* mask last for extensibility */
+};
+
+#endif /* !_ASMPPC_UCONTEXT_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-ppc/unistd.h linux/include/asm-ppc/unistd.h
--- v2.1.78/linux/include/asm-ppc/unistd.h	Wed Sep 24 20:05:48 1997
+++ linux/include/asm-ppc/unistd.h	Mon Jan 12 15:18:13 1998
@@ -174,7 +174,9 @@
 #define __NR_query_module	166
 #define __NR_poll		167
 #define __NR_nfsservctl		168
-#define __NR_prctl		169
+#define __NR_setresgid		169
+#define __NR_getresgid		170
+#define __NR_prctl		171
 
 #define __NR(n)	#n
 #define __do_syscall(n) \
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/asm_offsets.h linux/include/asm-sparc/asm_offsets.h
--- v2.1.78/linux/include/asm-sparc/asm_offsets.h	Tue May 13 22:41:17 1997
+++ linux/include/asm-sparc/asm_offsets.h	Mon Jan 12 15:15:45 1998
@@ -8,32 +8,32 @@
 #define ASIZ_task_counter	0x00000004
 #define AOFF_task_priority	0x00000008
 #define ASIZ_task_priority	0x00000004
-#define AOFF_task_signal	0x0000000c
-#define ASIZ_task_signal	0x00000004
-#define AOFF_task_blocked	0x00000010
-#define ASIZ_task_blocked	0x00000004
-#define AOFF_task_flags	0x00000014
+#define AOFF_task_flags	0x0000000c
 #define ASIZ_task_flags	0x00000004
-#define AOFF_task_errno	0x00000018
-#define ASIZ_task_errno	0x00000004
-#define AOFF_task_debugreg	0x0000001c
+#define AOFF_task_addr_limit	0x00000010
+#define ASIZ_task_addr_limit	0x00000004
+#define AOFF_task_sigpending	0x00000014
+#define ASIZ_task_sigpending	0x00000004
+#define AOFF_task_debugreg	0x00000018
 #define ASIZ_task_debugreg	0x00000020
-#define AOFF_task_exec_domain	0x0000003c
+#define AOFF_task_exec_domain	0x00000038
 #define ASIZ_task_exec_domain	0x00000004
-#define AOFF_task_binfmt	0x00000040
+#define AOFF_task_binfmt	0x0000003c
 #define ASIZ_task_binfmt	0x00000004
-#define AOFF_task_next_task	0x00000044
+#define AOFF_task_next_task	0x00000040
 #define ASIZ_task_next_task	0x00000004
-#define AOFF_task_prev_task	0x00000048
+#define AOFF_task_prev_task	0x00000044
 #define ASIZ_task_prev_task	0x00000004
-#define AOFF_task_next_run	0x0000004c
+#define AOFF_task_next_run	0x00000048
 #define ASIZ_task_next_run	0x00000004
-#define AOFF_task_prev_run	0x00000050
+#define AOFF_task_prev_run	0x0000004c
 #define ASIZ_task_prev_run	0x00000004
-#define AOFF_task_exit_code	0x00000054
+#define AOFF_task_exit_code	0x00000050
 #define ASIZ_task_exit_code	0x00000004
-#define AOFF_task_exit_signal	0x00000058
+#define AOFF_task_exit_signal	0x00000054
 #define ASIZ_task_exit_signal	0x00000004
+#define AOFF_task_pdeath_signal	0x00000058
+#define ASIZ_task_pdeath_signal	0x00000004
 #define AOFF_task_personality	0x0000005c
 #define ASIZ_task_personality	0x00000004
 #define AOFF_task_pid	0x00000064
@@ -104,14 +104,8 @@
 #define ASIZ_task_it_virt_incr	0x00000004
 #define AOFF_task_real_timer	0x00000114
 #define ASIZ_task_real_timer	0x00000014
-#define AOFF_task_utime	0x00000128
-#define ASIZ_task_utime	0x00000004
-#define AOFF_task_stime	0x0000012c
-#define ASIZ_task_stime	0x00000004
-#define AOFF_task_cutime	0x00000130
-#define ASIZ_task_cutime	0x00000004
-#define AOFF_task_cstime	0x00000134
-#define ASIZ_task_cstime	0x00000004
+#define AOFF_task_times	0x00000128
+#define ASIZ_task_times	0x00000010
 #define AOFF_task_start_time	0x00000138
 #define ASIZ_task_start_time	0x00000004
 #define AOFF_task_min_flt	0x0000013c
@@ -138,37 +132,47 @@
 #define ASIZ_task_rlim	0x00000050
 #define AOFF_task_used_math	0x000001b8
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_comm	0x000001ba
+#define AOFF_task_io_usage	0x000001bc
+#define ASIZ_task_io_usage	0x00000004
+#define AOFF_task_comm	0x000001c0
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x000001cc
+#define AOFF_task_link_count	0x000001d0
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x000001d0
+#define AOFF_task_tty	0x000001d4
 #define ASIZ_task_tty	0x00000004
-#define AOFF_task_semundo	0x000001d4
+#define AOFF_task_semundo	0x000001d8
 #define ASIZ_task_semundo	0x00000004
-#define AOFF_task_semsleeping	0x000001d8
+#define AOFF_task_semsleeping	0x000001dc
 #define ASIZ_task_semsleeping	0x00000004
-#define AOFF_task_ldt	0x000001dc
+#define AOFF_task_ldt	0x000001e0
 #define ASIZ_task_ldt	0x00000004
-#define AOFF_task_tss	0x000001e0
+#define AOFF_task_tss	0x000001e8
 #define ASIZ_task_tss	0x00000390
-#define AOFF_task_fs	0x00000570
+#define AOFF_task_fs	0x00000578
 #define ASIZ_task_fs	0x00000004
-#define AOFF_task_files	0x00000574
+#define AOFF_task_files	0x0000057c
 #define ASIZ_task_files	0x00000004
-#define AOFF_task_mm	0x00000578
+#define AOFF_task_mm	0x00000580
 #define ASIZ_task_mm	0x00000004
-#define AOFF_task_sig	0x0000057c
+#define AOFF_task_sig	0x00000584
 #define ASIZ_task_sig	0x00000004
-#define AOFF_task_has_cpu	0x00000580
+#define AOFF_task_signal	0x00000588
+#define ASIZ_task_signal	0x00000008
+#define AOFF_task_blocked	0x00000590
+#define ASIZ_task_blocked	0x00000008
+#define AOFF_task_sigqueue	0x00000598
+#define ASIZ_task_sigqueue	0x00000004
+#define AOFF_task_sigqueue_tail	0x0000059c
+#define ASIZ_task_sigqueue_tail	0x00000004
+#define AOFF_task_has_cpu	0x000005a0
 #define ASIZ_task_has_cpu	0x00000004
-#define AOFF_task_processor	0x00000584
+#define AOFF_task_processor	0x000005a4
 #define ASIZ_task_processor	0x00000004
-#define AOFF_task_last_processor	0x00000588
+#define AOFF_task_last_processor	0x000005a8
 #define ASIZ_task_last_processor	0x00000004
-#define AOFF_task_lock_depth	0x0000058c
+#define AOFF_task_lock_depth	0x000005ac
 #define ASIZ_task_lock_depth	0x00000004
-#define AOFF_task_sigmask_lock	0x00000590
+#define AOFF_task_sigmask_lock	0x000005b0
 #define ASIZ_task_sigmask_lock	0x00000000
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000004
@@ -214,6 +218,8 @@
 #define ASIZ_mm_locked_vm	0x00000004
 #define AOFF_mm_def_flags	0x0000005c
 #define ASIZ_mm_def_flags	0x00000004
+#define AOFF_mm_cpu_vm_mask	0x00000060
+#define ASIZ_mm_cpu_vm_mask	0x00000004
 #define AOFF_thread_uwinmask	0x00000000
 #define ASIZ_thread_uwinmask	0x00000004
 #define AOFF_thread_kregs	0x00000004
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/auxio.h linux/include/asm-sparc/auxio.h
--- v2.1.78/linux/include/asm-sparc/auxio.h	Tue May 13 22:41:17 1997
+++ linux/include/asm-sparc/auxio.h	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: auxio.h,v 1.17 1997/05/01 01:42:02 davem Exp $
+/* $Id: auxio.h,v 1.18 1997/11/07 15:01:45 jj Exp $
  * auxio.h:  Definitions and code for the Auxiliary I/O register.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -58,6 +58,8 @@
 			break;     /* VME chassic sun4m, no auxio. */ \
 		regval = *AUXREG; \
 		*AUXREG = ((regval | bits_on) & ~bits_off) | AUXIO_ORMEIN4M; \
+		break; \
+	case sun4d: \
 		break; \
 	default: \
 		panic("Can't set AUXIO register on this machine."); \
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/bitops.h linux/include/asm-sparc/bitops.h
--- v2.1.78/linux/include/asm-sparc/bitops.h	Thu May 15 16:48:04 1997
+++ linux/include/asm-sparc/bitops.h	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: bitops.h,v 1.47 1997/05/14 20:47:56 davem Exp $
+/* $Id: bitops.h,v 1.48 1997/12/18 02:44:06 ecd Exp $
  * bitops.h: Bit string operations on the Sparc.
  *
  * Copyright 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -9,6 +9,7 @@
 #define _SPARC_BITOPS_H
 
 #include <linux/kernel.h>
+#include <asm/byteorder.h>
 
 #ifndef __KERNEL__
 
@@ -335,12 +336,6 @@
 
 #define find_first_zero_le_bit(addr, size) \
         find_next_zero_le_bit((addr), (size), 0)
-
-extern __inline__ unsigned long __swab32(unsigned long value)
-{
-	return((value>>24) | ((value>>8)&0xff00) |
-	       ((value<<8)&0xff0000) | (value<<24));
-}     
 
 extern __inline__ unsigned long find_next_zero_le_bit(void *addr, unsigned long size, unsigned long offset)
 {
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/byteorder.h linux/include/asm-sparc/byteorder.h
--- v2.1.78/linux/include/asm-sparc/byteorder.h	Wed Dec 10 11:12:45 1997
+++ linux/include/asm-sparc/byteorder.h	Mon Jan 12 15:15:45 1998
@@ -1,3 +1,4 @@
+/* $Id: byteorder.h,v 1.15 1997/12/16 19:20:44 davem Exp $ */
 #ifndef _SPARC_BYTEORDER_H
 #define _SPARC_BYTEORDER_H
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/delay.h linux/include/asm-sparc/delay.h
--- v2.1.78/linux/include/asm-sparc/delay.h	Mon Apr 14 16:28:20 1997
+++ linux/include/asm-sparc/delay.h	Mon Jan 12 15:15:45 1998
@@ -1,4 +1,4 @@
-/* $Id: delay.h,v 1.9 1997/04/11 00:42:19 davem Exp $
+/* $Id: delay.h,v 1.10 1997/11/07 18:24:30 mj Exp $
  * delay.h: Linux delay routines on the Sparc.
  *
  * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu).
@@ -22,8 +22,4 @@
 /* This is too messy with inline asm on the Sparc. */
 extern void udelay(unsigned long usecs);
 
-/* calibrate_delay() wants this... */
-#define muldiv(a, b, c)    (((a)*(b))/(c))
-
 #endif /* defined(__SPARC_DELAY_H) */
-
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/elf.h linux/include/asm-sparc/elf.h
--- v2.1.78/linux/include/asm-sparc/elf.h	Wed Sep 24 20:05:48 1997
+++ linux/include/asm-sparc/elf.h	Mon Jan 12 14:52:46 1998
@@ -1,4 +1,4 @@
-/* $Id: elf.h,v 1.7 1997/08/29 17:04:34 richard Exp $ */
+/* $Id: elf.h,v 1.11 1997/09/26 18:37:32 tdyas Exp $ */
 #ifndef __ASMSPARC_ELF_H
 #define __ASMSPARC_ELF_H
 
@@ -7,6 +7,7 @@
  */
 
 #include <asm/ptrace.h>
+#include <asm/mbus.h>
 
 typedef unsigned long elf_greg_t;
 
@@ -37,5 +38,30 @@
 
 #define ELF_ET_DYN_BASE         (TASK_UNMAPPED_BASE + 0x1000000)
 
+/* This yields a mask that user programs can use to figure out what
+   instruction set this cpu supports.  This can NOT be done in userspace
+   on Sparc.  */
+
+/* Sun4c has none of the capabilities, most sun4m's have them all.
+ * XXX This is gross, set some global variable at boot time. -DaveM
+ */
+#define ELF_HWCAP	((sparc_cpu_model == sun4c) ? 0 : \
+			 (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | \
+			  HWCAP_SPARC_SWAP | \
+			  ((srmmu_modtype != Cypress && \
+			    srmmu_modtype != Cypress_vE && \
+			    srmmu_modtype != Cypress_vD) ? \
+			   HWCAP_SPARC_MULDIV : 0)))
+
+/* This yields a string that ld.so will use to load implementation
+   specific libraries for optimization.  This is more specific in
+   intent than poking at uname or /proc/cpuinfo.  */
+
+#define ELF_PLATFORM	(NULL)
+
+#ifdef __KERNEL__
+#define SET_PERSONALITY(ibcs2) \
+	current->personality = (ibcs2 ? PER_SVR4 : PER_LINUX)
+#endif
 
 #endif /* !(__ASMSPARC_ELF_H) */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/head.h linux/include/asm-sparc/head.h
--- v2.1.78/linux/include/asm-sparc/head.h	Fri Dec 13 01:37:40 1996
+++ linux/include/asm-sparc/head.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: head.h,v 1.32 1996/12/04 00:12:48 ecd Exp $ */
+/* $Id: head.h,v 1.33 1997/10/04 08:54:22 ecd Exp $ */
 #ifndef __SPARC_HEAD_H
 #define __SPARC_HEAD_H
 
@@ -111,4 +111,4 @@
 #define WINDOW_FILL \
         rd %psr, %l0; rd %wim, %l3; b fill_window_entry; andcc %l0, PSR_PS, %g0;
 
-#endif __SPARC_HEAD_H
+#endif /* __SPARC_HEAD_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/io-unit.h linux/include/asm-sparc/io-unit.h
--- v2.1.78/linux/include/asm-sparc/io-unit.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/io-unit.h	Mon Jan 12 15:15:54 1998
@@ -0,0 +1,43 @@
+/* io-unit.h: Definitions for the sun4d IO-UNIT.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+#ifndef _SPARC_IO_UNIT_H
+#define _SPARC_IO_UNIT_H
+
+#include <asm/page.h>
+#include <asm/spinlock.h>
+
+/* The io-unit handles all virtual to physical address translations
+ * that occur between the SBUS and physical memory.  Access by
+ * the cpu to IO registers and similar go over the xdbus so are
+ * translated by the on chip SRMMU.  The io-unit and the srmmu do
+ * not need to have the same translations at all, in fact most
+ * of the time the translations they handle are a disjunct set.
+ * Basically the io-unit handles all dvma sbus activity.
+ */
+ 
+/* AIEEE, unlike the nice sun4m, these monsters have 
+   fixed DMA range 64M */
+ 
+#define IOUNIT_DMA_BASE	    0xfc000000 /* TOP - 64M */
+#define IOUNIT_DMA_SIZE	    0x04000000 /* 64M */
+/* We use last 4M for sparc_dvma_malloc */
+#define IOUNIT_DVMA_SIZE    0x00400000 /* 4M */
+
+/* The format of an iopte in the external page tables */
+#define IOUPTE_PAGE          0xffffff00 /* Physical page number (PA[35:12]) */
+#define IOUPTE_CACHE         0x00000080 /* Cached (in Viking/MXCC) */
+#define IOUPTE_STREAM        0x00000040
+#define IOUPTE_INTRA	     0x00000008 /* Not regular memory - probably direct sbus<->sbus dma 
+					   FIXME: Play with this and find out how we can make use of this */
+#define IOUPTE_WRITE         0x00000004 /* Writeable */
+#define IOUPTE_VALID         0x00000002 /* IOPTE is valid */
+#define IOUPTE_PARITY        0x00000001
+
+struct iounit_struct {
+	spinlock_t		iommu_lock;
+	iopte_t			*page_table;
+};
+
+#endif /* !(_SPARC_IO_UNIT_H) */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/irq.h linux/include/asm-sparc/irq.h
--- v2.1.78/linux/include/asm-sparc/irq.h	Thu May 29 21:53:09 1997
+++ linux/include/asm-sparc/irq.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: irq.h,v 1.20 1997/05/28 05:25:07 davem Exp $
+/* $Id: irq.h,v 1.21 1997/11/19 15:12:20 jj Exp $
  * irq.h: IRQ registers on the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -10,6 +10,17 @@
 #include <linux/linkage.h>
 
 #include <asm/system.h>     /* For NCPUS */
+
+/* This is used for sun4d */
+struct devid_cookie {
+        /* Caller specifies these. */
+        void            *real_dev_id;           /* What dev_id would usually contain. */
+        void            *bus_cookie;            /* linux_sbus_device *, etc. */
+        /* Return values. */
+        unsigned int    ret_ino;
+};
+
+#define SA_DCOOKIE      0x10000
 
 #define NR_IRQS    15
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/kdebug.h linux/include/asm-sparc/kdebug.h
--- v2.1.78/linux/include/asm-sparc/kdebug.h	Sat Nov  9 11:54:14 1996
+++ linux/include/asm-sparc/kdebug.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: kdebug.h,v 1.9 1996/05/10 19:52:35 davem Exp $
+/* $Id: kdebug.h,v 1.10 1997/12/14 23:24:40 ecd Exp $
  * kdebug.h:  Defines and definitions for debugging the Linux kernel
  *            under various kernel debuggers.
  *
@@ -71,9 +71,5 @@
 #define KDEBUG_DUNNO_OFF    0x4
 #define KDEBUG_DUNNO2_OFF   0x8
 #define KDEBUG_TEACH_OFF    0xc
-
-/* ugh... */
-#define TRAP_TRACE(reg1, reg2) \
-     
 
 #endif /* !(_SPARC_KDEBUG_H) */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/kgdb.h linux/include/asm-sparc/kgdb.h
--- v2.1.78/linux/include/asm-sparc/kgdb.h	Sat Nov  9 00:29:34 1996
+++ linux/include/asm-sparc/kgdb.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: kgdb.h,v 1.7 1995/11/27 02:43:18 davem Exp $
+/* $Id: kgdb.h,v 1.8 1998/01/07 06:33:44 baccala Exp $
  * kgdb.h: Defines and declarations for serial line source level
  *         remote debugging of the Linux kernel using gdb.
  *
@@ -83,8 +83,12 @@
         ldd     [%reg + REGWIN_SZ + KGDB_I4], %i4; \
         ldd     [%reg + REGWIN_SZ + KGDB_I6], %i6;
 
-#define LOAD_KGDB_SREGS(reg, reg_y_and_psr, reg_pc_and_npc) \
-        ldd     [%reg + REGWIN_SZ + KGDB_Y], %reg_y_and_psr; \
-        ldd     [%reg + REGWIN_SZ + KGDB_PC], %reg_pc_and_npc;
+#define LOAD_KGDB_SREGS(reg, reg_y, reg_psr, reg_wim, reg_tbr, reg_pc, reg_npc) \
+	ld	[%reg + REGWIN_SZ + KGDB_Y], %reg_y; \
+	ld	[%reg + REGWIN_SZ + KGDB_PSR], %reg_psr; \
+	ld	[%reg + REGWIN_SZ + KGDB_WIM], %reg_wim; \
+	ld	[%reg + REGWIN_SZ + KGDB_TBR], %reg_tbr; \
+	ld	[%reg + REGWIN_SZ + KGDB_PC], %reg_pc; \
+	ld	[%reg + REGWIN_SZ + KGDB_NPC], %reg_npc;
 
 #endif /* !(_SPARC_KGDB_H) */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/md.h linux/include/asm-sparc/md.h
--- v2.1.78/linux/include/asm-sparc/md.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/md.h	Mon Jan 12 15:15:54 1998
@@ -0,0 +1,13 @@
+/* $Id: md.h,v 1.1 1997/12/15 15:12:39 jj Exp $
+ * md.h: High speed xor_block operation for RAID4/5 
+ *
+ */
+ 
+#ifndef __ASM_MD_H
+#define __ASM_MD_H
+
+/* #define HAVE_ARCH_XORBLOCK */
+
+#define MD_XORBLOCK_ALIGNMENT	sizeof(long)
+
+#endif /* __ASM_MD_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/namei.h linux/include/asm-sparc/namei.h
--- v2.1.78/linux/include/asm-sparc/namei.h	Sun Sep  7 13:10:43 1997
+++ linux/include/asm-sparc/namei.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: namei.h,v 1.8 1997/09/05 12:38:51 jj Exp $
+/* $Id: namei.h,v 1.10 1997/09/24 16:20:36 jj Exp $
  * linux/include/asm-sparc/namei.h
  *
  * Routines to handle famous /usr/gnemul/s*.
@@ -14,37 +14,48 @@
 static inline struct dentry *
 __sparc_lookup_dentry(const char *name, int follow_link)
 {
-	int error;
 	struct dentry *base;
+	char *emul;
 
 	switch (current->personality) {
 	case PER_BSD:
+		emul = SPARC_BSD_EMUL; break;
 	case PER_SVR4:
-		break;
+		emul = SPARC_SOL_EMUL; break;
 	default:
-		return ERR_PTR(-ENOENT);
+		return NULL;
 	}
 
-	base = lookup_dentry ((current->personality == PER_BSD) ?
-			SPARC_BSD_EMUL : SPARC_SOL_EMUL,
-			dget (current->fs->root), 1);
+	base = lookup_dentry (emul, dget (current->fs->root), 1);
 			
-	if (IS_ERR (base)) return base;
+	if (IS_ERR (base)) return NULL;
 	
 	base = lookup_dentry (name, base, follow_link);
-
-	if (IS_ERR (base)) return base;
-
+	
+	if (IS_ERR (base)) return NULL;
+	
 	if (!base->d_inode) {
-		dput(base);
-		return ERR_PTR(-ENOENT);
+		struct dentry *fromroot;
+		
+		fromroot = lookup_dentry (name, dget (current->fs->root), follow_link);
+		
+		if (IS_ERR (fromroot)) return base;
+		
+		if (fromroot->d_inode) {
+			dput(base);
+			return fromroot;
+		}
+		
+		dput(fromroot);
 	}
-        
-        return base;
+	
+	return base;
 }
 
 #define __prefix_lookup_dentry(name, follow_link)				\
-	dentry = __sparc_lookup_dentry (name, follow_link);			\
-	if (!IS_ERR (dentry)) return dentry;
+	if (current->personality) {						\
+		dentry = __sparc_lookup_dentry (name, follow_link);		\
+		if (dentry) return dentry;					\
+	}
 
 #endif /* __SPARC_NAMEI_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/obio.h linux/include/asm-sparc/obio.h
--- v2.1.78/linux/include/asm-sparc/obio.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/obio.h	Mon Jan 12 15:15:54 1998
@@ -0,0 +1,203 @@
+/* $Id: obio.h,v 1.3 1997/12/18 14:21:41 jj Exp $
+ * obio.h:  Some useful locations in 0xFXXXXXXXX PA obio space on sun4d.
+ *
+ * Copyright (C) 1997 Jakub Jelinek <jj@sunsite.mff.cuni.cz>
+ */
+
+#ifndef _SPARC_OBIO_H
+#define _SPARC_OBIO_H
+
+#include <asm/asi.h>
+
+/* This weird monster likes to use the very upper parts of
+   36bit PA for these things :) */
+   
+/* CSR space (for each XDBUS)
+ *  ------------------------------------------------------------------------
+ *  |   0xFE  |   DEVID    |                | XDBUS ID |                   |
+ *  ------------------------------------------------------------------------
+ *  35      28 27        20 19            10 9        8 7                 0
+ */
+   
+#define CSR_BASE_ADDR		0xe0000000
+#define CSR_CPU_SHIFT		(32 - 4 - 5)
+#define CSR_XDBUS_SHIFT		8
+
+#define CSR_BASE(cpu) (((CSR_BASE_ADDR >> CSR_CPU_SHIFT) + cpu) << CSR_CPU_SHIFT)
+
+/* ECSR space (not for each XDBUS)
+ *  ------------------------------------------------------------------------
+ *  |   0xF  | DEVID[7:1] |                			           |
+ *  ------------------------------------------------------------------------
+ *  35     32 31        25 24                 				  0
+ */
+   
+#define ECSR_BASE_ADDR		0x00000000
+#define ECSR_CPU_SHIFT		(32 - 5)
+#define ECSR_DEV_SHIFT		(32 - 8)
+
+#define ECSR_BASE(cpu) ((cpu) << ECSR_CPU_SHIFT)
+#define ECSR_DEV_BASE(devid) ((devid) << ECSR_DEV_SHIFT) 
+
+/* Bus Watcher */
+#define BW_LOCAL_BASE		0xfff00000
+
+#define BW_CID			0x00000000
+#define BW_DBUS_CTRL		0x00000008
+#define BW_DBUS_DATA		0x00000010
+#define BW_CTRL			0x00001000
+#define BW_INTR_TABLE		0x00001040
+#define BW_INTR_TABLE_CLEAR	0x00001080
+#define BW_PRESCALER		0x000010c0
+#define BW_PTIMER_LIMIT		0x00002000
+#define BW_PTIMER_COUNTER2	0x00002004
+#define BW_PTIMER_NDLIMIT	0x00002008
+#define BW_PTIMER_CTRL		0x0000200c
+#define BW_PTIMER_COUNTER	0x00002010
+#define BW_TIMER_LIMIT		0x00003000
+#define BW_TIMER_COUNTER2	0x00003004
+#define BW_TIMER_NDLIMIT	0x00003008
+#define BW_TIMER_CTRL		0x0000300c
+#define BW_TIMER_COUNTER	0x00003010
+
+/* BW Control */
+#define BW_CTRL_USER_TIMER	0x00000004	/* Is User Timer Free run enabled */
+
+/* Boot Bus */
+#define BB_LOCAL_BASE		0xf0000000
+
+#define BB_LEDS			0x002e0000
+
+/* Cache Controller */
+#define CC_DATSTREAM	0x1F00000  /* Data stream register */
+#define CC_DATSIZE	0x1F0003F  /* Size */
+#define CC_SRCSTREAM	0x1F00100  /* Source stream register */
+#define CC_DESSTREAM	0x1F00200  /* Destination stream register */
+#define CC_RMCOUNT	0x1F00300  /* Count of references and misses */
+#define CC_IPEN		0x1F00406  /* Pending Interrupts */
+#define CC_IMSK		0x1F00506  /* Interrupt Mask */
+#define CC_ICLR		0x1F00606  /* Clear pending Interrupts */
+#define CC_IGEN		0x1F00704  /* Generate Interrupt register */
+#define CC_STEST	0x1F00804  /* Internal self-test */
+#define CC_CREG		0x1F00A04  /* Control register */
+#define CC_SREG		0x1F00B00  /* Status register */
+#define CC_RREG		0x1F00C04  /* Reset register */
+#define CC_EREG		0x1F00E00  /* Error code register */
+#define CC_CID		0x1F00F04  /* Component ID */
+
+#ifndef __ASSEMBLY__
+
+extern __inline__ int bw_get_intr_mask(int sbus_level)
+{
+	int mask;
+	
+	__asm__ __volatile__ ("lduha [%1] %2, %0" :
+			      "=r" (mask) :
+			      "r" (BW_LOCAL_BASE + BW_INTR_TABLE + (sbus_level << 3)),
+			      "i" (ASI_M_CTL));
+	return mask;
+}
+
+extern __inline__ void bw_clear_intr_mask(int sbus_level, int mask)
+{
+	__asm__ __volatile__ ("stha %0, [%1] %2" : :
+			      "r" (mask),
+			      "r" (BW_LOCAL_BASE + BW_INTR_TABLE_CLEAR + (sbus_level << 3)),
+			      "i" (ASI_M_CTL));
+}
+
+extern __inline__ unsigned bw_get_prof_limit(int cpu)
+{
+	unsigned limit;
+	
+	__asm__ __volatile__ ("lda [%1] %2, %0" :
+			      "=r" (limit) :
+			      "r" (CSR_BASE(cpu) + BW_PTIMER_LIMIT),
+			      "i" (ASI_M_CTL));
+	return limit;
+}
+
+extern __inline__ void bw_set_prof_limit(int cpu, unsigned limit)
+{
+	__asm__ __volatile__ ("sta %0, [%1] %2" : :
+			      "r" (limit),
+			      "r" (CSR_BASE(cpu) + BW_PTIMER_LIMIT),
+			      "i" (ASI_M_CTL));
+}
+
+extern __inline__ unsigned bw_get_ctrl(int cpu)
+{
+	unsigned ctrl;
+	
+	__asm__ __volatile__ ("lda [%1] %2, %0" :
+			      "=r" (ctrl) :
+			      "r" (CSR_BASE(cpu) + BW_CTRL),
+			      "i" (ASI_M_CTL));
+	return ctrl;
+}
+
+extern __inline__ void bw_set_ctrl(int cpu, unsigned ctrl)
+{
+	__asm__ __volatile__ ("sta %0, [%1] %2" : :
+			      "r" (ctrl),
+			      "r" (CSR_BASE(cpu) + BW_CTRL),
+			      "i" (ASI_M_CTL));
+}
+
+extern __inline__ void show_leds(int cpuid, unsigned char mask)
+{
+	__asm__ __volatile__ ("stba %0, [%1] %2" : :
+			      "r" (mask),
+			      "r" (ECSR_BASE(cpuid) | BB_LEDS),
+			      "i" (ASI_M_CTL));
+}
+
+extern __inline__ unsigned cc_get_ipen(void)
+{
+	unsigned pending;
+	
+	__asm__ __volatile__ ("lduha [%1] %2, %0" :
+			      "=r" (pending) :
+			      "r" (CC_IPEN),
+			      "i" (ASI_M_MXCC));
+	return pending;
+}
+
+extern __inline__ void cc_set_iclr(unsigned clear)
+{
+	__asm__ __volatile__ ("stha %0, [%1] %2" : :
+			      "r" (clear),
+			      "r" (CC_ICLR),
+			      "i" (ASI_M_MXCC));
+}
+
+extern __inline__ unsigned cc_get_imsk(void)
+{
+	unsigned mask;
+	
+	__asm__ __volatile__ ("lduha [%1] %2, %0" :
+			      "=r" (mask) :
+			      "r" (CC_IMSK),
+			      "i" (ASI_M_MXCC));
+	return mask;
+}
+
+extern __inline__ void cc_set_imsk(unsigned mask)
+{
+	__asm__ __volatile__ ("stha %0, [%1] %2" : :
+			      "r" (mask),
+			      "r" (CC_IMSK),
+			      "i" (ASI_M_MXCC));
+}
+
+extern __inline__ void cc_set_igen(unsigned gen)
+{
+	__asm__ __volatile__ ("sta %0, [%1] %2" : :
+			      "r" (gen),
+			      "r" (CC_IGEN),
+			      "i" (ASI_M_MXCC));
+}
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* !(_SPARC_OBIO_H) */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/oplib.h linux/include/asm-sparc/oplib.h
--- v2.1.78/linux/include/asm-sparc/oplib.h	Mon Jul  7 08:18:55 1997
+++ linux/include/asm-sparc/oplib.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: oplib.h,v 1.16 1997/06/27 14:55:04 jj Exp $
+/* $Id: oplib.h,v 1.18 1997/09/24 11:34:18 jj Exp $
  * oplib.h:  Describes the interface and available routines in the
  *           Linux Prom library.
  *
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/page.h linux/include/asm-sparc/page.h
--- v2.1.78/linux/include/asm-sparc/page.h	Thu Dec 19 01:03:37 1996
+++ linux/include/asm-sparc/page.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: page.h,v 1.35 1996/12/19 08:08:22 davem Exp $
+/* $Id: page.h,v 1.37 1997/11/28 15:59:21 jj Exp $
  * page.h:  Various defines and such for MMU operations on the Sparc for
  *          the Linux kernel.
  *
@@ -8,14 +8,15 @@
 #ifndef _SPARC_PAGE_H
 #define _SPARC_PAGE_H
 
-#include <linux/config.h>
-#include <asm/head.h>       /* for KERNBASE */
-
 #define PAGE_SHIFT   12
 #define PAGE_SIZE    (1 << PAGE_SHIFT)
 #define PAGE_MASK    (~(PAGE_SIZE-1))
 
 #ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <asm/head.h>       /* for KERNBASE */
+
 #ifndef __ASSEMBLY__
 
 #define clear_page(page)	memset((void *)(page), 0, PAGE_SIZE)
@@ -29,8 +30,8 @@
 extern unsigned long (*mmu_v2p)(unsigned long);
 extern unsigned long (*mmu_p2v)(unsigned long);
 
-#define __pa(x)    mmu_v2p((unsigned long)(x))
-#define __va(x)    mmu_p2v((unsigned long)(x))
+#define __pa(x)    (mmu_v2p((unsigned long)(x)))
+#define __va(x)    ((void *)(mmu_p2v((unsigned long)(x))))
 
 /* The following structure is used to hold the physical
  * memory configuration of the machine.  This is filled in
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/pgtable.h linux/include/asm-sparc/pgtable.h
--- v2.1.78/linux/include/asm-sparc/pgtable.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc/pgtable.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.63 1997/08/13 04:44:15 paulus Exp $ */
+/* $Id: pgtable.h,v 1.64 1997/11/07 15:01:44 jj Exp $ */
 #ifndef _SPARC_PGTABLE_H
 #define _SPARC_PGTABLE_H
 
@@ -376,6 +376,7 @@
 	case sun4c:
 		return sun4c_get_pte (addr) << PAGE_SHIFT;
 	case sun4m:
+	case sun4d:
 		return ((srmmu_get_pte (addr) & 0xffffff00) << 4);
 	default:
 		return 0;
@@ -389,6 +390,7 @@
 	case sun4c:
 		return -1; /* Don't check iospace on sun4c */
 	case sun4m:
+	case sun4d:
 		return (srmmu_get_pte (addr) >> 28);
 	default:
 		return -1;
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/processor.h linux/include/asm-sparc/processor.h
--- v2.1.78/linux/include/asm-sparc/processor.h	Tue May 13 22:41:18 1997
+++ linux/include/asm-sparc/processor.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.59 1997/05/01 01:42:03 davem Exp $
+/* $Id: processor.h,v 1.61 1997/10/22 09:25:42 jj Exp $
  * include/asm-sparc/processor.h
  *
  * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
@@ -35,11 +35,20 @@
  */
 #define TASK_SIZE	(page_offset)
 
+#define COPY_TASK_STRUCT(dst, src) 	\
+do {					\
+	*dst = *src;			\
+} while (0)
+
 struct fpq {
 	unsigned long *insn_addr;
 	unsigned long insn;
 };
 
+typedef struct {
+	int seg;
+} mm_segment_t;
+
 /* The Sparc processor specific thread struct. */
 struct thread_struct {
 	unsigned long uwinmask __attribute__ ((aligned (8)));
@@ -74,7 +83,7 @@
 	struct fpq	fpqueue[16];
 	struct sigstack sstk_info;
 	unsigned long flags;
-	int current_ds;
+	mm_segment_t current_ds;
 	struct exec core_exec;     /* just what it says. */
 	int new_signal;
 };
@@ -103,7 +112,7 @@
 /* sstk_info */ \
 { 0, 0, }, \
 /* flags,              current_ds, */ \
-   SPARC_FLAG_KTHREAD, USER_DS, \
+   SPARC_FLAG_KTHREAD, KERNEL_DS, \
 /* core_exec */ \
 { 0, }, \
 /* new_signal */ \
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/psr.h linux/include/asm-sparc/psr.h
--- v2.1.78/linux/include/asm-sparc/psr.h	Mon Apr 14 16:28:21 1997
+++ linux/include/asm-sparc/psr.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: psr.h,v 1.14 1997/04/11 00:42:19 davem Exp $
+/* $Id: psr.h,v 1.15 1997/10/04 08:54:22 ecd Exp $
  * psr.h: This file holds the macros for masking off various parts of
  *        the processor status register on the Sparc. This is valid
  *        for Version 8. On the V9 this is renamed to the PSTATE
@@ -41,12 +41,12 @@
 extern __inline__ unsigned int get_psr(void)
 {
 	unsigned int psr;
-	__asm__ __volatile__("
-		rd	%%psr, %0
-		nop
-		nop
-		nop
-"	: "=r" (psr)
+	__asm__ __volatile__(
+		"rd	%%psr, %0\n\t"
+		"nop\n\t"
+		"nop\n\t"
+		"nop\n\t"
+	: "=r" (psr)
 	: /* no inputs */
 	: "memory");
 
@@ -55,12 +55,12 @@
 
 extern __inline__ void put_psr(unsigned int new_psr)
 {
-	__asm__ __volatile__("
-		wr	%0, 0x0, %%psr
-		nop
-		nop
-		nop
-"	: /* no outputs */
+	__asm__ __volatile__(
+		"wr	%0, 0x0, %%psr\n\t"
+		"nop\n\t"
+		"nop\n\t"
+		"nop\n\t"
+	: /* no outputs */
 	: "r" (new_psr)
 	: "memory", "cc");
 }
@@ -76,10 +76,10 @@
 {
 	unsigned int fsr = 0;
 
-	__asm__ __volatile__("
-		st	%%fsr, %1
-		ld	%1, %0
-"	: "=r" (fsr)
+	__asm__ __volatile__(
+		"st	%%fsr, %1\n\t"
+		"ld	%1, %0\n\t"
+	: "=r" (fsr)
 	: "m" (fsr_storage));
 
 	return fsr;
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/ross.h linux/include/asm-sparc/ross.h
--- v2.1.78/linux/include/asm-sparc/ross.h	Thu Mar 27 14:40:09 1997
+++ linux/include/asm-sparc/ross.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: ross.h,v 1.12 1997/03/10 09:16:57 davem Exp $
+/* $Id: ross.h,v 1.13 1998/01/07 06:49:11 baccala Exp $
  * ross.h: Ross module specific definitions and defines.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -8,6 +8,7 @@
 #define _SPARC_ROSS_H
 
 #include <asm/asi.h>
+#include <asm/page.h>
 
 /* Ross made Hypersparcs have a %psr 'impl' field of '0001'.  The 'vers'
  * field has '1111'.
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/sbi.h linux/include/asm-sparc/sbi.h
--- v2.1.78/linux/include/asm-sparc/sbi.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/sbi.h	Mon Jan 12 15:15:54 1998
@@ -0,0 +1,107 @@
+/* $Id: sbi.h,v 1.1 1997/11/19 15:12:16 jj Exp $
+ * sbi.h:  SBI (Sbus Interface on sun4d) definitions
+ *
+ * Copyright (C) 1997 Jakub Jelinek <jj@sunsite.mff.cuni.cz>
+ */
+
+#ifndef _SPARC_SBI_H
+#define _SPARC_SBI_H
+
+#include <asm/obio.h>
+
+/* SBI */
+struct sbi_regs {
+/* 0x0000 */	u32		cid;		/* Component ID */
+/* 0x0004 */	u32		ctl;		/* Control */
+/* 0x0008 */	u32		status;		/* Status */
+		u32		_unused1;
+		
+/* 0x0010 */	u32		cfg0;		/* Slot0 config reg */
+/* 0x0014 */	u32		cfg1;		/* Slot1 config reg */
+/* 0x0018 */	u32		cfg2;		/* Slot2 config reg */
+/* 0x001c */	u32		cfg3;		/* Slot3 config reg */
+
+/* 0x0020 */	u32		stb0;		/* Streaming buf control for slot 0 */
+/* 0x0024 */	u32		stb1;		/* Streaming buf control for slot 1 */
+/* 0x0028 */	u32		stb2;		/* Streaming buf control for slot 2 */
+/* 0x002c */	u32		stb3;		/* Streaming buf control for slot 3 */
+
+/* 0x0030 */	u32		intr_state;	/* Interrupt state */
+/* 0x0034 */	u32		intr_tid;	/* Interrupt target ID */
+/* 0x0038 */	u32		intr_diag;	/* Interrupt diagnostics */
+};
+
+#define SBI_CID			0x02800000
+#define SBI_CTL			0x02800004
+#define SBI_STATUS		0x02800008
+#define SBI_CFG0		0x02800010
+#define SBI_CFG1		0x02800014
+#define SBI_CFG2		0x02800018
+#define SBI_CFG3		0x0280001c
+#define SBI_STB0		0x02800020
+#define SBI_STB1		0x02800024
+#define SBI_STB2		0x02800028
+#define SBI_STB3		0x0280002c
+#define SBI_INTR_STATE		0x02800030
+#define SBI_INTR_TID		0x02800034
+#define SBI_INTR_DIAG		0x02800038
+
+/* Burst bits for 8, 16, 32, 64 are in cfgX registers at bits 2, 3, 4, 5 respectively */
+#define SBI_CFG_BURST_MASK	0x0000001e
+
+/* How to make devid from sbi no */
+#define SBI2DEVID(sbino) ((sbino<<4)|2)
+
+/* intr_state has 4 bits for slots 0 .. 3 and these bits are repeated for each sbus irq level
+ *
+ *		   +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  SBUS IRQ LEVEL |   7   |   6   |   5   |   4   |   3   |   2   |   1   |       |
+ *		   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Reser |
+ *  SLOT #         |3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0|  ved  |
+ *                 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------+
+ *  Bits           31      27      23      19      15      11      7       3      0
+ */
+
+
+#ifndef __ASSEMBLY__
+
+extern __inline__ int acquire_sbi(int devid, int mask)
+{
+	__asm__ __volatile__ ("swapa [%2] %3, %0" :
+			      "=r" (mask) :
+			      "0" (mask),
+			      "r" (ECSR_DEV_BASE(devid) | SBI_INTR_STATE),
+			      "i" (ASI_M_CTL));
+	return mask;
+}
+
+extern __inline__ void release_sbi(int devid, int mask)
+{
+	__asm__ __volatile__ ("sta %0, [%1] %2" : :
+			      "r" (mask),
+			      "r" (ECSR_DEV_BASE(devid) | SBI_INTR_STATE),
+			      "i" (ASI_M_CTL));
+}
+
+extern __inline__ int get_sbi_ctl(int devid, int cfgno)
+{
+	int cfg;
+	
+	__asm__ __volatile__ ("lda [%1] %2, %0" :
+			      "=r" (cfg) :
+			      "r" ((ECSR_DEV_BASE(devid) | SBI_CFG0) + (cfgno<<2)),
+			      "i" (ASI_M_CTL));
+	return cfg;
+}
+
+extern __inline__ void set_sbi_ctl(int devid, int cfgno, int cfg)
+{
+	__asm__ __volatile__ ("sta %0, [%1] %2" : :
+			      "r" (cfg),
+			      "r" ((ECSR_DEV_BASE(devid) | SBI_CFG0) + (cfgno<<2)),
+			      "i" (ASI_M_CTL));
+}
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* !(_SPARC_SBI_H) */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/sbus.h linux/include/asm-sparc/sbus.h
--- v2.1.78/linux/include/asm-sparc/sbus.h	Mon Mar 17 14:54:31 1997
+++ linux/include/asm-sparc/sbus.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: sbus.h,v 1.12 1997/02/04 07:29:21 davem Exp $
+/* $Id: sbus.h,v 1.13 1997/11/19 15:12:18 jj Exp $
  * sbus.h:  Defines for the Sun SBus.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -69,10 +69,12 @@
 	struct linux_sbus_device *devices;   /* Link to devices on this SBus */
 	struct iommu_struct *iommu;          /* IOMMU for this sbus if applicable */
 	int prom_node;                       /* PROM device tree node for this SBus */
-	char prom_name[64];                  /* Usually "sbus" or "sbi" */
+	char prom_name[16];                  /* Usually "sbus" or "sbi" */
 	int clock_freq;
 	struct linux_prom_ranges sbus_ranges[PROMREG_MAX];
 	int num_sbus_ranges;
+	int devid;
+	int board;
 };
 
 extern struct linux_sbus *SBus_chain;
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/siginfo.h linux/include/asm-sparc/siginfo.h
--- v2.1.78/linux/include/asm-sparc/siginfo.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/siginfo.h	Mon Jan 12 15:15:54 1998
@@ -0,0 +1,199 @@
+/* $Id: siginfo.h,v 1.1 1997/12/14 23:24:40 ecd Exp $
+ * siginfo.c:
+ */
+
+#ifndef _SPARC_SIGINFO_H
+#define _SPARC_SIGINFO_H
+
+#include <linux/types.h>
+
+typedef union sigval {
+	int sival_int;
+	void *sival_ptr;
+} sigval_t;
+
+#define SI_MAX_SIZE	128
+#define SI_PAD_SIZE	((SI_MAX_SIZE/sizeof(int)) - 3)
+
+typedef struct siginfo {
+	int si_signo;
+	int si_errno;
+	int si_code;
+
+	union {
+		int _pad[SI_PAD_SIZE];
+
+		/* kill() */
+		struct {
+			pid_t _pid;		/* sender's pid */
+			uid_t _uid;		/* sender's uid */
+		} _kill;
+
+		/* POSIX.1b timers */
+		struct {
+			unsigned int _timer1;
+			unsigned int _timer2;
+		} _timer;
+
+		/* POSIX.1b signals */
+		struct {
+			pid_t _pid;		/* sender's pid */
+			uid_t _uid;		/* sender's uid */
+			sigval_t _sigval;
+		} _rt;
+
+		/* SIGCHLD */
+		struct {
+			pid_t _pid;		/* which child */
+			int _status;		/* exit code */
+			clock_t _utime;
+			clock_t _stime;
+		} _sigchld;
+
+		/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+		struct {
+			void *_addr;	/* faulting insn/memory ref. */
+			int  _trapno;	/* TRAP # which caused the signal */
+		} _sigfault;
+
+		/* SIGPOLL */
+		struct {
+			int _band;	/* POLL_IN, POLL_OUT, POLL_MSG */
+			int _fd;
+		} _sigpoll;
+	} _sifields;
+} siginfo_t;
+
+/*
+ * How these fields are to be accessed.
+ */
+#define si_pid		_sifields._kill._pid
+#define si_uid		_sifields._kill._uid
+#define si_status	_sifields._sigchld._status
+#define si_utime	_sifields._sigchld._utime
+#define si_stime	_sifields._sigchld._stime
+#define si_value	_sifields._rt._sigval
+#define si_int		_sifields._rt._sigval.sival_int
+#define si_ptr		_sifields._rt._sigval.sival_ptr
+#define si_addr		_sifields._sigfault._addr
+#define si_trapno	_sifields._sigfault._trapno
+#define si_band		_sifields._sigpoll._band
+#define si_fd		_sifields._sigpoll._fd
+
+/*
+ * si_code values
+ * Digital reserves positive values for kernel-generated signals.
+ */
+#define SI_USER		0	/* sent by kill, sigsend, raise */
+#define SI_KERNEL	0x80	/* sent by the kernel from somewhere */
+#define SI_QUEUE	-1	/* sent by sigqueue */
+#define SI_TIMER	-2	/* sent by timer expiration */
+#define SI_MESGQ	-3	/* sent by real time mesq state change */
+#define SI_ASYNCIO	-4	/* sent by AIO completion */
+
+#define SI_FROMUSER(siptr)	((siptr)->si_code <= 0)
+#define SI_FROMKERNEL(siptr)	((siptr)->si_code > 0)
+
+/*
+ * SIGILL si_codes
+ */
+#define ILL_ILLOPC	1	/* illegal opcode */
+#define ILL_ILLOPN	2	/* illegal operand */
+#define ILL_ILLADR	3	/* illegal addressing mode */
+#define ILL_ILLTRP	4	/* illegal trap */
+#define ILL_PRVOPC	5	/* privileged opcode */
+#define ILL_PRVREG	6	/* privileged register */
+#define ILL_COPROC	7	/* coprocessor error */
+#define ILL_BADSTK	8	/* internal stack error */
+#define NSIGILL		8
+
+/*
+ * SIGFPE si_codes
+ */
+#define FPE_INTDIV	1	/* integer divide by zero */
+#define FPE_INTOVF	2	/* integer overflow */
+#define FPE_FLTDIV	3	/* floating point divide by zero */
+#define FPE_FLTOVF	4	/* floating point overflow */
+#define FPE_FLTUND	5	/* floating point underflow */
+#define FPE_FLTRES	6	/* floating point inexact result */
+#define FPE_FLTINV	7	/* floating point invalid operation */
+#define FPE_FLTSUB	8	/* subscript out of range */
+#define NSIGFPE		8
+
+/*
+ * SIGSEGV si_codes
+ */
+#define SEGV_MAPERR	1	/* address not mapped to object */
+#define SRGV_ACCERR	2	/* invalid permissions for mapped object */
+#define NSIGSEGV	2
+
+/*
+ * SIGBUS si_codes
+ */
+#define BUS_ADRALN	1	/* invalid address alignment */
+#define BUS_ADRERR	2	/* non-existant physical address */
+#define BUS_OBJERR	3	/* object specific hardware error */
+#define NSIGBUS		3
+
+/*
+ * SIGTRAP si_codes
+ */
+#define TRAP_BRKPT	1	/* process breakpoint */
+#define TRAP_TRACE	2	/* process trace trap */
+#define NSIGTRAP
+
+/*
+ * SIGCHLD si_codes
+ */
+#define CLD_EXITED	1	/* child has exited */
+#define CLD_KILLED	2	/* child was killed */
+#define CLD_DUMPED	3	/* child terminated abnormally */
+#define CLD_TRAPPED	4	/* traced child has trapped */
+#define CLD_STOPPED	5	/* child has stopped */
+#define CLD_CONTINUED	6	/* stopped child has continued */
+#define NSIGCHLD
+
+/*
+ * SIGPOLL si_codes
+ */
+#define POLL_IN		1	/* data input available */
+#define POLL_OUT	2	/* output buffers available */
+#define POLL_MSG	3	/* input message available */
+#define POLL_ERR	4	/* i/o error */
+#define POLL_PRI	5	/* high priority input available */
+#define POLL_HUP	6	/* device disconnected */
+#define NSIGPOLL	6
+
+/*
+ * sigevent definitions
+ * 
+ * It seems likely that SIGEV_THREAD will have to be handled from 
+ * userspace, libpthread transmuting it to SIGEV_SIGNAL, which the
+ * thread manager then catches and does the appropriate nonsense.
+ * However, everything is written out here so as to not get lost.
+ */
+#define SIGEV_SIGNAL	0	/* notify via signal */
+#define SIGEV_NONE	1	/* other notification: meaningless */
+#define SIGEV_THREAD	2	/* deliver via thread creation */
+
+#define SIGEV_MAX_SIZE	64
+#define SIGEV_PAD_SIZE	((SIGEV_MAX_SIZE/sizeof(int)) - 3)
+
+typedef struct sigevent {
+	sigval_t sigev_value;
+	int sigev_signo;
+	int sigev_notify;
+	union {
+		int _pad[SIGEV_PAD_SIZE];
+
+		struct {
+			void (*_function)(sigval_t);
+			void *_attribute;	/* really pthread_attr_t */
+		} _sigev_thread;
+	} _sigev_un;
+} sigevent_t;
+
+#define sigev_notify_function	_sigev_un._sigev_thread._function
+#define sigev_notify_attributes	_sigev_un._sigev_thread._attribute
+
+#endif /* !(_SPARC_SIGINFO_H) */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/signal.h linux/include/asm-sparc/signal.h
--- v2.1.78/linux/include/asm-sparc/signal.h	Mon Dec 30 02:00:00 1996
+++ linux/include/asm-sparc/signal.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: signal.h,v 1.30 1996/12/24 08:59:36 davem Exp $ */
+/* $Id: signal.h,v 1.31 1997/12/14 23:24:41 ecd Exp $ */
 #ifndef _ASMSPARC_SIGNAL_H
 #define _ASMSPARC_SIGNAL_H
 
@@ -7,15 +7,13 @@
 #ifdef __KERNEL__
 #ifndef __ASSEMBLY__
 #include <linux/personality.h>
+#include <linux/types.h>
 #endif
 #endif
 
 /* On the Sparc the signal handlers get passed a 'sub-signal' code
  * for certain signal types, which we document here.
  */
-#define _NSIG             32
-#define NSIG		_NSIG
-
 #define SIGHUP		 1
 #define SIGINT		 2
 #define SIGQUIT		 3
@@ -80,13 +78,37 @@
 #define SIGUSR1		30
 #define SIGUSR2		31
 
+/* Most things should be clean enough to redefine this at will, if care
+ * is taken to make libc match.
+ */
+
+#define __OLD_NSIG	32
+#define __NEW_NSIG	64
+#define _NSIG_BPW	32
+#define _NSIG_WORDS	(__NEW_NSIG / _NSIG_BPW)
+
+#define SIGRTMIN	32
+#define SIGRTMAX	(__NEW_NSIG - 1)
+
+#if defined(__KERNEL__) || defined(__WANT_POSIX1B_SIGNALS__)
+#define	_NSIG		__NEW_NSIG
+#define __new_sigset_t	sigset_t
+#define __new_sigaction	sigaction
+#define __old_sigset_t	old_sigset_t
+#define __old_sigaction	old_sigaction
+#else
+#define _NSIG		__OLD_NSIG
+#define __old_sigset_t	sigset_t
+#define __old_sigaction	sigaction
+#endif
+
 #ifndef __ASSEMBLY__
 
-typedef unsigned long sigset_t;
+typedef unsigned long __old_sigset_t;
 
-#ifdef __KERNEL__
-#include <asm/sigcontext.h>
-#endif
+typedef struct {
+	unsigned long	sig[_NSIG_WORDS];
+} __new_sigset_t;
 
 /* A SunOS sigstack */
 struct sigstack {
@@ -116,6 +138,8 @@
 #define SA_INTERRUPT	0x10
 #define SA_NOMASK	0x20
 #define SA_SHIRQ	0x40
+#define SA_NOCLDWAIT	0x100	/* not supported yet */
+#define SA_SIGINFO	0x200
 
 #define SIG_BLOCK          0x01	/* for blocking signals */
 #define SIG_UNBLOCK        0x02	/* for unblocking signals */
@@ -154,12 +178,30 @@
 #define SIG_IGN	((__sighandler_t)1)	/* ignore signal */
 #define SIG_ERR	((__sighandler_t)-1)	/* error return from signal */
 
-struct sigaction {
-	__sighandler_t  sa_handler;
-	sigset_t        sa_mask;
-	unsigned long   sa_flags;
-	void (*sa_restorer) (void);     /* not used by Linux/SPARC yet */
+struct __new_sigaction {
+	__sighandler_t	sa_handler;
+	unsigned long	sa_flags;
+	void		(*sa_restorer)(void);	/* Not used by Linux/SPARC */
+	__new_sigset_t	sa_mask;
+};
+
+struct k_sigaction {
+	struct __new_sigaction	sa;
+	void			*ka_restorer;
 };
+
+struct __old_sigaction {
+	__sighandler_t	sa_handler;
+	__old_sigset_t	sa_mask;
+	unsigned long	sa_flags;
+	void		(*sa_restorer) (void);	/* not used by Linux/SPARC */
+};
+
+typedef struct sigaltstack {
+	void		*ss_sp;
+	int		ss_flags;
+	__kernel_size_t	ss_size;
+} stack_t;
 
 #endif /* !(__ASSEMBLY__) */
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/socket.h linux/include/asm-sparc/socket.h
--- v2.1.78/linux/include/asm-sparc/socket.h	Thu Mar 27 14:40:09 1997
+++ linux/include/asm-sparc/socket.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: socket.h,v 1.9 1997/03/17 04:50:50 davem Exp $ */
+/* $Id: socket.h,v 1.11 1997/10/19 00:19:14 davem Exp $ */
 #ifndef _ASM_SOCKET_H
 #define _ASM_SOCKET_H
 
@@ -35,9 +35,11 @@
 #define SO_NO_CHECK	0x000b
 #define SO_PRIORITY	0x000c
 
+#define SO_BINDTODEVICE 0x000d
+
 /* 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
+#define SO_SECURITY_AUTHENTICATION		0x5001
+#define SO_SECURITY_ENCRYPTION_TRANSPORT	0x5002
+#define SO_SECURITY_ENCRYPTION_NETWORK		0x5004
 
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/string.h linux/include/asm-sparc/string.h
--- v2.1.78/linux/include/asm-sparc/string.h	Thu May 29 21:53:09 1997
+++ linux/include/asm-sparc/string.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: string.h,v 1.32 1997/05/20 07:58:59 jj Exp $
+/* $Id: string.h,v 1.33 1997/11/19 07:57:48 jj Exp $
  * string.h: External definitions for optimized assembly string
  *           routines for the Linux Kernel.
  *
@@ -17,7 +17,7 @@
 extern __kernel_size_t __memcpy(void *,const void *,__kernel_size_t);
 extern __kernel_size_t __memset(void *,int,__kernel_size_t);
 
-#ifndef EXPORT_SYMTAB
+#ifndef EXPORT_SYMTAB_STROPS
 
 /* First the mem*() things. */
 #define __HAVE_ARCH_BCOPY
@@ -192,7 +192,7 @@
  __constant_strncmp(__arg0, __arg1, __arg2) : \
  __strncmp(__arg0, __arg1, __arg2))
  
-#endif /* EXPORT_SYMTAB */
+#endif /* !EXPORT_SYMTAB_STROPS */
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/timer.h linux/include/asm-sparc/timer.h
--- v2.1.78/linux/include/asm-sparc/timer.h	Sat Nov  9 12:36:13 1996
+++ linux/include/asm-sparc/timer.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: timer.h,v 1.13 1996/08/29 09:48:59 davem Exp $
+/* $Id: timer.h,v 1.15 1997/12/18 14:21:43 jj Exp $
  * timer.h:  Definitions for the timer chips on the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -76,6 +76,20 @@
 };
 
 extern struct sun4m_timer_regs *sun4m_timers;
+
+#define SUN4D_PRM_CNT_L       0x80000000
+#define SUN4D_PRM_CNT_LVALUE  0x7FFFFC00
+
+struct sun4d_timer_regs {
+	volatile unsigned int l10_timer_limit;
+	volatile unsigned int l10_cur_countx;
+	volatile unsigned int l10_limit_noclear;
+	volatile unsigned int ctrl;
+	volatile unsigned int l10_cur_count;
+};
+
+extern struct sun4d_timer_regs *sun4d_timers;
+
 extern __volatile__ unsigned int *master_l10_counter;
 extern __volatile__ unsigned int *master_l10_limit;
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/types.h linux/include/asm-sparc/types.h
--- v2.1.78/linux/include/asm-sparc/types.h	Sat Nov  9 00:30:19 1996
+++ linux/include/asm-sparc/types.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: types.h,v 1.10 1996/08/29 09:49:04 davem Exp $ */
+/* $Id: types.h,v 1.11 1997/12/22 13:28:22 mj Exp $ */
 #ifndef _SPARC_TYPES_H
 #define _SPARC_TYPES_H
 
@@ -42,6 +42,8 @@
 
 typedef __signed__ long long s64;
 typedef unsigned long long u64;
+
+#define BITS_PER_LONG 32
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/uaccess.h linux/include/asm-sparc/uaccess.h
--- v2.1.78/linux/include/asm-sparc/uaccess.h	Mon Apr 14 16:28:21 1997
+++ linux/include/asm-sparc/uaccess.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: uaccess.h,v 1.13 1997/04/11 00:42:22 davem Exp $
+/* $Id: uaccess.h,v 1.14 1997/09/18 10:42:02 rth Exp $
  * uaccess.h: User space memore access functions.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -21,15 +21,18 @@
  *
  * "For historical reasons, these macros are grossly misnamed." -Linus
  */
-#define KERNEL_DS   0
-#define USER_DS     -1
+
+#define KERNEL_DS   ((mm_segment_t) { 0 })
+#define USER_DS     ((mm_segment_t) { -1 })
 
 #define VERIFY_READ	0
 #define VERIFY_WRITE	1
 
-#define get_fs() (current->tss.current_ds)
-#define get_ds() (KERNEL_DS)
-#define set_fs(val) ((current->tss.current_ds) = (val))
+#define get_ds()	(KERNEL_DS)
+#define get_fs()	(current->tss.current_ds)
+#define set_fs(val)	((current->tss.current_ds) = (val))
+
+#define segment_eq(a,b)	((a).seg == (b).seg)
 
 /* We have there a nice not-mapped page at page_offset - PAGE_SIZE, so that this test
  * can be fairly lightweight.
@@ -37,8 +40,8 @@
  * large size and address near to page_offset - a fault will break his intentions.
  */
 #define __user_ok(addr,size) ((addr) < stack_top)
-#define __kernel_ok (get_fs() == KERNEL_DS)
-#define __access_ok(addr,size) (__user_ok((addr) & get_fs(),(size)))
+#define __kernel_ok (segment_eq(get_fs(), KERNEL_DS))
+#define __access_ok(addr,size) (__user_ok((addr) & get_fs().seg,(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)
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/unaligned.h linux/include/asm-sparc/unaligned.h
--- v2.1.78/linux/include/asm-sparc/unaligned.h	Sat Nov  9 00:30:22 1996
+++ linux/include/asm-sparc/unaligned.h	Mon Jan 12 15:15:54 1998
@@ -3,7 +3,7 @@
 
 /* Sparc can't handle unaligned accesses. */
 
-#include <asm/string.h>
+#include <linux/string.h>
 
 
 /* Use memmove here, so gcc does not insert a __builtin_memcpy. */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc/unistd.h linux/include/asm-sparc/unistd.h
--- v2.1.78/linux/include/asm-sparc/unistd.h	Wed Sep 24 20:05:48 1997
+++ linux/include/asm-sparc/unistd.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.33 1997/05/21 10:21:55 jj Exp $ */
+/* $Id: unistd.h,v 1.36 1997/12/14 23:24:43 ecd Exp $ */
 #ifndef _SPARC_UNISTD_H
 #define _SPARC_UNISTD_H
 
@@ -83,8 +83,8 @@
 #define __NR_getpagesize         64 /* Common                                      */
 #define __NR_msync               65 /* Common in newer 1.3.x revs...               */
 /* #define __NR_ni_syscall       66    ENOSYS under SunOS                          */
-/* #define __NR_ni_syscall       67    ENOSYS under SunOS                          */
-/* #define __NR_ni_syscall       68    ENOSYS under SunOS                          */
+#define __NR_pread               67 /* Linux Specific                              */
+#define __NR_pwrite              68 /* Linux Specific                              */
 #define __NR_sbrk                69 /* SunOS Specific                              */
 #define __NR_sstk                70 /* SunOS Specific                              */
 #define __NR_mmap                71 /* Common                                      */
@@ -117,13 +117,13 @@
 #define __NR_connect             98 /* Common                                      */
 #define __NR_accept              99 /* Common                                      */
 #define __NR_getpriority        100 /* Common                                      */
-#define __NR_send               101 /* Common                                      */
-#define __NR_recv               102 /* Common                                      */
-/* #define __NR_ni_syscall      103    ENOSYS under SunOS                          */
-#define __NR_bind               104 /* Common                                      */
-#define __NR_setsockopt         105 /* Common                                      */
-#define __NR_listen             106 /* Common                                      */
-/* #define __NR_ni_syscall      107    ENOSYS under SunOS                          */
+#define __NR_rt_sigreturn	101 /* Linux Specific                              */
+#define __NR_rt_sigaction	102 /* Linux Specific                              */
+#define __NR_rt_sigprocmask	103 /* Linux Specific                              */
+#define __NR_rt_sigpending	104 /* Linux Specific                              */
+#define __NR_rt_sigtimedwait	105 /* Linux Specific                              */
+#define __NR_rt_sigqueueinfo	106 /* Linux Specific                              */
+#define __NR_rt_sigsuspend	107 /* Linux Specific                              */
 #define __NR_sigvec             108 /* SunOS Specific                              */
 #define __NR_sigblock           109 /* SunOS Specific                              */
 #define __NR_sigsetmask         110 /* SunOS Specific                              */
@@ -163,7 +163,7 @@
 #define __NR_getrlimit          144 /* Common                                      */
 #define __NR_setrlimit          145 /* Common                                      */
 #define __NR_killpg             146 /* SunOS Specific                              */
-/* #define __NR_ni_syscall      147    ENOSYS under SunOS                          */
+#define __NR_prctl		147 /* ENOSYS under SunOS                          */
 /* #define __NR_ni_syscall      148    ENOSYS under SunOS                          */
 /* #define __NR_ni_syscall      149    ENOSYS under SunOS                          */
 #define __NR_getsockname        150 /* Common                                      */
@@ -272,7 +272,6 @@
 #define __NR_fdatasync          253
 #define __NR_nfsservctl         254
 #define __NR_aplib              255
-#define __NR_prctl              256
 
 #define _syscall0(type,name) \
 type name(void) \
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/asm_offsets.h linux/include/asm-sparc64/asm_offsets.h
--- v2.1.78/linux/include/asm-sparc64/asm_offsets.h	Mon Jul  7 08:18:55 1997
+++ linux/include/asm-sparc64/asm_offsets.h	Mon Jan 12 15:15:54 1998
@@ -8,32 +8,32 @@
 #define ASIZ_task_counter	0x00000008
 #define AOFF_task_priority	0x00000010
 #define ASIZ_task_priority	0x00000008
-#define AOFF_task_signal	0x00000018
-#define ASIZ_task_signal	0x00000008
-#define AOFF_task_blocked	0x00000020
-#define ASIZ_task_blocked	0x00000008
-#define AOFF_task_flags	0x00000028
+#define AOFF_task_flags	0x00000018
 #define ASIZ_task_flags	0x00000008
-#define AOFF_task_errno	0x00000030
-#define ASIZ_task_errno	0x00000004
-#define AOFF_task_debugreg	0x00000038
+#define AOFF_task_addr_limit	0x00000020
+#define ASIZ_task_addr_limit	0x00000008
+#define AOFF_task_sigpending	0x00000028
+#define ASIZ_task_sigpending	0x00000004
+#define AOFF_task_debugreg	0x00000030
 #define ASIZ_task_debugreg	0x00000040
-#define AOFF_task_exec_domain	0x00000078
+#define AOFF_task_exec_domain	0x00000070
 #define ASIZ_task_exec_domain	0x00000008
-#define AOFF_task_binfmt	0x00000080
+#define AOFF_task_binfmt	0x00000078
 #define ASIZ_task_binfmt	0x00000008
-#define AOFF_task_next_task	0x00000088
+#define AOFF_task_next_task	0x00000080
 #define ASIZ_task_next_task	0x00000008
-#define AOFF_task_prev_task	0x00000090
+#define AOFF_task_prev_task	0x00000088
 #define ASIZ_task_prev_task	0x00000008
-#define AOFF_task_next_run	0x00000098
+#define AOFF_task_next_run	0x00000090
 #define ASIZ_task_next_run	0x00000008
-#define AOFF_task_prev_run	0x000000a0
+#define AOFF_task_prev_run	0x00000098
 #define ASIZ_task_prev_run	0x00000008
-#define AOFF_task_exit_code	0x000000a8
+#define AOFF_task_exit_code	0x000000a0
 #define ASIZ_task_exit_code	0x00000004
-#define AOFF_task_exit_signal	0x000000ac
+#define AOFF_task_exit_signal	0x000000a4
 #define ASIZ_task_exit_signal	0x00000004
+#define AOFF_task_pdeath_signal	0x000000a8
+#define ASIZ_task_pdeath_signal	0x00000004
 #define AOFF_task_personality	0x000000b0
 #define ASIZ_task_personality	0x00000008
 #define AOFF_task_pid	0x000000bc
@@ -69,100 +69,110 @@
 #define AOFF_task_wait_chldexit	0x00000198
 #define ASIZ_task_wait_chldexit	0x00000008
 #define AOFF_task_uid	0x000001a0
-#define ASIZ_task_uid	0x00000002
-#define AOFF_task_euid	0x000001a2
-#define ASIZ_task_euid	0x00000002
-#define AOFF_task_suid	0x000001a4
-#define ASIZ_task_suid	0x00000002
-#define AOFF_task_fsuid	0x000001a6
-#define ASIZ_task_fsuid	0x00000002
-#define AOFF_task_gid	0x000001a8
-#define ASIZ_task_gid	0x00000002
-#define AOFF_task_egid	0x000001aa
-#define ASIZ_task_egid	0x00000002
-#define AOFF_task_sgid	0x000001ac
-#define ASIZ_task_sgid	0x00000002
-#define AOFF_task_fsgid	0x000001ae
-#define ASIZ_task_fsgid	0x00000002
-#define AOFF_task_timeout	0x000001b0
+#define ASIZ_task_uid	0x00000004
+#define AOFF_task_euid	0x000001a4
+#define ASIZ_task_euid	0x00000004
+#define AOFF_task_suid	0x000001a8
+#define ASIZ_task_suid	0x00000004
+#define AOFF_task_fsuid	0x000001ac
+#define ASIZ_task_fsuid	0x00000004
+#define AOFF_task_gid	0x000001b0
+#define ASIZ_task_gid	0x00000004
+#define AOFF_task_egid	0x000001b4
+#define ASIZ_task_egid	0x00000004
+#define AOFF_task_sgid	0x000001b8
+#define ASIZ_task_sgid	0x00000004
+#define AOFF_task_fsgid	0x000001bc
+#define ASIZ_task_fsgid	0x00000004
+#define AOFF_task_timeout	0x000001c0
 #define ASIZ_task_timeout	0x00000008
-#define AOFF_task_policy	0x000001b8
+#define AOFF_task_policy	0x000001c8
 #define ASIZ_task_policy	0x00000008
-#define AOFF_task_rt_priority	0x000001c0
+#define AOFF_task_rt_priority	0x000001d0
 #define ASIZ_task_rt_priority	0x00000008
-#define AOFF_task_it_real_value	0x000001c8
+#define AOFF_task_it_real_value	0x000001d8
 #define ASIZ_task_it_real_value	0x00000008
-#define AOFF_task_it_prof_value	0x000001d0
+#define AOFF_task_it_prof_value	0x000001e0
 #define ASIZ_task_it_prof_value	0x00000008
-#define AOFF_task_it_virt_value	0x000001d8
+#define AOFF_task_it_virt_value	0x000001e8
 #define ASIZ_task_it_virt_value	0x00000008
-#define AOFF_task_it_real_incr	0x000001e0
+#define AOFF_task_it_real_incr	0x000001f0
 #define ASIZ_task_it_real_incr	0x00000008
-#define AOFF_task_it_prof_incr	0x000001e8
+#define AOFF_task_it_prof_incr	0x000001f8
 #define ASIZ_task_it_prof_incr	0x00000008
-#define AOFF_task_it_virt_incr	0x000001f0
+#define AOFF_task_it_virt_incr	0x00000200
 #define ASIZ_task_it_virt_incr	0x00000008
-#define AOFF_task_real_timer	0x000001f8
+#define AOFF_task_real_timer	0x00000208
 #define ASIZ_task_real_timer	0x00000028
-#define AOFF_task_times	0x00000220
+#define AOFF_task_times	0x00000230
 #define ASIZ_task_times	0x00000020
-#define AOFF_task_start_time	0x00000240
+#define AOFF_task_start_time	0x00000250
 #define ASIZ_task_start_time	0x00000008
-#define AOFF_task_min_flt	0x00000248
+#define AOFF_task_min_flt	0x00000258
 #define ASIZ_task_min_flt	0x00000008
-#define AOFF_task_maj_flt	0x00000250
+#define AOFF_task_maj_flt	0x00000260
 #define ASIZ_task_maj_flt	0x00000008
-#define AOFF_task_nswap	0x00000258
+#define AOFF_task_nswap	0x00000268
 #define ASIZ_task_nswap	0x00000008
-#define AOFF_task_cmin_flt	0x00000260
+#define AOFF_task_cmin_flt	0x00000270
 #define ASIZ_task_cmin_flt	0x00000008
-#define AOFF_task_cmaj_flt	0x00000268
+#define AOFF_task_cmaj_flt	0x00000278
 #define ASIZ_task_cmaj_flt	0x00000008
-#define AOFF_task_cnswap	0x00000270
+#define AOFF_task_cnswap	0x00000280
 #define ASIZ_task_cnswap	0x00000008
-#define AOFF_task_swap_address	0x00000280
+#define AOFF_task_swap_address	0x00000290
 #define ASIZ_task_swap_address	0x00000008
-#define AOFF_task_old_maj_flt	0x00000288
+#define AOFF_task_old_maj_flt	0x00000298
 #define ASIZ_task_old_maj_flt	0x00000008
-#define AOFF_task_dec_flt	0x00000290
+#define AOFF_task_dec_flt	0x000002a0
 #define ASIZ_task_dec_flt	0x00000008
-#define AOFF_task_swap_cnt	0x00000298
+#define AOFF_task_swap_cnt	0x000002a8
 #define ASIZ_task_swap_cnt	0x00000008
-#define AOFF_task_rlim	0x000002a0
+#define AOFF_task_rlim	0x000002b0
 #define ASIZ_task_rlim	0x000000a0
-#define AOFF_task_used_math	0x00000340
+#define AOFF_task_used_math	0x00000350
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_comm	0x00000342
+#define AOFF_task_io_usage	0x00000358
+#define ASIZ_task_io_usage	0x00000008
+#define AOFF_task_comm	0x00000360
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x00000354
+#define AOFF_task_link_count	0x00000370
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x00000358
+#define AOFF_task_tty	0x00000378
 #define ASIZ_task_tty	0x00000008
-#define AOFF_task_semundo	0x00000360
+#define AOFF_task_semundo	0x00000380
 #define ASIZ_task_semundo	0x00000008
-#define AOFF_task_semsleeping	0x00000368
+#define AOFF_task_semsleeping	0x00000388
 #define ASIZ_task_semsleeping	0x00000008
-#define AOFF_task_ldt	0x00000370
+#define AOFF_task_ldt	0x00000390
 #define ASIZ_task_ldt	0x00000008
-#define AOFF_task_tss	0x00000380
-#define ASIZ_task_tss	0x000004c0
-#define AOFF_task_fs	0x00000840
+#define AOFF_task_tss	0x000003a0
+#define ASIZ_task_tss	0x00000490
+#define AOFF_task_fs	0x00000830
 #define ASIZ_task_fs	0x00000008
-#define AOFF_task_files	0x00000848
+#define AOFF_task_files	0x00000838
 #define ASIZ_task_files	0x00000008
-#define AOFF_task_mm	0x00000850
+#define AOFF_task_mm	0x00000840
 #define ASIZ_task_mm	0x00000008
-#define AOFF_task_sig	0x00000858
+#define AOFF_task_sig	0x00000848
 #define ASIZ_task_sig	0x00000008
-#define AOFF_task_has_cpu	0x00000860
+#define AOFF_task_signal	0x00000850
+#define ASIZ_task_signal	0x00000008
+#define AOFF_task_blocked	0x00000858
+#define ASIZ_task_blocked	0x00000008
+#define AOFF_task_sigqueue	0x00000860
+#define ASIZ_task_sigqueue	0x00000008
+#define AOFF_task_sigqueue_tail	0x00000868
+#define ASIZ_task_sigqueue_tail	0x00000008
+#define AOFF_task_has_cpu	0x00000870
 #define ASIZ_task_has_cpu	0x00000004
-#define AOFF_task_processor	0x00000864
+#define AOFF_task_processor	0x00000874
 #define ASIZ_task_processor	0x00000004
-#define AOFF_task_last_processor	0x00000868
+#define AOFF_task_last_processor	0x00000878
 #define ASIZ_task_last_processor	0x00000004
-#define AOFF_task_lock_depth	0x0000086c
+#define AOFF_task_lock_depth	0x0000087c
 #define ASIZ_task_lock_depth	0x00000004
-#define AOFF_task_sigmask_lock	0x00000870
+#define AOFF_task_sigmask_lock	0x00000880
 #define ASIZ_task_sigmask_lock	0x00000000
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000008
@@ -213,34 +223,34 @@
 #define AOFF_thread_ksp	0x00000000
 #define ASIZ_thread_ksp	0x00000008
 #define AOFF_thread_kpc	0x00000008
-#define ASIZ_thread_kpc	0x00000008
-#define AOFF_thread_wstate	0x00000010
-#define ASIZ_thread_wstate	0x00000008
-#define AOFF_thread_cwp	0x00000018
-#define ASIZ_thread_cwp	0x00000004
-#define AOFF_thread_ctx	0x0000001c
-#define ASIZ_thread_ctx	0x00000004
-#define AOFF_thread_flags	0x00000020
-#define ASIZ_thread_flags	0x00000004
-#define AOFF_thread_new_signal	0x00000024
-#define ASIZ_thread_new_signal	0x00000004
-#define AOFF_thread_current_ds	0x00000028
+#define ASIZ_thread_kpc	0x00000004
+#define AOFF_thread_wstate	0x0000000c
+#define ASIZ_thread_wstate	0x00000002
+#define AOFF_thread_cwp	0x0000000e
+#define ASIZ_thread_cwp	0x00000002
+#define AOFF_thread_flags	0x00000010
+#define ASIZ_thread_flags	0x00000002
+#define AOFF_thread_ctx	0x00000012
+#define ASIZ_thread_ctx	0x00000002
+#define AOFF_thread_w_saved	0x00000014
+#define ASIZ_thread_w_saved	0x00000002
+#define AOFF_thread_new_signal	0x00000016
+#define ASIZ_thread_new_signal	0x00000002
+#define AOFF_thread_current_ds	0x00000018
 #define ASIZ_thread_current_ds	0x00000008
-#define AOFF_thread_w_saved	0x00000030
-#define ASIZ_thread_w_saved	0x00000008
-#define AOFF_thread_kregs	0x00000038
+#define AOFF_thread_kregs	0x00000020
 #define ASIZ_thread_kregs	0x00000008
-#define AOFF_thread_reg_window	0x00000040
+#define AOFF_thread_utraps	0x00000028
+#define ASIZ_thread_utraps	0x00000008
+#define AOFF_thread_reg_window	0x00000030
 #define ASIZ_thread_reg_window	0x00000400
-#define AOFF_thread_rwbuf_stkptrs	0x00000440
+#define AOFF_thread_rwbuf_stkptrs	0x00000430
 #define ASIZ_thread_rwbuf_stkptrs	0x00000040
-#define AOFF_thread_sig_address	0x00000480
+#define AOFF_thread_sig_address	0x00000470
 #define ASIZ_thread_sig_address	0x00000008
-#define AOFF_thread_sig_desc	0x00000488
+#define AOFF_thread_sig_desc	0x00000478
 #define ASIZ_thread_sig_desc	0x00000008
-#define AOFF_thread_sstk_info	0x00000490
+#define AOFF_thread_sstk_info	0x00000480
 #define ASIZ_thread_sstk_info	0x00000010
-#define AOFF_thread_core_exec	0x000004a0
-#define ASIZ_thread_core_exec	0x00000020
 
 #endif /* __ASM_OFFSETS_H__ */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/bitops.h linux/include/asm-sparc64/bitops.h
--- v2.1.78/linux/include/asm-sparc64/bitops.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/bitops.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: bitops.h,v 1.22 1997/08/07 02:54:04 davem Exp $
+/* $Id: bitops.h,v 1.23 1997/12/18 02:44:12 ecd Exp $
  * bitops.h: Bit string operations on the V9.
  *
  * Copyright 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -7,6 +7,7 @@
 #ifndef _SPARC64_BITOPS_H
 #define _SPARC64_BITOPS_H
 
+#include <asm/byteorder.h>
 #include <asm/asi.h>         /* For the little endian spaces. */
 
 /* These can all be exported to userland, because the atomic
@@ -286,15 +287,6 @@
 
 #define find_first_zero_le_bit(addr, size) \
         find_next_zero_le_bit((addr), (size), 0)
-
-extern __inline__ unsigned long __swab64p(unsigned long *addr)
-{
-	unsigned long ret;
-	__asm__ __volatile__ ("ldxa [%1] %2, %0"
-			      : "=r" (ret)
-			      : "r" (addr), "i" (ASI_PL));
-	return ret;
-}
 
 extern __inline__ unsigned long find_next_zero_le_bit(void *addr, unsigned long size, unsigned long offset)
 {
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/byteorder.h linux/include/asm-sparc64/byteorder.h
--- v2.1.78/linux/include/asm-sparc64/byteorder.h	Wed Dec 10 11:12:45 1997
+++ linux/include/asm-sparc64/byteorder.h	Mon Jan 12 15:15:54 1998
@@ -1,3 +1,4 @@
+/* $Id: byteorder.h,v 1.8 1997/12/18 02:44:14 ecd Exp $ */
 #ifndef _SPARC64_BYTEORDER_H
 #define _SPARC64_BYTEORDER_H
 
@@ -26,7 +27,8 @@
 	return ret;
 }
 
-static __inline__ __u64 ___arch__swab64p(__u64 *addr) {
+static __inline__ __u64 ___arch__swab64p(__u64 *addr)
+{
 	__u64 ret;
 
 	__asm__ __volatile__ ("ldxa [%1] %2, %0"
@@ -40,6 +42,8 @@
 #define __arch__swab64p(x) ___arch__swab64p(x)
 
 #define __BYTEORDER_HAS_U64__
+
+#endif /* __GNUC__ */
 
 #include <linux/byteorder/big_endian.h>
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/delay.h linux/include/asm-sparc64/delay.h
--- v2.1.78/linux/include/asm-sparc64/delay.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/delay.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: delay.h,v 1.6 1997/07/29 21:11:22 davem Exp $
+/* $Id: delay.h,v 1.7 1997/11/07 18:24:31 mj Exp $
  * delay.h: Linux delay routines on the V9.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu).
@@ -46,10 +46,4 @@
 
 #define udelay(usecs) __udelay((usecs),__udelay_val)
 
-extern __inline__ unsigned long muldiv(unsigned long a, unsigned long b, unsigned long c)
-{
-	return (a*b)/c;
-}
-
 #endif /* defined(__SPARC64_DELAY_H) */
-
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/ebus.h linux/include/asm-sparc64/ebus.h
--- v2.1.78/linux/include/asm-sparc64/ebus.h	Sun Sep  7 13:10:43 1997
+++ linux/include/asm-sparc64/ebus.h	Mon Jan 12 15:15:54 1998
@@ -1,4 +1,4 @@
-/* $Id: ebus.h,v 1.3 1997/09/05 23:00:14 ecd Exp $
+/* $Id: ebus.h,v 1.4 1998/01/10 18:26:08 ecd Exp $
  * ebus.h: PCI to Ebus pseudo driver software state.
  *
  * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
@@ -12,6 +12,7 @@
 struct linux_ebus_child {
 	struct linux_ebus_child		*next;
 	struct linux_ebus_device	*parent;
+	struct linux_ebus		*bus;
 	int				 prom_node;
 	char				 prom_name[64];
 	unsigned long			 base_address[PROMREG_MAX];
@@ -23,7 +24,7 @@
 struct linux_ebus_device {
 	struct linux_ebus_device	*next;
 	struct linux_ebus_child		*children;
-	struct linux_ebus		*parent;
+	struct linux_ebus		*bus;
 	int				 prom_node;
 	char				 prom_name[64];
 	unsigned long			 base_address[PROMREG_MAX];
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/elf.h linux/include/asm-sparc64/elf.h
--- v2.1.78/linux/include/asm-sparc64/elf.h	Wed Sep 24 20:05:48 1997
+++ linux/include/asm-sparc64/elf.h	Mon Jan 12 14:52:46 1998
@@ -1,4 +1,4 @@
-/* $Id: elf.h,v 1.8 1997/08/21 18:09:07 richard Exp $ */
+/* $Id: elf.h,v 1.13 1997/10/03 18:44:14 davem Exp $ */
 #ifndef __ASM_SPARC64_ELF_H
 #define __ASM_SPARC64_ELF_H
 
@@ -45,7 +45,31 @@
    that it will "exec", and that there is sufficient room for the brk.  */
 
 #ifndef ELF_ET_DYN_BASE
-#define ELF_ET_DYN_BASE         (2 * TASK_SIZE / 3)
+#define ELF_ET_DYN_BASE         0x50000000000
+#endif
+
+
+/* This yields a mask that user programs can use to figure out what
+   instruction set this cpu supports.  */
+
+/* On Ultra, we support all of the v8 capabilities. */
+#define ELF_HWCAP	(HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | \
+			 HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV)
+
+/* This yields a string that ld.so will use to load implementation
+   specific libraries for optimization.  This is more specific in
+   intent than poking at uname or /proc/cpuinfo.  */
+
+#define ELF_PLATFORM	(NULL)
+
+#ifdef __KERNEL__
+#define SET_PERSONALITY(ibcs2)					\
+do {								\
+	if (ibcs2)						\
+		current->personality = PER_SVR4;		\
+	else if (current->personality != PER_LINUX32)		\
+		current->personality = PER_LINUX;		\
+} while (0)
 #endif
 
 #endif /* !(__ASM_SPARC64_ELF_H) */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/floppy.h linux/include/asm-sparc64/floppy.h
--- v2.1.78/linux/include/asm-sparc64/floppy.h	Sun Sep  7 13:10:43 1997
+++ linux/include/asm-sparc64/floppy.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: floppy.h,v 1.4 1997/09/05 23:00:15 ecd Exp $
+/* $Id: floppy.h,v 1.7 1997/09/07 03:34:08 davem Exp $
  * asm-sparc64/floppy.h: Sparc specific parts of the Floppy driver.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -42,6 +42,7 @@
 /* You'll only ever find one controller on a SparcStation anyways. */
 static struct sun_flpy_controller *sun_fdc = NULL;
 volatile unsigned char *fdc_status;
+static struct linux_sbus_device *floppy_sdev = NULL;
 
 struct sun_floppy_ops {
 	unsigned char	(*fd_inb) (unsigned long port);
@@ -215,10 +216,25 @@
 	int error;
 
 	if(!once) {
+		struct devid_cookie dcookie;
+
 		once = 1;
-		error = request_fast_irq(FLOPPY_IRQ, floppy_hardint, SA_INTERRUPT, "floppy");
+
+		dcookie.real_dev_id = NULL;
+		dcookie.imap = dcookie.iclr = 0;
+		dcookie.pil = -1;
+		dcookie.bus_cookie = floppy_sdev->my_bus;
+
+		error = request_fast_irq(FLOPPY_IRQ, floppy_hardint,
+					 (SA_INTERRUPT | SA_SBUS | SA_DCOOKIE),
+					 "floppy", &dcookie);
+
+		if(error == 0)
+			FLOPPY_IRQ = dcookie.ret_ino;
+
 		return ((error == 0) ? 0 : -1);
-	} else return 0;
+	}
+	return 0;
 }
 
 static void sun_fd_enable_irq(void)
@@ -364,14 +380,16 @@
 	char state[128];
 	int fd_node, num_regs;
 	struct linux_sbus *bus;
-	struct linux_sbus_device *sdev;
+	struct linux_sbus_device *sdev = NULL;
 
-	FLOPPY_IRQ = 11;
 	for_all_sbusdev (sdev, bus) {
 		if (!strcmp(sdev->prom_name, "SUNW,fdtwo")) 
 			break;
 	}
-	if (!bus) {
+	if(sdev) {
+		floppy_sdev = sdev;
+		FLOPPY_IRQ = sdev->irqs[0].pri;
+	} else {
 #ifdef CONFIG_PCI
 		struct linux_ebus *ebus;
 		struct linux_ebus_device *edev;
@@ -424,7 +442,8 @@
 	}
 	fd_node = sdev->prom_node;
 	prom_getproperty(fd_node, "status", state, sizeof(state));
-	if(!strcmp(state, "disabled")) return -1;
+	if(!strncmp(state, "disabled", 8))
+		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_sbus_ranges(sdev->my_bus, fd_regs, num_regs, sdev);
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/fpumacro.h linux/include/asm-sparc64/fpumacro.h
--- v2.1.78/linux/include/asm-sparc64/fpumacro.h	Mon Jul  7 08:18:55 1997
+++ linux/include/asm-sparc64/fpumacro.h	Mon Jan 12 15:15:58 1998
@@ -7,6 +7,16 @@
 #ifndef _SPARC64_FPUMACRO_H
 #define _SPARC64_FPUMACRO_H
 
+#include <asm/asi.h>
+
+struct fpustate {
+	u32	regs[64];
+	u64	fsr;
+	u64	gsr;
+};
+
+#define FPUSTATE (struct fpustate *)(((unsigned long)current) + (((PAGE_SIZE<<1)-((64*4)+(2*8))) & ~(64 - 1)))
+
 extern __inline__ unsigned long fprs_read(void)
 {
 	unsigned long retval;
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/io.h linux/include/asm-sparc64/io.h
--- v2.1.78/linux/include/asm-sparc64/io.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-sparc64/io.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: io.h,v 1.12 1997/08/19 03:11:52 davem Exp $ */
+/* $Id: io.h,v 1.14 1997/11/01 10:23:58 ecd Exp $ */
 #ifndef __SPARC64_IO_H
 #define __SPARC64_IO_H
 
@@ -91,21 +91,56 @@
 extern void insl(unsigned long addr, void *dst, unsigned long count);
 
 /* Memory functions, same as I/O accesses on Ultra. */
-#define readb(addr)		inb(addr)
-#define readw(addr)		inw(addr)
-#define readl(addr)		inl(addr)
-#define writeb(b, addr)		outb((b), (addr))
-#define writew(w, addr)		outw((w), (addr))
-#define writel(l, addr)		outl((l), (addr))
+#define readb(addr)		inb((unsigned long)addr)
+#define readw(addr)		inw((unsigned long)addr)
+#define readl(addr)		inl((unsigned long)addr)
+#define writeb(b, addr)		outb((b), (unsigned long)(addr))
+#define writew(w, addr)		outw((w), (unsigned long)(addr))
+#define writel(l, addr)		outl((l), (unsigned long)(addr))
 
 /* Memcpy to/from I/O space is just a regular memory operation on Ultra as well. */
 
+/*
+ * FIXME: Write faster routines using ASL_*L for this.
+ */
+static inline void *
+memset_io(void *dst, int c, __kernel_size_t n)
+{
+	char *d = dst;
+
+	while (n--)
+		*d++ = c;
+
+	return dst;
+}
+
+static inline void *
+memcpy_fromio(void *dst, const void *src, __kernel_size_t n)
+{
+	const char *s = src;
+	char *d = dst;
+
+	while (n--)
+		*d++ = *s++;
+
+	return dst;
+}
+
+static inline void *
+memcpy_toio(void *dst, const void *src, __kernel_size_t n)
+{
+	const char *s = src;
+	char *d = dst;
+
+	while (n--)
+		*d++ = *s++;
+
+	return dst;
+}
+
 #if 0 /* XXX Not exactly, we need to use ASI_*L from/to the I/O end,
        * XXX so these are disabled until we code that stuff.
        */
-#define memset_io(a,b,c)		memset(((char *)(a)),(b),(c))
-#define memcpy_fromio(a,b,c)		memcpy((a),((char *)(b)),(c))
-#define memcpy_toio(a,b,c)		memcpy(((char *)(a)),(b),(c))
 #define eth_io_copy_and_sum(a,b,c,d)	eth_copy_and_sum((a),((char *)(b)),(c),(d))
 #endif
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/ioctls.h linux/include/asm-sparc64/ioctls.h
--- v2.1.78/linux/include/asm-sparc64/ioctls.h	Thu Dec  4 14:53:57 1997
+++ linux/include/asm-sparc64/ioctls.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: ioctls.h,v 1.5 1997/08/12 04:13:13 ecd Exp $ */
+/* $Id: ioctls.h,v 1.6 1997/12/16 19:20:58 davem Exp $ */
 #ifndef _ASM_SPARC64_IOCTLS_H
 #define _ASM_SPARC64_IOCTLS_H
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/irq.h linux/include/asm-sparc64/irq.h
--- v2.1.78/linux/include/asm-sparc64/irq.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/irq.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: irq.h,v 1.6 1997/08/07 08:06:40 davem Exp $
+/* $Id: irq.h,v 1.7 1997/09/07 02:56:44 davem Exp $
  * irq.h: IRQ registers on the 64-bit Sparc.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -49,7 +49,8 @@
 
 extern int request_fast_irq(unsigned int irq,
 			    void (*handler)(int, void *, struct pt_regs *),
-			    unsigned long flags, __const__ char *devname);
+			    unsigned long flags, __const__ char *devname,
+			    void *dev_id);
 
 extern __inline__ void set_softint(unsigned long bits)
 {
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/kdebug.h linux/include/asm-sparc64/kdebug.h
--- v2.1.78/linux/include/asm-sparc64/kdebug.h	Mon Apr 14 16:28:22 1997
+++ linux/include/asm-sparc64/kdebug.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: kdebug.h,v 1.2 1997/04/04 00:50:21 davem Exp $
+/* $Id: kdebug.h,v 1.3 1997/12/14 23:24:47 ecd Exp $
  * kdebug.h:  Defines and definitions for debugging the Linux kernel
  *            under various kernel debuggers.
  *
@@ -73,9 +73,5 @@
 #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.78/linux/include/asm-sparc64/keyboard.h linux/include/asm-sparc64/keyboard.h
--- v2.1.78/linux/include/asm-sparc64/keyboard.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-sparc64/keyboard.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: keyboard.h,v 1.1 1997/09/04 05:50:39 ecd Exp $
+/* $Id: keyboard.h,v 1.2 1997/09/07 15:40:49 ecd Exp $
  * linux/include/asm-sparc64/keyboard.h
  *
  * Created Aug 29 1997 by Eddie C. Dost (ecd@skynet.be)
@@ -26,15 +26,23 @@
 extern void pcikbd_init_hw(void);
 extern unsigned char pcikbd_sysrq_xlate[128];
 
-#define kbd_setkeycode		pcikbd_setkeycode
-#define kbd_getkeycode		pcikbd_getkeycode
-#define kbd_pretranslate	pcikbd_pretranslate
-#define kbd_translate		pcikbd_translate
-#define kbd_unexpected_up	pcikbd_unexpected_up
-#define kbd_leds		pcikbd_leds
-#define kbd_init_hw		pcikbd_init_hw
-#define kbd_sysrq_xlate		pcikbd_sysrq_xlate
-#define kbd_init		pcikbd_init
+#define kbd_setkeycode			pcikbd_setkeycode
+#define kbd_getkeycode			pcikbd_getkeycode
+#define kbd_pretranslate		pcikbd_pretranslate
+#define kbd_translate			pcikbd_translate
+#define kbd_unexpected_up		pcikbd_unexpected_up
+#define kbd_leds			pcikbd_leds
+#define kbd_init_hw			pcikbd_init_hw
+#define kbd_sysrq_xlate			pcikbd_sysrq_xlate
+#define kbd_init			pcikbd_init
+
+#define compute_shiftstate		pci_compute_shiftstate
+#define keyboard_wait_for_keypress	pci_wait_for_keypress
+#define getkeycode			pci_getkeycode
+#define setkeycode			pci_setkeycode
+#define getledstate			pci_getledstate
+#define setledstate			pci_setledstate
+#define register_leds			pci_register_leds
 
 #define SYSRQ_KEY 0x54
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/md.h linux/include/asm-sparc64/md.h
--- v2.1.78/linux/include/asm-sparc64/md.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/md.h	Mon Jan 12 15:15:58 1998
@@ -0,0 +1,91 @@
+/* $Id: md.h,v 1.2 1997/12/27 16:28:38 jj Exp $
+ * md.h: High speed xor_block operation for RAID4/5 
+ *            utilizing the UltraSparc Visual Instruction Set.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+ 
+#ifndef __ASM_MD_H
+#define __ASM_MD_H
+
+#include <asm/head.h>
+#include <asm/asi.h>
+
+#define HAVE_ARCH_XORBLOCK
+
+#define MD_XORBLOCK_ALIGNMENT	64
+
+/*	void __xor_block (char *dest, char *src, long len)
+ *	{
+ *		while (len--) *dest++ ^= *src++;
+ * 	}
+ *
+ *	Requirements:
+ *	!(((long)dest | (long)src) & (MD_XORBLOCK_ALIGNMENT - 1)) &&
+ *	!(len & 127) && len >= 256
+ */
+
+static inline void __xor_block (char *dest, char *src, long len)
+{
+	__asm__ __volatile__ ("
+	wr	%%g0, %3, %%fprs
+	wr	%%g0, %4, %%asi
+	membar	#LoadStore|#StoreLoad|#StoreStore
+	sub	%2, 128, %2
+	ldda	[%0] %4, %%f0
+	ldda	[%1] %4, %%f16
+1:	ldda	[%0 + 64] %%asi, %%f32
+	fxor	%%f0, %%f16, %%f16
+	fxor	%%f2, %%f18, %%f18
+	fxor	%%f4, %%f20, %%f20
+	fxor	%%f6, %%f22, %%f22
+	fxor	%%f8, %%f24, %%f24
+	fxor	%%f10, %%f26, %%f26
+	fxor	%%f12, %%f28, %%f28
+	fxor	%%f14, %%f30, %%f30
+	stda	%%f16, [%0] %4
+	ldda	[%1 + 64] %%asi, %%f48
+	ldda	[%0 + 128] %%asi, %%f0
+	fxor	%%f32, %%f48, %%f48
+	fxor	%%f34, %%f50, %%f50
+	add	%0, 128, %0
+	fxor	%%f36, %%f52, %%f52
+	add	%1, 128, %1
+	fxor	%%f38, %%f54, %%f54
+	subcc	%2, 128, %2
+	fxor	%%f40, %%f56, %%f56
+	fxor	%%f42, %%f58, %%f58
+	fxor	%%f44, %%f60, %%f60
+	fxor	%%f46, %%f62, %%f62
+	stda	%%f48, [%0 - 64] %%asi
+	bne,pt	%%xcc, 1b
+	 ldda	[%1] %4, %%f16
+	ldda	[%0 + 64] %%asi, %%f32
+	fxor	%%f0, %%f16, %%f16
+	fxor	%%f2, %%f18, %%f18
+	fxor	%%f4, %%f20, %%f20
+	fxor	%%f6, %%f22, %%f22
+	fxor	%%f8, %%f24, %%f24
+	fxor	%%f10, %%f26, %%f26
+	fxor	%%f12, %%f28, %%f28
+	fxor	%%f14, %%f30, %%f30
+	stda	%%f16, [%0] %4
+	ldda	[%1 + 64] %%asi, %%f48
+	membar	#Sync
+	fxor	%%f32, %%f48, %%f48
+	fxor	%%f34, %%f50, %%f50
+	fxor	%%f36, %%f52, %%f52
+	fxor	%%f38, %%f54, %%f54
+	fxor	%%f40, %%f56, %%f56
+	fxor	%%f42, %%f58, %%f58
+	fxor	%%f44, %%f60, %%f60
+	fxor	%%f46, %%f62, %%f62
+	stda	%%f48, [%0 + 64] %%asi
+	membar	#Sync|#StoreStore|#StoreLoad
+	wr	%%g0, 0, %%fprs
+	" : :
+	"r" (dest), "r" (src), "r" (len), "i" (FPRS_FEF), "i" (ASI_BLK_P) :
+	"cc", "memory");
+}
+
+#endif /* __ASM_MD_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/mmu_context.h linux/include/asm-sparc64/mmu_context.h
--- v2.1.78/linux/include/asm-sparc64/mmu_context.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/mmu_context.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: mmu_context.h,v 1.19 1997/08/07 02:54:08 davem Exp $ */
+/* $Id: mmu_context.h,v 1.20 1997/09/18 10:42:08 rth Exp $ */
 #ifndef __SPARC64_MMU_CONTEXT_H
 #define __SPARC64_MMU_CONTEXT_H
 
@@ -54,7 +54,8 @@
 			get_new_mmu_context(mm, &tlb_context_cache);
 
 		/* Don't worry, set_fs() will restore it... */
-		tsk->tss.ctx = (tsk->tss.current_ds ?
+		/* Sigh, damned include loops... just poke seg directly.  */
+		tsk->tss.ctx = (tsk->tss.current_ds.seg ?
 				(mm->context & 0x1fff) : 0);
 	} else
 		tsk->tss.ctx = 0;
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/namei.h linux/include/asm-sparc64/namei.h
--- v2.1.78/linux/include/asm-sparc64/namei.h	Sun Sep  7 13:10:43 1997
+++ linux/include/asm-sparc64/namei.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: namei.h,v 1.8 1997/09/06 02:25:27 davem Exp $
+/* $Id: namei.h,v 1.10 1997/09/24 16:20:41 jj Exp $
  * linux/include/asm-sparc64/namei.h
  *
  * Routines to handle famous /usr/gnemul/s*.
@@ -10,40 +10,55 @@
 
 #define SPARC_BSD_EMUL "usr/gnemul/sunos/"
 #define SPARC_SOL_EMUL "usr/gnemul/solaris/"
+#define SPARC_LIN_EMUL "usr/gnemul/linux32/"
 
 static inline struct dentry *
 __sparc64_lookup_dentry(const char *name, int follow_link)
 {
 	struct dentry *base;
+	char *emul;
 
 	switch (current->personality) {
 	case PER_BSD:
+		emul = SPARC_BSD_EMUL; break;
 	case PER_SVR4:
-		break;
+		emul = SPARC_SOL_EMUL; break;
+	case PER_LINUX32:
+		emul = SPARC_LIN_EMUL; break;
 	default:
-		return ERR_PTR(-ENOENT);
+		return NULL;
 	}
 
-	base = lookup_dentry ((current->personality == PER_BSD) ?
-			SPARC_BSD_EMUL : SPARC_SOL_EMUL,
-			dget (current->fs->root), 1);
+	base = lookup_dentry (emul, dget (current->fs->root), 1);
 			
-	if (IS_ERR (base)) return base;
+	if (IS_ERR (base)) return NULL;
 	
 	base = lookup_dentry (name, base, follow_link);
 	
-	if (IS_ERR (base)) return base;
+	if (IS_ERR (base)) return NULL;
 	
 	if (!base->d_inode) {
-		dput(base);
-		return ERR_PTR(-ENOENT);
+		struct dentry *fromroot;
+		
+		fromroot = lookup_dentry (name, dget (current->fs->root), follow_link);
+		
+		if (IS_ERR (fromroot)) return base;
+		
+		if (fromroot->d_inode) {
+			dput(base);
+			return fromroot;
+		}
+		
+		dput(fromroot);
 	}
 	
 	return base;
 }
 
 #define __prefix_lookup_dentry(name, follow_link)				\
-	dentry = __sparc64_lookup_dentry (name, follow_link);			\
-	if (!IS_ERR (dentry)) return dentry;
+	if (current->personality) {						\
+		dentry = __sparc64_lookup_dentry (name, follow_link);		\
+		if (dentry) return dentry;					\
+	}
 
 #endif /* __SPARC64_NAMEI_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/ns87303.h linux/include/asm-sparc64/ns87303.h
--- v2.1.78/linux/include/asm-sparc64/ns87303.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/ns87303.h	Mon Jan 12 15:15:58 1998
@@ -0,0 +1,84 @@
+/* $Id: ns87303.h,v 1.1 1997/10/14 13:30:37 ecd Exp $
+ * ns87303.h: Configuration Register Description for the
+ *            National Semiconductor PC87303 (SuperIO).
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ */
+
+#ifndef _SPARC_NS87303_H
+#define _SPARC_NS87303_H 1
+
+/*
+ * Controll Register Index Values
+ */
+#define FER	0x00
+#define FAR	0x01
+#define PTR	0x02
+#define FCR	0x03
+#define PCR	0x04
+#define KRR	0x05
+#define PMC	0x06
+#define TUP	0x07
+#define SID	0x08
+#define ASC	0x09
+#define CS0CF0	0x0a
+#define CS0CF1	0x0b
+#define CS1CF0	0x0c
+#define CS1CF1	0x0d
+
+/* Function Address Register (FAR) bits */
+#define FAR_LPT_MASK	0x03
+#define FAR_LPTB	0x00
+#define FAR_LPTA	0x01
+#define FAR_LPTC	0x02
+
+/* Power and Test Register (PTR) bits */
+#define PTR_LPTB_IRQ7	0x08
+#define PTR_LEVEL_IRQ	0x80	/* When not ECP/EPP: Use level IRQ           */
+#define PTR_LPT_REG_DIR	0x80	/* When ECP/EPP: LPT CTR controlls direction */
+				/*               of the parallel port	     */
+
+/* Function Control Register (FCR) bits */
+#define FCR_ZWS_ENA	0x20	/* Enable short host read/write in ECP/EPP   */
+
+/* Printer Controll Register (PCR) bits */
+#define PCR_EPP_ENABLE	0x01
+#define PCR_EPP_IEEE	0x02	/* Enable EPP Version 1.9 (IEEE 1284)        */
+#define PCR_ECP_ENABLE	0x04
+#define PCR_ECP_CLK_ENA	0x08	/* If 0 ECP Clock is stopped on Power down   */
+#define PCR_IRQ_POLAR	0x20	/* If 0 IRQ is level high or negative pulse, */
+				/* if 1 polarity is inverted                 */
+#define PCR_IRQ_ODRAIN	0x40	/* If 1, IRQ is open drain                   */
+
+/* Tape UARTs and Parallel Port Config Register (TUP) bits */
+#define TUP_EPP_TIMO	0x02	/* Enable EPP timeout IRQ                    */
+
+/* Advanced SuperIO Config Register (ASC) bits */
+#define ASC_LPT_IRQ7	0x01	/* Allways use IRQ7 for LPT                  */
+
+#ifdef __KERNEL__
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+static __inline__ void ns87303_writeb(unsigned long port, int index,
+				     unsigned char value)
+{
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	outb(index, port);
+	outb(value, port + 1);
+	outb(value, port + 1);
+	restore_flags(flags);
+}
+
+static __inline__ unsigned char ns87303_readb(unsigned long port, int index)
+{
+	outb(index, port);
+	return inb(port + 1);
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* !(_SPARC_NS87303_H) */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/page.h linux/include/asm-sparc64/page.h
--- v2.1.78/linux/include/asm-sparc64/page.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/page.h	Mon Jan 12 15:15:58 1998
@@ -1,12 +1,9 @@
-/* $Id: page.h,v 1.15 1997/08/09 04:56:54 davem Exp $ */
+/* $Id: page.h,v 1.16 1997/11/28 15:59:34 jj Exp $ */
 
 #ifndef _SPARC64_PAGE_H
 #define _SPARC64_PAGE_H
 
 #define PAGE_SHIFT   13
-
-#ifdef __KERNEL__
-
 #ifndef __ASSEMBLY__
 /* I have my suspicions... -DaveM */
 #define PAGE_SIZE    (1UL << PAGE_SHIFT)
@@ -15,6 +12,8 @@
 #endif
 
 #define PAGE_MASK    (~(PAGE_SIZE-1))
+
+#ifdef __KERNEL__
 
 #ifndef __ASSEMBLY__
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/pbm.h linux/include/asm-sparc64/pbm.h
--- v2.1.78/linux/include/asm-sparc64/pbm.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-sparc64/pbm.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: pbm.h,v 1.7 1997/08/25 06:01:14 davem Exp $
+/* $Id: pbm.h,v 1.8 1998/01/10 18:26:10 ecd Exp $
  * pbm.h: U2P PCI bus module pseudo driver software state.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -53,6 +53,7 @@
 	unsigned long			*pci_IO_space;
 	unsigned long			*pci_mem_space;
 	u32				upa_portid;
+	int				index;
 	struct linux_pbm_info		pbm_A;
 	struct linux_pbm_info		pbm_B;
 };
@@ -67,6 +68,16 @@
 };
 
 extern struct linux_psycho *psycho_root;
+extern struct linux_psycho **psycho_index_map;
+extern int linux_num_psycho;
+
+static __inline__ struct linux_psycho *
+psycho_by_index(int index)
+{
+	if (index >= linux_num_psycho)
+		return NULL;
+	return psycho_index_map[index];
+}
 
 /* Special PCI IRQ encoding, this just makes life easier for the generic
  * irq registry layer, there is already enough crap in there due to sbus,
@@ -75,8 +86,8 @@
 #define PCI_IRQ_IDENT		0x80000000	/* This tells irq.c what we are        */
 #define PCI_IRQ_IMAP_OFF	0x7ff00000	/* Offset from first PSYCHO imap       */
 #define PCI_IRQ_IMAP_OFF_SHFT	20
-#define PCI_IRQ_BUSNO		0x000f8000	/* PSYCHO instance, currently unused   */
-#define PCI_IRQ_BUSNO_SHFT	15
+#define PCI_IRQ_BUSNO		0x000fc000	/* PSYCHO instance, currently unused   */
+#define PCI_IRQ_BUSNO_SHFT	14
 #define PCI_IRQ_IGN		0x000007c0	/* PSYCHO "Int Group Number"           */
 #define PCI_IRQ_INO		0x0000003f	/* PSYCHO INO                          */
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/pgtable.h linux/include/asm-sparc64/pgtable.h
--- v2.1.78/linux/include/asm-sparc64/pgtable.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/pgtable.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.57 1997/08/13 04:44:20 paulus Exp $
+/* $Id: pgtable.h,v 1.59 1997/10/12 06:20:43 davem Exp $
  * pgtable.h: SpitFire page table operations.
  *
  * Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -198,7 +198,7 @@
 	struct mm_struct *mm = vma->vm_mm;
 
 	if(mm->context != NO_CONTEXT)
-		__flush_tlb_page(mm->context & 0x1fff, page & PAGE_MASK);
+		__flush_tlb_page(mm->context & 0x1fff, page);
 }
 
 #else /* __SMP__ */
@@ -231,7 +231,7 @@
 	struct mm_struct *mm = vma->vm_mm;
 
 	if(mm->context != NO_CONTEXT)
-		smp_flush_tlb_page(mm, page & PAGE_MASK);
+		smp_flush_tlb_page(mm, page);
 }
 
 #endif
@@ -499,7 +499,36 @@
 /* These do nothing with the way I have things setup. */
 #define mmu_lockarea(vaddr, len)		(vaddr)
 #define mmu_unlockarea(vaddr, len)		do { } while(0)
-#define update_mmu_cache(vma, address, pte)	do { } while(0)
+
+extern __inline__ void update_mmu_cache(struct vm_area_struct *vma,
+					unsigned long address, pte_t pte)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	unsigned long ctx = mm->context & 0x1fff;
+	unsigned long tag_access;
+
+	tag_access = address | ctx;
+
+	__asm__ __volatile__("
+	rdpr	%%pstate, %%g1
+	wrpr	%%g1, %0, %%pstate
+	brz,pt	%1, 1f
+	 mov	%2, %%g2
+	stxa	%3, [%%g2] %5
+	b,pt	%%xcc, 2f
+	 stxa	%4, [%%g0] %6
+1:
+	stxa	%3, [%%g2] %7
+	stxa	%4, [%%g0] %8
+2:
+	wrpr	%%g1, 0x0, %%pstate
+"	: /* no outputs */
+	: "i" (PSTATE_IE), "r" (vma->vm_flags & VM_EXEC),
+	  "i" (TLB_TAG_ACCESS), "r" (tag_access), "r" (pte_val(pte)),
+	  "i" (ASI_IMMU), "i" (ASI_ITLB_DATA_IN),
+	  "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_IN)
+	: "g1", "g2");
+}
 
 /* Make a non-present pseudo-TTE. */
 extern inline pte_t mk_swap_pte(unsigned long type, unsigned long offset)
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/posix_types.h linux/include/asm-sparc64/posix_types.h
--- v2.1.78/linux/include/asm-sparc64/posix_types.h	Wed Dec 10 11:12:45 1997
+++ linux/include/asm-sparc64/posix_types.h	Mon Jan 12 15:15:58 1998
@@ -18,7 +18,7 @@
 typedef unsigned int           __kernel_uid_t;
 typedef unsigned int           __kernel_gid_t;
 typedef unsigned int           __kernel_dev_t;
-typedef unsigned int           __kernel_ino_t;
+typedef unsigned long          __kernel_ino_t;
 typedef unsigned int           __kernel_mode_t;
 typedef unsigned short         __kernel_umode_t;
 typedef unsigned int           __kernel_nlink_t;
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/processor.h linux/include/asm-sparc64/processor.h
--- v2.1.78/linux/include/asm-sparc64/processor.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-sparc64/processor.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.33 1997/08/19 14:18:36 jj Exp $
+/* $Id: processor.h,v 1.40 1997/10/24 11:57:59 jj Exp $
  * include/asm-sparc64/processor.h
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -7,6 +7,7 @@
 #ifndef __ASM_SPARC64_PROCESSOR_H
 #define __ASM_SPARC64_PROCESSOR_H
 
+#include <asm/asi.h>
 #include <asm/a.out.h>
 #include <asm/pstate.h>
 #include <asm/ptrace.h>
@@ -26,23 +27,42 @@
 /* User lives in his very own context, and cannot reference us. */
 #define TASK_SIZE	((1UL << (PAGE_SHIFT - 3)) * PGDIR_SIZE)
 
+#define COPY_TASK_STRUCT(dst, src)										\
+do {														\
+	if (src->tss.w_saved)											\
+		*dst = *src;											\
+	else {													\
+		memcpy (dst, src, ((const unsigned long)(&((struct task_struct *)0)->tss.reg_window)));		\
+		memcpy ((char *)dst + ((const unsigned long)(&((struct task_struct *)0)->tss.sig_address)),	\
+			(char *)src + ((const unsigned long)(&((struct task_struct *)0)->tss.sig_address)),	\
+			sizeof(struct task_struct) - 								\
+			  ((const unsigned long)(&((struct task_struct *)0)->tss.sig_address)));		\
+	}													\
+} while (0)
+
 #ifndef __ASSEMBLY__
 
 #define NSWINS		8
 
+typedef struct {
+	unsigned long seg;
+} mm_segment_t;
+
 /* The Sparc processor specific thread struct. */
 struct thread_struct {
 /*DC1*/	unsigned long ksp __attribute__ ((aligned(16)));
-	unsigned long kpc;
-/*DC2*/	unsigned long wstate;
-	unsigned int cwp;
-	unsigned int ctx;
-
-/*DC3*/	unsigned int flags;
-	unsigned int new_signal;
-	unsigned long current_ds;
-/*DC4*/	unsigned long w_saved;
-	struct pt_regs *kregs;
+	unsigned int kpc;
+	unsigned short wstate;
+	unsigned short cwp;
+
+/*DC2*/	unsigned short flags;
+	unsigned short ctx;
+	unsigned short w_saved;
+	unsigned short new_signal;
+	mm_segment_t current_ds;
+
+/*DC3*/	struct pt_regs *kregs;
+	unsigned long *utraps;
 
 	struct reg_window reg_window[NSWINS] __attribute__ ((aligned (16)));
 	unsigned long rwbuf_stkptrs[NSWINS] __attribute__ ((aligned (8)));
@@ -50,7 +70,6 @@
 	unsigned long sig_address __attribute__ ((aligned (8)));
 	unsigned long sig_desc;
 	struct sigstack sstk_info;
-	struct exec core_exec;     /* just what it says. */
 };
 
 #endif /* !(__ASSEMBLY__) */
@@ -66,19 +85,19 @@
 #define INIT_MMAP { &init_mm, 0xfffff80000000000, 0xfffff80001000000, \
 		    PAGE_SHARED , VM_READ | VM_WRITE | VM_EXEC, NULL, &init_mm.mmap }
 
-#define INIT_TSS  {							\
-/* ksp, kpc, wstate, cwp, secctx */ 					\
-   0,   0,   0,	     0,	  0,						\
-/* flags,              new_signal, current_ds, */			\
-   SPARC_FLAG_KTHREAD, 0,          USER_DS,				\
-/* w_saved, kregs, */							\
-   0,       0,								\
-/* reg_window */							\
-   { { { 0, }, { 0, } }, }, 						\
-/* rwbuf_stkptrs */							\
-   { 0, 0, 0, 0, 0, 0, 0, 0, },						\
-/* sig_address, sig_desc, sstk_info, core_exec */			\
-   0,           0,        { 0, 0, }, { 0, },				\
+#define INIT_TSS  {						\
+/* ksp, kpc, wstate, cwp */ 					\
+   0,   0,   0,	     0,						\
+/* flags,              ctx, w_saved, new_signal, current_ds, */	\
+   SPARC_FLAG_KTHREAD, 0,   0,       0,          KERNEL_DS,	\
+/* kregs,   utraps, */						\
+   0,       0,							\
+/* reg_window */						\
+   { { { 0, }, { 0, } }, }, 					\
+/* rwbuf_stkptrs */						\
+   { 0, 0, 0, 0, 0, 0, 0, 0, },					\
+/* sig_address, sig_desc, sstk_info */				\
+   0,           0,        { 0, 0, },				\
 }
 
 #ifndef __ASSEMBLY__
@@ -89,15 +108,29 @@
 	return t->kpc;
 }
 
+/* On Uniprocessor, even in RMO processes see TSO semantics */
+#ifdef __SMP__
+#define TSTATE_INITIAL_MM	TSTATE_TSO
+#else
+#define TSTATE_INITIAL_MM	TSTATE_RMO
+#endif
+
 /* Do necessary setup to start up a newly executed thread. */
 #define start_thread(regs, pc, sp) \
 do { \
-	regs->tstate = (regs->tstate & (TSTATE_CWP)) | (TSTATE_IE); \
+	regs->tstate = (regs->tstate & (TSTATE_CWP)) | (TSTATE_INITIAL_MM|TSTATE_IE) | (ASI_PNF << 24); \
 	regs->tpc = ((pc & (~3)) - 4); \
 	regs->tnpc = regs->tpc + 4; \
 	regs->y = 0; \
 	current->tss.flags &= ~SPARC_FLAG_32BIT; \
 	current->tss.wstate = (1 << 3); \
+	if (current->tss.utraps) { \
+		if (*(current->tss.utraps) < 2) \
+			kfree (current->tss.utraps); \
+		else \
+			(*(current->tss.utraps))--; \
+		current->tss.utraps = NULL; \
+	} \
 	__asm__ __volatile__( \
 	"stx		%%g0, [%0 + %2 + 0x00]\n\t" \
 	"stx		%%g0, [%0 + %2 + 0x08]\n\t" \
@@ -128,12 +161,19 @@
 	pc &= 0x00000000ffffffffUL; \
 	sp &= 0x00000000ffffffffUL; \
 \
-	regs->tstate = (regs->tstate & (TSTATE_CWP))|(TSTATE_IE|TSTATE_AM); \
+	regs->tstate = (regs->tstate & (TSTATE_CWP))|(TSTATE_INITIAL_MM|TSTATE_IE|TSTATE_AM); \
 	regs->tpc = ((pc & (~3)) - 4); \
 	regs->tnpc = regs->tpc + 4; \
 	regs->y = 0; \
 	current->tss.flags |= SPARC_FLAG_32BIT; \
 	current->tss.wstate = (2 << 3); \
+	if (current->tss.utraps) { \
+		if (*(current->tss.utraps) < 2) \
+			kfree (current->tss.utraps); \
+		else \
+			(*(current->tss.utraps))--; \
+		current->tss.utraps = NULL; \
+	} \
 	zero = 0; \
 	__asm__ __volatile__( \
 	"stx		%%g0, [%0 + %2 + 0x00]\n\t" \
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/ptrace.h linux/include/asm-sparc64/ptrace.h
--- v2.1.78/linux/include/asm-sparc64/ptrace.h	Mon Jul  7 08:18:56 1997
+++ linux/include/asm-sparc64/ptrace.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: ptrace.h,v 1.12 1997/06/24 16:30:35 davem Exp $ */
+/* $Id: ptrace.h,v 1.13 1997/09/17 17:27:51 davem Exp $ */
 #ifndef _SPARC64_PTRACE_H
 #define _SPARC64_PTRACE_H
 
@@ -114,7 +114,9 @@
 #include <asm/asm_offsets.h>
 #endif
 
+#ifdef __KERNEL__
 #define STACK_BIAS		2047
+#endif
 
 /* These are for pt_regs. */
 #define PT_V9_G0     0x00
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/reg.h linux/include/asm-sparc64/reg.h
--- v2.1.78/linux/include/asm-sparc64/reg.h	Mon Jul  7 08:18:56 1997
+++ linux/include/asm-sparc64/reg.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: reg.h,v 1.2 1997/06/24 23:19:55 davem Exp $
+/* $Id: reg.h,v 1.4 1997/09/14 08:40:29 davem Exp $
  * linux/asm-sparc64/reg.h
  * Layout of the registers as expected by gdb on the Sparc
  * we should replace the user.h definitions with those in
@@ -15,69 +15,7 @@
 #define __SPARC64_REG_H
 
 struct regs {
-	int     r_psr;
-#define r_ps r_psr
-        int     r_pc; 
-        int     r_npc;
-        int     r_y;  
-        int     r_g1; 
-        int     r_g2;
-        int     r_g3;
-        int     r_g4;
-        int     r_g5;
-        int     r_g6;
-        int     r_g7;
-        int     r_o0;
-        int     r_o1;
-        int     r_o2;
-        int     r_o3;
-        int     r_o4;
-        int     r_o5;
-        int     r_o6;
-        int     r_o7;
-};
-
-struct fpq {
-        unsigned int *addr;
-        unsigned ing instr;
-};
-
-struct  fq {
-        union {
-                double  whole;
-                struct  fpq fpq;
-        } FQu;
-};
-
-#define FPU_REGS_TYPE unsigned int
-#define FPU_FSR_TYPE unsigned
-
-struct fp_status {
-        union {
-                FPU_REGS_TYPE Fpu_regs[32];
-                double  Fpu_dregs[16];
-        } fpu_fr;
-        FPU_FSR_TYPE Fpu_fsr;
-        unsigned Fpu_flags;
-        unsigned Fpu_extra;
-        unsigned Fpu_qcnt;
-        struct fq Fpu_q[16];
-};
-
-#define fpu_regs  f_fpstatus.fpu_fr.Fpu_regs
-#define fpu_dregs f_fpstatus.fpu_fr.Fpu_dregs
-#define fpu_fsr   f_fpstatus.Fpu_fsr
-#define fpu_flags f_fpstatus.Fpu_flags
-#define fpu_extra f_fpstatus.Fpu_extra
-#define fpu_q     f_fpstatus.Fpu_q
-#define fpu_qcnt  f_fpstatus.Fpu_qcnt
-
-struct fpu {
-        struct fp_status f_fpstatus;
-};
-
-struct regs64 {
-        unsigned long r_g1; 
+        unsigned long r_g1;
         unsigned long r_g2;
         unsigned long r_g3;
         unsigned long r_g4;
@@ -92,17 +30,23 @@
         unsigned long r_o5;
         unsigned long r_o6;
         unsigned long r_o7;
-        unsigned long tstate;
-        unsigned long tpc;
-        unsigned long tnpc;
-        unsigned int  y;
-        unsigned int  fprs;
+        unsigned long __pad;
+        unsigned long r_tstate;
+        unsigned long r_tpc;
+        unsigned long r_tnpc;
+        unsigned int  r_y;
+        unsigned int  r_fprs;
 };
 
-struct fp_status64 {
-        unsigned long regs[32];
-        unsigned long fsr;
+struct fp_status {
+        unsigned long fpu_fr[32];
+        unsigned long Fpu_fsr;
+};
+
+struct fpu {
+	struct fp_status f_fpstatus;
 };
 
+#define fpu_regs  f_fpstatus.fpu_fr
 
 #endif /* __SPARC64_REG_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/sigcontext.h linux/include/asm-sparc64/sigcontext.h
--- v2.1.78/linux/include/asm-sparc64/sigcontext.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-sparc64/sigcontext.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: sigcontext.h,v 1.9 1997/08/19 14:18:38 jj Exp $ */
+/* $Id: sigcontext.h,v 1.10 1997/12/11 15:16:11 jj Exp $ */
 #ifndef __SPARC64_SIGCONTEXT_H
 #define __SPARC64_SIGCONTEXT_H
 
@@ -67,7 +67,7 @@
 
 typedef struct {
 	struct     pt_regs si_regs;
-	int si_mask;
+	long	   si_mask;
 } __siginfo_t;
 
 typedef struct {
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/siginfo.h linux/include/asm-sparc64/siginfo.h
--- v2.1.78/linux/include/asm-sparc64/siginfo.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/siginfo.h	Mon Jan 12 15:15:58 1998
@@ -0,0 +1,277 @@
+#ifndef _SPARC64_SIGINFO_H
+#define _SPARC64_SIGINFO_H
+
+#include <linux/types.h>
+
+typedef union sigval {
+	int sival_int;
+	void *sival_ptr;
+} sigval_t;
+
+#ifdef __KERNEL__
+
+typedef union sigval32 {
+	int sival_int;
+	u32 sival_ptr;
+} sigval_t32;
+
+#endif /* __KERNEL__ */
+
+#define SI_MAX_SIZE	128
+#define SI_PAD_SIZE	((SI_MAX_SIZE/sizeof(int)) - 4)
+#define SI_PAD_SIZE32	((SI_MAX_SIZE/sizeof(int)) - 3)
+
+typedef struct siginfo {
+	int si_signo;
+	int si_errno;
+	int si_code;
+
+	union {
+		int _pad[SI_PAD_SIZE];
+
+		/* kill() */
+		struct {
+			pid_t _pid;		/* sender's pid */
+			uid_t _uid;		/* sender's uid */
+		} _kill;
+
+		/* POSIX.1b timers */
+		struct {
+			unsigned int _timer1;
+			unsigned int _timer2;
+		} _timer;
+
+		/* POSIX.1b signals */
+		struct {
+			pid_t _pid;		/* sender's pid */
+			uid_t _uid;		/* sender's uid */
+			sigval_t _sigval;
+		} _rt;
+
+		/* SIGCHLD */
+		struct {
+			pid_t _pid;		/* which child */
+			int _status;		/* exit code */
+			clock_t _utime;
+			clock_t _stime;
+		} _sigchld;
+
+		/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+		struct {
+			void *_addr; /* faulting insn/memory ref. */
+			int  _trapno; /* TRAP # which caused the signal */
+		} _sigfault;
+
+		/* SIGPOLL */
+		struct {
+			int _band;	/* POLL_IN, POLL_OUT, POLL_MSG */
+			int _fd;
+		} _sigpoll;
+	} _sifields;
+} siginfo_t;
+
+#ifdef __KERNEL__
+
+typedef struct siginfo32 {
+	int si_signo;
+	int si_errno;
+	int si_code;
+
+	union {
+		int _pad[SI_PAD_SIZE32];
+
+		/* kill() */
+		struct {
+			__kernel_pid_t32 _pid;		/* sender's pid */
+			__kernel_uid_t32 _uid;		/* sender's uid */
+		} _kill;
+
+		/* POSIX.1b timers */
+		struct {
+			unsigned int _timer1;
+			unsigned int _timer2;
+		} _timer;
+
+		/* POSIX.1b signals */
+		struct {
+			__kernel_pid_t32 _pid;		/* sender's pid */
+			__kernel_uid_t32 _uid;		/* sender's uid */
+			sigval_t32 _sigval;
+		} _rt;
+
+		/* SIGCHLD */
+		struct {
+			__kernel_pid_t32 _pid;		/* which child */
+			int _status;		/* exit code */
+			__kernel_clock_t32 _utime;
+			__kernel_clock_t32 _stime;
+		} _sigchld;
+
+		/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+		struct {
+			u32 _addr; /* faulting insn/memory ref. */
+			int _trapno;
+		} _sigfault;
+
+		/* SIGPOLL */
+		struct {
+			int _band;	/* POLL_IN, POLL_OUT, POLL_MSG */
+			int _fd;
+		} _sigpoll;
+	} _sifields;
+} siginfo_t32;
+
+#endif /* __KERNEL__ */
+
+/*
+ * How these fields are to be accessed.
+ */
+#define si_pid		_sifields._kill._pid
+#define si_uid		_sifields._kill._uid
+#define si_status	_sifields._sigchld._status
+#define si_utime	_sifields._sigchld._utime
+#define si_stime	_sifields._sigchld._stime
+#define si_value	_sifields._rt._sigval
+#define si_int		_sifields._rt._sigval.sival_int
+#define si_ptr		_sifields._rt._sigval.sival_ptr
+#define si_addr		_sifields._sigfault._addr
+#define si_trapno	_sifields._sigfault._trapno
+#define si_band		_sifields._sigpoll._band
+#define si_fd		_sifields._sigpoll._fd
+
+/*
+ * si_code values
+ * Digital reserves positive values for kernel-generated signals.
+ */
+#define SI_USER		0	/* sent by kill, sigsend, raise */
+#define SI_KERNEL	0x80	/* sent by the kernel from somewhere */
+#define SI_QUEUE	-1	/* sent by sigqueue */
+#define SI_TIMER	-2	/* sent by timer expiration */
+#define SI_MESGQ	-3	/* sent by real time mesq state change */
+#define SI_ASYNCIO	-4	/* sent by AIO completion */
+
+#define SI_FROMUSER(siptr)	((siptr)->si_code <= 0)
+#define SI_FROMKERNEL(siptr)	((siptr)->si_code > 0)
+
+/*
+ * SIGILL si_codes
+ */
+#define ILL_ILLOPC	1	/* illegal opcode */
+#define ILL_ILLOPN	2	/* illegal operand */
+#define ILL_ILLADR	3	/* illegal addressing mode */
+#define ILL_ILLTRP	4	/* illegal trap */
+#define ILL_PRVOPC	5	/* privileged opcode */
+#define ILL_PRVREG	6	/* privileged register */
+#define ILL_COPROC	7	/* coprocessor error */
+#define ILL_BADSTK	8	/* internal stack error */
+#define NSIGILL		8
+
+/*
+ * SIGFPE si_codes
+ */
+#define FPE_INTDIV	1	/* integer divide by zero */
+#define FPE_INTOVF	2	/* integer overflow */
+#define FPE_FLTDIV	3	/* floating point divide by zero */
+#define FPE_FLTOVF	4	/* floating point overflow */
+#define FPE_FLTUND	5	/* floating point underflow */
+#define FPE_FLTRES	6	/* floating point inexact result */
+#define FPE_FLTINV	7	/* floating point invalid operation */
+#define FPE_FLTSUB	8	/* subscript out of range */
+#define NSIGFPE		8
+
+/*
+ * SIGSEGV si_codes
+ */
+#define SEGV_MAPERR	1	/* address not mapped to object */
+#define SRGV_ACCERR	2	/* invalid permissions for mapped object */
+#define NSIGSEGV	2
+
+/*
+ * SIGBUS si_codes
+ */
+#define BUS_ADRALN	1	/* invalid address alignment */
+#define BUS_ADRERR	2	/* non-existant physical address */
+#define BUS_OBJERR	3	/* object specific hardware error */
+#define NSIGBUS		3
+
+/*
+ * SIGTRAP si_codes
+ */
+#define TRAP_BRKPT	1	/* process breakpoint */
+#define TRAP_TRACE	2	/* process trace trap */
+#define NSIGTRAP
+
+/*
+ * SIGCHLD si_codes
+ */
+#define CLD_EXITED	1	/* child has exited */
+#define CLD_KILLED	2	/* child was killed */
+#define CLD_DUMPED	3	/* child terminated abnormally */
+#define CLD_TRAPPED	4	/* traced child has trapped */
+#define CLD_STOPPED	5	/* child has stopped */
+#define CLD_CONTINUED	6	/* stopped child has continued */
+#define NSIGCHLD
+
+/*
+ * SIGPOLL si_codes
+ */
+#define POLL_IN		1	/* data input available */
+#define POLL_OUT	2	/* output buffers available */
+#define POLL_MSG	3	/* input message available */
+#define POLL_ERR	4	/* i/o error */
+#define POLL_PRI	5	/* high priority input available */
+#define POLL_HUP	6	/* device disconnected */
+#define NSIGPOLL	6
+
+/*
+ * sigevent definitions
+ * 
+ * It seems likely that SIGEV_THREAD will have to be handled from 
+ * userspace, libpthread transmuting it to SIGEV_SIGNAL, which the
+ * thread manager then catches and does the appropriate nonsense.
+ * However, everything is written out here so as to not get lost.
+ */
+#define SIGEV_SIGNAL	0	/* notify via signal */
+#define SIGEV_NONE	1	/* other notification: meaningless */
+#define SIGEV_THREAD	2	/* deliver via thread creation */
+
+#define SIGEV_MAX_SIZE	64
+#define SIGEV_PAD_SIZE	((SIGEV_MAX_SIZE/sizeof(int)) - 4)
+#define SIGEV_PAD_SIZE32 ((SIGEV_MAX_SIZE/sizeof(int)) - 3)
+
+typedef struct sigevent {
+	sigval_t sigev_value;
+	int sigev_signo;
+	int sigev_notify;
+	union {
+		int _pad[SIGEV_PAD_SIZE];
+
+		struct {
+			void (*_function)(sigval_t);
+			void *_attribute;	/* really pthread_attr_t */
+		} _sigev_thread;
+	} _sigev_un;
+} sigevent_t;
+
+#ifdef __KERNEL__
+
+typedef struct sigevent32 {
+	sigval_t sigev_value;
+	int sigev_signo;
+	int sigev_notify;
+	union {
+		int _pad[SIGEV_PAD_SIZE32];
+
+		struct {
+			u32 _function;
+			u32 _attribute;	/* really pthread_attr_t */
+		} _sigev_thread;
+	} _sigev_un;
+} sigevent_t32;
+
+#endif /* __KERNEL__ */
+
+#define sigev_notify_function	_sigev_un._sigev_thread._function
+#define sigev_notify_attributes	_sigev_un._sigev_thread._attribute
+
+#endif
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/signal.h linux/include/asm-sparc64/signal.h
--- v2.1.78/linux/include/asm-sparc64/signal.h	Wed Apr 23 19:01:28 1997
+++ linux/include/asm-sparc64/signal.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: signal.h,v 1.3 1997/04/18 14:34:47 jj Exp $ */
+/* $Id: signal.h,v 1.4 1997/12/11 15:16:06 jj Exp $ */
 #ifndef _ASMSPARC64_SIGNAL_H
 #define _ASMSPARC64_SIGNAL_H
 
@@ -7,15 +7,13 @@
 #ifdef __KERNEL__
 #ifndef __ASSEMBLY__
 #include <linux/personality.h>
+#include <linux/types.h>
 #endif
 #endif
 
 /* On the Sparc the signal handlers get passed a 'sub-signal' code
  * for certain signal types, which we document here.
  */
-#define _NSIG             32
-#define NSIG		_NSIG
-
 #define SIGHUP		 1
 #define SIGINT		 2
 #define SIGQUIT		 3
@@ -80,14 +78,51 @@
 #define SIGUSR1		30
 #define SIGUSR2		31
 
+/* Most things should be clean enough to redefine this at will, if care
+   is taken to make libc match.  */
+
+#define __OLD_NSIG	32
+#define __NEW_NSIG      64
+#define _NSIG_BPW     	64
+#define _NSIG_WORDS   	(__NEW_NSIG / _NSIG_BPW)
+
+#define _NSIG_BPW32   	32
+#define _NSIG_WORDS32 	(__NEW_NSIG / _NSIG_BPW32)
+
+#define SIGRTMIN       32
+#define SIGRTMAX       (__NEW_NSIG - 1)
+
+#if defined(__KERNEL__) || defined(__WANT_POSIX1B_SIGNALS__)
+#define _NSIG			__NEW_NSIG
+#define __new_sigset_t		sigset_t
+#define __new_sigset_t32	sigset_t32
+#define __new_sigaction		sigaction
+#define __new_sigaction32	sigaction32
+#define __old_sigset_t		old_sigset_t
+#define __old_sigset_t32	old_sigset_t32
+#define __old_sigaction		old_sigaction
+#define __old_sigaction32	old_sigaction32
+#else
+#define _NSIG			__OLD_NSIG
+#define NSIG			_NSIG
+#define __old_sigset_t		sigset_t
+#define __old_sigset_t32	sigset_t32
+#define __old_sigaction		sigaction
+#define __old_sigaction32	sigaction32
+#endif
+
 #ifndef __ASSEMBLY__
 
-typedef unsigned long sigset_t;
-typedef unsigned int sigset_t32;
+typedef unsigned long __old_sigset_t;            /* at least 32 bits */
+typedef unsigned int __old_sigset_t32;
 
-#ifdef __KERNEL__
-#include <asm/sigcontext.h>
-#endif
+typedef struct {
+       unsigned long sig[_NSIG_WORDS];
+} __new_sigset_t;
+
+typedef struct {
+       unsigned int sig[_NSIG_WORDS32];
+} __new_sigset_t32;
 
 /* A SunOS sigstack */
 struct sigstack {
@@ -118,6 +153,9 @@
 #define SA_INTERRUPT	0x10
 #define SA_NOMASK	0x20
 #define SA_SHIRQ	0x40
+#define SA_NOCLDWAIT    0x100 /* not supported yet */
+#define SA_SIGINFO      0x200
+
 
 #define SIG_BLOCK          0x01	/* for blocking signals */
 #define SIG_UNBLOCK        0x02	/* for unblocking signals */
@@ -156,19 +194,44 @@
 #define SIG_IGN	((__sighandler_t)1)	/* ignore signal */
 #define SIG_ERR	((__sighandler_t)-1)	/* error return from signal */
 
-struct sigaction {
-	__sighandler_t  sa_handler;
-	sigset_t        sa_mask;
-	unsigned long   sa_flags;
-	void (*sa_restorer) (void);     /* not used by Linux/SPARC yet */
+struct __new_sigaction {
+	__sighandler_t		sa_handler;
+	unsigned long		sa_flags;
+	void 			(*sa_restorer)(void);     /* not used by Linux/SPARC yet */
+	__new_sigset_t		sa_mask;
+};
+
+struct __new_sigaction32 {
+	unsigned		sa_handler;
+	unsigned int    	sa_flags;
+	unsigned		sa_restorer;     /* not used by Linux/SPARC yet */
+	__new_sigset_t32 	sa_mask;
+};
+
+struct k_sigaction {
+	struct __new_sigaction 	sa;
+	void			*ka_restorer;
+};
+
+struct __old_sigaction {
+	__sighandler_t  	sa_handler;
+	__old_sigset_t  	sa_mask;
+	unsigned long   	sa_flags;
+	void 			(*sa_restorer)(void);     /* not used by Linux/SPARC yet */
 };
 
-struct sigaction32 {
-	unsigned	sa_handler;
-	sigset_t32      sa_mask;
-	unsigned int    sa_flags;
-	unsigned	sa_restorer;     /* not used by Linux/SPARC yet */
+struct __old_sigaction32 {
+	unsigned		sa_handler;
+	__old_sigset_t32  	sa_mask;
+	unsigned int    	sa_flags;
+	unsigned		sa_restorer;     /* not used by Linux/SPARC yet */
 };
+
+typedef struct sigaltstack {
+       void 			*ss_sp;
+       int 			ss_flags;
+       __kernel_size_t 		ss_size;
+} stack_t;
 
 #endif /* !(__ASSEMBLY__) */
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/socket.h linux/include/asm-sparc64/socket.h
--- v2.1.78/linux/include/asm-sparc64/socket.h	Thu Mar 27 14:40:09 1997
+++ linux/include/asm-sparc64/socket.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: socket.h,v 1.2 1997/03/17 04:50:55 davem Exp $ */
+/* $Id: socket.h,v 1.4 1997/10/19 00:19:23 davem Exp $ */
 #ifndef _ASM_SOCKET_H
 #define _ASM_SOCKET_H
 
@@ -35,9 +35,11 @@
 #define SO_NO_CHECK	0x000b
 #define SO_PRIORITY	0x000c
 
+#define SO_BINDTODEVICE 0x000d
+
 /* 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
+#define SO_SECURITY_AUTHENTICATION		0x5001
+#define SO_SECURITY_ENCRYPTION_TRANSPORT	0x5002
+#define SO_SECURITY_ENCRYPTION_NETWORK		0x5004
 
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/string.h linux/include/asm-sparc64/string.h
--- v2.1.78/linux/include/asm-sparc64/string.h	Thu Jul 17 10:06:08 1997
+++ linux/include/asm-sparc64/string.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: string.h,v 1.7 1997/07/13 18:23:44 davem Exp $
+/* $Id: string.h,v 1.8 1997/11/19 07:57:50 jj Exp $
  * string.h: External definitions for optimized assembly string
  *           routines for the Linux Kernel.
  *
@@ -23,7 +23,7 @@
 extern __kernel_size_t __memcpy_384plus(void *,const void *,__kernel_size_t,long,long);
 extern __kernel_size_t __memset(void *,int,__kernel_size_t);
 
-#ifndef EXPORT_SYMTAB
+#ifndef EXPORT_SYMTAB_STROPS
 
 /* First the mem*() things. */
 #define __HAVE_ARCH_BCOPY
@@ -192,7 +192,7 @@
  __constant_strncmp(__arg0, __arg1, __arg2) : \
  __strncmp(__arg0, __arg1, __arg2))
 
-#endif /* !EXPORT_SYMTAB */
+#endif /* !EXPORT_SYMTAB_STROPS */
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/system.h linux/include/asm-sparc64/system.h
--- v2.1.78/linux/include/asm-sparc64/system.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-sparc64/system.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: system.h,v 1.36 1997/08/19 14:18:33 jj Exp $ */
+/* $Id: system.h,v 1.37 1997/10/20 00:14:22 davem Exp $ */
 #ifndef __SPARC64_SYSTEM_H
 #define __SPARC64_SYSTEM_H
 
@@ -154,17 +154,17 @@
 	"stx	%%i7, [%%sp + 2047 + 0x78]\n\t"					\
 	"rdpr	%%wstate, %%o5\n\t"						\
 	"stx	%%o6, [%%g6 + %3]\n\t"						\
-	"stx	%%o5, [%%g6 + %2]\n\t"						\
+	"stw	%%o7, [%%g6 + %4]\n\t"						\
+	"sth	%%o5, [%%g6 + %2]\n\t"						\
 	"rdpr	%%cwp, %%o5\n\t"						\
-	"stx	%%o7, [%%g6 + %4]\n\t"						\
-	"st	%%o5, [%%g6 + %5]\n\t"						\
+	"sth	%%o5, [%%g6 + %5]\n\t"						\
 	"membar #Sync\n\t"							\
 	"mov	%0, %%g6\n\t"							\
-	"ld	[%0 + %5], %%g1\n\t"						\
+	"lduh	[%0 + %5], %%g1\n\t"						\
 	"wrpr	%%g1, %%cwp\n\t"						\
-	"ldx	[%%g6 + %2], %%o5\n\t"						\
 	"ldx	[%%g6 + %3], %%o6\n\t"						\
-	"ldx	[%%g6 + %4], %%o7\n\t"						\
+	"lduw	[%%g6 + %4], %%o7\n\t"						\
+	"lduh	[%%g6 + %2], %%o5\n\t"						\
 	"mov	%%g6, %0\n\t"							\
 	"wrpr	%%o5, 0x0, %%wstate\n\t"					\
 	"ldx	[%%sp + 2047 + 0x70], %%i6\n\t"					\
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/ttable.h linux/include/asm-sparc64/ttable.h
--- v2.1.78/linux/include/asm-sparc64/ttable.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-sparc64/ttable.h	Mon Jan 12 15:15:58 1998
@@ -1,8 +1,10 @@
-/* $Id: ttable.h,v 1.3 1997/08/29 15:52:35 jj Exp $ */
+/* $Id: ttable.h,v 1.5 1997/10/14 16:21:34 jj Exp $ */
 #ifndef _SPARC64_TTABLE_H
 #define _SPARC64_TTABLE_H
 
 #include <linux/config.h>
+#include <asm/asm_offsets.h>
+#include <asm/utrap.h>
 
 #define BOOT_KERNEL b sparc64_boot; nop; nop; nop; nop; nop; nop; nop;
 
@@ -97,9 +99,19 @@
 	ba,pt	%xcc, tl0_solaris + 0xc;		\
 	 mov	num, %g1;				\
 	nop;nop;nop;
+	
+#define TRAP_UTRAP(handler,lvl)						\
+	ldx	[%g6 + AOFF_task_tss + AOFF_thread_utraps], %g1;	\
+	sethi	%hi(109f), %g7;						\
+	brz,pn	%g1, utrap;						\
+	 or	%g7, %lo(109f), %g7;					\
+	ba,pt	%xcc, utrap;						\
+109:	 ldx	[%g1 + handler*8], %g1;					\
+	ba,pt	%xcc, utrap_ill;					\
+	 mov	lvl, %o1;
 
-#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 SUNOS_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall32, sunos_sys_table)
+#define	LINUX_32BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall32, sys_call_table32)
 #define LINUX_64BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sys_call_table64)
 #define GETCC_TRAP TRAP(getcc)
 #define SETCC_TRAP TRAP(setcc)
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/types.h linux/include/asm-sparc64/types.h
--- v2.1.78/linux/include/asm-sparc64/types.h	Fri Dec 13 01:37:47 1996
+++ linux/include/asm-sparc64/types.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: types.h,v 1.1 1996/11/20 12:03:51 davem Exp $ */
+/* $Id: types.h,v 1.2 1997/12/22 13:28:33 mj Exp $ */
 #ifndef _SPARC64_TYPES_H
 #define _SPARC64_TYPES_H
 
@@ -42,6 +42,8 @@
 
 typedef __signed__ long s64;
 typedef unsigned long u64;
+
+#define BITS_PER_LONG 64
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/uaccess.h linux/include/asm-sparc64/uaccess.h
--- v2.1.78/linux/include/asm-sparc64/uaccess.h	Thu Sep  4 17:07:31 1997
+++ linux/include/asm-sparc64/uaccess.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: uaccess.h,v 1.22 1997/08/19 15:25:35 jj Exp $ */
+/* $Id: uaccess.h,v 1.24 1997/12/15 15:05:14 jj Exp $ */
 #ifndef _ASM_UACCESS_H
 #define _ASM_UACCESS_H
 
@@ -17,13 +17,21 @@
 
 #ifndef __ASSEMBLY__
 
-/* Sparc is not segmented, however we need to be able to fool verify_area()
- * when doing system calls from kernel mode legitimately.
+/*
+ * Sparc64 is segmented, though more like the M68K than the I386. 
+ * We use the secondary ASI to address user memory, which references a
+ * completely different VM map, thus there is zero chance of the user
+ * doing something queer and tricking us into poking kernel memory.
+ *
+ * What is left here is basically what is needed for the other parts of
+ * the kernel that expect to be able to manipulate, erum, "segments".
+ * Or perhaps more properly, permissions.
  *
  * "For historical reasons, these macros are grossly misnamed." -Linus
  */
-#define KERNEL_DS   0x00
-#define USER_DS     0x2B /* har har har */
+
+#define KERNEL_DS   ((mm_segment_t) { 0x00 })
+#define USER_DS     ((mm_segment_t) { 0x2B })	/* har har har */
 
 #define VERIFY_READ	0
 #define VERIFY_WRITE	1
@@ -31,24 +39,29 @@
 #define get_fs() (current->tss.current_ds)
 #define get_ds() (KERNEL_DS)
 
+#define segment_eq(a,b)  ((a).seg == (b).seg)
+
 extern spinlock_t scheduler_lock;
 
-#define set_fs(val)				\
-do {	spin_lock(&scheduler_lock);		\
-	current->tss.current_ds = (val);	\
-	if ((val) == KERNEL_DS) {		\
-		flushw_user ();			\
-		current->tss.ctx = 0;		\
-	} else {				\
-		current->tss.ctx = (current->mm->context & 0x1fff); \
-	}					\
-	spitfire_set_secondary_context(current->tss.ctx); \
-	__asm__ __volatile__("flush %g6");	\
-	spin_unlock(&scheduler_lock);		\
+#define set_fs(val)								\
+do {										\
+	if (current->tss.current_ds.seg != val.seg) {				\
+		spin_lock(&scheduler_lock);					\
+		current->tss.current_ds = (val);				\
+		if (segment_eq((val), KERNEL_DS)) {				\
+			flushw_user ();						\
+			current->tss.ctx = 0;					\
+		} else {							\
+			current->tss.ctx = (current->mm->context & 0x1fff);	\
+		}								\
+		spitfire_set_secondary_context(current->tss.ctx); 		\
+		__asm__ __volatile__("flush %g6");				\
+		spin_unlock(&scheduler_lock);					\
+	}									\
 } while(0)
 
 #define __user_ok(addr,size) 1
-#define __kernel_ok (get_fs() == KERNEL_DS)
+#define __kernel_ok (segment_eq(get_fs(), KERNEL_DS))
 #define __access_ok(addr,size) 1
 #define access_ok(type,addr,size) 1
 
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/unaligned.h linux/include/asm-sparc64/unaligned.h
--- v2.1.78/linux/include/asm-sparc64/unaligned.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/unaligned.h	Mon Jan 12 15:15:58 1998
@@ -3,7 +3,7 @@
 
 /* Sparc can't handle unaligned accesses. */
 
-#include <asm/string.h>
+#include <linux/string.h>
 
 
 /* Use memmove here, so gcc does not insert a __builtin_memcpy. */
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/unistd.h linux/include/asm-sparc64/unistd.h
--- v2.1.78/linux/include/asm-sparc64/unistd.h	Wed Sep 24 20:05:48 1997
+++ linux/include/asm-sparc64/unistd.h	Mon Jan 12 15:15:58 1998
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.7 1997/06/16 05:37:44 davem Exp $ */
+/* $Id: unistd.h,v 1.14 1997/12/11 15:16:08 jj Exp $ */
 #ifndef _SPARC64_UNISTD_H
 #define _SPARC64_UNISTD_H
 
@@ -68,7 +68,7 @@
 #define __NR_geteuid             49 /* SunOS calls getuid()                        */
 #define __NR_getegid             50 /* SunOS calls getgid()                        */
 #define __NR_acct                51 /* Common                                      */
-/* #define __NR_ni_syscall       52    ENOSYS under SunOS                          */
+#define __NR_memory_ordering	 52 /* Linux Specific				   */
 #define __NR_mctl                53 /* SunOS specific                              */
 #define __NR_ioctl               54 /* Common                                      */
 #define __NR_reboot              55 /* Common                                      */
@@ -83,8 +83,8 @@
 #define __NR_getpagesize         64 /* Common                                      */
 #define __NR_msync               65 /* Common in newer 1.3.x revs...               */
 /* #define __NR_ni_syscall       66    ENOSYS under SunOS                          */
-/* #define __NR_ni_syscall       67    ENOSYS under SunOS                          */
-/* #define __NR_ni_syscall       68    ENOSYS under SunOS                          */
+#define __NR_pread               67 /* Linux Specific                              */
+#define __NR_pwrite              68 /* Linux Specific                              */
 #define __NR_sbrk                69 /* SunOS Specific                              */
 #define __NR_sstk                70 /* SunOS Specific                              */
 #define __NR_mmap                71 /* Common                                      */
@@ -117,13 +117,13 @@
 #define __NR_connect             98 /* Common                                      */
 #define __NR_accept              99 /* Common                                      */
 #define __NR_getpriority        100 /* Common                                      */
-#define __NR_send               101 /* Common                                      */
-#define __NR_recv               102 /* Common                                      */
-/* #define __NR_ni_syscall      103    ENOSYS under SunOS                          */
-#define __NR_bind               104 /* Common                                      */
-#define __NR_setsockopt         105 /* Common                                      */
-#define __NR_listen             106 /* Common                                      */
-/* #define __NR_ni_syscall      107    ENOSYS under SunOS                          */
+#define __NR_rt_sigreturn       101 /* Linux Specific                              */
+#define __NR_rt_sigaction       102 /* Linux Specific                              */
+#define __NR_rt_sigprocmask     103 /* Linux Specific                              */
+#define __NR_rt_sigpending      104 /* Linux Specific                              */
+#define __NR_rt_sigtimedwait    105 /* Linux Specific                              */
+#define __NR_rt_sigqueueinfo    106 /* Linux Specific                              */
+#define __NR_rt_sigsuspend      107 /* Linux Specific                              */
 #define __NR_sigvec             108 /* SunOS Specific                              */
 #define __NR_sigblock           109 /* SunOS Specific                              */
 #define __NR_sigsetmask         110 /* SunOS Specific                              */
@@ -163,9 +163,9 @@
 #define __NR_getrlimit          144 /* Common                                      */
 #define __NR_setrlimit          145 /* Common                                      */
 #define __NR_killpg             146 /* SunOS Specific                              */
-/* #define __NR_ni_syscall      147    ENOSYS under SunOS                          */
-/* #define __NR_ni_syscall      148    ENOSYS under SunOS                          */
-/* #define __NR_ni_syscall      149    ENOSYS under SunOS                          */
+#define __NR_prctl		147 /* ENOSYS under SunOS                          */
+#define __NR_pciconfig_read	148 /* ENOSYS under SunOS                          */
+#define __NR_pciconfig_write	149 /* ENOSYS under SunOS                          */
 #define __NR_getsockname        150 /* Common                                      */
 #define __NR_getmsg             151 /* SunOS Specific                              */
 #define __NR_putmsg             152 /* SunOS Specific                              */
@@ -180,7 +180,7 @@
 #define __NR_getfh              161 /* SunOS Specific                              */
 #define __NR_getdomainname      162 /* SunOS Specific                              */
 #define __NR_setdomainname      163 /* Common                                      */
-/* #define __NR_ni_syscall      164    ENOSYS under SunOS                          */
+#define __NR_utrap_install	164 /* SYSV ABI/v9 required			   */
 #define __NR_quotactl           165 /* Common                                      */
 #define __NR_exportfs           166 /* SunOS Specific                              */
 #define __NR_mount              167 /* Common                                      */
@@ -200,7 +200,7 @@
 #define __NR_aiowait            181 /* SunOS Specific                              */
 #define __NR_aiocancel          182 /* SunOS Specific                              */
 #define __NR_sigpending         183 /* Common                                      */
-/* #define __NR_ni_syscall      184    ENOSYS under SunOS                          */
+#define __NR_query_module	184 /* Linux Specific				   */
 #define __NR_setpgid            185 /* Common                                      */
 #define __NR_pathconf           186 /* SunOS Specific                              */
 #define __NR_fpathconf          187 /* SunOS Specific                              */
@@ -272,14 +272,13 @@
 #define __NR_fdatasync          253
 #define __NR_nfsservctl         254
 #define __NR_aplib              255
-#define __NR_prctl              256
 
 #define _syscall0(type,name) \
 type name(void) \
 { \
 long __res; \
 __asm__ __volatile__ ("mov %0, %%g1\n\t" \
-		      "t 0x11\n\t" \
+		      "t 0x6d\n\t" \
 		      "sub %%g0, %%o0, %0\n\t" \
 		      "movcc %%xcc, %%o0, %0\n\t" \
 		      : "=r" (__res)\
@@ -297,7 +296,7 @@
 long __res; \
 __asm__ __volatile__ ("mov %0, %%g1\n\t" \
 		      "mov %1, %%o0\n\t" \
-		      "t 0x11\n\t" \
+		      "t 0x6d\n\t" \
 		      "sub %%g0, %%o0, %0\n\t" \
 		      "movcc %%xcc, %%o0, %0\n\t" \
 		      : "=r" (__res), "=r" ((long)(arg1)) \
@@ -316,7 +315,7 @@
 __asm__ __volatile__ ("mov %0, %%g1\n\t" \
 		      "mov %1, %%o0\n\t" \
 		      "mov %2, %%o1\n\t" \
-		      "t 0x11\n\t" \
+		      "t 0x6d\n\t" \
 		      "sub %%g0, %%o0, %0\n\t" \
 		      "movcc %%xcc, %%o0, %0\n\t" \
 		      : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)) \
@@ -336,7 +335,7 @@
 		      "mov %1, %%o0\n\t" \
 		      "mov %2, %%o1\n\t" \
 		      "mov %3, %%o2\n\t" \
-		      "t 0x11\n\t" \
+		      "t 0x6d\n\t" \
 		      "sub %%g0, %%o0, %0\n\t" \
 		      "movcc %%xcc, %%o0, %0\n\t" \
 		      : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
@@ -359,7 +358,7 @@
 		      "mov %2, %%o1\n\t" \
 		      "mov %3, %%o2\n\t" \
 		      "mov %4, %%o3\n\t" \
-		      "t 0x11\n\t" \
+		      "t 0x6d\n\t" \
 		      "sub %%g0,%%o0, %0\n\t" \
 		      "movcc %%xcc, %%o0, %0\n\t" \
 		      : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
@@ -385,7 +384,7 @@
 		      "mov %4, %%o3\n\t" \
 		      "mov %5, %%o4\n\t" \
 		      "mov %6, %%g1\n\t" \
-		      "t 0x11\n\t" \
+		      "t 0x6d\n\t" \
 		      "sub %%g0, %%o0, %0\n\t" \
 		      "movcc %%xcc, %%o0, %0\n\t" \
 		      : "=r" (__res) \
@@ -450,13 +449,13 @@
 			   "mov %1, %%g1\n\t"
 			   "mov %2, %%o0\n\t"    /* Clone flags. */
 			   "mov 0, %%o1\n\t"     /* usp arg == 0 */
-			   "t 0x11\n\t"          /* Linux/Sparc clone(). */
+			   "t 0x6d\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 0x11\n\t"          /* Linux/Sparc exit(). */
+			   "t 0x6d\n\t"          /* Linux/Sparc exit(). */
 			   /* Notreached by child. */
 			   "1:" :
 			   "=r" (retval) :
diff -u --recursive --new-file v2.1.78/linux/include/asm-sparc64/utrap.h linux/include/asm-sparc64/utrap.h
--- v2.1.78/linux/include/asm-sparc64/utrap.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/utrap.h	Mon Jan 12 15:15:58 1998
@@ -0,0 +1,51 @@
+/* $Id: utrap.h,v 1.1 1997/10/14 16:21:31 jj Exp $
+ * include/asm-sparc64/utrap.h
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#ifndef __ASM_SPARC64_UTRAP_H
+#define __ASM_SPARC64_UTRAP_H
+
+#define UT_INSTRUCTION_EXCEPTION		1
+#define UT_INSTRUCTION_ERROR			2
+#define UT_INSTRUCTION_PROTECTION		3
+#define UT_ILLTRAP_INSTRUCTION			4
+#define UT_ILLEGAL_INSTRUCTION			5
+#define UT_PRIVILEGED_OPCODE			6
+#define UT_FP_DISABLED				7
+#define UT_FP_EXCEPTION_IEEE_754		8
+#define UT_FP_EXCEPTION_OTHER			9
+#define UT_TAG_OVERVIEW				10
+#define UT_DIVISION_BY_ZERO			11
+#define UT_DATA_EXCEPTION			12
+#define UT_DATA_ERROR				13
+#define UT_DATA_PROTECTION			14
+#define UT_MEM_ADDRESS_NOT_ALIGNED		15
+#define UT_PRIVILEGED_ACTION			16
+#define UT_ASYNC_DATA_ERROR			17
+#define UT_TRAP_INSTRUCTION_16			18
+#define UT_TRAP_INSTRUCTION_17			19
+#define UT_TRAP_INSTRUCTION_18			20
+#define UT_TRAP_INSTRUCTION_19			21
+#define UT_TRAP_INSTRUCTION_20			22
+#define UT_TRAP_INSTRUCTION_21			23
+#define UT_TRAP_INSTRUCTION_22			24
+#define UT_TRAP_INSTRUCTION_23			25
+#define UT_TRAP_INSTRUCTION_24			26
+#define UT_TRAP_INSTRUCTION_25			27
+#define UT_TRAP_INSTRUCTION_26			28
+#define UT_TRAP_INSTRUCTION_27			29
+#define UT_TRAP_INSTRUCTION_28			30
+#define UT_TRAP_INSTRUCTION_29			31
+#define UT_TRAP_INSTRUCTION_30			32
+#define UT_TRAP_INSTRUCTION_31			33
+
+#define	UTH_NOCHANGE				(-1)
+
+#ifndef __ASSEMBLY__ 
+typedef int utrap_entry_t;
+typedef void *utrap_handler_t;
+#endif /* __ASSEMBLY__ */
+
+#endif /* !(__ASM_SPARC64_PROCESSOR_H) */
diff -u --recursive --new-file v2.1.78/linux/include/linux/affs_fs.h linux/include/linux/affs_fs.h
--- v2.1.78/linux/include/linux/affs_fs.h	Thu Dec  4 14:53:57 1997
+++ linux/include/linux/affs_fs.h	Mon Jan 12 14:46:26 1998
@@ -65,7 +65,7 @@
 extern int	affs_create(struct inode *dir, struct dentry *dentry, int mode);
 extern int	affs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
 extern int	affs_rmdir(struct inode *dir, struct dentry *dentry);
-extern int	affs_link(struct inode *oldinode, struct inode *dir,
+extern int	affs_link(struct dentry *olddentry, struct inode *dir,
 			  struct dentry *dentry);
 extern int	affs_symlink(struct inode *dir, struct dentry *dentry,
 			     const char *symname);
@@ -78,7 +78,7 @@
 extern void			 affs_brelse(struct buffer_head *buf);
 extern unsigned long		 affs_parent_ino(struct inode *dir);
 extern struct inode		*affs_new_inode(const struct inode *dir);
-extern int			 affs_notify_change(struct inode *inode, struct iattr *attr);
+extern int			 affs_notify_change(struct dentry *dentry, struct iattr *attr);
 extern int			 affs_add_entry(struct inode *dir, struct inode *link,
 					  struct inode *inode, struct dentry *dentry, s32 type);
 extern void			 affs_put_inode(struct inode *inode);
@@ -106,5 +106,8 @@
 extern struct inode_operations	 affs_symlink_inode_operations;
 extern struct inode_operations	 affs_chrdev_inode_operations;
 extern struct inode_operations	 affs_blkdev_inode_operations;
+
+extern struct dentry_operations	 affs_dentry_operations;
+extern struct dentry_operations	 affs_dentry_operations_intl;
 
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/linux/byteorder/pdp_endian.h linux/include/linux/byteorder/pdp_endian.h
--- v2.1.78/linux/include/linux/byteorder/pdp_endian.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/byteorder/pdp_endian.h	Mon Jan 12 19:47:22 1998
@@ -0,0 +1,76 @@
+#ifndef _LINUX_BYTEORDER_PDP_ENDIAN_H
+#define _LINUX_BYTEORDER_PDP_ENDIAN_H
+
+/*
+ * Could have been named NUXI-endian, but we use the same name as in glibc.
+ * hopefully only the PDP and its evolutions (old VAXen in compatibility mode)
+ * should ever use this braindead byteorder.
+ * This file *should* work, but has not been tested.
+ *
+ * little-endian is 1234; big-endian is 4321; nuxi/pdp-endian is 3412
+ *
+ * I thought vaxen were NUXI-endian, but was told they were correct-endian
+ * (little-endian), though indeed there existed NUXI-endian machines
+ * (DEC PDP-11 and old VAXen in compatibility mode).
+ * This makes this file a bit useless, but as a proof-of-concept.
+ *
+ * But what does a __u64 look like: is it 34127856 or 78563412 ???
+ * I don't dare imagine! Hence, no 64-bit byteorder support yet.
+ * Hopefully, there 64-bit pdp-endian support shouldn't ever be required.
+ *
+ */
+
+#ifndef __PDP_ENDIAN
+#define __PDP_ENDIAN 3412
+#endif
+#ifndef __PDP_ENDIAN_BITFIELD
+#define __PDP_ENDIAN_BITFIELD
+#endif
+
+#include <linux/byteorder/swab.h>
+#include <linux/byteorder/swabb.h>
+
+#define __constant_htonl(x) ___swahb32((x))
+#define __constant_ntohl(x) ___swahb32((x))
+#define __constant_htons(x) ___swab16((x))
+#define __constant_ntohs(x) ___swab16((x))
+#define __cpu_to_le64(x) I DON'T KNOW
+#define __le64_to_cpu(x) I DON'T KNOW
+#define __cpu_to_le32(x) ___swahw32((x))
+#define __le32_to_cpu(x) ___swahw32((x))
+#define __cpu_to_le16(x) ((__u16)(x)
+#define __le16_to_cpu(x) ((__u16)(x)
+#define __cpu_to_be64(x) I DON'T KNOW
+#define __be64_to_cpu(x) I DON'T KNOW
+#define __cpu_to_be32(x) __swahb32((x))
+#define __be32_to_cpu(x) __swahb32((x))
+#define __cpu_to_be16(x) __swab16((x))
+#define __be16_to_cpu(x) __swab16((x))
+#define __cpu_to_le64p(x) I DON'T KNOW
+#define __le64_to_cpup(x) I DON'T KNOW
+#define __cpu_to_le32p(x) ___swahw32p((x))
+#define __le32_to_cpup(x) ___swahw32p((x))
+#define __cpu_to_le16p(x) (*(__u16*)(x))
+#define __le16_to_cpup(x) (*(__u16*)(x))
+#define __cpu_to_be64p(x) I DON'T KNOW
+#define __be64_to_cpup(x) I DON'T KNOW
+#define __cpu_to_be32p(x) __swahb32p((x))
+#define __be32_to_cpup(x) __swahb32p((x))
+#define __cpu_to_be16p(x) __swab16p((x))
+#define __be16_to_cpup(x) __swab16p((x))
+#define __cpu_to_le64s(x) I DON'T KNOW
+#define __le64_to_cpus(x) I DON'T KNOW
+#define __cpu_to_le32s(x) ___swahw32s((x))
+#define __le32_to_cpus(x) ___swahw32s((x))
+#define __cpu_to_le16s(x) do {} while (0)
+#define __le16_to_cpus(x) do {} while (0)
+#define __cpu_to_be64s(x) I DON'T KNOW
+#define __be64_to_cpus(x) I DON'T KNOW
+#define __cpu_to_be32s(x) __swahb32s((x))
+#define __be32_to_cpus(x) __swahb32s((x))
+#define __cpu_to_be16s(x) __swab16s((x))
+#define __be16_to_cpus(x) __swab16s((x))
+
+#include <linux/byteorder/generic.h>
+
+#endif /* _LINUX_BYTEORDER_PDP_ENDIAN_H */
diff -u --recursive --new-file v2.1.78/linux/include/linux/byteorder/swabb.h linux/include/linux/byteorder/swabb.h
--- v2.1.78/linux/include/linux/byteorder/swabb.h	Wed Dec 10 11:12:45 1997
+++ linux/include/linux/byteorder/swabb.h	Mon Jan 12 19:47:22 1998
@@ -6,7 +6,10 @@
  * SWAp Bytes Bizarrely
  *	swaHHXX[ps]?(foo)
  *
- * Support for obNUXIous vax-endian and other bizarre architectures...
+ * Support for obNUXIous pdp-endian and other bizarre architectures.
+ * Will Linux ever run on such ancient beasts? if not, this file
+ * will be but a programming pearl. Still, it's a reminder that
+ * cpu_to_beXX and beXX_to_cpu should be well distinguished.
  *
  */
 
@@ -15,13 +18,11 @@
  * swahw32	swap 16-bit half-words in a 32-bit word
  * swahb32	swap 8-bit halves of each 16-bit half-word in a 32-bit word
  *
- * No 64-bit support yet. I don't know VAX conventions for long longs.
+ * No 64-bit support yet. I don't know NUXI conventions for long longs.
  * I guarantee it will be a mess when it's there, though :->
- * It will be even worse if there are conflicting 64-bit conventions for vaxen
+ * It will be even worse if there are conflicting 64-bit conventions.
+ * Hopefully, no one ever used 64-bit objects on NUXI machines.
  *
- * Note that if communicating with vax machines becomes useful in some kernel
- * FS driver, we'd have to move that mess into byteorder/swab.h, and
- * create cpu_to_ve32 and suches. Ouch.
  */
 
 
@@ -31,8 +32,8 @@
 		(((__u32)(x) & (__u32)0xffff0000UL) >> 16) ))
 #define ___swahb32(x) \
 	((__u32)( \
-		(((__u32)(x) & (__u32)0x00ff00ffUL) << 16) | \
-		(((__u32)(x) & (__u32)0xff00ff00UL) >> 16) ))
+		(((__u32)(x) & (__u32)0x00ff00ffUL) << 8) | \
+		(((__u32)(x) & (__u32)0xff00ff00UL) >> 8) ))
 
 /*
  * provide defaults when no architecture-specific optimization is detected
diff -u --recursive --new-file v2.1.78/linux/include/linux/byteorder/vax_endian.h linux/include/linux/byteorder/vax_endian.h
--- v2.1.78/linux/include/linux/byteorder/vax_endian.h	Wed Dec 10 11:12:45 1997
+++ linux/include/linux/byteorder/vax_endian.h	Wed Dec 31 16:00:00 1969
@@ -1,69 +0,0 @@
-#ifndef _LINUX_BYTEORDER_VAX_ENDIAN_H
-#define _LINUX_BYTEORDER_VAX_ENDIAN_H
-
-/*
- * Could have been named NUXI-endian
- * This file *should* work, but has not been tested.
- * It's the beginning of what vaxlinux implementers will have to do.
- * I just hope we won't have to write standardized cpu_to_ve32() and suches!
- * little endian is 1234; bigendian is 4321; vaxendian is 3412
- *
- * But what does a __u64 look like: is it 34127856 or 78563412 ???
- * I don't dare imagine! Hence, no 64-bit byteorder support yet.
- *
- */
-
-#ifndef __VAX_ENDIAN
-#define __VAX_ENDIAN 3412
-#endif
-#ifndef __VAX_ENDIAN_BITFIELD
-#define __VAX_ENDIAN_BITFIELD
-#endif
-
-#include <linux/byteorder/swab.h>
-#include <linux/byteorder/swabb.h>
-
-#define __constant_htonl(x) ___swahb32((x))
-#define __constant_ntohl(x) ___swahb32((x))
-#define __constant_htons(x) ___swab16((x))
-#define __constant_ntohs(x) ___swab16((x))
-#define __cpu_to_le64(x) I DON'T KNOW
-#define __le64_to_cpu(x) I DON'T KNOW
-#define __cpu_to_le32(x) ___swahw32((x))
-#define __le32_to_cpu(x) ___swahw32((x))
-#define __cpu_to_le16(x) ((__u16)(x)
-#define __le16_to_cpu(x) ((__u16)(x)
-#define __cpu_to_be64(x) I DON'T KNOW
-#define __be64_to_cpu(x) I DON'T KNOW
-#define __cpu_to_be32(x) __swahb32((x))
-#define __be32_to_cpu(x) __swahb32((x))
-#define __cpu_to_be16(x) __swab16((x))
-#define __be16_to_cpu(x) __swab16((x))
-#define __cpu_to_le64p(x) I DON'T KNOW
-#define __le64_to_cpup(x) I DON'T KNOW
-#define __cpu_to_le32p(x) ___swahw32p((x))
-#define __le32_to_cpup(x) ___swahw32p((x))
-#define __cpu_to_le16p(x) (*(__u16*)(x))
-#define __le16_to_cpup(x) (*(__u16*)(x))
-#define __cpu_to_be64p(x) I DON'T KNOW
-#define __be64_to_cpup(x) I DON'T KNOW
-#define __cpu_to_be32p(x) __swahb32p((x))
-#define __be32_to_cpup(x) __swahb32p((x))
-#define __cpu_to_be16p(x) __swab16p((x))
-#define __be16_to_cpup(x) __swab16p((x))
-#define __cpu_to_le64s(x) I DON'T KNOW
-#define __le64_to_cpus(x) I DON'T KNOW
-#define __cpu_to_le32s(x) ___swahw32s((x))
-#define __le32_to_cpus(x) ___swahw32s((x))
-#define __cpu_to_le16s(x) do {} while (0)
-#define __le16_to_cpus(x) do {} while (0)
-#define __cpu_to_be64s(x) I DON'T KNOW
-#define __be64_to_cpus(x) I DON'T KNOW
-#define __cpu_to_be32s(x) __swahb32s((x))
-#define __be32_to_cpus(x) __swahb32s((x))
-#define __cpu_to_be16s(x) __swab16s((x))
-#define __be16_to_cpus(x) __swab16s((x))
-
-#include <linux/byteorder/generic.h>
-
-#endif /* _LINUX_BYTEORDER_VAX_ENDIAN_H */
diff -u --recursive --new-file v2.1.78/linux/include/linux/coda.h linux/include/linux/coda.h
--- v2.1.78/linux/include/linux/coda.h	Sun Dec 21 22:36:17 1997
+++ linux/include/linux/coda.h	Tue Jan  6 10:00:22 1998
@@ -16,10 +16,13 @@
 #endif 
 
 #ifdef __linux__
+#define cdev_t u_quad_t
 #if !defined(_UQUAD_T_) && (!defined(__GLIBC__) || __GLIBC__ < 2)
 #define _UQUAD_T_ 1
 typedef unsigned long long u_quad_t;
 #endif 
+#else
+#define cdev_t dev_t
 #endif
 
 
@@ -146,7 +149,7 @@
 	struct timespec	va_ctime;	/* time file changed */
 	u_long		va_gen;		/* generation number of file */
 	u_long		va_flags;	/* flags defined for file */
-	dev_t		va_rdev;	/* device the special file represents */
+	cdev_t	        va_rdev;	/* device special file represents */
 	u_quad_t	va_bytes;	/* bytes of disk space held by file */
 	u_quad_t	va_filerev;	/* file modification number */
 };
@@ -237,7 +240,7 @@
 
 struct cfs_open_out {
     struct cfs_out_hdr oh;
-    dev_t	dev;
+    cdev_t	dev;
     ino_t	inode;
 };
 
diff -u --recursive --new-file v2.1.78/linux/include/linux/coda_cnode.h linux/include/linux/coda_cnode.h
--- v2.1.78/linux/include/linux/coda_cnode.h	Sun Dec 21 22:36:17 1997
+++ linux/include/linux/coda_cnode.h	Tue Jan  6 10:00:22 1998
@@ -26,6 +26,7 @@
         u_short            c_mmcount;   /* count of mmappers */
         struct inode      *c_ovp;       /* open vnode pointer */
         struct list_head   c_cnhead;    /* head of cache entries */
+	struct list_head   c_volrootlist; /* list of volroot cnoddes */
 };
 
 /* flags */
@@ -36,7 +37,6 @@
 #define C_ZAPDIR      0x10
 
 void coda_cnode_free(struct cnode *);
-struct cnode *coda_cnode_alloc(void);
 int coda_cnode_make(struct inode **, struct ViceFid *, struct super_block *);
 int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
 struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb);
diff -u --recursive --new-file v2.1.78/linux/include/linux/coda_linux.h linux/include/linux/coda_linux.h
--- v2.1.78/linux/include/linux/coda_linux.h	Sun Dec 21 22:36:17 1997
+++ linux/include/linux/coda_linux.h	Tue Jan  6 10:00:22 1998
@@ -45,6 +45,7 @@
 /* this file:  heloers */
 char *coda_f2s(ViceFid *f, char *s);
 int coda_isroot(struct inode *i);
+int coda_fid_is_volroot(struct ViceFid *);
 int coda_iscontrol(const char *name, size_t length);
 void coda_load_creds(struct coda_cred *cred);
 int coda_mycred(struct coda_cred *);
diff -u --recursive --new-file v2.1.78/linux/include/linux/coda_psdev.h linux/include/linux/coda_psdev.h
--- v2.1.78/linux/include/linux/coda_psdev.h	Sun Dec 21 22:36:17 1997
+++ linux/include/linux/coda_psdev.h	Tue Jan  6 10:00:22 1998
@@ -26,6 +26,7 @@
 	struct vcomm *      sbi_vcomm;
 	struct inode *      sbi_root;
 	struct list_head    sbi_cchead;
+	struct list_head    sbi_volroothead;
 };
 
 /* communication pending/processing queues queues */
diff -u --recursive --new-file v2.1.78/linux/include/linux/dcache.h linux/include/linux/dcache.h
--- v2.1.78/linux/include/linux/dcache.h	Tue Jan  6 09:37:38 1998
+++ linux/include/linux/dcache.h	Mon Jan 12 14:38:40 1998
@@ -71,9 +71,10 @@
 
 struct dentry_operations {
 	int (*d_revalidate)(struct dentry *);
-	int (*d_hash) (struct dentry *,struct qstr *);
-	int (*d_compare) (struct dentry *,struct qstr *, struct qstr *);
+	int (*d_hash) (struct dentry *, struct qstr *);
+	int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
 	void (*d_delete)(struct dentry *);
+	void (*d_iput)(struct dentry *, struct inode *);
 	void (*d_release)(struct dentry *);
 };
 
diff -u --recursive --new-file v2.1.78/linux/include/linux/elf.h linux/include/linux/elf.h
--- v2.1.78/linux/include/linux/elf.h	Thu May 29 21:53:10 1997
+++ linux/include/linux/elf.h	Mon Jan 12 14:52:46 1998
@@ -127,7 +127,8 @@
 #define AT_EUID   12	/* effective uid */
 #define AT_GID    13	/* real gid */
 #define AT_EGID   14	/* effective gid */
-
+#define AT_PLATFORM 15  /* string identifying cpu for optimizations */
+#define AT_HWCAP  16    /* arch dependant hints at cpu capabilities */
 
 typedef struct dynamic{
   Elf32_Sword d_tag;
@@ -203,6 +204,14 @@
 #define R_SPARC_5		44
 #define R_SPARC_6		45
 
+/* Bits present in AT_HWCAP, primarily for Sparc32.  */
+
+#define HWCAP_SPARC_FLUSH       1       /* The cpu supports flush insn.  */
+#define HWCAP_SPARC_STBAR       2
+#define HWCAP_SPARC_SWAP        4
+#define HWCAP_SPARC_MULDIV      8
+
+
 /*
  * 68k ELF relocation types
  */
@@ -448,8 +457,6 @@
   Elf32_Word n_descsz;	/* Content size */
   Elf32_Word n_type;	/* Content type */
 } Elf64_Nhdr;
-
-#define ELF_START_MMAP 0x80000000
 
 #if ELF_CLASS == ELFCLASS32
 
diff -u --recursive --new-file v2.1.78/linux/include/linux/etherdevice.h linux/include/linux/etherdevice.h
--- v2.1.78/linux/include/linux/etherdevice.h	Mon Dec  1 12:04:14 1997
+++ linux/include/linux/etherdevice.h	Mon Jan 12 19:24:34 1998
@@ -35,8 +35,7 @@
 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 neighbour *neigh,
+extern int		eth_header_cache(struct neighbour *neigh,
 					 struct hh_cache *hh);
 extern int		eth_header_parse(struct sk_buff *skb,
 					 unsigned char *haddr);
diff -u --recursive --new-file v2.1.78/linux/include/linux/fat_cvf.h linux/include/linux/fat_cvf.h
--- v2.1.78/linux/include/linux/fat_cvf.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/fat_cvf.h	Thu Jan  8 14:02:41 1998
@@ -0,0 +1,49 @@
+#ifndef _FAT_CVF
+#define _FAT_CVF
+
+#define CVF_USE_READPAGE  0x0001
+
+struct cvf_format
+{ int cvf_version;
+  char* cvf_version_text;
+  unsigned long flags;
+  int (*detect_cvf) (struct super_block*sb);
+  int (*mount_cvf) (struct super_block*sb,char*options);
+  int (*unmount_cvf) (struct super_block*sb);
+  struct buffer_head* (*cvf_bread) (struct super_block*sb,int block);
+  struct buffer_head* (*cvf_getblk) (struct super_block*sb,int block);
+  void (*cvf_brelse) (struct super_block *sb,struct buffer_head *bh);
+  void (*cvf_mark_buffer_dirty) (struct super_block *sb,
+                              struct buffer_head *bh,
+                              int dirty_val);
+  void (*cvf_set_uptodate) (struct super_block *sb,
+                         struct buffer_head *bh,
+                         int val);
+  int (*cvf_is_uptodate) (struct super_block *sb,struct buffer_head *bh);
+  void (*cvf_ll_rw_block) (struct super_block *sb,
+                        int opr,
+                        int nbreq,
+                        struct buffer_head *bh[32]);
+  int (*fat_access) (struct super_block *sb,int nr,int new_value);
+  int (*cvf_statfs) (struct super_block *sb,struct statfs *buf, int bufsiz);
+  int (*cvf_bmap) (struct inode *inode,int block);
+  int (*cvf_smap) (struct inode *inode,int sector);
+  ssize_t (*cvf_file_read) ( struct file *, char *, size_t, loff_t *);
+  ssize_t (*cvf_file_write) ( struct file *, const char *, size_t, loff_t *);
+  int (*cvf_mmap) (struct file *, struct vm_area_struct *);
+  int (*cvf_readpage) (struct inode *, struct page *);
+  int (*cvf_writepage) (struct inode *, struct page *);
+  int (*cvf_dir_ioctl) (struct inode * inode, struct file * filp,
+                        unsigned int cmd, unsigned long arg);
+  void (*zero_out_cluster) (struct inode*, int clusternr);
+};
+
+int register_cvf_format(struct cvf_format*cvf_format);
+int unregister_cvf_format(struct cvf_format*cvf_format);
+void dec_cvf_format_use_count_by_version(int version);
+int detect_cvf(struct super_block*sb,char*force);
+
+extern struct cvf_format *cvf_formats[];
+extern int cvf_format_use_count[];
+
+#endif
diff -u --recursive --new-file v2.1.78/linux/include/linux/hdreg.h linux/include/linux/hdreg.h
--- v2.1.78/linux/include/linux/hdreg.h	Fri Dec 19 15:53:04 1997
+++ linux/include/linux/hdreg.h	Mon Jan 12 15:31:09 1998
@@ -62,10 +62,24 @@
 #define WIN_READDMA		0xc8	/* read sectors using DMA transfers */
 #define WIN_WRITEDMA		0xca	/* write sectors using DMA transfers */
 
+#define WIN_SMART		0xb0	/* self-monitoring and reporting */
+
 /* Additional drive command codes used by ATAPI devices. */
 #define WIN_PIDENTIFY		0xA1	/* identify ATAPI device	*/
 #define WIN_SRST		0x08	/* ATAPI soft reset command */
 #define WIN_PACKETCMD		0xa0	/* Send a packet command. */
+
+/* WIN_SMART sub-commands */
+
+#define SMART_READ_VALUES	0xd0
+#define SMART_READ_THRESHOLDS	0xd1
+#define SMART_AUTOSAVE		0xd2
+#define SMART_SAVE		0xd3
+#define SMART_IMMEDIATE_OFFLINE	0xd4
+#define SMART_ENABLE		0xd8
+#define SMART_DISABLE		0xd9
+#define SMART_STATUS		0xda
+#define SMART_AUTO_OFFLINE	0xdb
 
 /* Bits for HD_ERROR */
 #define MARK_ERR	0x01	/* Bad address mark */
diff -u --recursive --new-file v2.1.78/linux/include/linux/hfs_fs.h linux/include/linux/hfs_fs.h
--- v2.1.78/linux/include/linux/hfs_fs.h	Tue Jan  6 09:37:38 1998
+++ linux/include/linux/hfs_fs.h	Mon Jan 12 16:46:33 1998
@@ -208,8 +208,6 @@
 
 #ifdef __KERNEL__
 
-#if defined(CONFIG_HFS_FS) || defined(CONFIG_HFS_FS_MODULE)
-
 typedef ssize_t hfs_rwret_t;
 typedef size_t hfs_rwarg_t;
 
@@ -221,7 +219,6 @@
 struct hfs_cat_entry;
 extern struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *,
 					 const struct hfs_cat_key *);
-extern void hfs_tolower(unsigned char *, int);
 
 /* dir.c */
 extern hfs_rwret_t hfs_dir_read(struct file *, char *, hfs_rwarg_t,
@@ -280,7 +277,7 @@
 
 /* inode.c */
 extern void hfs_put_inode(struct inode *);
-extern int hfs_notify_change(struct inode *, struct iattr *);
+extern int hfs_notify_change(struct dentry *, struct iattr *);
 extern struct inode *hfs_iget(struct hfs_cat_entry *, ino_t, struct dentry *);
 
 extern void hfs_cap_ifill(struct inode *, ino_t);
@@ -304,6 +301,7 @@
 extern int hfs_mac2eight(char *, const struct hfs_name *);
 extern int hfs_mac2alpha(char *, const struct hfs_name *);
 extern int hfs_mac2triv(char *, const struct hfs_name *);
+extern void hfs_tolower(unsigned char *, int);
 
 #define	HFS_I(X)	(&((X)->u.hfs_i))
 #define	HFS_SB(X)	(&((X)->u.hfs_sb))
@@ -322,41 +320,5 @@
 	return len;
 }
 
-static __inline__ struct dentry 
-*hfs_lookup_dentry(const char *name, const int len, 
-		   struct dentry *base)
-{
-  struct qstr this;
-
-  this.name = name;
-  this.len = len;
-  this.hash = full_name_hash(name, len);
-
-  return d_lookup(base, &this);
-}
-
-/* drop a dentry for one of the special subdirectories */
-static __inline__ void hfs_drop_special(const struct hfs_name *name,
-					struct dentry *base,
-					struct dentry *dentry)
-{
-  struct dentry *dparent, *de;
-  
-  dparent = hfs_lookup_dentry(name->Name, name->Len, base);
-  if (dparent) {
-    de = hfs_lookup_dentry(dentry->d_name.name, dentry->d_name.len,
-			   dparent);
-    dput(dparent);
-
-    if (de) {
-      if (!de->d_inode)
-	d_drop(de);
-      dput(de);
-    }
-  }
-}
-
-#endif
 #endif /* __KERNEL__ */
-
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/linux/if.h linux/include/linux/if.h
--- v2.1.78/linux/include/linux/if.h	Fri Jan  2 14:37:02 1998
+++ linux/include/linux/if.h	Mon Jan 12 15:28:18 1998
@@ -43,7 +43,7 @@
 
 #define IFF_PORTSEL	0x2000          /* can set media type		*/
 #define IFF_AUTOMEDIA	0x4000		/* auto media select active	*/
-
+#define IFF_NODYNARP	0x8000		/* use static ARP only (HIPPI)	*/
 /*
  * The ifaddr structure contains information about one address
  * of an interface.  They are maintained by the different address
diff -u --recursive --new-file v2.1.78/linux/include/linux/if_arp.h linux/include/linux/if_arp.h
--- v2.1.78/linux/include/linux/if_arp.h	Sat Nov 29 11:25:12 1997
+++ linux/include/linux/if_arp.h	Mon Jan 12 16:44:26 1998
@@ -96,7 +96,6 @@
 #define ATF_NETMASK     0x20            /* want to use a netmask (only
 					   for proxy entries) */
 #define ATF_DONTPUB	0x40		/* don't answer this addresses	*/
-#define ATF_MAGIC	0x80		/* automatically added entry	*/
 
 /*
  *	This structure defines an ethernet arp header.
@@ -120,22 +119,6 @@
 	unsigned char		ar_tip[4];		/* target IP address		*/
 #endif
 
-};
-
-/* Support for the user space arp daemon, arpd */
-
-#define ARPD_UPDATE	0x01
-#define ARPD_LOOKUP	0x02
-#define ARPD_FLUSH	0x03
-
-struct arpd_request
-{
-	unsigned short	req;			/* request type */
-	__u32		ip;			/* ip address of entry */
-	unsigned long	dev;			/* Device entry is tied to */
-	unsigned long	stamp;
-	unsigned long	updated;
-	unsigned char	ha[MAX_ADDR_LEN];	/* Hardware address */
 };
 
 #endif	/* _LINUX_IF_ARP_H */
diff -u --recursive --new-file v2.1.78/linux/include/linux/if_packet.h linux/include/linux/if_packet.h
--- v2.1.78/linux/include/linux/if_packet.h	Sat Nov 29 11:25:12 1997
+++ linux/include/linux/if_packet.h	Mon Jan 12 15:28:18 1998
@@ -25,8 +25,9 @@
 #define PACKET_BROADCAST	1		/* To all		*/
 #define PACKET_MULTICAST	2		/* To group		*/
 #define PACKET_OTHERHOST	3		/* To someone else 	*/
-#define PACKET_OUTGOING		4		/* Originated by us	*/	
-#define PACKET_NDISC		17		/* Outgoing NDISC packet*/
+#define PACKET_OUTGOING		4		/* Originated by us	*/
+#define PACKET_LOOPBACK		5
+#define PACKET_FASTROUTE	6
 
 /* Packet socket options */
 
diff -u --recursive --new-file v2.1.78/linux/include/linux/if_ppp.h linux/include/linux/if_ppp.h
--- v2.1.78/linux/include/linux/if_ppp.h	Tue Dec 23 16:31:00 1997
+++ linux/include/linux/if_ppp.h	Mon Jan 12 15:28:18 1998
@@ -1,4 +1,4 @@
-/*	$Id: if_ppp.h,v 1.3 1995/06/12 11:36:50 paulus Exp $	*/
+/*	$Id: if_ppp.h,v 1.2 1998/01/01 15:05:12 phil Exp $	*/
 
 /*
  * if_ppp.h - Point-to-Point Protocol definitions.
diff -u --recursive --new-file v2.1.78/linux/include/linux/if_shaper.h linux/include/linux/if_shaper.h
--- v2.1.78/linux/include/linux/if_shaper.h	Thu Jul 17 10:06:08 1997
+++ linux/include/linux/if_shaper.h	Mon Jan 12 15:28:18 1998
@@ -33,8 +33,7 @@
 		void *saddr,
 		unsigned len);
 	int  (*rebuild_header)(struct sk_buff *skb);
-	int (*hard_header_cache)(struct dst_entry *dst, struct neighbour *neigh,
-		struct hh_cache *hh);
+	int (*hard_header_cache)(struct neighbour *neigh, struct hh_cache *hh);
 	void (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char *  haddr);
 	struct net_device_stats* (*get_stats)(struct device *dev);
 	struct wait_queue *wait_queue;
diff -u --recursive --new-file v2.1.78/linux/include/linux/igmp.h linux/include/linux/igmp.h
--- v2.1.78/linux/include/linux/igmp.h	Mon Dec  1 12:04:14 1997
+++ linux/include/linux/igmp.h	Mon Jan 12 15:28:18 1998
@@ -98,6 +98,7 @@
 	char			tm_running;
 	char			reporter;
 	char			unsolicit_count;
+	char			loaded;
 };
 
 extern __inline__ int ip_check_mc(struct device *dev, u32 mc_addr)
diff -u --recursive --new-file v2.1.78/linux/include/linux/in_route.h linux/include/linux/in_route.h
--- v2.1.78/linux/include/linux/in_route.h	Mon Dec  1 12:04:14 1997
+++ linux/include/linux/in_route.h	Mon Jan 12 15:28:18 1998
@@ -12,7 +12,7 @@
 #define RTCF_DIRECTDST	0x00020000
 #define RTCF_REDIRECTED	0x00040000
 
-#define RTCF_VALVE	0x00200000
+#define RTCF_FAST	0x00200000
 #define RTCF_MASQ	0x00400000
 #define RTCF_SNAT	0x00800000
 #define RTCF_DOREDIRECT 0x01000000
diff -u --recursive --new-file v2.1.78/linux/include/linux/inetdevice.h linux/include/linux/inetdevice.h
--- v2.1.78/linux/include/linux/inetdevice.h	Mon Dec  1 12:04:14 1997
+++ linux/include/linux/inetdevice.h	Mon Jan 12 15:28:18 1998
@@ -22,24 +22,17 @@
 	struct ip_mc_list	*mc_list;	/* IP multicast filter chain    */
 	unsigned long		mr_v1_seen;
 	unsigned		flags;
+	struct neigh_parms	*arp_parms;
 };
 
 
 #define IN_DEV_RPFILTER(in_dev)	(ipv4_config.rfc1812_filter && ((in_dev)->flags&IFF_IP_RPFILTER))
 #define IN_DEV_MFORWARD(in_dev)	(ipv4_config.multicast_route && ((in_dev)->flags&IFF_IP_MFORWARD))
-#define IN_DEV_PROXY_ARP(in_dev)	((in_dev)->flags&IFF_IP_PROXYARP)
-
-#if 1
-#define IN_DEV_FORWARD(in_dev)		(IS_ROUTER)
-#define IN_DEV_RX_REDIRECTS(in_dev)	(ipv4_config.accept_redirects)
-#define IN_DEV_TX_REDIRECTS(in_dev)	(1)
-#define IN_DEV_SHARED_MEDIA(in_dev)	(ipv4_config.rfc1620_redirects)
-#else
-#define IN_DEV_FORWARD(in_dev)	(ipv4_config.ip_forwarding==1 && ((in_dev)->flags&IFF_IP_FORWARD))
-#define IN_DEV_RX_REDIRECTS(in_dev)	((in_dev)->flags&IFF_IP_RXREDIRECTS)
-#define IN_DEV_TX_REDIRECTS(in_dev)	((in_dev)->flags&IFF_IP_TXREDIRECTS)
-#define IN_DEV_SHARED_MEDIA(in_dev)	((in_dev)->flags&IFF_IP_SHAREDMEDIA)
-#endif
+#define IN_DEV_PROXY_ARP(in_dev)	(ipv4_config.proxy_arp || (in_dev)->flags&IFF_IP_PROXYARP)
+#define IN_DEV_FORWARD(in_dev)		(IS_ROUTER || ((in_dev)->flags&IFF_IP_FORWARD))
+#define IN_DEV_SHARED_MEDIA(in_dev)	(ipv4_config.rfc1620_redirects || (in_dev)->flags&IFF_IP_SHAREDMEDIA)
+#define IN_DEV_RX_REDIRECTS(in_dev)	(ipv4_config.accept_redirects || (in_dev)->flags&IFF_IP_RXREDIRECTS)
+#define IN_DEV_TX_REDIRECTS(in_dev)	(/*ipv4_config.send_redirects ||*/ (in_dev)->flags&IFF_IP_TXREDIRECTS)
 
 struct in_ifaddr
 {
diff -u --recursive --new-file v2.1.78/linux/include/linux/ip_fw.h linux/include/linux/ip_fw.h
--- v2.1.78/linux/include/linux/ip_fw.h	Fri Dec 19 15:53:04 1997
+++ linux/include/linux/ip_fw.h	Mon Jan 12 19:25:10 1998
@@ -126,8 +126,8 @@
 #define IP_FW_IN		1
 #define IP_FW_OUT		2
 #define IP_FW_ACCT		3
-#define IP_FW_MASQ		4
-#define IP_FW_CHAINS		5	/* total number of ip_fw chains */
+#define IP_FW_CHAINS		4	/* total number of ip_fw chains */
+#define IP_FW_MASQ		5
 
 #define IP_FW_INSERT		(IP_FW_BASE_CTL)
 #define IP_FW_APPEND		(IP_FW_BASE_CTL+1)
@@ -173,6 +173,11 @@
 #define IP_FW_MASQ_DEL		(IP_FW_DELETE | (IP_FW_MASQ << IP_FW_SHIFT))
 #define IP_FW_MASQ_FLUSH  	(IP_FW_FLUSH  | (IP_FW_MASQ << IP_FW_SHIFT))
 
+#define IP_FW_MASQ_INSERT	(IP_FW_INSERT | (IP_FW_MASQ << IP_FW_SHIFT))
+#define IP_FW_MASQ_ADD		(IP_FW_APPEND | (IP_FW_MASQ << IP_FW_SHIFT))
+#define IP_FW_MASQ_DEL		(IP_FW_DELETE | (IP_FW_MASQ << IP_FW_SHIFT))
+#define IP_FW_MASQ_FLUSH  	(IP_FW_FLUSH  | (IP_FW_MASQ << IP_FW_SHIFT))
+
 struct ip_fwpkt
 {
 	struct iphdr fwp_iph;			/* IP header */
@@ -234,10 +239,14 @@
 #ifdef CONFIG_IP_MASQUERADE
 extern int ip_masq_ctl(int, void *, int);
 #endif
+#ifdef CONFIG_IP_MASQUERADE
+extern int ip_masq_ctl(int, void *, int);
+#endif
 
 extern int ip_fw_chk(struct iphdr *, struct device *, __u16 *, struct ip_fw *, int, int);
 extern void ip_fw_init(void);
 #endif /* KERNEL */
+
 
 
 #endif /* _IP_FW_H */
diff -u --recursive --new-file v2.1.78/linux/include/linux/ipv6_route.h linux/include/linux/ipv6_route.h
--- v2.1.78/linux/include/linux/ipv6_route.h	Sat Nov 29 11:25:12 1997
+++ linux/include/linux/ipv6_route.h	Mon Jan 12 15:28:19 1998
@@ -44,6 +44,5 @@
 #define RTMSG_DELDEVICE		0x12
 #define RTMSG_NEWROUTE		0x21
 #define RTMSG_DELROUTE		0x22
-#define RTMSG_AR_FAILED		0x51	/* Address Resolution failed	*/
 
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/linux/kernel_stat.h linux/include/linux/kernel_stat.h
--- v2.1.78/linux/include/linux/kernel_stat.h	Mon Jun 30 15:23:12 1997
+++ linux/include/linux/kernel_stat.h	Mon Jan 12 14:46:26 1998
@@ -2,6 +2,7 @@
 #define _LINUX_KERNEL_STAT_H
 
 #include <asm/irq.h>
+#include <linux/tasks.h>
 
 /*
  * 'kernel_stat.h' contains the definitions needed for doing
@@ -12,7 +13,10 @@
 #define DK_NDRIVE 4
 
 struct kernel_stat {
-	unsigned int cpu_user, cpu_nice, cpu_system;
+	unsigned int cpu_user, cpu_nice, cpu_system;	
+	unsigned int per_cpu_user[NR_CPUS],
+	             per_cpu_nice[NR_CPUS],
+	             per_cpu_system[NR_CPUS];
 	unsigned int dk_drive[DK_NDRIVE];
 	unsigned int dk_drive_rio[DK_NDRIVE];
 	unsigned int dk_drive_wio[DK_NDRIVE];
diff -u --recursive --new-file v2.1.78/linux/include/linux/minix_fs.h linux/include/linux/minix_fs.h
--- v2.1.78/linux/include/linux/minix_fs.h	Tue Sep 23 16:48:50 1997
+++ linux/include/linux/minix_fs.h	Tue Jan  6 12:26:59 1998
@@ -95,7 +95,7 @@
 extern int minix_unlink(struct inode * dir, struct dentry *dentry);
 extern int minix_symlink(struct inode * inode, struct dentry *dentry,
 	const char * symname);
-extern int minix_link(struct inode * oldinode, struct inode * dir, struct dentry *dentry);
+extern int minix_link(struct dentry * old_dentry, struct inode * dir, struct dentry *dentry);
 extern int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int rdev);
 extern int minix_rename(struct inode * old_dir, struct dentry *old_dentry,
 			struct inode * new_dir, struct dentry *new_dentry);
diff -u --recursive --new-file v2.1.78/linux/include/linux/miscdevice.h linux/include/linux/miscdevice.h
--- v2.1.78/linux/include/linux/miscdevice.h	Mon Nov 17 18:47:22 1997
+++ linux/include/linux/miscdevice.h	Mon Jan 12 15:12:42 1998
@@ -9,6 +9,8 @@
 #define ATARIMOUSE_MINOR 5
 #define SUN_MOUSE_MINOR 6
 #define PC110PAD_MINOR 9
+#define WATCHDOG_MINOR		130	/* Watchdog timer     */
+#define TEMP_MINOR		131	/* Temperature Sensor */
 #define RTC_MINOR 135
 #define SUN_OPENPROM_MINOR 139
 #define NVRAM_MINOR 144
diff -u --recursive --new-file v2.1.78/linux/include/linux/mm.h linux/include/linux/mm.h
--- v2.1.78/linux/include/linux/mm.h	Fri Dec 19 15:53:04 1997
+++ linux/include/linux/mm.h	Mon Jan 12 16:43:16 1998
@@ -123,7 +123,6 @@
 	struct wait_queue *wait;
 	struct page **pprev_hash;
 	struct buffer_head * buffers;
-	unsigned long pg_swap_entry;
 	unsigned long map_nr;	/* page->map_nr == page - mem_map */
 } mem_map_t;
 
diff -u --recursive --new-file v2.1.78/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
--- v2.1.78/linux/include/linux/msdos_fs.h	Tue Jan  6 09:37:38 1998
+++ linux/include/linux/msdos_fs.h	Mon Jan 12 16:46:33 1998
@@ -161,8 +161,9 @@
 	__u8    name11_12[4];	/* last 2 characters in name */
 };
 
-struct slot_info {
+struct vfat_slot_info {
 	int is_long;		       /* was the found entry long */
+	int is_alias;		       /* was the found entry an alias */
 	int long_slots;		       /* number of long slots in filename */
 	int total_slots;	       /* total slots (long and short) */
 	loff_t longname_offset;	       /* dir offset for longname start */
@@ -248,12 +249,14 @@
 /* file.c */
 extern struct inode_operations fat_file_inode_operations;
 extern struct inode_operations fat_file_inode_operations_1024;
+extern struct inode_operations fat_file_inode_operations_readpage;
 extern ssize_t fat_file_read(struct file *, char *, size_t, loff_t *);
 extern ssize_t fat_file_write(struct file *, const char *, size_t, loff_t *);
 extern void fat_truncate(struct inode *inode);
 
 /* mmap.c */
 extern int fat_mmap(struct file *, struct vm_area_struct *);
+extern int fat_readpage(struct inode *, struct page *);
 
 
 /* vfat.c */
diff -u --recursive --new-file v2.1.78/linux/include/linux/msdos_fs_sb.h linux/include/linux/msdos_fs_sb.h
--- v2.1.78/linux/include/linux/msdos_fs_sb.h	Sat Oct 25 02:44:18 1997
+++ linux/include/linux/msdos_fs_sb.h	Thu Jan  8 14:02:41 1998
@@ -1,5 +1,6 @@
 #ifndef _MSDOS_FS_SB
 #define _MSDOS_FS_SB
+#include<linux/fat_cvf.h>
 
 /*
  * MS-DOS file system in-core superblock data
@@ -47,6 +48,8 @@
 	struct fat_mount_options options;
 	struct nls_table *nls_disk;  /* Codepage used on disk */
 	struct nls_table *nls_io;    /* Charset used for input and display */
+	struct cvf_format* cvf_format;
+	void* private_data;	
 };
 
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/linux/net.h linux/include/linux/net.h
--- v2.1.78/linux/include/linux/net.h	Mon Dec  1 12:04:15 1997
+++ linux/include/linux/net.h	Mon Jan 12 15:28:19 1998
@@ -137,7 +137,9 @@
 extern int	sock_readv_writev(int type, struct inode * inode, struct file * file,
 				  const struct iovec * iov, long count, long size);
 
-int net_ratelimit(void);
+extern int	net_ratelimit(void);
+extern unsigned long net_random(void);
+extern void net_srandom(unsigned long);
 
 #endif /* __KERNEL__ */
 #endif	/* _LINUX_NET_H */
diff -u --recursive --new-file v2.1.78/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.1.78/linux/include/linux/netdevice.h	Mon Dec  1 12:04:15 1997
+++ linux/include/linux/netdevice.h	Mon Jan 12 16:44:25 1998
@@ -31,6 +31,12 @@
 
 #include <asm/atomic.h>
 
+#ifdef __KERNEL__
+#ifdef CONFIG_NET_PROFILE
+#include <net/profile.h>
+#endif
+#endif
+
 /*
  *	For future expansion when we will have different priorities. 
  */
@@ -60,6 +66,7 @@
 #endif
 
 struct neighbour;
+struct sk_buff;
 
 /*
  *	We tag multicasts with these structures.
@@ -67,10 +74,11 @@
  
 struct dev_mc_list
 {	
-	struct dev_mc_list *next;
-	char dmi_addr[MAX_ADDR_LEN];
-	unsigned short dmi_addrlen;
-	unsigned short dmi_users;
+	struct dev_mc_list	*next;
+	__u8			dmi_addr[MAX_ADDR_LEN];
+	unsigned char		dmi_addrlen;
+	int			dmi_users;
+	int			dmi_gusers;
 };
 
 struct hh_cache
@@ -78,7 +86,7 @@
 	struct hh_cache *hh_next;	/* Next entry			     */
 	atomic_t	hh_refcnt;	/* number of users                   */
 	unsigned short  hh_type;	/* protocol identifier, f.e ETH_P_IP */
-	char		hh_uptodate;	/* hh_data is valid                  */
+	int		(*hh_output)(struct sk_buff *skb);
 	/* cached hardware header; allow for machine alignment needs.        */
 	unsigned long	hh_data[16/sizeof(unsigned long)];
 };
@@ -118,6 +126,16 @@
 	
 };
 
+#ifdef CONFIG_NET_FASTROUTE
+struct net_fastroute_stats
+{
+	int		hits;
+	int		succeed;
+	int		deferred;
+	int		latency_reduction;
+};
+#endif
+
 /* Media selection options. */
 enum {
         IF_PORT_UNKNOWN = 0,
@@ -260,8 +278,7 @@
 	int			(*set_config)(struct device *dev,
 					      struct ifmap *map);
 #define HAVE_HEADER_CACHE
-	int			(*hard_header_cache)(struct dst_entry *dst,
-						     struct neighbour *neigh,
+	int			(*hard_header_cache)(struct neighbour *neigh,
 						     struct hh_cache *hh);
 	void			(*header_cache_update)(struct hh_cache *hh,
 						       struct device *dev,
@@ -271,6 +288,18 @@
 
 	int			(*hard_header_parse)(struct sk_buff *skb,
 						     unsigned char *haddr);
+	int			(*neigh_setup)(struct neighbour *n);
+	int			(*accept_fastpath)(struct device *, struct dst_entry*);
+
+#ifdef CONFIG_NET_FASTROUTE
+	/* Really, this semaphore may be necessary and for not fastroute code;
+	   f.e. SMP??
+	 */
+	int			tx_semaphore;
+#define NETDEV_FASTROUTE_HMASK 0xF
+	/* Semi-private data. Keep it at the end of device struct. */
+	struct dst_entry	*fastpath[NETDEV_FASTROUTE_HMASK+1];
+#endif
 };
 
 
@@ -316,7 +345,6 @@
 #define HAVE_NETIF_RX 1
 extern void		netif_rx(struct sk_buff *skb);
 extern void		net_bh(void);
-extern void		dev_tint(struct device *dev);
 extern int		dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
 extern int		dev_ioctl(unsigned int cmd, void *);
 extern int		dev_change_flags(struct device *, unsigned);
@@ -383,13 +411,26 @@
 extern void		unregister_trdev(struct device *dev);
 /* Functions used for multicast support */
 extern void		dev_mc_upload(struct device *dev);
-extern void 		dev_mc_delete(struct device *dev, void *addr, int alen, int all);
-extern void		dev_mc_add(struct device *dev, void *addr, int alen, int newonly);
+extern int 		dev_mc_delete(struct device *dev, void *addr, int alen, int all);
+extern int		dev_mc_add(struct device *dev, void *addr, int alen, int newonly);
 extern void		dev_mc_discard(struct device *dev);
 extern void		dev_set_promiscuity(struct device *dev, int inc);
 extern void		dev_set_allmulti(struct device *dev, int inc);
 /* Load a device via the kerneld */
 extern void		dev_load(const char *name);
+extern void		dev_mcast_init(void);
+extern int		netdev_register_fc(struct device *dev, void (*stimul)(struct device *dev));
+extern void		netdev_unregister_fc(int bit);
+extern int		netdev_dropping;
+extern int		netdev_max_backlog;
+extern atomic_t		netdev_rx_dropped;
+extern unsigned long	netdev_fc_xoff;
+#ifdef CONFIG_NET_FASTROUTE
+extern int		netdev_fastroute;
+extern int		netdev_fastroute_obstacles;
+extern void		dev_clear_fastroute(struct device *dev);
+extern struct net_fastroute_stats dev_fastroute_stat;
+#endif
 
 
 #endif /* __KERNEL__ */
diff -u --recursive --new-file v2.1.78/linux/include/linux/netlink.h linux/include/linux/netlink.h
--- v2.1.78/linux/include/linux/netlink.h	Mon Dec  1 12:04:15 1997
+++ linux/include/linux/netlink.h	Mon Jan 12 15:28:19 1998
@@ -119,6 +119,7 @@
 	struct nlmsghdr	*nlh;
 	int		(*dump)(struct sk_buff * skb, struct netlink_callback *cb);
 	int		(*done)(struct netlink_callback *cb);
+	int		family;
 	long		args[4];
 };
 
diff -u --recursive --new-file v2.1.78/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- v2.1.78/linux/include/linux/nfs_fs.h	Tue Jan  6 09:37:38 1998
+++ linux/include/linux/nfs_fs.h	Mon Jan 12 16:43:34 1998
@@ -13,7 +13,7 @@
 #include <linux/sched.h>
 #include <linux/in.h>
 
-#include <linux/sunrpc/debug.h>
+#include <linux/sunrpc/sched.h>
 #include <linux/nfs.h>
 #include <linux/nfs_mount.h>
 
@@ -78,8 +78,6 @@
 
 #define NFS_FLAGS(inode)		((inode)->u.nfs_i.flags)
 #define NFS_REVALIDATING(inode)		(NFS_FLAGS(inode) & NFS_INO_REVALIDATE)
-
-#define NFS_RENAMED_DIR(inode)		((inode)->u.nfs_i.silly_inode)
 #define NFS_WRITEBACK(inode)		((inode)->u.nfs_i.writeback)
 
 /*
@@ -90,6 +88,46 @@
 #ifdef __KERNEL__
 
 /*
+ * This struct describes a file region to be written.
+ * It's kind of a pity we have to keep all these lists ourselves, rather
+ * than sticking an extra pointer into struct page.
+ */
+struct nfs_wreq {
+	struct rpc_listitem	wb_list;	/* linked list of req's */
+	struct rpc_task		wb_task;	/* RPC task */
+	struct dentry *		wb_dentry;	/* dentry referenced */
+	struct inode *		wb_inode;	/* inode referenced */
+	struct page *		wb_page;	/* page to be written */
+	unsigned int		wb_offset;	/* offset within page */
+	unsigned int		wb_bytes;	/* dirty range */
+	pid_t			wb_pid;		/* owner process */
+	unsigned short		wb_flags;	/* status flags */
+
+	struct nfs_writeargs *	wb_args;	/* NFS RPC stuff */
+	struct nfs_fattr *	wb_fattr;	/* file attributes */
+};
+#define wb_status		wb_task.tk_status
+
+#define WB_NEXT(req)		((struct nfs_wreq *) ((req)->wb_list.next))
+
+/*
+ * Various flags for wb_flags
+ */
+#define NFS_WRITE_WANTLOCK	0x0001	/* needs to lock page */
+#define NFS_WRITE_LOCKED	0x0002	/* holds lock on page */
+#define NFS_WRITE_CANCELLED	0x0004	/* has been cancelled */
+#define NFS_WRITE_UNCOMMITTED	0x0008	/* written but uncommitted (NFSv3) */
+#define NFS_WRITE_INVALIDATE	0x0010	/* invalidate after write */
+#define NFS_WRITE_INPROGRESS	0x0020	/* RPC call in progress */
+
+#define WB_WANTLOCK(req)	((req)->wb_flags & NFS_WRITE_WANTLOCK)
+#define WB_HAVELOCK(req)	((req)->wb_flags & NFS_WRITE_LOCKED)
+#define WB_CANCELLED(req)	((req)->wb_flags & NFS_WRITE_CANCELLED)
+#define WB_UNCOMMITTED(req)	((req)->wb_flags & NFS_WRITE_UNCOMMITTED)
+#define WB_INVALIDATE(req)	((req)->wb_flags & NFS_WRITE_INVALIDATE)
+#define WB_INPROGRESS(req)	((req)->wb_flags & NFS_WRITE_INPROGRESS)
+
+/*
  * linux/fs/nfs/proc.c
  */
 extern int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
@@ -171,6 +209,7 @@
  * linux/fs/nfs/write.c
  */
 extern int  nfs_writepage(struct dentry *, struct page *);
+extern int  nfs_find_dentry_request(struct inode *, struct dentry *);
 extern int  nfs_check_failed_request(struct inode *);
 extern int  nfs_check_error(struct inode *);
 extern int  nfs_flush_dirty_pages(struct inode *, pid_t, off_t, off_t);
diff -u --recursive --new-file v2.1.78/linux/include/linux/nfs_fs_sb.h linux/include/linux/nfs_fs_sb.h
--- v2.1.78/linux/include/linux/nfs_fs_sb.h	Mon Jul  7 16:02:01 1997
+++ linux/include/linux/nfs_fs_sb.h	Mon Jan 12 15:31:09 1998
@@ -5,8 +5,7 @@
 #include <linux/in.h>
 
 /*
- * NFS client parameters
- * Part of this is duplicated in rpc_clnt and is therefore obsolete.
+ * NFS client parameters stored in the superblock.
  */
 struct nfs_server {
 	struct rpc_clnt *	client;		/* RPC client handle */
@@ -18,7 +17,7 @@
 	unsigned int		acregmax;
 	unsigned int		acdirmin;
 	unsigned int		acdirmax;
-	char			hostname[256];	/* remote hostname */
+	char *			hostname;	/* remote hostname */
 };
 
 /*
diff -u --recursive --new-file v2.1.78/linux/include/linux/nfsd/nfsd.h linux/include/linux/nfsd/nfsd.h
--- v2.1.78/linux/include/linux/nfsd/nfsd.h	Sun Jul 27 12:11:01 1997
+++ linux/include/linux/nfsd/nfsd.h	Mon Jan 12 16:45:48 1998
@@ -111,6 +111,9 @@
 int		nfsd_notify_change(struct inode *, struct iattr *);
 int		nfsd_permission(struct svc_export *, struct dentry *, int);
 
+/* nfsd/nfsctl.c */
+void		nfsd_modcount(struct inode *, int);
+
 /*
  * lockd binding
  */
diff -u --recursive --new-file v2.1.78/linux/include/linux/nfsd/nfsfh.h linux/include/linux/nfsd/nfsfh.h
--- v2.1.78/linux/include/linux/nfsd/nfsfh.h	Sat Nov 29 11:25:12 1997
+++ linux/include/linux/nfsd/nfsfh.h	Mon Jan 12 16:45:48 1998
@@ -70,6 +70,8 @@
  * Shorthand for dprintk()'s
  */
 #define SVCFH_DENTRY(f)		((f)->fh_dentry)
+#define SVCFH_INO(f)		((f)->fh_handle.fh_ino)
+#define SVCFH_DEV(f)		((f)->fh_handle.fh_dev)
 
 /*
  * Function prototypes
diff -u --recursive --new-file v2.1.78/linux/include/linux/notifier.h linux/include/linux/notifier.h
--- v2.1.78/linux/include/linux/notifier.h	Thu Jun 12 15:28:33 1997
+++ linux/include/linux/notifier.h	Mon Jan 12 15:28:19 1998
@@ -97,6 +97,7 @@
 #define NETDEV_UNREGISTER	0x0006
 #define NETDEV_CHANGEMTU	0x0007
 #define NETDEV_CHANGEADDR	0x0008
+#define NETDEV_GOING_DOWN	0x0009
 
 #define SYS_DOWN	0x0001	/* Notify of system down */
 #define SYS_RESTART	SYS_DOWN
diff -u --recursive --new-file v2.1.78/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.78/linux/include/linux/proc_fs.h	Fri Jan  2 14:37:02 1998
+++ linux/include/linux/proc_fs.h	Mon Jan 12 16:43:16 1998
@@ -72,6 +72,7 @@
 #if CONFIG_AP1000
 	PROC_PID_RINGBUF,
 #endif
+	PROC_PID_CPU,
 };
 
 enum pid_subdirectory_inos {
@@ -244,6 +245,7 @@
 			 int count, int *eof, void *data);
 	int (*write_proc)(struct file *file, const char *buffer,
 			  unsigned long count, void *data);
+	int (*readlink_proc)(struct proc_dir_entry *de, char *page);
 	unsigned int count;	/* use count */
 	int deleted;		/* delete flag */
 };
diff -u --recursive --new-file v2.1.78/linux/include/linux/rtnetlink.h linux/include/linux/rtnetlink.h
--- v2.1.78/linux/include/linux/rtnetlink.h	Mon Dec  1 12:04:15 1997
+++ linux/include/linux/rtnetlink.h	Mon Jan 12 15:28:19 1998
@@ -54,9 +54,6 @@
 {
 	unsigned short	rta_len;
 	unsigned short	rta_type;
-/*
-	unsigned char	rta_data[0];
- */
 };
 
 enum rtattr_type_t
@@ -72,10 +69,11 @@
 	RTA_WINDOW,
 	RTA_RTT,
 	RTA_MTU,
-	RTA_IFNAME
+	RTA_IFNAME,
+	RTA_CACHEINFO
 };
 
-#define RTA_MAX RTA_IFNAME
+#define RTA_MAX RTA_CACHEINFO
 
 /* Macros to handle rtattributes */
 
@@ -89,6 +87,14 @@
 #define RTA_SPACE(len)	RTA_ALIGN(RTA_LENGTH(len))
 #define RTA_DATA(rta)   ((void*)(((char*)(rta)) + RTA_LENGTH(0)))
 
+struct rta_cacheinfo
+{
+	__u32	rta_clntref;
+	__u32	rta_lastuse;
+	__s32	rta_expires;
+	__u32	rta_error;
+	__u32	rta_used;
+};
 
 /*
  * "struct rtnexthop" describres all necessary nexthop information,
@@ -104,9 +110,6 @@
 	unsigned char		rtnh_flags;
 	unsigned char		rtnh_hops;
 	int			rtnh_ifindex;
-/*
-	struct rtattr		rtnh_data[0];
- */
 };
 
 /* rtnh_flags */
@@ -141,10 +144,6 @@
 	unsigned char		rtm_scope;	/* See below */	
 	unsigned char		rtm_whatsit;	/* Unused byte */
 	unsigned		rtm_flags;
-/*
-	struct rtattr		rtm_opt[0];
-	struct rtnexthop	rtm_nh[0];
- */
 };
 
 #define RTM_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg))))
@@ -191,8 +190,9 @@
  */
 
 #define RTPROT_GATED	8	/* Apparently, GateD */
-#define RTPROT_RA	9	/* RDISC router advertisment */
-
+#define RTPROT_RA	9	/* RDISC/ND router advertisments */
+#define RTPROT_MRT	10	/* Merit MRT */
+#define RTPROT_ZEBRA	11	/* Zebra */
 
 /* rtm_scope
 
@@ -209,6 +209,7 @@
 {
 	RT_SCOPE_UNIVERSE=0,
 /* User defined values f.e. "site" */
+	RT_SCOPE_SITE=200,
 	RT_SCOPE_LINK=253,
 	RT_SCOPE_HOST=254,
 	RT_SCOPE_NOWHERE=255
@@ -245,9 +246,6 @@
 	unsigned char	ifa_flags;	/* Flags			*/
 	unsigned char	ifa_scope;	/* See above			*/
 	int		ifa_index;	/* Link index			*/
-/*
-	struct rtattr	ifa_data[0];
- */
 };
 
 enum
@@ -257,14 +255,25 @@
 	IFA_LOCAL,
 	IFA_LABEL,
 	IFA_BROADCAST,
-	IFA_ANYCAST
+	IFA_ANYCAST,
+	IFA_CACHEINFO
 };
 
-#define IFA_MAX IFA_ANYCAST
+#define IFA_MAX IFA_CACHEINFO
 
 /* ifa_flags */
 
-#define IFA_F_SECONDARY	1
+#define IFA_F_SECONDARY		0x01
+
+#define IFA_F_DEPRECATED	0x20
+#define IFA_F_TENTATIVE		0x40
+#define IFA_F_PERMANENT		0x80
+
+struct ifa_cacheinfo
+{
+	__s32	ifa_prefered;
+	__s32	ifa_valid;
+};
 
 
 #define IFA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
@@ -283,12 +292,11 @@
 
 struct ndmsg
 {
-	unsigned char	nd_family;
-	int		nd_ifindex;	/* Link index			*/
-	unsigned	nd_flags;
-/*
-	struct rtattr	nd_data[0];
- */
+	unsigned char	ndm_family;
+	int		ndm_ifindex;	/* Link index			*/
+	__u16		ndm_state;
+	__u8		ndm_flags;
+	__u8		ndm_type;
 };
 
 enum
@@ -296,12 +304,21 @@
 	NDA_UNSPEC,
 	NDA_DST,
 	NDA_LLADDR,
+	NDA_CACHEINFO
 };
 
-#define NDA_MAX NDA_LLADDR
+#define NDA_MAX NDA_CACHEINFO
 
 #define NDA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
 
+struct nda_cacheinfo
+{
+	__u32		ndm_confirmed;
+	__u32		ndm_used;
+	__u32		ndm_updated;
+	__u32		ndm_refcnt;
+};
+
 /****
  *		General form of address family dependent message.
  ****/
@@ -336,6 +353,20 @@
 	int		ifi_qdisc;		/* Packet scheduler handle */
 };
 
+enum
+{
+	IFLA_UNSPEC,
+	IFLA_ADDRESS,
+	IFLA_BROADCAST,
+	IFLA_IFNAME,
+	IFLA_QDISC,
+	IFLA_STATS
+};
+
+#define IFLA_MAX IFLA_STATS
+
+#define IFLA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+
 /* ifi_flags.
 
    IFF_* flags.
@@ -367,16 +398,15 @@
 
 #define RTMGRP_LINK		1
 #define RTMGRP_NOTIFY		2
+#define RTMGRP_NEIGH		4
 
 #define RTMGRP_IPV4_IFADDR	0x10
-#define RTMGRP_IPV4_NDISC	0x20
+#define RTMGRP_IPV4_MROUTE	0x20
 #define RTMGRP_IPV4_ROUTE	0x40
-#define RTMGRP_IPV4_MROUTE	0x80
 
 #define RTMGRP_IPV6_IFADDR	0x100
-#define RTMGRP_IPV6_NDISC	0x200
+#define RTMGRP_IPV6_MROUTE	0x200
 #define RTMGRP_IPV6_ROUTE	0x400
-#define RTMGRP_IPV6_MROUTE	0x800
 
 
 #ifdef __KERNEL__
@@ -394,6 +424,7 @@
 	unsigned	*rta_rtt;
 	unsigned	*rta_mtu;
 	unsigned char	*rta_ifname;
+	struct rta_cacheinfo *rta_ci;
 };
 
 struct kern_ifa
diff -u --recursive --new-file v2.1.78/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.1.78/linux/include/linux/sched.h	Tue Jan  6 09:37:38 1998
+++ linux/include/linux/sched.h	Mon Jan 12 16:43:16 1998
@@ -147,7 +147,7 @@
 	struct semaphore mmap_sem;
 	unsigned long context;
 	unsigned long start_code, end_code, start_data, end_data;
-	unsigned long start_brk, brk, start_stack, start_mmap;
+	unsigned long start_brk, brk, start_stack;
 	unsigned long arg_start, arg_end, env_start, env_end;
 	unsigned long rss, total_vm, locked_vm;
 	unsigned long def_flags;
@@ -159,7 +159,7 @@
 		MUTEX,					\
 		0,					\
 		0, 0, 0, 0,				\
-		0, 0, 0, 0,				\
+		0, 0, 0, 				\
 		0, 0, 0, 0,				\
 		0, 0, 0,				\
 		0, 0 }
@@ -179,17 +179,18 @@
 struct task_struct {
 /* these are hardcoded - don't touch */
 	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
-	long counter;
-	long priority;
 	unsigned long flags;	/* per process flags, defined below */
 	int sigpending;
-	long debugreg[8];  /* Hardware debugging registers */
-	struct exec_domain *exec_domain;
-/* various fields */
 	mm_segment_t addr_limit;	/* thread address space:
 					 	0-0xBFFFFFFF for user-thead
 						0-0xFFFFFFFF for kernel-thread
 					 */
+	struct exec_domain *exec_domain;
+
+/* various fields */
+	long debugreg[8];  /* Hardware debugging registers */
+	long counter;
+	long priority;
 	struct linux_binfmt *binfmt;
 	struct task_struct *next_task, *prev_task;
 	struct task_struct *next_run,  *prev_run;
@@ -230,6 +231,7 @@
 	struct timer_list real_timer;
 	struct tms times;
 	unsigned long start_time;
+	long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];
 /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
 	unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
 	int swappable:1;
@@ -311,10 +313,9 @@
  * your own risk!. Base=0, limit=0x1fffff (=2MB)
  */
 #define INIT_TASK \
-/* state etc */	{ 0,DEF_PRIORITY,DEF_PRIORITY,0,0, \
+/* state etc */	{ 0,0,0,KERNEL_DS,&default_exec_domain, \
 /* debugregs */ { 0, },            \
-/* exec domain */&default_exec_domain, \
-/* mm_seg */	KERNEL_DS, \
+/* counter */	DEF_PRIORITY,DEF_PRIORITY, \
 /* binfmt */	NULL, \
 /* schedlink */	&init_task,&init_task, &init_task, &init_task, \
 /* ec,brk... */	0,0,0,0,0,0, \
@@ -328,6 +329,7 @@
 /* timeout */	0,SCHED_OTHER,0,0,0,0,0,0,0, \
 /* timer */	{ NULL, NULL, 0, 0, it_real_fn }, \
 /* utime */	{0,0,0,0},0, \
+/* per cpu times */ {0, 0, }, {0, 0, }, \
 /* flt */	0,0,0,0,0,0, \
 /* swp */	0,0,0,0,0, \
 /* rlimits */   INIT_RLIMITS, \
diff -u --recursive --new-file v2.1.78/linux/include/linux/sdla_fr.h linux/include/linux/sdla_fr.h
--- v2.1.78/linux/include/linux/sdla_fr.h	Thu Jul 17 10:06:08 1997
+++ linux/include/linux/sdla_fr.h	Mon Jan 12 14:46:26 1998
@@ -1,7 +1,8 @@
 /*****************************************************************************
 * sdla_fr.h	Sangoma frame relay firmware API definitions.
 *
-* Author:	Gene Kozin	<74604.152@compuserve.com>
+* Author:	Jaspreet Singh  <jaspreet@sangoma.com>
+*		Gene Kozin	<74604.152@compuserve.com>
 *
 * Copyright:	(c) 1995-1996 Sangoma Technologies Inc.
 *
@@ -10,6 +11,9 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
+* Oct 12, 1997	Jaspreet Singh	Added FR_READ_DLCI_IB_MAPPING
+* Jul 21, 1997 	Jaspreet Singh	Changed FRRES_TOO_LONG and FRRES_TOO_MANY to 
+*				0x05 and 0x06 respectively.
 * Dec 23, 1996	Gene Kozin	v2.0
 * Apr 29, 1996	Gene Kozin	v1.0 (merged version S502 & S508 definitions).
 * Sep 26, 1995	Gene Kozin	Initial version.
@@ -94,6 +98,7 @@
 #define FR_SET_MODEM_STATUS	0x31
 #define FR_READ_ERROR_STATS	0x32
 #define FR_FLUSH_ERROR_STATS	0x33
+#define FR_READ_DLCI_IB_MAPPING 0x34
 #define FR_READ_CODE_VERSION	0x40
 #define	FR_SET_INTR_MODE	0x50
 #define	FR_READ_INTR_MODE	0x51
@@ -104,8 +109,8 @@
 #define	FRRES_INOPERATIVE	0x02	/* channel inoperative */
 #define	FRRES_DLCI_INACTIVE	0x03	/* DLCI is inactive */
 #define	FRRES_DLCI_INVALID	0x04	/* DLCI is not configured */
-#define	FRRES_TOO_LONG		0x04
-#define	FRRES_TOO_MANY		0x05
+#define	FRRES_TOO_LONG		0x05
+#define	FRRES_TOO_MANY		0x06
 #define	FRRES_CIR_OVERFLOW	0x07	/* Tx throughput has exceeded CIR */
 #define	FRRES_BUFFER_OVERFLOW	0x08
 #define	FRRES_MODEM_FAILURE	0x10	/* DCD and/or CTS dropped */
@@ -160,6 +165,7 @@
 	unsigned char iflag	PACKED;	/* 10h: interrupt flag */
 	unsigned char imask	PACKED;	/* 11h: interrupt mask */
 	unsigned long tse_offs	PACKED;	/* 12h: Tx status element */
+	unsigned short dlci	PACKED; /* 16h: DLCI NUMBER */
 } fr508_flags_t;
 
 /* 'event' field defines */
diff -u --recursive --new-file v2.1.78/linux/include/linux/sdla_x25.h linux/include/linux/sdla_x25.h
--- v2.1.78/linux/include/linux/sdla_x25.h	Thu Jul 17 10:06:08 1997
+++ linux/include/linux/sdla_x25.h	Mon Jan 12 14:46:26 1998
@@ -244,7 +244,8 @@
 	unsigned short ogc_map	PACKED;	/* 06h: Outgoing Chan. map */
 	TX25TimeStamp tstamp	PACKED;	/* 08h: timestamp (BCD) */
 	unsigned char iflags	PACKED;	/* 0Dh: interrupt flags */
-	unsigned char resrv[2]	PACKED;	/* 0Eh: */
+	unsigned char imask     PACKED; /* 0Eh: interrupt mask  */
+	unsigned char resrv	PACKED;	/* 0Eh: */
 	unsigned char gflags	PACKED;	/* 10h: misc. HDLC/X25 flags */
 	unsigned char cflags[X25_MAX_CHAN] PACKED; /* channel status bytes */
 } TX25Status;
diff -u --recursive --new-file v2.1.78/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v2.1.78/linux/include/linux/skbuff.h	Mon Dec  1 12:04:15 1997
+++ linux/include/linux/skbuff.h	Mon Jan 12 16:43:19 1998
@@ -86,8 +86,7 @@
   
 	unsigned int 	len;			/* Length of actual data			*/
 	unsigned int	csum;			/* Checksum 					*/
-	volatile char 	used,			/* Are we in use ?				*/
-			arp;			/* Has IP/ARP resolution finished		*/
+	volatile char 	used;
 	unsigned char	tries,			/* Times tried					*/
   			inclone,		/* Inline clone					*/
   			pkt_type,		/* Packet class					*/
@@ -484,33 +483,6 @@
 	}
 }
 
-/* dev_tint can lock buffer at any moment,
- * so that cli(), unlink it and sti(),
- * now it is safe.
- */
-
-extern __inline__ int skb_steal(struct sk_buff *skb)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	if (skb->next) {
-		skb_unlink(skb);
-		atomic_dec(&skb->users);
-	}
-	restore_flags(flags);
-	return 1;
-}
-
-extern __inline__ void __skb_steal(struct sk_buff *skb)
-{
-	if (skb->next) {
-		skb_unlink(skb);
-		atomic_dec(&skb->users);
-	}
-}
-
 extern __inline__ void skb_orphan(struct sk_buff *skb)
 {
 	if (skb->destructor)
@@ -524,6 +496,16 @@
 	struct sk_buff *skb;
 	while ((skb=skb_dequeue(list))!=NULL)
 		kfree_skb(skb,0);
+}
+
+extern __inline__ struct sk_buff *dev_alloc_skb(unsigned int length)
+{
+	struct sk_buff *skb;
+
+	skb = alloc_skb(length+16, GFP_ATOMIC);
+	if (skb)
+		skb_reserve(skb,16);
+	return skb;
 }
 
 extern struct sk_buff *		skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
diff -u --recursive --new-file v2.1.78/linux/include/linux/sunrpc/sched.h linux/include/linux/sunrpc/sched.h
--- v2.1.78/linux/include/linux/sunrpc/sched.h	Wed Oct 15 16:04:24 1997
+++ linux/include/linux/sunrpc/sched.h	Mon Jan 12 16:43:34 1998
@@ -89,6 +89,7 @@
 #define RPC_TASK_ROOTCREDS	0x0100		/* force root creds */
 #define RPC_TASK_DYNAMIC	0x0200		/* task was kmalloc'ed */
 #define RPC_TASK_KILLED		0x0400		/* task was killed */
+#define RPC_TASK_NFSWRITE	0x1000		/* an NFS writeback */
 
 #define RPC_IS_RUNNING(t)	((t)->tk_flags & RPC_TASK_RUNNING)
 #define RPC_IS_ASYNC(t)		((t)->tk_flags & RPC_TASK_ASYNC)
@@ -145,6 +146,9 @@
 void		rpc_free(void *);
 int		rpciod_up(void);
 void		rpciod_down(void);
+#ifdef RPC_DEBUG
+void		rpc_show_tasks(void);
+#endif
 
 extern __inline__ void *
 rpc_malloc(struct rpc_task *task, unsigned int size)
diff -u --recursive --new-file v2.1.78/linux/include/linux/sunrpc/stats.h linux/include/linux/sunrpc/stats.h
--- v2.1.78/linux/include/linux/sunrpc/stats.h	Mon Jul  7 16:03:29 1997
+++ linux/include/linux/sunrpc/stats.h	Mon Jan 12 16:45:38 1998
@@ -40,6 +40,9 @@
 
 void			rpc_proc_init(void);
 void			rpc_proc_exit(void);
+#ifdef MODULE
+void			rpc_modcount(struct inode *, int);
+#endif
 
 struct proc_dir_entry *	rpc_proc_register(struct rpc_stat *);
 void			rpc_proc_unregister(const char *);
diff -u --recursive --new-file v2.1.78/linux/include/linux/swap.h linux/include/linux/swap.h
--- v2.1.78/linux/include/linux/swap.h	Mon Oct 20 10:36:53 1997
+++ linux/include/linux/swap.h	Mon Jan 12 14:33:20 1998
@@ -38,6 +38,7 @@
 extern int min_free_pages;
 extern int free_pages_low;
 extern int free_pages_high;
+extern struct inode swapper_inode;
 
 /* Incomplete types for prototype declarations: */
 struct task_struct;
@@ -100,39 +101,29 @@
 extern inline unsigned long in_swap_cache(struct page *page)
 {
 	if (PageSwapCache(page))
-		return page->pg_swap_entry;
+		return page->offset;
 	return 0;
 }
 
-extern inline long find_in_swap_cache(struct page *page)
-{
-#ifdef SWAP_CACHE_INFO
-	swap_cache_find_total++;
-#endif
-	if (PageTestandClearSwapCache(page))  {
-#ifdef SWAP_CACHE_INFO
-		swap_cache_find_success++;
-#endif	
-		return page->pg_swap_entry;
-	}
-	return 0;
-}
-
-extern inline int delete_from_swap_cache(struct page *page)
+/*
+ * Work out if there are any other processes sharing this page, ignoring
+ * any page reference coming from the page or swap cache.  
+ */
+static inline int is_page_shared(struct page *page)
 {
-#ifdef SWAP_CACHE_INFO
-	swap_cache_del_total++;
-#endif	
-	if (PageTestandClearSwapCache(page))  {
-#ifdef SWAP_CACHE_INFO
-		swap_cache_del_success++;
-#endif
-		swap_free(page->pg_swap_entry);
-		return 1;
-	}
-	return 0;
+	int count = atomic_read(&page->count);
+	if (page->inode)
+		count--;
+	return (count > 1);
 }
 
+/*
+ * Make these inline later once they are working properly.
+ */
+extern long find_in_swap_cache(struct page *page);
+extern int delete_from_swap_cache(struct page *page);
+extern void remove_from_swap_cache(struct page *page);
+extern void free_page_and_swap_cache(unsigned long addr);
 
 #endif /* __KERNEL__*/
 
diff -u --recursive --new-file v2.1.78/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.1.78/linux/include/linux/sysctl.h	Fri Dec 19 15:53:05 1997
+++ linux/include/linux/sysctl.h	Mon Jan 12 15:28:19 1998
@@ -114,6 +114,8 @@
 	NET_CORE_WMEM_DEFAULT,
 	NET_CORE_RMEM_DEFAULT,
 	NET_CORE_DESTROY_DELAY,
+	NET_CORE_MAX_BACKLOG,
+	NET_CORE_FASTROUTE,
 };
 
 /* /proc/sys/net/ethernet */
@@ -131,15 +133,7 @@
 /* /proc/sys/net/ipv4 */
 enum
 {
-	NET_IPV4_ARP_RES_TIME=1,
-	NET_IPV4_ARP_DEAD_RES_TIME,
-	NET_IPV4_ARP_MAX_TRIES,
-	NET_IPV4_ARP_MAX_PINGS,
-	NET_IPV4_ARP_TIMEOUT,
-	NET_IPV4_ARP_CHECK_INTERVAL,
-	NET_IPV4_ARP_CONFIRM_INTERVAL,
-	NET_IPV4_ARP_CONFIRM_TIMEOUT,
-	NET_IPV4_TCP_HOE_RETRANSMITS,
+	NET_IPV4_TCP_HOE_RETRANSMITS=1,
 	NET_IPV4_TCP_SACK,
 	NET_IPV4_TCP_TSACK,
 	NET_IPV4_TCP_TIMESTAMPS,
@@ -186,7 +180,8 @@
 	NET_IPV4_ICMP_DESTUNREACH_RATE,
 	NET_IPV4_ICMP_TIMEEXCEED_RATE,
 	NET_IPV4_ICMP_PARAMPROB_RATE,
-	NET_IPV4_ICMP_ECHOREPLY_RATE
+	NET_IPV4_ICMP_ECHOREPLY_RATE,
+	NET_IPV4_NEIGH,
 };
 
 
@@ -198,12 +193,6 @@
 	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,
@@ -211,6 +200,27 @@
 	NET_IPV6_RTR_SOLICIT_DELAY,
 
 	NET_IPV6_ICMPV6_TIME,
+	NET_IPV6_NEIGH,
+};
+
+/* /proc/sys/net/<protocol>/neigh/<dev> */
+enum {
+	NET_NEIGH_MCAST_SOLICIT=1,
+	NET_NEIGH_UCAST_SOLICIT,
+	NET_NEIGH_APP_SOLICIT,
+	NET_NEIGH_RETRANS_TIME,
+	NET_NEIGH_REACHABLE_TIME,
+	NET_NEIGH_DELAY_PROBE_TIME,
+	NET_NEIGH_GC_STALE_TIME,
+	NET_NEIGH_UNRES_QLEN,
+	NET_NEIGH_PROXY_QLEN,
+	NET_NEIGH_ANYCAST_DELAY,
+	NET_NEIGH_PROXY_DELAY,
+	NET_NEIGH_LOCKTIME,
+	NET_NEIGH_GC_INTERVAL,
+	NET_NEIGH_GC_THRESH1,
+	NET_NEIGH_GC_THRESH2,
+	NET_NEIGH_GC_THRESH3
 };
 
 /* /proc/sys/net/ipx */
diff -u --recursive --new-file v2.1.78/linux/include/linux/sysv_fs.h linux/include/linux/sysv_fs.h
--- v2.1.78/linux/include/linux/sysv_fs.h	Wed Dec 10 11:12:46 1997
+++ linux/include/linux/sysv_fs.h	Mon Jan 12 16:46:33 1998
@@ -367,7 +367,7 @@
 extern int sysv_rmdir(struct inode * dir, struct dentry * dentry);
 extern int sysv_unlink(struct inode * dir, struct dentry * dentry);
 extern int sysv_symlink(struct inode * inode, struct dentry * dentry, const char * symname);
-extern int sysv_link(struct inode * oldinode, struct inode * dir, struct dentry * dentry);
+extern int sysv_link(struct dentry * old_dentry, struct inode * dir, struct dentry * dentry);
 extern int sysv_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev);
 extern int sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
 		       struct inode * new_dir, struct dentry * new_dentry);
@@ -390,7 +390,7 @@
 extern int init_sysv_fs(void);
 extern void sysv_write_super(struct super_block *);
 extern void sysv_read_inode(struct inode *);
-extern int sysv_notify_change(struct inode *, struct iattr *);
+extern int sysv_notify_change(struct dentry *, struct iattr *);
 extern void sysv_write_inode(struct inode *);
 extern void sysv_put_inode(struct inode *);
 extern int sysv_statfs(struct super_block *, struct statfs *, int);
diff -u --recursive --new-file v2.1.78/linux/include/linux/ufs_fs.h linux/include/linux/ufs_fs.h
--- v2.1.78/linux/include/linux/ufs_fs.h	Thu Dec  4 14:53:57 1997
+++ linux/include/linux/ufs_fs.h	Mon Jan 12 19:47:22 1998
@@ -8,6 +8,7 @@
  *
  * Clean swab support by Fare <rideau@ens.fr>
  * just hope no one is using NNUUXXI on __?64 structure elements
+ * 64-bit clean thanks to Maciej W. Rozycki <macro@ds2.pg.gda.pl>
  */
 
 #ifndef __LINUX_UFS_FS_H
@@ -92,6 +93,11 @@
 #define ufs_boff(sb, block)		((block) & ~((sb)->u.ufs_sb.s_lmask))
 #define ufs_dbn(sb, block, boff)	((block) + ufs_boff((sb), (boff)))
 
+struct ufs_timeval {
+	__s32	tv_sec;
+	__s32	tv_usec;
+};
+
 struct ufs_direct {
 	__u32  d_ino;			/* inode number of this entry */
 	__u16  d_reclen;		/* length of this entry */
@@ -184,13 +190,13 @@
 	__u8	fs_fsmnt[MAXMNTLEN];	/* name mounted on */
 /* these fields retain the current block allocation info */
 	__u32	fs_cgrotor;	/* last cg searched */
-	struct ufs_csum * fs_csp[MAXCSBUFS];	/* list of fs_cs info buffers */
+	__u32	fs_csp[MAXCSBUFS];	/* list of fs_cs info buffers */
 	__u32	fs_cpc;		/* cyl per cycle in postbl */
 	__u16	fs_opostbl[16][8];	/* old rotation block list head */	
 	__s32	fs_sparecon[55];	/* reserved for future constants */
 	__s32	fs_state;		/* file system state time stamp */
-	__s64	fs_qbmask;		/* ~usb_bmask */
-	__s64	fs_qfmask;		/* ~usb_fmask */
+	__u32	fs_qbmask[2];		/* ~usb_bmask */
+	__u32	fs_qfmask[2];		/* ~usb_fmask */
 	__s32	fs_postblformat;	/* format of positional layout tables */
 	__s32	fs_nrpos;		/* number of rotational positions */
 	__s32	fs_postbloff;		/* (__s16) rotation block list head */
@@ -208,9 +214,9 @@
 	__u16	ui_suid;		/*  0x4 */
 	__u16	ui_sgid;		/*  0x6 */
 	__u64	ui_size;		/*  0x8 */
-	struct timeval ui_atime;	/* 0x10 access */
-	struct timeval ui_mtime;	/* 0x18 modification */
-	struct timeval ui_ctime;	/* 0x20 creation */
+	struct ufs_timeval ui_atime;	/* 0x10 access */
+	struct ufs_timeval ui_mtime;	/* 0x18 modification */
+	struct ufs_timeval ui_ctime;	/* 0x20 creation */
 	__u32	ui_db[UFS_NDADDR];	/* 0x28 data blocks */
 	__u32	ui_ib[UFS_NINDIR];	/* 0x58 indirect blocks */
 	__u32	ui_flags;		/* 0x64 unused -- "status flags (chflags)" ??? */
diff -u --recursive --new-file v2.1.78/linux/include/linux/umsdos_fs.h linux/include/linux/umsdos_fs.h
--- v2.1.78/linux/include/linux/umsdos_fs.h	Mon Jul  7 16:03:54 1997
+++ linux/include/linux/umsdos_fs.h	Mon Jan 12 16:46:33 1998
@@ -135,6 +135,7 @@
 extern struct file_operations  umsdos_file_operations;
 extern struct inode_operations umsdos_file_inode_operations;
 extern struct inode_operations umsdos_file_inode_operations_no_bmap;
+extern struct inode_operations umsdos_file_inode_operations_readpage;
 extern struct inode_operations umsdos_symlink_inode_operations;
 extern int init_umsdos_fs(void);
 
diff -u --recursive --new-file v2.1.78/linux/include/linux/videodev.h linux/include/linux/videodev.h
--- v2.1.78/linux/include/linux/videodev.h	Sun Dec 21 22:36:17 1997
+++ linux/include/linux/videodev.h	Mon Jan 12 14:48:22 1998
@@ -159,7 +159,7 @@
 #define VIDIOCGFBUF		_IOR('v',11, struct video_buffer)	/* Get frame buffer */
 #define VIDIOCSFBUF		_IOW('v',12, struct video_buffer)	/* Set frame buffer - root only */
 #define VIDIOCKEY		_IOR('v',13, struct video_key)		/* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */
-#define VIDIOCGFREQ		_IOR('v',15, unsigned long)		/* Set tuner */
+#define VIDIOCGFREQ		_IOR('v',14, unsigned long)		/* Set tuner */
 #define VIDIOCSFREQ		_IOW('v',15, unsigned long)		/* Set tuner */
 #define VIDIOCGAUDIO		_IOR('v',16, struct video_audio)	/* Get audio info */
 #define VIDIOCSAUDIO		_IOW('v',17, struct video_audio)	/* Audio source, mute etc */
@@ -171,5 +171,6 @@
 #define VID_HARDWARE_BT848	1
 #define VID_HARDWARE_QCAM_BW	2
 #define VID_HARDWARE_PMS	3
+#define VID_HARDWARE_QCAM_C	4
 
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/linux/wanpipe.h linux/include/linux/wanpipe.h
--- v2.1.78/linux/include/linux/wanpipe.h	Thu Jul 17 10:06:09 1997
+++ linux/include/linux/wanpipe.h	Mon Jan 12 14:46:27 1998
@@ -3,6 +3,7 @@
 *		User-level API definitions.
 *
 * Author:	Gene Kozin	<genek@compuserve.com>
+*		Jaspreet Singh	<jaspreet@sangoma.com>
 *
 * Copyright:	(c) 1995-1997 Sangoma Technologies Inc.
 *
@@ -11,6 +12,15 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
+* Nov 26, 1997	Jaspreet Singh	Added 'load_sharing' structure.  Also added 
+*				'devs_struct','dev_to_devtint_next' to 'sdla_t'	
+* Nov 24, 1997	Jaspreet Singh	Added 'irq_dis_if_send_count', 
+*				'irq_dis_poll_count' to 'sdla_t'.
+* Nov 06, 1997	Jaspreet Singh	Added a define called 'INTR_TEST_MODE'
+* Oct 20, 1997	Jaspreet Singh	Added 'buff_intr_mode_unbusy' and 
+*				'dlci_intr_mode_unbusy' to 'sdla_t'
+* Oct 18, 1997	Jaspreet Singh	Added structure to maintain global driver
+*				statistics.
 * Jan 15, 1997	Gene Kozin	Version 3.1.0
 *				 o added UDP management stuff
 * Jan 02, 1997	Gene Kozin	Version 3.0.0
@@ -61,6 +71,46 @@
 	unsigned char reserved[6];	/* 0Ah: reserved */
 } wum_header_t;
 
+/*************************************************************************
+ Data Structure for global statistics
+*************************************************************************/
+
+typedef struct global_stats
+{
+	unsigned long isr_entry;
+	unsigned long isr_already_critical;		
+	unsigned long isr_rx;
+	unsigned long isr_tx;
+	unsigned long isr_intr_test;
+	unsigned long isr_spurious;
+	unsigned long isr_enable_tx_int;
+	unsigned long rx_intr_corrupt_rx_bfr;
+	unsigned long rx_intr_on_orphaned_DLCI;
+	unsigned long rx_intr_dev_not_started;
+	unsigned long tx_intr_dev_not_started;
+	unsigned long poll_entry;
+	unsigned long poll_already_critical;
+	unsigned long poll_processed;
+	unsigned long poll_tbusy_bad_status;
+	unsigned long poll_host_disable_irq;
+	unsigned long poll_host_enable_irq;
+
+} global_stats_t;
+
+/* This structure is used for maitaining a circular linked list of all
+ * interfaces(devices) per card. It is used in the Interrupt Service routine
+ * for a transmit interrupt where the start of the loop to dev_tint all
+ * interfaces changes.
+ */
+typedef struct load_sharing
+{
+        struct device*  dev_ptr;
+        struct load_sharing* next;
+} load_sharing_t;
+
+/* This is used for interrupt testing */
+#define INTR_TEST_MODE	0x02
+
 #define	WUM_SIGNATURE_L	0x50495046
 #define	WUM_SIGNATURE_H	0x444E3845
 
@@ -100,8 +150,21 @@
 	wan_device_t wandev;		/* WAN device data space */
 	unsigned open_cnt;		/* number of open interfaces */
 	unsigned long state_tick;	/* link state timestamp */
-/*	unsigned tx_int_enabled; */	/* tranmit interrupt enabled or not */
+	unsigned intr_mode;		/* Type of Interrupt Mode */
 	char in_isr;			/* interrupt-in-service flag */
+	char buff_int_mode_unbusy;	/* flag for carrying out dev_tint */  
+	char dlci_int_mode_unbusy;	/* flag for carrying out dev_tint */
+	unsigned short irq_dis_if_send_count; /* Disabling irqs in if_send*/
+	unsigned short irq_dis_poll_count;   /* Disabling irqs in poll routine*/
+	global_stats_t statistics;	/* global statistics */
+	
+	/* The following is used as  a pointer to the structure in our 
+	   circular linked list which changes the start of the loop for 
+	   dev_tint of all interfaces */
+	
+	load_sharing_t* dev_to_devtint_next;
+	load_sharing_t* devs_struct;	
+
 	void* mbox;			/* -> mailbox */
 	void* rxmb;			/* -> receive mailbox */
 	void* flags;			/* -> adapter status flags */
@@ -123,7 +186,7 @@
 			void* rxmb_last;	/* -> last Rx buffer */
 			unsigned rx_base;	/* S508 receive buffer base */
 			unsigned rx_top;	/* S508 receive buffer end */
-			unsigned short node_dlci;
+			unsigned short node_dlci[100];
 			unsigned short dlci_num;
 		} f;
 		struct			/****** PPP-specific data ***********/
diff -u --recursive --new-file v2.1.78/linux/include/linux/wanrouter.h linux/include/linux/wanrouter.h
--- v2.1.78/linux/include/linux/wanrouter.h	Thu Jul 17 10:06:09 1997
+++ linux/include/linux/wanrouter.h	Mon Jan 12 19:31:43 1998
@@ -4,6 +4,7 @@
 *		Drivers and is completely hardware-independent.
 *
 * Author:	Gene Kozin	<genek@compuserve.com>
+*		Jaspreet Singh	<jaspreet@sangoma.com>
 *
 * Copyright:	(c) 1995-1997 Sangoma Technologies Inc.
 *
@@ -12,7 +13,16 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
-* May 29, 1997 	Jaspreet Singh	Added 'tx_int_enabled' tp 'wan_device_t'
+* Nov 06, 1997	Jaspreet Singh	Changed Router Driver version to 1.1 from 1.0
+* Oct 20, 1997	Jaspreet Singh	Added 'cir','bc','be' and 'mc' to 'wanif_conf_t'
+*				Added 'enable_IPX' and 'network_number' to 
+*				'wan_device_t'.  Also added defines for
+*				UDP PACKET TYPE, Interrupt test, critical values*				for RACE conditions.
+* Oct 05, 1997	Jaspreet Singh	Added 'dlci_num' and 'dlci[100]' to 
+*				'wan_fr_conf_t' to configure a list of dlci(s)
+*				for a NODE 
+* Jul 07, 1997	Jaspreet Singh	Added 'ttl' to 'wandev_conf_t' & 'wan_device_t'
+* May 29, 1997 	Jaspreet Singh	Added 'tx_int_enabled' to 'wan_device_t'
 * May 21, 1997	Jaspreet Singh	Added 'udp_port' to 'wan_device_t'
 * Apr 25, 1997  Farhan Thawar   Added 'udp_port' to 'wandev_conf_t'
 * Jan 16, 1997	Gene Kozin	router_devlist made public
@@ -23,7 +33,7 @@
 
 #define	ROUTER_NAME	"wanrouter"	/* in case we ever change it */
 #define	ROUTER_VERSION	1		/* version number */
-#define	ROUTER_RELEASE	0		/* release (minor version) number */
+#define	ROUTER_RELEASE	1		/* release (minor version) number */
 #define	ROUTER_IOCTL	'W'		/* for IOCTL calls */
 #define	ROUTER_MAGIC	0x524D4157L	/* signature: 'WANR' reversed */
 
@@ -53,6 +63,27 @@
 #define	WAN_DRVNAME_SZ	15	/* max length of the link driver name */
 #define	WAN_ADDRESS_SZ	31	/* max length of the WAN media address */
 
+/* Defines for UDP PACKET TYPE */
+#define UDP_PTPIPE_TYPE 	0x01
+#define UDP_FPIPE_TYPE		0x02
+#define UDP_DRVSTATS_TYPE 	0x03
+#define UDP_INVALID_TYPE  	0x04
+
+/* Command return code */
+#define CMD_OK		0		/* normal firmware return code */
+#define CMD_TIMEOUT	0xFF		/* firmware command timed out */
+
+/* UDP Packet Management */
+#define UDP_PKT_FRM_STACK	0x00
+#define UDP_PKT_FRM_NETWORK	0x01
+
+/* Maximum interrupt test counter */
+#define MAX_INTR_TEST_COUNTER	100
+
+/* Critical Values for RACE conditions*/
+#define CRITICAL_IN_ISR		0xA1
+#define CRITICAL_INTR_HANDLED	0xB1
+
 /****** Data Types **********************************************************/
 
 /*----------------------------------------------------------------------------
@@ -87,15 +118,14 @@
  */
 typedef struct wan_fr_conf
 {
-	unsigned cir;		/* committed information rate */
 	unsigned signalling;	/* local in-channel signalling type */
 	unsigned t391;		/* link integrity verification timer */
 	unsigned t392;		/* polling verification timer */
 	unsigned n391;		/* full status polling cycle counter */
 	unsigned n392;		/* error threshold counter */
 	unsigned n393;		/* monitored events counter */
-	unsigned dlci;		/* first DLC number (access node) */
 	unsigned dlci_num;	/* number of DLCs (access node) */
+	unsigned  dlci[100];    /* List of all DLCIs */
 } wan_fr_conf_t;
 
 /*----------------------------------------------------------------------------
@@ -133,12 +163,15 @@
 	unsigned bps;		/* data transfer rate */
 	unsigned mtu;		/* maximum transmit unit size */
         unsigned udp_port;      /* UDP port for management */
+	unsigned char ttl;	/* Time To Live for UDP security */
         char interface;		/* RS-232/V.35, etc. */
 	char clocking;		/* external/internal */
 	char line_coding;	/* NRZ/NRZI/FM0/FM1, etc. */
 	char station;		/* DTE/DCE, primary/secondary, etc. */
 	char connection;	/* permanent/switched/on-demand */
 	unsigned hw_opt[4];	/* other hardware options */
+	unsigned char enable_IPX;	/* Enable or Disable IPX */
+	unsigned long network_number;	/* Network Number for IPX */
 	unsigned reserved[4];
 				/****** arbitrary data ***************/
 	unsigned data_size;	/* data buffer size */
@@ -261,6 +294,10 @@
 	char addr[WAN_ADDRESS_SZ+1];	/* media address, ASCIIZ */
 	unsigned idle_timeout;		/* sec, before disconnecting */
 	unsigned hold_timeout;		/* sec, before re-connecting */
+	unsigned cir;			/* Committed Information Rate fwd,bwd*/
+	unsigned bc;			/* Committed Burst Size fwd, bwd */
+	unsigned be;			/* Excess Burst Size fwd, bwd */ 
+	char mc;			/* Multicast on or off */
 	int reserved[8];		/* reserved for future extensions */
 } wanif_conf_t;
 
@@ -289,13 +326,16 @@
 	unsigned bps;			/* data transfer rate */
 	unsigned mtu;			/* max physical transmit unit size */
 	unsigned udp_port;              /* UDP port for management */
-        unsigned tx_int_enabled; 	/* Transmit Interrupt enabled or not */
+        unsigned char ttl;		/* Time To Live for UDP security */
+	unsigned enable_tx_int; 	/* Transmit Interrupt enabled or not */
 	char interface;			/* RS-232/V.35, etc. */
 	char clocking;			/* external/internal */
 	char line_coding;		/* NRZ/NRZI/FM0/FM1, etc. */
 	char station;			/* DTE/DCE, primary/secondary, etc. */
 	char connection;		/* permanent/switched/on-demand */
 	unsigned hw_opt[4];		/* other hardware options */
+	unsigned char enable_IPX;	/* Enable or Disable IPX */
+	unsigned long network_number;	/* Network Number for IPX */
 					/****** status and statistics *******/
 	char state;			/* device state */
 	unsigned modem_status;		/* modem status */
diff -u --recursive --new-file v2.1.78/linux/include/net/addrconf.h linux/include/net/addrconf.h
--- v2.1.78/linux/include/net/addrconf.h	Thu Sep  4 17:07:31 1997
+++ linux/include/net/addrconf.h	Mon Jan 12 15:28:19 1998
@@ -40,10 +40,6 @@
 
 #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);
 
@@ -55,7 +51,8 @@
 extern int			addrconf_del_ifaddr(void *arg);
 extern int			addrconf_set_dstaddr(void *arg);
 
-extern struct inet6_ifaddr *	ipv6_chk_addr(struct in6_addr *addr);
+extern struct inet6_ifaddr *	ipv6_chk_addr(struct in6_addr *addr,
+					      struct device *dev, int nd);
 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);
@@ -64,10 +61,10 @@
  *	multicast prototypes (mcast.c)
  */
 extern int			ipv6_sock_mc_join(struct sock *sk, 
-						  struct device *dev, 
+						  int ifindex, 
 						  struct in6_addr *addr);
 extern int			ipv6_sock_mc_drop(struct sock *sk,
-						  struct device *dev, 
+						  int ifindex, 
 						  struct in6_addr *addr);
 extern void			ipv6_sock_mc_close(struct sock *sk);
 
@@ -75,6 +72,10 @@
 						struct in6_addr *addr);
 extern int			ipv6_dev_mc_dec(struct device *dev,
 						struct in6_addr *addr);
+extern void			ipv6_mc_up(struct inet6_dev *idev);
+extern void			ipv6_mc_down(struct inet6_dev *idev);
+extern void			ipv6_mc_destroy_dev(struct inet6_dev *idev);
+extern void			addrconf_dad_failure(struct inet6_ifaddr *ifp);
 
 extern int			ipv6_chk_mcast_addr(struct device *dev,
 						    struct in6_addr *addr);
@@ -115,22 +116,32 @@
  *	compute link-local solicited-node multicast address
  */
 
-static __inline__ void addrconf_addr_solict_mult(struct in6_addr *addr,
-						 struct in6_addr *solicited)
+extern __inline__ void addrconf_addr_solict_mult_old(struct in6_addr *addr,
+						     struct in6_addr *solicited)
 {
 	ipv6_addr_set(solicited,
 		      __constant_htonl(0xFF020000), 0,
 		      __constant_htonl(0x1), addr->s6_addr32[3]);
 }
 
-static __inline__ void ipv6_addr_all_nodes(struct in6_addr *addr)
+extern __inline__ void addrconf_addr_solict_mult_new(struct in6_addr *addr,
+						     struct in6_addr *solicited)
+{
+	ipv6_addr_set(solicited,
+		      __constant_htonl(0xFF020000), 0,
+		      __constant_htonl(0x1),
+		      __constant_htonl(0xFF000000) | addr->s6_addr32[3]);
+}
+
+
+extern __inline__ void ipv6_addr_all_nodes(struct in6_addr *addr)
 {
 	ipv6_addr_set(addr,
 		      __constant_htonl(0xFF020000), 0, 0,
 		      __constant_htonl(0x1));
 }
 
-static __inline__ void ipv6_addr_all_routers(struct in6_addr *addr)
+extern __inline__ void ipv6_addr_all_routers(struct in6_addr *addr)
 {
 	ipv6_addr_set(addr,
 		      __constant_htonl(0xFF020000), 0, 0,
diff -u --recursive --new-file v2.1.78/linux/include/net/arp.h linux/include/net/arp.h
--- v2.1.78/linux/include/net/arp.h	Mon Jul  7 16:04:03 1997
+++ linux/include/net/arp.h	Mon Jan 12 16:46:43 1998
@@ -4,45 +4,18 @@
 
 #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 struct neigh_table arp_tbl;
 
 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 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, 
 			 unsigned char *dest_hw, unsigned char *src_hw, unsigned char *th);
-extern int	arp_req_set(struct arpreq *r, struct device *dev);
-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 neighbour *arp_find_neighbour(struct dst_entry *dst, int);
+extern int	arp_bind_neighbour(struct dst_entry *dst);
+extern int	arp_mc_map(u32 addr, u8 *haddr, struct device *dev, int dir);
+extern void	arp_ifdown(struct device *dev);
 #endif	/* _ARP_H */
diff -u --recursive --new-file v2.1.78/linux/include/net/checksum.h linux/include/net/checksum.h
--- v2.1.78/linux/include/net/checksum.h	Mon Jul  7 16:04:03 1997
+++ linux/include/net/checksum.h	Mon Jan 12 16:46:43 1998
@@ -29,6 +29,7 @@
 #include <asm/types.h>
 #include <asm/byteorder.h>
 #include <net/ip.h>
+#include <asm/uaccess.h>
 #include <asm/checksum.h>
 
 #ifndef _HAVE_ARCH_IPV6_CSUM
@@ -89,6 +90,21 @@
 	return csum_fold(csum);
 }
 
+#endif
+
+#ifndef _HAVE_ARCH_COPY_AND_CSUM_FROM_USER
+extern __inline__
+unsigned int csum_and_copy_from_user (const char *src, char *dst,
+				      int len, int sum, int *err_ptr)
+{
+	if (verify_area(VERIFY_READ, src, len) == 0)
+		return csum_partial_copy_from_user(src, dst, len, sum, err_ptr);
+
+	if (len)
+		*err_ptr = -EFAULT;
+
+	return sum;
+}
 #endif
 
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/net/dst.h linux/include/net/dst.h
--- v2.1.78/linux/include/net/dst.h	Mon Dec  1 12:04:15 1997
+++ linux/include/net/dst.h	Mon Jan 12 16:46:43 1998
@@ -33,9 +33,8 @@
 	atomic_t		refcnt;		/* tree/hash references	*/
 	atomic_t		use;		/* client references	*/
 	struct device	        *dev;
-	char			obsolete;
-	char			priority;
-	char			__pad1, __pad2;
+	int			obsolete;
+	__u32			priority;
 	unsigned long		lastuse;
 	unsigned		window;
 	unsigned		pmtu;
@@ -60,10 +59,12 @@
 struct dst_ops
 {
 	unsigned short		family;
+	unsigned short		protocol;
 	struct dst_entry *	(*check)(struct dst_entry *, __u32 cookie);
 	struct dst_entry *	(*reroute)(struct dst_entry *,
 					   struct sk_buff *);
 	void			(*destroy)(struct dst_entry *);
+	struct dst_entry *	(*negative_advice)(struct dst_entry *);
 };
 
 #ifdef __KERNEL__
@@ -71,7 +72,7 @@
 extern struct dst_entry * dst_garbage_list;
 extern atomic_t	dst_total;
 
-static __inline__
+extern __inline__
 struct dst_entry * dst_clone(struct dst_entry * dst)
 {
 	if (dst)
@@ -79,14 +80,14 @@
 	return dst;
 }
 
-static __inline__
+extern __inline__
 void dst_release(struct dst_entry * dst)
 {
 	if (dst)
 		atomic_dec(&dst->use);
 }
 
-static __inline__
+extern __inline__
 struct dst_entry * dst_check(struct dst_entry ** dst_p, u32 cookie)
 {
 	struct dst_entry * dst = *dst_p;
@@ -95,7 +96,7 @@
 	return (*dst_p = dst);
 }
 
-static __inline__
+extern __inline__
 struct dst_entry * dst_reroute(struct dst_entry ** dst_p, struct sk_buff *skb)
 {
 	struct dst_entry * dst = *dst_p;
@@ -104,21 +105,12 @@
 	return (*dst_p = dst);
 }
 
-static __inline__
-void dst_destroy(struct dst_entry * dst)
-{
-	if (dst->neighbour)
-		neigh_release(dst->neighbour);
-	if (dst->ops->destroy)
-		dst->ops->destroy(dst);
-	kfree(dst);
-	atomic_dec(&dst_total);
-}
 
 extern void * dst_alloc(int size, struct dst_ops * ops);
 extern void __dst_free(struct dst_entry * dst);
+extern void dst_destroy(struct dst_entry * dst);
 
-static __inline__
+extern __inline__
 void dst_free(struct dst_entry * dst)
 {
 	if (!atomic_read(&dst->use)) {
@@ -126,6 +118,19 @@
 		return;
 	}
 	__dst_free(dst);
+}
+
+extern __inline__ void dst_confirm(struct dst_entry *dst)
+{
+	if (dst)
+		neigh_confirm(dst->neighbour);
+}
+
+extern __inline__ void dst_negative_advice(struct dst_entry **dst_p)
+{
+	struct dst_entry * dst = *dst_p;
+	if (dst && dst->ops->negative_advice)
+		*dst_p = dst->ops->negative_advice(dst);
 }
 #endif
 
diff -u --recursive --new-file v2.1.78/linux/include/net/if_inet6.h linux/include/net/if_inet6.h
--- v2.1.78/linux/include/net/if_inet6.h	Thu Mar 27 14:40:11 1997
+++ linux/include/net/if_inet6.h	Mon Jan 12 15:28:19 1998
@@ -15,15 +15,21 @@
 #ifndef _NET_IF_INET6_H
 #define _NET_IF_INET6_H
 
+/* These flags match corresponding IFA_F_* flags but ADDR_INVALID,
+   which is invisible externally.
+ */
+
+#define ADDR_PERMANENT	0x80
+
 #define DAD_COMPLETE	0x00
-#define DAD_INCOMPLETE	0x01
-#define DAD_STATUS	0x01
+#define DAD_INCOMPLETE	0x40
+#define DAD_STATUS	0x40
+
+#define ADDR_STATUS	0x21
+#define ADDR_DEPRECATED 0x20
+#define ADDR_INVALID	0x01
 
-#define ADDR_STATUS	0x06
-#define ADDR_DEPRECATED 0x02
-#define ADDR_INVALID	0x04
 
-#define ADDR_PERMANENT	0x80
 
 #define IF_RA_RCVD	0x20
 #define IF_RS_SENT	0x10
@@ -53,22 +59,25 @@
 };
 
 
-struct ipv6_mc_socklist {
+struct ipv6_mc_socklist
+{
 	struct in6_addr		addr;
-	struct device		*dev;
+	int			ifindex;
 	struct ipv6_mc_socklist *next;
 };
 
 #define MAF_TIMER_RUNNING	0x01
 #define MAF_LAST_REPORTER	0x02
+#define MAF_LOADED		0x04
 
-struct ifmcaddr6 {
+struct ifmcaddr6
+{
 	struct in6_addr		mca_addr;
 	struct device		*dev;
 	struct ifmcaddr6	*next;
 	struct ifmcaddr6	*if_next;
 	struct timer_list	mca_timer;
-	unsigned long		mca_flags;
+	unsigned		mca_flags;
 	atomic_t		mca_users;	
 };
 
@@ -77,8 +86,6 @@
 #define	IFA_SITE	IPV6_ADDR_SITELOCAL
 #define	IFA_GLOBAL	0x0000U
 
-extern int		in6_ifnum;
-
 struct inet6_dev 
 {
 	struct device		*dev;
@@ -90,11 +97,12 @@
 	__u32			router:1,
 				unused:31;
 
+	struct neigh_parms	*nd_parms;
 	struct inet6_dev	*next;
 };
 
 
-extern __inline__ void ipv6_mc_map(struct in6_addr *addr, char *buf)
+extern __inline__ void ipv6_eth_mc_map(struct in6_addr *addr, char *buf)
 {
 	/*
 	 *	+-------+-------+-------+-------+-------+-------+
diff -u --recursive --new-file v2.1.78/linux/include/net/ip.h linux/include/net/ip.h
--- v2.1.78/linux/include/net/ip.h	Mon Dec  1 12:04:15 1997
+++ linux/include/net/ip.h	Mon Jan 12 16:46:43 1998
@@ -28,6 +28,7 @@
 #include <linux/inetdevice.h>
 #include <linux/in_route.h>
 #include <net/route.h>
+#include <net/arp.h>
 
 #ifndef _SNMP_H
 #include <net/snmp.h>
@@ -96,7 +97,7 @@
 #else
 #define ip_acct_output	dev_queue_xmit
 #endif
-extern void		ip_fragment(struct sk_buff *skb, int, int (*out)(struct sk_buff*));
+extern void		ip_fragment(struct sk_buff *skb, int (*out)(struct sk_buff*));
 extern struct sk_buff *	ip_reply(struct sk_buff *skb, int payload);
 extern int		ip_do_nat(struct sk_buff *skb);
 extern void		ip_send_check(struct iphdr *ip);
@@ -109,33 +110,13 @@
 						   unsigned int,
 						   unsigned int),
 				      const void *frag,
-				      unsigned short int length,
+				      unsigned length,
 				      struct ipcm_cookie *ipc,
 				      struct rtable *rt,
 				      int flags);
 
+extern int __ip_finish_output(struct sk_buff *skb);
 
-static __inline__
-void ip_send(struct sk_buff *skb)
-{
-	ip_ll_header(skb);
-
-	if (skb->len > skb->dev->mtu + skb->dev->hard_header_len)
-		ip_fragment(skb, 0, ip_acct_output);
-	else
-		ip_acct_output(skb);
-}
-
-static __inline__
-int ip_decrease_ttl(struct iphdr *iph)
-{
-	u16 check = iph->check;
-	check = ntohs(check) + 0x0100;
-	if ((check & 0xFF00) == 0)
-		check++;		/* carry overflow */
-	iph->check = htons(check);
-	return --iph->ttl;
-}
 
 extern struct ip_mib	ip_statistics;
 
@@ -159,6 +140,72 @@
 extern int sysctl_local_port_range[2];
 
 #define IS_ROUTER	(ip_statistics.IpForwarding == 1)
+
+extern __inline__ int ip_finish_output(struct sk_buff *skb)
+{
+	struct dst_entry *dst = skb->dst;
+	struct device *dev = dst->dev;
+	struct hh_cache *hh = dst->hh;
+
+	skb->dev = dev;
+	skb->protocol = __constant_htons(ETH_P_IP);
+
+	if (hh) {
+#ifdef __alpha__
+		/* Alpha has disguisting memcpy. Help it. */
+	        u64 *aligned_hdr = (u64*)(skb->data - 16);
+		u64 *aligned_hdr0 = hh->hh_data;
+		aligned_hdr[0] = aligned_hdr0[0];
+		aligned_hdr[1] = aligned_hdr0[1];
+#else
+		memcpy(skb->data - 16, hh->hh_data, 16);
+#endif
+	        skb_push(skb, dev->hard_header_len);
+		return hh->hh_output(skb);
+	} else if (dst->neighbour)
+		return dst->neighbour->output(skb);
+
+	printk(KERN_DEBUG "khm\n");
+	kfree_skb(skb, FREE_WRITE);
+	return -EINVAL;
+}
+
+extern __inline__ void ip_send(struct sk_buff *skb)
+{
+	if (skb->len > skb->dst->pmtu)
+		ip_fragment(skb, __ip_finish_output);
+	else
+		ip_finish_output(skb);
+}
+
+static __inline__
+int ip_decrease_ttl(struct iphdr *iph)
+{
+	u16 check = iph->check;
+	check = ntohs(check) + 0x0100;
+	if ((check & 0xFF00) == 0)
+		check++;		/* carry overflow */
+	iph->check = htons(check);
+	return --iph->ttl;
+}
+
+/*
+ *	Map a multicast IP onto multicast MAC for type ethernet.
+ */
+
+extern __inline__ void ip_eth_mc_map(u32 addr, char *buf)
+{
+	addr=ntohl(addr);
+	buf[0]=0x01;
+	buf[1]=0x00;
+	buf[2]=0x5e;
+	buf[5]=addr&0xFF;
+	addr>>=8;
+	buf[4]=addr&0xFF;
+	addr>>=8;
+	buf[3]=addr&0x7F;
+}
+
 
 extern int	ip_call_ra_chain(struct sk_buff *skb);
 
diff -u --recursive --new-file v2.1.78/linux/include/net/ip6_fib.h linux/include/net/ip6_fib.h
--- v2.1.78/linux/include/net/ip6_fib.h	Thu Mar 27 14:40:11 1997
+++ linux/include/net/ip6_fib.h	Mon Jan 12 15:28:19 1998
@@ -19,6 +19,7 @@
 
 #include <net/dst.h>
 #include <net/flow.h>
+#include <linux/rtnetlink.h>
 
 struct rt6_info;
 
@@ -66,8 +67,8 @@
 	
 	int				rt6i_keylen;
 
-	unsigned long			rt6i_flags;
-	unsigned long			rt6i_metric;
+	u32				rt6i_flags;
+	u32				rt6i_metric;
 	unsigned long			rt6i_expires;
 
 	union {
@@ -137,17 +138,7 @@
 
 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);
+extern void			inet6_rt_notify(int event, struct rt6_info *rt);
 
 #endif
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/net/ip6_route.h linux/include/net/ip6_route.h
--- v2.1.78/linux/include/net/ip6_route.h	Thu Mar 27 14:40:11 1997
+++ linux/include/net/ip6_route.h	Mon Jan 12 15:28:19 1998
@@ -55,6 +55,9 @@
 extern int			ip6_rt_addr_add(struct in6_addr *addr,
 						struct device *dev);
 
+extern int			ip6_rt_addr_del(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,
@@ -87,7 +90,13 @@
 						   struct device *dev,
 						   int pmtu);
 
-extern struct rt6_info *	ip6_rt_copy(struct rt6_info *rt);
+struct nlmsghdr;
+struct netlink_callback;
+extern int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb);
+extern int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
+extern int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
+
+extern void rt6_ifdown(struct device *dev);
 
 /*
  *	Store a destination cache entry in a socket
@@ -100,7 +109,7 @@
 	struct rt6_info *rt;
 		
 	np = &sk->net_pinfo.af_inet6;
-	np->dst = dst;
+	sk->dst_cache = dst;
 	
 	rt = (struct rt6_info *) dst;
 	
diff -u --recursive --new-file v2.1.78/linux/include/net/ip_fib.h linux/include/net/ip_fib.h
--- v2.1.78/linux/include/net/ip_fib.h	Mon Dec  1 12:04:15 1997
+++ linux/include/net/ip_fib.h	Mon Jan 12 15:28:19 1998
@@ -128,8 +128,9 @@
 
 extern __inline__ int fib_lookup(const struct rt_key *key, struct fib_result *res)
 {
-	if (local_table->tb_lookup(local_table, key, res))
-		return main_table->tb_lookup(main_table, key, res);
+	if (local_table->tb_lookup(local_table, key, res) &&
+	    main_table->tb_lookup(main_table, key, res))
+		return -ENETUNREACH;
 	return 0;
 }
 
@@ -180,7 +181,7 @@
 extern int fib_dump_info(struct sk_buff *skb, pid_t pid, u32 seq, int event,
 			 u8 tb_id, u8 type, u8 scope, void *dst, int dst_len, u8 tos,
 			 struct fib_info *fi);
-extern int fib_sync_down(u32 local, struct device *dev);
+extern int fib_sync_down(u32 local, struct device *dev, int force);
 extern int fib_sync_up(struct device *dev);
 extern int fib_convert_rtentry(int cmd, struct nlmsghdr *nl, struct rtmsg *rtm,
 			       struct kern_rta *rta, struct rtentry *r);
diff -u --recursive --new-file v2.1.78/linux/include/net/ipv6.h linux/include/net/ipv6.h
--- v2.1.78/linux/include/net/ipv6.h	Mon Jul  7 16:04:08 1997
+++ linux/include/net/ipv6.h	Mon Jan 12 16:46:57 1998
@@ -4,7 +4,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: ipv6.h,v 1.6 1997/04/01 02:22:58 davem Exp $
+ *	$Id: ipv6.h,v 1.8 1997/12/29 19:52:09 kuznet Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -94,12 +94,6 @@
 	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;
@@ -236,7 +230,7 @@
 					       inet_getfrag_t getfrag,
 					       const void *data,
 					       struct flowi *fl,
-					       unsigned short length,
+					       unsigned length,
 					       struct ipv6_options *opt,
 					       int hlimit, int flags);
 
@@ -244,6 +238,7 @@
  *	skb processing functions
  */
 
+extern int			ip6_output(struct sk_buff *skb);
 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);
diff -u --recursive --new-file v2.1.78/linux/include/net/ndisc.h linux/include/net/ndisc.h
--- v2.1.78/linux/include/net/ndisc.h	Mon Jul  7 16:04:08 1997
+++ linux/include/net/ndisc.h	Mon Jan 12 16:46:57 1998
@@ -1,34 +1,6 @@
 #ifndef _NDISC_H
 #define _NDISC_H
 
-
-/*
- *	Neighbor Cache Entry States (7.3.2.)
- */
-
-/*
- *	The lsb is set for states that have a timer associated
- */
-
-#define NUD_NONE	0x00
-#define NUD_INCOMPLETE	0x11
-#define NUD_REACHABLE	0x20
-#define NUD_STALE	0x30
-#define NUD_DELAY	0x41
-#define NUD_PROBE	0x51
-#define NUD_FAILED	0x60	/* neighbour discovery failed	*/
-
-#define NUD_IN_TIMER	0x01
-
-#define NDISC_QUEUE_LEN	3
-
-#define NCF_NOARP		0x0100	/* no ARP needed on this device */
-#define NCF_SUBNET		0x0200  /* NC entry for subnet		*/
-#define NCF_INVALID		0x0400
-#define NCF_DELAY_EXPIRED	0x0800	/* time to move to PROBE	*/
-#define NCF_ROUTER		0x1000	/* neighbour is a router	*/
-#define NCF_HHVALID		0x2000	/* Hardware header is valid	*/
-
 /*
  *	ICMP codes for neighbour discovery messages
  */
@@ -51,13 +23,11 @@
 
 #define MAX_RTR_SOLICITATION_DELAY	HZ
 
-#define RECHABLE_TIME			(30*HZ)
-#define RETRANS_TIMER			HZ
-
-#define MIN_RANDOM_FACTOR		(1/2)
-#define MAX_RANDOM_FACTOR		(3/2)
+#define ND_REACHABLE_TIME		(30*HZ)
+#define ND_RETRANS_TIMER		HZ
 
-#define REACH_RANDOM_INTERVAL		(60*60*HZ)	/* 1 hour */
+#define ND_MIN_RANDOM_FACTOR		(1/2)
+#define ND_MAX_RANDOM_FACTOR		(3/2)
 
 #ifdef __KERNEL__
 
@@ -67,28 +37,7 @@
 #include <net/neighbour.h>
 #include <asm/atomic.h>
 
-/*
- *	neighbour cache entry
- *	used by neighbour discovery module
- */
-
-struct nd_neigh {
-	struct neighbour	neigh;
-	struct in6_addr		ndn_addr;	/* next hop addr */
-
-	__u8			ndn_plen,	/* prefix len	 */
-				ndn_type,	/* {unicast, multicast} */
-				ndn_nud_state,
-				ndn_probes;
-	
-	unsigned long		ndn_expires;	/* timer expires at	*/
-
-#define ndn_refcnt		neigh.refcnt
-#define ndn_tstamp		neigh.lastused
-#define ndn_dev			neigh.dev
-#define ndn_flags		neigh.flags
-#define ndn_ha			neigh.ha
-};
+extern struct neigh_table nd_tbl;
 
 struct nd_msg {
         struct icmp6hdr	icmph;
@@ -106,25 +55,8 @@
 	__u32			retrans_timer;
 };
 
-struct ndisc_statistics {
-	__u32	allocs;			/* allocated entries		*/
-	__u32	free_delayed;		/* zombie entries		*/
-	__u32	snt_probes_ucast;	/* ns probes sent (ucast)	*/
-	__u32	snt_probes_mcast;	/* ns probes sent (mcast)	*/
-	__u32	rcv_probes_ucast;	/* ns probes rcv  (ucast)	*/
-	__u32	rcv_probes_mcast;	/* ns probes rcv  (mcast)	*/
-	__u32	rcv_upper_conf;		/* confirmations from upper layers */
-	__u32	res_failed;		/* address resolution failures	*/
-};
-
-extern struct neighbour *	ndisc_find_neigh(struct device *dev, 
-						 struct in6_addr *addr);
-
-extern void			ndisc_validate(struct neighbour *neigh);
 
 extern void			ndisc_init(struct net_proto_family *ops);
-extern struct neighbour*	ndisc_get_neigh(struct device *dev,
-						struct in6_addr *addr);
 extern void			ndisc_cleanup(void);
 
 extern int			ndisc_rcv(struct sk_buff *skb,
@@ -134,9 +66,6 @@
 					  struct ipv6_options *opt,
 					  unsigned short len);
 
-extern void			ndisc_event_send(struct neighbour *neigh,
-						 struct sk_buff *skb);
-
 extern void			ndisc_send_ns(struct device *dev,
 					      struct neighbour *neigh,
 					      struct in6_addr *solicit,
@@ -147,9 +76,6 @@
 					      struct in6_addr *saddr,
 					      struct in6_addr *daddr);
 
-extern int			ndisc_eth_resolv(unsigned char *,
-						struct sk_buff *);
-
 extern void			ndisc_forwarding_on(void);
 extern void			ndisc_forwarding_off(void);
 
@@ -157,10 +83,10 @@
 						    struct neighbour *neigh,
 						    struct in6_addr *target);
 
-struct rt6_info *		dflt_rt_lookup(void);
+extern int			ndisc_mc_map(struct in6_addr *addr, char *buf, struct device *dev, int dir);
 
-extern unsigned long	nd_rand_seed;
-extern unsigned long	ipv6_random(void);
+
+struct rt6_info *		dflt_rt_lookup(void);
 
 /*
  *	IGMP
@@ -174,6 +100,22 @@
 extern int			igmp6_event_report(struct sk_buff *skb,
 						   struct icmp6hdr *hdr,
 						   int len);
+
+extern __inline__ struct neighbour * ndisc_get_neigh(struct device *dev, struct in6_addr *addr)
+{
+
+	if (dev) {
+		struct neighbour *neigh;
+
+		start_bh_atomic();
+		neigh = __neigh_lookup(&nd_tbl, addr, dev, 1);
+		end_bh_atomic();
+
+		return neigh;
+	}
+	return NULL;
+}
+
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.78/linux/include/net/neighbour.h linux/include/net/neighbour.h
--- v2.1.78/linux/include/net/neighbour.h	Mon Jul  7 16:04:02 1997
+++ linux/include/net/neighbour.h	Mon Jan 12 16:46:43 1998
@@ -4,8 +4,41 @@
 /*
  *	Generic neighbour manipulation
  *
- *	authors:
+ *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
+ *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
+ */
+
+/* The following flags & states are exported to user space,
+   so that they should be moved to include/linux/ directory.
+ */
+
+/*
+ *	Neighbor Cache Entry Flags
+ */
+
+#define NTF_PROXY	0x08	/* == ATF_PUBL */
+#define NTF_ROUTER	0x80
+
+/*
+ *	Neighbor Cache Entry States.
+ */
+
+#define NUD_INCOMPLETE	0x01
+#define NUD_REACHABLE	0x02
+#define NUD_STALE	0x04
+#define NUD_DELAY	0x08
+#define NUD_PROBE	0x10
+#define NUD_FAILED	0x20
+
+/* Dummy states */
+#define NUD_NOARP	0x40
+#define NUD_PERMANENT	0x80
+#define NUD_NONE	0x00
+
+/* NUD_NOARP & NUD_PERMANENT are pseudostates, they never change
+   and make no address resolution or NUD.
+   NUD_PERMANENT is also cannot be deleted by garbage collectors.
  */
 
 #ifdef __KERNEL__
@@ -13,37 +46,152 @@
 #include <asm/atomic.h>
 #include <linux/skbuff.h>
 
-/*
- *	flags
- *
- */
-#define NTF_COMPLETE	0x02
-#define NTF_PERMANENT	0x04
+#define NUD_IN_TIMER	(NUD_INCOMPLETE|NUD_DELAY|NUD_PROBE)
+#define NUD_VALID	(NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
+#define NUD_CONNECTED	(NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)
 
-struct neighbour {
+struct neigh_parms
+{
+	struct neigh_parms *next;
+	void	*sysctl_table;
+	int	base_reachable_time;
+	int	retrans_time;
+	int	gc_staletime;
+	int	reachable_time;
+	int	delay_probe_time;
+
+	int	queue_len;
+	int	ucast_probes;
+	int	app_probes;
+	int	mcast_probes;
+	int	anycast_delay;
+	int	proxy_delay;
+	int	proxy_qlen;
+	int	locktime;
+};
+
+struct neigh_statistics
+{
+	unsigned long allocs;
+	unsigned long res_failed;
+	unsigned long rcv_probes_mcast;
+	unsigned long rcv_probes_ucast;
+};
+
+struct neighbour
+{
 	struct neighbour	*next;
-	struct neighbour	*prev;
 	struct neigh_table	*tbl;
+	struct neigh_parms	*parms;
 	struct device		*dev;
-	unsigned long		lastused;
-	unsigned long		flags;
+	unsigned long		used;
+	unsigned long		confirmed;
+	unsigned long		updated;
+	__u8			flags;
+	__u8			nud_state;
+	__u8			type;
+	__u8			probes;
 	unsigned char		ha[MAX_ADDR_LEN];
 	struct hh_cache		*hh;
 	atomic_t		refcnt;
-	struct neigh_ops	*ops;
+	int			(*output)(struct sk_buff *skb);
 	struct sk_buff_head	arp_queue;
-	char			primary_key[0];
+	struct timer_list	timer;
+	struct neigh_ops	*ops;
+	u8			primary_key[0];
 };
 
-struct neigh_ops {
+struct neigh_ops
+{
 	int			family;
-	unsigned int		(*hash)(void *primary_key);
-	int			(*resolve)(unsigned char *h_dest,
-					   struct sk_buff *skb);
 	void			(*destructor)(struct neighbour *);
+	void			(*solicit)(struct neighbour *, struct sk_buff*);
+	void			(*error_report)(struct neighbour *, struct sk_buff*);
+	int			(*output)(struct sk_buff*);
+	int			(*connected_output)(struct sk_buff*);
+	int			(*hh_output)(struct sk_buff*);
+	int			(*queue_xmit)(struct sk_buff*);
 };
 
-extern struct neighbour		*neigh_alloc(int size, struct neigh_ops *);
+struct pneigh_entry
+{
+	struct pneigh_entry	*next;
+	struct device		*dev;
+	u8			key[0];
+};
+
+#define NEIGH_HASHMASK		0x1F
+#define PNEIGH_HASHMASK		0xF
+
+/*
+ *	neighbour table manipulation
+ */
+
+
+struct neigh_table
+{
+	struct neigh_table	*next;
+	int			family;
+	int			entry_size;
+	int			key_len;
+	int			(*constructor)(struct neighbour *);
+	int			(*pconstructor)(struct pneigh_entry *);
+	void			(*pdestructor)(struct pneigh_entry *);
+	void			(*proxy_redo)(struct sk_buff *skb);
+	struct neigh_parms	parms;
+	/* HACK. gc_* shoul follow parms without a gap! */
+	int			gc_interval;
+	int			gc_thresh1;
+	int			gc_thresh2;
+	int			gc_thresh3;
+	unsigned long		last_flush;
+	struct timer_list 	gc_timer;
+	struct timer_list 	proxy_timer;
+	struct sk_buff_head	proxy_queue;
+	int			entries;
+	atomic_t		lock;
+	unsigned long		last_rand;
+	struct neigh_parms	*parms_list;
+	struct neigh_statistics	stats;
+	struct neighbour	*hash_buckets[NEIGH_HASHMASK+1];
+	struct pneigh_entry	*phash_buckets[PNEIGH_HASHMASK+1];
+};
+
+extern void			neigh_table_init(struct neigh_table *tbl);
+extern int			neigh_table_clear(struct neigh_table *tbl);
+extern struct neighbour		*__neigh_lookup(struct neigh_table *tbl,
+					       const void *pkey, struct device *dev,
+					       int creat);
+extern void			neigh_destroy(struct neighbour *neigh);
+extern int			__neigh_event_send(struct neighbour *neigh, struct sk_buff *skb);
+extern int			neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int arp);
+extern int			neigh_ifdown(struct neigh_table *tbl, struct device *dev);
+extern int			neigh_resolve_output(struct sk_buff *skb);
+extern int			neigh_connected_output(struct sk_buff *skb);
+extern int			neigh_compat_output(struct sk_buff *skb);
+extern struct neighbour 	*neigh_event_ns(struct neigh_table *tbl,
+						u8 *lladdr, void *saddr,
+						struct device *dev);
+
+extern struct neigh_parms	*neigh_parms_alloc(struct neigh_table *tbl);
+extern void			neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms);
+extern unsigned long		neigh_rand_reach_time(unsigned long base);
+
+extern void			pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
+					       struct sk_buff *skb);
+extern struct pneigh_entry	*pneigh_lookup(struct neigh_table *tbl, const void *key, struct device *dev, int creat);
+extern int			pneigh_delete(struct neigh_table *tbl, const void *key, struct device *dev);
+
+struct netlink_callback;
+struct nlmsghdr;
+extern int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb);
+extern int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
+extern int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
+extern void neigh_app_ns(struct neighbour *n);
+
+extern void 			*neigh_sysctl_register(struct device *dev, struct neigh_parms *p,
+						       int p_id, int pdev_id, char *p_name);
+extern void			neigh_sysctl_unregister(struct neigh_parms *p);
 
 /*
  *	Neighbour references
@@ -51,122 +199,66 @@
  *	When neighbour pointers are passed to "client" code the
  *	reference count is increased. The count is 0 if the node
  *	is only referenced by the corresponding table.
- *
- *	Nodes cannot be unlinked from the table if their
- *	reference count != 0.
- *
- *	i.e. you can't reclaim a neighbour if it is being used by a
- *	dst_cache or routing entry - hopefully those will react
- *	to memory shortage and GC their unused entries
  */
 
-
-static __inline__ void neigh_release(struct neighbour *neigh)
+extern __inline__ void neigh_release(struct neighbour *neigh)
 {
-	if (atomic_dec_and_test(&neigh->refcnt))
-		neigh->lastused = jiffies;
+	if (atomic_dec_and_test(&neigh->refcnt) && neigh->tbl == NULL)
+		neigh_destroy(neigh);
 }
 
-static __inline__ struct neighbour * neighbour_clone(struct neighbour *neigh)
+extern __inline__ struct neighbour * neigh_clone(struct neighbour *neigh)
 {
 	if (neigh)
 		atomic_inc(&neigh->refcnt);
 	return neigh;
 }
 
-#define NT_MASK_QUEUE	0x01
-#define NT_MASK_GC	0x02
-
-/*
- *	neighbour table manipulation
- */
-
-struct neigh_table {
-	int			tbl_size;	/* num. of hash	buckets	*/
-	int			tbl_entries;	/* entry count		*/
-	struct neighbour	**hash_buckets;
-	atomic_t		tbl_lock;
-	unsigned int		tbl_bh_mask;	/* bh mask		*/
-	struct neigh_ops	*neigh_ops;
-	struct neighbour	*request_queue; /* pending inserts	*/
-};
-
-extern void			neigh_table_init(struct neigh_table *tbl,
-						 struct neigh_ops *ops,
-						 int size);
-extern void			neigh_table_destroy(struct neigh_table *tbl);
-
-extern void			neigh_table_run_bh(struct neigh_table *tbl);
-
-extern void			neigh_table_ins(struct neigh_table *tbl,
-						struct neighbour *neigh);
-
-extern void			neigh_queue_ins(struct neigh_table *tbl,
-						struct neighbour *neigh);
-
-extern void			neigh_unlink(struct neighbour *neigh);
-
-extern struct neighbour *	neigh_lookup(struct neigh_table *tbl,
-					     void *pkey, int key_len,
-					     struct device *dev);
+extern __inline__ void neigh_confirm(struct neighbour *neigh)
+{
+	if (neigh)
+		neigh->confirmed = jiffies;
+}
 
-extern void			neigh_destroy(struct neighbour *neigh);
 
-static __inline__ void neigh_insert(struct neigh_table *tbl,
-				    struct neighbour *neigh)
+extern __inline__ struct neighbour *
+neigh_lookup(struct neigh_table *tbl, const void *pkey, struct device *dev)
 {
+	struct neighbour *neigh;
 	start_bh_atomic();
-	if (atomic_read(&tbl->tbl_lock) == 1)
-	{
-		neigh_table_ins(tbl, neigh);
-	}
-	else
-	{
-		tbl->tbl_bh_mask |= NT_MASK_QUEUE;
-		neigh_queue_ins(tbl, neigh);
-	}
-	end_bh_atomic();	
+	neigh = __neigh_lookup(tbl, pkey, dev, 0);
+	end_bh_atomic();
+	return neigh;
 }
 
+extern __inline__ int neigh_is_connected(struct neighbour *neigh)
+{
+	return neigh->nud_state&NUD_CONNECTED;
+}
 
+extern __inline__ int neigh_is_valid(struct neighbour *neigh)
+{
+	return neigh->nud_state&NUD_VALID;
+}
 
-typedef int (*ntbl_examine_t) (struct neighbour *neigh, void *arg);
-
-/*
- *	examine every element of a neighbour table.
- *	For every neighbour the callback function will be called.
- *
- *	parameters:
- *		max	:	max bucket index (<= tbl_size, 0 all)
- *		filter	:	(neigh->flags & (~filter)) -> call func
- *		args	:	opaque pointer
- *
- *	return values
- *		0		nop
- *		!0		unlink node from table and destroy it
- */
-
-extern void			ntbl_walk_table(struct neigh_table *tbl,
-						ntbl_examine_t func,
-						unsigned long filter,
-						int max, void *args);
-
-static __inline__ void neigh_table_lock(struct neigh_table *tbl)
+extern __inline__ int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
 {
-	atomic_inc(&tbl->tbl_lock);
+	neigh->used = jiffies;
+	if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE)))
+		return __neigh_event_send(neigh, skb);
+	return 0;
 }
 
-extern void			neigh_tbl_run_bh(struct neigh_table *tbl);
+extern __inline__ int neigh_table_lock(struct neigh_table *tbl)
+{
+	atomic_inc(&tbl->lock);
+}
 
-static __inline__ void neigh_table_unlock(struct neigh_table *tbl)
+extern __inline__ int neigh_table_unlock(struct neigh_table *tbl)
 {
-	start_bh_atomic();
-	if (atomic_dec_and_test(&tbl->tbl_lock) && tbl->tbl_bh_mask)
-	{
-		neigh_tbl_run_bh(tbl);
-	}
-	end_bh_atomic();
+	atomic_dec(&tbl->lock);
 }
+
 
 #endif
 #endif
diff -u --recursive --new-file v2.1.78/linux/include/net/pkt_sched.h linux/include/net/pkt_sched.h
--- v2.1.78/linux/include/net/pkt_sched.h	Mon Dec  1 12:04:15 1997
+++ linux/include/net/pkt_sched.h	Mon Jan 12 15:28:19 1998
@@ -15,6 +15,7 @@
 	void			(*destroy)(struct Qdisc *);
 	int			(*init)(struct Qdisc *, void *arg);
 	int			(*control)(struct Qdisc *, void *);
+	int 			(*requeue)(struct sk_buff *skb, struct Qdisc *);
 };
 
 struct Qdisc_head
@@ -34,7 +35,6 @@
 	struct Qdisc		*parent;
 	struct sk_buff_head	q;
 	struct device 		*dev;
-	struct sk_buff_head	failure_q;
 	unsigned long		dropped;
 	unsigned long		tx_last;
 	unsigned long		tx_timeo;
@@ -109,7 +109,7 @@
 #define PSCHED_US2JIFFIE(usecs) (((usecs)+(1000000/HZ-1))/(1000000/HZ))
 
 #define PSCHED_TLESS(tv1, tv2) (((tv1).tv_usec < (tv2).tv_usec && \
-				(tv1).tv_sec < (tv2).tv_sec) || \
+				(tv1).tv_sec <= (tv2).tv_sec) || \
 				 (tv1).tv_sec < (tv2).tv_sec)
 
 #define PSCHED_TADD2(tv, delta, tv_res) \
@@ -117,7 +117,7 @@
 	   int __delta = (tv).tv_usec + (delta); \
 	   (tv_res).tv_sec = (tv).tv_sec; \
 	   if (__delta > 1000000) { (tv_res).tv_sec++; __delta -= 1000000; } \
-	   (tv_res).tv_sec = __delta; \
+	   (tv_res).tv_usec = __delta; \
 })
 
 #define PSCHED_TADD(tv, delta) \
diff -u --recursive --new-file v2.1.78/linux/include/net/profile.h linux/include/net/profile.h
--- v2.1.78/linux/include/net/profile.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/profile.h	Mon Jan 12 15:28:19 1998
@@ -0,0 +1,310 @@
+#ifndef _NET_PROFILE_H_
+#define _NET_PROFILE_H_ 1
+
+#ifdef CONFIG_NET_PROFILE
+
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/kernel.h>
+#include <asm/system.h>
+
+struct net_profile_slot
+{
+	char   id[16];
+	struct net_profile_slot *next;
+	struct timeval entered;
+	struct timeval accumulator;
+	struct timeval irq;
+	int    	hits;
+	int    	active;
+	int	underflow;
+};
+
+extern atomic_t net_profile_active;
+extern struct timeval net_profile_adjust;
+extern void net_profile_irq_adjust(struct timeval *entered, struct timeval* leaved);
+
+#if CPU == 586 || CPU == 686
+
+
+extern __inline__ void  net_profile_stamp(struct timeval *pstamp)
+{
+	__asm__ __volatile__ (".byte 0x0f,0x31"
+		:"=a" (pstamp->tv_usec),
+		"=d" (pstamp->tv_sec));
+}
+
+extern __inline__ void  net_profile_accumulate(struct timeval *entered,
+					       struct timeval *leaved,
+					       struct timeval *acc)
+{
+	__asm__ __volatile__ ("subl %2,%0\n\t" 
+			      "sbbl %3,%1\n\t" 
+			      "addl %4,%0\n\t" 
+			      "adcl %5,%1\n\t" 
+			      "subl " SYMBOL_NAME_STR(net_profile_adjust) "+4,%0\n\t" 
+			      "sbbl $0,%1\n\t" 
+			      : "=r" (acc->tv_usec), "=r" (acc->tv_sec)
+			      : "g" (entered->tv_usec), "g" (entered->tv_sec),
+			      "g" (leaved->tv_usec), "g" (leaved->tv_sec),
+			      "0" (acc->tv_usec), "1" (acc->tv_sec));
+}
+
+extern __inline__ void  net_profile_sub(struct timeval *sub,
+					struct timeval *acc)
+{
+	__asm__ __volatile__ ("subl %2,%0\n\t" 
+			      "sbbl %3,%1\n\t" 
+			      : "=r" (acc->tv_usec), "=r" (acc->tv_sec)
+			      : "g" (sub->tv_usec), "g" (sub->tv_sec),
+			      "0" (acc->tv_usec), "1" (acc->tv_sec));
+}
+
+extern __inline__ void  net_profile_add(struct timeval *add,
+					struct timeval *acc)
+{
+	__asm__ __volatile__ ("addl %2,%0\n\t" 
+			      "adcl %3,%1\n\t" 
+			      : "=r" (acc->tv_usec), "=r" (acc->tv_sec)
+			      : "g" (add->tv_usec), "g" (add->tv_sec),
+			      "0" (acc->tv_usec), "1" (acc->tv_sec));
+}
+
+
+#elif defined (__alpha__)
+
+extern __u32 alpha_lo;
+extern long alpha_hi;
+
+/* On alpha cycle counter has only 32 bits :-( :-( */
+
+extern __inline__ void  net_profile_stamp(struct timeval *pstamp)
+{
+	__u32 result;
+	__asm__ __volatile__ ("rpcc %0" : "r="(result));
+	if (result <= alpha_lo)
+		alpha_hi++;
+	alpha_lo = result;
+	pstamp->tv_sec = alpha_hi;
+	pstamp->tv_usec = alpha_lo;
+}
+
+extern __inline__ void  net_profile_accumulate(struct timeval *entered,
+					       struct timeval *leaved,
+					       struct timeval *acc)
+{
+	time_t usecs = acc->tv_usec + leaved->tv_usec - entered->tv_usec
+		- net_profile_adjust.tv_usec;
+	time_t secs = acc->tv_sec + leaved->tv_sec - entered->tv_sec;
+
+	if (usecs >= 0x100000000L) {
+		usecs -= 0x100000000L;
+		secs++;
+	} else if (usecs < -0x100000000L) {
+		usecs += 0x200000000L;
+		secs -= 2;
+	} else if (usecs < 0) {
+		usecs += 0x100000000L;
+		secs--;
+	}
+	acc->tv_sec = secs;
+	acc->tv_usec = usecs;
+}
+
+extern __inline__ void  net_profile_sub(struct timeval *entered,
+					struct timeval *leaved)
+{
+	time_t usecs = leaved->tv_usec - entered->tv_usec;
+	time_t secs = leaved->tv_sec - entered->tv_sec;
+
+	if (usecs < 0) {
+		usecs += 0x100000000L;
+		secs--;
+	}
+	leaved->tv_sec = secs;
+	leaved->tv_usec = usecs;
+}
+
+extern __inline__ void  net_profile_add(struct timeval *entered, struct timeval *leaved)
+{
+	time_t usecs = leaved->tv_usec + entered->tv_usec;
+	time_t secs = leaved->tv_sec + entered->tv_sec;
+
+	if (usecs >= 0x100000000L) {
+		usecs -= 0x100000000L;
+		secs++;
+	}
+	leaved->tv_sec = secs;
+	leaved->tv_usec = usecs;
+}
+
+
+#else
+
+extern __inline__ void  net_profile_stamp(struct timeval *pstamp)
+{
+	/* Not "fast" counterpart! On architectures without
+	   cpu clock "fast" routine is absolutely useless in this
+	   situation. do_gettimeofday still says something on slow-slow-slow
+	   boxes, though it eats more cpu time than the sobject of
+	   investigation :-) :-)
+	 */
+	do_gettimeofday(pstamp);
+}
+
+extern __inline__ void  net_profile_accumulate(struct timeval *entered,
+					       struct timeval *leaved,
+					       struct timeval *acc)
+{
+	time_t usecs = acc->tv_usec + leaved->tv_usec - entered->tv_usec
+		- net_profile_adjust.tv_usec;
+	time_t secs = acc->tv_sec + leaved->tv_sec - entered->tv_sec;
+
+	if (usecs >= 1000000) {
+		usecs -= 1000000;
+		secs++;
+	} else if (usecs < -1000000) {
+		usecs += 2000000;
+		secs -= 2;
+	} else if (usecs < 0) {
+		usecs += 1000000;
+		secs--;
+	}
+	acc->tv_sec = secs;
+	acc->tv_usec = usecs;
+}
+
+extern __inline__ void  net_profile_sub(struct timeval *entered,
+					struct timeval *leaved)
+{
+	time_t usecs = leaved->tv_usec - entered->tv_usec;
+	time_t secs = leaved->tv_sec - entered->tv_sec;
+
+	if (usecs < 0) {
+		usecs += 1000000;
+		secs--;
+	}
+	leaved->tv_sec = secs;
+	leaved->tv_usec = usecs;
+}
+
+extern __inline__ void  net_profile_add(struct timeval *entered, struct timeval *leaved)
+{
+	time_t usecs = leaved->tv_usec + entered->tv_usec;
+	time_t secs = leaved->tv_sec + entered->tv_sec;
+
+	if (usecs >= 1000000) {
+		usecs -= 1000000;
+		secs++;
+	}
+	leaved->tv_sec = secs;
+	leaved->tv_usec = usecs;
+}
+
+
+
+#endif
+
+extern __inline__ void net_profile_enter(struct net_profile_slot *s)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (s->active++ == 0) {
+		net_profile_stamp(&s->entered);
+		atomic_inc(&net_profile_active);
+	}
+	restore_flags(flags);
+}
+
+extern __inline__ void net_profile_leave_irq(struct net_profile_slot *s)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (--s->active <= 0) {
+		if (s->active == 0) {
+			struct timeval curr_pstamp;
+			net_profile_stamp(&curr_pstamp);
+			net_profile_accumulate(&s->entered, &curr_pstamp, &s->accumulator);
+			if (!atomic_dec_and_test(&net_profile_active))
+				net_profile_irq_adjust(&s->entered, &curr_pstamp);
+		} else {
+			s->underflow++;
+		}
+	}
+	s->hits++;
+	restore_flags(flags);
+}
+
+extern __inline__ void net_profile_leave(struct net_profile_slot *s)
+{
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	if (--s->active <= 0) {
+		if (s->active == 0) {
+			struct timeval curr_pstamp;
+			net_profile_stamp(&curr_pstamp);
+			net_profile_accumulate(&s->entered, &curr_pstamp, &s->accumulator);
+			atomic_dec(&net_profile_active);
+		} else {
+			s->underflow++;
+		}
+	}
+	s->hits++;
+	restore_flags(flags);
+}
+
+
+#define NET_PROFILE_ENTER(slot) net_profile_enter(&net_prof_##slot)
+#define NET_PROFILE_LEAVE(slot) net_profile_leave(&net_prof_##slot)
+#define NET_PROFILE_LEAVE_IRQ(slot) net_profile_leave_irq(&net_prof_##slot)
+
+#define NET_PROFILE_SKB_CLEAR(skb) ({ \
+ skb->pstamp.tv_usec = 0; \
+})
+
+#define NET_PROFILE_SKB_INIT(skb) ({ \
+ net_profile_stamp(&skb->pstamp); \
+})
+
+#define NET_PROFILE_SKB_PASSED(skb, slot) ({ \
+ if (skb->pstamp.tv_usec) { \
+   struct timeval cur_pstamp = skb->pstamp; \
+   net_profile_stamp(&skb->pstamp); \
+   net_profile_accumulate(&cur_pstamp, &skb->pstamp, &net_prof_##slot.accumulator); \
+   net_prof_##slot.hits++; \
+ }})
+
+#define NET_PROFILE_DECL(slot) \
+  extern struct net_profile_slot net_prof_##slot;
+
+#define NET_PROFILE_DEFINE(slot) \
+  struct net_profile_slot net_prof_##slot = { #slot, };
+
+#define NET_PROFILE_REGISTER(slot) net_profile_register(&net_prof_##slot)
+#define NET_PROFILE_UNREGISTER(slot) net_profile_unregister(&net_prof_##slot)
+
+extern int net_profile_init(void);
+extern int net_profile_register(struct net_profile_slot *);
+extern int net_profile_unregister(struct net_profile_slot *);
+
+#else
+
+#define NET_PROFILE_ENTER(slot) do { /* nothing */ } while(0)
+#define NET_PROFILE_LEAVE(slot) do { /* nothing */ } while(0)
+#define NET_PROFILE_LEAVE_IRQ(slot) do { /* nothing */ } while(0)
+#define NET_PROFILE_SKB_CLEAR(skb) do { /* nothing */ } while(0)
+#define NET_PROFILE_SKB_INIT(skb) do { /* nothing */ } while(0)
+#define NET_PROFILE_SKB_PASSED(skb, slot) do { /* nothing */ } while(0)
+#define NET_PROFILE_DECL(slot)
+#define NET_PROFILE_DEFINE(slot)
+#define NET_PROFILE_REGISTER(slot) do { /* nothing */ } while(0)
+#define NET_PROFILE_UNREGISTER(slot) do { /* nothing */ } while(0)
+
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.78/linux/include/net/route.h linux/include/net/route.h
--- v2.1.78/linux/include/net/route.h	Mon Dec  1 12:04:15 1997
+++ linux/include/net/route.h	Mon Jan 12 16:46:43 1998
@@ -36,6 +36,11 @@
 #define RT_CACHE_TIMEOUT		(HZ*300)
 
 /*
+ * Periodic timer frequency
+ */
+#define RT_GC_INTERVAL			(HZ*60)
+
+/*
  * Cache invalidations can be delayed by:
  */
 #define RT_FLUSH_DELAY (5*HZ)
@@ -104,7 +109,6 @@
 extern void		ip_rt_init(void);
 extern void		ip_rt_redirect(u32 old_gw, u32 dst, u32 new_gw,
 				       u32 src, u8 tos, struct device *dev);
-extern void		ip_rt_check_expire(void);
 extern void		ip_rt_advice(struct rtable **rp, int advice);
 extern void		rt_cache_flush(int how);
 extern int		ip_route_output(struct rtable **, u32 dst, u32 src, u8 tos, int oif);
@@ -144,26 +148,6 @@
 	return ip_route_output(rp, dst, src, tos, oif);
 }
 
-extern __inline__ void ip_ll_header(struct sk_buff *skb)
-{
-	struct rtable *rt = (struct rtable*)skb->dst;
-	struct device *dev = rt->u.dst.dev;
-	struct hh_cache *hh = rt->u.dst.hh;
-	int hh_len = dev->hard_header_len;
-
-	skb->dev = dev;
-	skb->arp = 1;
-	skb->protocol = htons(ETH_P_IP);
-
-	if (hh) {
-		memcpy(skb_push(skb, hh_len), hh->hh_data, hh_len);
-		skb->arp = hh->hh_uptodate;
-	} else if (dev->hard_header &&
-		   dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, 0)<0)
-		skb->arp = 0;
-		
-	skb->mac.raw = skb->data;
-}
 #endif
 
 
diff -u --recursive --new-file v2.1.78/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.78/linux/include/net/sock.h	Fri Jan  2 14:37:03 1998
+++ linux/include/net/sock.h	Mon Jan 12 16:44:27 1998
@@ -168,14 +168,6 @@
 	struct device		*oif;
 
 	struct ipv6_mc_socklist	*ipv6_mc_list;
-	/* 
-	 * destination cache entry pointer
-	 * contains a pointer to neighbour cache
-	 * and other info related to network level 
-	 * (ex. PMTU)
-	 */
-	
-	struct dst_entry	*dst;
 	__u32			dst_cookie;
 
 	struct ipv6_options	*opt;
@@ -785,6 +777,9 @@
 						     unsigned long fallback,
 						     int noblock,
 						     int *errcode);
+extern void *sock_kmalloc(struct sock *sk, int size, int priority);
+extern void sock_kfree_s(struct sock *sk, void *mem, int size);
+
 
 /*
  * Functions to fill in entries in struct proto_ops when a protocol
diff -u --recursive --new-file v2.1.78/linux/kernel/exit.c linux/kernel/exit.c
--- v2.1.78/linux/kernel/exit.c	Mon Dec  1 12:04:15 1997
+++ linux/kernel/exit.c	Sat Jan 10 15:21:42 1998
@@ -418,8 +418,7 @@
 				if (ru != NULL)
 					getrusage(p, RUSAGE_BOTH, ru);
 				if (stat_addr)
-					__put_user((p->exit_code << 8) | 0x7f,
-						   stat_addr);
+					__put_user((p->exit_code << 8) | 0x7f, stat_addr);
 				p->exit_code = 0;
 				retval = p->pid;
 				goto end_wait4;
diff -u --recursive --new-file v2.1.78/linux/kernel/fork.c linux/kernel/fork.c
--- v2.1.78/linux/kernel/fork.c	Tue Dec  2 16:45:20 1997
+++ linux/kernel/fork.c	Mon Jan 12 14:46:27 1998
@@ -435,6 +435,7 @@
  */
 int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
 {
+        int i;
 	int nr;
 	int error = -ENOMEM;
 	struct task_struct *p;
@@ -482,6 +483,9 @@
 #ifdef __SMP__
 	p->has_cpu = 0;
 	p->processor = NO_PROC_ID;
+	/* ?? should we just memset this ?? */
+	for(i = 0; i < smp_num_cpus; i++)
+		p->per_cpu_utime[i] = p->per_cpu_stime[i] = 0;
 #endif
 	p->lock_depth = 0;
 	p->start_time = jiffies;
diff -u --recursive --new-file v2.1.78/linux/kernel/sched.c linux/kernel/sched.c
--- v2.1.78/linux/kernel/sched.c	Fri Jan  2 14:37:03 1998
+++ linux/kernel/sched.c	Mon Jan 12 14:46:27 1998
@@ -1031,8 +1031,10 @@
 }
 
 void update_one_process(struct task_struct *p,
-	unsigned long ticks, unsigned long user, unsigned long system)
+	unsigned long ticks, unsigned long user, unsigned long system, int cpu)
 {
+	p->per_cpu_utime[cpu] += user;
+	p->per_cpu_stime[cpu] += system;
 	do_process_times(p, user, system);
 	do_it_virt(p, user);
 	do_it_prof(p, ticks);
@@ -1058,7 +1060,7 @@
 			kstat.cpu_user += user;
 		kstat.cpu_system += system;
 	}
-	update_one_process(p, ticks, user, system);
+	update_one_process(p, ticks, user, system, 0);
 #endif
 }
 
diff -u --recursive --new-file v2.1.78/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.1.78/linux/kernel/sysctl.c	Sun Dec 21 22:36:17 1997
+++ linux/kernel/sysctl.c	Sat Jan 10 15:15:21 1998
@@ -435,12 +435,16 @@
 	return tmp;
 }
 
-void unregister_sysctl_table(struct ctl_table_header * table)
+/*
+ * Unlink and free a ctl_table.
+ */
+void unregister_sysctl_table(struct ctl_table_header * header)
 {
-	DLIST_DELETE(table, ctl_entry);
+	DLIST_DELETE(header, ctl_entry);
 #ifdef CONFIG_PROC_FS
-	unregister_proc_table(table->ctl_table, &proc_sys_root);
+	unregister_proc_table(header->ctl_table, &proc_sys_root);
 #endif
+	kfree(header);
 }
 
 /*
@@ -457,18 +461,20 @@
 	mode_t mode;
 	
 	for (; table->ctl_name; table++) {
-		de = 0;
 		/* Can't do anything without a proc name. */
 		if (!table->procname)
 			continue;
 		/* Maybe we can't do anything with it... */
-		if (!table->proc_handler &&
-		    !table->child)
+		if (!table->proc_handler && !table->child) {
+			printk(KERN_WARNING "SYSCTL: Can't register %s\n",
+				table->procname);
 			continue;
+		}
 
 		len = strlen(table->procname);
 		mode = table->mode;
 
+		de = NULL;
 		if (table->proc_handler)
 			mode |= S_IFREG;
 		else {
@@ -495,6 +501,9 @@
 	}
 }
 
+/*
+ * Unregister a /proc sysctl table and any subdirectories.
+ */
 static void unregister_proc_table(ctl_table * table, struct proc_dir_entry *root)
 {
 	struct proc_dir_entry *de;
@@ -512,8 +521,11 @@
 		   entries... */
 		if (!((de->mode & S_IFDIR) && de->subdir)) {
 			proc_unregister(root, de->low_ino);
+			table->de = NULL;
 			kfree(de);
-		}
+		} else
+			printk("unregister_proc_table: %s not empty!\n",
+				table->procname);
 	}
 }
 
diff -u --recursive --new-file v2.1.78/linux/mm/filemap.c linux/mm/filemap.c
--- v2.1.78/linux/mm/filemap.c	Tue Jan  6 09:37:38 1998
+++ linux/mm/filemap.c	Mon Jan 12 14:33:20 1998
@@ -164,6 +164,8 @@
 
 				/* is it a page cache page? */
 				if (page->inode) {
+					if (page->inode == &swapper_inode)
+						panic ("Shrinking a swap cache page");
 					remove_page_from_hash_queue(page);
 					remove_page_from_inode_queue(page);
 					__free_page(page);
@@ -208,6 +210,8 @@
 		return count;
 	if (!p->inode)
 		return count;
+	if (PageSwapCache(p))
+		panic ("Doing a normal page_unuse of a swap cache page");
 	remove_page_from_hash_queue(p);
 	remove_page_from_inode_queue(p);
 	free_page(page);
diff -u --recursive --new-file v2.1.78/linux/mm/memory.c linux/mm/memory.c
--- v2.1.78/linux/mm/memory.c	Mon Dec  1 12:04:15 1997
+++ linux/mm/memory.c	Mon Jan 12 14:33:20 1998
@@ -175,100 +175,16 @@
 	return 0;
 }
 
-static inline void copy_one_pte(pte_t * old_pte, pte_t * new_pte, int cow)
-{
-	pte_t pte = *old_pte;
-	unsigned long page_nr;
-
-	if (pte_none(pte))
-		return;
-	if (!pte_present(pte)) {
-		swap_duplicate(pte_val(pte));
-		set_pte(new_pte, pte);
-		return;
-	}
-	page_nr = MAP_NR(pte_page(pte));
-	if (page_nr >= max_mapnr || PageReserved(mem_map+page_nr)) {
-		set_pte(new_pte, pte);
-		return;
-	}
-	if (cow)
-		pte = pte_wrprotect(pte);
-	if (delete_from_swap_cache(&mem_map[page_nr]))
-		pte = pte_mkdirty(pte);
-	set_pte(new_pte, pte_mkold(pte));
-	set_pte(old_pte, pte);
-	atomic_inc(&mem_map[page_nr].count);
-}
-
-static inline int copy_pte_range(pmd_t *dst_pmd, pmd_t *src_pmd, unsigned long address, unsigned long size, int cow)
-{
-	pte_t * src_pte, * dst_pte;
-	unsigned long end;
-
-	if (pmd_none(*src_pmd))
-		return 0;
-	if (pmd_bad(*src_pmd)) {
-		printk("copy_pte_range: bad pmd (%08lx)\n", pmd_val(*src_pmd));
-		pmd_clear(src_pmd);
-		return 0;
-	}
-	src_pte = pte_offset(src_pmd, address);
-	if (pmd_none(*dst_pmd)) {
-		if (!pte_alloc(dst_pmd, 0))
-			return -ENOMEM;
-	}
-	dst_pte = pte_offset(dst_pmd, address);
-	address &= ~PMD_MASK;
-	end = address + size;
-	if (end >= PMD_SIZE)
-		end = PMD_SIZE;
-	do {
-		/* I would like to switch arguments here, to make it
-		 * consistent with copy_xxx_range and memcpy syntax.
-		 */
-		copy_one_pte(src_pte++, dst_pte++, cow);
-		address += PAGE_SIZE;
-	} while (address < end);
-	return 0;
-}
-
-static inline int copy_pmd_range(pgd_t *dst_pgd, pgd_t *src_pgd, unsigned long address, unsigned long size, int cow)
-{
-	pmd_t * src_pmd, * dst_pmd;
-	unsigned long end;
-	int error = 0;
-
-	if (pgd_none(*src_pgd))
-		return 0;
-	if (pgd_bad(*src_pgd)) {
-		printk("copy_pmd_range: bad pgd (%08lx)\n", pgd_val(*src_pgd));
-		pgd_clear(src_pgd);
-		return 0;
-	}
-	src_pmd = pmd_offset(src_pgd, address);
-	if (pgd_none(*dst_pgd)) {
-		if (!pmd_alloc(dst_pgd, 0))
-			return -ENOMEM;
-	}
-	dst_pmd = pmd_offset(dst_pgd, address);
-	address &= ~PGDIR_MASK;
-	end = address + size;
-	if (end > PGDIR_SIZE)
-		end = PGDIR_SIZE;
-	do {
-		error = copy_pte_range(dst_pmd++, src_pmd++, address, end - address, cow);
-		if (error)
-			break;
-		address = (address + PMD_SIZE) & PMD_MASK; 
-	} while (address < end);
-	return error;
-}
+#define PTE_TABLE_MASK	((PTRS_PER_PTE-1) * sizeof(pte_t))
+#define PMD_TABLE_MASK	((PTRS_PER_PMD-1) * sizeof(pmd_t))
 
 /*
  * copy one vm_area from one task to the other. Assumes the page tables
  * already present in the new task to be cleared in the whole range
  * covered by this vma.
+ *
+ * 08Jan98 Merged into one routine from several inline routines to reduce
+ *         variable count and make things faster. -jj
  */
 int copy_page_range(struct mm_struct *dst, struct mm_struct *src,
 			struct vm_area_struct *vma)
@@ -276,18 +192,103 @@
 	pgd_t * src_pgd, * dst_pgd;
 	unsigned long address = vma->vm_start;
 	unsigned long end = vma->vm_end;
-	int error = 0, cow;
+	unsigned long cow = (vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE;
+	
+	src_pgd = pgd_offset(src, address)-1;
+	dst_pgd = pgd_offset(dst, address)-1;
+	
+	for (;;) {
+		pmd_t * src_pmd, * dst_pmd;
 
-	cow = (vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE;
-	src_pgd = pgd_offset(src, address);
-	dst_pgd = pgd_offset(dst, address);
-	while (address < end) {
-		error = copy_pmd_range(dst_pgd++, src_pgd++, address, end - address, cow);
-		if (error)
-			break;
-		address = (address + PGDIR_SIZE) & PGDIR_MASK;
+		src_pgd++; dst_pgd++;
+		
+		/* copy_pmd_range */
+		
+		if (pgd_none(*src_pgd))
+			goto skip_copy_pmd_range;
+		if (pgd_bad(*src_pgd)) {
+			printk("copy_pmd_range: bad pgd (%08lx)\n", 
+				pgd_val(*src_pgd));
+			pgd_clear(src_pgd);
+skip_copy_pmd_range:	address = (address + PGDIR_SIZE) & PGDIR_MASK;
+			if (address >= end)
+				goto out;
+			continue;
+		}
+		if (pgd_none(*dst_pgd)) {
+			if (!pmd_alloc(dst_pgd, 0))
+				goto nomem;
+		}
+		
+		src_pmd = pmd_offset(src_pgd, address);
+		dst_pmd = pmd_offset(dst_pgd, address);
+
+		do {
+			pte_t * src_pte, * dst_pte;
+		
+			/* copy_pte_range */
+		
+			if (pmd_none(*src_pmd))
+				goto skip_copy_pte_range;
+			if (pmd_bad(*src_pmd)) {
+				printk("copy_pte_range: bad pmd (%08lx)\n", pmd_val(*src_pmd));
+				pmd_clear(src_pmd);
+skip_copy_pte_range:		address = (address + PMD_SIZE) & PMD_MASK;
+				if (address >= end)
+					goto out;
+				goto cont_copy_pmd_range;
+			}
+			if (pmd_none(*dst_pmd)) {
+				if (!pte_alloc(dst_pmd, 0))
+					goto nomem;
+			}
+			
+			src_pte = pte_offset(src_pmd, address);
+			dst_pte = pte_offset(dst_pmd, address);
+			
+			do {
+				pte_t pte = *src_pte;
+				unsigned long page_nr;
+				
+				/* copy_one_pte */
+
+				if (pte_none(pte))
+					goto cont_copy_pte_range;
+				if (!pte_present(pte)) {
+					swap_duplicate(pte_val(pte));
+					set_pte(dst_pte, pte);
+					goto cont_copy_pte_range;
+				}
+				page_nr = MAP_NR(pte_page(pte));
+				if (page_nr >= max_mapnr || 
+				    PageReserved(mem_map+page_nr)) {
+					set_pte(dst_pte, pte);
+					goto cont_copy_pte_range;
+				}
+				if (cow)
+					pte = pte_wrprotect(pte);
+				if (delete_from_swap_cache(&mem_map[page_nr]))
+					pte = pte_mkdirty(pte);
+				set_pte(dst_pte, pte_mkold(pte));
+				set_pte(src_pte, pte);
+				atomic_inc(&mem_map[page_nr].count);
+			
+cont_copy_pte_range:		address += PAGE_SIZE;
+				if (address >= end)
+					goto out;
+				src_pte++;
+				dst_pte++;
+			} while ((unsigned long)src_pte & PTE_TABLE_MASK);
+		
+cont_copy_pmd_range:	src_pmd++;
+			dst_pmd++;
+		} while ((unsigned long)src_pmd & PMD_TABLE_MASK);
 	}
-	return error;
+out:
+	return 0;
+
+nomem:
+	return -ENOMEM;
 }
 
 /*
@@ -299,7 +300,11 @@
 		unsigned long addr = pte_page(page);
 		if (MAP_NR(addr) >= max_mapnr || PageReserved(mem_map+MAP_NR(addr)))
 			return 0;
-		free_page(addr);
+		/* 
+		 * free_page() used to be able to clear swap cache
+		 * entries.  We may now have to do it manually.  
+		 */
+		free_page_and_swap_cache(addr);
 		return 1;
 	}
 	swap_free(pte_val(page));
@@ -542,7 +547,7 @@
 static void put_page(pte_t * page_table, pte_t pte)
 {
 	if (!pte_none(*page_table)) {
-		free_page(pte_page(pte));
+		free_page_and_swap_cache(pte_page(pte));
 		return;
 	}
 /* no need for flush_tlb */
@@ -609,7 +614,8 @@
 {
 	pte_t pte;
 	unsigned long old_page, new_page;
-
+	struct page * page_map;
+	
 	new_page = __get_free_page(GFP_KERNEL);
 	pte = *page_table;
 	if (!pte_present(pte))
@@ -620,6 +626,11 @@
 	if (MAP_NR(old_page) >= max_mapnr)
 		goto bad_wp_page;
 	tsk->min_flt++;
+
+	page_map = mem_map + MAP_NR(old_page);
+	if (PageSwapCache(page_map))
+		delete_from_swap_cache(page_map);
+	
 	/*
 	 * Do we need to copy?
 	 */
diff -u --recursive --new-file v2.1.78/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.1.78/linux/mm/page_alloc.c	Fri Jan  2 14:37:03 1998
+++ linux/mm/page_alloc.c	Mon Jan 12 14:33:20 1998
@@ -133,9 +133,12 @@
 void __free_page(struct page *page)
 {
 	if (!PageReserved(page) && atomic_dec_and_test(&page->count)) {
-		delete_from_swap_cache(page);
+		if (PageSwapCache(page))
+			panic ("Freeing swap cache page");
 		free_pages_ok(page->map_nr, 0);
 	}
+	if (PageSwapCache(page) && atomic_read(&page->count) == 1)
+		panic ("Releasing swap cache page");
 }
 
 void free_pages(unsigned long addr, unsigned long order)
@@ -147,10 +150,14 @@
 		if (PageReserved(map))
 			return;
 		if (atomic_dec_and_test(&map->count)) {
-			delete_from_swap_cache(map);
+			if (PageSwapCache(map))
+				panic ("Freeing swap cache pages");
 			free_pages_ok(map_nr, order);
 			return;
 		}
+		if (PageSwapCache(map) && atomic_read(&map->count) == 1)
+			panic ("Releasing swap cache pages at %p",
+			       __builtin_return_address(0));
 	}
 }
 
diff -u --recursive --new-file v2.1.78/linux/mm/page_io.c linux/mm/page_io.c
--- v2.1.78/linux/mm/page_io.c	Thu Jul 17 10:06:09 1997
+++ linux/mm/page_io.c	Mon Jan 12 14:33:20 1998
@@ -45,8 +45,10 @@
 {
 	unsigned long type, offset;
 	struct swap_info_struct * p;
-	struct page *page;
-	
+	struct page *page = mem_map + MAP_NR(buf);
+
+	if (page->inode && page->inode != &swapper_inode)
+		panic ("Tried to swap a non-swapper page");
 	type = SWP_TYPE(entry);
 	if (type >= nr_swapfiles) {
 		printk("Internal error: bad swap-device\n");
@@ -66,26 +68,43 @@
 		printk("Trying to swap to unused swap-device\n");
 		return;
 	}
+	/*
+	 * For now, this is not legal!
+	 */
+	if (PageSwapCache(page))
+		panic ("Trying to swap a swap-cache page!");
+
 	/* Make sure we are the only process doing I/O with this swap page. */
 	while (test_and_set_bit(offset,p->swap_lockmap)) {
 		run_task_queue(&tq_disk);
 		sleep_on(&lock_queue);
 	}
+
 	if (rw == READ)
 		kstat.pswpin++;
 	else
 		kstat.pswpout++;
-	page = mem_map + MAP_NR(buf);
 	atomic_inc(&page->count);
 	wait_on_page(page);
+	/* 
+	 * Make sure that we have a swap cache association for this
+	 * page.  We need this to find which swap page to unlock once
+	 * the swap IO has completed to the physical page.  If the page
+	 * is not already in the cache, just overload the offset entry
+	 * as if it were: we are not allowed to manipulate the inode
+	 * hashing for locked pages.
+	 */
+	if (PageSwapCache(page)) {
+		if (page->offset != entry)
+			panic ("swap entry mismatch");
+	} else 
+		page->offset = entry;
+
 	if (p->swap_device) {
 		if (!wait) {
 			set_bit(PG_free_after, &page->flags);
 			set_bit(PG_decr_after, &page->flags);
 			set_bit(PG_swap_unlock_after, &page->flags);
-			/* swap-cache  shouldn't be set, but play safe */
-			PageClearSwapCache(page);
-			page->pg_swap_entry = entry;
 			atomic_inc(&nr_async_pages);
 		}
 		ll_rw_page(rw,p->swap_device,offset,buf);
@@ -132,16 +151,19 @@
 			for (i=0, j=0; j< PAGE_SIZE ; i++, j +=swapf->i_sb->s_blocksize)
 				if (!(zones[i] = bmap(swapf,block++))) {
 					printk("rw_swap_page: bad swap file\n");
+					return;
 				}
 		}
 		ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf);
 	} else
 		printk("rw_swap_page: no swap file or device\n");
+
 	atomic_dec(&page->count);
 	if (offset && !test_and_clear_bit(offset,p->swap_lockmap))
 		printk("rw_swap_page: lock already cleared\n");
 	wake_up(&lock_queue);
 }
+
 
 /* This is run when asynchronous page I/O has completed. */
 void swap_after_unlock_page (unsigned long entry)
diff -u --recursive --new-file v2.1.78/linux/mm/swap_state.c linux/mm/swap_state.c
--- v2.1.78/linux/mm/swap_state.c	Sat Nov  1 11:04:27 1997
+++ linux/mm/swap_state.c	Mon Jan 12 14:33:20 1998
@@ -3,6 +3,8 @@
  *
  *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
  *  Swap reorganised 29.12.95, Stephen Tweedie
+ *
+ *  Rewritten to use page cache, (C) 1998 Stephen Tweedie
  */
 
 #include <linux/mm.h>
@@ -17,6 +19,7 @@
 #include <linux/fs.h>
 #include <linux/swapctl.h>
 #include <linux/init.h>
+#include <linux/pagemap.h>
 
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
@@ -29,6 +32,18 @@
 unsigned long swap_cache_find_total = 0;
 unsigned long swap_cache_find_success = 0;
 
+/* 
+ * Keep a reserved false inode which we will use to mark pages in the
+ * page cache are acting as swap cache instead of file cache. 
+ *
+ * We only need a unique pointer to satisfy the page cache, but we'll
+ * reserve an entire zeroed inode structure for the purpose just to
+ * ensure that any mistaken dereferences of this structure cause a
+ * kernel oops.
+ */
+struct inode swapper_inode;
+
+
 void show_swap_cache_info(void)
 {
 	printk("Swap cache: add %ld/%ld, delete %ld/%ld, find %ld/%ld\n",
@@ -45,10 +60,18 @@
 #ifdef SWAP_CACHE_INFO
 	swap_cache_add_total++;
 #endif
+	if (PageLocked(page))
+		panic("Adding page cache to locked page");
 	if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
-		page->pg_swap_entry = entry;
 		if (PageTestandSetSwapCache(page))
-			printk("swap_cache: replacing non-empty entry\n");
+			panic("swap_cache: replacing non-empty entry");
+		if (page->inode)
+			panic("swap_cache: replacing page-cached entry");
+		atomic_inc(&page->count);
+		page->inode = &swapper_inode;
+		page->offset = entry;
+		add_page_to_hash_queue(page, &swapper_inode, entry);
+		add_page_to_inode_queue(&swapper_inode, page);
 #ifdef SWAP_CACHE_INFO
 		swap_cache_add_success++;
 #endif
@@ -101,3 +124,74 @@
 	goto out;
 }
 
+
+void remove_from_swap_cache(struct page *page)
+{
+	if (!page->inode)
+		panic ("Removing swap cache page with zero inode hash");
+	if (page->inode != &swapper_inode)
+		panic ("Removing swap cache page with wrong inode hash");
+	if (PageLocked(page))
+		panic ("Removing swap cache from locked page");
+	/*
+	 * This will be a legal case once we have a more mature swap cache.
+	 */
+	if (atomic_read(&page->count) == 1)
+		panic ("Removing page cache on unshared page");
+	
+	remove_page_from_hash_queue (page);
+	remove_page_from_inode_queue (page);
+	PageClearSwapCache (page);
+	__free_page (page);
+}
+
+
+long find_in_swap_cache(struct page *page)
+{
+#ifdef SWAP_CACHE_INFO
+	swap_cache_find_total++;
+#endif
+	if (PageSwapCache (page))  {
+		long entry = page->offset;
+#ifdef SWAP_CACHE_INFO
+		swap_cache_find_success++;
+#endif	
+		remove_from_swap_cache (page);
+		return entry;
+	}
+	return 0;
+}
+
+int delete_from_swap_cache(struct page *page)
+{
+#ifdef SWAP_CACHE_INFO
+	swap_cache_del_total++;
+#endif	
+	if (PageSwapCache (page))  {
+		long entry = page->offset;
+#ifdef SWAP_CACHE_INFO
+		swap_cache_del_success++;
+#endif
+		remove_from_swap_cache (page);
+		swap_free (entry);
+		return 1;
+	}
+	return 0;
+}
+
+/* 
+ * Perform a free_page(), also freeing any swap cache associated with
+ * this page if it is the last user of the page. 
+ */
+
+void free_page_and_swap_cache(unsigned long addr)
+{
+	struct page *page = mem_map + MAP_NR(addr);
+	/* 
+	 * If we are the only user, then free up the swap cache. 
+	 */
+	if (PageSwapCache(page) && !is_page_shared(page))
+		delete_from_swap_cache(page);
+	
+	free_page(addr);
+}
diff -u --recursive --new-file v2.1.78/linux/mm/swapfile.c linux/mm/swapfile.c
--- v2.1.78/linux/mm/swapfile.c	Sat Nov  1 11:04:27 1997
+++ linux/mm/swapfile.c	Mon Jan 12 14:33:20 1998
@@ -129,6 +129,7 @@
 
 	if (!entry)
 		goto out;
+
 	type = SWP_TYPE(entry);
 	if (type & SHM_SWP_TYPE)
 		goto out;
diff -u --recursive --new-file v2.1.78/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.1.78/linux/mm/vmscan.c	Tue Dec 23 16:31:00 1997
+++ linux/mm/vmscan.c	Mon Jan 12 14:33:20 1998
@@ -82,7 +82,11 @@
 		return 0;
 	/* Deal with page aging.  Pages age from being unused; they
 	 * rejuvenate on being accessed.  Only swap old pages (age==0
-	 * is oldest). */
+	 * is oldest).
+	 *
+	 * This test will no longer work once swap cached pages can be
+	 * shared!  
+	 */
 	if ((pte_dirty(pte) && delete_from_swap_cache(page_map)) 
 	    || pte_young(pte))  {
 		set_pte(page_table, pte_mkold(pte));
@@ -93,6 +97,8 @@
 	if (page_map->age)
 		return 0;
 	if (pte_dirty(pte)) {
+		if (PageSwapCache(page_map))
+			panic ("Can't still be swap cached!!!");
 		if (vma->vm_ops && vma->vm_ops->swapout) {
 			pid_t pid = tsk->pid;
 			vma->vm_mm->rss--;
@@ -108,9 +114,19 @@
 			tsk->nswap++;
 			rw_swap_page(WRITE, entry, (char *) page, wait);
 		}
+		/* 
+		 * For now, this is safe, because the test above makes
+		 * sure that this page is currently not swap-cached.  
+		 */
+		if (PageSwapCache(page_map))
+			panic ("Page became cached after IO");
 		free_page(page);
 		return 1;	/* we slept: the process may not exist any more */
 	}
+	/* 
+	 * Eventually, find_in_swap_cache will be able to return true
+	 * even for pages shared with other processes.  
+	 */
         if ((entry = find_in_swap_cache(page_map)))  {
 		if (atomic_read(&page_map->count) != 1) {
 			set_pte(page_table, pte_mkdirty(pte));
@@ -129,6 +145,8 @@
 	pte_clear(page_table);
 	flush_tlb_page(vma, address);
 	entry = page_unuse(page);
+	if (PageSwapCache(page_map))
+		panic ("How can this page _still_ be cached?");
 	free_page(page);
 	return entry;
 }
diff -u --recursive --new-file v2.1.78/linux/net/802/fddi.c linux/net/802/fddi.c
--- v2.1.78/linux/net/802/fddi.c	Sat Nov 29 11:25:12 1997
+++ linux/net/802/fddi.c	Mon Jan 12 15:28:19 1998
@@ -97,18 +97,6 @@
 int fddi_rebuild_header(struct sk_buff	*skb)
 {
 	struct fddihdr *fddi = (struct fddihdr *)skb->data;
-#if 0
- 	struct neighbour *neigh = NULL;
-
-	if (skb->dst)
-		neigh = skb->dst->neighbour;
-
-	if (neigh)
-		return neigh->ops->resolve(fddi->daddr, skb);
-#endif
-	/*
-	 * Only ARP/IP is currently supported
-	 */
 
 	if (fddi->hdr.llc_snap.ethertype != __constant_htons(ETH_P_IP))
 	{
diff -u --recursive --new-file v2.1.78/linux/net/802/llc_sendpdu.c linux/net/802/llc_sendpdu.c
--- v2.1.78/linux/net/802/llc_sendpdu.c	Sat Nov 29 11:25:12 1997
+++ linux/net/802/llc_sendpdu.c	Mon Jan 12 15:28:19 1998
@@ -161,7 +161,6 @@
 		}
 		lp->dev->hard_header(skb, lp->dev, ETH_P_802_3,
 			 lp->remote_mac, NULL, fl);
-		skb->arp = 1;
 		skb->dev=lp->dev;
 		dev_queue_xmit(skb);
 	}
@@ -213,7 +212,6 @@
 		lp->vs = 0;
 	lp->dev->hard_header(skb, lp->dev, ETH_P_802_3,
 		lp->remote_mac, NULL, skb->len);
-	skb->arp = 1;
 	ADD_TO_RTQ(skb);		/* add skb to the retransmit queue */
 	tmp=skb_clone(skb, GFP_ATOMIC);
 	if(tmp!=NULL)
@@ -284,7 +282,6 @@
 		tmp=skb_clone(skb, GFP_ATOMIC);
 		if(tmp!=NULL)
 		{
-			tmp->arp = 1;
 			tmp->dev = lp->dev;
 			dev_queue_xmit(skb);
 		}
diff -u --recursive --new-file v2.1.78/linux/net/802/transit/pdutr.h linux/net/802/transit/pdutr.h
--- v2.1.78/linux/net/802/transit/pdutr.h	Thu Dec 12 06:54:22 1996
+++ linux/net/802/transit/pdutr.h	Mon Jan 12 14:46:27 1998
@@ -1,5 +1,5 @@
 
-/* this file was generated on Thu Dec  5 13:58:11 GMT 1996  */
+/* this file was generated on Thu Jan  8 00:21:19 GMT 1998  */
 
 /* index name #defines: */
 
diff -u --recursive --new-file v2.1.78/linux/net/802/transit/timertr.h linux/net/802/transit/timertr.h
--- v2.1.78/linux/net/802/transit/timertr.h	Thu Mar 27 14:40:12 1997
+++ linux/net/802/transit/timertr.h	Mon Jan 12 14:46:27 1998
@@ -1,5 +1,5 @@
 
-/* this file was generated on Mon Mar 10 22:45:36 GMT 1997  */
+/* this file was generated on Thu Jan  8 00:21:21 GMT 1998  */
 
 
 /* size of transition table is 898 bytes */
diff -u --recursive --new-file v2.1.78/linux/net/Config.in linux/net/Config.in
--- v2.1.78/linux/net/Config.in	Tue Dec 23 16:31:00 1997
+++ linux/net/Config.in	Mon Jan 12 15:28:48 1998
@@ -11,7 +11,6 @@
 fi
 bool 'Network firewalls' CONFIG_FIREWALL
 bool 'Network aliasing'  CONFIG_NET_ALIAS
-bool 'Socket Filtering'  CONFIG_FILTER
 tristate 'Unix domain sockets' CONFIG_UNIX
 bool 'TCP/IP networking' CONFIG_INET
 if [ "$CONFIG_INET" = "y" ]; then
@@ -43,19 +42,15 @@
 #   bool 'Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
 #  fi
   tristate 'WAN router' CONFIG_WAN_ROUTER
+  bool 'Fast switching (read help!)' CONFIG_NET_FASTROUTE
+  bool 'Forwarding between high speed interfaces' CONFIG_NET_HW_FLOWCONTROL
   bool 'CPU is too slow to handle full bandwidth' CONFIG_CPU_IS_SLOW
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   bool 'QoS and/or fair queueing' CONFIG_NET_SCHED
   if [ "$CONFIG_NET_SCHED" = "y" ]; then
-    tristate 'CBQ packet scheduler' CONFIG_NET_SCH_CBQ
-    tristate 'CSZ packet scheduler' CONFIG_NET_SCH_CSZ
-    tristate 'HFQ packet scheduler' CONFIG_NET_SCH_HFQ
-    tristate 'RED queueing discipline' CONFIG_NET_SCH_RED
-    tristate 'SFQ queueing discipline' CONFIG_NET_SCH_SFQ
-    tristate 'auxiliary TBF queue' CONFIG_NET_SCH_TBF
-    tristate 'auxiliary FIFO queue' CONFIG_NET_SCH_PFIFO
-    tristate 'auxiliary PRIO queue' CONFIG_NET_SCH_PRIO
+    source net/sched/Config.in
   fi
+  bool 'Network code profiler' CONFIG_NET_PROFILE
   fi
 fi
 endmenu
diff -u --recursive --new-file v2.1.78/linux/net/appletalk/aarp.c linux/net/appletalk/aarp.c
--- v2.1.78/linux/net/appletalk/aarp.c	Sat Nov 29 11:25:12 1997
+++ linux/net/appletalk/aarp.c	Mon Jan 12 15:28:19 1998
@@ -122,7 +122,6 @@
 
 	skb_reserve(skb,dev->hard_header_len+aarp_dl->header_length);
 	eah		=	(struct elapaarp *)skb_put(skb,sizeof(struct elapaarp));
-	skb->arp	=	1;
 	skb->dev	=	dev;
 	
 	/*
@@ -181,7 +180,6 @@
 
 	skb_reserve(skb,dev->hard_header_len+aarp_dl->header_length);
 	eah		=	(struct elapaarp *)skb_put(skb,sizeof(struct elapaarp));	 
-	skb->arp	=	1;
 	skb->dev	=	dev;
 	
 	/*
@@ -243,7 +241,6 @@
 	skb_reserve(skb,dev->hard_header_len+aarp_dl->header_length);
 	eah		=	(struct elapaarp *)skb_put(skb,sizeof(struct elapaarp));
 	
-	skb->arp	=	1;
 	skb->dev	=	dev;
 	
 	/*
diff -u --recursive --new-file v2.1.78/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.1.78/linux/net/appletalk/ddp.c	Fri Dec 19 15:53:05 1997
+++ linux/net/appletalk/ddp.c	Mon Jan 12 15:28:19 1998
@@ -1397,7 +1397,6 @@
 		 */
 		if(skb)
 		{
-			skb->arp = 1;	/* Resolved */
 			if(aarp_send_ddp(rt->dev, skb, &ta, NULL) == -1)
 				kfree_skb(skb, FREE_READ);
 		}
@@ -1621,7 +1620,6 @@
 		return (err);
 
 	skb->sk  = sk;
-	skb->arp = 1;
 	skb_reserve(skb, ddp_dl->header_length);
 	skb_reserve(skb, dev->hard_header_len);
 
diff -u --recursive --new-file v2.1.78/linux/net/ax25/ax25_ds_subr.c linux/net/ax25/ax25_ds_subr.c
--- v2.1.78/linux/net/ax25/ax25_ds_subr.c	Thu Dec  4 14:53:57 1997
+++ linux/net/ax25/ax25_ds_subr.c	Mon Jan 12 15:28:19 1998
@@ -155,7 +155,6 @@
 	*p++ = cmd;
 	*p++ = param;
 
-	skb->arp      = 1;
 	skb->dev      = ax25_dev->dev;
 	skb->protocol = htons(ETH_P_AX25);
 
diff -u --recursive --new-file v2.1.78/linux/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- v2.1.78/linux/net/ax25/ax25_out.c	Thu Dec  4 14:53:57 1997
+++ linux/net/ax25/ax25_out.c	Mon Jan 12 15:28:19 1998
@@ -382,7 +382,6 @@
 
 	skb->protocol = htons(ETH_P_AX25);
 	skb->dev      = ax25_fwd_dev(skb->dev);
-	skb->arp      = 1;
 
 	ptr  = skb_push(skb, 1);
 	*ptr = 0x00;			/* KISS */
diff -u --recursive --new-file v2.1.78/linux/net/core/Makefile linux/net/core/Makefile
--- v2.1.78/linux/net/core/Makefile	Sun Dec 21 22:36:17 1997
+++ linux/net/core/Makefile	Mon Jan 12 15:28:19 1998
@@ -10,16 +10,12 @@
 O_TARGET := core.o
 
 O_OBJS	:= sock.o skbuff.o iovec.o datagram.o dst.o scm.o \
-	neighbour.o rtnetlink.o
+	neighbour.o rtnetlink.o utils.o
 
 ifeq ($(CONFIG_SYSCTL),y)
 O_OBJS += sysctl_net_core.o
 endif
 
-ifdef CONFIG_FILTER
-O_OBJS += filter.o
-endif
-
 ifdef CONFIG_NET
 
 O_OBJS	+= dev.o dev_mcast.o
@@ -28,6 +24,10 @@
 OX_OBJS += firewall.o
 endif
 
+endif
+
+ifdef CONFIG_NET_PROFILE
+OX_OBJS += profile.o
 endif
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.78/linux/net/core/dev.c linux/net/core/dev.c
--- v2.1.78/linux/net/core/dev.c	Fri Jan  2 14:37:03 1998
+++ linux/net/core/dev.c	Mon Jan 12 15:28:19 1998
@@ -48,6 +48,7 @@
  *					1 device.
  *	    Thomas Bogendoerfer :	Return ENODEV for dev_open, if there
  *					is no device open function.
+ *		Andi Kleen	:	Fix error reporting for SIOCGIFCONF
  *
  */
 
@@ -75,7 +76,9 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <net/br.h>
+#include <net/dst.h>
 #include <net/pkt_sched.h>
+#include <net/profile.h>
 #include <linux/init.h>
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
@@ -87,6 +90,10 @@
 extern int plip_init(void);
 #endif
 
+NET_PROFILE_DEFINE(dev_queue_xmit)
+NET_PROFILE_DEFINE(net_bh)
+NET_PROFILE_DEFINE(net_bh_skb)
+
 
 const char *if_port_text[] = {
   "unknown",
@@ -141,6 +148,13 @@
 
 static struct sk_buff_head backlog;
 
+#ifdef CONFIG_NET_FASTROUTE
+int netdev_fastroute;
+int netdev_fastroute_obstacles;
+struct net_fastroute_stats dev_fastroute_stat;
+#endif
+
+
 /******************************************************************************************
 
 		Protocol management and registration routines
@@ -162,6 +176,13 @@
 void dev_add_pack(struct packet_type *pt)
 {
 	int hash;
+#ifdef CONFIG_NET_FASTROUTE
+	/* Hack to detect packet socket */
+	if (pt->data) {
+		netdev_fastroute_obstacles++;
+		dev_clear_fastroute(pt->dev);
+	}
+#endif
 	if(pt->type==htons(ETH_P_ALL))
 	{
 		netdev_nit++;
@@ -196,6 +217,10 @@
 		if(pt==(*pt1))
 		{
 			*pt1=pt->next;
+#ifdef CONFIG_NET_FASTROUTE
+			if (pt->data)
+				netdev_fastroute_obstacles--;
+#endif
 			return;
 		}
 	}
@@ -305,7 +330,7 @@
 static int
 default_rebuild_header(struct sk_buff *skb)
 {
-	printk(KERN_DEBUG "%s: !skb->arp & !rebuild_header -- BUG!\n", skb->dev->name);
+	printk(KERN_DEBUG "%s: default_rebuild_header called -- BUG!\n", skb->dev ? skb->dev->name : "NULL!!!");
 	kfree_skb(skb, FREE_WRITE);
 	return 1;
 }
@@ -370,6 +395,24 @@
 	return(ret);
 }
 
+#ifdef CONFIG_NET_FASTROUTE
+void dev_clear_fastroute(struct device *dev)
+{
+	int i;
+
+	if (dev) {
+		for (i=0; i<=NETDEV_FASTROUTE_HMASK; i++)
+			dst_release(xchg(dev->fastpath+i, NULL));
+	} else {
+		for (dev = dev_base; dev; dev = dev->next) {
+			if (dev->accept_fastpath) {
+				for (i=0; i<=NETDEV_FASTROUTE_HMASK; i++)
+					dst_release(xchg(dev->fastpath+i, NULL));
+			}
+		}
+	}
+}
+#endif
 
 /*
  *	Completely shutdown an interface.
@@ -400,6 +443,9 @@
 	 */
 	 
 	dev->flags&=~(IFF_UP|IFF_RUNNING);
+#ifdef CONFIG_NET_FASTROUTE
+	dev_clear_fastroute(dev);
+#endif
 
 	/*
 	 *	Tell people we are going down
@@ -488,7 +534,9 @@
 	if (newskb==NULL)
 		return;
 
+	newskb->mac.raw = newskb->data;
 	skb_pull(newskb, newskb->nh.raw - newskb->data);
+	newskb->pkt_type = PACKET_LOOPBACK;
 	newskb->ip_summed = CHECKSUM_UNNECESSARY;
 	if (newskb->dst==NULL)
 		printk(KERN_DEBUG "BUG: packet without dst looped back 1\n");
@@ -500,24 +548,23 @@
 	struct device *dev = skb->dev;
 	struct Qdisc  *q;
 
-	/*
-	 *	If the address has not been resolved. Call the device header rebuilder.
-	 *	This can cover all protocols and technically not just ARP either.
-	 *
-	 *	This call must be moved to protocol layer.
-	 *	Now it works only for IPv6 and for IPv4 in
-	 *	some unusual curcumstances (eql device). --ANK
-	 */
-	 
-	if (!skb->arp && dev->rebuild_header(skb))
-		return 0;
+#ifdef CONFIG_NET_PROFILE
+	start_bh_atomic();
+	NET_PROFILE_ENTER(dev_queue_xmit);
+#endif
 
+	start_bh_atomic();
 	q = dev->qdisc;
 	if (q->enqueue) {
-		start_bh_atomic();
 		q->enqueue(skb, q);
 		qdisc_wakeup(dev);
 		end_bh_atomic();
+
+#ifdef CONFIG_NET_PROFILE
+	        NET_PROFILE_LEAVE(dev_queue_xmit);
+		end_bh_atomic();
+#endif
+
 		return 0;
 	}
 
@@ -530,18 +577,30 @@
 	   made by us here.
 	 */
 	if (dev->flags&IFF_UP) {
-		start_bh_atomic();
 		if (netdev_nit) 
 			dev_queue_xmit_nit(skb,dev);
 		if (dev->hard_start_xmit(skb, dev) == 0) {
 			end_bh_atomic();
+
+#ifdef CONFIG_NET_PROFILE
+			NET_PROFILE_LEAVE(dev_queue_xmit);
+			end_bh_atomic();
+#endif
+
 			return 0;
 		}
 		if (net_ratelimit())
 			printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name);
-		end_bh_atomic();
 	}
+	end_bh_atomic();
+
 	kfree_skb(skb, FREE_WRITE);
+
+#ifdef CONFIG_NET_PROFILE
+	NET_PROFILE_LEAVE(dev_queue_xmit);
+	end_bh_atomic();
+#endif
+
 	return 0;
 }
 
@@ -551,7 +610,74 @@
   =======================================================================*/
 
 int netdev_dropping = 0;
+int netdev_max_backlog = 300;
 atomic_t netdev_rx_dropped;
+#ifdef CONFIG_CPU_IS_SLOW
+int net_cpu_congestion;
+#endif
+
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+int netdev_throttle_events;
+static unsigned long netdev_fc_mask = 1;
+unsigned long netdev_fc_xoff = 0;
+
+static struct
+{
+	void (*stimul)(struct device *);
+	struct device *dev;
+} netdev_fc_slots[32];
+
+int netdev_register_fc(struct device *dev, void (*stimul)(struct device *dev))
+{
+	int bit = 0;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (netdev_fc_mask != ~0UL) {
+		bit = ffz(netdev_fc_mask);
+		netdev_fc_slots[bit].stimul = stimul;
+		netdev_fc_slots[bit].dev = dev;
+		set_bit(bit, &netdev_fc_mask);
+		clear_bit(bit, &netdev_fc_xoff);
+	}
+	sti();
+	return bit;
+}
+
+void netdev_unregister_fc(int bit)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (bit > 0) {
+		netdev_fc_slots[bit].stimul = NULL;
+		netdev_fc_slots[bit].dev = NULL;
+		clear_bit(bit, &netdev_fc_mask);
+		clear_bit(bit, &netdev_fc_xoff);
+	}
+	sti();
+}
+
+static void netdev_wakeup(void)
+{
+	unsigned long xoff;
+
+	cli();
+	xoff = netdev_fc_xoff;
+	netdev_fc_xoff = 0;
+	netdev_dropping = 0;
+	netdev_throttle_events++;
+	while (xoff) {
+		int i = ffz(~xoff);
+		xoff &= ~(1<<i);
+		netdev_fc_slots[i].stimul(netdev_fc_slots[i].dev);
+	}
+	sti();
+}
+#endif
+
 
 /*
  *	Receive a packet from a device driver and queue it for the upper
@@ -560,38 +686,41 @@
 
 void netif_rx(struct sk_buff *skb)
 {
+#ifndef CONFIG_CPU_IS_SLOW
 	if(skb->stamp.tv_sec==0)
 		get_fast_time(&skb->stamp);
+#else
+	skb->stamp = xtime;
+#endif
 
-	/*
-	 *	Check that we aren't overdoing things.
+	/* The code is rearranged so that the path is the most
+	   short when CPU is congested, but is still operating.
 	 */
 
-	if (!backlog.qlen)
-  		netdev_dropping = 0;
-	else if (backlog.qlen > 300)
-		netdev_dropping = 1;
-
-	if (netdev_dropping)
-	{
-		atomic_inc(&netdev_rx_dropped);
-		kfree_skb(skb, FREE_READ);
+	if (backlog.qlen <= netdev_max_backlog) {
+		if (backlog.qlen) {
+			if (netdev_dropping == 0) {
+				skb_queue_tail(&backlog,skb);
+				mark_bh(NET_BH);
+				return;
+			}
+			atomic_inc(&netdev_rx_dropped);
+			kfree_skb(skb, FREE_READ);
+			return;
+		}
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+		if (netdev_dropping)
+			netdev_wakeup();
+#else
+		netdev_dropping = 0;
+#endif
+		skb_queue_tail(&backlog,skb);
+		mark_bh(NET_BH);
 		return;
 	}
-
-	/*
-	 *	Add it to the "backlog" queue. 
-	 */
-
-	skb_queue_tail(&backlog,skb);
-  
-	/*
-	 *	If any packet arrived, mark it for processing after the
-	 *	hardware interrupt returns.
-	 */
-
-	mark_bh(NET_BH);
-	return;
+	netdev_dropping = 1;
+	atomic_inc(&netdev_rx_dropped);
+	kfree_skb(skb, FREE_READ);
 }
 
 #ifdef CONFIG_BRIDGE
@@ -622,9 +751,6 @@
 }
 #endif
 
-#ifdef CONFIG_CPU_IS_SLOW
-int net_cpu_congestion;
-#endif
 
 /*
  *	When we are called the queue is ready to grab, the interrupts are
@@ -649,6 +775,7 @@
 	net_cpu_congestion = ave_busy>>8;
 #endif
 
+	NET_PROFILE_ENTER(net_bh);
 	/*
 	 *	Can we send anything now? We want to clear the
 	 *	decks for any more sends that get done as we
@@ -677,11 +804,9 @@
 	{
 		struct sk_buff * skb = backlog.next;
 
-		if (jiffies - start_time > 1) {
-			/* Give chance to other bottom halves to run */
-			mark_bh(NET_BH);
-			return;
-		}
+		/* Give chance to other bottom halves to run */
+		if (jiffies - start_time > 1)
+			goto net_bh_break;
 
 		/*
 		 *	We have a packet. Therefore the queue has shrunk
@@ -699,7 +824,17 @@
 		}
 #endif
 
-		
+
+#if 0
+		NET_PROFILE_SKB_PASSED(skb, net_bh_skb);
+#endif
+#ifdef CONFIG_NET_FASTROUTE
+		if (skb->pkt_type == PACKET_FASTROUTE) {
+			dev_queue_xmit(skb);
+			continue;
+		}
+#endif
+
 		/*
 		 * 	Fetch the packet protocol ID. 
 		 */
@@ -726,6 +861,12 @@
 		/* XXX until we figure out every place to modify.. */
 		skb->h.raw = skb->nh.raw = skb->data;
 
+		if (skb->mac.raw < skb->head || skb->mac.raw > skb->data) {
+			printk(KERN_CRIT "%s: wrong mac.raw ptr, proto=%04x\n", skb->dev->name, skb->protocol);
+			kfree_skb(skb, FREE_READ);
+			continue;
+		}
+
 		/*
 		 *	We got a packet ID.  Now loop over the "known protocols"
 		 * 	list. There are two lists. The ptype_all list of taps (normally empty)
@@ -800,12 +941,25 @@
 		qdisc_run_queues();
 
 #ifdef  CONFIG_CPU_IS_SLOW
-{
-	unsigned long start_idle = jiffies;
-	ave_busy += ((start_idle - start_busy)<<3) - (ave_busy>>4);
-	start_busy = 0;
-}
+        if (1) {
+		unsigned long start_idle = jiffies;
+		ave_busy += ((start_idle - start_busy)<<3) - (ave_busy>>4);
+		start_busy = 0;
+	}
+#endif
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+	if (netdev_dropping)
+		netdev_wakeup();
+#else
+	netdev_dropping = 0;
 #endif
+	NET_PROFILE_LEAVE(net_bh);
+	return;
+
+net_bh_break:
+	mark_bh(NET_BH);
+	NET_PROFILE_LEAVE(net_bh);
+	return;
 }
 
 /* Protocol dependent address dumping routines */
@@ -950,11 +1104,10 @@
 	if (copy_to_user(arg, &ifc, sizeof(struct ifconf)))
 		return -EFAULT; 
 
-	/*
-	 *	Report how much was filled in
+	/* 
+	 * 	Both BSD and Solaris return 0 here, so we do too.
 	 */
-	 
-	return ifc.ifc_len;
+	return 0;
 }
 
 /*
@@ -1006,7 +1159,7 @@
 
 	size = sprintf(buffer, 
 		"Inter-|   Receive                           |  Transmit\n"
-		" face |bytes    packets errs drop fifo frame|bytes    packets errs drop fifo colls carrier\n");
+		" face |bytes    packets errs drop fifo frame|bytes    packets errs drop fifo colls carrier multicast\n");
 	
 	pos+=size;
 	len+=size;
@@ -1033,6 +1186,41 @@
 		len=length;		/* Ending slop */
 	return len;
 }
+
+static int dev_proc_stats(char *buffer, char **start, off_t offset,
+			  int length, int *eof, void *data)
+{
+	int len;
+
+	len = sprintf(buffer, "%08x %08x %08x %08x %08x\n",
+		      atomic_read(&netdev_rx_dropped),
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+		      netdev_throttle_events,
+#else
+		      0,
+#endif
+#ifdef CONFIG_NET_FASTROUTE
+		      dev_fastroute_stat.hits,
+		      dev_fastroute_stat.succeed,
+		      dev_fastroute_stat.deferred
+#else
+		      0, 0, 0
+#endif
+		      );
+
+	len -= offset;
+
+	if (len > length)
+		len = length;
+	if(len < 0)
+		len = 0;
+
+	*start = buffer + offset;
+	*eof = 1;
+
+	return len;
+}
+
 #endif	/* CONFIG_PROC_FS */
 
 
@@ -1125,9 +1313,16 @@
 	if ((dev->promiscuity += inc) == 0)
 		dev->flags &= ~IFF_PROMISC;
 	if (dev->flags^old_flags) {
+#ifdef CONFIG_NET_FASTROUTE
+		if (dev->flags&IFF_PROMISC) {
+			netdev_fastroute_obstacles++;
+			dev_clear_fastroute(dev);
+		} else
+			netdev_fastroute_obstacles--;
+#endif
 		dev_mc_upload(dev);
 		printk(KERN_INFO "device %s %s promiscuous mode\n",
-		       dev->name, (dev->flags&IFF_PROMISC) ? "entered" : "left");
+		       dev->name, (dev->flags&IFF_PROMISC) ? "entered" : "leaved");
 	}
 }
 
@@ -1305,16 +1500,6 @@
 			ifr->ifr_ifindex = dev->ifindex;
 			return 0;
 
-		case SIOCGIFTXQLEN:
-			ifr->ifr_qlen = dev->tx_queue_len;
-			return 0;
-
-		case SIOCSIFTXQLEN:
-			if(ifr->ifr_qlen<2 || ifr->ifr_qlen>1024)
-				return -EINVAL;
-			dev->tx_queue_len = ifr->ifr_qlen;
-			return 0;
-
 		/*
 		 *	Unknown or private ioctl
 		 */
@@ -1360,9 +1545,9 @@
 	   
 	if (cmd == SIOCGIFCONF) {
 		rtnl_shlock();
-		dev_ifconf((char *) arg);
+		ret = dev_ifconf((char *) arg);
 		rtnl_shunlock();
-		return 0;
+		return ret;
 	}
 	if (cmd == SIOCGIFCOUNT) {
 		return dev_ifcount((unsigned int*)arg);
@@ -1406,10 +1591,8 @@
 		case SIOCGIFSLAVE:
 		case SIOCGIFMAP:
 		case SIOCGIFINDEX:
-		case SIOCGIFTXQLEN:
 			ret = dev_ifsioc(&ifr, cmd);
-			if (!ret)
-			{
+			if (!ret) {
 #ifdef CONFIG_NET_ALIAS
 				if (colon)
 					*colon = ':';
@@ -1432,7 +1615,6 @@
 		case SIOCSIFMAP:
 		case SIOCSIFHWADDR:
 		case SIOCSIFSLAVE:
-		case SIOCSIFTXQLEN:
 		case SIOCADDMULTI:
 		case SIOCDELMULTI:
 		case SIOCSIFHWBROADCAST:
@@ -1553,6 +1735,10 @@
 		if (dev->flags & IFF_UP)
 			dev_close(dev);
 
+#ifdef CONFIG_NET_FASTROUTE
+		dev_clear_fastroute(dev);
+#endif
+
 		/* Shutdown queueing discipline. */
 		dev_shutdown(dev);
 
@@ -1598,11 +1784,10 @@
 extern void dlci_setup(void);
 extern int dmascc_init(void);
 extern int sm_init(void);
-extern int baycom_ser_fdx_init(void);
-extern int baycom_ser_hdx_init(void);
-extern int baycom_par_init(void);
+extern int baycom_init(void);
 extern int lapbeth_init(void);
 extern void arcnet_init(void);
+extern void ip_auto_config(void);
 
 #ifdef CONFIG_PROC_FS
 static struct proc_dir_entry proc_net_dev = {
@@ -1668,14 +1853,8 @@
 #if defined(CONFIG_SDLA)
 	sdla_setup();
 #endif
-#if defined(CONFIG_BAYCOM_PAR)
-	baycom_par_init();
-#endif
-#if defined(CONFIG_BAYCOM_SER_FDX)
-	baycom_ser_fdx_init();
-#endif
-#if defined(CONFIG_BAYCOM_SER_HDX)
-	baycom_ser_hdx_init();
+#if defined(CONFIG_BAYCOM)
+	baycom_init();
 #endif
 #if defined(CONFIG_SOUNDMODEM)
 	sm_init();
@@ -1699,7 +1878,14 @@
 	slhc_install();
 #endif	
 
-
+#ifdef CONFIG_NET_PROFILE
+	net_profile_init();
+	NET_PROFILE_REGISTER(dev_queue_xmit);
+	NET_PROFILE_REGISTER(net_bh);
+#if 0
+	NET_PROFILE_REGISTER(net_bh_skb);
+#endif
+#endif
 	/*
 	 *	Add the devices.
 	 *	If the call to dev->init fails, the dev is removed
@@ -1730,6 +1916,10 @@
 
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_net_dev);
+	if (1) {
+		struct proc_dir_entry *ent = create_proc_entry("net/dev_stat", 0, 0);
+		ent->read_proc = dev_proc_stats;
+	}
 #endif
 
 #ifdef CONFIG_NET_RADIO
@@ -1741,6 +1931,8 @@
 	init_bh(NET_BH, net_bh);
 
 	dev_boot_phase = 0;
+
+	dev_mcast_init();
 
 #ifdef CONFIG_IP_PNP
 	ip_auto_config();
diff -u --recursive --new-file v2.1.78/linux/net/core/dev_mcast.c linux/net/core/dev_mcast.c
--- v2.1.78/linux/net/core/dev_mcast.c	Mon Dec  1 12:04:16 1997
+++ linux/net/core/dev_mcast.c	Mon Jan 12 15:28:19 1998
@@ -19,7 +19,8 @@
  *	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 <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -37,6 +38,8 @@
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
 #include <net/ip.h>
 #include <net/route.h>
 #include <linux/skbuff.h>
@@ -52,6 +55,9 @@
  *	that a casual user application can add/delete multicasts used by 
  *	protocols without doing damage to the protocols when it deletes the
  *	entries. It also helps IP as it tracks overlapping maps.
+ *
+ *	BUGGGG! IPv6 calls dev_mac_add/delete from BH, it means
+ *	that all the functions in this file are racy. [NOT FIXED] --ANK
  */
  
 
@@ -82,64 +88,81 @@
  *	Delete a device level multicast
  */
  
-void dev_mc_delete(struct device *dev, void *addr, int alen, int all)
+int dev_mc_delete(struct device *dev, void *addr, int alen, int glbl)
 {
-	struct dev_mc_list **dmi;
+	struct dev_mc_list *dmi, **dmip;
 
-	for(dmi=&dev->mc_list;*dmi!=NULL;dmi=&(*dmi)->next)
-	{
+	for (dmip=&dev->mc_list; (dmi=*dmip)!=NULL; dmip=&dmi->next) {
 		/*
 		 *	Find the entry we want to delete. The device could
 		 *	have variable length entries so check these too.
 		 */
-		if(memcmp((*dmi)->dmi_addr,addr,(*dmi)->dmi_addrlen)==0 && alen==(*dmi)->dmi_addrlen)
-		{
-			struct dev_mc_list *tmp= *dmi;
-			if(--(*dmi)->dmi_users && !all)
-				return;
+		if (memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && alen==dmi->dmi_addrlen) {
+			if (glbl) {
+				int old_glbl = dmi->dmi_gusers;
+				dmi->dmi_gusers = 0;
+				if (old_glbl == 0)
+					return -ENOENT;
+			}
+			if(--dmi->dmi_users)
+				return 0;
+
 			/*
 			 *	Last user. So delete the entry.
 			 */
-			*dmi=(*dmi)->next;
+			*dmip = dmi->next;
 			dev->mc_count--;
-			kfree_s(tmp,sizeof(*tmp));
+			kfree_s(dmi,sizeof(*dmi));
 			/*
 			 *	We have altered the list, so the card
 			 *	loaded filter is now wrong. Fix it
 			 */
 			dev_mc_upload(dev);
-			return;
+			return 0;
 		}
 	}
+	return -ENOENT;
 }
 
 /*
  *	Add a device level multicast
  */
  
-void dev_mc_add(struct device *dev, void *addr, int alen, int newonly)
+int dev_mc_add(struct device *dev, void *addr, int alen, int glbl)
 {
 	struct dev_mc_list *dmi;
 
-	for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
-	{
-		if(memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen)
-		{
-			if(!newonly)
-				dmi->dmi_users++;
-			return;
+	for(dmi=dev->mc_list; dmi!=NULL; dmi=dmi->next) {
+		if (memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen) {
+			if (glbl) {
+				int old_glbl = dmi->dmi_gusers;
+				dmi->dmi_gusers = 1;
+				if (old_glbl)
+					return 0;
+			}
+			dmi->dmi_users++;
+			return 0;
 		}
 	}
-	dmi=(struct dev_mc_list *)kmalloc(sizeof(*dmi),GFP_KERNEL);
-	if(dmi==NULL)
-		return;	/* GFP_KERNEL so can't happen anyway */
+
+	/* GFP_ATOMIC!! It is used by IPv6 from interrupt,
+	   when new address arrives.
+
+	   Particularly, it means that this part of code is weirdly
+	   racy, and needs numerous *_bh_atomic --ANK
+	 */
+	dmi=(struct dev_mc_list *)kmalloc(sizeof(*dmi), GFP_ATOMIC);
+	if (dmi==NULL)
+		return -ENOBUFS;
 	memcpy(dmi->dmi_addr, addr, alen);
 	dmi->dmi_addrlen=alen;
 	dmi->next=dev->mc_list;
 	dmi->dmi_users=1;
+	dmi->dmi_gusers=glbl ? 1 : 0;
 	dev->mc_list=dmi;
 	dev->mc_count++;
 	dev_mc_upload(dev);
+	return 0;
 }
 
 /*
@@ -148,13 +171,64 @@
 
 void dev_mc_discard(struct device *dev)
 {
-	while(dev->mc_list!=NULL)
-	{
+	while (dev->mc_list!=NULL) {
 		struct dev_mc_list *tmp=dev->mc_list;
-		dev->mc_list=dev->mc_list->next;
-		if (tmp->dmi_users)
+		dev->mc_list=tmp->next;
+		if (tmp->dmi_users > tmp->dmi_gusers)
 			printk("dev_mc_discard: multicast leakage! dmi_users=%d\n", tmp->dmi_users);
 		kfree_s(tmp,sizeof(*tmp));
 	}
 	dev->mc_count=0;
 }
+
+#ifdef CONFIG_PROC_FS
+static int dev_mc_read_proc(char *buffer, char **start, off_t offset,
+			    int length, int *eof, void *data)
+{
+	off_t pos=0, begin=0;
+	struct dev_mc_list *m;
+	int len=0;
+	struct device *dev;
+	
+	for (dev = dev_base; dev; dev = dev->next) {
+		for (m = dev->mc_list; m; m = m->next) {
+			int i;
+
+			len += sprintf(buffer+len,"%-4d %-15s %-5d %-5d ", dev->ifindex, dev->name,
+				       m->dmi_users, m->dmi_gusers);
+
+			for (i=0; i<m->dmi_addrlen; i++)
+				len += sprintf(buffer+len, "%02x", m->dmi_addr[i]);
+
+			len+=sprintf(buffer+len, "\n");
+
+			pos=begin+len;
+			if (pos < offset) {
+				len=0;
+				begin=pos;
+			}
+			if (pos > offset+length)
+				goto done;
+		}
+	}
+	*eof = 1;
+
+done:
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;
+	return len;
+}
+#endif
+
+__initfunc(void dev_mcast_init(void))
+{
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *ent;
+
+	ent = create_proc_entry("net/dev_mcast", 0, 0);
+	ent->read_proc = dev_mc_read_proc;
+#endif
+}
+
diff -u --recursive --new-file v2.1.78/linux/net/core/dst.c linux/net/core/dst.c
--- v2.1.78/linux/net/core/dst.c	Wed Apr 23 19:01:29 1997
+++ linux/net/core/dst.c	Mon Jan 12 15:28:19 1998
@@ -58,9 +58,8 @@
 	dst_gc_timer_inc += DST_GC_INC;
 	dst_gc_timer.expires = jiffies + dst_gc_timer_expires;
 #if RT_CACHE_DEBUG >= 2
-	printk("dst_total: %d/%d/%d %ld\n",
-	       atomic_read(&dst_total), delayed,
-	       atomic_read(&hh_count), dst_gc_timer_expires);
+	printk("dst_total: %d/%d %ld\n",
+	       atomic_read(&dst_total), delayed,  dst_gc_timer_expires);
 #endif
 	add_timer(&dst_gc_timer);
 }
@@ -85,7 +84,7 @@
 		return NULL;
 	memset(dst, 0, size);
 	dst->ops = ops;
-	atomic_set(&dst->refcnt, 1);
+	atomic_set(&dst->refcnt, 0);
 	dst->lastuse = jiffies;
 	dst->input = dst_discard;
 	dst->output = dst_blackhole;
@@ -107,4 +106,24 @@
 		add_timer(&dst_gc_timer);
 	}
 	end_bh_atomic();
+}
+
+void dst_destroy(struct dst_entry * dst)
+{
+	struct neighbour *neigh = dst->neighbour;
+	struct hh_cache *hh = dst->hh;
+
+	dst->hh = NULL;
+	if (hh && atomic_dec_and_test(&hh->hh_refcnt))
+		kfree(hh);
+
+	if (neigh) {
+		dst->neighbour = NULL;
+		neigh_release(neigh);
+	}
+
+	if (dst->ops->destroy)
+		dst->ops->destroy(dst);
+	atomic_dec(&dst_total);
+	kfree(dst);
 }
diff -u --recursive --new-file v2.1.78/linux/net/core/iovec.c linux/net/core/iovec.c
--- v2.1.78/linux/net/core/iovec.c	Fri Dec 19 15:53:06 1997
+++ linux/net/core/iovec.c	Mon Jan 12 15:28:25 1998
@@ -26,13 +26,7 @@
 #include <linux/in6.h>
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
-#include <asm/checksum.h>
-
-extern inline int min(int x, int y)
-{
-	return x>y?y:x;
-}
-
+#include <net/checksum.h>
 
 /*
  *	Verify iovec
@@ -211,8 +205,8 @@
 
 			/* Normal case (single iov component) is fastly detected */
 			if (len <= copy) {
-				*csump = csum_partial_copy_from_user(base, kdata, 
-								     len, *csump, &err);
+				*csump = csum_and_copy_from_user(base, kdata, 
+								 len, *csump, &err);
 				return err;
 			}
 
@@ -222,8 +216,8 @@
 				err |= copy_from_user(kdata+copy, base+copy, partial_cnt);
 			}
 
-			*csump = csum_partial_copy_from_user(base, kdata, 
-							     copy, *csump, &err);
+			*csump = csum_and_copy_from_user(base, kdata, 
+							 copy, *csump, &err);
 
 			len   -= copy + partial_cnt;
 			kdata += copy + partial_cnt;
@@ -241,14 +235,6 @@
 		u8 *base = iov->iov_base;
 		unsigned int copy = min(len, iov->iov_len);
 
-		/* FIXME: more sanity checking is needed here, because
-                 * the iovs are copied from the user.
-		 */
-		if (base == NULL) {
-			printk(KERN_DEBUG "%s: iov too short\n",current->comm);
-			return -EINVAL;
-		}
-		
 		/* There is a remnant from previous iov. */
 		if (partial_cnt)
 		{
@@ -289,7 +275,7 @@
 		if (copy == 0)
 			break;
 
-		csum = csum_partial_copy_from_user(base, kdata, copy, csum, &err);
+		csum = csum_and_copy_from_user(base, kdata, copy, csum, &err);
 		len   -= copy + partial_cnt;
 		kdata += copy + partial_cnt;
 		iov++;
diff -u --recursive --new-file v2.1.78/linux/net/core/neighbour.c linux/net/core/neighbour.c
--- v2.1.78/linux/net/core/neighbour.c	Thu Mar 27 14:40:15 1997
+++ linux/net/core/neighbour.c	Mon Jan 12 15:28:25 1998
@@ -1,8 +1,9 @@
 /*
- *	Generic address resultion entity
+ *	Generic address resolution entity
  *
  *	Authors:
- *	Pedro Roque	<roque@di.fc.ul.pt>
+ *	Pedro Roque		<roque@di.fc.ul.pt>
+ *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -10,144 +11,293 @@
  *      2 of the License, or (at your option) any later version.
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/socket.h>
 #include <linux/sched.h>
 #include <linux/netdevice.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
 #include <net/neighbour.h>
+#include <net/dst.h>
+#include <linux/rtnetlink.h>
 
+#define NEIGH_DEBUG 1
 
-static void neigh_purge_send_q(struct neighbour *neigh);
+#define NEIGH_PRINTK(x...) printk(x)
+#define NEIGH_NOPRINTK(x...) do { ; } while(0)
+#define NEIGH_PRINTK0 NEIGH_PRINTK
+#define NEIGH_PRINTK1 NEIGH_NOPRINTK
+#define NEIGH_PRINTK2 NEIGH_NOPRINTK
+
+#if NEIGH_DEBUG >= 1
+#undef NEIGH_PRINTK1
+#define NEIGH_PRINTK1 NEIGH_PRINTK
+#endif
+#if NEIGH_DEBUG >= 2
+#undef NEIGH_PRINTK2
+#define NEIGH_PRINTK2 NEIGH_PRINTK
+#endif
+
+static void neigh_timer_handler(unsigned long arg);
+#ifdef CONFIG_ARPD
+static void neigh_app_notify(struct neighbour *n);
+#endif
 
-void neigh_table_init(struct neigh_table *tbl, struct neigh_ops *ops, int size)
-{
-	int bmemlen;
-
-	memset(tbl, 0, sizeof(struct neigh_table));
-	
-	tbl->tbl_size = size;
-	tbl->neigh_ops = ops;
-	
-	/*
-	 *	This should only be called on initialization
-	 *	And interrupts should be on
-	 */
+static int neigh_glbl_allocs;
+static struct neigh_table *neigh_tables;
 
-	bmemlen = size * sizeof(struct neighbour *);
-	tbl->hash_buckets = kmalloc(bmemlen, GFP_KERNEL);
+static int neigh_blackhole(struct sk_buff *skb)
+{
+	kfree_skb(skb, FREE_WRITE);
+	return -ENETDOWN;
+}
 
-	if (tbl->hash_buckets == NULL)
-	{
-		panic("unable to initialize neigh_table");
-	}
+/*
+ * It is random distribution in the interval (1/2)*base...(3/2)*base.
+ * It corresponds to default IPv6 settings and is not overridable,
+ * because it is really reasonbale choice.
+ */
 
-	memset(tbl->hash_buckets, 0, bmemlen);
+unsigned long neigh_rand_reach_time(unsigned long base)
+{
+	return (net_random() % base) + (base>>1);
 }
 
-struct neighbour *neigh_alloc(int size, struct neigh_ops *ops)
+
+static int neigh_forced_gc(struct neigh_table *tbl)
 {
-	struct neighbour *neigh;
-	
-	neigh = kmalloc(size, GFP_ATOMIC);
-	if (neigh == NULL)
-	{
-		return NULL;
-	}
+	int shrunk = 0;
+	int i;
+
+	if (atomic_read(&tbl->lock))
+		return 0;
 
-	memset(neigh, 0, size);
+	for (i=0; i<=NEIGH_HASHMASK; i++) {
+		struct neighbour *n, **np;
+
+		np = &tbl->hash_buckets[i];
+		while ((n = *np) != NULL) {
+			if (atomic_read(&n->refcnt) == 0 &&
+			    !(n->nud_state&NUD_PERMANENT)) {
+				*np = n->next;
+				n->tbl = NULL;
+				tbl->entries--;
+				shrunk = 1;
+				neigh_destroy(n);
+				continue;
+			}
+			np = &n->next;
+		}
+	}
 	
-	skb_queue_head_init(&neigh->arp_queue);
-	neigh->ops = ops;
-	return neigh;
+	tbl->last_flush = jiffies;
+	return shrunk;
 }
 
-void neigh_queue_ins(struct neigh_table *tbl, struct neighbour *neigh)
+int neigh_ifdown(struct neigh_table *tbl, struct device *dev)
 {
-	struct neighbour *entry, **head;
-	entry = tbl->request_queue;
+	int i;
 
-	head = &tbl->request_queue;
-	
-	for (; entry; entry = entry->next)
-	{
-		head = &entry->next;
+	if (atomic_read(&tbl->lock)) {
+		NEIGH_PRINTK1("neigh_ifdown: impossible event 1763\n");
+		return -EBUSY;
 	}
 
-	*head = neigh;
-	neigh->next = neigh->prev = NULL;
+	start_bh_atomic();
+	for (i=0; i<=NEIGH_HASHMASK; i++) {
+		struct neighbour *n, **np;
+
+		np = &tbl->hash_buckets[i];
+		while ((n = *np) != NULL) {
+			if (dev && n->dev != dev) {
+				np = &n->next;
+				continue;
+			}
+			*np = n->next;
+			n->tbl = NULL;
+			tbl->entries--;
+			if (atomic_read(&n->refcnt)) {
+				/* The most unpleasant situation.
+				   We must destroy neighbour entry,
+				   but someone still uses it.
+
+				   The destroy will be delayed until
+				   the last user releases us, but
+				   we must kill timers etc. and move
+				   it to safe state.
+				 */
+				if (n->nud_state & NUD_IN_TIMER)
+					del_timer(&n->timer);
+				n->parms = &tbl->parms;
+				skb_queue_purge(&n->arp_queue);
+				n->output = neigh_blackhole;
+				if (n->nud_state&NUD_VALID)
+					n->nud_state = NUD_NOARP;
+				else
+					n->nud_state = NUD_NONE;
+				NEIGH_PRINTK2("neigh %p is stray.\n", n);
+			} else
+				neigh_destroy(n);
+		}
+	}
+
+	del_timer(&tbl->proxy_timer);
+	skb_queue_purge(&tbl->proxy_queue);
+	end_bh_atomic();
+	return 0;
 }
 
-static struct neighbour *neigh_dequeue(struct neigh_table *tbl)
+static struct neighbour *neigh_alloc(struct neigh_table *tbl, int creat)
 {
-	struct neighbour *neigh;
+	struct neighbour *n;
 
-	if ((neigh = tbl->request_queue))
-	{
-		tbl->request_queue = neigh->next;
+	if (tbl->entries > tbl->gc_thresh1) {
+		if (creat < 0)
+			return NULL;
+		if (tbl->entries > tbl->gc_thresh2 ||
+		    jiffies - tbl->last_flush > 5*HZ) {
+			if (neigh_forced_gc(tbl) == 0 &&
+			    tbl->entries > tbl->gc_thresh3)
+				return NULL;
+		}
 	}
-	return neigh;
+
+	n = kmalloc(tbl->entry_size, GFP_ATOMIC);
+	if (n == NULL)
+		return NULL;
+
+	memset(n, 0, tbl->entry_size);
+	
+	skb_queue_head_init(&n->arp_queue);
+	n->updated = n->used = jiffies;
+	n->nud_state = NUD_NONE;
+	n->output = neigh_blackhole;
+	n->parms = &tbl->parms;
+	init_timer(&n->timer);
+	n->timer.function = neigh_timer_handler;
+	n->timer.data = (unsigned long)n;
+	tbl->stats.allocs++;
+	neigh_glbl_allocs++;
+	return n;
 }
 
-void neigh_table_ins(struct neigh_table *tbl, struct neighbour *neigh)
+
+struct neighbour * __neigh_lookup(struct neigh_table *tbl, const void *pkey,
+				    struct device *dev, int creat)
 {
-        unsigned int hash_val;
-	struct neighbour **head;
-	
-	hash_val = tbl->neigh_ops->hash(neigh->primary_key) % tbl->tbl_size;
-	
-	neigh->tbl = tbl;
-	
-	head = &tbl->hash_buckets[hash_val];
-	
-	if (!(*head))
-	{
-		neigh->next = neigh;
-		neigh->prev = neigh;
+	struct neighbour *n;
+	u32 hash_val;
+	int key_len = tbl->key_len;
+
+	hash_val = *(u32*)(pkey + key_len - 4);
+	hash_val ^= (hash_val>>16);
+	hash_val ^= hash_val>>8;
+	hash_val ^= hash_val>>3;
+	hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK;
+
+	for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
+		if (dev == n->dev &&
+		    memcmp(n->primary_key, pkey, key_len) == 0) {
+			atomic_inc(&n->refcnt);
+			return n;
+		}
 	}
-	else
-	{
-		struct neighbour *prev;
-		struct neighbour *next;
-	
-		next = *head;
-		prev = next->prev;
-		
-
-		neigh->next = next;
-		neigh->prev = prev;
-		next->prev = neigh;
-		prev->next = neigh;
+	if (!creat)
+		return NULL;
+
+	n = neigh_alloc(tbl, creat);
+	if (n == NULL)
+		return NULL;
+
+	memcpy(n->primary_key, pkey, key_len);
+	n->dev = dev;
+
+	/* Protocol specific setup. */
+	if (tbl->constructor &&	tbl->constructor(n) < 0) {
+		neigh_destroy(n);
+		return NULL;
 	}
-	
-	*head = neigh;
+
+	/* Device specific setup. */
+	if (dev->neigh_setup && dev->neigh_setup(n) < 0) {
+		neigh_destroy(n);
+		return NULL;
+	}
+
+	n->confirmed = jiffies - (n->parms->base_reachable_time<<1);
+	atomic_set(&n->refcnt, 1);
+	tbl->entries++;
+	n->next = tbl->hash_buckets[hash_val];
+	tbl->hash_buckets[hash_val] = n;
+	n->tbl = tbl;
+	NEIGH_PRINTK2("neigh %p is created.\n", n);
+	return n;
 }
 
-struct neighbour * neigh_lookup(struct neigh_table *tbl, void *pkey,
-				int key_len, struct device *dev)
+struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey,
+				    struct device *dev, int creat)
 {
-	struct neighbour *neigh, *head;
-	unsigned int hash_val;
-	
-	hash_val = tbl->neigh_ops->hash(pkey) % tbl->tbl_size;
-	head = tbl->hash_buckets[hash_val];
+	struct pneigh_entry *n;
+	u32 hash_val;
+	int key_len = tbl->key_len;
+
+	hash_val = *(u32*)(pkey + key_len - 4);
+	hash_val ^= (hash_val>>16);
+	hash_val ^= hash_val>>8;
+	hash_val ^= hash_val>>4;
+	hash_val &= PNEIGH_HASHMASK;
+
+	for (n = tbl->phash_buckets[hash_val]; n; n = n->next) {
+		if (memcmp(n->key, pkey, key_len) == 0 &&
+		    (n->dev == dev || !n->dev))
+			return n;
+	}
+	if (!creat)
+		return NULL;
 
-	neigh = head;
+	n = kmalloc(sizeof(*n) + key_len, GFP_KERNEL);
+	if (n == NULL)
+		return NULL;
 
-	if (neigh)
-	{
-		do {
-			if (memcmp(neigh->primary_key, pkey, key_len) == 0)
-			{
-				if (!dev || dev == neigh->dev)
-					return neigh;
-			}
-			neigh = neigh->next;
+	memcpy(n->key, pkey, key_len);
+	n->dev = dev;
 
-		} while (neigh != head);
+	if (tbl->pconstructor && tbl->pconstructor(n)) {
+		kfree(n);
+		return NULL;
 	}
 
-	return NULL;
+	n->next = tbl->phash_buckets[hash_val];
+	tbl->phash_buckets[hash_val] = n;
+	return n;
+}
+
+
+int pneigh_delete(struct neigh_table *tbl, const void *pkey, struct device *dev)
+{
+	struct pneigh_entry *n, **np;
+	u32 hash_val;
+	int key_len = tbl->key_len;
+
+	hash_val = *(u32*)(pkey + key_len - 4);
+	hash_val ^= (hash_val>>16);
+	hash_val ^= hash_val>>8;
+	hash_val ^= hash_val>>4;
+	hash_val &= PNEIGH_HASHMASK;
+
+	for (np = &tbl->phash_buckets[hash_val]; (n=*np) != NULL; np = &n->next) {
+		if (memcmp(n->key, pkey, key_len) == 0 && n->dev == dev) {
+			*np = n->next;
+			if (tbl->pdestructor)
+				tbl->pdestructor(n);
+			kfree(n);
+			return 0;
+		}
+	}
+	return -ENOENT;
 }
 
 /*
@@ -156,132 +306,987 @@
  */
 void neigh_destroy(struct neighbour *neigh)
 {	
-	if (neigh->tbl)
-	{
-		printk(KERN_DEBUG "neigh_destroy: neighbour still in table. "
-		       "called from %p\n", __builtin_return_address(0));
+	struct hh_cache *hh;
+
+	if (neigh->tbl || atomic_read(&neigh->refcnt)) {
+		NEIGH_PRINTK1("neigh_destroy: neighbour is use tbl=%p, ref=%d: "
+		       "called from %p\n", neigh->tbl, atomic_read(&neigh->refcnt), __builtin_return_address(0));
+		return;
 	}
 
-	if (neigh->ops->destructor)
-	{
-		(neigh->ops->destructor)(neigh);
+	if (neigh->nud_state&NUD_IN_TIMER)
+		del_timer(&neigh->timer);
+
+	while ((hh = neigh->hh) != NULL) {
+		neigh->hh = hh->hh_next;
+		hh->hh_next = NULL;
+		hh->hh_output = neigh_blackhole;
+		if (atomic_dec_and_test(&hh->hh_refcnt))
+			kfree(hh);
 	}
 
-	neigh_purge_send_q(neigh);
+	if (neigh->ops && neigh->ops->destructor)
+		(neigh->ops->destructor)(neigh);
+
+	skb_queue_purge(&neigh->arp_queue);
 
+	NEIGH_PRINTK2("neigh %p is destroyed.\n", neigh);
+
+	neigh_glbl_allocs--;
 	kfree(neigh);
 }
 
-void neigh_unlink(struct neighbour *neigh)
+/* Neighbour state is suspicious;
+   disable fast path.
+ */
+static void neigh_suspect(struct neighbour *neigh)
 {
-	struct neigh_table *tbl;
-	struct neighbour **head;
-	unsigned int hash_val;
-	struct neighbour *next, *prev;
-	
-	tbl = neigh->tbl;
-	neigh->tbl = NULL;
+	struct hh_cache *hh;
 
-	hash_val = neigh->ops->hash(neigh->primary_key) % tbl->tbl_size;
+	NEIGH_PRINTK2("neigh %p is suspecteded.\n", neigh);
 
-	head = &tbl->hash_buckets[hash_val];
-	tbl->tbl_entries--;
+	neigh->output = neigh->ops->output;
 
-	next = neigh->next;
-	if (neigh == (*head))
-	{
-		if (next == neigh)
-		{
-			*head = NULL;
-			goto out;
-		}
-		*head = next;
-	}
-	
-	prev = neigh->prev;
-	next->prev = prev;
-	prev->next = next;
-  out:	
-	neigh->next = neigh->prev = NULL;
+	for (hh = neigh->hh; hh; hh = hh->hh_next)
+		hh->hh_output = neigh->ops->output;
+}
+
+/* Neighbour state is OK;
+   enable fast path.
+ */
+static void neigh_connect(struct neighbour *neigh)
+{
+	struct hh_cache *hh;
+
+	NEIGH_PRINTK2("neigh %p is connected.\n", neigh);
+
+	neigh->output = neigh->ops->connected_output;
+
+	for (hh = neigh->hh; hh; hh = hh->hh_next)
+		hh->hh_output = neigh->ops->hh_output;
 }
 
 /*
- *	Must only be called with an exclusive lock and bh disabled
- *
+   Transitions NUD_STALE <-> NUD_REACHABLE do not occur
+   when fast path is built: we have no timers assotiated with
+   these states, we do not have time to check state when sending.
+   neigh_periodic_timer check periodically neigh->confirmed
+   time and moves NUD_REACHABLE -> NUD_STALE.
+
+   If a routine wants to know TRUE entry state, it calls
+   neigh_sync before checking state.
  */
 
-void ntbl_walk_table(struct neigh_table *tbl, ntbl_examine_t func,
-		     unsigned long filter, int max, void *args)
+static void neigh_sync(struct neighbour *n)
+{
+	unsigned long now = jiffies;
+	u8 state = n->nud_state;
+
+	if (state&(NUD_NOARP|NUD_PERMANENT))
+		return;
+	if (state&NUD_REACHABLE) {
+		if (now - n->confirmed > n->parms->reachable_time) {
+			n->nud_state = NUD_STALE;
+			neigh_suspect(n);
+		}
+	} else if (state&NUD_VALID) {
+		if (now - n->confirmed < n->parms->reachable_time) {
+			if (state&NUD_IN_TIMER)
+				del_timer(&n->timer);
+			n->nud_state = NUD_REACHABLE;
+			neigh_connect(n);
+		}
+	}
+}
+
+static void neigh_periodic_timer(unsigned long arg)
 {
+	struct neigh_table *tbl = (struct neigh_table*)arg;
+	unsigned long now = jiffies;
 	int i;
 
-	if (max == 0)
-		max = tbl->tbl_size;
+	if (atomic_read(&tbl->lock)) {
+		tbl->gc_timer.expires = now + 1*HZ;
+		add_timer(&tbl->gc_timer);
+		return;
+	}
+
+	/*
+	 *	periodicly recompute ReachableTime from random function
+	 */
+	
+	if (now - tbl->last_rand > 300*HZ) {
+		struct neigh_parms *p;
+		tbl->last_rand = now;
+		for (p=&tbl->parms; p; p = p->next)
+			p->reachable_time = neigh_rand_reach_time(p->base_reachable_time);
+	}
 
-	for (i=0; i < max; i++)
-	{
-		struct neighbour **head;
-		struct neighbour *entry;
+	for (i=0; i <= NEIGH_HASHMASK; i++) {
+		struct neighbour *n, **np;
 
-		head = &tbl->hash_buckets[i];
-		entry = *head;
+		np = &tbl->hash_buckets[i];
+		while ((n = *np) != NULL) {
+			unsigned state = n->nud_state;
+
+			if (state&(NUD_PERMANENT|NUD_IN_TIMER))
+				goto next_elt;
+
+			if (n->used - n->confirmed < 0)
+				n->used = n->confirmed;
+
+			if (atomic_read(&n->refcnt) == 0 &&
+			    (state == NUD_FAILED || now - n->used > n->parms->gc_staletime)) {
+				*np = n->next;
+				n->tbl = NULL;
+				n->next = NULL;
+				tbl->entries--;
+				neigh_destroy(n);
+				continue;
+			}
 
-		if (!entry)
-			continue;
+			if (n->nud_state&NUD_REACHABLE &&
+			    now - n->confirmed > n->parms->reachable_time) {
+				n->nud_state = NUD_STALE;
+				neigh_suspect(n);
+			}
+
+next_elt:
+			np = &n->next;
+		}
+	}
+
+	tbl->gc_timer.expires = now + tbl->gc_interval;
+	add_timer(&tbl->gc_timer);
+}
+
+static __inline__ int neigh_max_probes(struct neighbour *n)
+{
+	struct neigh_parms *p = n->parms;
+	return p->ucast_probes + p->app_probes + p->mcast_probes;
+}
 
-		do {
-			if (entry->flags & (~filter))
-			{
-				int ret;
-				ret = (*func)(entry, args);
-
-				if (ret)
-				{
-					struct neighbour *curp;
-
-					curp = entry;
-					entry = curp->next;
-
-					neigh_unlink(curp);
-					neigh_destroy(curp);
-
-					if ((*head) == NULL)
-						break;
-					continue;
+
+/* Called when a timer expires for a neighbour entry. */
+
+static void neigh_timer_handler(unsigned long arg) 
+{
+	unsigned long now = jiffies;
+	struct neighbour *neigh = (struct neighbour*)arg;
+	unsigned state = neigh->nud_state;
+
+	if (!(state&NUD_IN_TIMER)) {
+		NEIGH_PRINTK1("neigh: timer & !nud_in_timer\n");
+		return;
+	}
+
+	if ((state&NUD_VALID) &&
+	    now - neigh->confirmed < neigh->parms->reachable_time) {
+		neigh->nud_state = NUD_REACHABLE;
+		NEIGH_PRINTK2("neigh %p is still alive.\n", neigh);
+		neigh_connect(neigh);
+		return;
+	}
+	if (state == NUD_DELAY) {
+		NEIGH_PRINTK2("neigh %p is probed.\n", neigh);
+		neigh->nud_state = NUD_PROBE;
+		neigh->probes = 0;
+	}
+
+	if (neigh->probes >= neigh_max_probes(neigh)) {
+		struct sk_buff *skb;
+
+		neigh->nud_state = NUD_FAILED;
+		neigh->tbl->stats.res_failed++;
+		NEIGH_PRINTK2("neigh %p is failed.\n", neigh);
+
+		/* It is very thin place. report_unreachable is very complicated
+		   routine. Particularly, it can hit the same neighbour entry!
+		   
+		   So that, we try to be accurate and avoid dead loop. --ANK
+		 */
+		while(neigh->nud_state==NUD_FAILED && (skb=__skb_dequeue(&neigh->arp_queue)) != NULL)
+			neigh->ops->error_report(neigh, skb);
+		skb_queue_purge(&neigh->arp_queue);
+		return;
+	}
+
+	neigh->probes++;
+	neigh->timer.expires = now + neigh->parms->retrans_time;
+	add_timer(&neigh->timer);
+
+	neigh->ops->solicit(neigh, skb_peek(&neigh->arp_queue));
+}
+
+int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
+{
+	start_bh_atomic();
+	if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE))) {
+		if (!(neigh->nud_state&(NUD_STALE|NUD_INCOMPLETE))) {
+			if (neigh->tbl == NULL) {
+				NEIGH_PRINTK2("neigh %p used after death.\n", neigh);
+				if (skb)
+					kfree_skb(skb, FREE_WRITE);
+				end_bh_atomic();
+				return 1;
+			}
+			if (neigh->parms->mcast_probes + neigh->parms->app_probes) {
+				neigh->probes = neigh->parms->ucast_probes;
+				neigh->nud_state = NUD_INCOMPLETE;
+				neigh->timer.expires = jiffies + neigh->parms->retrans_time;
+				add_timer(&neigh->timer);
+
+				neigh->ops->solicit(neigh, skb);
+			} else {
+				neigh->nud_state = NUD_FAILED;
+				if (skb)
+					kfree_skb(skb, FREE_WRITE);
+				end_bh_atomic();
+				return 1;
+			}
+		}
+		if (neigh->nud_state == NUD_INCOMPLETE) {
+			if (skb) {
+				if (skb_queue_len(&neigh->arp_queue) >= neigh->parms->queue_len) {
+					struct sk_buff *buff;
+					buff = neigh->arp_queue.prev;
+					__skb_unlink(buff, &neigh->arp_queue);
+					kfree_skb(buff, FREE_WRITE);
 				}
+				__skb_queue_head(&neigh->arp_queue, skb);
 			}
-			entry = entry->next;
+			end_bh_atomic();
+			return 1;
+		}
+		if (neigh->nud_state == NUD_STALE) {
+			NEIGH_PRINTK2("neigh %p is delayed.\n", neigh);
+			neigh->nud_state = NUD_DELAY;
+			neigh->timer.expires = jiffies + neigh->parms->delay_probe_time;
+			add_timer(&neigh->timer);
+		}
+	}
+	end_bh_atomic();
+	return 0;
+}
+
+static __inline__ void neigh_update_hhs(struct neighbour *neigh)
+{
+	struct hh_cache *hh;
+	void (*update)(struct hh_cache*, struct device*, unsigned char*) =
+		neigh->dev->header_cache_update;
+
+	if (update) {
+		for (hh=neigh->hh; hh; hh=hh->hh_next)
+			update(hh, neigh->dev, neigh->ha);
+	}
+}
+
+
+
+/* Generic update routine.
+   -- lladdr is new lladdr or NULL, if it is not supplied.
+   -- new    is new state.
+   -- override==1 allows to override existing lladdr, if it is different.
+   -- arp==0 means that that the change is administrative.
+ */
+
+int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int arp)
+{
+	u8 old = neigh->nud_state;
+	struct device *dev = neigh->dev;
+
+	if (arp && (old&(NUD_NOARP|NUD_PERMANENT)))
+		return -EPERM;
+
+	if (!(new&NUD_VALID)) {
+		if (old&NUD_IN_TIMER)
+			del_timer(&neigh->timer);
+		if (old&NUD_CONNECTED)
+			neigh_suspect(neigh);
+		neigh->nud_state = new;
+		return 0;
+	}
+
+	/* Compare new lladdr with cached one */
+	if (dev->addr_len == 0) {
+		/* First case: device needs no address. */
+		lladdr = neigh->ha;
+	} else if (lladdr) {
+		/* The second case: if something is already cached
+		   and a new address is proposed:
+		   - compare new & old
+		   - if they are different, check override flag
+		 */
+		if (old&NUD_VALID) {
+			if (memcmp(lladdr, neigh->ha, dev->addr_len) == 0)
+				lladdr = neigh->ha;
+			else if (!override)
+				return -EPERM;
+		}
+	} else {
+		/* No address is supplied; if we know something,
+		   use it, otherwise discard the request.
+		 */
+		if (!(old&NUD_VALID))
+			return -EINVAL;
+		lladdr = neigh->ha;
+	}
+
+	neigh->used = jiffies;
+	neigh_sync(neigh);
+	old = neigh->nud_state;
+	if (new&NUD_CONNECTED)
+		neigh->confirmed = jiffies;
+	neigh->updated = jiffies;
 
-		} while (entry != *head);
+	/* If entry was valid and address is not changed,
+	   do not change entry state, if new one is STALE.
+	 */
+	if (old&NUD_VALID) {
+		if (lladdr == neigh->ha)
+			if (new == old || (new == NUD_STALE && (old&NUD_CONNECTED)))
+				return 0;
+	}
+	if (old&NUD_IN_TIMER)
+		del_timer(&neigh->timer);
+	neigh->nud_state = new;
+	if (lladdr != neigh->ha) {
+		memcpy(neigh->ha, lladdr, dev->addr_len);
+		neigh_update_hhs(neigh);
+		neigh->confirmed = jiffies - (neigh->parms->base_reachable_time<<1);
+#ifdef CONFIG_ARPD
+		if (neigh->parms->app_probes)
+			neigh_app_notify(neigh);
+#endif
+	}
+	if (new == old)
+		return 0;
+	if (new&NUD_CONNECTED)
+		neigh_connect(neigh);
+	else
+		neigh_suspect(neigh);
+	if (!(old&NUD_VALID)) {
+		struct sk_buff *skb;
+		while ((skb=__skb_dequeue(&neigh->arp_queue)) != NULL)
+			neigh->output(skb);
 	}
+	return 0;
 }
 
-void neigh_tbl_run_bh(struct neigh_table *tbl)
-{       
-	if ((tbl->tbl_bh_mask & NT_MASK_QUEUE))
-	{
-		struct neighbour *neigh;
+struct neighbour * neigh_event_ns(struct neigh_table *tbl,
+				  u8 *lladdr, void *saddr,
+				  struct device *dev)
+{
+	struct neighbour *neigh;
 
-		while((neigh = neigh_dequeue(tbl)))
-		{
-			neigh_table_ins(tbl, neigh);
+	neigh = __neigh_lookup(tbl, saddr, dev, lladdr || !dev->addr_len);
+	if (neigh)
+		neigh_update(neigh, lladdr, NUD_STALE, 1, 1);
+	return neigh;
+}
+
+static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, u16 protocol)
+{
+	struct hh_cache	*hh = NULL;
+	struct device *dev = dst->dev;
+
+	for (hh=n->hh; hh; hh = hh->hh_next)
+		if (hh->hh_type == protocol)
+			break;
+
+	if (!hh && (hh = kmalloc(sizeof(*hh), GFP_ATOMIC)) != NULL) {
+		memset(hh, 0, sizeof(struct hh_cache));
+		hh->hh_type = protocol;
+		atomic_set(&hh->hh_refcnt, 0);
+		hh->hh_next = NULL;
+		if (dev->hard_header_cache(n, hh)) {
+			kfree(hh);
+			hh = NULL;
+		} else {
+			atomic_inc(&hh->hh_refcnt);
+			hh->hh_next = n->hh;
+			n->hh = hh;
+			if (n->nud_state&NUD_CONNECTED)
+				hh->hh_output = n->ops->hh_output;
+			else
+				hh->hh_output = n->ops->output;
 		}
-		tbl->tbl_bh_mask &= ~NT_MASK_QUEUE;
+	}
+	if (hh)	{
+		atomic_inc(&hh->hh_refcnt);
+		dst->hh = hh;
 	}
 }
 
-/*
- * Purge all linked skb's of the entry.
+/* This function can be used in contexts, where only old dev_queue_xmit
+   worked, f.e. if you want to override normal output path (eql, shaper),
+   but resoltution is not made yet.
  */
 
-static void neigh_purge_send_q(struct neighbour *neigh)
+int neigh_compat_output(struct sk_buff *skb)
+{
+	struct device *dev = skb->dev;
+
+	__skb_pull(skb, skb->nh.raw - skb->data);
+
+	if (dev->hard_header &&
+	    dev->hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL, skb->len) < 0 &&
+	    dev->rebuild_header(skb))
+		return 0;
+
+	return dev_queue_xmit(skb);
+}
+
+/* Slow and careful. */
+
+int neigh_resolve_output(struct sk_buff *skb)
+{
+	struct dst_entry *dst = skb->dst;
+	struct neighbour *neigh;
+
+	if (!dst || !(neigh = dst->neighbour))
+		goto discard;
+
+	__skb_pull(skb, skb->nh.raw - skb->data);
+
+	if (neigh_event_send(neigh, skb) == 0) {
+		struct device *dev = neigh->dev;
+		if (dev->hard_header_cache) {
+			start_bh_atomic();
+			if (dst->hh == NULL)
+				neigh_hh_init(neigh, dst, dst->ops->protocol);
+			end_bh_atomic();
+		}
+		if (dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len) >= 0)
+			return neigh->ops->queue_xmit(skb);
+		kfree_skb(skb, FREE_WRITE);
+		return -EINVAL;
+	}
+	return 0;
+
+discard:
+	NEIGH_PRINTK1("neigh_resolve_output: dst=%p neigh=%p\n", dst, dst ? dst->neighbour : NULL);
+	kfree_skb(skb, FREE_WRITE);
+	return -EINVAL;
+}
+
+/* As fast as possible without hh cache */
+
+int neigh_connected_output(struct sk_buff *skb)
+{
+	struct dst_entry *dst = skb->dst;
+	struct neighbour *neigh = dst->neighbour;
+	struct device *dev = neigh->dev;
+
+	__skb_pull(skb, skb->nh.raw - skb->data);
+
+	if (dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len) >= 0)
+		return neigh->ops->queue_xmit(skb);
+	kfree_skb(skb, FREE_WRITE);
+	return -EINVAL;
+}
+
+static void neigh_proxy_process(unsigned long arg)
+{
+	struct neigh_table *tbl = (struct neigh_table *)arg;
+	long sched_next = 0;
+	unsigned long now = jiffies;
+	struct sk_buff *skb = tbl->proxy_queue.next;
+
+	while (skb != (struct sk_buff*)&tbl->proxy_queue) {
+		struct sk_buff *back = skb;
+		skb = skb->next;
+		if (back->stamp.tv_usec - now <= 0) {
+			__skb_unlink(back, &tbl->proxy_queue);
+			if (tbl->proxy_redo)
+				tbl->proxy_redo(back);
+			else
+				kfree_skb(back, FREE_WRITE);
+		} else {
+			if (!sched_next || back->stamp.tv_usec - now < sched_next)
+				sched_next = back->stamp.tv_usec - now;
+		}
+	}
+	del_timer(&tbl->proxy_timer);
+	if (sched_next) {
+		tbl->proxy_timer.expires = jiffies + sched_next;
+		add_timer(&tbl->proxy_timer);
+	}
+}
+
+void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
+		    struct sk_buff *skb)
+{
+	unsigned long now = jiffies;
+	long sched_next = net_random()%p->proxy_delay;
+
+	if (tbl->proxy_queue.qlen > p->proxy_qlen) {
+		kfree_skb(skb, FREE_READ);
+		return;
+	}
+	skb->stamp.tv_sec = 0;
+	skb->stamp.tv_usec = now + sched_next;
+	if (del_timer(&tbl->proxy_timer)) {
+		long tval = tbl->proxy_timer.expires - now;
+		if (tval < sched_next)
+			sched_next = tval;
+	}
+	tbl->proxy_timer.expires = now + sched_next;
+	dst_release(skb->dst);
+	skb->dst = NULL;
+	__skb_queue_tail(&tbl->proxy_queue, skb);
+	add_timer(&tbl->proxy_timer);
+}
+
+
+struct neigh_parms *neigh_parms_alloc(struct neigh_table *tbl)
+{
+	struct neigh_parms *p;
+	p = kmalloc(sizeof(*p), GFP_KERNEL);
+	if (p) {
+		memcpy(p, &tbl->parms, sizeof(*p));
+		p->reachable_time = neigh_rand_reach_time(p->base_reachable_time);
+		start_bh_atomic();
+		p->next = tbl->parms.next;
+		tbl->parms.next = p;
+		end_bh_atomic();
+	}
+	return p;
+}
+
+void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
+{
+	struct neigh_parms **p;
+	
+	if (parms == NULL || parms == &tbl->parms)
+		return;
+	for (p = &tbl->parms.next; *p; p = &(*p)->next) {
+		if (*p == parms) {
+			start_bh_atomic();
+			*p = parms->next;
+			end_bh_atomic();
+#ifdef CONFIG_SYSCTL
+			neigh_sysctl_unregister(parms);
+#endif
+			kfree(parms);
+			return;
+		}
+	}
+	NEIGH_PRINTK1("neigh_release_parms: not found\n");
+}
+
+
+void neigh_table_init(struct neigh_table *tbl)
+{
+	unsigned long now = jiffies;
+
+	tbl->parms.reachable_time = neigh_rand_reach_time(tbl->parms.base_reachable_time);
+
+	init_timer(&tbl->gc_timer);
+	tbl->gc_timer.data = (unsigned long)tbl;
+	tbl->gc_timer.function = neigh_periodic_timer;
+	tbl->gc_timer.expires = now + tbl->gc_interval + tbl->parms.reachable_time;
+	add_timer(&tbl->gc_timer);
+
+	init_timer(&tbl->proxy_timer);
+	tbl->proxy_timer.data = (unsigned long)tbl;
+	tbl->proxy_timer.function = neigh_proxy_process;
+	skb_queue_head_init(&tbl->proxy_queue);
+
+	tbl->last_flush = now;
+	tbl->last_rand = now + tbl->parms.reachable_time*20;
+	tbl->next = neigh_tables;
+	neigh_tables = tbl;
+}
+
+int neigh_table_clear(struct neigh_table *tbl)
+{
+	struct neigh_table **tp;
+
+	start_bh_atomic();
+	del_timer(&tbl->gc_timer);
+	del_timer(&tbl->proxy_timer);
+	skb_queue_purge(&tbl->proxy_queue);
+	if (tbl->entries)
+		neigh_ifdown(tbl, NULL);
+	end_bh_atomic();
+	if (tbl->entries)
+		printk(KERN_CRIT "neighbour leakage\n");
+	for (tp = &neigh_tables; *tp; tp = &(*tp)->next) {
+		if (*tp == tbl) {
+			*tp = tbl->next;
+			break;
+		}
+	}
+#ifdef CONFIG_SYSCTL
+	neigh_sysctl_unregister(&tbl->parms);
+#endif
+	return 0;
+}
+
+#ifdef CONFIG_RTNETLINK
+
+
+int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+	struct ndmsg *ndm = NLMSG_DATA(nlh);
+	struct rtattr **nda = arg;
+	struct neigh_table *tbl;
+	struct device *dev = NULL;
+
+	if (ndm->ndm_ifindex) {
+		if ((dev = dev_get_by_index(ndm->ndm_ifindex)) == NULL)
+			return -ENODEV;
+	}
+
+	for (tbl=neigh_tables; tbl; tbl = tbl->next) {
+		int err = 0;
+		struct neighbour *n;
+
+		if (tbl->family != ndm->ndm_family)
+			continue;
+
+		if (nda[NDA_DST-1] == NULL ||
+		    nda[NDA_DST-1]->rta_len != RTA_LENGTH(tbl->key_len))
+			return -EINVAL;
+
+		if (ndm->ndm_flags&NTF_PROXY)
+			return pneigh_delete(tbl, RTA_DATA(nda[NDA_DST-1]), dev);
+
+		if (dev == NULL)
+			return -EINVAL;
+
+		start_bh_atomic();
+		n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev);
+		if (n) {
+			err = neigh_update(n, NULL, NUD_FAILED, 1, 0);
+			neigh_release(n);
+		}
+		end_bh_atomic();
+		return err;
+	}
+
+	return -EADDRNOTAVAIL;
+}
+
+int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+	struct ndmsg *ndm = NLMSG_DATA(nlh);
+	struct rtattr **nda = arg;
+	struct neigh_table *tbl;
+	struct device *dev = NULL;
+
+	if (ndm->ndm_ifindex) {
+		if ((dev = dev_get_by_index(ndm->ndm_ifindex)) == NULL)
+			return -ENODEV;
+	}
+
+	for (tbl=neigh_tables; tbl; tbl = tbl->next) {
+		int err = 0;
+		struct neighbour *n;
+
+		if (tbl->family != ndm->ndm_family)
+			continue;
+		if (nda[NDA_DST-1] == NULL ||
+		    nda[NDA_DST-1]->rta_len != RTA_LENGTH(tbl->key_len))
+			return -EINVAL;
+		if (ndm->ndm_flags&NTF_PROXY) {
+			if (pneigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 1))
+				return 0;
+			return -ENOBUFS;
+		}
+		if (dev == NULL)
+			return -EINVAL;
+		if (nda[NDA_LLADDR-1] != NULL &&
+		    nda[NDA_LLADDR-1]->rta_len != RTA_LENGTH(dev->addr_len))
+			return -EINVAL;
+		start_bh_atomic();
+		n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev);
+		if (n) {
+			if (nlh->nlmsg_flags&NLM_F_EXCL)
+				err = -EEXIST;
+		} else if (!(nlh->nlmsg_flags&NLM_F_CREATE))
+			err = -ENOENT;
+		else {
+			n = __neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 1);
+			if (n == NULL)
+				err = -ENOBUFS;
+		}
+		if (err == 0) {
+			err = neigh_update(n, nda[NDA_LLADDR-1] ? RTA_DATA(nda[NDA_LLADDR-1]) : NULL,
+					   ndm->ndm_state,
+					   nlh->nlmsg_flags&NLM_F_REPLACE, 0);
+		}
+		neigh_release(n);
+		end_bh_atomic();
+		return err;
+	}
+
+	return -EADDRNOTAVAIL;
+}
+
+
+static int neigh_fill_info(struct sk_buff *skb, struct neighbour *n,
+			    pid_t pid, u32 seq, int event)
+{
+	unsigned long now = jiffies;
+	struct ndmsg *ndm;
+	struct nlmsghdr  *nlh;
+	unsigned char	 *b = skb->tail;
+	struct nda_cacheinfo ci;
+
+	nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*ndm));
+	ndm = NLMSG_DATA(nlh);
+	ndm->ndm_family = n->ops->family;
+	ndm->ndm_flags = n->flags;
+	ndm->ndm_type = n->type;
+	ndm->ndm_state = n->nud_state;
+	ndm->ndm_ifindex = n->dev->ifindex;
+	RTA_PUT(skb, NDA_DST, n->tbl->key_len, n->primary_key);
+	if (n->nud_state&NUD_VALID)
+		RTA_PUT(skb, NDA_LLADDR, n->dev->addr_len, n->ha);
+	ci.ndm_used = now - n->used;
+	ci.ndm_confirmed = now - n->confirmed;
+	ci.ndm_updated = now - n->updated;
+	ci.ndm_refcnt = atomic_read(&n->refcnt);
+	RTA_PUT(skb, NDA_CACHEINFO, sizeof(ci), &ci);
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+	skb_put(skb, b - skb->tail);
+	return -1;
+}
+
+
+static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct neighbour *n;
+	int h, s_h;
+	int idx, s_idx;
+
+	s_h = cb->args[1];
+	s_idx = idx = cb->args[2];
+	for (h=0; h <= NEIGH_HASHMASK; h++) {
+		if (h < s_h) continue;
+		if (h > s_h)
+			memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(int));
+		start_bh_atomic();
+		for (n = tbl->hash_buckets[h], idx = 0; n;
+		     n = n->next, idx++) {
+			if (idx < s_idx)
+				continue;
+			if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid,
+					    cb->nlh->nlmsg_seq, RTM_NEWNEIGH) <= 0) {
+				end_bh_atomic();
+				goto done;
+			}
+		}
+		end_bh_atomic();
+	}
+done:
+	cb->args[1] = h;
+	cb->args[2] = idx;
+	return skb->len;
+}
+
+int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	int t;
+	int s_t;
+	struct neigh_table *tbl;
+	int family = ((struct rtgenmsg*)NLMSG_DATA(cb->nlh))->rtgen_family;
+
+	s_t = cb->args[0];
+
+	for (tbl=neigh_tables, t=0; tbl; tbl = tbl->next, t++) {
+		if (t < s_t) continue;
+		if (family && tbl->family != family)
+			continue;
+		if (t > s_t)
+			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int));
+		if (neigh_dump_table(tbl, skb, cb) < 0) 
+			break;
+	}
+
+	cb->args[0] = t;
+
+	return skb->len;
+}
+
+#ifdef CONFIG_ARPD
+void neigh_app_ns(struct neighbour *n)
 {
 	struct sk_buff *skb;
+	struct nlmsghdr  *nlh;
+	int size = NLMSG_SPACE(sizeof(struct ndmsg)+256);
 
-	/* Release the list of `skb' pointers. */
-	while ((skb = skb_dequeue(&neigh->arp_queue)))
-	{
-		dev_kfree_skb(skb, FREE_WRITE);
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (!skb)
+		return;
+
+	if (neigh_fill_info(skb, n, 0, 0, RTM_GETNEIGH) < 0) {
+		kfree_skb(skb, 0);
+		return;
 	}
-	return;
+	nlh = (struct nlmsghdr*)skb->data;
+	nlh->nlmsg_flags = NLM_F_REQUEST;
+	NETLINK_CB(skb).dst_groups = RTMGRP_NEIGH;
+	netlink_broadcast(rtnl, skb, 0, RTMGRP_NEIGH, GFP_ATOMIC);
 }
+
+static void neigh_app_notify(struct neighbour *n)
+{
+	struct sk_buff *skb;
+	struct nlmsghdr  *nlh;
+	int size = NLMSG_SPACE(sizeof(struct ndmsg)+256);
+
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (!skb)
+		return;
+
+	if (neigh_fill_info(skb, n, 0, 0, RTM_NEWNEIGH) < 0) {
+		kfree_skb(skb, 0);
+		return;
+	}
+	nlh = (struct nlmsghdr*)skb->data;
+	NETLINK_CB(skb).dst_groups = RTMGRP_NEIGH;
+	netlink_broadcast(rtnl, skb, 0, RTMGRP_NEIGH, GFP_ATOMIC);
+}
+
+
+
+#endif
+
+
+#endif
+
+#ifdef CONFIG_SYSCTL
+
+struct neigh_sysctl_table
+{
+	struct ctl_table_header *sysctl_header;
+	ctl_table neigh_vars[17];
+	ctl_table neigh_dev[2];
+	ctl_table neigh_neigh_dir[2];
+	ctl_table neigh_proto_dir[2];
+	ctl_table neigh_root_dir[2];
+} neigh_sysctl_template = {
+	NULL,
+        {{NET_NEIGH_MCAST_SOLICIT, "mcast_solicit",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_UCAST_SOLICIT, "ucast_solicit",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_APP_SOLICIT, "app_solicit",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_RETRANS_TIME, "retrans_time",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_REACHABLE_TIME, "base_reachable_time",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_DELAY_PROBE_TIME, "delay_first_probe_time",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_GC_STALE_TIME, "gc_stale_time",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_UNRES_QLEN, "unres_qlen",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_PROXY_QLEN, "proxy_qlen",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_ANYCAST_DELAY, "anycast_delay",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_PROXY_DELAY, "proxy_delay",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_LOCKTIME, "locktime",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_GC_INTERVAL, "gc_interval",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_GC_THRESH1, "gc_thresh1",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_GC_THRESH2, "gc_thresh2",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_NEIGH_GC_THRESH3, "gc_thresh3",
+         NULL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	 {0}},
+
+	{{1, "default", NULL, 0, 0555, NULL},{0}},
+	{{0, "neigh", NULL, 0, 0555, NULL},{0}},
+	{{0, NULL, NULL, 0, 0555, NULL},{0}},
+	{{CTL_NET, "net", NULL, 0, 0555, NULL},{0}}
+};
+
+void * neigh_sysctl_register(struct device *dev, struct neigh_parms *p,
+			     int p_id, int pdev_id, char *p_name)
+{
+	struct neigh_sysctl_table *t;
+
+	t = kmalloc(sizeof(*t), GFP_KERNEL);
+	if (t == NULL)
+		return NULL;
+	memcpy(t, &neigh_sysctl_template, sizeof(*t));
+	t->neigh_vars[0].data = &p->mcast_probes;
+	t->neigh_vars[1].data = &p->ucast_probes;
+	t->neigh_vars[2].data = &p->app_probes;
+	t->neigh_vars[3].data = &p->retrans_time;
+	t->neigh_vars[4].data = &p->reachable_time;
+	t->neigh_vars[5].data = &p->delay_probe_time;
+	t->neigh_vars[6].data = &p->gc_staletime;
+	t->neigh_vars[7].data = &p->queue_len;
+	t->neigh_vars[8].data = &p->proxy_qlen;
+	t->neigh_vars[9].data = &p->anycast_delay;
+	t->neigh_vars[10].data = &p->proxy_delay;
+	t->neigh_vars[11].data = &p->locktime;
+	if (dev) {
+		t->neigh_dev[0].procname = dev->name;
+		t->neigh_dev[0].ctl_name = dev->ifindex+1;
+		memset(&t->neigh_vars[12], 0, sizeof(ctl_table));
+	} else {
+		t->neigh_vars[12].data = (&p->locktime) + 1;
+		t->neigh_vars[13].data = (&p->locktime) + 2;
+		t->neigh_vars[14].data = (&p->locktime) + 3;
+		t->neigh_vars[15].data = (&p->locktime) + 4;
+	}
+	t->neigh_neigh_dir[0].ctl_name = pdev_id;
+
+	t->neigh_proto_dir[0].procname = p_name;
+	t->neigh_proto_dir[0].ctl_name = p_id;
+
+	t->neigh_dev[0].child = t->neigh_vars;
+	t->neigh_neigh_dir[0].child = t->neigh_dev;
+	t->neigh_proto_dir[0].child = t->neigh_neigh_dir;
+	t->neigh_root_dir[0].child = t->neigh_proto_dir;
+
+	t->sysctl_header = register_sysctl_table(t->neigh_root_dir, 0);
+	if (t->sysctl_header == NULL) {
+		kfree(t);
+		return NULL;
+	}
+	return t;
+}
+
+void neigh_sysctl_unregister(struct neigh_parms *p)
+{
+	if (p->sysctl_table) {
+		struct neigh_sysctl_table *t = p->sysctl_table;
+		p->sysctl_table = NULL;
+		unregister_sysctl_table(t->sysctl_header);
+		kfree(t);
+	}
+}
+
+#endif	/* CONFIG_SYSCTL */
diff -u --recursive --new-file v2.1.78/linux/net/core/profile.c linux/net/core/profile.c
--- v2.1.78/linux/net/core/profile.c	Wed Dec 31 16:00:00 1969
+++ linux/net/core/profile.c	Mon Jan 12 15:28:25 1998
@@ -0,0 +1,304 @@
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/ip.h>
+#include <linux/inet.h>
+#include <net/checksum.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <net/profile.h>
+
+#ifdef CONFIG_NET_PROFILE
+
+atomic_t net_profile_active;
+struct timeval net_profile_adjust;
+
+NET_PROFILE_DEFINE(total);
+
+struct net_profile_slot *net_profile_chain = &net_prof_total;
+
+#ifdef __alpha__
+__u32 alpha_lo;
+long alpha_hi;
+
+static void alpha_tick(unsigned long);
+
+static struct timer_list alpha_timer =
+	{ NULL, NULL, 0, 0L, alpha_tick };
+
+void alpha_tick(unsigned long dummy)
+{
+	struct timeval dummy_stamp;
+	net_profile_stamp(&dummy_stamp);
+	alpha_timer.expires = jiffies + 4*HZ;
+	add_timer(&alpha_timer);
+}
+
+#endif
+
+void net_profile_irq_adjust(struct timeval *entered, struct timeval* leaved)
+{
+	struct net_profile_slot *s;
+
+	net_profile_sub(entered, leaved);
+	for (s = net_profile_chain; s; s = s->next) {
+		if (s->active)
+			net_profile_add(leaved, &s->irq);
+	}
+}
+
+
+#ifdef CONFIG_PROC_FS
+static int profile_read_proc(char *buffer, char **start, off_t offset,
+			     int length, int *eof, void *data)
+{
+	off_t pos=0;
+	off_t begin=0;
+	int len=0;
+	struct net_profile_slot *s;
+
+	len+= sprintf(buffer, "Slot            Hits       Hi         Lo         OnIrqHi    OnIrqLo    Ufl\n");
+
+	if (offset == 0) {
+		cli();
+		net_prof_total.active = 1;
+		atomic_inc(&net_profile_active);
+		NET_PROFILE_LEAVE(total);
+		sti();
+	}
+	for (s = net_profile_chain; s; s = s->next) {
+		struct net_profile_slot tmp;
+
+		cli();
+		tmp = *s;
+
+		/* Wrong, but pretty close to truth */
+
+		s->accumulator.tv_sec = 0;
+		s->accumulator.tv_usec = 0;
+		s->irq.tv_sec = 0;
+		s->irq.tv_usec = 0;
+		s->hits = 0;
+		s->underflow = 0;
+		/* Repair active count, it is possible, only if code has a bug */
+		if (s->active) {
+			s->active = 0;
+			atomic_dec(&net_profile_active);
+		}
+		sti();
+
+		net_profile_sub(&tmp.irq, &tmp.accumulator);
+
+		len += sprintf(buffer+len,"%-15s %-10d %-10ld %-10lu %-10lu %-10lu %d/%d",
+			       tmp.id,
+			       tmp.hits,
+			       tmp.accumulator.tv_sec,
+			       tmp.accumulator.tv_usec,
+			       tmp.irq.tv_sec,
+			       tmp.irq.tv_usec,
+			       tmp.underflow, tmp.active);
+
+			buffer[len++]='\n';
+		
+			pos=begin+len;
+			if(pos<offset) {
+				len=0;
+				begin=pos;
+			}
+			if(pos>offset+length)
+				goto done;
+	}
+	*eof = 1;
+
+done:
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;
+	if (len < 0) {
+		len = 0;
+		printk(KERN_CRIT "Yep, guys... our template for proc_*_read is crappy :-)\n");
+	}
+	if (offset == 0) {
+		cli();
+		net_prof_total.active = 0;
+		net_prof_total.hits = 0;
+		net_profile_stamp(&net_prof_total.entered);
+		sti();
+	}
+	return len;
+}
+#endif
+
+struct iphdr whitehole_iph;
+int whitehole_count;
+
+static int whitehole_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct net_device_stats *stats;
+	dev_kfree_skb(skb, FREE_WRITE);
+	stats = (struct net_device_stats *)dev->priv;
+	stats->tx_packets++;
+	stats->tx_bytes+=skb->len;
+
+	return 0;
+}
+
+static void whitehole_inject(unsigned long);
+int whitehole_init(struct device *dev);
+
+static struct timer_list whitehole_timer =
+	{ NULL, NULL, 0, 0L, whitehole_inject };
+
+static struct device whitehole_dev = {
+	"whitehole", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, whitehole_init, };
+
+static int whitehole_open(struct device *dev)
+{
+	whitehole_count = 100000;
+	whitehole_timer.expires = jiffies + 5*HZ;
+	add_timer(&whitehole_timer);
+	return 0;
+}
+
+static int whitehole_close(struct device *dev)
+{
+	del_timer(&whitehole_timer);
+	return 0;
+}
+
+static void whitehole_inject(unsigned long dummy)
+{
+	struct net_device_stats *stats = (struct net_device_stats *)whitehole_dev.priv;
+	extern int netdev_dropping;
+
+	do {
+		struct iphdr *iph;
+		struct sk_buff *skb = alloc_skb(128, GFP_ATOMIC);
+		if (!skb)
+			break;
+		skb_reserve(skb, 32);
+		iph = (struct iphdr*)skb_put(skb, sizeof(*iph));
+		skb->mac.raw = ((u8*)iph) - 14;
+		memcpy(iph, &whitehole_iph, sizeof(*iph));
+		skb->protocol = __constant_htons(ETH_P_IP);
+		skb->dev = &whitehole_dev;
+		skb->pkt_type = PACKET_HOST;
+		stats->rx_packets++;
+		stats->rx_bytes += skb->len;
+		netif_rx(skb);
+		whitehole_count--;
+	} while (netdev_dropping == 0 && whitehole_count>0);
+	if (whitehole_count > 0) {
+		whitehole_timer.expires = jiffies + 1;
+		add_timer(&whitehole_timer);
+	}
+}
+
+static struct net_device_stats *whitehole_get_stats(struct device *dev)
+{
+	struct net_device_stats *stats = (struct net_device_stats *) dev->priv;
+	return stats;
+}
+
+__initfunc(int whitehole_init(struct device *dev))
+{
+	dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
+	if (dev->priv == NULL)
+		return -ENOBUFS;
+	memset(dev->priv, 0, sizeof(struct net_device_stats));
+	dev->get_stats	= whitehole_get_stats;
+	dev->hard_start_xmit = whitehole_xmit;
+	dev->open = whitehole_open;
+	dev->stop = whitehole_close;
+	ether_setup(dev);
+	dev->tx_queue_len = 0;
+	dev->flags |= IFF_NOARP;
+	dev->flags &= ~(IFF_BROADCAST|IFF_MULTICAST);
+	dev->iflink = 0;
+	whitehole_iph.ihl = 5;
+	whitehole_iph.version = 4;
+	whitehole_iph.ttl = 2;
+	whitehole_iph.saddr = in_aton("193.233.7.21");
+	whitehole_iph.daddr = in_aton("193.233.7.10");
+	whitehole_iph.tot_len = htons(20);
+	whitehole_iph.check = ip_compute_csum((void *)&whitehole_iph, 20);
+	return 0;
+}
+
+int net_profile_register(struct net_profile_slot *slot)
+{
+	cli();
+	slot->next = net_profile_chain;
+	net_profile_chain = slot;
+	sti();
+	return 0;
+}
+
+int net_profile_unregister(struct net_profile_slot *slot)
+{
+	struct net_profile_slot **sp, *s;
+
+	for (sp = &net_profile_chain; (s = *sp) != NULL; sp = &s->next) {
+		if (s == slot) {
+			cli();
+			*sp = s->next;
+			sti();
+			return 0;
+		}
+	}
+	return -ESRCH;
+}
+
+
+__initfunc(int net_profile_init(void))
+{
+	int i;
+
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *ent;
+
+	ent = create_proc_entry("net/profile", 0, 0);
+	ent->read_proc = profile_read_proc;
+#endif
+
+	register_netdevice(&whitehole_dev);
+
+	printk("Evaluating net profiler cost ...");
+#if CPU == 586 || CPU == 686
+	if (!(boot_cpu_data.x86_capability & 16)) {
+		panic("Sorry, you CPU does not support tsc. I am dying...\n");
+		return -1;
+	}
+#endif
+	start_bh_atomic();
+#ifdef __alpha__
+	alpha_tick(0);
+#endif
+	for (i=0; i<1024; i++) {
+		NET_PROFILE_ENTER(total);
+		NET_PROFILE_LEAVE(total);
+	}
+	if (net_prof_total.accumulator.tv_sec) {
+		printk(" too high!\n");
+	} else {
+		net_profile_adjust.tv_usec = net_prof_total.accumulator.tv_usec>>10;
+		printk("%ld units\n", net_profile_adjust.tv_usec);
+	}
+	net_prof_total.hits = 0;
+	net_profile_stamp(&net_prof_total.entered);
+	end_bh_atomic();
+	return 0;
+}
+
+#endif
diff -u --recursive --new-file v2.1.78/linux/net/core/rtnetlink.c linux/net/core/rtnetlink.c
--- v2.1.78/linux/net/core/rtnetlink.c	Mon Dec  1 12:04:16 1997
+++ linux/net/core/rtnetlink.c	Mon Jan 12 15:28:25 1998
@@ -134,6 +134,22 @@
 	return 0;
 }
 
+static int rtnetlink_get_ga(struct rtattr **rta, int sz,
+			    struct rtattr *attr, int attrlen)
+{
+	while (RTA_OK(attr, attrlen)) {
+		int type = attr->rta_type;
+		if (type > 0) {
+			if (type > sz)
+				return -EINVAL;
+			rta[type-1] = attr;
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+	return 0;
+}
+
+
 void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data)
 {
 	struct rtattr *rta;
@@ -150,6 +166,7 @@
 {
 	struct ifinfomsg *r;
 	struct nlmsghdr  *nlh;
+	unsigned char	 *b = skb->tail;
 
 	nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r));
 	if (pid) nlh->nlmsg_flags |= NLM_F_MULTI;
@@ -168,9 +185,17 @@
 	r->ifi_qdisc = dev->qdisc_sleeping->handle;
 	if (dev->qdisc_sleeping->ops)
 		strcpy(r->ifi_qdiscname, dev->qdisc_sleeping->ops->id);
+	if (dev->get_stats) {
+		struct net_device_stats *stats = dev->get_stats(dev);
+		if (stats)
+			RTA_PUT(skb, IFLA_STATS, sizeof(*stats), stats);
+	}
+	nlh->nlmsg_len = skb->tail - b;
 	return skb->len;
 
 nlmsg_failure:
+rtattr_failure:
+	skb_put(skb, b - skb->tail);
 	return -1;
 }
 
@@ -191,10 +216,38 @@
 	return skb->len;
 }
 
+int rtnetlink_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	int idx;
+	int s_idx = cb->family;
+
+	if (s_idx == 0)
+		s_idx = 1;
+	for (idx=1; idx<NPROTO; idx++) {
+		int type = cb->nlh->nlmsg_type-RTM_BASE;
+		if (idx < s_idx || idx == AF_PACKET)
+			continue;
+		if (rtnetlink_links[idx] == NULL ||
+		    rtnetlink_links[idx][type].dumpit == NULL)
+			continue;
+		if (idx > s_idx)
+			memset(&cb->args[0], 0, sizeof(cb->args));
+		if (rtnetlink_links[idx][type].dumpit(skb, cb) == 0)
+			continue;
+		if (skb_tailroom(skb) < 256)
+			break;
+	}
+	cb->family = idx;
+
+	return skb->len;
+}
+
+
 void rtmsg_ifinfo(int type, struct device *dev)
 {
 	struct sk_buff *skb;
-	int size = NLMSG_SPACE(sizeof(struct ifinfomsg));
+	int size = NLMSG_SPACE(sizeof(struct ifinfomsg)+
+			       RTA_LENGTH(sizeof(struct net_device_stats)));
 
 	skb = alloc_skb(size, GFP_KERNEL);
 	if (!skb)
@@ -223,9 +276,11 @@
 	union {
 		struct kern_rta rta;
 		struct kern_ifa ifa;
+		struct rtattr	*ga[RTA_MAX-1];
 	} u;
 	struct rtmsg *rtm;
 	struct ifaddrmsg *ifm;
+	struct ndmsg *ndm;
 	int exclusive = 0;
 	int family;
 	int type;
@@ -307,13 +362,22 @@
 				      nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifm))) < 0)
 			goto err_inval;
 		break;
-	
-	case RTM_NEWLINK:
-	case RTM_DELLINK:
-	case RTM_GETLINK:
 	case RTM_NEWNEIGH:
 	case RTM_DELNEIGH:
 	case RTM_GETNEIGH:
+		ndm = NLMSG_DATA(nlh);
+		if (nlh->nlmsg_len < sizeof(*ndm))
+			goto err_inval;
+
+		if (nlh->nlmsg_len > NLMSG_LENGTH(sizeof(*ndm)) &&
+		    rtnetlink_get_ga(u.ga, NDA_MAX, NDA_RTA(ndm),
+				      nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ndm))) < 0)
+			goto err_inval;
+		break;
+
+	case RTM_NEWLINK:
+	case RTM_DELLINK:
+	case RTM_GETLINK:
 		/* Not urgent and even not necessary */
 	default:
 		goto err_inval;
@@ -399,6 +463,35 @@
 	rtnl_shunlock();
 }
 
+static struct rtnetlink_link link_rtnetlink_table[RTM_MAX-RTM_BASE+1] =
+{
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			rtnetlink_dump_ifinfo,	},
+	{ NULL,			NULL,			},
+
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			rtnetlink_dump_all,	},
+	{ NULL,			NULL,			},
+
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			rtnetlink_dump_all,	},
+	{ NULL,			NULL,			},
+
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			neigh_dump_info,	},
+	{ NULL,			NULL,			},
+
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+};
+
+
 static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
 	struct device *dev = ptr;
@@ -429,6 +522,8 @@
 	if (rtnl == NULL)
 		panic("rtnetlink_init: cannot initialize rtnetlink\n");
 	register_netdevice_notifier(&rtnetlink_dev_notifier);
+	rtnetlink_links[AF_UNSPEC] = link_rtnetlink_table;
+	rtnetlink_links[AF_PACKET] = link_rtnetlink_table;
 }
 
 
diff -u --recursive --new-file v2.1.78/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v2.1.78/linux/net/core/skbuff.c	Mon Dec  1 12:04:16 1997
+++ linux/net/core/skbuff.c	Mon Jan 12 15:28:25 1998
@@ -303,7 +303,6 @@
 	n->ack_seq=skb->ack_seq;
 	memcpy(n->cb, skb->cb, sizeof(skb->cb));
 	n->used=skb->used;
-	n->arp=skb->arp;
 	n->tries=0;
 	atomic_set(&n->users, 1);
 	n->pkt_type=skb->pkt_type;
@@ -354,7 +353,6 @@
  	n->end_seq=skb->end_seq;
 	n->ack_seq=skb->ack_seq;
 	n->used=skb->used;
-	n->arp=skb->arp;
 	n->tries=0;
 	atomic_set(&n->users, 1);
 	n->pkt_type=skb->pkt_type;
@@ -363,14 +361,4 @@
 	n->security=skb->security;
 
 	return n;
-}
-  
-struct sk_buff *dev_alloc_skb(unsigned int length)
-{
-	struct sk_buff *skb;
-
-	skb = alloc_skb(length+16, GFP_ATOMIC);
-	if (skb)
-		skb_reserve(skb,16);
-	return skb;
 }
diff -u --recursive --new-file v2.1.78/linux/net/core/sock.c linux/net/core/sock.c
--- v2.1.78/linux/net/core/sock.c	Sun Dec 21 22:36:17 1997
+++ linux/net/core/sock.c	Mon Jan 12 15:28:25 1998
@@ -76,7 +76,7 @@
  *              Steve Whitehouse:       Added various other default routines
  *                                      common to several socket families.
  *              Chris Evans     :       Call suser() check last on F_SETOWN
- *		Jay Schulist	:	Added SO_ATTACH_FILTER and SO_DETACH_FILTER.
+ *		Andi Kleen	:	Add sock_kmalloc()/sock_kfree_s()
  *
  * To Fix:
  *
@@ -123,10 +123,6 @@
 #include <net/icmp.h>
 #include <linux/ipsec.h>
 
-#ifdef CONFIG_FILTER
-#include <linux/filter.h>
-#endif
-
 #define min(a,b)	((a)<(b)?(a):(b))
 
 /* Run time adjustable parameters. */
@@ -152,10 +148,6 @@
 	struct linger ling;
 	struct ifreq req;
 	int ret = 0;
-
-#ifdef CONFIG_FILTER
-	struct sock_fprog fprog;
-#endif
 	
 	/*
 	 *	Options without arguments
@@ -287,6 +279,48 @@
 			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
 		case SO_BINDTODEVICE:
 			/* Bind this socket to a particular device like "eth0",
 			 * as specified in an ifreq structure. If the device
@@ -326,33 +360,6 @@
 			}
 			return 0;
 
-#ifdef CONFIG_FILTER
-		case SO_ATTACH_FILTER:
-			if(optlen < sizeof(struct sock_fprog))
-				return -EINVAL;
-
-			if(copy_from_user(&fprog, optval, sizeof(fprog)))
-			{
-				ret = -EFAULT;
-				break;
-			}
-
-			ret = sk_attach_filter(&fprog, sk);
-			break;
-
-		case SO_DETACH_FILTER:
-                        if(sk->filter)
-			{
-				fprog.filter = sk->filter_data;
-				kfree_s(fprog.filter, (sizeof(fprog.filter) * sk->filter));
-				sk->filter_data = NULL;
-				sk->filter = 0;
-				return 0;
-			}
-			else
-				return -EINVAL;
-			break;
-#endif
 
 		/* We implement the SO_SNDLOWAT etc to
 		   not be settable (1003.1g 5.3) */
@@ -583,6 +590,40 @@
 	return NULL;
 }
 
+void *sock_kmalloc(struct sock *sk, int size, int priority)
+{
+	void *mem = NULL;
+	/* Always use wmem.. */
+	if (atomic_read(&sk->wmem_alloc)+size < sk->sndbuf) {
+		/* First do the add, to avoid the race if kmalloc
+ 		 * might sleep.
+		 */
+		atomic_add(size, &sk->wmem_alloc);
+		mem = kmalloc(size, priority);
+		if (mem) {
+			/* Recheck because kmalloc might sleep */
+			if (atomic_read(&sk->wmem_alloc)+size < sk->sndbuf)
+				return mem; 
+			kfree_s(mem, size);
+		} 
+		atomic_sub(size, &sk->wmem_alloc);
+	}
+	return mem;
+}
+
+void sock_kfree_s(struct sock *sk, void *mem, int size)
+{
+#if 1 /* Debug */
+	if (atomic_read(&sk->wmem_alloc) < size) {
+		printk(KERN_DEBUG "sock_kfree_s: mem not accounted.\n");
+		return;
+	}
+#endif
+	kfree_s(mem, size); 
+	atomic_sub(size, &sk->wmem_alloc);
+	sk->write_space(sk);
+}
+
 
 /* FIXME: this is insane. We are trying suppose to be controlling how
  * how much space we have for data bytes, not packet headers.
@@ -621,7 +662,7 @@
 	if (sk != NULL) {
 		if (sk->shutdown & SEND_SHUTDOWN)
 			return(0);
-		if (atomic_read(&sk->wmem_alloc) >= sk->sndbuf)
+		if (atomic_read(&sk->wmem_alloc) >= sk->sndbuf) 
 			return(0);
 		return sk->sndbuf - atomic_read(&sk->wmem_alloc);
 	}
@@ -1003,8 +1044,8 @@
 	init_timer(&sk->timer);
 	
 	sk->allocation	=	GFP_KERNEL;
-	sk->rcvbuf	=	sysctl_rmem_default*2;
-	sk->sndbuf	=	sysctl_wmem_default*2;
+	sk->rcvbuf	=	sysctl_rmem_default;
+	sk->sndbuf	=	sysctl_wmem_default;
 	sk->state 	= 	TCP_CLOSE;
 	sk->zapped	=	1;
 	sk->socket	=	sock;
diff -u --recursive --new-file v2.1.78/linux/net/core/sysctl_net_core.c linux/net/core/sysctl_net_core.c
--- v2.1.78/linux/net/core/sysctl_net_core.c	Mon Jul  7 08:19:59 1997
+++ linux/net/core/sysctl_net_core.c	Mon Jan 12 15:28:25 1998
@@ -11,6 +11,9 @@
 
 #ifdef CONFIG_SYSCTL
 
+extern int netdev_max_backlog;
+extern int netdev_fastroute;
+
 extern __u32 sysctl_wmem_max;
 extern __u32 sysctl_rmem_max;
 extern __u32 sysctl_wmem_default;
@@ -34,6 +37,14 @@
 	{NET_CORE_DESTROY_DELAY, "destroy_delay",
 	 &sysctl_core_destroy_delay, sizeof(int), 0644, NULL,
 	 &proc_dointvec_jiffies},
+	{NET_CORE_MAX_BACKLOG, "netdev_max_backlog",
+	 &netdev_max_backlog, sizeof(int), 0644, NULL,
+	 &proc_dointvec},
+#ifdef CONFIG_NET_FASTROUTE
+	{NET_CORE_FASTROUTE, "netdev_fastroute",
+	 &netdev_fastroute, sizeof(int), 0644, NULL,
+	 &proc_dointvec},
+#endif
 	{ 0 }
 };
 #endif
diff -u --recursive --new-file v2.1.78/linux/net/core/utils.c linux/net/core/utils.c
--- v2.1.78/linux/net/core/utils.c	Wed Dec 31 16:00:00 1969
+++ linux/net/core/utils.c	Mon Jan 12 15:28:25 1998
@@ -0,0 +1,59 @@
+/*
+ *	Generic address resultion entity
+ *
+ *	Authors:
+ *	net_random Alan Cox
+ *	net_ratelimit Andy Kleen
+ *
+ *	Created by Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ *
+ *	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 <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+
+static unsigned long net_rand_seed = 152L;
+
+unsigned long net_random(void)
+{
+	net_rand_seed=net_rand_seed*69069L+1;
+        return net_rand_seed^jiffies;
+}
+
+void net_srandom(unsigned long entropy)
+{
+	net_rand_seed ^= entropy;
+	net_random();
+}
+
+
+/* 
+ * This enforces a rate limit: not more than one kernel message
+ * every 5secs to make a denial-of-service attack impossible.
+ *
+ * All warning printk()s should be guarded by this function. 
+ */ 
+int net_ratelimit(void)
+{
+	static unsigned long last_msg; 
+	static int missed;
+	
+	if ((jiffies - last_msg) >= 5*HZ) {
+		if (missed)	
+			printk(KERN_WARNING "ipv4: (%d messages suppressed. Flood?)\n", missed);
+		missed = 0; 
+		last_msg = jiffies;
+		return 1;
+	}
+	missed++; 
+	return 0; 
+}
diff -u --recursive --new-file v2.1.78/linux/net/ethernet/eth.c linux/net/ethernet/eth.c
--- v2.1.78/linux/net/ethernet/eth.c	Mon Dec  1 12:04:16 1997
+++ linux/net/ethernet/eth.c	Mon Jan 12 15:28:25 1998
@@ -55,6 +55,7 @@
 #include <net/arp.h>
 #include <net/sock.h>
 #include <net/ipv6.h>
+#include <net/ip.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/checksum.h>
@@ -120,7 +121,7 @@
 	 *	Anyway, the loopback-device should never use this function... 
 	 */
 
-	if (dev->flags & IFF_LOOPBACK) 
+	if (dev->flags & (IFF_LOOPBACK|IFF_NOARP)) 
 	{
 		memset(eth->h_dest, 0, dev->addr_len);
 		return(dev->hard_header_len);
@@ -140,24 +141,16 @@
  *	Rebuild the Ethernet MAC header. This is called after an ARP
  *	(or in future other address resolution) has completed on this
  *	sk_buff. We now let ARP fill in the other fields.
+ *
+ *	This routine CANNOT use cached dst->neigh!
+ *	Really, it is used only when dst->neigh is wrong.
  */
- 
+
 int eth_rebuild_header(struct sk_buff *skb)
 {
 	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
@@ -170,11 +163,10 @@
 		       dev->name, (int)eth->h_proto);
 		
 		memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
-		return 0;
 		break;
 	}
 
-	return 0;	
+	return 0;
 }
 
 
@@ -204,9 +196,12 @@
 	/*
 	 *	This ALLMULTI check should be redundant by 1.4
 	 *	so don't forget to remove it.
+	 *
+	 *	Seems, you forgot to remove it. All silly devices
+	 *	seems to set IFF_PROMISC.
 	 */
 	 
-	else if(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))
+	else if(dev->flags&(IFF_PROMISC/*|IFF_ALLMULTI*/))
 	{
 		if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
 			skb->pkt_type=PACKET_OTHERHOST;
@@ -239,38 +234,18 @@
 	return ETH_ALEN;
 }
 
-int eth_header_cache(struct dst_entry *dst, struct neighbour *neigh,
-		     struct hh_cache *hh)
+int eth_header_cache(struct neighbour *neigh, struct hh_cache *hh)
 {
 	unsigned short type = hh->hh_type;
-	struct ethhdr *eth = (struct ethhdr*)hh->hh_data;
-	struct device *dev = dst->dev;
+	struct ethhdr *eth = (struct ethhdr*)(((u8*)hh->hh_data) + 2);
+	struct device *dev = neigh->dev;
 
-	if (type == ETH_P_802_3)
+	if (type == __constant_htons(ETH_P_802_3))
 		return -1;
-	
-	eth->h_proto = htons(type);
 
+	eth->h_proto = type;
 	memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
-
-	if (dev->flags & IFF_LOOPBACK) {
-		memset(eth->h_dest, 0, dev->addr_len);
-		hh->hh_uptodate = 1;
-		return 0;
-	}
-
-	if (type != ETH_P_IP) 
-	{
-		printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n",dev->name,(int)eth->h_proto);
-		hh->hh_uptodate = 0;
-		return 0;
-	}
-
-#ifdef CONFIG_INET
-	hh->hh_uptodate = arp_find_1(eth->h_dest, dst, neigh);
-#else
-	hh->hh_uptodate = 0;
-#endif
+	memcpy(eth->h_dest, neigh->ha, dev->addr_len);
 	return 0;
 }
 
@@ -280,13 +255,7 @@
 
 void eth_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr)
 {
-	if (hh->hh_type != ETH_P_IP)
-	{
-		printk(KERN_DEBUG "eth_header_cache_update: %04x cache is not implemented\n", hh->hh_type);
-		return;
-	}
-	memcpy(hh->hh_data, haddr, ETH_ALEN);
-	hh->hh_uptodate = 1;
+	memcpy(((u8*)hh->hh_data) + 2, haddr, dev->addr_len);
 }
 
 #ifndef CONFIG_IP_ROUTER
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- v2.1.78/linux/net/ipv4/Config.in	Fri Jan  2 14:37:03 1998
+++ linux/net/ipv4/Config.in	Mon Jan 12 15:28:25 1998
@@ -57,9 +57,9 @@
     bool 'IP: PIM-SM version 2 support' CONFIG_IP_PIMSM_V2
   fi
 fi
-tristate 'IP: aliasing support' CONFIG_IP_ALIAS
+bool 'IP: aliasing support' CONFIG_IP_ALIAS
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  if [ "$CONFIG_NETLINK" = "y" ]; then
+  if [ "$CONFIG_RTNETLINK" = "y" ]; then
     bool 'IP: ARP daemon support (EXPERIMENTAL)' CONFIG_ARPD
   fi
 fi
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.1.78/linux/net/ipv4/af_inet.c	Tue Jan  6 09:37:38 1998
+++ linux/net/ipv4/af_inet.c	Mon Jan 12 14:46:27 1998
@@ -912,29 +912,6 @@
 		case SIOCGIFPFLAGS:	
 		case SIOCSIFFLAGS:
 			return(devinet_ioctl(cmd,(void *) arg));
-		case SIOCGIFCONF:
-		case SIOCGIFFLAGS:
-		case SIOCADDMULTI:
-		case SIOCDELMULTI:
-		case SIOCGIFMETRIC:
-		case SIOCSIFMETRIC:
-		case SIOCGIFMEM:
-		case SIOCSIFMEM:
-		case SIOCGIFMTU:
-		case SIOCSIFMTU:
-		case SIOCSIFLINK:
-		case SIOCGIFHWADDR:
-		case SIOCSIFHWADDR:
-		case SIOCSIFMAP:
-		case SIOCGIFMAP:
-		case SIOCSIFSLAVE:
-		case SIOCGIFSLAVE:
-		case SIOCGIFINDEX:
- 		case SIOCGIFNAME:
- 		case SIOCGIFCOUNT:
-		case SIOCSIFHWBROADCAST:
-			return(dev_ioctl(cmd,(void *) arg));
-
 		case SIOCGIFBR:
 		case SIOCSIFBR:
 #ifdef CONFIG_BRIDGE		
@@ -971,9 +948,9 @@
 				return(dev_ioctl(cmd,(void *) arg));
 #endif
 
-			if (sk->prot->ioctl==NULL) 
-				return(-EINVAL);
-			return(sk->prot->ioctl(sk, cmd, arg));
+			if (sk->prot->ioctl==NULL || (err=sk->prot->ioctl(sk, cmd, arg))==-ENOIOCTLCMD)
+				return(dev_ioctl(cmd,(void *) arg));		
+			return err;
 	}
 	/*NOTREACHED*/
 	return(0);
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.1.78/linux/net/ipv4/arp.c	Fri Dec 19 15:53:06 1997
+++ linux/net/ipv4/arp.c	Mon Jan 12 15:28:25 1998
@@ -1,6 +1,6 @@
 /* linux/net/inet/arp.c
  *
- * Version:	$Id: arp.c,v 1.57 1997/12/09 16:11:30 jes Exp $
+ * Version:	$Id: arp.c,v 1.58 1997/12/13 21:52:46 kuznet Exp $
  *
  * Copyright (C) 1994 by Florian  La Roche
  *
@@ -62,6 +62,8 @@
  *					one in...
  *		Jes Sorensen	:	Make FDDI work again in 2.1.x and
  *					clean up the APFDDI & gen. FDDI bits.
+ *		Alexey Kuznetsov:	new arp state machine;
+ *					now it is in net/core/neighbour.c.
  */
 
 /* RFC1122 Status:
@@ -95,6 +97,9 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <linux/init.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
 
 #include <net/ip.h>
 #include <net/icmp.h>
@@ -109,1113 +114,294 @@
 #include <net/netrom.h>
 #endif
 #endif
-#ifdef CONFIG_ARPD
-#include <net/netlink.h>
-#endif
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
 /*
- *	Configurable Parameters
- */
-
-/*
- *	After that time, an unused entry is deleted from the arp table.
- *	RFC1122 recommends set it to 60*HZ, if your site uses proxy arp
- *	and dynamic routing.
- */
-
-#define ARP_TIMEOUT		(60*HZ)
-
-int sysctl_arp_timeout = ARP_TIMEOUT;
-
-/*
- *	How often is ARP cache checked for expire.
- *	It is useless to set ARP_CHECK_INTERVAL > ARP_TIMEOUT
- */
-
-#define ARP_CHECK_INTERVAL	(60*HZ)
-
-int sysctl_arp_check_interval = ARP_CHECK_INTERVAL;
-
-/*
- *	Soft limit on ARP cache size.
- */
-
-#if RT_CACHE_DEBUG >= 2
-#define ARP_MAXSIZE	4
-#else
-#ifdef CONFIG_ARPD
-#define ARP_MAXSIZE	64
-#else
-#define ARP_MAXSIZE	256
-#endif /* CONFIG_ARPD */
-#endif
-
-/*
- *	Limit on unresolved ARP cache entries.
- */
-#define ARP_MAX_UNRES (ARP_MAXSIZE/2)
-
-/*
- *	Maximal number of skb's queued for resolution.
- */
-#define ARP_MAX_UNRES_PACKETS 3
-
-/*
- *	If an arp request is send, ARP_RES_TIME is the timeout value until the
- *	next request is send.
- * 	RFC1122: OK.  Throttles ARPing, as per 2.3.2.1. (MUST)
- *	The recommended minimum timeout is 1 second per destination.
- *
- */
-
-#define ARP_RES_TIME		(5*HZ)
-
-int sysctl_arp_res_time = ARP_RES_TIME;
-
-/*
- *	The number of times an broadcast arp request is send, until
- *	the host is considered temporarily unreachable.
- */
-
-#define ARP_MAX_TRIES		3
-
-int sysctl_arp_max_tries = ARP_MAX_TRIES;
-
-/*
- *	The entry is reconfirmed by sending point-to-point ARP
- *	request after ARP_CONFIRM_INTERVAL.
- *	RFC1122 recommends 60*HZ.
- *
- *	Warning: there exist nodes, that answer only broadcast
- *	ARP requests (Cisco-4000 in hot standby mode?)
- *	Now arp code should work with such nodes, but
- *	it still will generate redundant broadcast requests, so that
- *	this interval should be enough long.
- */
-
-#define ARP_CONFIRM_INTERVAL	(300*HZ)
-
-int sysctl_arp_confirm_interval = ARP_CONFIRM_INTERVAL;
-
-/*
- *	We wait for answer to unicast request for ARP_CONFIRM_TIMEOUT.
- */
-
-#define ARP_CONFIRM_TIMEOUT	ARP_RES_TIME
-
-int sysctl_arp_confirm_timeout = ARP_CONFIRM_TIMEOUT;
-
-/*
- *	The number of times an unicast arp request is retried, until
- *	the cache entry is considered suspicious.
- *	Value 0 means that no unicast pings will be sent.
- *	RFC1122 recommends 2.
- */
-
-#define ARP_MAX_PINGS		1
-
-int sysctl_arp_max_pings = ARP_MAX_PINGS;
-
-/*
- *	When a host is dead, but someone tries to connect it,
- *	we do not remove corresponding cache entry (it would
- *	be useless, it will be created again immediately)
- *	Instead we prolongate interval between broadcasts
- *	to ARP_DEAD_RES_TIME.
- *	This interval should be not very long.
- *	(When the host will be up again, we will notice it only
- *	when ARP_DEAD_RES_TIME expires, or when the host will arp us.
- */
-
-#define ARP_DEAD_RES_TIME	(60*HZ)
-
-int sysctl_arp_dead_res_time = ARP_DEAD_RES_TIME;
-
-static void arp_neigh_destroy(struct neighbour *neigh);
-
-/*
  *	Interface to generic neighbour cache.
  */
+static int arp_constructor(struct neighbour *neigh);
+static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb);
+static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb);
+static void parp_redo(struct sk_buff *skb);
 
-struct neigh_ops arp_neigh_ops = {
+static struct neigh_ops arp_generic_ops =
+{
 	AF_INET,
 	NULL,
-	arp_find,
-	arp_neigh_destroy
+	arp_solicit,
+	arp_error_report,
+	neigh_resolve_output,
+	neigh_connected_output,
+	ip_acct_output,
+	ip_acct_output
 };
 
-
-static atomic_t arp_size = ATOMIC_INIT(0);
-static atomic_t arp_unres_size = ATOMIC_INIT(0);
-
-#ifdef CONFIG_ARPD
-static int arpd_not_running;
-static int arpd_stamp;
-struct sock *arpd_sk;
-#endif
-
-static void arp_check_expire (unsigned long);  
-static int  arp_update (u32 sip, char *sha, struct device * dev,
-			unsigned long updated, int grat);
-
-static struct timer_list arp_timer =
-	{ NULL, NULL, ARP_CHECK_INTERVAL, 0L, &arp_check_expire };
-
-/*
- * The default arp netmask is just 255.255.255.255 which means it's
- * a single machine entry. Only proxy entries can have other netmasks
- */
-
-#define DEF_ARP_NETMASK (~0)
-
-/*
- * 	The size of the hash table. Must be a power of two.
- */
-
-#define ARP_TABLE_SIZE		16
-#define FULL_ARP_TABLE_SIZE	(ARP_TABLE_SIZE+1)
-
-struct arp_table *arp_tables[FULL_ARP_TABLE_SIZE] =
+static struct neigh_ops arp_hh_ops =
 {
+	AF_INET,
 	NULL,
+	arp_solicit,
+	arp_error_report,
+	neigh_resolve_output,
+	neigh_resolve_output,
+	ip_acct_output,
+	ip_acct_output
 };
 
-#define arp_proxy_list arp_tables[ARP_TABLE_SIZE]
-
-/*
- *	The last bits in the IP address are used for the cache lookup.
- *	A special entry is used for proxy arp entries
- */
-
-#define HASH(paddr) 		(htonl(paddr) & (ARP_TABLE_SIZE - 1))
-
-/*
- *	Hardware header cache.
- *
- */
-
-/*
- * Signal to device layer, that hardware address may be changed.
- */
-
-static __inline__ void arp_update_hhs(struct arp_table * entry)
-{
-	struct hh_cache *hh;
-	void (*update)(struct hh_cache*, struct device*, unsigned char*) =
-		entry->u.neigh.dev->header_cache_update;
-
-#if RT_CACHE_DEBUG >= 1
-	if (!update && entry->u.neigh.hh)
-	{
-		printk(KERN_DEBUG "arp_update_hhs: no update callback for %s\n", entry->u.neigh.dev->name);
-		return;
-	}
-#endif
-	for (hh=entry->u.neigh.hh; hh; hh=hh->hh_next)
-		update(hh, entry->u.neigh.dev, entry->u.neigh.ha);
-}
-
-/*
- *	Invalidate all hh's, so that higher level will not try to use it.
- */
-
-static __inline__ void arp_invalidate_hhs(struct arp_table * entry)
-{
-	struct hh_cache *hh;
-
-	for (hh=entry->u.neigh.hh; hh; hh=hh->hh_next)
-		hh->hh_uptodate = 0;
-}
-
-/*
- * Purge all linked skb's of the entry.
- */
-
-static void arp_purge_send_q(struct arp_table *entry)
-{
-	struct sk_buff *skb;
-
-	/* Release the list of `skb' pointers. */
-	while ((skb = skb_dequeue(&entry->u.neigh.arp_queue)) != NULL)
-		kfree_skb(skb, FREE_WRITE);
-}
-
-static void arp_free(struct arp_table **entryp)
+static struct neigh_ops arp_direct_ops =
 {
-	struct arp_table *entry = *entryp;
-	*entryp = entry->u.next;
-
-	if (!(entry->flags&ATF_PUBL)) {
-		atomic_dec(&arp_size);
-		if (!(entry->flags&ATF_COM))
-			atomic_dec(&arp_unres_size);
-	}
-	del_timer(&entry->timer);
-	arp_purge_send_q(entry);
-	arp_invalidate_hhs(entry);
-
-	neigh_destroy(&entry->u.neigh);
-}
-
+	AF_INET,
+	NULL,
+	NULL,
+	NULL,
+	ip_acct_output,
+	ip_acct_output,
+	ip_acct_output,
+	ip_acct_output
+};
 
-static void arp_neigh_destroy(struct neighbour *neigh)
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25)
+static struct neigh_ops arp_broken_ops =
 {
-	struct arp_table *entry = (struct arp_table*)neigh;
-	struct hh_cache *hh, *next;
-
-	del_timer(&entry->timer);
-	arp_purge_send_q(entry);
-
-	hh = entry->u.neigh.hh;
-	entry->u.neigh.hh = NULL;
-
-	for ( ; hh; hh = next)
-	{
-		next = hh->hh_next;
-		hh->hh_uptodate = 0;
-		hh->hh_next = NULL;
-		if (atomic_dec_and_test(&hh->hh_refcnt))
-		{
-#if RT_CACHE_DEBUG >= 2
-			extern atomic_t hh_count;
-			atomic_dec(&hh_count);
+	AF_INET,
+	NULL,
+	NULL,
+	NULL,
+	neigh_compat_output,
+	neigh_compat_output,
+	neigh_compat_output,
+	neigh_compat_output,
+};
 #endif
-			kfree_s(hh, sizeof(struct hh_cache));
-		}
-	}
-}
-
-
-#ifdef CONFIG_ARPD
-
-/*
- *	Send ARPD message.
- */
-static void arpd_send(int req, u32 addr, struct device * dev, char *ha,
-		      unsigned long updated)
-{
-	int retval;
-	struct sk_buff *skb;
-	struct arpd_request *arpreq;
-
-	if (arpd_not_running)
-		return;
-
-	skb = alloc_skb(sizeof(struct arpd_request), GFP_ATOMIC);
-	if (skb == NULL)
-		return;
-
-	arpreq=(struct arpd_request *)skb_put(skb, sizeof(struct arpd_request));
-	arpreq->req = req;
-	arpreq->ip  = addr;
-	arpreq->dev = (unsigned long)dev;
-	arpreq->stamp = arpd_stamp;
-	arpreq->updated = updated;
-	if (ha)
-		memcpy(arpreq->ha, ha, sizeof(arpreq->ha));
-
-	retval = netlink_post(NETLINK_ARPD, skb);
-	if (retval)
-	{
-		kfree_skb(skb, FREE_WRITE);
-		if (retval == -EUNATCH)
-			arpd_not_running = 1;
-	}
-}
-
-/*
- *	Send ARPD update message.
- */
-
-static __inline__ void arpd_update(u32 ip, struct device *dev, char *ha)
-{
-	arpd_send(ARPD_UPDATE, ip, dev, ha, jiffies);
-}
-
-
-/*
- *	Send ARPD lookup request.
- */
-
-static __inline__ void arpd_lookup(u32 addr, struct device * dev)
-{
-	arpd_send(ARPD_LOOKUP, addr, dev, NULL, 0);
-}
-
-/*
- *	Send ARPD flush message.
- */
-
-static __inline__ void arpd_flush(struct device * dev)
-{
-	arpd_send(ARPD_FLUSH, 0, dev, NULL, 0);
-}
-
-
-static int arpd_callback(struct sk_buff *skb, struct sock *sk)
-{
-	struct device * dev;
-	struct arpd_request *retreq;
-
-	arpd_not_running = 0;
-
-	if (skb->len != sizeof(struct arpd_request))
-	{
-		kfree_skb(skb, FREE_READ);
-		return -EINVAL;
-	}
-
-	retreq = (struct arpd_request *)skb->data;
-	dev = (struct device*)retreq->dev;
-
-	if (retreq->stamp != arpd_stamp || !dev)
-	{
-		kfree_skb(skb, FREE_READ);
-		return -EINVAL;
-	}
-
-	if (!retreq->updated)
-	{
-/*
- *	Invalid mapping: drop it and send ARP broadcast.
- */
-		arp_send(ARPOP_REQUEST, ETH_P_ARP, retreq->ip, dev, 
-			 inet_select_addr(dev, retreq->ip, RT_SCOPE_LINK), 
-			 NULL, 
-			 dev->dev_addr, NULL);
-	}
-	else
-	{
-		start_bh_atomic();
-		arp_update(retreq->ip, retreq->ha, dev, retreq->updated, 0);
-		end_bh_atomic();
-	}
-
-	kfree_skb(skb, FREE_READ);
-	return sizeof(struct arpd_request);
-}
-
-#else
 
-static __inline__ void arpd_update(u32 ip, struct device *dev, char *ha)
+struct neigh_table arp_tbl =
 {
-	return;
-}
-
-#endif /* CONFIG_ARPD */
-
-
-
-
-/*
- *	ARP expiration routines.
- */
-
-/*
- *	Force the expiry of an entry in the internal cache so the memory
- *	can be used for a new request.
- */
+	NULL,
+	AF_INET,
+	sizeof(struct neighbour) + 4,
+	4,
+	arp_constructor,
+	NULL,
+	NULL,
+	parp_redo,
+        { NULL, NULL, 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 1*HZ, 64 },
+	30*HZ, 128, 512, 1024,
+};
 
-static int arp_force_expire(void)
+int arp_mc_map(u32 addr, u8 *haddr, struct device *dev, int dir)
 {
-	int i;
-	struct arp_table *entry, **pentry;
-	struct arp_table **oldest_entry = NULL;
-	unsigned long oldest_used = ~0;
-	unsigned long now = jiffies;
-	int result = 0;
-
-	static int last_index;
-
-	if (last_index >= ARP_TABLE_SIZE)
-		last_index = 0;
-
-	for (i = 0; i < ARP_TABLE_SIZE; i++, last_index++)
-	{
-		pentry = &arp_tables[last_index & (ARP_TABLE_SIZE-1)];
-
-		while ((entry = *pentry) != NULL)
-		{
-			if (!(entry->flags & ATF_PERM))
-			{
-				if (!atomic_read(&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);
-#endif
-					arp_free(pentry);
-					result++;
-					if (atomic_read(&arp_size) < ARP_MAXSIZE)
-						goto done;
-					continue;
-				}
-				if (!atomic_read(&entry->u.neigh.refcnt) &&
-				    entry->u.neigh.lastused < oldest_used)
-				{
-					oldest_entry = pentry;
-					oldest_used = entry->u.neigh.lastused;
-				}
-			}
-			pentry = &entry->u.next;
+	switch (dev->type) {
+	case ARPHRD_ETHER:
+	case ARPHRD_IEEE802:
+	case ARPHRD_FDDI:
+		ip_eth_mc_map(addr, haddr);
+		return 0;
+	default:
+		if (dir) {
+			memcpy(haddr, dev->broadcast, dev->addr_len);
+			return 0;
 		}
 	}
-
-done:
-	if (result || !oldest_entry)
-		return result;
-
-#if RT_CACHE_DEBUG >= 2
-	printk("arp_force_expire: expiring %08x\n", (*oldest_entry)->ip);
-#endif
-	arp_free(oldest_entry);
-	return 1;
+	return -EINVAL;
 }
 
-static void arp_unres_expire(void)
-{
-	int i;
-	struct arp_table *entry, **pentry;
-	unsigned long now = jiffies;
-
-	for (i = 0; i < ARP_TABLE_SIZE; i++) {
-		pentry = &arp_tables[i & (ARP_TABLE_SIZE-1)];
-
-		while ((entry = *pentry) != NULL) {
-			if (!(entry->flags & (ATF_PERM|ATF_COM)) &&
-			    (entry->retries < sysctl_arp_max_tries ||
-			     entry->timer.expires - now <
-			     sysctl_arp_res_time - sysctl_arp_res_time/32)) {
-				if (!atomic_read(&entry->u.neigh.refcnt)) {
-#if RT_CACHE_DEBUG >= 2
-					printk("arp_unres_expire: %08x discarded\n", entry->ip);
-#endif
-					arp_free(pentry);
-					continue;
-				}
-				arp_purge_send_q(entry);
-			}
-			pentry = &entry->u.next;
-		}
-	}
-}
 
 
-/*
- *	Check if there are entries that are too old and remove them. If the
- *	ATF_PERM flag is set, they are always left in the arp cache (permanent
- *      entries). If an entry was not confirmed for ARP_CONFIRM_INTERVAL,
- *	send point-to-point ARP request.
- *	If it will not be confirmed for ARP_CONFIRM_TIMEOUT,
- *	give it to shred by arp_expire_entry.
- */
-
-static void arp_check_expire(unsigned long dummy)
+static int arp_constructor(struct neighbour *neigh)
 {
-	int i;
-	unsigned long now = jiffies;
-
-	del_timer(&arp_timer);
-
-#ifdef CONFIG_ARPD
-	arpd_not_running = 0;
-#endif
+	u32 addr = *(u32*)neigh->primary_key;
+	struct device *dev = neigh->dev;
+	struct in_device *in_dev = dev->ip_ptr;
 
-	ip_rt_check_expire();
-
-	for (i = 0; i < ARP_TABLE_SIZE; i++)
-	{
-		struct arp_table *entry, **pentry;
-		
-		pentry = &arp_tables[i];
-
-		while ((entry = *pentry) != NULL)
-		{
-			if (entry->flags & ATF_PERM)
-			{
-				pentry = &entry->u.next;
-				continue;
-			}
-
-			if (!atomic_read(&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);
-#endif
-				arp_free(pentry);
-				continue;
-			}
-			if (entry->last_updated &&
-			    now - entry->last_updated > sysctl_arp_confirm_interval)
-			{
-				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, inet_select_addr(dev, entry->ip, RT_SCOPE_LINK),
-					 entry->u.neigh.ha, dev->dev_addr, NULL);
-#if RT_CACHE_DEBUG >= 2
-				printk("arp_expire: %08x requires confirmation\n", entry->ip);
-#endif
-			}
-			pentry = &entry->u.next;	/* go to next entry */
-		}
-	}
-
-	/*
-	 *	Set the timer again.
-	 */
-
-	arp_timer.expires = jiffies + sysctl_arp_check_interval;
-	add_timer(&arp_timer);
-}
-
-/*
- *	This function is called, if an entry is not resolved in ARP_RES_TIME.
- *	When more than MAX_ARP_TRIES retries was done, release queued skb's,
- *	but not discard entry itself if  it is in use.
- */
-
-static void arp_expire_request (unsigned long arg)
-{
-	struct arp_table *entry = (struct arp_table *) arg;
-	struct arp_table **pentry;
-	unsigned long hash;
-
-	del_timer(&entry->timer);
-
-	/*	If entry is COMPLETE but old,
-	 *	it means that point-to-point ARP ping has been failed
-	 *	(It really occurs with Cisco 4000 routers)
-	 *	We should reconfirm it.
-	 */
-	
-	if ((entry->flags & ATF_COM) && entry->last_updated
-	    && jiffies - entry->last_updated <= sysctl_arp_confirm_interval)
-		return;
-
-	if (entry->last_updated && --entry->retries > 0)
-	{
-		struct device *dev = entry->u.neigh.dev;
-
-#if RT_CACHE_DEBUG >= 2
-		printk("arp_expire_request: %08x timed out\n", entry->ip);
-#endif
-		/* Set new timer. */
-		entry->timer.expires = jiffies + sysctl_arp_res_time;
-		add_timer(&entry->timer);
-		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev,
-			 inet_select_addr(dev, entry->ip, RT_SCOPE_LINK),
-			 entry->retries > sysctl_arp_max_tries ? entry->u.neigh.ha : NULL,
-			 dev->dev_addr, NULL);
-		return;
-	}
-
-	/*
-	 *	The host is really dead.
-	 */
+	if (in_dev == NULL)
+		return -EINVAL;
 
-	arp_purge_send_q(entry);
+	neigh->type = inet_addr_type(addr);
+	if (in_dev->arp_parms)
+		neigh->parms = in_dev->arp_parms;
+
+	if (dev->hard_header == NULL) {
+		neigh->nud_state = NUD_NOARP;
+		neigh->ops = &arp_direct_ops;
+		neigh->output = neigh->ops->queue_xmit;
+	} else {
+		/* Good devices (checked by reading texts, but only ethernet is
+		   tested)
 
-	if (atomic_read(&entry->u.neigh.refcnt))
-	{
-		/*
-		 *	The host is dead, but someone refers to it.
-		 *	It is useless to drop this entry just now,
-		 *	it will be born again, so that
-		 *	we keep it, but slow down retransmitting
-		 *	to ARP_DEAD_RES_TIME.
+		   ARPHRD_ETHER: (ethernet, apfddi)
+		   ARPHRD_FDDI: (fddi)
+		   ARPHRD_IEEE802: (tr)
+		   ARPHRD_METRICOM: (strip)
+		   ARPHRD_ARCNET:
+		   etc. etc. etc.
+
+		   ARPHRD_IPDDP will also work, if author repaires it.
+		   I did not it, because this driver does not work even
+		   in old paradigm.
 		 */
 
-		struct device *dev = entry->u.neigh.dev;
-#if RT_CACHE_DEBUG >= 2
-		printk("arp_expire_request: %08x is dead\n", entry->ip);
-#endif
-		entry->retries = sysctl_arp_max_tries;
-		if (entry->flags&ATF_COM)
-			atomic_inc(&arp_unres_size);
-		entry->flags &= ~ATF_COM;
-		arp_invalidate_hhs(entry);
-
-		/*
-		 *	Declare the entry dead.
+#if 1
+		/* So... these "amateur" devices are hopeless.
+		   The only thing, that I can say now:
+		   It is very sad that we need to keep ugly obsolete
+		   code to make them happy.
+
+		   They should be moved to more reasonable state, now
+		   they use rebuild_header INSTEAD OF hard_start_xmit!!!
+		   Besides that, they are sort of out of date
+		   (a lot of redundant clones/copies, useless in 2.1),
+		   I wonder why people believe that they work.
 		 */
-		entry->last_updated = 0;
-
-		entry->timer.expires = jiffies + sysctl_arp_dead_res_time;
-		add_timer(&entry->timer);
-		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev,
-			 inet_select_addr(dev, entry->ip, RT_SCOPE_LINK), 
-			 NULL, dev->dev_addr, NULL);
-		return;
-	}
-
-	entry->last_updated = 0;
-
-	hash = HASH(entry->ip);
-
-	pentry = &arp_tables[hash];
-
-	while (*pentry != NULL)
-	{
-		if (*pentry != entry)
-		{
-			pentry = &(*pentry)->u.next;
-			continue;
-		}
-#if RT_CACHE_DEBUG >= 2
-		printk("arp_expire_request: %08x is killed\n", entry->ip);
+		switch (dev->type) {
+		default:
+			break;
+		case ARPHRD_ROSE:	
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25)
+		case ARPHRD_AX25:
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+		case ARPHRD_NETROM:
 #endif
-		arp_free(pentry);
-	}
-}
-
-
-/* 
- * Allocate memory for a new entry.  If we are at the maximum limit
- * of the internal ARP cache, arp_force_expire() an entry.
- */
-
-static struct arp_table * arp_alloc(int how)
-{
-	struct arp_table * entry;
-
-	if (how && atomic_read(&arp_size) >= ARP_MAXSIZE)
-		arp_force_expire();
-	if (how > 1 && atomic_read(&arp_unres_size) >= ARP_MAX_UNRES) {
-		arp_unres_expire();
-		if (atomic_read(&arp_unres_size) >= ARP_MAX_UNRES) {
-			printk(KERN_DEBUG "arp_unres_size=%d\n",
-			       atomic_read(&arp_unres_size));
-			return NULL;
-		}
-	}
-
-	entry = (struct arp_table *)neigh_alloc(sizeof(struct arp_table),
-						&arp_neigh_ops);
-	if (entry != NULL) {
-		atomic_set(&entry->u.neigh.refcnt, 1);
-
-		if (how)
-			atomic_inc(&arp_size);
-
-                entry->mask = DEF_ARP_NETMASK;
-		init_timer(&entry->timer);
-		entry->timer.function = arp_expire_request;
-		entry->timer.data = (unsigned long)entry;
-		entry->last_updated = jiffies;
-	}
-	return entry;
-}
-
-
-
-/*
- *	Purge a device from the ARP queue
- */
- 
-int arp_device_event(struct notifier_block *this, unsigned long event, void *ptr)
-{
-	struct device *dev=ptr;
-	int i;
-	
-	if (event != NETDEV_DOWN)
-		return NOTIFY_DONE;
-
-#ifdef  CONFIG_ARPD
-	arpd_flush(dev);
-	arpd_stamp++;
+			neigh->ops = &arp_broken_ops;
+			neigh->output = neigh->ops->output;
+			return 0;
 #endif
-
-	for (i = 0; i < FULL_ARP_TABLE_SIZE; i++)
-	{
-		struct arp_table *entry;
-		struct arp_table **pentry = &arp_tables[i];
-
-		start_bh_atomic();
-
-		while ((entry = *pentry) != NULL)
-		{
-			if (entry->u.neigh.dev != dev)
-			{
-				pentry = &entry->u.next;
-				continue;
-			}
-			arp_free(pentry);
 		}
-
-		end_bh_atomic();
-	}
-	return NOTIFY_DONE;
-}
-
-
-
-/*
- *	This will try to retransmit everything on the queue.
- */
-
-static void arp_send_q(struct arp_table *entry)
-{
-	struct sk_buff *skb;
-
-	while((skb = skb_dequeue(&entry->u.neigh.arp_queue)) != NULL)
-		dev_queue_xmit(skb);
-}
-
-
-static int
-arp_update (u32 sip, char *sha, struct device * dev,
-	    unsigned long updated, int grat)
-{
-	struct arp_table * entry;
-	unsigned long hash;
-
-	if (updated == 0)
-	{
-		updated = jiffies;
-		arpd_update(sip, dev, sha);
-	}
-
-	hash = HASH(sip);
-
-	for (entry=arp_tables[hash]; entry; entry = entry->u.next)
-		if (entry->ip == sip && entry->u.neigh.dev == dev)
-			break;
-
-	if (entry)
-	{
-/*
- *	Entry found; update it only if it is not a permanent entry.
- */
-		if (!(entry->flags & ATF_PERM)) 
-		{
-			del_timer(&entry->timer);
-			entry->last_updated = updated;
-			if (memcmp(entry->u.neigh.ha, sha, dev->addr_len) != 0)
-			{
-				memcpy(entry->u.neigh.ha, sha, dev->addr_len);
-				if (entry->flags & ATF_COM)
-					arp_update_hhs(entry);
-			}
-		}
-
-		if (!(entry->flags & ATF_COM))
-		{
-/*
- *	Switch to complete status.
- */
-			entry->flags |= ATF_COM;
-			atomic_dec(&arp_unres_size);
-			arp_update_hhs(entry);
-/* 
- *	Send out waiting packets.
- */
-			arp_send_q(entry);
+#endif
+		if (neigh->type == RTN_MULTICAST) {
+			neigh->nud_state = NUD_NOARP;
+			arp_mc_map(addr, neigh->ha, dev, 1);
+		} else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
+			neigh->nud_state = NUD_NOARP;
+			memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
+		} else if (neigh->type == RTN_BROADCAST || dev->flags&IFF_POINTOPOINT) {
+			neigh->nud_state = NUD_NOARP;
+			memcpy(neigh->ha, dev->broadcast, dev->addr_len);
 		}
-		return 1;
+		if (dev->hard_header_cache)
+			neigh->ops = &arp_hh_ops;
+		else
+			neigh->ops = &arp_generic_ops;
+		if (neigh->nud_state&NUD_VALID)
+			neigh->output = neigh->ops->connected_output;
+		else
+			neigh->output = neigh->ops->output;
 	}
 
-/*
- * 	No entry found.  Need to add a new entry to the arp table.
- */
-	if (grat)
-		return 0;
-
-	entry = arp_alloc(1);
-	if (!entry)
-		return 0;
-
-	entry->ip = sip;
-	entry->flags = ATF_COM;
-	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;
-	neigh_release(&entry->u.neigh);
-	return 0;
-}
-
-
-
-static __inline__ struct arp_table *arp_lookup(u32 paddr, struct device * dev)
-{
-	struct arp_table *entry;
-
-	for (entry = arp_tables[HASH(paddr)]; entry != NULL; entry = entry->u.next)
-		if (entry->ip == paddr && entry->u.neigh.dev == dev)
-			break;
-	return entry;
-}
-
-static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, struct device * dev)
-{
-	switch (addr_hint)
-	{
-		case RTN_LOCAL:
-			printk(KERN_DEBUG "ARP: arp called for own IP address\n");
-			memcpy(haddr, dev->dev_addr, dev->addr_len);
-			return 1;
-		case RTN_MULTICAST:
-			if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802 
-			   || dev->type==ARPHRD_FDDI)
-			{
-				u32 taddr;
-				haddr[0]=0x01;
-				haddr[1]=0x00;
-				haddr[2]=0x5e;
-				taddr=ntohl(paddr);
-				haddr[5]=taddr&0xff;
-				taddr=taddr>>8;
-				haddr[4]=taddr&0xff;
-				taddr=taddr>>8;
-				haddr[3]=taddr&0x7f;
-				return 1;
-			}
-		/*
-		 *	If a device does not support multicast broadcast the stuff (eg AX.25 for now)
-		 */
-		
-		case RTN_BROADCAST:
-			memcpy(haddr, dev->broadcast, dev->addr_len);
-			return 1;
-	}
 	return 0;
 }
 
+static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb)
+{
+	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+	kfree_skb(skb, FREE_WRITE);
+}
 
-static void arp_start_resolution(struct arp_table *entry)
+static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
 {
-	struct device * dev = entry->u.neigh.dev;
+	u32 saddr;
+	u8  *dst_ha = NULL;
+	struct device *dev = neigh->dev;
+	u32 target = *(u32*)neigh->primary_key;
+	int probes = neigh->probes;
 
-	del_timer(&entry->timer);
-	entry->timer.expires = jiffies + sysctl_arp_res_time;
-	entry->retries = sysctl_arp_max_tries;
-	add_timer(&entry->timer);
-#ifdef CONFIG_ARPD
-	if (!arpd_not_running)
-		arpd_lookup(entry->ip, dev);
+	if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL)
+		saddr = skb->nh.iph->saddr;
 	else
+		saddr = inet_select_addr(dev, target, RT_SCOPE_LINK);
+
+	if ((probes -= neigh->parms->ucast_probes) < 0) {
+		if (!(neigh->nud_state&NUD_VALID))
+			printk(KERN_DEBUG "trying to ucast probe in NUD_INVALID\n");
+		dst_ha = neigh->ha;
+	} else if ((probes -= neigh->parms->app_probes) < 0) {
+#ifdef CONFIG_ARPD
+		neigh_app_ns(neigh);
 #endif
-		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev,
-			 inet_select_addr(dev, entry->ip, RT_SCOPE_LINK), NULL,
-			 dev->dev_addr, NULL);
+		return;
+	}
+
+	arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,
+		 dst_ha, dev->dev_addr, NULL);
 }
 
+/* OBSOLETE FUNCTIONS */
+
 /*
- *	Create a new unresolved entry.
+ *	Find an arp mapping in the cache. If not found, post a request.
  *
- *	NOTE: Always make sure no possibility of sleeping is introduced here,
- *            since nearly all callers are inside of BH atomic.  Don't let
- *            the arp_alloc() fool you, at neigh_alloc() it is using GFP_ATOMIC
- *            always.
+ *	It is very UGLY routine: it DOES NOT use skb->dst->neighbour,
+ *	even if it exists. It is supposed that skb->dev was mangled
+ *	by a virtual device (eql, shaper). Nobody but broken devices
+ *	is allowed to use this function, it is scheduled to be removed. --ANK
  */
 
-struct arp_table * arp_new_entry(u32 paddr, struct device *dev, struct sk_buff *skb)
+static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, struct device * dev)
 {
-	struct arp_table *entry;
-	unsigned long hash = HASH(paddr);
-
-	entry = arp_alloc(2);
-
-	if (entry != NULL)
-	{
-		entry->ip = paddr;
-		entry->u.neigh.dev = dev;
-		entry->hatype = dev->type;
-
-		if (skb != NULL)
-			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);
-		neigh_release(&entry->u.neigh);
+	switch (addr_hint) {
+	case RTN_LOCAL:
+		printk(KERN_DEBUG "ARP: arp called for own IP address\n");
+		memcpy(haddr, dev->dev_addr, dev->addr_len);
+		return 1;
+	case RTN_MULTICAST:
+		arp_mc_map(paddr, haddr, dev, 1);
+		return 1;
+	case RTN_BROADCAST:
+		memcpy(haddr, dev->broadcast, dev->addr_len);
+		return 1;
 	}
-	return entry;
+	return 0;
 }
 
 
-/*
- *	Find an arp mapping in the cache. If not found, post a request.
- */
-
 int arp_find(unsigned char *haddr, struct sk_buff *skb)
 {
 	struct device *dev = skb->dev;
 	u32 paddr;
-	struct arp_table *entry;
+	struct neighbour *n;
 
 	if (!skb->dst) {
-		printk(KERN_DEBUG "arp_find called with dst==NULL\n");
+		printk(KERN_DEBUG "arp_find is called with dst==NULL\n");
+		kfree_skb(skb, FREE_WRITE);
 		return 1;
 	}
 
 	paddr = ((struct rtable*)skb->dst)->rt_gateway;
 
-	if (arp_set_predefined(inet_addr_type(paddr), haddr, paddr, dev)) {
-		skb->arp = 1;
+	if (arp_set_predefined(inet_addr_type(paddr), haddr, paddr, dev))
 		return 0;
-	}
 
 	start_bh_atomic();
+	n = __neigh_lookup(&arp_tbl, &paddr, dev, 1);
 
-	/*
-	 *	Find an entry
-	 */
-	entry = arp_lookup(paddr, dev);
-
-	if (entry != NULL) 	/* It exists */
-	{
-		if (entry->flags & ATF_COM)
-		{
-			entry->u.neigh.lastused = jiffies;
-			memcpy(haddr, entry->u.neigh.ha, dev->addr_len);
-			skb->arp = 1;
+	if (n) {
+		n->used = jiffies;
+		if (n->nud_state&NUD_VALID || neigh_event_send(n, skb) == 0) {
+			memcpy(haddr, n->ha, dev->addr_len);
+			neigh_release(n);
 			end_bh_atomic();
 			return 0;
 		}
-
-		/*
-		 *	A request was already sent, but no reply yet. Thus
-		 *	queue the packet with the previous attempt
-		 */
-			
-		if (entry->last_updated) {
-			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);
-		} else {
-			/* If last_updated==0 host is dead, so
-			 * drop skb's and set socket error.
-			 */
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
-			kfree_skb(skb, FREE_WRITE);
-		}
-		end_bh_atomic();
-		return 1;
-	}
-
-	entry = arp_new_entry(paddr, dev, skb);
-
-	if (entry == NULL)
+	} else
 		kfree_skb(skb, FREE_WRITE);
-
+	neigh_release(n);
 	end_bh_atomic();
 	return 1;
 }
 
-int arp_find_1(unsigned char *haddr, struct dst_entry *dst,
-	       struct neighbour *neigh)
-{
-	struct rtable *rt = (struct rtable*)dst;
-	struct device *dev = dst->dev;
-	u32 paddr = rt->rt_gateway;
-	struct arp_table *entry;
-
-	if (!neigh)
-	{
-		if (rt->rt_type == RTN_MULTICAST &&
- 		    (dev->type == ARPHRD_ETHER ||
-		     dev->type == ARPHRD_IEEE802 ||
-		     dev->type == ARPHRD_FDDI))
-		{
-			u32 taddr;
-			haddr[0]=0x01;
-			haddr[1]=0x00;
-			haddr[2]=0x5e;
-			taddr=ntohl(paddr);
-			haddr[5]=taddr&0xff;
-			taddr=taddr>>8;
-			haddr[4]=taddr&0xff;
-			taddr=taddr>>8;
-			haddr[3]=taddr&0x7f;
-			return 1;
-		}
-		if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
-		{
-			memcpy(haddr, dev->broadcast, dev->addr_len);
-			return 1;
-		}
-		if (rt->rt_flags & RTCF_LOCAL)
-		{
-			printk(KERN_DEBUG "ARP: arp called for own IP address\n");
-			memcpy(haddr, dev->dev_addr, dev->addr_len);
-			return 1;
-		}
-		return 0;
-	}
-
-	start_bh_atomic();
-
-	entry = (struct arp_table*)neigh;
+/* END OF OBSOLETE FUNCTIONS */
 
-	if (entry->flags & ATF_COM)
-	{
-		entry->u.neigh.lastused = jiffies;
-		memcpy(haddr, entry->u.neigh.ha, dev->addr_len);
-		end_bh_atomic();
-		return 1;
-	}
-
-	end_bh_atomic();
-	return 0;
-}
 
-
-struct neighbour* arp_find_neighbour(struct dst_entry *dst, int resolve)
+int arp_bind_neighbour(struct dst_entry *dst)
 {
-	struct rtable *rt = (struct rtable*)dst;
-	struct device *dev = rt->u.dst.dev;
-	u32 paddr = rt->rt_gateway;
-	struct arp_table *entry;
-
-	if (dst->ops->family != AF_INET)
-		return NULL;
-
-	if ((dev->flags & (IFF_LOOPBACK|IFF_NOARP)) ||
-	    (rt->rt_flags & (RTCF_LOCAL|RTCF_BROADCAST|RTCF_MULTICAST)))
-		return NULL;
-
-	start_bh_atomic();
-
-	/*
-	 *	Find an entry
-	 */
-	entry = arp_lookup(paddr, dev);
-
-	if (entry != NULL) 	/* It exists */
-	{
-		atomic_inc(&entry->u.neigh.refcnt);
-		end_bh_atomic();
-		entry->u.neigh.lastused = jiffies;
-		return (struct neighbour*)entry;
-	}
-
-	if (!resolve) {
-		end_bh_atomic();
-		return NULL;
-	}
-
-	entry = arp_new_entry(paddr, dev, NULL);
-
-	if (entry)
-		atomic_inc(&entry->u.neigh.refcnt);
-
-	end_bh_atomic();
+	struct device *dev = dst->dev;
 
-	return (struct neighbour*)entry;
+	if (dev == NULL)
+		return 0;
+	if (dst->neighbour == NULL)
+		dst->neighbour = __neigh_lookup(&arp_tbl, &((struct rtable*)dst)->rt_gateway, dev, 1);
+	return (dst->neighbour != NULL);
 }
 
 /*
@@ -1250,22 +436,22 @@
 	skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
 				+ dev->hard_header_len + 15, GFP_ATOMIC);
 	if (skb == NULL)
-	{
-		printk(KERN_DEBUG "ARP: no memory to send an arp packet\n");
 		return;
-	}
 
 	skb_reserve(skb, (dev->hard_header_len+15)&~15);
 	skb->nh.raw = skb->data;
 	arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
-	skb->arp = 1;
 	skb->dev = dev;
 	skb->protocol = __constant_htons (ETH_P_ARP);
+	if (src_hw == NULL)
+		src_hw = dev->dev_addr;
+	if (dest_hw == NULL)
+		dest_hw = dev->broadcast;
 
 	/*
 	 *	Fill the device header for the ARP frame
 	 */
-	dev->hard_header(skb,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len);
+	dev->hard_header(skb,dev,ptype,dest_hw,src_hw,skb->len);
 
 	/*
 	 * Fill out the arp protocol part.
@@ -1273,24 +459,38 @@
 	 * The arp hardware type should match the device type, except for FDDI,
 	 * which (according to RFC 1390) should always equal 1 (Ethernet).
 	 */
-#ifdef CONFIG_FDDI
-	arp->ar_hrd = (dev->type == ARPHRD_FDDI) ? htons(ARPHRD_ETHER) : htons(dev->type);
-#else
-	arp->ar_hrd = htons(dev->type);
-#endif
 	/*
 	 *	Exceptions everywhere. AX.25 uses the AX.25 PID value not the
 	 *	DIX code for the protocol. Make these device structure fields.
 	 */
+	switch (dev->type) {
+	default:
+		arp->ar_hrd = htons(dev->type);
+		arp->ar_pro = __constant_htons(ETH_P_IP);
+		break;
+
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+	case ARPHRD_AX25:
+		arp->ar_hrd = __constant_htons(ARPHRD_AX25);
+		arp->ar_pro = __constant_htons(AX25_P_IP);
+		break;
+
 #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
-	arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP);
-#else
-	arp->ar_pro = (dev->type != ARPHRD_AX25) ? htons(ETH_P_IP) : htons(AX25_P_IP);
+	case ARPHRD_NETROM:
+		arp->ar_hrd = __constant_htons(ARPHRD_NETROM);
+		arp->ar_pro = __constant_htons(AX25_P_IP);
+		break;
 #endif
-#else
-	arp->ar_pro = __constant_htons(ETH_P_IP);
 #endif
+
+#ifdef CONFIG_FDDI
+	case ARPHRD_FDDI:
+		arp->ar_hrd = __constant_htons(ARPHRD_ETHER);
+		arp->ar_pro = __constant_htons(ETH_P_IP);
+		break;
+#endif
+	}
+
 	arp->ar_hln = dev->addr_len;
 	arp->ar_pln = 4;
 	arp->ar_op = htons(type);
@@ -1308,24 +508,13 @@
 	arp_ptr+=dev->addr_len;
 	memcpy(arp_ptr, &dest_ip, 4);
 	skb->dev = dev;
-	skb->priority = 0;
 
 	dev_queue_xmit(skb);
 }
 
-static __inline__ int arp_check_published(u32 tip, struct device *dev)
+static void parp_redo(struct sk_buff *skb)
 {
-	struct arp_table *entry;
-
-	for (entry = arp_proxy_list; entry; entry = entry->u.next) {
-		if (!((entry->ip^tip)&entry->mask) &&
-		    ((!entry->u.neigh.dev &&
-		      (!(entry->flags & ATF_COM) || entry->hatype == dev->type))
-		     || entry->u.neigh.dev == dev) )
-			break;
-	}
-
-	return entry && !(entry->flags & ATF_DONTPUB);
+	arp_rcv(skb, skb->dev, NULL);
 }
 
 /*
@@ -1340,6 +529,9 @@
 	unsigned char *sha, *tha;
 	u32 sip, tip;
 	u16 dev_type = dev->type;
+	int addr_type;
+	struct in_device *in_dev = dev->ip_ptr;
+	struct neighbour *n;
 
 /*
  *	The hardware length of the packet should match the hardware length
@@ -1348,74 +540,50 @@
  *	is not from an IP number.  We can't currently handle this, so toss
  *	it. 
  */  
-#if defined(CONFIG_FDDI)
-	if (dev_type == ARPHRD_FDDI)
-	{
+	if (arp->ar_hln != dev->addr_len    || 
+	    dev->flags & IFF_NOARP          ||
+	    skb->pkt_type == PACKET_OTHERHOST ||
+	    skb->pkt_type == PACKET_LOOPBACK ||
+	    arp->ar_pln != 4)
+		goto out;
+
+	switch (dev_type) {
+	default:	
+		if (arp->ar_pro != __constant_htons(ETH_P_IP))
+			goto out;
+		if (htons(dev_type) != arp->ar_hrd)
+			goto out;
+		break;
+#ifdef CONFIG_FDDI
+	case ARPHRD_FDDI:
 		/*
 		 * According to RFC 1390, FDDI devices should accept ARP hardware types
 		 * of 1 (Ethernet).  However, to be more robust, we'll accept hardware
 		 * types of either 1 (Ethernet) or 6 (IEEE 802.2).
 		 */
-
-		if (arp->ar_hln != dev->addr_len    || 
-		    ((ntohs(arp->ar_hrd) != ARPHRD_ETHER) && (ntohs(arp->ar_hrd) != ARPHRD_IEEE802)) ||
-		    dev->flags & IFF_NOARP          ||
-		    skb->pkt_type == PACKET_OTHERHOST ||
-		    arp->ar_pln != 4)
+		if (arp->ar_hrd != __constant_htons(ARPHRD_ETHER) &&
+		    arp->ar_hrd != __constant_htons(ARPHRD_IEEE802))
 			goto out;
-	}
-	else
-	{
-		if (arp->ar_hln != dev->addr_len    || 
-		    dev_type != ntohs(arp->ar_hrd) ||
-		    dev->flags & IFF_NOARP          ||
-		    skb->pkt_type == PACKET_OTHERHOST ||
-		    arp->ar_pln != 4)
+		if (arp->ar_pro != __constant_htons(ETH_P_IP))
 			goto out;
-	}
-#else
-	if (arp->ar_hln != dev->addr_len    || 
-	    dev_type != ntohs(arp->ar_hrd) || 
-		dev->flags & IFF_NOARP          ||
-	        skb->pkt_type == PACKET_OTHERHOST ||
-		arp->ar_pln != 4)
-		goto out;
+		break;
 #endif
-
-/*
- *	Another test.
- *	The logic here is that the protocol being looked up by arp should 
- *	match the protocol the device speaks.  If it doesn't, there is a
- *	problem, so toss the packet.
- */
-
-  	switch (dev_type)
-  	{
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
-		case ARPHRD_AX25:
-			if(arp->ar_pro != htons(AX25_P_IP))
-				goto out;
-			break;
-#endif
+	case ARPHRD_AX25:
+		if (arp->ar_pro != __constant_htons(AX25_P_IP))
+			goto out;
+		if (arp->ar_hrd != __constant_htons(ARPHRD_AX25))
+			goto out;
+		break;
 #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
-		case ARPHRD_NETROM:
-			if(arp->ar_pro != htons(AX25_P_IP))
-				goto out;
-			break;
-#endif
-		case ARPHRD_ETHER:
-		case ARPHRD_ARCNET:
-		case ARPHRD_METRICOM:
-		case ARPHRD_IEEE802:
-		case ARPHRD_FDDI:
-		case ARPHRD_IPGRE:
-			if(arp->ar_pro != htons(ETH_P_IP))
-				goto out;
-			break;
-
-		default:
-			printk(KERN_ERR "ARP: dev->type mangled!\n");
+	case ARPHRD_NETROM:
+		if (arp->ar_pro != __constant_htons(AX25_P_IP))
+			goto out;
+		if (arp->ar_hrd != __constant_htons(ARPHRD_NETROM))
 			goto out;
+		break;
+#endif
+#endif
 	}
 
 /*
@@ -1451,30 +619,57 @@
  *  and in the case of requests for us we add the requester to the arp 
  *  cache.
  */
-	if (arp->ar_op == htons(ARPOP_REQUEST)) {
-		int addr_type;
-		struct in_device *in_dev = dev->ip_ptr;
-
+	switch (arp->ar_op) {
+	case __constant_htons(ARPOP_REQUEST):
 		if (ip_route_input(skb, tip, sip, 0, dev))
 			goto out;
 		rt = (struct rtable*)skb->dst;
 		addr_type = rt->rt_type;
 
-		if (addr_type == RTN_LOCAL || (rt->rt_flags&RTCF_DNAT) ||
-		    (addr_type == RTN_UNICAST && rt->u.dst.dev != dev &&
-		     ((in_dev && IN_DEV_PROXY_ARP(in_dev) && IN_DEV_FORWARD(in_dev)) ||
-		      arp_check_published(tip, dev))))
-			arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
-	} else {
-		if (arp->ar_op != htons(ARPOP_REPLY) ||
-		    inet_addr_type(sip) != RTN_UNICAST)
+		if (addr_type == RTN_LOCAL) {
+			struct neighbour *n;
+			n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
+			if (n) {
+				arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
+				neigh_release(n);
+			}
+		} else if (in_dev && IN_DEV_FORWARD(in_dev)) {
+			if ((rt->rt_flags&RTCF_DNAT) ||
+			    (addr_type == RTN_UNICAST  && rt->u.dst.dev != dev &&
+			     (IN_DEV_PROXY_ARP(in_dev) || pneigh_lookup(&arp_tbl, &tip, dev, 0)))) {
+				n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
+				neigh_release(n);
+
+				if (skb->stamp.tv_sec == 0 ||
+				    skb->pkt_type == PACKET_HOST ||
+				    in_dev->arp_parms->proxy_delay == 0) {
+					arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
+				} else {
+					pneigh_enqueue(&arp_tbl, in_dev->arp_parms, skb);
+					return 0;
+				}
+			}
+		}
+		break;
+
+	case __constant_htons(ARPOP_REPLY):
+		if (inet_addr_type(sip) != RTN_UNICAST)
 			goto out;
+		n = __neigh_lookup(&arp_tbl, &sip, dev, -1);
+		if (n) {
+			int state = NUD_REACHABLE;
+			int override = 1;
+			if (jiffies - n->updated < n->parms->locktime &&
+			    jiffies - n->updated >= 0)
+				override = 0;
+			if (skb->pkt_type != PACKET_HOST)
+				state = NUD_STALE;
+			neigh_update(n, sha, state, override, 1);
+			neigh_release(n);
+		}
+		break;
 	}
 
-	start_bh_atomic();
-	arp_update(sip, sha, dev, 0, arp->ar_op == htons(ARPOP_REPLY));
-	end_bh_atomic();
-
 out:
 	kfree_skb(skb, FREE_READ);
 	return 0;
@@ -1492,175 +687,74 @@
 
 int arp_req_set(struct arpreq *r, struct device * dev)
 {
-	struct arp_table *entry, **entryp;
-	struct sockaddr_in *si;
-	unsigned char *ha = NULL;
-	u32 ip;
-	u32 mask = DEF_ARP_NETMASK;
-
-	/*
-	 *	Extract netmask (if supplied).
-	 */
-
-	if (r->arp_flags&ATF_NETMASK)
-	{
-		si = (struct sockaddr_in *) &r->arp_netmask;
-		mask = si->sin_addr.s_addr;
-	}
-
-	/*
-	 *	Extract destination.
-	 */
-	
-	si = (struct sockaddr_in *) &r->arp_pa;
-	ip = si->sin_addr.s_addr;
+	u32 ip = ((struct sockaddr_in *) &r->arp_pa)->sin_addr.s_addr;
+	struct neighbour *neigh;
+	int err;
 
-	if (r->arp_flags&ATF_PUBL)
-	{
-		if (ip & ~mask)
+	if (r->arp_flags&ATF_PUBL) {
+		u32 mask = ((struct sockaddr_in *) &r->arp_netmask)->sin_addr.s_addr;
+		if (mask && mask != 0xFFFFFFFF)
 			return -EINVAL;
-		if (!dev && (r->arp_flags & ATF_COM))
-		{
+		if (!dev && (r->arp_flags & ATF_COM)) {
 			dev = dev_getbyhwaddr(r->arp_ha.sa_family, r->arp_ha.sa_data);
 			if (!dev)
 				return -ENODEV;
 		}
+		if (mask) {
+			if (pneigh_lookup(&arp_tbl, &ip, dev, 1) == NULL)
+				return -ENOBUFS;
+			return 0;
+		}
+		if (dev == NULL) {
+			ipv4_config.proxy_arp = 1;
+			return 0;
+		}
+		if (dev->ip_ptr) {
+			((struct in_device*)dev->ip_ptr)->flags |= IFF_IP_PROXYARP;
+			return 0;
+		}
+		return -ENXIO;
 	}
-	else
-	{
-		struct rtable * rt;
-		int err;
 
-		if ((r->arp_flags & ATF_PERM) && !(r->arp_flags & ATF_COM))
-			r->arp_flags |= ATF_COM;
-
-		err = ip_route_output(&rt, ip, 0, 1, dev ? dev->ifindex : 0);
-		if (err)
+	if (r->arp_flags & ATF_PERM)
+		r->arp_flags |= ATF_COM;
+	if (dev == NULL) {
+		struct rtable * rt;
+		if ((err = ip_route_output(&rt, ip, 0, 1, 0)) != 0)
 			return err;
+		dev = rt->u.dst.dev;
+		ip_rt_put(rt);
 		if (!dev)
-			dev = rt->u.dst.dev;
-		if (rt->rt_flags&(RTCF_LOCAL|RTCF_BROADCAST|RTCF_MULTICAST|RTCF_DNAT)) {
-			if (rt->rt_flags&RTCF_BROADCAST &&
-			    dev->type == ARPHRD_METRICOM &&
-			    r->arp_ha.sa_family == ARPHRD_METRICOM) {
-				memcpy(dev->broadcast, r->arp_ha.sa_data, dev->addr_len);
-				ip_rt_put(rt);
-				return 0;
-			}
-			ip_rt_put(rt);
 			return -EINVAL;
-		}
-		ip_rt_put(rt);
 	}
-	
-	if (dev && (dev->flags&(IFF_LOOPBACK|IFF_NOARP)))
-		return -ENODEV;
-
-	if (dev && r->arp_ha.sa_family != dev->type)	
+	if (r->arp_ha.sa_family != dev->type)	
 		return -EINVAL;
 
+	err = -ENOBUFS;
 	start_bh_atomic();
-
-	if (!(r->arp_flags & ATF_PUBL))
-		entryp = &arp_tables[HASH(ip)];
-	else
-		entryp = &arp_proxy_list;
-
-	while ((entry = *entryp) != NULL)
-	{
-		if (entry->mask == mask)
-			break;
-		if ((entry->mask & mask) != mask)
-			break;
-		entryp = &entry->u.next;
-	}
-	while ((entry = *entryp) != NULL && entry->mask == mask)
-	{
-		if (entry->ip == ip)
-			break;
-		entryp = &entry->u.next;
-	}
-	while ((entry = *entryp) != NULL && entry->mask == mask &&
-	       entry->ip == ip)
-	{
-		if (!entry->u.neigh.dev || entry->u.neigh.dev == dev)
-			break;
-		entryp = &entry->u.next;
-	}
-
-	while ((entry = *entryp) != NULL)
-	{
-		if (entry->ip != ip || entry->mask != mask ||
-		    entry->u.neigh.dev != dev)
-		{
-			entry = NULL;
-			break;
-		}
-		if (entry->hatype == r->arp_ha.sa_family &&
-		    (!(r->arp_flags & ATF_MAGIC) ||
-		     entry->flags == r->arp_flags))
-			break;
-		entryp = &entry->u.next;
-	}
-
-	if (entry)
-		atomic_inc(&entry->u.neigh.refcnt);
-	else
-	{
-		entry = arp_alloc(r->arp_flags&ATF_PUBL ? 0 : 1);
-		if (entry == NULL)
-		{
-			end_bh_atomic();
-			return -ENOMEM;
-		}
-		entry->ip = ip;
-		entry->u.neigh.dev = dev;
-		entry->mask = mask;
-
-		if (dev)
-			entry->hatype = dev->type;
-
-		entry->u.next = *entryp;
-		*entryp = entry;
-	}
-	entry->flags = r->arp_flags;
-	if (!(entry->flags&(ATF_PUBL|ATF_COM)))
-		atomic_inc(&arp_unres_size);
-
-	if (entry->flags & ATF_PUBL)
-	{
-		if (entry->flags & ATF_COM)
-		{
-			entry->hatype = r->arp_ha.sa_family;
-			ha = r->arp_ha.sa_data;
-		}
-		else if (dev)
-			ha = dev->dev_addr;
-	}
-	else
-		ha = r->arp_ha.sa_data;
-
-	if (ha)
-		memcpy(entry->u.neigh.ha, ha, dev ? dev->addr_len : MAX_ADDR_LEN);
-	else
-		memset(entry->u.neigh.ha, 0, MAX_ADDR_LEN);
-
-	entry->last_updated = entry->u.neigh.lastused = jiffies;
-	
-	if (!(entry->flags & ATF_PUBL))
-	{
-		if (entry->flags & ATF_COM)
-		{
-			arpd_update(entry->ip, entry->u.neigh.dev, ha);
-			arp_update_hhs(entry);
-		}
+	neigh = __neigh_lookup(&arp_tbl, &ip, dev, 1);
+	if (neigh) {
+		unsigned state = 0;
+		if (r->arp_flags&ATF_PERM)
+			state = NUD_PERMANENT;
 		else
-			arp_start_resolution(entry);
+			state = NUD_STALE;
+		err = neigh_update(neigh, (r->arp_flags&ATF_COM) ?
+				   r->arp_ha.sa_data : NULL, state, 1, 0);
+		neigh_release(neigh);
 	}
-
-	neigh_release(&entry->u.neigh);
 	end_bh_atomic();
-	return 0;
+	return err;
+}
+
+static unsigned arp_state_to_flags(struct neighbour *neigh)
+{
+	unsigned flags = 0;
+	if (neigh->nud_state&NUD_PERMANENT)
+		flags = ATF_PERM;
+	else if (neigh->nud_state&NUD_VALID)
+		flags = ATF_COM;
+	return flags;
 }
 
 /*
@@ -1669,97 +763,52 @@
 
 static int arp_req_get(struct arpreq *r, struct device *dev)
 {
-	struct arp_table *entry;
-	struct sockaddr_in *si;
-	u32 mask = DEF_ARP_NETMASK;
-
-	if (r->arp_flags&ATF_NETMASK)
-	{
-		si = (struct sockaddr_in *) &r->arp_netmask;
-		mask = si->sin_addr.s_addr;
-	}
-
-	si = (struct sockaddr_in *) &r->arp_pa;
-
-	start_bh_atomic();
-
-	if (!(r->arp_flags & ATF_PUBL))
-		entry = arp_tables[HASH(si->sin_addr.s_addr)];
-	else
-		entry = arp_proxy_list;
-
-	for ( ; entry ;entry = entry->u.next)
-	{
-		if (entry->ip == si->sin_addr.s_addr &&
-		    (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask) &&
-		    ( (r->arp_flags&ATF_PUBL) ?
-		      (entry->u.neigh.dev == dev && entry->hatype == r->arp_ha.sa_family)
-		     : (entry->u.neigh.dev == dev || !dev)))
-		{
-			if (entry->u.neigh.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
-			{
-				r->arp_ha.sa_family = entry->hatype;
-				memset(r->arp_ha.sa_data, 0, sizeof(r->arp_ha.sa_data));
-			}
-			r->arp_flags = entry->flags;
-			end_bh_atomic();
-			return 0;
-		}
+	u32 ip = ((struct sockaddr_in *) &r->arp_pa)->sin_addr.s_addr;
+	struct neighbour *neigh = neigh_lookup(&arp_tbl, &ip, dev);
+	if (neigh) {
+		memcpy(r->arp_ha.sa_data, neigh->ha, dev->addr_len);
+		r->arp_ha.sa_family = dev->type;
+		strncpy(r->arp_dev, dev->name, sizeof(r->arp_dev));
+		r->arp_flags = arp_state_to_flags(neigh);
+		neigh_release(neigh);
+		return 0;
 	}
-
-	end_bh_atomic();
 	return -ENXIO;
 }
 
 int arp_req_delete(struct arpreq *r, struct device * dev)
 {
-	struct sockaddr_in	*si;
-	struct arp_table	*entry, **entryp;
-	int	retval = -ENXIO;
-	u32	mask = DEF_ARP_NETMASK;
-
-	if (r->arp_flags&ATF_NETMASK)
-	{
-		si = (struct sockaddr_in *) &r->arp_netmask;
-		mask = si->sin_addr.s_addr;
-	}
-
-	si = (struct sockaddr_in *) &r->arp_pa;
-
-	start_bh_atomic();
-
-	if (!(r->arp_flags & ATF_PUBL))
-		entryp = &arp_tables[HASH(si->sin_addr.s_addr)];
-	else
-		entryp = &arp_proxy_list;
+	int err;
+	u32 ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
+	struct neighbour *neigh;
 
-	while ((entry = *entryp) != NULL)
-	{
-		if (entry->ip == si->sin_addr.s_addr
-		    && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask)
-		    && (entry->u.neigh.dev == dev || (!(r->arp_flags&ATF_PUBL) && !dev))
-		    && (!(r->arp_flags&ATF_MAGIC) || r->arp_flags == entry->flags))
-		{
-			if (!atomic_read(&entry->u.neigh.refcnt))
-			{
-				arp_free(entryp);
-				retval = 0;
-				continue;
+	if (r->arp_flags & ATF_PUBL) {
+		u32 mask = ((struct sockaddr_in *) &r->arp_netmask)->sin_addr.s_addr;
+		if (mask == 0xFFFFFFFF)
+			return pneigh_delete(&arp_tbl, &ip, dev);
+		if (mask == 0) {
+			if (dev == NULL) {
+				ipv4_config.proxy_arp = 0;
+				return 0;
+			}
+			if (dev->ip_ptr) {
+				((struct in_device*)dev->ip_ptr)->flags &= ~IFF_IP_PROXYARP;
+				return 0;
 			}
-			if (retval)
-				retval = -EBUSY;
+			return -ENXIO;
 		}
-		entryp = &entry->u.next;
+		return -EINVAL;
 	}
 
+	err = -ENXIO;
+	start_bh_atomic();
+	neigh = neigh_lookup(&arp_tbl, &ip, dev);
+	if (neigh) {
+		err = neigh_update(neigh, NULL, NUD_FAILED, 1, 0);
+		neigh_release(neigh);
+	}
 	end_bh_atomic();
-	return retval;
+	return err;
 }
 
 /*
@@ -1772,8 +821,7 @@
 	struct arpreq r;
 	struct device * dev = NULL;
 
-	switch(cmd)
-	{
+	switch(cmd) {
 		case SIOCDARP:
 		case SIOCSARP:
 			if (!suser())
@@ -1791,41 +839,47 @@
 		return -EPFNOSUPPORT;
 
 	if (!(r.arp_flags & ATF_PUBL) &&
-	    (r.arp_flags & (ATF_NETMASK|ATF_DONTPUB|ATF_MAGIC)))
+	    (r.arp_flags & (ATF_NETMASK|ATF_DONTPUB)))
 		return -EINVAL;
 	if (!(r.arp_flags & ATF_NETMASK))
-		((struct sockaddr_in *)&r.arp_netmask)->sin_addr.s_addr=DEF_ARP_NETMASK;
+		((struct sockaddr_in *)&r.arp_netmask)->sin_addr.s_addr=__constant_htonl(0xFFFFFFFFUL);
 
-	if (r.arp_dev[0])
-	{
+	rtnl_lock();
+	if (r.arp_dev[0]) {
+		err = -ENODEV;
 		if ((dev = dev_get(r.arp_dev)) == NULL)
-			return -ENODEV;
+			goto out;
 
+		/* Mmmm... It is wrong... ARPHRD_NETROM==0 */
 		if (!r.arp_ha.sa_family)
 			r.arp_ha.sa_family = dev->type;
+		err = -EINVAL;
 		if ((r.arp_flags & ATF_COM) && r.arp_ha.sa_family != dev->type)
-			return -EINVAL;
+			goto out;
 	}
 
-	switch(cmd)
-	{
-		case SIOCDARP:
-		        return arp_req_delete(&r, dev);
-		case SIOCSARP:
-			return arp_req_set(&r, dev);
-		case SIOCGARP:
-			err = arp_req_get(&r, dev);
-			if (!err)
-				err = copy_to_user(arg, &r, sizeof(r));
-			return err;
+	switch(cmd) {
+	case SIOCDARP:
+	        err = arp_req_delete(&r, dev);
+		break;
+	case SIOCSARP:
+		err = arp_req_set(&r, dev);
+		break;
+	case SIOCGARP:
+		err = arp_req_get(&r, dev);
+		if (!err && copy_to_user(arg, &r, sizeof(r)))
+			err = -EFAULT;
+		break;
 	}
-	/*NOTREACHED*/
-	return 0;
+out:
+	rtnl_unlock();
+	return err;
 }
 
 /*
  *	Write the contents of the ARP cache to a PROCfs file.
  */
+#ifdef CONFIG_PROC_FS
 
 #define HBUFFERLEN 30
 
@@ -1834,7 +888,7 @@
 	int len=0;
 	off_t pos=0;
 	int size;
-	struct arp_table *entry;
+	struct neighbour *n;
 	char hbuffer[HBUFFERLEN];
 	int i,j,k;
 	const char hexbuf[] =  "0123456789ABCDEF";
@@ -1844,90 +898,86 @@
 	pos+=size;
 	len+=size;
 
+	neigh_table_lock(&arp_tbl);
 
-	for(i=0; i<FULL_ARP_TABLE_SIZE; i++)
-	{
-		start_bh_atomic();
-
-		for(entry=arp_tables[i]; entry!=NULL; entry=entry->u.next)
-		{
+	for(i=0; i<=NEIGH_HASHMASK; i++)	{
+		for (n=arp_tbl.hash_buckets[i]; n; n=n->next) {
+			struct device *dev = n->dev;
+			int hatype = dev->type;
+
+			/* I'd get great pleasure deleting
+			   this ugly code. Let's output it in hexadecimal format.
+			   "arp" utility will eventually repaired  --ANK
+			 */
+#if 1 /* UGLY CODE */
 /*
  *	Convert hardware address to XX:XX:XX:XX ... form.
  */
 #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->u.neigh.ha));
-			else {
-#else
-			if(entry->hatype==ARPHRD_AX25)
-			     strcpy(hbuffer,ax2asc((ax25_address *)entry->u.neigh.ha));
+			if (hatype == ARPHRD_AX25 || hatype == ARPHRD_NETROM)
+			     strcpy(hbuffer,ax2asc((ax25_address *)n->ha));
 			else {
 #endif
-#endif
-				
-			if (entry->u.neigh.dev)
-			{
-				for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->u.neigh.dev->addr_len;j++)
-				{
-					hbuffer[k++]=hexbuf[ (entry->u.neigh.ha[j]>>4)&15 ];
-					hbuffer[k++]=hexbuf[  entry->u.neigh.ha[j]&15     ];
-					hbuffer[k++]=':';
-				}
-				hbuffer[--k]=0;
+			for (k=0,j=0;k<HBUFFERLEN-3 && j<dev->addr_len;j++) {
+				hbuffer[k++]=hexbuf[(n->ha[j]>>4)&15 ];
+				hbuffer[k++]=hexbuf[n->ha[j]&15     ];
+				hbuffer[k++]=':';
 			}
-			else
-				strcpy(hbuffer, "00:00:00:00:00:00");
+			hbuffer[--k]=0;
 	
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
-			}
+		}
+#endif
+#else
+			if ((neigh->nud_state&NUD_VALID) && dev->addr_len) {
+				int j;
+				for (j=0; j < dev->addr_len; j++)
+					sprintf(hbuffer+2*j, "%02x", neigh->ha[j]);
+			} else
+				sprintf(hbuffer, "0");
 #endif
 
 			size = sprintf(buffer+len,
 				"%-17s0x%-10x0x%-10x%s",
-				in_ntoa(entry->ip),
-				entry->hatype,
-				entry->flags,
+				in_ntoa(*(u32*)n->primary_key),
+				hatype,
+				arp_state_to_flags(n), 
 				hbuffer);
-#if RT_CACHE_DEBUG < 2
 			size += sprintf(buffer+len+size,
 				 "     %-17s %s\n",
-				 entry->mask==DEF_ARP_NETMASK ?
-				 "*" : in_ntoa(entry->mask),
-					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.neigh.dev ? entry->u.neigh.dev->name : "*", 
-				 atomic_read(&entry->u.neigh.refcnt),
-				 entry->u.neigh.hh ? atomic_read(&entry->u.neigh.hh->hh_refcnt) : -1,
-				 entry->u.neigh.hh ? entry->u.neigh.hh->hh_uptodate : 0);
-#endif
-	
+				 "*", dev->name);
+
 			len += size;
 			pos += size;
 		  
 			if (pos <= offset)
 				len=0;
 			if (pos >= offset+length)
-			{
-				end_bh_atomic();
 				goto done;
-			}
 		}
-		end_bh_atomic();	
 	}
+
 done:
+	neigh_table_unlock(&arp_tbl);
   
 	*start = buffer+len-(pos-offset);	/* Start of wanted data */
 	len = pos-offset;			/* Start slop */
 	if (len>length)
 		len = length;			/* Ending slop */
+	if (len<0)
+		len = 0;
 	return len;
 }
+#endif
 
+/* Note, that it is not on notifier chain.
+   It is necessary, that this routine was called after route cache will be
+   flushed.
+ */
+void arp_ifdown(struct device *dev)
+{
+	neigh_ifdown(&arp_tbl, dev);
+}
 
 
 /*
@@ -1943,12 +993,6 @@
 	NULL
 };
 
-static struct notifier_block arp_dev_notifier={
-	arp_device_event,
-	NULL,
-	0
-};
-
 #ifdef CONFIG_PROC_FS
 static struct proc_dir_entry proc_net_arp = {
 	PROC_NET_ARP, 3, "arp",
@@ -1960,18 +1004,15 @@
 
 __initfunc(void arp_init (void))
 {
+	neigh_table_init(&arp_tbl);
+
 	dev_add_pack(&arp_packet_type);
-	/* Start with the regular checks for expired arp entries. */
-	add_timer(&arp_timer);
-	/* Register for device down reports */
-	register_netdevice_notifier(&arp_dev_notifier);
 
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_net_arp);
 #endif
-
-#ifdef CONFIG_ARPD
-	arpd_sk = netlink_kernel_create(NETLINK_ARPD, arpd_callback);
+#ifdef CONFIG_SYSCTL
+	arp_tbl.parms.sysctl_table = neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4");
 #endif
 }
 
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.1.78/linux/net/ipv4/devinet.c	Mon Dec  1 12:04:16 1997
+++ linux/net/ipv4/devinet.c	Mon Jan 12 15:28:27 1998
@@ -1,7 +1,7 @@
 /*
  *	NET3	IP device support routines.
  *
- *	Version: $Id: devinet.c,v 1.14 1997/10/10 22:40:44 davem Exp $
+ *	Version: $Id: devinet.c,v 1.15 1997/12/13 21:52:47 kuznet Exp $
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -46,6 +46,9 @@
 #include <linux/notifier.h>
 #include <linux/inetdevice.h>
 #include <linux/igmp.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
 #endif
@@ -96,8 +99,16 @@
 	inet_dev_count++;
 	memset(in_dev, 0, sizeof(*in_dev));
 	in_dev->dev = dev;
+	if ((in_dev->arp_parms = neigh_parms_alloc(&arp_tbl)) == NULL)
+		in_dev->arp_parms = &arp_tbl.parms;
+#ifdef CONFIG_SYSCTL
+	else
+		in_dev->arp_parms->sysctl_table =
+			neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4");
+#endif
 	dev->ip_ptr = in_dev;
-	ip_mc_init_dev(in_dev);
+	if (dev->flags&IFF_UP)
+		ip_mc_up(in_dev);
 	return in_dev;
 }
 
@@ -113,6 +124,7 @@
 	}
 
 	in_dev->dev->ip_ptr = NULL;
+	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
 	kfree(in_dev);
 }
 
@@ -201,8 +213,10 @@
 		}
 	}
 
-	if (!(ifa->ifa_flags&IFA_F_SECONDARY))
+	if (!(ifa->ifa_flags&IFA_F_SECONDARY)) {
+		net_srandom(ifa->ifa_local);
 		ifap = last_primary;
+	}
 
 	cli();
 	ifa->ifa_next = *ifap;
@@ -704,7 +718,7 @@
 	ifm = NLMSG_DATA(nlh);
 	ifm->ifa_family = AF_INET;
 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
-	ifm->ifa_flags = ifa->ifa_flags;
+	ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
 	ifm->ifa_scope = ifa->ifa_scope;
 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
 	if (ifa->ifa_prefixlen)
@@ -796,9 +810,9 @@
 	{ inet_rtm_getroute,	inet_dump_fib,		},
 	{ NULL,			NULL,			},
 
-	{ NULL,			NULL,			},
-	{ NULL,			NULL,			},
-	{ NULL,			NULL,			},
+	{ neigh_add,		NULL,			},
+	{ neigh_delete,		NULL,			},
+	{ NULL,			neigh_dump_info,	},
 	{ NULL,			NULL,			},
 
 #ifdef CONFIG_IP_MULTIPLE_TABLES
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/fib_frontend.c linux/net/ipv4/fib_frontend.c
--- v2.1.78/linux/net/ipv4/fib_frontend.c	Fri Dec 19 15:53:06 1997
+++ linux/net/ipv4/fib_frontend.c	Mon Jan 12 15:28:27 1998
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: FIB frontend.
  *
- * Version:	$Id: fib_frontend.c,v 1.4 1997/11/09 20:05:23 kuznet Exp $
+ * Version:	$Id: fib_frontend.c,v 1.6 1997/12/13 21:52:48 kuznet Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -477,7 +477,7 @@
 			   First of all, we scan fib_info list searching
 			   for stray nexthop entries, then ignite fib_flush.
 			*/
-			if (fib_sync_down(ifa->ifa_local, NULL))
+			if (fib_sync_down(ifa->ifa_local, NULL, 0))
 				fib_flush();
 		}
 	}
@@ -523,14 +523,18 @@
 		rt_cache_flush(2*HZ);
 		break;
 	case NETDEV_DOWN:
-		if (fib_sync_down(0, dev))
+		if (fib_sync_down(0, dev, 0))
 			fib_flush();
 		rt_cache_flush(0);
+		arp_ifdown(dev);
 		break;
 	case NETDEV_UNREGISTER:
 		if (in_dev->ifa_list)
 			printk("About to crash!\n");
+		if (fib_sync_down(0, dev, 1))
+			fib_flush();
 		rt_cache_flush(0);
+		arp_ifdown(dev);
 		break;
 	}
 	return NOTIFY_DONE;
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/fib_semantics.c linux/net/ipv4/fib_semantics.c
--- v2.1.78/linux/net/ipv4/fib_semantics.c	Sun Dec 21 22:36:17 1997
+++ linux/net/ipv4/fib_semantics.c	Mon Jan 12 15:28:27 1998
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: semantics.
  *
- * Version:	$Id: fib_semantics.c,v 1.5 1997/10/10 22:40:50 davem Exp $
+ * Version:	$Id: fib_semantics.c,v 1.6 1997/12/13 21:52:49 kuznet Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -184,7 +184,6 @@
 	struct rtnexthop *nhp = RTM_RTNH(r);
 	int nhlen = RTM_NHLEN(nlh, r);
 
-printk("get nhs %d/%d\n", r->rtm_nhs, nhlen);	
 	change_nexthops(fi) {
 		int attrlen = nhlen - sizeof(struct rtnexthop);
 		if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0)
@@ -194,8 +193,6 @@
 		nh->nh_weight = nhp->rtnh_hops + 1;
 		if (attrlen)
 			nh->nh_gw = fib_get_attr32(RTNH_DATA(nhp), attrlen, RTA_GATEWAY);
-printk("Got nh: via %08x dev %d w %d fl %02x\n", nh->nh_gw, nh->nh_oif,
-       nh->nh_weight, nh->nh_flags);
 		nhp = RTNH_NEXT(nhp);
 	} endfor_nexthops(fi);
 	return 0;
@@ -457,6 +454,15 @@
 
 link_it:
 	if ((ofi = fib_find_info(fi)) != NULL) {
+		if (fi->fib_nh[0].nh_scope != ofi->fib_nh[0].nh_scope) {
+			printk("nh %d/%d gw=%08x/%08x dev=%s/%s\n",
+			       fi->fib_nh[0].nh_scope,
+			       ofi->fib_nh[0].nh_scope,
+			       fi->fib_nh[0].nh_gw,
+			       ofi->fib_nh[0].nh_gw,
+			       fi->fib_nh[0].nh_dev->name,
+			       ofi->fib_nh[0].nh_dev->name);
+		}
 		kfree(fi);
 		ofi->fib_refcnt++;
 		return ofi;
@@ -743,9 +749,13 @@
    - device went down -> we must shutdown all nexthops going via it.
  */
 
-int fib_sync_down(u32 local, struct device *dev)
+int fib_sync_down(u32 local, struct device *dev, int force)
 {
 	int ret = 0;
+	int scope = RT_SCOPE_NOWHERE;
+	
+	if (force)
+		scope = -1;
 
 	for_fib_info() {
 		if (local && fi->fib_prefsrc == local) {
@@ -758,7 +768,7 @@
 				if (nh->nh_flags&RTNH_F_DEAD)
 					dead++;
 				else if (nh->nh_dev == dev &&
-					 nh->nh_scope != RT_SCOPE_NOWHERE) {
+					 nh->nh_scope != scope) {
 					nh->nh_flags |= RTNH_F_DEAD;
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 					fi->fib_power -= nh->nh_power;
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v2.1.78/linux/net/ipv4/igmp.c	Mon Dec  1 12:04:16 1997
+++ linux/net/ipv4/igmp.c	Mon Jan 12 15:28:27 1998
@@ -8,7 +8,7 @@
  *	the older version didn't come out right using gcc 2.5.8, the newer one
  *	seems to fall out with gcc 2.6.2.
  *
- *	Version: $Id: igmp.c,v 1.22 1997/10/29 20:27:24 kuznet Exp $
+ *	Version: $Id: igmp.c,v 1.24 1997/12/17 20:14:10 kuznet Exp $
  *
  *	Authors:
  *		Alan Cox <Alan.Cox@linux.org>
@@ -131,19 +131,12 @@
 	}
 }
 
-extern __inline__ unsigned int random(void)
-{
-	static unsigned long seed=152L;
-	seed=seed*69069L+1;
-	return seed^jiffies;
-}
-
 static __inline__ void igmp_start_timer(struct ip_mc_list *im, int max_delay)
 {
 	int tv;
 	if (im->tm_running)
 		return;
-	tv=random() % max_delay;
+	tv=net_random() % max_delay;
 	im->timer.expires=jiffies+tv+2;
 	im->tm_running=1;
 	add_timer(&im->timer);
@@ -186,7 +179,6 @@
 	skb->dst = &rt->u.dst;
 
 	skb_reserve(skb, (dev->hard_header_len+15)&~15);
-	ip_ll_header(skb);
 
 	skb->nh.iph = iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4);
 
@@ -342,22 +334,6 @@
 
 #endif
 
-/*
- *	Map a multicast IP onto multicast MAC for type ethernet.
- */
-
-extern __inline__ void ip_mc_map(u32 addr, char *buf)
-{
-	addr=ntohl(addr);
-	buf[0]=0x01;
-	buf[1]=0x00;
-	buf[2]=0x5e;
-	buf[5]=addr&0xFF;
-	addr>>=8;
-	buf[4]=addr&0xFF;
-	addr>>=8;
-	buf[3]=addr&0x7F;
-}
 
 /*
  *	Add a filter to a device
@@ -365,15 +341,18 @@
 
 static void ip_mc_filter_add(struct in_device *in_dev, u32 addr)
 {
-	char buf[6];
+	char buf[MAX_ADDR_LEN];
 	struct device *dev = in_dev->dev;
 
-	if (!(dev->flags & IFF_MULTICAST))
-		return;
-	if (dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_FDDI)
-		return; /* Only do ethernet or FDDI for now */
-	ip_mc_map(addr, buf);
-	dev_mc_add(dev,buf,ETH_ALEN,0);
+	/* Checking for IFF_MULTICAST here is WRONG-WRONG-WRONG.
+	   We will get multicast token leakage, when IFF_MULTICAST
+	   is changed. This check should be done in dev->set_multicast_list
+	   routine. Something sort of:
+	   if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; }
+	   --ANK
+	 */
+	if (arp_mc_map(addr, buf, dev, 0) == 0)
+		dev_mc_add(dev,buf,dev->addr_len,0);
 }
 
 /*
@@ -382,18 +361,19 @@
 
 static void ip_mc_filter_del(struct in_device *in_dev, u32 addr)
 {
-	char buf[6];
+	char buf[MAX_ADDR_LEN];
 	struct device *dev = in_dev->dev;
 
-	if (dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_FDDI)
-		return; /* Only do ethernet or FDDI for now */
-	ip_mc_map(addr,buf);
-	dev_mc_delete(dev,buf,ETH_ALEN,0);
+	if (arp_mc_map(addr, buf, dev, 0) == 0)
+		dev_mc_delete(dev,buf,dev->addr_len,0);
 }
 
 static void igmp_group_dropped(struct ip_mc_list *im)
 {
-	ip_mc_filter_del(im->interface, im->multiaddr);
+	if (im->loaded) {
+		im->loaded = 0;
+		ip_mc_filter_del(im->interface, im->multiaddr);
+	}
 
 #ifdef CONFIG_IP_MULTICAST
 	if (LOCAL_MCAST(im->multiaddr))
@@ -410,7 +390,10 @@
 
 static void igmp_group_added(struct ip_mc_list *im)
 {
-	ip_mc_filter_add(im->interface, im->multiaddr);
+	if (im->loaded == 0) {
+		im->loaded = 1;
+		ip_mc_filter_add(im->interface, im->multiaddr);
+	}
 
 #ifdef CONFIG_IP_MULTICAST
 	if (LOCAL_MCAST(im->multiaddr))
@@ -458,13 +441,13 @@
 	im->timer.function=&igmp_timer_expire;
 	im->unsolicit_count = IGMP_Unsolicited_Report_Count;
 	im->reporter = 0;
+	im->loaded = 0;
 #endif
 	im->next=in_dev->mc_list;
 	in_dev->mc_list=im;
-	if (in_dev->dev->flags & IFF_UP) {
-		igmp_group_added(im);
+	igmp_group_added(im);
+	if (in_dev->dev->flags & IFF_UP)
 		ip_rt_multicast_event(in_dev);
-	}
 	return;
 }
 
@@ -480,10 +463,9 @@
 		if (i->multiaddr==addr) {
 			if (--i->users == 0) {
 				*ip = i->next;
-				if (in_dev->dev->flags & IFF_UP) {
-					igmp_group_dropped(i);
+				igmp_group_dropped(i);
+				if (in_dev->dev->flags & IFF_UP)
 					ip_rt_multicast_event(in_dev);
-				}
 				kfree_s(i, sizeof(*i));
 			}
 			return 0;
@@ -500,6 +482,8 @@
 
 	for (i=in_dev->mc_list; i; i=i->next)
 		igmp_group_dropped(i);
+
+	ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);
 }
 
 /* Device going up */
@@ -508,6 +492,8 @@
 {
 	struct ip_mc_list *i;
 
+	ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
+
 	for (i=in_dev->mc_list; i; i=i->next)
 		igmp_group_added(i);
 }
@@ -522,19 +508,11 @@
 
 	while ((i = in_dev->mc_list) != NULL) {
 		in_dev->mc_list = i->next;
+		igmp_group_dropped(i);
 		kfree_s(i, sizeof(*i));
 	}
 }
 
-/* Initialize multicasting on an IP interface */
-
-void ip_mc_init_dev(struct in_device *in_dev)
-{
-	in_dev->mc_list = NULL;
-	in_dev->mr_v1_seen = 0;
-	ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
-}
-
 static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
 {
 	struct rtable *rt;
@@ -697,9 +675,10 @@
 				begin=pos;
 			}
 			if(pos>offset+length)
-				break;
+				goto done;
 		}
 	}
+done:
 	*start=buffer+(offset-begin);
 	len-=(offset-begin);
 	if(len>length)
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v2.1.78/linux/net/ipv4/ip_forward.c	Sun Dec 21 22:36:17 1997
+++ linux/net/ipv4/ip_forward.c	Mon Jan 12 15:28:27 1998
@@ -5,7 +5,7 @@
  *
  *		The IP forwarding functionality.
  *		
- * Version:	$Id: ip_forward.c,v 1.33 1997/11/28 15:32:03 alan Exp $
+ * Version:	$Id: ip_forward.c,v 1.37 1997/12/18 17:01:11 kuznet Exp $
  *
  * Authors:	see ip.c
  *
@@ -112,7 +112,7 @@
 	if (ip_decrease_ttl(iph) <= 0)
                 goto too_many_hops;
 
-	if (opt->is_strictroute && (rt->rt_flags&RTF_GATEWAY))
+	if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
                 goto sr_failed;
 
 	/*
@@ -141,51 +141,44 @@
 	 * If the indicated interface is up and running, kick it.
 	 */
 
-	if (dev2->flags & IFF_UP) {
-		if (skb->len > mtu && (ntohs(iph->frag_off) & IP_DF))
-			goto frag_needed;
+	if (skb->len > mtu && (ntohs(iph->frag_off) & IP_DF))
+		goto frag_needed;
 
 #ifdef CONFIG_IP_ROUTE_NAT
-		if (rt->rt_flags & RTCF_NAT) {
-			if (skb_headroom(skb) < dev2->hard_header_len || skb_cloned(skb)) {
-				struct sk_buff *skb2;
-				skb2 = skb_realloc_headroom(skb, (dev2->hard_header_len + 15)&~15);
-				kfree_skb(skb, FREE_WRITE);
-				skb = skb2;
-			}
-			if (ip_do_nat(skb)) {
-				kfree_skb(skb, FREE_WRITE);
+	if (rt->rt_flags & RTCF_NAT) {
+		if (skb_headroom(skb) < dev2->hard_header_len || skb_cloned(skb)) {
+			struct sk_buff *skb2;
+			skb2 = skb_realloc_headroom(skb, (dev2->hard_header_len + 15)&~15);
+			kfree_skb(skb, FREE_WRITE);
+			if (skb2 == NULL)
 				return -1;
-			}
+			skb = skb2;
+		}
+		if (ip_do_nat(skb)) {
+			kfree_skb(skb, FREE_WRITE);
+			return -1;
 		}
+	}
 #endif
 
 #ifdef CONFIG_IP_MASQUERADE
-		if(!(IPCB(skb)->flags&IPSKB_MASQUERADED)) {
-
-			if (rt->rt_flags&RTCF_VALVE) {
-				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PKT_FILTERED, 0);
-				kfree_skb(skb, FREE_READ);
-				return -1;
-			}
-
-			/* 
-			 *	Check that any ICMP packets are not for a 
-			 *	masqueraded connection.  If so rewrite them
-			 *	and skip the firewall checks
-			 */
-			if (iph->protocol == IPPROTO_ICMP) {
-				__u32 maddr;
+	if(!(IPCB(skb)->flags&IPSKB_MASQUERADED)) {
+		/* 
+		 *	Check that any ICMP packets are not for a 
+		 *	masqueraded connection.  If so rewrite them
+		 *	and skip the firewall checks
+		 */
+		if (iph->protocol == IPPROTO_ICMP) {
+			__u32 maddr;
 #ifdef CONFIG_IP_MASQUERADE_ICMP
-#define icmph ((struct icmphdr *)((char *)iph + (iph->ihl<<2)))
-			    if ((icmph->type==ICMP_DEST_UNREACH)||
-				(icmph->type==ICMP_SOURCE_QUENCH)||
-				(icmph->type==ICMP_TIME_EXCEEDED))
-			        {
+			struct icmphdr *icmph = (struct icmphdr *)((char*)iph + (iph->ihl << 2));
+			if ((icmph->type==ICMP_DEST_UNREACH)||
+			    (icmph->type==ICMP_SOURCE_QUENCH)||
+			    (icmph->type==ICMP_TIME_EXCEEDED))
+			{
 #endif
 				maddr = inet_select_addr(dev2, rt->rt_gateway, RT_SCOPE_UNIVERSE);
-				fw_res = ip_fw_masq_icmp(&skb, maddr);
-			        if (fw_res < 0) {
+			        if (fw_res = ip_fw_masq_icmp(&skb, maddr) < 0) {
 					kfree_skb(skb, FREE_READ);
 					return -1;
 				}
@@ -196,9 +189,9 @@
 #ifdef CONFIG_IP_MASQUERADE_ICMP
 			       }
 #endif				
-			}
-			if (rt->rt_flags&RTCF_MASQ)
-				goto skip_call_fw_firewall;
+		}
+		if (rt->rt_flags&RTCF_MASQ)
+			goto skip_call_fw_firewall;
 #endif /* CONFIG_IP_MASQUERADE */
 
 #ifdef CONFIG_FIREWALL
@@ -217,21 +210,21 @@
 #endif
 
 #ifdef CONFIG_IP_MASQUERADE
-		}
+	}
 
 skip_call_fw_firewall:
-		/*
-		 * If this fragment needs masquerading, make it so...
-		 * (Don't masquerade de-masqueraded fragments)
-		 */
-		if (!(IPCB(skb)->flags&IPSKB_MASQUERADED) &&
-		    (fw_res==FW_MASQUERADE || rt->rt_flags&RTCF_MASQ)) {
-			u32 maddr;
+	/*
+	 * If this fragment needs masquerading, make it so...
+	 * (Don't masquerade de-masqueraded fragments)
+	 */
+	if (!(IPCB(skb)->flags&IPSKB_MASQUERADED) &&
+	    (fw_res==FW_MASQUERADE || rt->rt_flags&RTCF_MASQ)) {
+		u32 maddr;
 
 #ifdef CONFIG_IP_ROUTE_NAT
-			maddr = (rt->rt_flags&RTCF_MASQ) ? rt->rt_src_map : 0;
+		maddr = (rt->rt_flags&RTCF_MASQ) ? rt->rt_src_map : 0;
 
-			if (maddr == 0)
+		if (maddr == 0)
 #endif
 			maddr = inet_select_addr(dev2, rt->rt_gateway, RT_SCOPE_UNIVERSE);
 
@@ -245,48 +238,55 @@
 				iph = skb->nh.iph;
 				opt = &(IPCB(skb)->opt);
 			}
-		}
+	}
 #endif
 
-		if (skb_headroom(skb) < dev2->hard_header_len || skb_cloned(skb)) {
-			struct sk_buff *skb2;
-			skb2 = skb_realloc_headroom(skb, (dev2->hard_header_len + 15)&~15);
-			kfree_skb(skb, FREE_WRITE);
+	if (skb_headroom(skb) < dev2->hard_header_len || skb_cloned(skb)) {
+		struct sk_buff *skb2;
+		skb2 = skb_realloc_headroom(skb, (dev2->hard_header_len + 15)&~15);
+		kfree_skb(skb, FREE_WRITE);
 
-			if (skb2 == NULL) {
-				NETDEBUG(printk(KERN_ERR "\nIP: No memory available for IP forward\n"));
-				return -1;
-			}
-			skb = skb2;
-			iph = skb2->nh.iph;
+		if (skb2 == NULL) {
+			NETDEBUG(printk(KERN_ERR "\nIP: No memory available for IP forward\n"));
+			return -1;
 		}
+		skb = skb2;
+		iph = skb2->nh.iph;
+	}
 
 #ifdef CONFIG_FIREWALL
-		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)
-				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
-			kfree_skb(skb,FREE_WRITE);
-			return -1;
-		}
+	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)
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+		kfree_skb(skb,FREE_WRITE);
+		return -1;
+	}
 #endif
 
-		ip_statistics.IpForwDatagrams++;
+	ip_statistics.IpForwDatagrams++;
 
-		if (opt->optlen == 0) {
-			ip_send(skb);
-			return 0;
+	if (opt->optlen == 0) {
+#ifdef CONFIG_NET_FASTROUTE
+		if (rt->rt_flags&RTCF_FAST && !netdev_fastroute_obstacles) {
+			unsigned h = ((*(u8*)&rt->key.dst)^(*(u8*)&rt->key.src))&NETDEV_FASTROUTE_HMASK;
+			/* Time to switch to functional programming :-) */
+			dst_release(xchg(&skb->dev->fastpath[h], dst_clone(&rt->u.dst)));
 		}
-	        ip_forward_options(skb);
+#endif
 		ip_send(skb);
+		return 0;
 	}
+
+	ip_forward_options(skb);
+	ip_send(skb);
 	return 0;
 
 #ifdef CONFIG_TRANSPARENT_PROXY
 local_pkt:
-#endif
 	return ip_local_deliver(skb);
+#endif
 
 frag_needed:
 	ip_statistics.IpFragFails++;
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.1.78/linux/net/ipv4/ip_fragment.c	Mon Dec  1 12:04:16 1997
+++ linux/net/ipv4/ip_fragment.c	Mon Jan 12 15:28:27 1998
@@ -5,7 +5,7 @@
  *
  *		The IP fragmentation functionality.
  *		
- * Version:	$Id: ip_fragment.c,v 1.29 1997/11/22 12:31:05 freitag Exp $
+ * Version:	$Id: ip_fragment.c,v 1.30 1997/12/29 19:52:32 kuznet Exp $
  *
  * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
  *		Alan Cox <Alan.Cox@linux.org>
@@ -32,7 +32,6 @@
 #include <linux/inet.h>
 #include <linux/firewall.h>
 #include <linux/ip_fw.h>
-#include <net/checksum.h>
 
 /* Fragment cache limits. We will commit 256K at one time. Should we
  * cross that limit we will prune down to 192K. This should cope with
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v2.1.78/linux/net/ipv4/ip_fw.c	Sun Dec 21 16:17:45 1997
+++ linux/net/ipv4/ip_fw.c	Mon Jan 12 15:28:27 1998
@@ -6,7 +6,7 @@
  *	license in recognition of the original copyright. 
  *				-- Alan Cox.
  *
- *	$Id: ip_fw.c,v 1.29 1997/10/10 22:41:01 davem Exp $
+ *	$Id: ip_fw.c,v 1.30 1997/12/19 12:06:27 freitag Exp $
  *
  *	Ported from BSD to Linux,
  *		Alan Cox 22/Nov/1994.
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/ip_gre.c linux/net/ipv4/ip_gre.c
--- v2.1.78/linux/net/ipv4/ip_gre.c	Mon Dec  1 12:04:16 1997
+++ linux/net/ipv4/ip_gre.c	Mon Jan 12 15:28:28 1998
@@ -622,13 +622,13 @@
 		else if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
 			struct in6_addr *addr6;
 			int addr_type;
-			struct nd_neigh *neigh = (struct nd_neigh *) skb->dst->neighbour;
+			struct neighbour *neigh = skb->dst->neighbour;
 
 			if (neigh == NULL)
 				goto tx_error;
 
-			addr6 = &neigh->ndn_addr;
-			addr_type = ipv6_addr_type(addr6);
+			addr6 = (struct in6_addr*)&neigh->primary_key;
+			addr_type = neigh->type;
 
 			if (addr_type == IPV6_ADDR_ANY) {
 				addr6 = &skb->nh.ipv6h->daddr;
@@ -962,28 +962,6 @@
 	return -t->hlen;
 }
 
-static int ipgre_rebuild_header(struct sk_buff *skb)
-{
-	struct device *dev = skb->dev;
-	struct iphdr *iph = (struct iphdr *)skb->data;
-	u16 *p = (u16*)(iph + 1);
- 	struct neighbour *neigh = NULL;
-
- 	if (skb->dst)
- 		neigh = skb->dst->neighbour;
-
- 	if (neigh)
- 		return neigh->ops->resolve((void*)&iph->daddr, skb);
-
-	if (p[1] == __constant_htons(ETH_P_IP))
- 		return arp_find((void*)&iph->daddr, skb);
-
-	if (net_ratelimit())
-		printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n", 
-		       dev->name, (int)p[1]);
-	return 0;
-}
-
 static int ipgre_open(struct device *dev)
 {
 	struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
@@ -1076,7 +1054,6 @@
 				return -EINVAL;
 			dev->flags = IFF_BROADCAST;
 			dev->hard_header = ipgre_header;
-			dev->rebuild_header = ipgre_rebuild_header;
 			dev->open = ipgre_open;
 			dev->stop = ipgre_close;
 		}
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.1.78/linux/net/ipv4/ip_input.c	Mon Dec  1 12:04:16 1997
+++ linux/net/ipv4/ip_input.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) module.
  *
- * Version:	$Id: ip_input.c,v 1.24 1997/10/24 17:15:58 kuznet Exp $
+ * Version:	$Id: ip_input.c,v 1.25 1997/12/13 21:52:53 kuznet Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -422,7 +422,9 @@
 	 */
 
 	if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4
+#ifndef CONFIG_IP_ROUTER
 	    || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0
+#endif
 		|| skb->len < ntohs(iph->tot_len))
 		goto inhdr_error;
 
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.1.78/linux/net/ipv4/ip_output.c	Fri Dec 19 15:53:06 1997
+++ linux/net/ipv4/ip_output.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) output module.
  *
- * Version:	$Id: ip_output.c,v 1.41 1997/11/28 15:32:37 alan Exp $
+ * Version:	$Id: ip_output.c,v 1.44 1997/12/27 20:41:14 kuznet Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -76,13 +76,6 @@
 
 int sysctl_ip_dynaddr = 0;
 
-static void __inline__ ip_ll_header_reserve(struct sk_buff *skb)
-{
-	struct rtable *rt = (struct rtable*)skb->dst;
-	skb_reserve(skb, (rt->u.dst.dev->hard_header_len+15)&~15);
-	ip_ll_header(skb);
-}
-
 
 int ip_id_count = 0;
 
@@ -105,19 +98,15 @@
 		return err;
 	}
 
-	if (opt && opt->is_strictroute && rt->rt_flags&RTF_GATEWAY) {
+	if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) {
 		ip_rt_put(rt);
 		ip_statistics.IpOutNoRoutes++;
 		return -ENETUNREACH;
 	}
 
 	skb->dst = dst_clone(&rt->u.dst);
+	skb_reserve(skb, (rt->u.dst.dev->hard_header_len+15)&~15);
 
-	skb->dev = rt->u.dst.dev;
-	skb->arp = 0;
-
-	ip_ll_header_reserve(skb);
-	
 	/*
 	 *	Now build the IP header.
 	 */
@@ -184,7 +173,7 @@
 		sk->dst_cache = &rt->u.dst;
 	}
 
-	if (opt && opt->is_strictroute && rt->rt_flags&RTF_GATEWAY) {
+	if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) {
 		sk->dst_cache = NULL;
 		ip_rt_put(rt);
 		ip_statistics.IpOutNoRoutes++;
@@ -192,11 +181,7 @@
 	}
 
 	skb->dst = dst_clone(sk->dst_cache);
-
-	skb->dev = rt->u.dst.dev;
-	skb->arp = 0;
 	skb_reserve(skb, MAX_HEADER);
-	skb->mac.raw = skb->data;
 	
 	/*
 	 *	Now build the IP header.
@@ -234,6 +219,11 @@
 	return 0;
 }
 
+int __ip_finish_output(struct sk_buff *skb)
+{
+	return ip_finish_output(skb);
+}
+
 int ip_mc_output(struct sk_buff *skb)
 {
 	struct sock *sk = skb->sk;
@@ -245,14 +235,13 @@
 	 */
 	 
 	ip_statistics.IpOutRequests++;
-#ifdef CONFIG_IP_ACCT
-	ip_fw_chk(skb->nh.iph, skb->dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
-#endif
 #ifdef CONFIG_IP_ROUTE_NAT
 	if (rt->rt_flags & RTCF_NAT)
 		ip_do_nat(skb);
 #endif
 
+	skb->dev = dev;
+
 	/*
 	 *	Multicasts are looped back for other local users
 	 */
@@ -279,7 +268,7 @@
 		dev_loopback_xmit(skb);
 
 		/* Multicasts with ttl 0 must not go beyond the host */
-		
+
 		if (skb->nh.iph->ttl == 0) {
 			kfree_skb(skb, FREE_WRITE);
 			return 0;
@@ -296,44 +285,23 @@
 		dev_loopback_xmit(skb);
 	}
 
-	if (dev->flags & IFF_UP) {
-		dev_queue_xmit(skb);
-		return 0;
-	}
-	ip_statistics.IpOutDiscards++;
-
-	kfree_skb(skb, FREE_WRITE);
-	return -ENETDOWN;
+	return ip_finish_output(skb);
 }
 
 int ip_output(struct sk_buff *skb)
 {
+#ifdef CONFIG_IP_ROUTE_NAT
 	struct rtable *rt = (struct rtable*)skb->dst;
-	struct device *dev = rt->u.dst.dev;
+#endif
 
-	/*
-	 *	If the indicated interface is up and running, send the packet.
-	 */
-	 
 	ip_statistics.IpOutRequests++;
 
-#ifdef CONFIG_IP_ACCT
-	ip_fw_chk(skb->nh.iph, skb->dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
-#endif
-
 #ifdef CONFIG_IP_ROUTE_NAT
 	if (rt->rt_flags&RTCF_NAT)
 		ip_do_nat(skb);
 #endif
 
-	if (dev->flags & IFF_UP) {
-		dev_queue_xmit(skb);
-		return 0;
-	}
-	ip_statistics.IpOutDiscards++;
-
-	kfree_skb(skb, FREE_WRITE);
-	return -ENETDOWN;
+	return ip_finish_output(skb);
 }
 
 #ifdef CONFIG_IP_ACCT
@@ -349,7 +317,7 @@
 
 	return 0;
 }
-#endif			
+#endif
 
 /*
  *	Generate a checksum for an outgoing IP datagram.
@@ -380,13 +348,7 @@
 	unsigned int tot_len;
 	struct iphdr *iph = skb->nh.iph;
 
-	/*
-	 *	Discard the surplus MAC header
-	 */
-		 
-	skb_pull(skb, skb->nh.raw - skb->data);
 	tot_len = skb->len;
-
 	iph->tot_len = htons(tot_len);
 	iph->id = htons(ip_id_count++);
 
@@ -399,7 +361,7 @@
 		kfree_skb(skb, FREE_WRITE);
 		return;
 	}
-	
+
 #ifdef CONFIG_NET_SECURITY	
 	/*
 	 *	Add an IP checksum (must do this before SECurity because
@@ -413,7 +375,7 @@
 		kfree_skb(skb, FREE_WRITE);
 		return;
 	}
-	
+
 	iph = skb->nh.iph;
 	/* don't update tot_len, as the dev->mtu is already decreased */	
 #endif
@@ -433,9 +395,6 @@
 		iph = skb->nh.iph;
 	}
 
-	ip_ll_header(skb);
-
-
 	/*
 	 *	Do we need to fragment. Again this is inefficient.
 	 *	We need to somehow lock the original buffer and use
@@ -484,13 +443,9 @@
 		return;
 	}
 	
-	ip_fragment(skb, 1, skb->dst->output);
-
-
+	ip_fragment(skb, skb->dst->output);
 }
 
-
-
 /*
  *	Build and send a packet, with as little as one copy
  *
@@ -509,7 +464,6 @@
  *	the source IP address (may depend on the routing table), the 
  *	destination address (char *), the offset to copy from, and the
  *	length to be copied.
- * 
  */
 
 int ip_build_xmit(struct sock *sk,
@@ -518,7 +472,7 @@
 			       unsigned int,	
 			       unsigned int),
 		  const void *frag,
-		  unsigned short length,
+		  unsigned length,
 		  struct ipcm_cookie *ipc,
 		  struct rtable *rt,
 		  int flags)
@@ -528,7 +482,7 @@
 	int offset, mf;
 	unsigned short id;
 	struct iphdr *iph;
-	int hh_len = rt->u.dst.dev->hard_header_len;
+	int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
 	int nfrags=0;
 	struct ip_options *opt = ipc->opt;
 	int df = htons(IP_DF);
@@ -551,7 +505,7 @@
 
 	if (length <= rt->u.dst.pmtu && opt == NULL) {
 		int error;
-		struct sk_buff *skb=sock_alloc_send_skb(sk, length+15+hh_len,
+		struct sk_buff *skb=sock_alloc_send_skb(sk, length+hh_len+15,
 							0, flags&MSG_DONTWAIT, &error);
 		if(skb==NULL) {
 			ip_statistics.IpOutDiscards++;
@@ -561,8 +515,7 @@
 		skb->when=jiffies;
 		skb->priority = sk->priority;
 		skb->dst = dst_clone(&rt->u.dst);
-
-		ip_ll_header_reserve(skb);
+		skb_reserve(skb, hh_len);
 
 		skb->nh.iph = iph = (struct iphdr *)skb_put(skb, length);
 
@@ -592,7 +545,7 @@
 		if (err)
 			err = -EFAULT;
 
-		if(!err && call_out_firewall(PF_INET, skb->dev, iph, NULL, &skb) < FW_ACCEPT)
+		if(!err && call_out_firewall(PF_INET, rt->u.dst.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)
@@ -618,12 +571,10 @@
 		length -= sizeof(struct iphdr);
 
 	if (opt) {
-		fragheaderlen = hh_len + sizeof(struct iphdr) + opt->optlen;
+		fragheaderlen = sizeof(struct iphdr) + opt->optlen;
 		maxfraglen = ((rt->u.dst.pmtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
 	} else {
-		fragheaderlen = hh_len;
-		if(!sk->ip_hdrincl)
-			fragheaderlen += sizeof(struct iphdr);
+		fragheaderlen = sk->ip_hdrincl ? 0 : sizeof(struct iphdr);
 		
 		/*
 		 *	Fragheaderlen is the size of 'overhead' on each buffer. Now work
@@ -633,6 +584,9 @@
 		maxfraglen = ((rt->u.dst.pmtu-sizeof(struct iphdr)) & ~7) + fragheaderlen;
         }
 
+	if (length + fragheaderlen > 0xFFFF)
+		return -EMSGSIZE;
+
 	/*
 	 *	Start at the end of the frame by handling the remainder.
 	 */
@@ -689,7 +643,7 @@
 		 *	Get the memory we require with some space left for alignment.
 		 */
 
-		skb = sock_alloc_send_skb(sk, fraglen+15, 0, flags&MSG_DONTWAIT, &error);
+		skb = sock_alloc_send_skb(sk, fraglen+hh_len+15, 0, flags&MSG_DONTWAIT, &error);
 		if (skb == NULL) {
 			ip_statistics.IpOutDiscards++;
 			if(nfrags>1)
@@ -705,14 +659,13 @@
 		skb->when = jiffies;
 		skb->priority = sk->priority;
 		skb->dst = dst_clone(&rt->u.dst);
-
-		ip_ll_header_reserve(skb);
+		skb_reserve(skb, hh_len);
 
 		/*
 		 *	Find where to start putting bytes.
 		 */
 		 
-		data = skb_put(skb, fraglen-hh_len);
+		data = skb_put(skb, fraglen);
 		skb->nh.iph = iph = (struct iphdr *)data;
 
 		/*
@@ -762,7 +715,7 @@
 		 *	Account for the fragment.
 		 */
 		 
-		if(!err && !offset && call_out_firewall(PF_INET, skb->dev, iph, NULL, &skb) < FW_ACCEPT)
+		if(!err && !offset && call_out_firewall(PF_INET, rt->u.dst.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)
@@ -800,17 +753,14 @@
 
 /*
  *	This IP datagram is too large to be sent in one piece.  Break it up into
- *	smaller pieces (each of size equal to the MAC header plus IP header plus
+ *	smaller pieces (each of size equal to IP header plus
  *	a block of the data of the original IP data part) that will yet fit in a
  *	single device frame, and queue such a frame for sending.
  *
- *	Assumption: packet was ready for transmission, link layer header
- *	is already in.
- *
  *	Yes this is inefficient, feel free to submit a quicker one.
  */
- 
-void ip_fragment(struct sk_buff *skb, int local, int (*output)(struct sk_buff*))
+
+void ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
 {
 	struct iphdr *iph;
 	unsigned char *raw;
@@ -823,14 +773,14 @@
 	u16 dont_fragment;
 	struct rtable *rt = (struct rtable*)skb->dst;
 
-	dev = skb->dev;
+	dev = rt->u.dst.dev;
 
 	/*
 	 *	Point into the IP datagram header.
 	 */
 
-	raw = skb->data;
-	iph = skb->nh.iph;
+	raw = skb->nh.raw;
+	iph = (struct iphdr*)raw;
 
 	/*
 	 *	Setup starting values.
@@ -838,11 +788,7 @@
 
 	hlen = iph->ihl * 4;
 	left = ntohs(iph->tot_len) - hlen;	/* Space per frame */
-	hlen += skb->nh.raw - raw;
-	if (local)
-		mtu = rt->u.dst.pmtu - hlen;	/* Size of data space */
-	else
-		mtu = dev->mtu - hlen;
+	mtu = rt->u.dst.pmtu - hlen;	/* Size of data space */
 	ptr = raw + hlen;			/* Where to start from */
 
 	/*
@@ -891,7 +837,7 @@
 		 *	Allocate buffer.
 		 */
 
-		if ((skb2 = alloc_skb(len+hlen+15,GFP_ATOMIC)) == NULL) {
+		if ((skb2 = alloc_skb(len+hlen+dev->hard_header_len+15,GFP_ATOMIC)) == NULL) {
 			NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));
 			ip_statistics.IpFragFails++;
 			kfree_skb(skb, FREE_WRITE);
@@ -902,15 +848,13 @@
 		 *	Set up data on packet
 		 */
 
-		skb2->arp = skb->arp;
-		skb2->dev = skb->dev;
 		skb2->when = skb->when;
 		skb2->pkt_type = skb->pkt_type;
 		skb2->priority = skb->priority;
+		skb_reserve(skb2, (dev->hard_header_len+15)&~15);
 		skb_put(skb2, len + hlen);
-		skb2->mac.raw = (char *) skb2->data;
-		skb2->nh.raw = skb2->mac.raw + dev->hard_header_len;
-		skb2->h.raw = skb2->mac.raw + hlen;
+		skb2->nh.raw = skb2->data;
+		skb2->h.raw = skb2->data + hlen;
 
 		/*
 		 *	Charge the memory for the fragment to any owner
@@ -925,7 +869,7 @@
 		 *	Copy the packet header into the new buffer.
 		 */
 
-		memcpy(skb2->mac.raw, raw, hlen);
+		memcpy(skb2->nh.raw, raw, hlen);
 
 		/*
 		 *	Copy a block of the IP datagram.
@@ -963,7 +907,7 @@
 
 		ip_statistics.IpFragCreates++;
 
-		iph->tot_len = htons(len + hlen - dev->hard_header_len);
+		iph->tot_len = htons(len + hlen);
 
 		ip_send_check(iph);
 
@@ -1008,8 +952,7 @@
 
 	reply->priority = skb->priority;
 	reply->dst = &rt->u.dst;
-
-	ip_ll_header_reserve(reply);
+	skb_reserve(reply, (rt->u.dst.dev->hard_header_len+15)&~15);
 
 	/*
 	 *	Now build the IP header.
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v2.1.78/linux/net/ipv4/ip_sockglue.c	Fri Dec 19 15:53:06 1997
+++ linux/net/ipv4/ip_sockglue.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *
  *		The IP to API glue.
  *		
- * Version:	$Id: ip_sockglue.c,v 1.29 1997/11/28 15:32:39 alan Exp $
+ * Version:	$Id: ip_sockglue.c,v 1.30 1997/12/29 19:52:39 kuznet Exp $
  *
  * Authors:	see ip.c
  *
@@ -32,7 +32,6 @@
 #include <linux/igmp.h>
 #include <linux/firewall.h>
 #include <linux/ip_fw.h>
-#include <net/checksum.h>
 #include <linux/route.h>
 #include <linux/mroute.h>
 #include <net/route.h>
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/ipconfig.c linux/net/ipv4/ipconfig.c
--- v2.1.78/linux/net/ipv4/ipconfig.c	Mon Dec  1 12:04:16 1997
+++ linux/net/ipv4/ipconfig.c	Mon Jan 12 15:28:28 1998
@@ -1,5 +1,5 @@
 /*
- *  $Id: ipconfig.c,v 1.5 1997/10/27 16:08:02 mj Exp $
+ *  $Id: ipconfig.c,v 1.6 1998/01/09 17:19:46 mj Exp $
  *
  *  Automatic Configuration of IP -- use BOOTP or RARP or user-supplied
  *  information to configure own IP address and routes.
@@ -799,6 +799,9 @@
 				ic_bootp_string(root_server_path, ext+1, *ext, sizeof(root_server_path));
 			break;
 	}
+
+	if (ic_gateway == INADDR_NONE && b->relay_ip)
+		ic_gateway = b->relay_ip;
 }
 
 
@@ -868,6 +871,9 @@
 			}
 		}
 	}
+
+	if (ic_gateway == INADDR_NONE && b->relay_ip)
+		ic_gateway = b->relay_ip;
 }
 
 #endif
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c
--- v2.1.78/linux/net/ipv4/ipmr.c	Mon Dec  1 12:04:16 1997
+++ linux/net/ipv4/ipmr.c	Mon Jan 12 15:28:28 1998
@@ -9,7 +9,7 @@
  *	as published by the Free Software Foundation; either version
  *	2 of the License, or (at your option) any later version.
  *
- *	Version: $Id: ipmr.c,v 1.28 1997/10/30 00:43:16 davem Exp $
+ *	Version: $Id: ipmr.c,v 1.29 1997/12/13 21:52:55 kuznet Exp $
  *
  *	Fixes:
  *	Michael Chastain	:	Incorrect size of copying.
@@ -176,6 +176,7 @@
 		kfree(dev);
 		return NULL;
 	}
+	dev->iflink = 0;
 
 	if ((in_dev = inetdev_init(dev)) == NULL)
 		goto failure;
@@ -924,7 +925,7 @@
 			}
 			return -EADDRNOTAVAIL;
 		default:
-			return -EINVAL;
+			return -ENOIOCTLCMD;
 	}
 }
 
@@ -1095,7 +1096,6 @@
 	 * not mrouter) cannot join to more than one interface - it will
 	 * result in receiving multiple packets.
 	 */
-	ip_ll_header(skb2);
 	skb2->dst->output(skb2);
 }
 
@@ -1282,6 +1282,7 @@
 		kfree_skb(skb, FREE_READ);
 		return -EINVAL;
 	}
+	skb->mac.raw = skb->nh.raw;
 	skb_pull(skb, (u8*)encap - skb->data);
 	skb->nh.iph = (struct iphdr *)skb->data;
 	skb->dev = reg_dev;
@@ -1321,6 +1322,7 @@
 		kfree_skb(skb, FREE_READ);
 		return -EINVAL;
 	}
+	skb->mac.raw = skb->nh.raw;
 	skb_pull(skb, (u8*)encap - skb->data);
 	skb->nh.iph = (struct iphdr *)skb->data;
 	skb->dev = reg_dev;
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v2.1.78/linux/net/ipv4/raw.c	Mon Dec  1 12:04:16 1997
+++ linux/net/ipv4/raw.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *
  *		RAW - implementation of IP "raw" sockets.
  *
- * Version:	$Id: raw.c,v 1.32 1997/10/24 17:16:00 kuznet Exp $
+ * Version:	$Id: raw.c,v 1.33 1997/12/27 20:41:15 kuznet Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -255,13 +255,24 @@
 {
 	struct ipcm_cookie ipc;
 	struct rawfakehdr rfh;
-	struct rtable *rt;
+	struct rtable *rt = NULL;
 	int free = 0;
 	u32 daddr;
 	u8  tos;
 	int err;
 
-	if (len>65535)
+	/* This check is ONLY to check for arithmetic overflow
+	   on integer(!) len. Not more! Real check will be made
+	   in ip_build_xmit --ANK
+
+	   BTW socket.c -> af_*.c -> ... make multiple
+	   invalid conversions size_t -> int. We MUST repair it f.e.
+	   by replacing all of them with size_t and revise all
+	   the places sort of len += sizeof(struct iphdr)
+	   If len was ULONG_MAX-10 it would be cathastrophe  --ANK
+	 */
+
+	if (len < 0 || len > 0xFFFF)
 		return -EMSGSIZE;
 
 	/*
@@ -308,10 +319,6 @@
 		int tmp = ip_cmsg_send(msg, &ipc);
 		if (tmp)
 			return tmp;
-		if (ipc.opt && sk->ip_hdrincl) {
-			kfree(ipc.opt);
-			return -EINVAL;
-		}
 		if (ipc.opt)
 			free=1;
 	}
@@ -321,10 +328,19 @@
 
 	if (!ipc.opt)
 		ipc.opt = sk->opt;
-	if (ipc.opt && ipc.opt->srr) {
-		if (!daddr)
-			return -EINVAL;
-		daddr = ipc.opt->faddr;
+
+	if (ipc.opt) {
+		err = -EINVAL;
+		/* Linux does not mangle headers on raw sockets,
+		 * so that IP options + IP_HDRINCL is non-sense.
+		 */
+		if (sk->ip_hdrincl)
+			goto done;
+		if (ipc.opt->srr) {
+			if (!daddr)
+				goto done;
+			daddr = ipc.opt->faddr;
+		}
 	}
 	tos = RT_TOS(sk->ip_tos) | (sk->localroute || (msg->msg_flags&MSG_DONTROUTE));
 
@@ -337,30 +353,21 @@
 
 	err = ip_route_output(&rt, daddr, rfh.saddr, tos, ipc.oif);
 
-	if (err) {
-		if (free) kfree(ipc.opt);
-		return err;
-	}
+	if (err)
+		goto done;
 
-	if (rt->rt_flags&RTCF_BROADCAST && !sk->broadcast) {
-		if (free) kfree(ipc.opt);
-		ip_rt_put(rt);
-		return -EACCES;
-	}
+	err = -EACCES;
+	if (rt->rt_flags&RTCF_BROADCAST && !sk->broadcast)
+		goto done;
 
 	rfh.iov = msg->msg_iov;
 	rfh.saddr = rt->rt_src;
 	if (!ipc.addr)
 		ipc.addr = rt->rt_dst;
-	if(sk->ip_hdrincl)
-		err=ip_build_xmit(sk, raw_getrawfrag, &rfh, len, &ipc, rt, msg->msg_flags);
-	else {
-		if (len>65535-sizeof(struct iphdr))
-			err = -EMSGSIZE;
-		else
-			err=ip_build_xmit(sk, raw_getfrag, &rfh, len, &ipc, rt, msg->msg_flags);
-	}
+	err=ip_build_xmit(sk, sk->ip_hdrincl ? raw_getrawfrag : raw_getfrag,
+			  &rfh, len, &ipc, rt, msg->msg_flags);
 
+done:
 	if (free)
 		kfree(ipc.opt);
 	ip_rt_put(rt);
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.1.78/linux/net/ipv4/route.c	Fri Dec 19 15:53:06 1997
+++ linux/net/ipv4/route.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.34 1997/12/04 03:42:22 freitag Exp $
+ * Version:	$Id: route.c,v 1.36 1997/12/17 20:14:18 kuznet Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -88,24 +88,28 @@
 #define RTprint(a...)	printk(KERN_DEBUG a)
 
 static struct timer_list rt_flush_timer =
-	{ NULL, NULL, RT_FLUSH_DELAY, 0L, NULL };
+	{ NULL, NULL, 0, 0L, NULL };
+static struct timer_list rt_periodic_timer =
+	{ NULL, NULL, 0, 0L, NULL };
 
 /*
  *	Interface to generic destination cache.
  */
 
-static void ipv4_dst_destroy(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 *);
+static struct dst_entry * ipv4_negative_advice(struct dst_entry *);
 
 
 struct dst_ops ipv4_dst_ops =
 {
 	AF_INET,
+	__constant_htons(ETH_P_IP),
 	ipv4_dst_check,
 	ipv4_dst_reroute,
-	ipv4_dst_destroy
+	NULL,
+	ipv4_negative_advice
 };
 
 __u8 ip_tos2prio[16] = {
@@ -188,7 +192,7 @@
 				r->u.dst.window,
 				(int)r->u.dst.rtt, r->key.tos,
 				r->u.dst.hh ? atomic_read(&r->u.dst.hh->hh_refcnt) : -1,
-				r->u.dst.hh ? r->u.dst.hh->hh_uptodate : 0,
+				r->u.dst.hh ? (r->u.dst.hh->hh_output == ip_acct_output) : 0,
 				r->rt_spec_dst,
 				i);
 			sprintf(buffer+len,"%-127s\n",temp);
@@ -215,7 +219,7 @@
 }
 
 
-void ip_rt_check_expire()
+static void rt_check_expire(unsigned long dummy)
 {
 	int i;
 	static int rover;
@@ -262,6 +266,8 @@
 			rthp = &rth->u.rt_next;
 		}
 	}
+	rt_periodic_timer.expires = now + RT_GC_INTERVAL;
+	add_timer(&rt_periodic_timer);
 }
 
 static void rt_run_flush(unsigned long dummy)
@@ -365,54 +371,6 @@
 	end_bh_atomic();
 }
 
-static int rt_ll_bind(struct rtable *rt)
-{
-	struct neighbour *neigh;
-	struct hh_cache	*hh = NULL;
-
-	if (rt->u.dst.dev && rt->u.dst.dev->hard_header_cache) {
-		neigh = rt->u.dst.neighbour;
-		if (!neigh)
-			neigh = arp_find_neighbour(&rt->u.dst, 1);
-
-		if (neigh) {
-			rt->u.dst.neighbour = neigh;
-			for (hh=neigh->hh; hh; hh = hh->hh_next)
-				if (hh->hh_type == ETH_P_IP)
-					break;
-		}
-
-		if (!hh && (hh = kmalloc(sizeof(*hh), GFP_ATOMIC)) != NULL) {
-#if RT_CACHE_DEBUG >= 2
-			extern atomic_t hh_count;
-			atomic_inc(&hh_count);
-#endif
-			memset(hh, 0, sizeof(struct hh_cache));
-			hh->hh_type = ETH_P_IP;
-			atomic_set(&hh->hh_refcnt, 0);
-			hh->hh_next = NULL;
-			if (rt->u.dst.dev->hard_header_cache(&rt->u.dst, neigh, hh)) {
-				kfree(hh);
-#if RT_CACHE_DEBUG >= 2
-				atomic_dec(&hh_count);
-#endif
-				hh = NULL;
-			} else if (neigh) {
-				atomic_inc(&hh->hh_refcnt);
-				hh->hh_next = neigh->hh;
-				neigh->hh = hh;
-			}
-		}
-		if (hh)	{
-			atomic_inc(&hh->hh_refcnt);
-			rt->u.dst.hh = hh;
-			return hh->hh_uptodate;
-		}
-	}
-	return 0;
-}
-
-
 static struct rtable *rt_intern_hash(unsigned hash, struct rtable * rt, u16 protocol)
 {
 	struct rtable	*rth, **rthp;
@@ -444,6 +402,12 @@
 		rthp = &rth->u.rt_next;
 	}
 
+	/* Try to bind route ro arp only if it is output
+	   route or unicast forwarding path.
+	 */
+	if (rt->rt_type == RTN_UNICAST || rt->key.iif == 0)
+		arp_bind_neighbour(&rt->u.dst);
+
 	if (atomic_read(&rt_cache_size) >= RT_CACHE_MAX_SIZE)
 		rt_garbage_collect();
 
@@ -460,9 +424,6 @@
 	rt_hash_table[hash] = rt;
 	atomic_inc(&rt_cache_size);
 
-	if (protocol == ETH_P_IP)
-		rt_ll_bind(rt);
-
 	end_bh_atomic();
 	return rt;
 }
@@ -534,7 +495,11 @@
 				/* Gateway is different ... */
 				rt->rt_gateway = new_gw;
 
-				if (!rt_ll_bind(rt)) {
+				/* Redirect received -> path was valid */
+				dst_confirm(&rth->u.dst);
+
+				if (!arp_bind_neighbour(&rt->u.dst) ||
+				    !(rt->u.dst.neighbour->nud_state&NUD_VALID)) {
 					ip_rt_put(rt);
 					rt_free(rt);
 					break;
@@ -560,25 +525,21 @@
 #endif
 }
 
-
-void ip_rt_advice(struct rtable **rp, int advice)
+static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst)
 {
-	struct rtable *rt;
-
-	if (advice)
-		return;
+	struct rtable *rt = (struct rtable*)dst;
 
-	start_bh_atomic();
-	if ((rt = *rp) != NULL && (rt->rt_flags&RTCF_REDIRECTED)) {
+	if (rt != NULL) {
+		if (dst->obsolete || rt->rt_flags&RTCF_REDIRECTED) {
 #if RT_CACHE_DEBUG >= 1
-		printk(KERN_DEBUG "ip_rt_advice: redirect to %08x/%02x dropped\n", rt->rt_dst, rt->key.tos);
+			printk(KERN_DEBUG "ip_rt_advice: redirect to %08x/%02x dropped\n", rt->rt_dst, rt->key.tos);
 #endif
-		*rp = NULL;
-		ip_rt_put(rt);
-		rt_cache_flush(0);
+			ip_rt_put(rt);
+			rt_cache_flush(0);
+			return NULL;
+		}
 	}
-	end_bh_atomic();
-	return;
+	return dst;
 }
 
 /*
@@ -712,6 +673,9 @@
 					mtu = guess_mtu(old_mtu);
 				}
 				if (mtu < rth->u.dst.pmtu) {
+					/* New mtu received -> path was valid */
+					dst_confirm(&rth->u.dst);
+
 					rth->u.dst.pmtu = mtu;
 					est_mtu = mtu;
 				}
@@ -721,23 +685,9 @@
 	return est_mtu;
 }
 
-
-static void ipv4_dst_destroy(struct dst_entry * dst)
-{
-	struct rtable * rt = (struct rtable*)dst;
-	struct hh_cache * hh = rt->u.dst.hh;
-	rt->u.dst.hh = NULL;
-	if (hh && atomic_dec_and_test(&hh->hh_refcnt)) {
-#if RT_CACHE_DEBUG >= 2
-		extern atomic_t hh_count;
-		atomic_dec(&hh_count);
-#endif
-		kfree(hh);
-	}
-}
-
 static struct dst_entry * ipv4_dst_check(struct dst_entry * dst, u32 cookie)
 {
+	dst_release(dst);
 	return NULL;
 }
 
@@ -965,9 +915,9 @@
 
 	if (skb->protocol != __constant_htons(ETH_P_IP)) {
 		/* Not IP (i.e. ARP). Do not make route for invalid
-		 * destination or if it is redirected.
+		 * destination AND it is not translated destination.
 		 */
-		if (out_dev == in_dev && flags&RTCF_DOREDIRECT)
+		if (out_dev == in_dev && !(flags&RTCF_DNAT))
 			return -EINVAL;
 	}
 
@@ -1008,6 +958,17 @@
 	rth->rt_flags = flags;
 	rth->rt_type = res.type;
 
+#ifdef CONFIG_NET_FASTROUTE
+	if (netdev_fastroute && !(flags&(RTCF_NAT|RTCF_MASQ|RTCF_DOREDIRECT))) {
+		struct device *odev = rth->u.dst.dev;
+		if (odev != dev &&
+		    dev->accept_fastpath &&
+		    odev->mtu >= dev->mtu &&
+		    dev->accept_fastpath(dev, &rth->u.dst) == 0)
+			rth->rt_flags |= RTCF_FAST;
+	}
+#endif
+
 	skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth, ntohs(skb->protocol));
 	return 0;
 
@@ -1418,6 +1379,7 @@
 	u32 src = 0;
 	int err;
 	struct sk_buff *skb;
+	struct rta_cacheinfo ci;
 	u8  *o;
 
 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
@@ -1487,6 +1449,12 @@
 	RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &rt->u.dst.window);
 	RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &rt->u.dst.rtt);
 	RTA_PUT(skb, RTA_PREFSRC, 4, &rt->rt_spec_dst);
+	ci.rta_lastuse = jiffies - rt->u.dst.lastuse;
+	ci.rta_used = atomic_read(&rt->u.dst.refcnt);
+	ci.rta_clntref = atomic_read(&rt->u.dst.use);
+	ci.rta_expires = 0;
+	ci.rta_error = rt->u.dst.error;
+	RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci);
 	rtm->rtm_optlen = skb->tail - o;
 	if (rta->rta_iif) {
 #ifdef CONFIG_IP_MROUTE
@@ -1525,6 +1493,12 @@
 {
 	devinet_init();
 	ip_fib_init();
+	rt_periodic_timer.function = rt_check_expire;
+	/* All the timers, started at system startup tend
+	   to synchronize. Perturb it a bit.
+	 */
+	rt_periodic_timer.expires = jiffies + net_random()%RT_GC_INTERVAL + RT_GC_INTERVAL;
+	add_timer(&rt_periodic_timer);
 
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&(struct proc_dir_entry) {
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c
--- v2.1.78/linux/net/ipv4/sysctl_net_ipv4.c	Fri Dec 19 15:53:06 1997
+++ linux/net/ipv4/sysctl_net_ipv4.c	Mon Jan 12 15:28:28 1998
@@ -1,7 +1,7 @@
 /*
  * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem.
  *
- * $Id: sysctl_net_ipv4.c,v 1.22 1997/11/28 15:32:42 alan Exp $
+ * $Id: sysctl_net_ipv4.c,v 1.23 1997/12/13 21:52:57 kuznet Exp $
  *
  * Begun April 1, 1996, Mike Shaver.
  * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS]
@@ -28,16 +28,6 @@
 static int boolean_max = 1;
 #endif
 
-/* From arp.c */
-extern int sysctl_arp_res_time;
-extern int sysctl_arp_dead_res_time;
-extern int sysctl_arp_max_tries;
-extern int sysctl_arp_timeout;
-extern int sysctl_arp_check_interval;
-extern int sysctl_arp_confirm_interval;
-extern int sysctl_arp_confirm_timeout;
-extern int sysctl_arp_max_pings;
-
 /* From icmp.c */
 extern int sysctl_icmp_echo_ignore_all;
 extern int sysctl_icmp_echo_ignore_broadcasts;
@@ -120,24 +110,6 @@
 }
 
 ctl_table ipv4_table[] = {
-        {NET_IPV4_ARP_RES_TIME, "arp_res_time",
-	 &sysctl_arp_res_time, sizeof(int), 0644, NULL, &proc_dointvec},
-        {NET_IPV4_ARP_DEAD_RES_TIME, "arp_dead_res_time",
-         &sysctl_arp_dead_res_time, sizeof(int), 0644, NULL, &proc_dointvec},
-        {NET_IPV4_ARP_MAX_TRIES, "arp_max_tries",
-         &sysctl_arp_max_tries, sizeof(int), 0644, NULL, &proc_dointvec},
-        {NET_IPV4_ARP_MAX_PINGS, "arp_max_pings",
-         &sysctl_arp_max_pings, sizeof(int), 0644, NULL, &proc_dointvec},
-        {NET_IPV4_ARP_TIMEOUT, "arp_timeout",
-         &sysctl_arp_timeout, sizeof(int), 0644, NULL, &proc_dointvec},
-        {NET_IPV4_ARP_CHECK_INTERVAL, "arp_check_interval",
-         &sysctl_arp_check_interval, sizeof(int), 0644, NULL, &proc_dointvec},
-        {NET_IPV4_ARP_CONFIRM_INTERVAL, "arp_confirm_interval",
-         &sysctl_arp_confirm_interval, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-        {NET_IPV4_ARP_CONFIRM_TIMEOUT, "arp_confirm_timeout",
-         &sysctl_arp_confirm_timeout, sizeof(int), 0644, NULL,
-         &proc_dointvec},
         {NET_IPV4_TCP_HOE_RETRANSMITS, "tcp_hoe_retransmits",
          &sysctl_tcp_hoe_retransmits, sizeof(int), 0644, NULL,
          &proc_dointvec},
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.78/linux/net/ipv4/tcp.c	Mon Dec  1 12:04:16 1997
+++ linux/net/ipv4/tcp.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.75 1997/10/16 02:57:34 davem Exp $
+ * Version:	$Id: tcp.c,v 1.76 1997/12/30 19:43:17 kuznet Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -659,7 +659,7 @@
 			return put_user(amount, (int *)arg);
 		}
 		default:
-			return(-EINVAL);
+			return(-ENOIOCTLCMD);
 	};
 }
 
@@ -814,7 +814,7 @@
 			struct sk_buff *skb;
 
 			if (err)
-				return (err);
+				return -EFAULT;
 
 			/* Stop on errors. */
 			if (sk->err) {
@@ -950,7 +950,7 @@
 				skb->h.th->urg_ptr = ntohs(copy);
 			}
 
-			skb->csum = csum_partial_copy_from_user(from,
+			skb->csum = csum_and_copy_from_user(from,
 					skb_put(skb, copy), copy, 0, &err);
 
 			from += copy;
@@ -968,7 +968,7 @@
 	sk->err = 0;
 
 	if (err)
-		return (err);
+		return -EFAULT;
 
 	return copied;
 }
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.1.78/linux/net/ipv4/tcp_input.c	Mon Dec  1 12:04:16 1997
+++ linux/net/ipv4/tcp_input.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.64 1997/10/30 23:52:24 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.65 1997/12/13 21:52:58 kuznet Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -819,6 +819,8 @@
 	if (after(ack, tp->snd_nxt) || before(ack, tp->snd_una))
 		goto uninteresting_ack;
 
+	dst_confirm(sk->dst_cache);
+
 	/* If there is data set flag 1 */
 	if (len != th->doff*4) {
 		flag |= FLAG_DATA;
@@ -1087,6 +1089,7 @@
 	if (skb->seq == tp->rcv_nxt) {
 		/* Ok. In sequence. */
 queue_and_out:
+		dst_confirm(sk->dst_cache);
 		skb_queue_tail(&sk->receive_queue, skb);
 		tp->rcv_nxt = skb->end_seq;
 		tcp_ofo_queue(sk);
@@ -1387,6 +1390,9 @@
 			
 			skb_pull(skb,th->doff*4);
 			
+			/* DO NOT notify forward progress here.
+			 * It saves dozen of CPU instructions in fast path. --ANK
+			 */
 			skb_queue_tail(&sk->receive_queue, skb);
 			tp->rcv_nxt = skb->end_seq;
 
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.78/linux/net/ipv4/tcp_ipv4.c	Fri Jan  2 14:37:03 1998
+++ linux/net/ipv4/tcp_ipv4.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.76 1997/12/07 04:44:19 freitag Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.77 1997/12/13 21:53:00 kuznet Exp $
  *
  *		IPv4 specific functions
  *
@@ -48,7 +48,6 @@
 #include <linux/fcntl.h>
 #include <linux/random.h>
 #include <linux/ipsec.h>
-#include <linux/inet.h>
 
 #include <net/icmp.h>
 #include <net/tcp.h>
@@ -56,6 +55,8 @@
 
 #include <asm/segment.h>
 
+#include <linux/inet.h>
+
 extern int sysctl_tcp_sack;
 extern int sysctl_tcp_tsack;
 extern int sysctl_tcp_timestamps;
@@ -1467,14 +1468,6 @@
 
 int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
 {
-#ifdef CONFIG_FILTER
-	if (sk->filter)
-	{
-		if (sk_filter(skb, sk->filter_data, sk->filter))
-			return -EPERM;	/* Toss packet */
-	}
-#endif /* CONFIG_FILTER */
-
 	skb_set_owner_r(skb, sk);
 
 	/*
@@ -1610,6 +1603,10 @@
 	rt = (struct rtable*)skb->dst;
 
 	/* Force route checking if want_rewrite */
+	/* The idea is good, the implementation is disguisting.
+	   Well, if I made bind on this socket, you cannot randomly ovewrite
+	   its source address. --ANK
+	 */
 	if (want_rewrite) {
 		int tmp;
 		__u32 old_saddr = rt->rt_src;
@@ -1639,9 +1636,6 @@
 		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;
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.1.78/linux/net/ipv4/tcp_timer.c	Fri Dec 19 15:53:06 1997
+++ linux/net/ipv4/tcp_timer.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_timer.c,v 1.32 1997/12/08 07:03:29 freitag Exp $
+ * Version:	$Id: tcp_timer.c,v 1.33 1997/12/13 21:53:01 kuznet Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -156,10 +156,7 @@
 	if ((sk->state == TCP_ESTABLISHED &&
 	     tp->retransmits && (tp->retransmits % TCP_QUICK_TRIES) == 0) ||
 	    (sk->state != TCP_ESTABLISHED && tp->retransmits > sysctl_tcp_retries1)) {
-		/*	Attempt to recover if arp has changed (unlikely!) or
-		 *	a route has shifted (not supported prior to 1.3).
-		 */
-		ip_rt_advice((struct rtable**)&sk->dst_cache, 0);
+		dst_negative_advice(&sk->dst_cache);
 	}
 	
 	/* Have we tried to SYN too many times (repent repent 8)) */
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.1.78/linux/net/ipv4/udp.c	Fri Dec 19 15:53:06 1997
+++ linux/net/ipv4/udp.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *
  *		The User Datagram Protocol (UDP).
  *
- * Version:	$Id: udp.c,v 1.45 1997/12/04 03:55:17 freitag Exp $
+ * Version:	$Id: udp.c,v 1.47 1997/12/27 20:41:16 kuznet Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -620,7 +620,18 @@
 	u8  tos;
 	int err;
 
-	if (len>65535)
+	/* This check is ONLY to check for arithmetic overflow
+	   on integer(!) len. Not more! Real check will be made
+	   in ip_build_xmit --ANK
+
+	   BTW socket.c -> af_*.c -> ... make multiple
+	   invalid conversions size_t -> int. We MUST repair it f.e.
+	   by replacing all of them with size_t and revise all
+	   the places sort of len += sizeof(struct iphdr)
+	   If len was ULONG_MAX-10 it would be cathastrophe  --ANK
+	 */
+
+	if (len < 0 || len > 0xFFFF)
 		return -EMSGSIZE;
 
 	/* 
@@ -633,7 +644,6 @@
 	if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT))
 	  	return -EINVAL;
 
-
 	/*
 	 *	Get and verify the address. 
 	 */
@@ -653,13 +663,27 @@
 		ufh.uh.dest = usin->sin_port;
 		if (ufh.uh.dest == 0)
 			return -EINVAL;
-		/* XXX: is a one-behind cache for the dst_entry worth it? */
+		/* XXX: is a one-behind cache for the dst_entry worth it?
+
+		   Nope. ip_route_output is slower than nothing, but it
+		   is enough fast to forget about caching its results.
+		   Really, checking route validity in general case
+		   is not much faster complete lookup.
+		   It was main reason why I removed it from 2.1.
+		   The second reason was that idle sockets held
+		   a lot of stray destinations.		--ANK
+		 */
 	} else {
 		if (sk->state != TCP_ESTABLISHED)
 			return -EINVAL;
 		ufh.daddr = sk->daddr;
 		ufh.uh.dest = sk->dummy_th.dest;
-		rt = (struct rtable *)sk->dst_cache;
+
+		/*
+		   BUGGG Khm... And who will validate it? Fixing it fastly...
+		                                                        --ANK
+		 */
+		rt = (struct rtable *)dst_check(&sk->dst_cache, 0);
   	}
 
 	ipc.addr = sk->saddr;
@@ -689,7 +713,7 @@
 		tos |= 1;
 		rt = NULL; /* sorry */
 	}
-		
+
 	if (MULTICAST(daddr)) {
 		if (!ipc.oif)
 			ipc.oif = sk->ip_mc_index;
@@ -777,7 +801,7 @@
 		}
 
 		default:
-			return(-EINVAL);
+			return(-ENOIOCTLCMD);
 	}
 	return(0);
 }
diff -u --recursive --new-file v2.1.78/linux/net/ipv4/utils.c linux/net/ipv4/utils.c
--- v2.1.78/linux/net/ipv4/utils.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv4/utils.c	Mon Jan 12 15:28:28 1998
@@ -6,7 +6,7 @@
  *		Various kernel-resident INET utility functions; mainly
  *		for format conversion and debugging output.
  *
- * Version:	$Id: utils.c,v 1.5 1997/09/17 18:50:31 freitag Exp $
+ * Version:	$Id: utils.c,v 1.6 1997/12/13 21:53:03 kuznet Exp $
  *
  * Author:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *
@@ -89,24 +89,3 @@
 	return(htonl(l));
 }
 
-/* 
- * This enforces a rate limit: not more than one kernel message
- * every 5secs to make a denial-of-service attack impossible.
- *
- * All warning printk()s should be guarded by this function. 
- */ 
-int net_ratelimit(void)
-{
-	static unsigned long last_msg; 
-	static int missed; 
-	
-	if ((jiffies - last_msg) >= 5*HZ) {
-		if (missed)	
-			printk(KERN_WARNING "ipv4: (%d messages suppressed. Flood?)\n", missed);
-		missed = 0; 
-		last_msg = jiffies;
-		return 1;
-	}
-	missed++; 
-	return 0; 
-}
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/Config.in linux/net/ipv6/Config.in
--- v2.1.78/linux/net/ipv6/Config.in	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/Config.in	Mon Jan 12 15:28:28 1998
@@ -2,6 +2,13 @@
 # IPv6 configuration
 # 
 bool 'IPv6: enable EUI-64 token format' CONFIG_IPV6_EUI64
-bool 'IPv6: disable provided based addresses' CONFIG_IPV6_NO_PB
+if [ "$CONFIG_IPV6_EUI64" = "y" ]; then
+  bool 'IPv6: disable provided based addresses' CONFIG_IPV6_NO_PB
+fi
+if [ "$CONFIG_NETLINK" = "y" ]; then
+  if [ "$CONFIG_RTNETLINK" = "n" ]; then
+    bool 'IPv6: routing messages via old netlink' CONFIG_IPV6_NETLINK
+  fi
+fi
 #bool 'IPv6: flow policy support' CONFIG_RT6_POLICY
 #bool 'IPv6: firewall support' CONFIG_IPV6_FIREWALL
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.1.78/linux/net/ipv6/addrconf.c	Fri Dec 19 15:53:06 1997
+++ linux/net/ipv6/addrconf.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: addrconf.c,v 1.30 1997/12/09 17:12:47 freitag Exp $
+ *	$Id: addrconf.c,v 1.32 1997/12/27 20:41:18 kuznet Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -35,6 +35,9 @@
 #include <linux/route.h>
 #include <linux/inetdevice.h>
 #include <linux/init.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
 
 #include <linux/proc_fs.h>
 #include <net/sock.h>
@@ -47,6 +50,7 @@
 #include <net/addrconf.h>
 #include <net/ip.h>
 #include <linux/if_tunnel.h>
+#include <linux/rtnetlink.h>
 
 #include <asm/uaccess.h>
 
@@ -62,17 +66,12 @@
 /*
  *	Configured unicast address list
  */
-struct inet6_ifaddr		*inet6_addr_lst[IN6_ADDR_HSIZE];
-
-/*
- *	Hash list of configured multicast addresses
- */
-struct ifmcaddr6		*inet6_mcast_lst[IN6_ADDR_HSIZE];
+static struct inet6_ifaddr		*inet6_addr_lst[IN6_ADDR_HSIZE];
 
 /*
  *	AF_INET6 device list
  */
-struct inet6_dev		*inet6_dev_lst[IN6_ADDR_HSIZE];
+static struct inet6_dev		*inet6_dev_lst[IN6_ADDR_HSIZE];
 
 static atomic_t			addr_list_lock = ATOMIC_INIT(0);
 
@@ -83,12 +82,13 @@
 	0, 0, addrconf_verify
 };
 
-static int addrconf_ifdown(struct device *dev);
+static int addrconf_ifdown(struct device *dev, int how);
 
 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);
+static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
 
 int ipv6_addr_type(struct in6_addr *addr)
 {
@@ -157,6 +157,14 @@
 		memset(ndev, 0, sizeof(struct inet6_dev));
 
 		ndev->dev = dev;
+		ndev->nd_parms = neigh_parms_alloc(&nd_tbl);
+		if (ndev->nd_parms == NULL)
+			ndev->nd_parms = &nd_tbl.parms;
+#ifdef CONFIG_SYSCTL
+		else
+			ndev->nd_parms->sysctl_table =
+				neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+#endif
 		hash = ipv6_devindex_hash(dev->ifindex);
 		bptr = &inet6_dev_lst[hash];
 		iter = *bptr;
@@ -165,10 +173,25 @@
 			bptr = &iter->next;
 
 		*bptr = ndev;
+
 	}
 	return ndev;
 }
 
+static struct inet6_dev * ipv6_find_idev(struct device *dev)
+{
+	struct inet6_dev *idev;
+
+	if ((idev = ipv6_get_idev(dev)) == NULL) {
+		idev = ipv6_add_dev(dev);
+		if (idev == NULL)
+			return NULL;
+	}
+	if (dev->flags&IFF_UP)
+		ipv6_mc_up(idev);
+	return idev;
+}
+
 void addrconf_forwarding_on(void)
 {
 	struct inet6_dev *idev;
@@ -180,18 +203,7 @@
 			printk(KERN_DEBUG "dev %s\n", idev->dev->name);
 #endif
 
-			if (idev->dev->type == ARPHRD_ETHER) {
-				struct in6_addr maddr;
-
-#if ACONF_DEBUG >= 2
-				printk(KERN_DEBUG "joining all-routers\n");
-#endif
-				idev->router = 1;
-
-				/* Wrong. It is user level function. */
-				ipv6_addr_all_routers(&maddr);
-				ipv6_dev_mc_inc(idev->dev, &maddr);
-			}
+			idev->router = 1;
 		}
 	}
 }
@@ -244,11 +256,13 @@
 	return ifa;
 }
 
-void ipv6_del_addr(struct inet6_ifaddr *ifp)
+static void ipv6_del_addr(struct inet6_ifaddr *ifp)
 {
 	struct inet6_ifaddr *iter, **back;
 	int hash;
 
+	ipv6_ifa_notify(RTM_DELADDR, ifp);
+
 	if (atomic_read(&addr_list_lock)) {
 		ifp->flags |= ADDR_INVALID;
 		return;
@@ -399,33 +413,75 @@
  *	to the host.
  */
 
-struct inet6_ifaddr * ipv6_chk_addr(struct in6_addr *addr)
+struct inet6_ifaddr * ipv6_chk_addr(struct in6_addr *addr, struct device *dev, int nd)
 {
 	struct inet6_ifaddr * ifp;
 	u8 hash;
+	unsigned flags = 0;
+
+	if (!nd)
+		flags |= DAD_STATUS|ADDR_INVALID;
 
 	atomic_inc(&addr_list_lock);
 
 	hash = ipv6_addr_hash(addr);
 	for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
-		if (ipv6_addr_cmp(&ifp->addr, addr) == 0)
-			break;
+		if (ipv6_addr_cmp(&ifp->addr, addr) == 0 && !(ifp->flags&flags)) {
+			if (dev == NULL || ifp->idev->dev == dev ||
+			    !(ifp->scope&(IFA_LINK|IFA_HOST)))
+				break;
+		}
 	}
 
 	atomic_dec(&addr_list_lock);
-	return ifp;	
+	return ifp;
 }
 
+void addrconf_dad_failure(struct inet6_ifaddr *ifp)
+{
+	printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name);
+	del_timer(&ifp->timer);
+	ipv6_del_addr(ifp);
+}
+
+
 /* Join to solicited addr multicast group. */
 
 static void addrconf_join_solict(struct device *dev, struct in6_addr *addr)
 {
 	struct in6_addr maddr;
 
-	addrconf_addr_solict_mult(addr, &maddr);
+	if (dev->flags&(IFF_LOOPBACK|IFF_NOARP))
+		return;
+
+#ifndef CONFIG_IPV6_NO_PB
+	addrconf_addr_solict_mult_old(addr, &maddr);
+	ipv6_dev_mc_inc(dev, &maddr);
+#endif
+#ifdef CONFIG_IPV6_EUI64
+	addrconf_addr_solict_mult_new(addr, &maddr);
 	ipv6_dev_mc_inc(dev, &maddr);
+#endif
 }
 
+static void addrconf_leave_solict(struct device *dev, struct in6_addr *addr)
+{
+	struct in6_addr maddr;
+
+	if (dev->flags&(IFF_LOOPBACK|IFF_NOARP))
+		return;
+
+#ifndef CONFIG_IPV6_NO_PB
+	addrconf_addr_solict_mult_old(addr, &maddr);
+	ipv6_dev_mc_dec(dev, &maddr);
+#endif
+#ifdef CONFIG_IPV6_EUI64
+	addrconf_addr_solict_mult_new(addr, &maddr);
+	ipv6_dev_mc_dec(dev, &maddr);
+#endif
+}
+
+
 #ifdef CONFIG_IPV6_EUI64
 static int ipv6_generate_eui64(u8 *eui, struct device *dev)
 {
@@ -462,6 +518,7 @@
 	rtmsg.rtmsg_ifindex = dev->ifindex;
 	rtmsg.rtmsg_info = info;
 	rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF;
+	rtmsg.rtmsg_type = RTMSG_NEWROUTE;
 
 	/* Prevent useless cloning on PtP SIT.
 	   This thing is done here expecting that the whole
@@ -469,12 +526,8 @@
 	 */
 	if (dev->type == ARPHRD_SIT && (dev->flags&IFF_POINTOPOINT))
 		rtmsg.rtmsg_flags |= RTF_NONEXTHOP;
-	rtmsg.rtmsg_type = RTMSG_NEWROUTE;
 
 	ip6_route_add(&rtmsg, &err);
-
-	if (err)
-		printk(KERN_DEBUG "IPv6: error %d adding prefix route\n", err);
 }
 
 /* Create "default" multicast route to the interface */
@@ -482,7 +535,6 @@
 static void addrconf_add_mroute(struct device *dev)
 {
 	struct in6_rtmsg rtmsg;
-	struct rt6_info *rt;
 	int err;
 
 	memset(&rtmsg, 0, sizeof(rtmsg));
@@ -493,25 +545,12 @@
 	rtmsg.rtmsg_ifindex = dev->ifindex;
 	rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF;
 	rtmsg.rtmsg_type = RTMSG_NEWROUTE;
-
-	rt = ip6_route_add(&rtmsg, &err);
-
-	/*
-	 * Pedro makes interesting thing here, he attached
-	 * fake nexthop to multicast route.
-	 * It is trick to avoid cloning, ugly, but efficient. --ANK
-	 */
-
-	if (err)
-		printk(KERN_DEBUG "IPv6: error %d adding mroute\n", err);
-	else
-		rt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
+	ip6_route_add(&rtmsg, &err);
 }
 
 static void sit_route_add(struct device *dev)
 {
 	struct in6_rtmsg rtmsg;
-	struct rt6_info *rt;
 	int err;
 
 	memset(&rtmsg, 0, sizeof(rtmsg));
@@ -521,19 +560,10 @@
 
 	/* prefix length - 96 bytes "::d.d.d.d" */
 	rtmsg.rtmsg_dst_len	= 96;
-	rtmsg.rtmsg_flags	= RTF_UP;
+	rtmsg.rtmsg_flags	= RTF_UP|RTF_NONEXTHOP;
 	rtmsg.rtmsg_ifindex	= dev->ifindex;
 
-	rt = ip6_route_add(&rtmsg, &err);
-	
-	/* See comment in addrconf_add_mroute.
-	 * It is the same trick, but to avoid cloning for direct
-	 * sit routes i.e. IPv4 comaptible destinations.
-	 */
-	if (err)
-		printk(KERN_DEBUG "sit_route_add: error %d in route_add\n", err);
-	else
-		rt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
+	ip6_route_add(&rtmsg, &err);
 }
 
 static void addrconf_add_lroute(struct device *dev)
@@ -546,24 +576,16 @@
 
 static struct inet6_dev *addrconf_add_dev(struct device *dev)
 {
-	struct in6_addr maddr;
 	struct inet6_dev *idev;
 
-	if ((idev = ipv6_get_idev(dev)) == NULL) {
-		idev = ipv6_add_dev(dev);
-		if (idev == NULL)
-			return NULL;
-	}
+	if ((idev = ipv6_find_idev(dev)) == NULL)
+		return NULL;
 
 	/* Add default multicast route */
 	addrconf_add_mroute(dev);
 
 	/* Add link local route */
 	addrconf_add_lroute(dev);
-	
-	/* Join to all nodes multicast group. */
-	ipv6_addr_all_nodes(&maddr);
-	ipv6_dev_mc_inc(dev, &maddr);
 	return idev;
 }
 
@@ -613,9 +635,15 @@
 	 *	2) Configure prefixes with the auto flag set
 	 */
 
-	rt_expires = jiffies + valid_lft * HZ;
-	if (rt_expires < jiffies)
-		rt_expires = ~0;
+	/* Avoid arithemtic overflow. Really, we could
+	   save rt_expires in seconds, likely valid_lft,
+	   but it would require division in fib gc, that it
+	   not good.
+	 */
+	if (valid_lft >= 0x7FFFFFFF/HZ)
+		rt_expires = 0;
+	else
+		rt_expires = jiffies + valid_lft * HZ;
 
 	rt = rt6_lookup(&pinfo->prefix, NULL, dev, RTF_LINKRT);
 
@@ -660,9 +688,9 @@
 		return;
 
 ok:
-		ifp = ipv6_chk_addr(&addr);
+		ifp = ipv6_chk_addr(&addr, dev, 1);
 
-		if (ifp == NULL && valid_lft) {
+		if ((ifp == NULL || (ifp->flags&ADDR_INVALID)) && valid_lft) {
 			struct inet6_dev *in6_dev = ipv6_get_idev(dev);
 
 			if (in6_dev == NULL) {
@@ -670,8 +698,8 @@
 				return;
 			}
 
-			ifp = ipv6_add_addr(in6_dev, &addr,
-					    addr_type & IPV6_ADDR_SCOPE_MASK);
+			if (ifp == NULL)
+				ifp = ipv6_add_addr(in6_dev, &addr, addr_type & IPV6_ADDR_SCOPE_MASK);
 
 			if (ifp == NULL)
 				return;
@@ -687,9 +715,14 @@
 		}
 
 		if (ifp) {
+			int event = 0;
 			ifp->valid_lft = valid_lft;
 			ifp->prefered_lft = prefered_lft;
 			ifp->tstamp = jiffies;
+			if (ifp->flags & ADDR_INVALID)
+				event = RTM_NEWADDR;
+			ifp->flags &= ~(ADDR_DEPRECATED|ADDR_INVALID);
+			ipv6_ifa_notify(event, ifp);
 		}
 	}
 }
@@ -705,25 +738,26 @@
 	struct device *dev;
 	int err = -EINVAL;
 
-	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) {
-		err = -EFAULT;
+	rtnl_lock();
+
+	err = -EFAULT;
+	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
 		goto err_exit;
-	}
 
 	dev = dev_get_by_index(ireq.ifr6_ifindex);
 
-	if (dev == NULL) {
-		err = -ENODEV;
+	err = -ENODEV;
+	if (dev == NULL)
 		goto err_exit;
-	}
 
 	if (dev->type == ARPHRD_SIT) {
 		struct ifreq ifr;
 		mm_segment_t	oldfs;
 		struct ip_tunnel_parm p;
 
+		err = -EADDRNOTAVAIL;
 		if (!(ipv6_addr_type(&ireq.ifr6_addr) & IPV6_ADDR_COMPATv4))
-			return -EADDRNOTAVAIL;
+			goto err_exit;
 
 		memset(&p, 0, sizeof(p));
 		p.iph.daddr = ireq.ifr6_addr.s6_addr32[3];
@@ -747,27 +781,21 @@
 	}
 
 err_exit:
+	rtnl_unlock();
 	return err;
 }
 
 /*
  *	Manual configuration of address on an interface
  */
-int addrconf_add_ifaddr(void *arg)
+static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen)
 {
-	struct inet6_dev *idev;
-	struct in6_ifreq ireq;
 	struct inet6_ifaddr *ifp;
+	struct inet6_dev *idev;
 	struct device *dev;
 	int scope;
 	
-	if (!suser())
-		return -EPERM;
-	
-	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
-		return -EFAULT;
-
-	if ((dev = dev_get_by_index(ireq.ifr6_ifindex)) == NULL)
+	if ((dev = dev_get_by_index(ifindex)) == NULL)
 		return -ENODEV;
 	
 	if (!(dev->flags&IFF_UP))
@@ -776,49 +804,83 @@
 	if ((idev = addrconf_add_dev(dev)) == NULL)
 		return -ENOBUFS;
 
-	scope = ipv6_addr_scope(&ireq.ifr6_addr);
+	scope = ipv6_addr_scope(pfx);
 
-	if((ifp = ipv6_add_addr(idev, &ireq.ifr6_addr, scope)) == NULL)
+	if ((ifp = ipv6_add_addr(idev, pfx, scope)) == NULL)
 		return -ENOMEM;
 
-	ifp->prefix_len = ireq.ifr6_prefixlen;
+	ifp->prefix_len = plen;
 	ifp->flags |= ADDR_PERMANENT;
 
 	addrconf_dad_start(ifp);
 	return 0;
 }
 
-int addrconf_del_ifaddr(void *arg)
+static int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen)
 {
-	struct in6_ifreq ireq;
 	struct inet6_ifaddr *ifp;
+	struct inet6_dev *idev;
 	struct device *dev;
 	int scope;
-	struct inet6_dev *idev;
 	
-	if (!suser())
-		return -EPERM;
-	
-	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
-		return -EFAULT;
-
-	if ((dev = dev_get_by_index(ireq.ifr6_ifindex)) == NULL)
+	if ((dev = dev_get_by_index(ifindex)) == NULL)
 		return -ENODEV;
 
 	if ((idev = ipv6_get_idev(dev)) == NULL)
 		return -ENXIO;
 
-	scope = ipv6_addr_scope(&ireq.ifr6_addr);
+	scope = ipv6_addr_scope(pfx);
 
-	for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
-		if (ifp->scope == scope && 
-		    (!memcmp(&ireq.ifr6_addr, &ifp->addr, sizeof(struct in6_addr)))) {
+	for (ifp = idev->addr_list; ifp; ifp=ifp->if_next) {
+		if (ifp->scope == scope && ifp->prefix_len == plen &&
+		    (!memcmp(pfx, &ifp->addr, sizeof(struct in6_addr)))) {
 			ipv6_del_addr(ifp);
-			break;
+
+			/* If the last address is deleted administratively,
+			   disable IPv6 on this interface.
+			 */
+			   
+			if (idev->addr_list == NULL)
+				addrconf_ifdown(idev->dev, 1);
+			return 0;
 		}
 	}
+	return -EADDRNOTAVAIL;
+}
 
-	return 0;
+
+int addrconf_add_ifaddr(void *arg)
+{
+	struct in6_ifreq ireq;
+	int err;
+	
+	if (!suser())
+		return -EPERM;
+	
+	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+		return -EFAULT;
+
+	rtnl_lock();
+	err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen);
+	rtnl_unlock();
+	return err;
+}
+
+int addrconf_del_ifaddr(void *arg)
+{
+	struct in6_ifreq ireq;
+	int err;
+	
+	if (!suser())
+		return -EPERM;
+	
+	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+		return -EFAULT;
+
+	rtnl_lock();
+	err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen);
+	rtnl_unlock();
+	return err;
 }
 
 static void sit_add_v4_addrs(struct inet6_dev *idev)
@@ -843,7 +905,7 @@
 		if (ifp) {
 			ifp->flags |= ADDR_PERMANENT;
 			ifp->prefix_len = 128;
-			ip6_rt_addr_add(&ifp->addr, idev->dev);
+			ipv6_ifa_notify(RTM_NEWADDR, ifp);
 		}
 		return;
 	}
@@ -876,7 +938,7 @@
 				else
 					ifp->prefix_len = 96;
 				ifp->flags |= ADDR_PERMANENT;
-				ip6_rt_addr_add(&ifp->addr, dev);
+				ipv6_ifa_notify(RTM_NEWADDR, ifp);
 			}
 		}
         }
@@ -887,16 +949,13 @@
 	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) {
+	if ((idev = ipv6_find_idev(dev)) == NULL) {
 		printk(KERN_DEBUG "init loopback: add_dev failed\n");
 		return;
 	}
@@ -909,10 +968,9 @@
 	}
 
 	ifp->flags |= ADDR_PERMANENT;
+	ifp->prefix_len = 128;
 
-	err = ip6_rt_addr_add(&addr, dev);
-	if (err)
-		printk(KERN_DEBUG "init_loopback: error in route_add\n");
+	ipv6_ifa_notify(RTM_NEWADDR, ifp);
 }
 
 static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
@@ -932,7 +990,6 @@
 static void addrconf_dev_config(struct device *dev)
 {
 	struct in6_addr addr;
-	struct in6_addr maddr;
 	struct inet6_dev    * idev;
 
 	if (dev->type != ARPHRD_ETHER) {
@@ -965,16 +1022,8 @@
 	addrconf_add_linklocal(idev, &addr);
 #endif
 
-	if (ipv6_config.forwarding) {
+	if (ipv6_config.forwarding)
 		idev->router = 1;
-
-		/* It is wrong.
-		   It is routing daemon or radvd that must make it,
-		   rather than kernel.
-		 */
-		ipv6_addr_all_routers(&maddr);
-		ipv6_dev_mc_inc(dev, &maddr);
-	}
 }
 
 static void addrconf_sit_config(struct device *dev)
@@ -987,8 +1036,7 @@
 	 * our v4 addrs in the tunnel
 	 */
 
-	idev = ipv6_add_dev(dev);
-	if (idev == NULL) {
+	if ((idev = ipv6_find_idev(dev)) == NULL) {
 		printk(KERN_DEBUG "init sit: add_dev failed\n");
 		return;
 	}
@@ -1026,19 +1074,20 @@
 			break;
 		};
 
+#ifdef CONFIG_IPV6_NETLINK
 		rt6_sndmsg(RTMSG_NEWDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
+#endif
 		break;
 
 	case NETDEV_DOWN:
+	case NETDEV_UNREGISTER:
 		/*
-		 *	Remove all addresses from this interface
-		 *	and take the interface out of the list.
+		 *	Remove all addresses from this interface.
 		 */
-		if (addrconf_ifdown(dev) == 0) {
-#if 0
-			rt6_ifdown(dev);
-#endif
+		if (addrconf_ifdown(dev, event == NETDEV_UNREGISTER) == 0) {
+#ifdef CONFIG_IPV6_NETLINK
 			rt6_sndmsg(RTMSG_DELDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
+#endif
 		}
 
 		break;
@@ -1047,57 +1096,65 @@
 	return NOTIFY_OK;
 }
 
-static int addrconf_ifdown(struct device *dev)
+static int addrconf_ifdown(struct device *dev, int how)
 {
 	struct inet6_dev *idev, **bidev;
 	struct inet6_ifaddr *ifa, **bifa;
 	int i, hash;
 
-	start_bh_atomic();
+	rt6_ifdown(dev);
+	neigh_ifdown(&nd_tbl, dev);
 
-	hash = ipv6_devindex_hash(dev->ifindex);
-	bidev = &inet6_dev_lst[hash];
+	idev = ipv6_get_idev(dev);
+	if (idev == NULL)
+		return -ENODEV;
 
-	for (idev = inet6_dev_lst[hash]; idev; idev = idev->next) {
-		if (idev->dev == dev) {
-			*bidev = idev->next;
-			break;
-		}
-		bidev = &idev->next;
-	}
+	start_bh_atomic();
 
-	if (idev == NULL) {
-		end_bh_atomic();
+	/* Discard multicast list */
 
-		printk(KERN_DEBUG "addrconf_ifdown: invalid device %p\n",dev);
-		return -ENODEV;
-	}
+	if (how == 1)
+		ipv6_mc_destroy_dev(idev);
+	else
+		ipv6_mc_down(idev);
 
-	/*
-	 *	FIXME: clear multicast group membership
-	 */
+	/* Discard address list */
+
+	idev->addr_list = NULL;
 
 	/*
-	 *	clean addr_list
+	 * Clean addresses hash table
 	 */
 
 	for (i=0; i<16; i++) {
 		bifa = &inet6_addr_lst[i];
 
-		for (ifa=inet6_addr_lst[i]; ifa; ) {
+		while ((ifa = *bifa) != NULL) {
 			if (ifa->idev == idev) {
 				*bifa = ifa->lst_next;
 				del_timer(&ifa->timer);
+				ipv6_ifa_notify(RTM_DELADDR, ifa);
 				kfree(ifa);
-				ifa = *bifa;
 				continue;
 			}
 			bifa = &ifa->lst_next;
-			ifa = *bifa;
 		}
 	}
 
-	kfree(idev);
+	/* Delete device from device hash table (if unregistered) */
+
+	if (how == 1) {
+		hash = ipv6_devindex_hash(dev->ifindex);
+
+		for (bidev = &inet6_dev_lst[hash]; (idev=*bidev) != NULL; bidev = &idev->next) {
+			if (idev->dev == dev) {
+				*bidev = idev->next;
+				neigh_parms_release(&nd_tbl, idev->nd_parms);
+				kfree(idev);
+				break;
+			}
+		}
+	}
 	end_bh_atomic();
 	return 0;
 }
@@ -1123,12 +1180,9 @@
 	if (ifp->probes++ <= ipv6_config.rtr_solicits) {
 		struct in6_addr all_routers;
 
-		ipv6_addr_set(&all_routers,
-			      __constant_htonl(0xff020000U), 0, 0,
-			      __constant_htonl(0x2U));
+		ipv6_addr_all_routers(&all_routers);
 
-		ndisc_send_rs(ifp->idev->dev, &ifp->addr,
-			      &all_routers);
+		ndisc_send_rs(ifp->idev->dev, &ifp->addr, &all_routers);
 		
 		ifp->timer.function = addrconf_rs_timer;
 		ifp->timer.expires = (jiffies + 
@@ -1158,7 +1212,6 @@
  */
 static void addrconf_dad_start(struct inet6_ifaddr *ifp)
 {
-	static int rand_seed = 1;
 	struct device *dev;
 	unsigned long rand_num;
 
@@ -1177,15 +1230,12 @@
 		return;
 	}
 
-	if (rand_seed) {
-		rand_seed = 0;
-		nd_rand_seed = ifp->addr.s6_addr32[3];
-	}
+	net_srandom(ifp->addr.s6_addr32[3]);
 
 	ifp->probes = ipv6_config.dad_transmits;
 	ifp->flags |= DAD_INCOMPLETE;
 
-	rand_num = ipv6_random() % ipv6_config.rtr_solicit_delay;
+	rand_num = net_random() % ipv6_config.rtr_solicit_delay;
 
 	ifp->timer.function = addrconf_dad_timer;
 	ifp->timer.expires = jiffies + rand_num;
@@ -1215,9 +1265,14 @@
 
 	/* send a neighbour solicitation for our addr */
 	memset(&unspec, 0, sizeof(unspec));
-	addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
-
+#ifdef CONFIG_IPV6_EUI64
+	addrconf_addr_solict_mult_new(&ifp->addr, &mcaddr);
+	ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
+#endif
+#ifndef CONFIG_IPV6_NO_PB
+	addrconf_addr_solict_mult_old(&ifp->addr, &mcaddr);
 	ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
+#endif
 
 	ifp->timer.expires = jiffies + ipv6_config.rtr_solicit_interval;
 	add_timer(&ifp->timer);
@@ -1231,7 +1286,7 @@
 	 *	Configure the address for reception. Now it is valid.
 	 */
 
-	ip6_rt_addr_add(&ifp->addr, dev);
+	ipv6_ifa_notify(RTM_NEWADDR, ifp);
 
 	/* If added prefix is link local and forwarding is off,
 	   start sending router solicitations.
@@ -1242,9 +1297,7 @@
 	    (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
 		struct in6_addr all_routers;
 
-		ipv6_addr_set(&all_routers,
-			      __constant_htonl(0xff020000U), 0, 0,
-			      __constant_htonl(0x2U));
+		ipv6_addr_all_routers(&all_routers);
 
 		/*
 		 *	If a host as already performed a random delay
@@ -1319,31 +1372,221 @@
 
 	for (i=0; i < IN6_ADDR_HSIZE; i++) {
 		for (ifp=inet6_addr_lst[i]; ifp;) {
+			if (ifp->flags & ADDR_INVALID) {
+				struct inet6_ifaddr *bp = ifp;
+				ifp= ifp->lst_next;
+				ipv6_del_addr(bp);
+				continue;
+			}
 			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;
+				ifp= ifp->lst_next;
 				
 				if (age > bp->valid_lft)
 					ipv6_del_addr(bp);
+				else if (age > bp->prefered_lft) {
+					bp->flags |= ADDR_DEPRECATED;
+					ipv6_ifa_notify(0, bp);
+				}
 
 				continue;
 			}
-			ifp=ifp->lst_next;
+			ifp = ifp->lst_next;
 		}
 	}
 
 	addr_chk_timer.expires = jiffies + ADDR_CHECK_FREQUENCY;
-	add_timer(&addr_chk_timer);	
+	add_timer(&addr_chk_timer);
+}
+
+#ifdef CONFIG_RTNETLINK
+static int
+inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+	struct kern_ifa  *k_ifa = arg;
+	struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+	struct in6_addr *pfx;
+
+	pfx = NULL;
+	if (k_ifa->ifa_address)
+		pfx = k_ifa->ifa_address;
+	if (k_ifa->ifa_local) {
+		if (pfx && memcmp(pfx, k_ifa->ifa_local, sizeof(*pfx)))
+			return -EINVAL;
+		pfx = k_ifa->ifa_local;
+	}
+
+	return inet6_addr_del(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
+}
+
+static int
+inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+	struct kern_ifa  *k_ifa = arg;
+	struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+	struct in6_addr *pfx;
+
+	pfx = NULL;
+	if (k_ifa->ifa_address)
+		pfx = k_ifa->ifa_address;
+	if (k_ifa->ifa_local) {
+		if (pfx && memcmp(pfx, k_ifa->ifa_local, sizeof(*pfx)))
+			return -EINVAL;
+		pfx = k_ifa->ifa_local;
+	}
+
+	return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
+}
+
+static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
+			    pid_t pid, u32 seq, int event)
+{
+	struct ifaddrmsg *ifm;
+	struct nlmsghdr  *nlh;
+	struct ifa_cacheinfo ci;
+	unsigned char	 *b = skb->tail;
+
+	nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*ifm));
+	ifm = NLMSG_DATA(nlh);
+	ifm->ifa_family = AF_INET6;
+	ifm->ifa_prefixlen = ifa->prefix_len;
+	ifm->ifa_flags = ifa->flags & ~ADDR_INVALID;
+	ifm->ifa_scope = RT_SCOPE_UNIVERSE;
+	if (ifa->scope&IFA_HOST)
+		ifm->ifa_scope = RT_SCOPE_HOST;
+	else if (ifa->scope&IFA_LINK)
+		ifm->ifa_scope = RT_SCOPE_LINK;
+	else if (ifa->scope&IFA_SITE)
+		ifm->ifa_scope = RT_SCOPE_SITE;
+	ifm->ifa_index = ifa->idev->dev->ifindex;
+	RTA_PUT(skb, IFA_ADDRESS, 16, &ifa->addr);
+	if (!(ifa->flags&IFA_F_PERMANENT)) {
+		ci.ifa_prefered = ifa->prefered_lft;
+		ci.ifa_valid = ifa->valid_lft;
+		if (ci.ifa_prefered != 0xFFFFFFFF) {
+			long tval = (jiffies - ifa->tstamp)/HZ;
+			ci.ifa_prefered -= tval;
+			if (ci.ifa_valid != 0xFFFFFFFF)
+				ci.ifa_valid -= tval;
+		}
+		RTA_PUT(skb, IFA_CACHEINFO, sizeof(ci), &ci);
+	}
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+	skb_put(skb, b - skb->tail);
+	return -1;
+}
+
+static int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	int idx, ip_idx;
+	int s_idx, s_ip_idx;
+ 	struct inet6_ifaddr *ifa;
+
+	s_idx = cb->args[0];
+	s_ip_idx = ip_idx = cb->args[1];
+
+	for (idx=0; idx < IN6_ADDR_HSIZE; idx++) {
+		if (idx < s_idx)
+			continue;
+		if (idx > s_idx)
+			s_ip_idx = 0;
+		start_bh_atomic();
+		for (ifa=inet6_addr_lst[idx], ip_idx = 0; ifa;
+		     ifa = ifa->lst_next, ip_idx++) {
+			if (ip_idx < s_ip_idx)
+				continue;
+			if (inet6_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid,
+					      cb->nlh->nlmsg_seq, RTM_NEWADDR) <= 0) {
+				end_bh_atomic();
+				goto done;
+			}
+		}
+		end_bh_atomic();
+	}
+done:
+	cb->args[0] = idx;
+	cb->args[1] = ip_idx;
+
+	return skb->len;
 }
 
+static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa)
+{
+	struct sk_buff *skb;
+	int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128);
+
+	skb = alloc_skb(size, GFP_KERNEL);
+	if (!skb) {
+		netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFADDR, ENOBUFS);
+		return;
+	}
+	if (inet6_fill_ifaddr(skb, ifa, 0, 0, event) < 0) {
+		kfree_skb(skb, 0);
+		netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFADDR, EINVAL);
+		return;
+	}
+	NETLINK_CB(skb).dst_groups = RTMGRP_IPV6_IFADDR;
+	netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_IFADDR, GFP_ATOMIC);
+}
+
+static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX-RTM_BASE+1] =
+{
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			rtnetlink_dump_ifinfo,	},
+	{ NULL,			NULL,			},
+
+	{ inet6_rtm_newaddr,	NULL,			},
+	{ inet6_rtm_deladdr,	NULL,			},
+	{ NULL,			inet6_dump_ifaddr,	},
+	{ NULL,			NULL,			},
+
+	{ inet6_rtm_newroute,	NULL,			},
+	{ inet6_rtm_delroute,	NULL,			},
+	{ NULL,			inet6_dump_fib,		},
+	{ NULL,			NULL,			},
+
+	{ neigh_add,		NULL,			},
+	{ neigh_delete,		NULL,			},
+	{ NULL,			neigh_dump_info,	},
+	{ NULL,			NULL,			},
+
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+};
+#endif
+
+static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
+{
+#ifdef CONFIG_RTNETLINK
+	inet6_ifa_notify(event ? : RTM_NEWADDR, ifp);
+#endif
+	switch (event) {
+	case RTM_NEWADDR:
+		ip6_rt_addr_add(&ifp->addr, ifp->idev->dev);
+		break;
+	case RTM_DELADDR:
+		start_bh_atomic();
+		addrconf_leave_solict(ifp->idev->dev, &ifp->addr);
+		if (ipv6_chk_addr(&ifp->addr, ifp->idev->dev, 0) == NULL)
+			ip6_rt_addr_del(&ifp->addr, ifp->idev->dev);
+		end_bh_atomic();
+		break;
+	}
+}
+
+
 /*
  *	Init / cleanup code
  */
@@ -1352,19 +1595,7 @@
 {
 #ifdef MODULE
 	struct device *dev;
-#endif
-
-	/*
-	 *	init address and device hash lists
-	 */
-
-	memset(inet6_addr_lst, 0, IN6_ADDR_HSIZE * sizeof(struct inet6_ifaddr *));
-
-	memset(inet6_mcast_lst, 0, IN6_ADDR_HSIZE * sizeof(struct ifmcaddr6 *));
 
-	memset(inet6_dev_lst, 0, IN6_ADDR_HSIZE * sizeof(struct inet6_dev *));
-
-#ifdef MODULE
 	/* This takes sense only during module load. */
 
 	for (dev = dev_base; dev; dev = dev->next) {
@@ -1390,6 +1621,9 @@
 	
 	addr_chk_timer.expires = jiffies + ADDR_CHECK_FREQUENCY;
 	add_timer(&addr_chk_timer);
+#ifdef CONFIG_RTNETLINK
+	rtnetlink_links[AF_INET6] = inet6_rtnetlink_table;
+#endif
 }
 
 #ifdef MODULE
@@ -1399,6 +1633,11 @@
  	struct inet6_ifaddr *ifa;
 	int i;
 
+#ifdef CONFIG_RTNETLINK
+	rtnetlink_links[AF_INET6] = NULL;
+#endif
+	start_bh_atomic();
+
 	del_timer(&addr_chk_timer);
 
 	/*
@@ -1409,7 +1648,7 @@
 		struct inet6_dev *next;
 		for (idev = inet6_dev_lst[i]; idev; idev = next) {
 			next = idev->next;
-			addrconf_ifdown(idev->dev);	
+			addrconf_ifdown(idev->dev, 1);
 		}
 	}
 
@@ -1423,9 +1662,13 @@
 
 			bifa = ifa;
 			ifa = ifa->lst_next;
-			kfree(bifa);
+			printk(KERN_DEBUG "bug: IPv6 address leakage detected: ifa=%p\n", bifa);
+			/* Do not free it; something is wrong.
+			   Now we can investigate it with debugger.
+			 */
 		}
 	}
+	end_bh_atomic();
 
 #ifdef CONFIG_PROC_FS
 	proc_net_unregister(iface_proc_entry.low_ino);
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.1.78/linux/net/ipv6/af_inet6.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/af_inet6.c	Mon Jan 12 15:28:28 1998
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/af_inet.c
  *
- *	$Id: af_inet6.c,v 1.23 1997/10/29 20:27:52 kuznet Exp $
+ *	$Id: af_inet6.c,v 1.24 1997/12/13 21:53:08 kuznet Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -209,7 +209,7 @@
 			 */
 			v4addr = LOOPBACK4_IPV6;
 			if (!(addr_type & IPV6_ADDR_MULTICAST))	{
-				if (ipv6_chk_addr(&addr->sin6_addr) == NULL)
+				if (ipv6_chk_addr(&addr->sin6_addr, NULL, 0) == NULL)
 					return(-EADDRNOTAVAIL);
 			}
 		}
@@ -282,7 +282,7 @@
 static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
 	struct sock *sk = sock->sk;
-	int err;
+	int err = -EINVAL;
 	int pid;
 
 	switch(cmd) 
@@ -318,47 +318,6 @@
 	  
 		return(ipv6_route_ioctl(cmd,(void *)arg));
 
-	case SIOCGIFCONF:
-	case SIOCGIFFLAGS:
-	case SIOCSIFFLAGS:
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-/*
-
-  this ioctls deal with addresses
-  must process the addr info before
-  calling dev_ioctl to perform dev specific functions
-
-	case SIOCGIFADDR:
-	case SIOCSIFADDR:
-
-
-	case SIOCGIFDSTADDR:
-
-	case SIOCGIFBRDADDR:
-	case SIOCSIFBRDADDR:
-	case SIOCGIFNETMASK:
-	case SIOCSIFNETMASK:
-	*/
-
-	case SIOCGIFMETRIC:
-	case SIOCSIFMETRIC:
-	case SIOCGIFMEM:
-	case SIOCSIFMEM:
-	case SIOCGIFMTU:
-	case SIOCSIFMTU:
-	case SIOCSIFLINK:
-	case SIOCGIFHWADDR:
-	case SIOCSIFHWADDR:
-	case SIOCSIFMAP:
-	case SIOCGIFMAP:
-	case SIOCSIFSLAVE:
-	case SIOCGIFSLAVE:
-	case SIOCGIFINDEX:
-	case SIOCGIFNAME:
-	case SIOCGIFCOUNT:
-		return(dev_ioctl(cmd,(void *) arg));		
-		
 	case SIOCSIFADDR:
 		return addrconf_add_ifaddr((void *) arg);
 	case SIOCDIFADDR:
@@ -370,9 +329,9 @@
 		    (cmd <= (SIOCDEVPRIVATE + 15)))
 			return(dev_ioctl(cmd,(void *) arg));
 		
-		if (sk->prot->ioctl==NULL) 
-			return(-EINVAL);
-		return(sk->prot->ioctl(sk, cmd, arg));
+		if(sk->prot->ioctl==0 || (err=sk->prot->ioctl(sk, cmd, arg))==-ENOIOCTLCMD)
+			return(dev_ioctl(cmd,(void *) arg));		
+		return err;
 	}
 	/*NOTREACHED*/
 	return(0);
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/datagram.c linux/net/ipv6/datagram.c
--- v2.1.78/linux/net/ipv6/datagram.c	Thu May 15 16:48:06 1997
+++ linux/net/ipv6/datagram.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: datagram.c,v 1.12 1997/05/15 18:55:09 davem Exp $
+ *	$Id: datagram.c,v 1.13 1997/12/13 21:53:09 kuznet Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -89,7 +89,7 @@
 			if (!ipv6_addr_any(&src_info->ipi6_addr)) {
 				struct inet6_ifaddr *ifp;
 
-				ifp = ipv6_chk_addr(&src_info->ipi6_addr);
+				ifp = ipv6_chk_addr(&src_info->ipi6_addr, *src_dev, 0);
 
 				if (ifp == NULL) {
 					err = -EINVAL;
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/icmp.c linux/net/ipv6/icmp.c
--- v2.1.78/linux/net/ipv6/icmp.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/icmp.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: icmp.c,v 1.11 1997/09/20 20:48:26 davem Exp $
+ *	$Id: icmp.c,v 1.12 1997/12/13 21:53:10 kuznet Exp $
  *
  *	Based on net/ipv4/icmp.c
  *
@@ -179,7 +179,7 @@
 	
 	addr_type = ipv6_addr_type(&hdr->daddr);
 
-	if (ipv6_chk_addr(&hdr->daddr))
+	if (ipv6_chk_addr(&hdr->daddr, NULL, 0))
 		saddr = &hdr->daddr;
 
 	/*
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/ip6_fib.c linux/net/ipv6/ip6_fib.c
--- v2.1.78/linux/net/ipv6/ip6_fib.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/ip6_fib.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_fib.c,v 1.9 1997/09/20 20:48:27 davem Exp $
+ *	$Id: ip6_fib.c,v 1.10 1997/12/13 21:53:10 kuznet Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -182,6 +182,16 @@
 	kfree(fn);
 }
 
+extern __inline__ void rt6_release(struct rt6_info *rt)
+{
+	struct dst_entry *dst = (struct dst_entry *) rt;
+	if (atomic_dec_and_test(&dst->refcnt)) {
+		rt->rt6i_node = NULL;
+		dst_free(dst);
+	}
+}
+
+
 /*
  *	Routing Table
  *
@@ -409,8 +419,12 @@
 			if ((iter->rt6i_dev == rt->rt6i_dev) &&
 			    (iter->rt6i_flowr == rt->rt6i_flowr) &&
 			    (ipv6_addr_cmp(&iter->rt6i_gateway,
-					   &rt->rt6i_gateway) == 0))
+					   &rt->rt6i_gateway) == 0)) {
+				if (rt->rt6i_expires == 0 ||
+				    rt->rt6i_expires - iter->rt6i_expires > 0)
+					rt->rt6i_expires = iter->rt6i_expires;
 				return -EEXIST;
+			}
 		}
 
 		if (iter->rt6i_metric > rt->rt6i_metric)
@@ -426,6 +440,9 @@
 	*ins = rt;
 	rt->u.next = iter;
 	atomic_inc(&rt->rt6i_ref);
+#ifdef CONFIG_RTNETLINK
+	inet6_rt_notify(RTM_NEWROUTE, rt);
+#endif
 	rt6_stats.fib_rt_entries++;
 
 	if ((fn->fn_flags & RTN_RTINFO) == 0) {
@@ -513,6 +530,8 @@
 	if (err == 0)
 		fib6_start_gc(rt);
 out:
+	if (err)
+		dst_free(&rt->u.dst);
 	return err;
 }
 
@@ -782,7 +801,11 @@
 				 */
 
 				*back = lf->u.next;
+#ifdef CONFIG_RTNETLINK
+				inet6_rt_notify(RTM_DELROUTE, lf);
+#endif			
 				rt6_release(lf);
+				rt6_stats.fib_rt_entries--;
 				return fn;
 			}
 			back = &lf->u.next;
@@ -810,14 +833,19 @@
 /*
  *	Tree transversal function
  *
+ *	Wau... It is NOT REENTERABLE!!!!!!! It is cathastrophe. --ANK
  */
 
+int fib6_walk_count;
+
 void fib6_walk_tree(struct fib6_node *root, f_pnode func, void *arg,
 		    int filter)
 {
 	struct fib6_node *fn;
 
 	fn = root;
+
+	fib6_walk_count++;
 	
 	do {
 		if (!(fn->fn_flags & RTN_TAG)) {
@@ -858,6 +886,8 @@
 		} while (!(fn->fn_flags & RTN_TAG));
 
 	} while (!(fn->fn_flags & RTN_ROOT) || (fn->fn_flags & RTN_TAG));
+
+	fib6_walk_count--;
 }
 
 /*
@@ -884,6 +914,10 @@
 				*back = rt;
 
 				old->rt6i_node = NULL;
+#ifdef CONFIG_RTNETLINK
+				inet6_rt_notify(RTM_DELROUTE, old);
+#endif
+				old->u.dst.obsolete = 1;
 				rt6_release(old);
 				rt6_stats.fib_rt_entries--;
 				continue;
@@ -893,7 +927,28 @@
 
 		/*
 		 *	check addrconf expiration here.
+		 *
+		 *	BUGGGG Crossing fingers and ...
+		 *	Seems, radix tree walking is absolutely broken,
+		 *	but we will try in any case --ANK
 		 */
+		if (rt->rt6i_expires && now - rt->rt6i_expires < 0) {
+			struct rt6_info *old;
+
+			old = rt;
+			rt = rt->u.next;
+
+			*back = rt;
+
+			old->rt6i_node = NULL;
+#ifdef CONFIG_RTNETLINK
+			inet6_rt_notify(RTM_DELROUTE, old);
+#endif
+			old->u.dst.obsolete = 1;
+			rt6_release(old);
+			rt6_stats.fib_rt_entries--;
+			continue;
+		}
 		back = &rt->u.next;
 		rt = rt->u.next;
 	}
@@ -994,7 +1049,10 @@
 		0
 	};
 
-	fib6_walk_tree(&ip6_routing_table, fib6_garbage_collect, &arg, 0);
+	if (fib6_walk_count == 0)
+		fib6_walk_tree(&ip6_routing_table, fib6_garbage_collect, &arg, 0);
+	else
+		arg.more = 1;
 
 	if (arg.more) {
 		ip6_fib_timer.expires = jiffies + ipv6_config.rt_gc_period;
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/ip6_fw.c linux/net/ipv6/ip6_fw.c
--- v2.1.78/linux/net/ipv6/ip6_fw.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/ip6_fw.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_fw.c,v 1.7 1997/10/06 23:09:54 davem Exp $
+ *	$Id: ip6_fw.c,v 1.8 1997/12/13 21:53:11 kuznet Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -302,6 +302,7 @@
 	rtmsg.rtmsg_flags = RTF_NONEXTHOP|RTF_POLICY;
 	rt = ip6_route_add(&rtmsg, &err);
 
+	/* BUGGGG! rt can point to nowhere. */
 	if (rt == NULL) {
 		ip6_fwrule_free(rl);
 		return -ENOMEM;
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
--- v2.1.78/linux/net/ipv6/ip6_output.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/ip6_output.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_output.c,v 1.5 1997/09/21 18:33:14 kuznet Exp $
+ *	$Id: ip6_output.c,v 1.7 1997/12/29 19:52:46 kuznet Exp $
  *
  *	Based on linux/net/ipv4/ip_output.c
  *
@@ -35,32 +35,33 @@
 
 static u32	ipv6_fragmentation_id = 1;
 
-static void ipv6_build_mac_hdr(struct sk_buff *skb, struct dst_entry *dst,
-			       int len)
+int ip6_output(struct sk_buff *skb)
 {
-	struct device *dev;
-	
-	
-	dev = dst->dev;
+	struct dst_entry *dst = skb->dst;
+	struct device *dev = dst->dev;
+	struct hh_cache *hh = dst->hh;
 
-	skb->arp = 1;
-	
-	if (dev->hard_header) {
-		int mac;
+	skb->protocol = __constant_htons(ETH_P_IPV6);
+	skb->dev = dev;
 
-		/* Maybe when Alexey has done his new magic I'll hack this
-		   it seems to be worth 1-2% on IPv4 */
-#if 0
-		if (dst->hh)
-			hh_copy_header(dst->hh, skb);
+	if (hh) {
+#ifdef __alpha__
+		/* Alpha has disguisting memcpy. Help it. */
+	        u64 *aligned_hdr = (u64*)(skb->data - 16);
+		u64 *aligned_hdr0 = hh->hh_data;
+		aligned_hdr[0] = aligned_hdr0[0];
+		aligned_hdr[1] = aligned_hdr0[1];
+#else
+		memcpy(skb->data - 16, hh->hh_data, 16);
 #endif
-		mac = dev->hard_header(skb, dev, ETH_P_IPV6, NULL, NULL, len);
-
-		if (mac < 0)
-			skb->arp = 0;
-	}
-	
-	skb->mac.raw = skb->data;
+	        skb_push(skb, dev->hard_header_len);
+		return hh->hh_output(skb);
+	} else if (dst->neighbour)
+		return dst->neighbour->output(skb);
+
+	printk(KERN_DEBUG "khm\n");
+	kfree_skb(skb, FREE_WRITE);
+	return -EINVAL;
 }
 
 /*
@@ -78,14 +79,15 @@
 
 	hdr = skb->nh.ipv6h;
 
-	if (sk)
+	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 (sk->dst_cache) {
+			/*
+			 *	dst_check returns NULL if route is no longer valid
+			 */
+			dst = dst_check(&sk->dst_cache, np->dst_cookie);
+		}
 	}
 
 	if (dst == NULL) {
@@ -95,24 +97,15 @@
 			/*
 			 *	NETUNREACH usually
 			 */
+			dst_release(dst);
 			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
 	 */
@@ -135,9 +128,10 @@
 	ipv6_statistics.Ip6OutRequests++;
 	dst->output(skb);
 
-	if (sk)
-		ip6_dst_store(sk, dst);
-	else
+	if (sk) {
+		if (sk->dst_cache == NULL)
+			ip6_dst_store(sk, dst);
+	} else
 		dst_release(dst);
 
 	return 0;
@@ -163,8 +157,6 @@
 
 	totlen = len + sizeof(struct ipv6hdr);
 
-	skb->mac.raw = skb->data;
-
 	hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
 	skb->nh.ipv6h = hdr;
 
@@ -211,7 +203,7 @@
 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)
+			 int hlimit, int flags, unsigned length)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct ipv6hdr *hdr;
@@ -245,8 +237,6 @@
 		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:
@@ -255,6 +245,8 @@
 
 	frag_len = (dst->pmtu - unfrag_len) & ~0x7;
 
+	nfrags = payl_len / frag_len;
+
 	/*
 	 *	We must send from end to start because of 
 	 *	UDP/ICMP checksums. We do a funny trick:
@@ -281,18 +273,9 @@
 		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);
-	}
+	skb_reserve(last_skb, (dst->dev->hard_header_len + 15) & ~15);
 	
 	hdr = (struct ipv6hdr *) skb_put(last_skb, sizeof(struct ipv6hdr));
 	last_skb->nh.ipv6h = hdr;
@@ -335,7 +318,9 @@
 			
 			struct frag_hdr *fhdr2;
 				
+#if 0
 			printk(KERN_DEBUG "sending frag %d\n", nfrags);
+#endif
 			skb = skb_copy(last_skb, sk->allocation);
 
 			if (skb == NULL)
@@ -370,7 +355,9 @@
 		return -EFAULT;
 	}
 
+#if 0
 	printk(KERN_DEBUG "sending last frag \n");
+#endif
 
 	hdr->payload_len = htons(unfrag_len + last_len - 
 				 sizeof(struct ipv6hdr));
@@ -383,18 +370,6 @@
 	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);
 
@@ -402,7 +377,7 @@
 }
 
 int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
-		   struct flowi *fl, unsigned short length,
+		   struct flowi *fl, unsigned length,
 		   struct ipv6_options *opt, int hlimit, int flags)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
@@ -419,8 +394,8 @@
 
 	dst = NULL;
 	
-	if (np->dst)
-		dst = dst_check(&np->dst, np->dst_cookie);
+	if (sk->dst_cache)
+		dst = dst_check(&sk->dst_cache, np->dst_cookie);
 
 	if (dst == NULL)
 		dst = ip6_route_output(sk, fl);
@@ -456,6 +431,16 @@
 		pktlength += sizeof(struct ipv6hdr);
 		if (opt)
 			pktlength += opt->opt_flen + opt->opt_nflen;
+
+		/* Due to conservative check made by caller,
+		   pktlength cannot overflow here.
+
+		   When (and if) jumbo option will be implemented
+		   we could try soemething sort of:
+
+		   if (pktlength < length) return -EMSGSIZE;
+
+		*/
 	}
 
 	if (pktlength <= dst->pmtu) {
@@ -475,15 +460,9 @@
 		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);
-		}
+		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
 
 		hdr = (struct ipv6hdr *) skb->tail;
 		skb->nh.ipv6h = hdr;
@@ -516,9 +495,18 @@
 	} else {
 		if (sk->ip_hdrincl)
 			return -EMSGSIZE;
-		
+
+		/* pktlength includes IPv6 header, not included
+		   in IPv6 payload length.
+		   FIXME are non-fragmentable options included
+		   in packet after defragmentation? If not, we
+		   should subtract opt_nflen also. --ANK
+		 */
+		if (pktlength > 0xFFFF + sizeof(struct ipv6hdr))
+			return -EMSGSIZE;
+
 		err = ip6_frag_xmit(sk, getfrag, data, dst, fl, opt, hlimit,
-				    flags, pktlength);
+				    flags, length);
 	}
 	
 	/*
@@ -526,7 +514,7 @@
 	 */
   out:
 	
-	if (np->dst)
+	if (sk->dst_cache)
 		ip6_dst_store(sk, dst);
 	else
 		dst_release(dst);
@@ -569,7 +557,7 @@
 	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;
+		struct neighbour *n = dst->neighbour;
 
 		/*
 		 *	incoming and outgoing devices are the same
@@ -578,7 +566,7 @@
 
 		rt = (struct rt6_info *) dst;
 		if ((rt->rt6i_flags & RTF_GATEWAY))
-			target = &ndn->ndn_addr;
+			target = (struct in6_addr*)&n->primary_key;
 		else
 			target = &hdr->daddr;
 
@@ -593,41 +581,12 @@
 		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);
+	if (skb_headroom(skb) < dst->dev->hard_header_len || skb_cloned(skb)) {
+		struct sk_buff *skb2;
+		skb2 = skb_realloc_headroom(skb, (dst->dev->hard_header_len + 15)&~15);
+		kfree_skb(skb, FREE_WRITE);
+		skb = skb2;
 	}
-
-	ipv6_build_mac_hdr(skb, dst, size);
-
-	if (dst->neighbour)
-		ndisc_event_send(dst->neighbour, skb);
 
 	ipv6_statistics.Ip6ForwDatagrams++;
 	dst->output(skb);
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c
--- v2.1.78/linux/net/ipv6/ipv6_sockglue.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/ipv6_sockglue.c	Mon Jan 12 15:28:28 1998
@@ -7,7 +7,7 @@
  *
  *	Based on linux/net/ipv4/ip_sockglue.c
  *
- *	$Id: ipv6_sockglue.c,v 1.15 1997/10/29 20:27:54 kuznet Exp $
+ *	$Id: ipv6_sockglue.c,v 1.16 1997/12/13 21:53:13 kuznet Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -166,7 +166,7 @@
 		} else {
 			struct inet6_ifaddr *ifp;
 
-			ifp = ipv6_chk_addr(&addr);
+			ifp = ipv6_chk_addr(&addr, NULL, 0);
 
 			if (ifp == NULL) {
 				retv = -EADDRNOTAVAIL;
@@ -182,39 +182,16 @@
 	case IPV6_DROP_MEMBERSHIP:
 	{
 		struct ipv6_mreq mreq;
-		struct device *dev = NULL;
 		int err;
 
 		err = copy_from_user(&mreq, optval, sizeof(struct ipv6_mreq));
 		if(err)
 			return -EFAULT;
 		
-		if (mreq.ipv6mr_ifindex == 0) {
-#if 0
-			struct in6_addr mcast;
-			struct dest_entry *dc;
-
-			ipv6_addr_set(&mcast, __constant_htonl(0xff000000),
-				      0, 0, 0);
-			dc = ipv6_dst_route(&mcast, NULL, 0);
-
-			if (dc)
-			{
-				dev = dc->rt.rt_dev;
-				ipv6_dst_unlock(dc);
-			}
-#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);
+			retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
 		else
-			retv = ipv6_sock_mc_drop(sk, dev, &mreq.ipv6mr_multiaddr);
+			retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
 	}
 	};
 
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/mcast.c linux/net/ipv6/mcast.c
--- v2.1.78/linux/net/ipv6/mcast.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/mcast.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: mcast.c,v 1.11 1997/10/29 20:27:50 kuznet Exp $
+ *	$Id: mcast.c,v 1.13 1998/01/04 15:28:31 mj Exp $
  *
  *	Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c 
  *
@@ -16,6 +16,7 @@
  */
 
 #define __NO_VERSION__
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
@@ -28,6 +29,7 @@
 #include <linux/if_arp.h>
 #include <linux/route.h>
 #include <linux/init.h>
+#include <linux/proc_fs.h>
 
 #include <net/sock.h>
 #include <net/snmp.h>
@@ -37,6 +39,7 @@
 #include <net/if_inet6.h>
 #include <net/ndisc.h>
 #include <net/addrconf.h>
+#include <net/ip6_route.h>
 
 #include <net/checksum.h>
 
@@ -59,27 +62,24 @@
 #define IGMP6_UNSOLICITED_IVAL	(10*HZ)
 
 /*
+ *	Hash list of configured multicast addresses
+ */
+static struct ifmcaddr6		*inet6_mcast_lst[IN6_ADDR_HSIZE];
+
+/*
  *	socket join on multicast group
  */
 
-int ipv6_sock_mc_join(struct sock *sk, struct device *dev, 
-		      struct in6_addr *addr)
+int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr)
 {
+	struct device *dev = NULL;
 	struct ipv6_mc_socklist *mc_lst;
 	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 = kmalloc(sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
 
 	if (mc_lst == NULL)
@@ -87,7 +87,20 @@
 
 	mc_lst->next = NULL;
 	memcpy(&mc_lst->addr, addr, sizeof(struct in6_addr));
-	mc_lst->dev = dev;
+	mc_lst->ifindex = ifindex;
+
+	if (ifindex == 0) {
+		struct rt6_info *rt;
+		rt = rt6_lookup(addr, NULL, NULL, 0);
+		if (rt)
+			dev = rt->rt6i_dev;
+	} else
+		dev = dev_get_by_index(ifindex);
+
+	if (dev == NULL) {
+		kfree(mc_lst);
+		return -ENODEV;
+	}
 
 	/*
 	 *	now add/increase the group membership on the device
@@ -109,30 +122,21 @@
 /*
  *	socket leave on multicast group
  */
-int ipv6_sock_mc_drop(struct sock *sk, struct device *dev, 
-		      struct in6_addr *addr)
+int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
 {
 	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 &&
+	for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) {
+		if (mc_lst->ifindex == ifindex &&
 		    ipv6_addr_cmp(&mc_lst->addr, addr) == 0) {
+			struct device *dev;
 			*lnk = mc_lst->next;
-			ipv6_dev_mc_dec(mc_lst->dev, &mc_lst->addr);
+			if ((dev = dev_get_by_index(ifindex)) != NULL)
+				ipv6_dev_mc_dec(dev, &mc_lst->addr);
 			kfree(mc_lst);
-
 			return 0;
 		}
-		lnk = &mc_lst->next;
 	}
 
 	return -ENOENT;
@@ -143,21 +147,48 @@
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct ipv6_mc_socklist *mc_lst;
 
-	for (mc_lst = np->ipv6_mc_list; mc_lst; ) {
-		struct ipv6_mc_socklist *back;
+	while ((mc_lst = np->ipv6_mc_list) != NULL) {
+		struct device *dev = dev_get_by_index(mc_lst->ifindex);
 
-		/*
-		 *	leave group
-		 */
-
-		ipv6_dev_mc_dec(mc_lst->dev, &mc_lst->addr);
-
-		back = mc_lst;
-		mc_lst = mc_lst->next;
-		kfree(back);
+		if (dev)
+			ipv6_dev_mc_dec(dev, &mc_lst->addr);
+
+		np->ipv6_mc_list = mc_lst->next;
+		kfree(mc_lst);
 	}
 }
 
+static int igmp6_group_added(struct ifmcaddr6 *mc)
+{
+	char buf[MAX_ADDR_LEN];
+
+	if (!(mc->mca_flags&MAF_LOADED)) {
+		mc->mca_flags |= MAF_LOADED;
+		if (ndisc_mc_map(&mc->mca_addr, buf, mc->dev, 0) == 0)
+			dev_mc_add(mc->dev, buf, mc->dev->addr_len, 0);
+	}
+
+	if (mc->dev->flags&IFF_UP)
+		igmp6_join_group(mc);
+	return 0;
+}
+
+static int igmp6_group_dropped(struct ifmcaddr6 *mc)
+{
+	char buf[MAX_ADDR_LEN];
+
+	if (mc->mca_flags&MAF_LOADED) {
+		mc->mca_flags &= ~MAF_LOADED;
+		if (ndisc_mc_map(&mc->mca_addr, buf, mc->dev, 0) == 0)
+			dev_mc_delete(mc->dev, buf, mc->dev->addr_len, 0);
+	}
+
+	if (mc->dev->flags&IFF_UP)
+		igmp6_leave_group(mc);
+	return 0;
+}
+
+
 /*
  *	device multicast group inc (add if not found)
  */
@@ -165,30 +196,17 @@
 {
 	struct ifmcaddr6 *mc;
 	struct inet6_dev    *idev;
-	char buf[6];
 	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;
+	idev = ipv6_get_idev(dev);
 
-	if (idev == NULL) {
-		printk(KERN_DEBUG "ipv6_dev_mc_inc: device not found\n");
+	if (idev == NULL)
 		return -EINVAL;
-	}
 
 	hash = ipv6_addr_hash(addr);
 
 	for (mc = inet6_mcast_lst[hash]; mc; mc = mc->next) {
-		if ((ipv6_addr_cmp(&mc->mca_addr, addr) == 0) &&
-		    (mc->dev->ifindex == dev->ifindex)) {
+		if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0 && mc->dev == dev) {
 			atomic_inc(&mc->mca_users);
 			return 0;
 		}
@@ -203,7 +221,6 @@
 	if (mc == NULL)
 		return -ENOMEM;
 
-	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;
@@ -218,23 +235,7 @@
 	mc->if_next = idev->mc_list;
 	idev->mc_list = mc;
 
-	/*
-	 *	multicast mapping is defined in IPv6-over-foo documents
-	 */
-
-	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");
-	};
-
-	igmp6_join_group(mc);
+	igmp6_group_added(mc);
 
 	return 0;
 }
@@ -247,15 +248,12 @@
 
 	if (idev) {
 		struct ifmcaddr6 *iter, **lnk;
-
-		lnk = &idev->mc_list;
 		
-		for (iter = *lnk; iter; iter = iter->if_next) {
+		for (lnk = &idev->mc_list; (iter = *lnk) != NULL; lnk = &iter->if_next) {
 			if (iter == ma) {
 				*lnk = iter->if_next;
-				break;
+				return;
 			}
-			lnk = &iter->if_next;
 		}
 	}
 }
@@ -270,19 +268,16 @@
 
 	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) {
+	for (lnk = &inet6_mcast_lst[hash]; (ma=*lnk) != NULL; lnk = &ma->next) {
+		if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0 && ma->dev == dev) {
 			if (atomic_dec_and_test(&ma->mca_users)) {
-				igmp6_leave_group(ma);
+				igmp6_group_dropped(ma);
 				*lnk = ma->next;
-				ipv6_mca_remove(ma->dev, ma);
+				ipv6_mca_remove(dev, ma);
 				kfree(ma);
 			}
 			return 0;
 		}
-		lnk = &ma->next;
 	}
 
 	return -ENOENT;
@@ -299,7 +294,7 @@
 	hash = ipv6_addr_hash(addr);
 
 	for (mc = inet6_mcast_lst[hash]; mc; mc=mc->next) {
-		if ((mc->dev == dev) && ipv6_addr_cmp(&mc->mca_addr, addr) == 0)
+		if (mc->dev == dev && ipv6_addr_cmp(&mc->mca_addr, addr) == 0)
 			return 1;
 	}
 
@@ -312,11 +307,15 @@
 
 static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
 {
-	unsigned long delay;
+	unsigned long delay = resptime;
 
-	ma->mca_flags |= MAF_TIMER_RUNNING;
+	if (del_timer(&ma->mca_timer))
+		delay = ma->mca_timer.expires - jiffies;
 
-	delay = ipv6_random() % resptime;
+	if (delay >= resptime)
+		delay = net_random() % resptime;
+
+	ma->mca_flags |= MAF_TIMER_RUNNING;
 	ma->mca_timer.expires = jiffies + delay;
 	add_timer(&ma->mca_timer);
 }
@@ -408,22 +407,16 @@
 
 	plen = sizeof(struct ipv6hdr) + len;
 
-	skb = sock_alloc_send_skb(sk, dev->hard_header_len + plen, 0, 0, &err);
+	skb = sock_alloc_send_skb(sk, dev->hard_header_len + plen + 15, 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];
-			if (dev->type == ARPHRD_ETHER)
-				ipv6_mc_map(addr, ha);
-			else
-				memcpy(ha, dev->broadcast, dev->addr_len);
-			dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, plen);
-			skb->arp = 1;
-		}
+	skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+	if (dev->hard_header) {
+		unsigned char ha[MAX_ADDR_LEN];
+		ndisc_mc_map(addr, ha, dev, 1);
+		dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, plen);
 	}
 
 	ifp = ipv6_get_lladdr(dev);
@@ -468,11 +461,16 @@
 
 	igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
 
-	delay = ipv6_random() % IGMP6_UNSOLICITED_IVAL;
+	delay = net_random() % IGMP6_UNSOLICITED_IVAL;
+	start_bh_atomic();
+	if (del_timer(&ma->mca_timer))
+		delay = ma->mca_timer.expires - jiffies;
+
 	ma->mca_timer.expires = jiffies + delay;
 
 	add_timer(&ma->mca_timer);
 	ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER;
+	end_bh_atomic();
 }
 
 static void igmp6_leave_group(struct ifmcaddr6 *ma)
@@ -500,8 +498,111 @@
 	ma->mca_flags &= ~MAF_TIMER_RUNNING;
 }
 
+/* Device going down */
+
+void ipv6_mc_down(struct inet6_dev *idev)
+{
+	struct ifmcaddr6 *i;
+	struct in6_addr maddr;
+
+	/* Withdraw multicast list */
+
+	for (i = idev->mc_list; i; i=i->if_next)
+		igmp6_group_dropped(i);
+
+	/* Delete all-nodes address. */
+
+	ipv6_addr_all_nodes(&maddr);
+	ipv6_dev_mc_dec(idev->dev, &maddr);
+}
+
+/* Device going up */
+
+void ipv6_mc_up(struct inet6_dev *idev)
+{
+	struct ifmcaddr6 *i;
+	struct in6_addr maddr;
+
+	/* Add all-nodes address. */
+
+	ipv6_addr_all_nodes(&maddr);
+	ipv6_dev_mc_inc(idev->dev, &maddr);
+
+	/* Install multicast list, except for all-nodes (already installed) */
+
+	for (i = idev->mc_list; i; i=i->if_next)
+		igmp6_group_added(i);
+}
+
+/*
+ *	Device is about to be destroyed: clean up.
+ */
+
+void ipv6_mc_destroy_dev(struct inet6_dev *idev)
+{
+	struct ifmcaddr6 *i;
+
+	while ((i = idev->mc_list) != NULL) {
+		idev->mc_list = i->if_next;
+		igmp6_group_dropped(i);
+		kfree(i);
+	}
+}
+
+#ifdef CONFIG_PROC_FS
+static int igmp6_read_proc(char *buffer, char **start, off_t offset,
+			   int length, int *eof, void *data)
+{
+	off_t pos=0, begin=0;
+	struct ifmcaddr6 *im;
+	int len=0;
+	struct device *dev;
+	
+	for (dev = dev_base; dev; dev = dev->next) {
+		struct inet6_dev *idev;
+
+		if ((idev = ipv6_get_idev(dev)) == NULL)
+			continue;
+
+		for (im = idev->mc_list; im; im = im->if_next) {
+			int i;
+
+			len += sprintf(buffer+len,"%-4d %-15s ", dev->ifindex, dev->name);
+
+			for (i=0; i<16; i++)
+				len += sprintf(buffer+len, "%02x", im->mca_addr.s6_addr[i]);
+
+			len+=sprintf(buffer+len,
+				     " %5d %08X %ld\n",
+				     atomic_read(&im->mca_users),
+				     im->mca_flags,
+				     (im->mca_flags&MAF_TIMER_RUNNING) ? im->mca_timer.expires-jiffies : 0);
+
+			pos=begin+len;
+			if (pos < offset) {
+				len=0;
+				begin=pos;
+			}
+			if (pos > offset+length)
+				goto done;
+		}
+	}
+	*eof = 1;
+
+done:
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;
+	return len;
+}
+#endif
+
 __initfunc(void igmp6_init(struct net_proto_family *ops))
 {
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *ent;
+#endif
 	struct sock *sk;
 	int err;
 
@@ -525,4 +626,9 @@
 	sk->num = 256;			/* Don't receive any data */
 
 	sk->net_pinfo.af_inet6.hop_limit = 1;
+#ifdef CONFIG_PROC_FS
+	ent = create_proc_entry("net/igmp6", 0, 0);
+	ent->read_proc = igmp6_read_proc;
+#endif
 }
+
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.1.78/linux/net/ipv6/ndisc.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/ndisc.c	Mon Jan 12 15:28:28 1998
@@ -19,15 +19,25 @@
  *						of an RA.
  *
  *	Janos Farkas			:	kmalloc failure checks
+ *	Alexey Kuznetsov		:	state machine reworked
+ *						and moved to net/core.
  */
 
 /* Set to 3 to get tracing... */
 #define ND_DEBUG 1
 
-#if ND_DEBUG >= 3
-#define NDBG(x) printk x
-#else
-#define NDBG(x)
+#define ND_PRINTK(x...) printk(KERN_DEBUG x)
+#define ND_NOPRINTK(x...) do { ; } while(0)
+#define ND_PRINTK0 ND_PRINTK
+#define ND_PRINTK1 ND_NOPRINTK
+#define ND_PRINTK2 ND_NOPRINTK
+#if ND_DEBUG >= 1
+#undef ND_PRINTK1
+#define ND_PRINTK1 ND_PRINTK
+#endif
+#if ND_DEBUG >= 2
+#undef ND_PRINTK2
+#define ND_PRINTK2 ND_PRINTK
 #endif
 
 #define __NO_VERSION__
@@ -42,6 +52,9 @@
 #include <linux/in6.h>
 #include <linux/route.h>
 #include <linux/init.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
 
 #include <linux/if_arp.h>
 #include <linux/ipv6.h>
@@ -61,380 +74,186 @@
 #include <net/checksum.h>
 #include <linux/proc_fs.h>
 
-#define NCACHE_NUM_BUCKETS 32
-
 static struct inode ndisc_inode;
 static struct socket *ndisc_socket=&ndisc_inode.u.socket_i;
 
-unsigned long nd_rand_seed = 152L;
-
-struct ndisc_statistics nd_stats;
-
-static struct neigh_table nd_tbl;
-
-unsigned int	ndisc_hash(void *primary_key);
-int		ndisc_eth_resolv(unsigned char *h_dest, struct sk_buff *skb);
-
-static struct neigh_ops nd_neigh_ops = {
-	ETH_P_IPV6,
-	ndisc_hash,
-	ndisc_eth_resolv,
-	NULL
+static int ndisc_constructor(struct neighbour *neigh);
+static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);
+static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);
+static int pndisc_constructor(struct pneigh_entry *n);
+static void pndisc_destructor(struct pneigh_entry *n);
+static void pndisc_redo(struct sk_buff *skb);
+
+static struct neigh_ops ndisc_generic_ops =
+{
+	AF_INET6,
+	NULL,
+	ndisc_solicit,
+	ndisc_error_report,
+	neigh_resolve_output,
+	neigh_connected_output,
+	dev_queue_xmit,
+	dev_queue_xmit
 };
 
-static struct timer_list ndisc_timer;
-static struct timer_list ndisc_gc_timer;
-
-/*
- *	Protocol variables
- */
-
-unsigned long	nd_reachable_time		= RECHABLE_TIME;
-int		nd_gc_interval			= 5 * HZ;
-
-/* 
- *	garbage collection timeout must be greater than reachable time
- *	since tstamp is updated by reachable confirmations only.
- *	gc_staletime actually means the time after last confirmation
- *	*NOT* after the last time the entry was used.
- */
-
-int	nd_gc_staletime			= 3 * RECHABLE_TIME;
-
-
-static int  ndisc_event_timer(struct nd_neigh *ndn);
-
-unsigned long ipv6_random(void)
+static struct neigh_ops ndisc_hh_ops =
 {
-	nd_rand_seed=nd_rand_seed*69069L+1;
-        return nd_rand_seed^jiffies;
-}
-
-static __inline__ unsigned long rand_reach_time(void)
-{
-	unsigned long val;
-
-	val = ipv6_random() % (MAX_RANDOM_FACTOR *
-			       ipv6_config.nd_base_reachable_time);
+	AF_INET6,
+	NULL,
+	ndisc_solicit,
+	ndisc_error_report,
+	neigh_resolve_output,
+	neigh_resolve_output,
+	dev_queue_xmit,
+	dev_queue_xmit
+};
 
-	if (val < (MIN_RANDOM_FACTOR * ipv6_config.nd_base_reachable_time))
-		val+= (MIN_RANDOM_FACTOR * ipv6_config.nd_base_reachable_time);
 
-	return val;
-}
-
-unsigned int ndisc_hash(void *primary_key)
+static struct neigh_ops ndisc_direct_ops =
 {
-        struct in6_addr *addr = (struct in6_addr *) primary_key;
-        __u32 hash_val;
-        
-	addr = (struct in6_addr *) primary_key;
-	
-        hash_val = addr->s6_addr32[2] ^ addr->s6_addr32[3];
-
-        hash_val ^= hash_val >> 16;
-        
-        return (hash_val & (NCACHE_NUM_BUCKETS - 1));
-}
-
-static int ndisc_gc_func(struct neighbour *neigh, void *arg);
+	AF_INET6,
+	NULL,
+	NULL,
+	NULL,
+	dev_queue_xmit,
+	dev_queue_xmit,
+	dev_queue_xmit,
+	dev_queue_xmit
+};
 
-static void ndisc_periodic_timer(unsigned long arg)
+struct neigh_table nd_tbl =
 {
-	static unsigned long last_rand = 0;
-	unsigned long now = jiffies;
-	
-	/*
-	 *	periodicly compute ReachableTime from random function
-	 */
-	
-	if ((now - last_rand) > REACH_RANDOM_INTERVAL) {
-		last_rand = now;
-		nd_reachable_time = rand_reach_time();
-	}
-
-	neigh_table_lock(&nd_tbl);
+	NULL,
+	AF_INET6,
+	sizeof(struct neighbour) + sizeof(struct in6_addr),
+	sizeof(struct in6_addr),
+	ndisc_constructor,
+	pndisc_constructor,
+	pndisc_destructor,
+	pndisc_redo,
+        { NULL, NULL, 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 0, 64 },
+	30*HZ, 128, 512, 1024,
+};
 
-	start_bh_atomic();
-	if (atomic_read(&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 {
-#if ND_DEBUG >= 2
-		printk(KERN_DEBUG "ndisc_gc delayed: table locked\n");
-#endif
-		ndisc_gc_timer.expires = now + HZ;
-	}
-	end_bh_atomic();
-	
-	neigh_table_unlock(&nd_tbl);
-	
-	add_timer(&ndisc_gc_timer);
-}
+#define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
 
-static int ndisc_gc_func(struct neighbour *neigh, void *arg)
+static u8 *ndisc_fill_option(u8 *opt, int type, void *data, int data_len)
 {
-	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
-        unsigned long now = jiffies;
+	int space = NDISC_OPT_SPACE(data_len);
 
-	if (atomic_read(&ndn->ndn_refcnt) == 0) {
-		switch (ndn->ndn_nud_state) {
-		
-		case NUD_REACHABLE:
-		case NUD_STALE:
-			if (now - ndn->ndn_tstamp < nd_gc_staletime)
-				break;
-		case NUD_FAILED:
-			return 1;
-		default:
-		};
-	}
-	return 0;
+	opt[0] = type;
+	opt[1] = space>>3;
+	memcpy(opt+2, data, data_len);
+	data_len += 2;
+	if ((space -= data_len) > 0)
+		memset(opt + data_len, 0, space);
+	return opt + space;
 }
 
-static __inline__ void ndisc_add_timer(struct nd_neigh *ndn, int timer)
+int ndisc_mc_map(struct in6_addr *addr, char *buf, struct device *dev, int dir)
 {
-	unsigned long now = jiffies;
-	unsigned long tval = ~0UL;
-
-	ndn->ndn_expires = now + timer;
-	
-	if (del_timer(&ndisc_timer))
-		tval = ndisc_timer.expires;
-
-	tval = min(tval, ndn->ndn_expires);
-
-	ndisc_timer.expires = tval;
-	add_timer(&ndisc_timer);
-}
-        
-static void ndisc_del_timer(struct nd_neigh *ndn)
-{
-	unsigned long tval = ~0UL;
-	unsigned long neigh_val;
-
-	if (del_timer(&ndisc_timer))
-		tval = ndisc_timer.expires;
-
-	neigh_val = ndn->ndn_expires;
-	ndn->ndn_expires = 0;
-
-	if (tval == neigh_val) {
-		int i;
-		
-		tval = ~0UL;
-
-		neigh_table_lock(&nd_tbl);
-		
-		/* need to search the entire neighbour cache */
-		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 {
-				struct nd_neigh *n;
-
-				n = (struct nd_neigh *) neigh;
-
-				if ((n->ndn_nud_state & NUD_IN_TIMER) &&
-				     n->ndn_expires)
-					tval = min(tval, n->ndn_expires);
-
-				neigh = neigh->next;
-
-			} while (neigh != head);
+	switch (dev->type) {
+	case ARPHRD_ETHER:
+	case ARPHRD_IEEE802:	/* Not sure. Check it later. --ANK */
+	case ARPHRD_FDDI:
+		ipv6_eth_mc_map(addr, buf);
+		return 0;
+	default:
+		if (dir) {
+			memcpy(buf, dev->broadcast, dev->addr_len);
+			return 0;
 		}
-		neigh_table_unlock(&nd_tbl);
-	}
-
-	if (tval == ~(0UL))
-		return;
-
-	ndisc_timer.expires = tval;
-	add_timer(&ndisc_timer);
-}
-
-static int ndisc_forced_gc(struct neighbour *neigh, void *arg)
-{
-	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
-
-	if (atomic_read(&ndn->ndn_refcnt) == 0) {
-		if (ndn->ndn_nud_state & NUD_IN_TIMER)
-			ndisc_del_timer(ndn);
-		
-		return 1;
 	}
-	return 0;
+	return -EINVAL;
 }
 
-static struct nd_neigh * ndisc_new_neigh(struct device *dev,
-					 struct in6_addr *addr)
+static int ndisc_constructor(struct neighbour *neigh)
 {
-	struct nd_neigh *ndn;
+	struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
+	struct device *dev = neigh->dev;
+	struct inet6_dev *in6_dev = ipv6_get_idev(dev);
+	int addr_type;
+
+	if (in6_dev == NULL)
+		return -EINVAL;
+
+	addr_type = ipv6_addr_type(addr);
+	if (in6_dev->nd_parms)
+		neigh->parms = in6_dev->nd_parms;
 
-	NDBG(("ndisc_new_neigh("));
-	if(dev)
-		NDBG(("%s,", dev->name));
+	if (addr_type&IPV6_ADDR_MULTICAST)
+		neigh->type = RTN_MULTICAST;
 	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),
-					      &nd_neigh_ops);
-	if (ndn == NULL) {
-
-#if ND_DEBUG >= 2
-		printk(KERN_DEBUG "neigh_alloc: out of memory\n");
-#endif
-
-		start_bh_atomic();
-		if (atomic_read(&nd_tbl.tbl_lock) == 1) {
-#if ND_DEBUG >= 2
-			printk(KERN_DEBUG "ndisc_alloc: forcing gc\n");
-#endif
-			ntbl_walk_table(&nd_tbl, ndisc_forced_gc, 0, 0, NULL);
+		neigh->type = RTN_UNICAST;
+	if (dev->hard_header == NULL) {
+		neigh->nud_state = NUD_NOARP;
+		neigh->ops = &ndisc_direct_ops;
+		neigh->output = neigh->ops->queue_xmit;
+	} else {
+		if (addr_type&IPV6_ADDR_MULTICAST) {
+			neigh->nud_state = NUD_NOARP;
+			ndisc_mc_map(addr, neigh->ha, dev, 1);
+		} else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
+			neigh->nud_state = NUD_NOARP;
+			memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
+			if (dev->flags&IFF_LOOPBACK)
+				neigh->type = RTN_LOCAL;
+		} else if (dev->flags&IFF_POINTOPOINT) {
+			neigh->nud_state = NUD_NOARP;
+			memcpy(neigh->ha, dev->broadcast, dev->addr_len);
 		}
-		
-		end_bh_atomic();
-#if ND_DEBUG >= 1
-		printk(KERN_DEBUG "ndisc_alloc failed\n");
-#endif
-		return NULL;
-	}
-
-	nd_stats.allocs++;
-
-	ipv6_addr_copy(&ndn->ndn_addr, addr);
-	ndn->ndn_plen = 128;
-	ndn->ndn_type = ipv6_addr_type(addr);
-	ndn->ndn_dev = dev;
-	ndn->ndn_tstamp = jiffies;
-
-	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;
+		if (dev->hard_header_cache)
+			neigh->ops = &ndisc_hh_ops;
+		else
+			neigh->ops = &ndisc_generic_ops;
+		if (neigh->nud_state&NUD_VALID)
+			neigh->output = neigh->ops->connected_output;
+		else
+			neigh->output = neigh->ops->output;
 	}
 
-	neigh_insert(&nd_tbl, (struct neighbour *) ndn);
-	NDBG(("returning ndn(%p)\n", ndn));
-	return ndn;
+	return 0;
 }
 
-/*
- *	Called when creating a new dest_cache entry for a given destination
- *	is likely that an entry for the refered gateway exists in cache
- *
- */
-
-struct neighbour * ndisc_get_neigh(struct device *dev, struct in6_addr *addr)
+static int pndisc_constructor(struct pneigh_entry *n)
 {
-	struct nd_neigh *neigh;
-
-	/*
-	 *	neighbour cache:
-	 *	cached information about nexthop and addr resolution
-	 */
-
-	if (dev == NULL) {
-#if ND_DEBUG >= 1
-		printk(KERN_DEBUG "ndisc_get_neigh: NULL device\n");
+	struct in6_addr *addr = (struct in6_addr*)&n->key;
+	struct in6_addr maddr;
+	struct device *dev = n->dev;
+
+	if (dev == NULL || ipv6_get_idev(dev) == NULL)
+		return -EINVAL;
+#ifndef CONFIG_IPV6_NO_PB
+	addrconf_addr_solict_mult_old(addr, &maddr);
+	ipv6_dev_mc_inc(dev, &maddr);
+#endif
+#ifdef CONFIG_IPV6_EUI64
+	addrconf_addr_solict_mult_new(addr, &maddr);
+	ipv6_dev_mc_inc(dev, &maddr);
 #endif
-		return NULL;
-	}
-
-	neigh_table_lock(&nd_tbl);
-
-        neigh = (struct nd_neigh *) neigh_lookup(&nd_tbl, (void *) addr,
-						 sizeof(struct in6_addr), dev);
-	if (neigh == NULL) {
-		neigh = ndisc_new_neigh(dev, addr);
-
-		if (neigh == NULL)
-			return NULL;
-	}
-
-	neigh_table_unlock(&nd_tbl);
-
-	return neighbour_clone((struct neighbour *) neigh);
+	return 0;
 }
 
-/*
- *	return values
- *	0 - Address Resolution succeded, send packet
- *	1 - Address Resolution unfinished / packet queued
- */
-
-int ndisc_eth_resolv(unsigned char *h_dest, struct sk_buff *skb)
+static void pndisc_destructor(struct pneigh_entry *n)
 {
-	struct nd_neigh *ndn = NULL;
-
-	if (skb->dst)
-		ndn = (struct nd_neigh *) skb->dst->neighbour;
+	struct in6_addr *addr = (struct in6_addr*)&n->key;
+	struct in6_addr maddr;
+	struct device *dev = n->dev;
 
-	if (ndn == NULL) {
-#if ND_DEBUG >= 2
-		printk(KERN_DEBUG "ndisc_eth_resolv: nexthop is NULL\n");
+	if (dev == NULL || ipv6_get_idev(dev) == NULL)
+		return;
+#ifndef CONFIG_IPV6_NO_PB
+	addrconf_addr_solict_mult_old(addr, &maddr);
+	ipv6_dev_mc_dec(dev, &maddr);
 #endif
-		goto discard;
-	}
-
-	if ((ndn->ndn_type & IPV6_ADDR_MULTICAST)) {
-		struct in6_addr *daddr;
-
-		daddr = &skb->nh.ipv6h->daddr;
-		if (skb->dev->type == ARPHRD_ETHER)
-			ipv6_mc_map(daddr, h_dest);
-		else
-			memcpy(h_dest, skb->dev->broadcast, skb->dev->addr_len);
-		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) {
-			struct sk_buff *buff;
-			
-			buff = ndn->neigh.arp_queue.prev;
-			skb_unlink(buff);
-			dev_kfree_skb(buff, FREE_WRITE);
-		}
-		skb_queue_head(&ndn->neigh.arp_queue, skb);
-		return 1;
-	default:
-		ndisc_event_send((struct neighbour *)ndn, skb);
-	};
-
-	if ((ndn->ndn_flags & NTF_COMPLETE) == 0) {
-#if ND_DEBUG >=1
-		/* This shouldn't happen */
-		printk(KERN_DEBUG "ND: using incomplete entry\n");
+#ifdef CONFIG_IPV6_EUI64
+	addrconf_addr_solict_mult_new(addr, &maddr);
+	ipv6_dev_mc_dec(dev, &maddr);
 #endif
-	}
-	memcpy(h_dest, ndn->ndn_ha, skb->dev->addr_len);
-	return 0;
-
-  discard:
-	
-	dev_kfree_skb(skb, FREE_WRITE);
-	return 1;
 }
 
+
+
 static int
 ndisc_build_ll_hdr(struct sk_buff *skb, struct device *dev,
 		   struct in6_addr *daddr, struct neighbour *neigh, int len)
@@ -442,44 +261,30 @@
 	unsigned char ha[MAX_ADDR_LEN];
 	unsigned char *h_dest = NULL;
 
-	skb->arp = 1;
-	if (dev->hard_header_len) {
-		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
-
-		if (dev->hard_header) {
-			if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST) {
-				nd_stats.snt_probes_mcast++;
-				if (dev->type == ARPHRD_ETHER)
-					ipv6_mc_map(daddr, ha);
-				else
-					memcpy(ha, dev->broadcast, dev->addr_len);
-				h_dest = ha;
-			} else if (neigh) {
-				h_dest = neigh->ha;
-				nd_stats.snt_probes_ucast++;
-			} else {
-				struct nd_neigh *ndn;
-
-				neigh_table_lock(&nd_tbl);
+	skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
 
-				neigh = neigh_lookup(&nd_tbl, (void *) daddr,
-						     sizeof(struct in6_addr), dev);
-				if (neigh) {
-					ndn = (struct nd_neigh*)neigh;
-					if (ndn->ndn_flags&NTF_COMPLETE) {
-						memcpy(ha, ndn->ndn_ha, dev->addr_len);
-						h_dest = ha;
-					}
+	if (dev->hard_header) {
+		if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST) {
+			ndisc_mc_map(daddr, ha, dev, 1);
+			h_dest = ha;
+		} else if (neigh) {
+			h_dest = neigh->ha;
+		} else {
+			neigh = neigh_lookup(&nd_tbl, daddr, dev);
+			if (neigh) {
+				if (neigh->nud_state&NUD_VALID) {
+					memcpy(ha, neigh->ha, dev->addr_len);
+					h_dest = ha;
 				}
-				neigh_table_unlock(&nd_tbl);
+				neigh_release(neigh);
 			}
-
-			if (dev->hard_header(skb, dev, ETH_P_IPV6, h_dest, NULL, len) < 0)
-				skb->arp = 0;
 		}
+
+		if (dev->hard_header(skb, dev, ETH_P_IPV6, h_dest, NULL, len) < 0)
+			return 0;
 	}
 
-	return skb->arp;
+	return 1;
 }
 
 
@@ -487,56 +292,34 @@
  *	Send a Neighbour Advertisement
  */
 
-void ndisc_send_na(struct device *dev, struct nd_neigh *ndn,
+void ndisc_send_na(struct device *dev, struct neighbour *neigh,
 		   struct in6_addr *daddr, struct in6_addr *solicited_addr,
 		   int router, int solicited, int override, int inc_opt) 
 {
         struct sock *sk = ndisc_socket->sk;
         struct nd_msg *msg;
-        int len, opt_len;
+        int len;
         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 icmp6hdr) + sizeof(struct in6_addr);
 
-#if ND_DEBUG >=1
-	if (dev == NULL) {
-		printk(KERN_DEBUG "send_na: null device\n");
-		return;
+	if (inc_opt) {
+		if (dev->addr_len)
+			len += NDISC_OPT_SPACE(dev->addr_len);
+		else
+			inc_opt = 0;
 	}
-#endif
-	if (inc_opt)
-		len += opt_len << 3;
 
 	skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
 				  0, 0, &err);
 
 	if (skb == NULL) {
-		printk(KERN_DEBUG "send_na: alloc skb failed\n");
+		ND_PRINTK1("send_na: alloc skb failed\n");
 		return;
 	}
 
-	if (ndisc_build_ll_hdr(skb, dev, daddr, (struct neighbour*)ndn, len) == 0) {
+	if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
 		kfree_skb(skb, FREE_WRITE);
 		return;
 	}
@@ -557,17 +340,8 @@
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
 
-	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)) {
-			memset(msg->opt.link_addr + dev->addr_len, 0,
-			       (opt_len << 3) - (2 + dev->addr_len));
-		}
-	}
+	if (inc_opt)
+		ndisc_fill_option((void*)&msg->opt, ND_OPT_TARGET_LL_ADDR, dev->dev_addr, dev->addr_len);
 
 	/* checksum */
 	msg->icmph.icmp6_cksum = csum_ipv6_magic(solicited_addr, daddr, len, 
@@ -585,48 +359,20 @@
         struct sock *sk = ndisc_socket->sk;
         struct sk_buff *skb;
         struct nd_msg *msg;
-        int len, opt_len;	
+        int len;
 	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 icmp6hdr) + sizeof(struct in6_addr) +
-                (opt_len << 3);
+	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+	if (dev->addr_len)
+		len += NDISC_OPT_SPACE(dev->addr_len);
 
 	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
+		ND_PRINTK1("send_ns: alloc skb failed\n");
 		return;
 	}
 
-#if 0
-	/* Why Pedro did it? Is it remnant of early
-	   attempts to avoid looping back? I have no idea. --ANK */
-	skb->pkt_type = PACKET_NDISC;
-#endif
-
 	if (saddr == NULL) {
 		struct inet6_ifaddr *ifa;
 
@@ -643,7 +389,7 @@
 	}
 
 	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
-	
+
 	msg = (struct nd_msg *)skb_put(skb, len);
 	msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;
 	msg->icmph.icmp6_code = 0;
@@ -653,16 +399,8 @@
 	/* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicit);
 
-	/* Set the source link-layer address option. */
-	msg->opt.opt_type = ND_OPT_SOURCE_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)) {
-		memset(msg->opt.link_addr + dev->addr_len, 0,
-		       (opt_len << 3) - (2 + dev->addr_len));
-	}
+	if (dev->addr_len)
+		ndisc_fill_option((void*)&msg->opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr, dev->addr_len);
 
 	/* checksum */
 	msg->icmph.icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr,
@@ -681,30 +419,17 @@
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
-        int len, opt_len;
+        int 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 icmp6hdr) + (opt_len << 3);
+	len = sizeof(struct icmp6hdr);
+	if (dev->addr_len)
+		len += NDISC_OPT_SPACE(dev->addr_len);
 
         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");
+		ND_PRINTK1("send_ns: alloc skb failed\n");
 		return;
 	}
 
@@ -714,7 +439,7 @@
 	}
 
 	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
-	
+
         hdr = (struct icmp6hdr *) skb_put(skb, len);
         hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;
         hdr->icmp6_code = 0;
@@ -723,16 +448,8 @@
 
 	opt = (u8*) (hdr + 1);
 
-        /* Set the source link-layer address option. */
-        opt[0] = ND_OPT_SOURCE_LL_ADDR;
-        opt[1] = opt_len;
-
-        memcpy(opt + 2, dev->dev_addr, 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));
-	}
+	if (dev->addr_len)
+		ndisc_fill_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr, dev->addr_len);
 
 	/* checksum */
 	hdr->icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len,
@@ -744,330 +461,79 @@
 }
 		   
 
-static int ndisc_store_hwaddr(struct nd_neigh *ndn, __u8 *opt, int opt_len,
-			      int option)
+static u8 * ndisc_find_option(u8 *opt, int opt_len, int len, int option)
 {
-	while (*opt != option && opt_len) {
-		int len;
+	while (opt_len <= len) {
+		int l = opt[1]<<3;
 
-		len = opt[1] << 3;
-		
-		if (len == 0)
-		{
-			printk(KERN_WARNING "nd: option has 0 len\n");
-			return -EINVAL;
-		}
+		if (opt[0] == option && l >= opt_len)
+			return opt + 2;
 
-		opt += len;
-		opt_len -= len;
-	}
+		if (l == 0) {
+			if (net_ratelimit())
+			    printk(KERN_WARNING "ndisc: option has 0 len\n");
+			return NULL;
+		}
 
-	if (*opt == option) {
-		memcpy(ndn->neigh.ha, opt + 2, ndn->ndn_dev->addr_len); 
-		return 0;
+		opt += l;
+		len -= l;
 	}
-
-	return -EINVAL;
+	return NULL;
 }
 
-/* Called when a timer expires for a neighbour entry. */
 
-static void ndisc_timer_handler(unsigned long arg) 
+static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
 {
-	unsigned long now = jiffies;
-	unsigned long ntimer = ~0UL;
-        int i;
-
-	neigh_table_lock(&nd_tbl);
-	
-	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) {
-				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)) {
-		unsigned long tval = jiffies + ntimer;
-		if (del_timer(&ndisc_timer)) {
-			if (ndisc_timer.expires - tval < 0)
-				tval = ndisc_timer.expires;
-		}
-		ndisc_timer.expires = tval;
-		add_timer(&ndisc_timer);
-	}
-
-	neigh_table_unlock(&nd_tbl);
+	/*
+	 *	"The sender MUST return an ICMP
+	 *	 destination unreachable"
+	 */
+	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
+	kfree_skb(skb, FREE_WRITE);
 }
 
-
-static int ndisc_event_timer(struct nd_neigh *ndn)
+static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
 {
-	struct in6_addr *daddr;
-	struct in6_addr *target;
+	struct in6_addr *saddr = NULL;
 	struct in6_addr mcaddr;
-	struct device *dev;
-	int max_probes;
-
-	if (ndn->ndn_nud_state == NUD_DELAY)
-		ndn->ndn_nud_state = NUD_PROBE;
-
-	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) {
-		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))) {
-			/*
-			 *	"The sender MUST return an ICMP
-			 *	 destination unreachable"
-			 */
-			icmpv6_send(skb, ICMPV6_DEST_UNREACH,
-				    ICMPV6_ADDR_UNREACH, 0, ndn->ndn_dev);
-
-			dev_kfree_skb(skb, FREE_WRITE);
-		}
-		return 0;
-	}
-
-	ndn->ndn_probes++;
-
-	dev = ndn->ndn_dev;
-	target = &ndn->ndn_addr;
-
-	if (ndn->ndn_nud_state == NUD_INCOMPLETE) {
-		addrconf_addr_solict_mult(&ndn->ndn_addr, &mcaddr);
-		daddr = &mcaddr;
-		ndn = NULL;
+	struct device *dev = neigh->dev;
+	struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
+	int probes = neigh->probes;
+
+	if (skb && ipv6_chk_addr(&skb->nh.ipv6h->saddr, dev, 0))
+		saddr = &skb->nh.ipv6h->saddr;
+
+	if ((probes -= neigh->parms->ucast_probes) < 0) {
+		if (!(neigh->nud_state&NUD_VALID))
+			ND_PRINTK1("trying to ucast probe in NUD_INVALID\n");
+		ndisc_send_ns(dev, neigh, target, target, saddr);
+	} else if ((probes -= neigh->parms->app_probes) < 0) {
+#ifdef CONFIG_ARPD
+		neigh_app_ns(neigh);
+#endif
 	} else {
-		daddr = &ndn->ndn_addr;
-	}
-
-	ndisc_send_ns(dev, (struct neighbour *) ndn, target, daddr, NULL);
-
-	return ipv6_config.nd_retrans_time;
-}
-
-void ndisc_event_send(struct neighbour *neigh, struct sk_buff *skb)
-{
-	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
-	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) {
-			/*
-			 *	skb->stamp allows us to know if we are
-			 *	originating the skb or forwarding it.
-			 *	(it is set on netif_rx)
-			 */
-			saddr = &skb->nh.ipv6h->saddr;
-		}
-
-		ndn->ndn_nud_state = NUD_INCOMPLETE;
-		addrconf_addr_solict_mult(&ndn->ndn_addr, &daddr);
-		ndisc_send_ns(ndn->ndn_dev, NULL, &ndn->ndn_addr, &daddr,
-			      saddr);
-		ndisc_add_timer(ndn, ipv6_config.nd_retrans_time);
-
-		break;
-
-	case NUD_REACHABLE:
-		if ((now - ndn->ndn_tstamp) < nd_reachable_time)
-			break;
-
-	case NUD_STALE:
-		ndn->ndn_nud_state = NUD_DELAY;
-		ndisc_add_timer(ndn, ipv6_config.nd_delay_probe_time);
-	}
-}
-
-/*
- *	Received a neighbour announce
- */
-void ndisc_event_na(struct nd_neigh *ndn, unsigned char *opt, int opt_len,
-		    int solicited, int override)
-{
-	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) {
-			printk(KERN_DEBUG "no opt on NA\n");
-		} else {
-			/* Record hardware address. */
-			ndn->ndn_flags |= NTF_COMPLETE;
-
-			if (ndisc_store_hwaddr(ndn, opt, opt_len,
-					       ND_OPT_TARGET_LL_ADDR)) {
-#if ND_DEBUG >= 2
-				printk(KERN_DEBUG
-				       "event_na: invalid TARGET_LL_ADDR\n");
+#ifdef CONFIG_IPV6_EUI64
+		addrconf_addr_solict_mult_new(target, &mcaddr);
+		ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
 #endif
-				ndn->ndn_flags &= ~NTF_COMPLETE;
-				ndn->ndn_nud_state = NUD_NONE;
-				return;
-			}
-		}
-	}
-
-	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 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 icmp6hdr) + sizeof(struct in6_addr);
-
-	len = skb->tail - opt;
-
-	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);
-
-       	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;
-
-		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 ND_DEBUG >= 1
-			printk(KERN_DEBUG
-			       "event_ns: invalid SOURCE_LL_ADDR\n");
+#ifndef CONFIG_IPV6_NO_PB
+		addrconf_addr_solict_mult_old(target, &mcaddr);
+		ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
 #endif
-
-			ndn->ndn_flags &= ~NTF_COMPLETE;
-			ndn->ndn_nud_state = NUD_NONE;
-			return ndn;
-		}
-
-		ndn->ndn_nud_state = NUD_STALE;
-		ndn->ndn_tstamp = jiffies;
-		ndn->ndn_probes = 0;
-	};
-
-	return ndn;
+	}
 }
 
 
-static void ndisc_ll_addr_update(struct nd_neigh *ndn, u8* opt, int len,
-				 int type)
+static void ndisc_update(struct neighbour *neigh, u8* opt, int len, int type)
 {
-	switch(ndn->ndn_nud_state) {
-	case NUD_REACHABLE:
-	case NUD_STALE:
-	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 ND_DEBUG >=1
-			printk(KERN_DEBUG "NDISC: invalid LL_ADDR\n");
-#endif
-			ndn->ndn_flags &= ~NTF_COMPLETE;
-			ndn->ndn_nud_state = NUD_NONE;
-			break;
-		}
-		
-		ndn->ndn_nud_state = NUD_STALE;
-		ndn->ndn_tstamp = jiffies;
-		ndn->ndn_probes = 0;
-	};
+	opt = ndisc_find_option(opt, neigh->dev->addr_len+2, len, type);
+	neigh_update(neigh, opt, NUD_STALE, 1, 1);
 }
 
 static void ndisc_router_discovery(struct sk_buff *skb)
 {
         struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw;
-	struct nd_neigh *ndn;
+	struct neighbour *neigh;
 	struct inet6_dev *in6_dev;
 	struct rt6_info *rt;
 	int lifetime;
@@ -1075,8 +541,6 @@
 
 	__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) {
@@ -1091,7 +555,7 @@
 
 	in6_dev = ipv6_get_idev(skb->dev);
 	if (in6_dev == NULL) {
-		printk(KERN_DEBUG "RA: can't find in6 device\n");
+		ND_PRINTK1("RA: can't find in6 device\n");
 		return;
 	}
 	
@@ -1113,28 +577,25 @@
 	}
 
 	if (rt == NULL && lifetime) {
-#if ND_DEBUG >= 2
-		printk(KERN_DEBUG "ndisc_rdisc: adding default router\n");
-#endif
+		ND_PRINTK2("ndisc_rdisc: adding default router\n");
 
 		rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
 
-		if (rt == NULL) {
-#if ND_DEBUG >= 1
-			printk(KERN_DEBUG "route_add failed\n");
+#if 1
+		/* BUGGGGG! Previous routine can return invalid pointer. */
+		rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
 #endif
+		if (rt == NULL) {
+			ND_PRINTK1("route_add failed\n");
 			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
+		neigh = rt->rt6i_nexthop;
+		if (neigh == NULL) {
+			ND_PRINTK1("nd: add default router: null neighbour\n");
 			return;
 		}
-		ndn->ndn_flags |= NCF_ROUTER;
+		neigh->flags |= NTF_ROUTER;
 	}
 
 	if (rt)
@@ -1147,31 +608,30 @@
 	 *	Update Reachable Time and Retrans Timer
 	 */
 
-	if (ra_msg->retrans_timer)
-		ipv6_config.nd_retrans_time = ntohl(ra_msg->retrans_timer);
-
-	if (ra_msg->reachable_time) {
-		__u32 rtime = ntohl(ra_msg->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();
+	if (in6_dev->nd_parms) {
+		if (ra_msg->retrans_timer)
+			in6_dev->nd_parms->retrans_time = (ntohl(ra_msg->retrans_timer)*HZ)/1000;
+
+		if (ra_msg->reachable_time) {
+			__u32 rtime = (ntohl(ra_msg->reachable_time)*HZ)/1000;
+
+			if (rtime != in6_dev->nd_parms->base_reachable_time) {
+				in6_dev->nd_parms->base_reachable_time = rtime;
+				in6_dev->nd_parms->gc_staletime = 3 * rtime;
+				in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
+			}
 		}
-		
 	}
 
 	/*
 	 *	Process options.
 	 */
 
-        while(optlen > 0) {
-                int len;
-
-                len = (opt[1] << 3);
+        while (optlen > 0) {
+                int len = (opt[1] << 3);
 
 		if (len == 0) {
-			printk(KERN_DEBUG "RA: opt has 0 len\n");
+			ND_PRINTK0("RA: opt has 0 len\n");
 			break;
 		}
 
@@ -1181,11 +641,9 @@
 			if (rt == NULL)
 				break;
 			
-			ndn = (struct nd_neigh *) rt->rt6i_nexthop;
-
-			if (ndn)
-				ndisc_ll_addr_update(ndn, opt, len,
-						     ND_OPT_SOURCE_LL_ADDR);
+			if ((neigh = rt->rt6i_nexthop) != NULL &&
+			    skb->dev->addr_len + 2 >= len)
+				neigh_update(neigh, opt+2, NUD_STALE, 1, 1);
 			break;
 
                 case ND_OPT_PREFIX_INFO:
@@ -1204,25 +662,30 @@
 					break;
 
 				if (mtu < 576) {
-					printk(KERN_DEBUG "NDISC: router "
-					       "announcement with mtu = %d\n",
-					       mtu);
+					ND_PRINTK0("NDISC: router "
+						   "announcement with mtu = %d\n",
+						   mtu);
 					break;
 				}
 
+#if 0
+				/* Bad idea. Sorry, this thing is not
+				   so easy to implement		--ANK
+				 */
 				if (dev->change_mtu)
 					dev->change_mtu(dev, mtu);
 				else
 					dev->mtu = mtu;
+#endif
 			}
                         break;
 
 		case ND_OPT_TARGET_LL_ADDR:
 		case ND_OPT_REDIRECT_HDR:
-			printk(KERN_DEBUG "got illegal option with RA");
+			ND_PRINTK0("got illegal option with RA");
 			break;
 		default:
-			printk(KERN_DEBUG "unkown option in RA\n");
+			ND_PRINTK0("unkown option in RA\n");
                 };
                 optlen -= len;
                 opt += len;
@@ -1251,13 +714,10 @@
 	struct icmp6hdr *icmph;
 	struct in6_addr *dest;
 	struct in6_addr *target;	/* new first hop to destination */
-	struct nd_neigh *ndn;
+	struct neighbour *neigh;
 	struct rt6_info *rt;
 	int on_link = 0;
 	int optlen;
-	u8 * opt;
-
-	NDBG(("ndisc_redirect_rcv(%p)\n", skb));
 
 	if (skb->nh.ipv6h->hop_limit != 255) {
 		printk(KERN_WARNING "NDISC: fake ICMP redirect received\n");
@@ -1293,28 +753,20 @@
 		return;
 	}
 
-	/* passed validation tests */
+	/* passed validation tests
+
+	   NOTE We should not install redirect if sender did not supply
+	   ll address on link, which requires it. It would break, if
+	   we have non-transitive address resolution protocol.
+	   Fix it later. --ANK
+	 */
 	rt = rt6_redirect(dest, &skb->nh.ipv6h->saddr, target, skb->dev, on_link);
 
 	if (rt == NULL)
 		return;
 
-	ndn = (struct nd_neigh *) rt->rt6i_nexthop;
-
-	opt = (u8 *) (dest + 1);
-
-	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;
-	}
+	neigh = rt->rt6i_nexthop;
+	ndisc_update(neigh, (u8*)(dest + 1), optlen, ND_OPT_TARGET_LL_ADDR);
 }
 
 void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
@@ -1323,13 +775,11 @@
 	struct sock *sk = ndisc_socket->sk;
 	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 icmp6hdr *icmph;
 	struct in6_addr *addrp;
 	struct device *dev;
 	struct rt6_info *rt;
-	int ta_len = 0;
 	u8 *opt;
 	int rd_len;
 	int err;
@@ -1339,22 +789,25 @@
 	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
+		ND_PRINTK1("ndisc_send_redirect: hostunreach\n");
 		return;
 	}
 
 	if (rt->rt6i_flags & RTF_GATEWAY) {
-#if ND_DEBUG >= 1
-		printk(KERN_DEBUG "ndisc_send_redirect: not a neighbour\n");
-#endif
+		ND_PRINTK1("ndisc_send_redirect: not a neighbour\n");
 		return;
 	}
 
-	if (ndn->ndn_nud_state == NUD_REACHABLE) {
-		ta_len  = ((dev->addr_len + 1) >> 3) + 1;
-		len += (ta_len << 3);
+	if (dev->addr_len) {
+		if (neigh->nud_state&NUD_VALID) {
+			len  += NDISC_OPT_SPACE(dev->addr_len);
+		} else {
+			/* If nexthop is not valid, do not redirect!
+			   We will make it later, when will be sure,
+			   that it is alive.
+			 */
+			return;
+		}
 	}
 
 	rd_len = min(536 - len, ntohs(skb->nh.ipv6h->payload_len) + 8);
@@ -1364,18 +817,14 @@
 	ifp = ipv6_get_lladdr(dev);
 
 	if (ifp == NULL) {
-#if ND_DEBUG >= 1
-		printk(KERN_DEBUG "redirect: no link_local addr for dev\n");
-#endif
+		ND_PRINTK1("redirect: no link_local addr for dev\n");
 		return;
 	}
 
 	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
+		ND_PRINTK1("ndisc_send_redirect: alloc_skb failed\n");
 		return;
 	}
 	
@@ -1409,29 +858,8 @@
 	 *	include target_address option
 	 */
 
-	if (ta_len) {
-		int zb;
-		
-		*(opt++) = ND_OPT_TARGET_LL_ADDR;
-		*(opt++) = ta_len;
-
-		memcpy(opt, neigh->ha, neigh->dev->addr_len);
-		opt += neigh->dev->addr_len;
-
-		/* 
-		 *	if link layer address doesn't end on a 8 byte
-		 *	boundary memset(0) the remider
-		 */
-
-		zb = (neigh->dev->addr_len + 2) & 0x7; 
-		if (zb) {
-			int comp;
-
-			comp = 8 - zb;
-			memset(opt, 0, comp);
-			opt += comp;
-		}
-	}
+	if (dev->addr_len)
+		opt = ndisc_fill_option(opt, ND_OPT_TARGET_LL_ADDR, neigh->ha, dev->addr_len);
 
 	/*
 	 *	build redirect option and copy skb over to the new packet.
@@ -1451,24 +879,37 @@
 	dev_queue_xmit(buff);
 }
 
-/* Called by upper layers to validate neighbour cache entries. */
-
-void ndisc_validate(struct neighbour *neigh)
+static __inline__ struct neighbour *
+ndisc_recv_ns(struct in6_addr *saddr, struct sk_buff *skb)
 {
-	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
+	u8 *opt;
 
-	if (neigh == NULL)
-		return;
+	opt = skb->h.raw;
+	opt += sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+	opt = ndisc_find_option(opt, skb->dev->addr_len+2, skb->tail - opt, ND_OPT_SOURCE_LL_ADDR);
+
+	return neigh_event_ns(&nd_tbl, opt, saddr, skb->dev);
+}
+
+static __inline__ int ndisc_recv_na(struct neighbour *neigh, struct sk_buff *skb)
+{
+	struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
+	u8 *opt;
 
-        if (ndn->ndn_nud_state == NUD_INCOMPLETE)
-                return;
+	opt = skb->h.raw;
+	opt += sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+	opt = ndisc_find_option(opt, skb->dev->addr_len+2, skb->tail - opt, ND_OPT_TARGET_LL_ADDR);
 
-        if (ndn->ndn_nud_state == NUD_DELAY) 
-                ndisc_del_timer(ndn);
+	return neigh_update(neigh, opt,
+			    msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
+			    msg->icmph.icmp6_override, 1);
+}
 
-        nd_stats.rcv_upper_conf++;
-        ndn->ndn_nud_state = NUD_REACHABLE;
-        ndn->ndn_tstamp = jiffies;
+static void pndisc_redo(struct sk_buff *skb)
+{
+	ndisc_rcv(skb, skb->dev, &skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
+		  NULL, skb->len);
+	kfree_skb(skb, FREE_WRITE);
 }
 
 int ndisc_rcv(struct sk_buff *skb, struct device *dev,
@@ -1476,27 +917,24 @@
 	      struct ipv6_options *opt, unsigned short len)
 {
 	struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
-	struct nd_neigh *ndn;
+	struct neighbour *neigh;
 	struct inet6_ifaddr *ifp;
 
-	NDBG(("ndisc_rcv(type=%d) ", msg->icmph.icmp6_type));
 	switch (msg->icmph.icmp6_type) {
 	case NDISC_NEIGHBOUR_SOLICITATION:
-		NDBG(("NS "));
-		if ((ifp = ipv6_chk_addr(&msg->target)) != NULL) {
+		if ((ifp = ipv6_chk_addr(&msg->target, dev, 1)) != NULL) {
 			int addr_type = ipv6_addr_type(saddr);
 
+			if (ifp->flags & ADDR_INVALID)
+				return 0;
 			if (ifp->flags & DAD_INCOMPLETE) {
 				/* Address is tentative. If the source
 				   is unspecified address, it is someone
 				   does DAD, otherwise we ignore solicitations
 				   until DAD timer expires.
 				 */
-				if (addr_type == IPV6_ADDR_ANY) {
-					printk(KERN_INFO "%s: duplicate address detected!\n",
-					       ifp->idev->dev->name);
-					del_timer(&ifp->timer);
-				}
+				if (addr_type == IPV6_ADDR_ANY)
+					addrconf_dad_failure(ifp);
 				return 0;
 			}
 
@@ -1510,46 +948,75 @@
 			}
 
 			if (addr_type & IPV6_ADDR_UNICAST) {
-				int inc;
+				int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
+
+				if (inc)
+					nd_tbl.stats.rcv_probes_mcast++;
+				else
+					nd_tbl.stats.rcv_probes_ucast++;
 
 				/* 
 				 *	update / create cache entry
 				 *	for the source adddress
 				 */
 
-				nd_stats.rcv_probes_ucast++;
+				neigh = ndisc_recv_ns(saddr, skb);
 
-				ndn = ndisc_event_ns(saddr, skb);
+				if (neigh) {
+					ndisc_send_na(dev, neigh, saddr, &ifp->addr, 
+						      ifp->idev->router, 1, inc, inc);
+					neigh_release(neigh);
+				}
+			}
+		} else {
+			struct inet6_dev *in6_dev = ipv6_get_idev(dev);
+			int addr_type = ipv6_addr_type(saddr);
 
-				if (ndn == NULL)
+			if (in6_dev && in6_dev->router &&
+			    (addr_type & IPV6_ADDR_UNICAST) &&
+			    pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) {
+				int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
+
+				if (skb->stamp.tv_sec == 0 ||
+				    skb->pkt_type == PACKET_HOST ||
+				    inc == 0 ||
+				    in6_dev->nd_parms->proxy_delay == 0) {
+					if (inc)
+						nd_tbl.stats.rcv_probes_mcast++;
+					else
+						nd_tbl.stats.rcv_probes_ucast++;
+
+					neigh = ndisc_recv_ns(saddr, skb);
+
+					if (neigh) {
+						ndisc_send_na(dev, neigh, saddr, &msg->target,
+							      1, 0, inc, inc);
+						neigh_release(neigh);
+					}
+				} else {
+					/* Hack. It will be freed upon exit from
+					   ndisc_rcv
+					 */
+					atomic_inc(&skb->users);
+					pneigh_enqueue(&nd_tbl, in6_dev->nd_parms, skb);
 					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 {
-#if ND_DEBUG >= 1
-				printk(KERN_DEBUG "ns: non unicast saddr\n");
-#endif
+				}
 			}
 		}
-		break;
+		return 0;
 
 	case NDISC_NEIGHBOUR_ADVERTISEMENT:
-		NDBG(("NA "));
 		if ((ipv6_addr_type(saddr)&IPV6_ADDR_MULTICAST) &&
 		    msg->icmph.icmp6_solicited) {
-			printk(KERN_DEBUG "NDISC: solicited NA is multicasted\n");
+			ND_PRINTK0("NDISC: solicited NA is multicasted\n");
 			return 0;
 		}
-		if ((ifp = ipv6_chk_addr(&msg->target))) {
+		/* BUG! Target can be link-local on ANOTHER interface. Fixed. */
+		if ((ifp = ipv6_chk_addr(&msg->target, dev, 1))) {
+			if (ifp->flags & ADDR_INVALID)
+				return 0;
 			if (ifp->flags & DAD_INCOMPLETE) {
-				/* Address is duplicate. */
-				printk(KERN_INFO "%s: duplicate address detected!\n",
-				       ifp->idev->dev->name);
-				del_timer(&ifp->timer);
+				addrconf_dad_failure(ifp);
 				return 0;
 			}
 			/* What should we make now? The advertisement
@@ -1557,18 +1024,14 @@
 			   about it. It could be misconfiguration, or
 			   an smart proxy agent tries to help us :-)
 			 */
-			printk(KERN_DEBUG "%s: someone avertise our address!\n",
-			       ifp->idev->dev->name);
+			ND_PRINTK0("%s: someone avertise our address!\n",
+				   ifp->idev->dev->name);
 			return 0;
 		}
-		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);
+		neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 0);
 
-		if (ndn) {
-			if (ndn->ndn_flags & NCF_ROUTER) {
+		if (neigh) {
+			if (neigh->flags & NTF_ROUTER) {
 				if (msg->icmph.icmp6_router == 0) {
 					/*
 					 *	Change: router to host
@@ -1583,27 +1046,23 @@
 				}
 			} else {
 				if (msg->icmph.icmp6_router)
-					ndn->ndn_flags |= NCF_ROUTER;
+					neigh->flags |= NTF_ROUTER;
 			}
-			ndisc_event_na(ndn, (unsigned char *) &msg->opt,
-				       skb->tail - (u8 *)&msg->opt /*opt_len*/,
-				       msg->icmph.icmp6_solicited,
-				       msg->icmph.icmp6_override);
+
+			ndisc_recv_na(neigh, skb);
+			neigh_release(neigh);
 		}
 		break;
-
 	};
 
 	if (ipv6_config.forwarding == 0) {
 		switch (msg->icmph.icmp6_type) {
 		case NDISC_ROUTER_ADVERTISEMENT:
-			NDBG(("RA "));
 			if (ipv6_config.accept_ra)
 				ndisc_router_discovery(skb);
 			break;
 
 		case NDISC_REDIRECT:
-			NDBG(("REDIR "));
 			if (ipv6_config.accept_redirects)
 				ndisc_redirect_rcv(skb);
 			break;
@@ -1614,68 +1073,70 @@
 }
 
 #ifdef CONFIG_PROC_FS
-int ndisc_get_info(char *buffer, char **start, off_t offset, int length,
-		   int dummy)
+#ifndef CONFIG_RTNETLINK
+int ndisc_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
+	int len=0;
+	off_t pos=0;
+	int size;
 	unsigned long now = jiffies;
-	int len = 0;
 	int i;
 
 	neigh_table_lock(&nd_tbl);
 
-	for (i = 0; i < nd_tbl.tbl_size; i++) {
-		struct neighbour *neigh, *head;
-		head = nd_tbl.hash_buckets[i];
-		
-		if ((neigh = head) == NULL)
-			continue;
+	for (i = 0; i <= NEIGH_HASHMASK; i++) {
+		struct neighbour *neigh;
 
-		do {
-			struct nd_neigh *ndn = (struct nd_neigh *) neigh;
+		for (neigh = nd_tbl.hash_buckets[i]; neigh; neigh = neigh->next) {
 			int j;
 
+			size = 0;
 			for (j=0; j<16; j++) {
-				sprintf(buffer + len, "%02x",
-					ndn->ndn_addr.s6_addr[j]);
-				len += 2;
+				sprintf(buffer+len+size, "%02x", neigh->primary_key[j]);
+				size += 2;
 			}
 
-			len += sprintf(buffer + len,
-				       " %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 ? ndn->ndn_expires - now : 0,
-				       now - ndn->ndn_tstamp,
-				       nd_reachable_time,
-				       nd_gc_staletime,
-				       atomic_read(&ndn->ndn_refcnt),
-				       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++) {
-					sprintf(buffer + len, "%02x",
-						neigh->ha[j]);
-					len += 2;
+			size += sprintf(buffer+len+size,
+				       " %02x %02x %02x %02x %08lx %08lx %08x %04x %04x %04x %8s ", i,
+				       128,
+				       neigh->type,
+				       neigh->nud_state,
+				       now - neigh->used,
+				       now - neigh->confirmed,
+				       neigh->parms->reachable_time,
+				       neigh->parms->gc_staletime,
+				       atomic_read(&neigh->refcnt),
+				       neigh->flags | (!neigh->hh ? 0 : (neigh->hh->hh_output==dev_queue_xmit ? 4 : 2)),
+				       neigh->dev->name);
+
+			if ((neigh->nud_state&NUD_VALID) && neigh->dev->addr_len) {
+				for (j=0; j < neigh->dev->addr_len; j++) {
+					sprintf(buffer+len+size, "%02x", neigh->ha[j]);
+					size += 2;
 				}
 			} else {
-                                len += sprintf(buffer + len, "000000000000");
+                                size += sprintf(buffer+len+size, "000000000000");
 			}
-			len += sprintf(buffer + len, "\n");
-			
-			neigh = neigh->next;
-		} while (neigh != head);
+			size += sprintf(buffer+len+size, "\n");
+			len += size;
+			pos += size;
+		  
+			if (pos <= offset)
+				len=0;
+			if (pos >= offset+length)
+				goto done;
+		}
 	}
 
+done:
 	neigh_table_unlock(&nd_tbl);
-	
-	*start = buffer + offset;
-
-	len -= offset;
 
-	if (len > length)
-		len = length;
+	*start = buffer+len-(pos-offset);	/* Start of wanted data */
+	len = pos-offset;			/* Start slop */
+	if (len>length)
+		len = length;			/* Ending slop */
+	if (len<0)
+		len = 0;
 	return len;
 }
 
@@ -1686,8 +1147,11 @@
         0, NULL,
         &ndisc_get_info
 };
+#endif
 #endif	/* CONFIG_PROC_FS */
 
+
+
 __initfunc(void ndisc_init(struct net_proto_family *ops))
 {
 	struct sock *sk;
@@ -1700,12 +1164,13 @@
 
         ndisc_socket->inode = &ndisc_inode;
         ndisc_socket->state = SS_UNCONNECTED;
-        ndisc_socket->type=SOCK_RAW;
+        ndisc_socket->type  = SOCK_RAW;
 
 	if((err=ops->create(ndisc_socket, IPPROTO_ICMPV6))<0)
 		printk(KERN_DEBUG 
 		       "Failed to create the NDISC control socket.\n");
 
+	/* Eeeh... What is it? --ANK */
 	MOD_DEC_USE_COUNT;
 
 	sk = ndisc_socket->sk;
@@ -1718,25 +1183,16 @@
          * Initialize the neighbour table
          */
 	
-	neigh_table_init(&nd_tbl, &nd_neigh_ops, NCACHE_NUM_BUCKETS);
- 
-        /* General ND state machine timer. */
-	init_timer(&ndisc_timer);
-	ndisc_timer.function = ndisc_timer_handler;
-	ndisc_timer.data = 0L;
-	ndisc_timer.expires = 0L;
-
-        /* ND GC timer */
-        init_timer(&ndisc_gc_timer);
-        ndisc_gc_timer.function = ndisc_periodic_timer;
-        ndisc_gc_timer.data = 0L;
-        ndisc_gc_timer.expires = jiffies + nd_gc_interval;
-
-	add_timer(&ndisc_gc_timer);
+	neigh_table_init(&nd_tbl);
 
 #ifdef CONFIG_PROC_FS
+#ifndef CONFIG_RTNETLINK
 	proc_net_register(&ndisc_proc_entry);
 #endif
+#endif
+#ifdef CONFIG_SYSCTL
+	nd_tbl.parms.sysctl_table = neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+#endif
 }
 
 #ifdef MODULE
@@ -1745,7 +1201,6 @@
 #ifdef CONFIG_PROC_FS
         proc_net_unregister(ndisc_proc_entry.low_ino);
 #endif
-	del_timer(&ndisc_gc_timer);
-	del_timer(&ndisc_timer);
+	neigh_table_clear(&nd_tbl);
 }
 #endif
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/raw.c linux/net/ipv6/raw.c
--- v2.1.78/linux/net/ipv6/raw.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/raw.c	Mon Jan 12 15:28:28 1998
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/raw.c
  *
- *	$Id: raw.c,v 1.13 1997/09/14 08:32:14 davem Exp $
+ *	$Id: raw.c,v 1.16 1997/12/29 19:52:48 kuznet Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -25,6 +25,7 @@
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
 #include <linux/icmpv6.h>
+#include <asm/uaccess.h>
 
 #include <net/sock.h>
 #include <net/snmp.h>
@@ -165,7 +166,7 @@
 			 */
 			v4addr = LOOPBACK4_IPV6;
 			if (!(addr_type & IPV6_ADDR_MULTICAST))	{
-				if (ipv6_chk_addr(&addr->sin6_addr) == NULL)
+				if (ipv6_chk_addr(&addr->sin6_addr, NULL, 0) == NULL)
 					return(-EADDRNOTAVAIL);
 			}
 		}
@@ -359,7 +360,15 @@
 	int hlimit = -1;
 	u16 proto;
 	int err;
-	
+
+	/* Rough check on arithmetic overflow,
+	   better check is made in ip6_build_xmit
+
+	   When jumbo header will be implemeted we will remove it
+	   at all (len will be size_t)
+	 */
+	if (len < 0 || len > 0xFFFF)
+		return -EMSGSIZE;
 
 	/* Mirror BSD error message compatibility */
 	if (msg->msg_flags & MSG_OOB)		
@@ -389,9 +398,12 @@
 
 		daddr = &sin6->sin6_addr;
 		
-		if (np->dst && ipv6_addr_cmp(daddr, &np->daddr)) {
-			dst_release(np->dst);
-			np->dst = NULL;
+		/* BUGGGG If route is not cloned, this check always
+		   fails, hence dst_cache only slows down tramsmission --ANK
+		 */
+		if (sk->dst_cache && ipv6_addr_cmp(daddr, &np->daddr)) {
+			dst_release(sk->dst_cache);
+			sk->dst_cache = NULL;
 		}		
 	} else {
 		if (sk->state != TCP_ESTABLISHED) 
@@ -409,12 +421,6 @@
 		return(-EINVAL);
 	}
 
-	/*
-	 *	We don't allow > 64K sends yet.		 
-	 */
-	if (len + (sk->ip_hdrincl ? 0 : sizeof(struct ipv6hdr)) > 65535)
-		return -EMSGSIZE;
-
 	if (msg->msg_controllen) {
 		opt = &opt_space;
 		memset(opt, 0, sizeof(struct ipv6_options));
@@ -592,14 +598,9 @@
 
 static void rawv6_close(struct sock *sk, unsigned long timeout)
 {
-	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-
 	sk->state = TCP_CLOSE;
-
-	if (np->dst)
-		dst_release(np->dst);
-
 	ipv6_sock_mc_close(sk);
+	sk->dead = 1;
 	destroy_sock(sk);
 }
 
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/reassembly.c linux/net/ipv6/reassembly.c
--- v2.1.78/linux/net/ipv6/reassembly.c	Thu Mar 27 14:40:17 1997
+++ linux/net/ipv6/reassembly.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: reassembly.c,v 1.7 1997/03/18 18:24:47 davem Exp $
+ *	$Id: reassembly.c,v 1.8 1997/12/29 19:52:50 kuznet Exp $
  *
  *	Based on: net/ipv4/ip_fragment.c
  *
@@ -273,7 +273,9 @@
 	payload_len = (unfrag_len + tail->offset + 
 		       (tail->skb->tail - (__u8 *) (tail->fhdr + 1)));
 
+#if 0
 	printk(KERN_DEBUG "reasm: payload len = %d\n", payload_len);
+#endif
 
 	if ((skb = dev_alloc_skb(sizeof(struct ipv6hdr) + payload_len))==NULL) {
 		printk(KERN_DEBUG "reasm_frag: no memory for reassembly\n");
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/route.c linux/net/ipv6/route.c
--- v2.1.78/linux/net/ipv6/route.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/route.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: route.c,v 1.18 1997/10/17 00:15:05 freitag Exp $
+ *	$Id: route.c,v 1.19 1997/12/13 21:53:16 kuznet Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -37,6 +37,7 @@
 #include <net/ndisc.h>
 #include <net/addrconf.h>
 #include <linux/netlink.h>
+#include <linux/rtnetlink.h>
 
 #include <asm/uaccess.h>
 
@@ -51,24 +52,29 @@
 #define RDBG(x)
 #endif
 
+static struct rt6_info * ip6_rt_copy(struct rt6_info *ort);
 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 struct dst_entry *ip6_negative_advice(struct dst_entry *);
 
 static int		ip6_pkt_discard(struct sk_buff *skb);
 
 struct dst_ops ip6_dst_ops = {
 	AF_INET6,
+	__constant_htons(ETH_P_IPV6),
 	ip6_dst_check,
 	ip6_dst_reroute,
-	NULL
+	NULL,
+	ip6_negative_advice
 };
 
 struct rt6_info ip6_null_entry = {
 	{{NULL, ATOMIC_INIT(0), ATOMIC_INIT(0), NULL,
-	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL,
+	  -1, 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,
+	NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0U,
 	0, {NULL}, {{{{0}}}, 128}, {{{{0}}}, 128}
 };
 
@@ -220,14 +226,14 @@
 
 	RDBG(("rt6_best_dflt(%p,%p): ", rt, dev));
 	for (sprt = rt; sprt; sprt = sprt->u.next) {
-		struct nd_neigh *ndn;
+		struct neighbour *neigh;
 
 		RDBG(("sprt(%p): ", sprt));
-		if ((ndn = (struct nd_neigh *) sprt->rt6i_nexthop)) {
+		if ((neigh = sprt->rt6i_nexthop)) {
 			int m = -1;
 
-			RDBG(("nxthop(%p,%d) ", ndn, ndn->ndn_nud_state));
-			switch (ndn->ndn_nud_state) {
+			RDBG(("nxthop(%p,%d) ", neigh, neigh->nud_state));
+			switch (neigh->nud_state) {
 			case NUD_REACHABLE:
 				RDBG(("NUD_REACHABLE "));
 				if (sprt != rt6_dflt_pointer) {
@@ -304,14 +310,16 @@
 	return rt;
 }
 
-static struct rt6_info *rt6_cow(struct rt6_info *rt, struct in6_addr *daddr,
+static struct rt6_info *rt6_cow(struct rt6_info *ort, struct in6_addr *daddr,
 				struct in6_addr *saddr)
 {
+	struct rt6_info *rt;
+
 	/*
 	 *	Clone the route.
 	 */
 
-	rt = ip6_rt_copy(rt);
+	rt = ip6_rt_copy(ort);
 
 	if (rt) {
 		ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
@@ -432,7 +440,7 @@
 
 	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;
+	strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL);
 
 	rt6_lock();
 #if RT6_DEBUG >= 3
@@ -461,12 +469,28 @@
 
 	RDBG(("-->(%p[%s])) ", fn, fn == &ip6_routing_table ? "ROOT" : "!ROOT"));
 
+restart:
 	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);
+
+			/* BUGGGG! It is capital bug, that was hidden
+			   by not-cloning multicast routes. However,
+			   the same problem was with link-local addresses.
+			   Fix is the following if-statement,
+			   but it will not properly handle Pedro's subtrees --ANK
+			 */
+			if (rt == &ip6_null_entry && strict) {
+				while ((fn = fn->parent) != NULL) {
+					if (fn->fn_flags & RTN_ROOT)
+						goto out;
+					if (fn->fn_flags & RTN_RTINFO)
+						goto restart;
+				}
+			}
 			RDBG(("devmatch(%p) ", rt));
 			goto out;
 		}
@@ -517,7 +541,7 @@
 }
 
 
-void rt6_ins(struct rt6_info *rt)
+static void rt6_ins(struct rt6_info *rt)
 {
 	start_bh_atomic();
 	if (atomic_read(&rt6_tbl_lock) == 1)
@@ -529,29 +553,33 @@
 
 /*
  *	Destination cache support functions
+ *
+ *	BUGGG! This function is absolutely wrong.
+ *	First of all it is never called. (look at include/net/dst.h)
+ *	Second, even when it is called rt->rt6i_node == NULL
+ *	  ** partially fixed: now dst->obsolete = -1 for IPv6 not cache routes.
+ *	Third, even we fixed previous bugs,
+ *	it will not work because sernum is incorrectly checked/updated and
+ *	it does not handle change of the parent of cloned route.
+ *	Purging stray clones is not easy task, it would require
+ *	massive remake of ip6_fib.c. Alas...
+ *							--ANK
  */
 
-struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
+static 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);
-
+	if (rt && rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
 		return dst;
-	}
 
 	dst_release(dst);
 	return NULL;
 }
 
-struct dst_entry *ip6_dst_reroute(struct dst_entry *dst, struct sk_buff *skb)
+static struct dst_entry *ip6_dst_reroute(struct dst_entry *dst, struct sk_buff *skb)
 {
 	/*
 	 *	FIXME
@@ -561,6 +589,13 @@
 	return NULL;
 }
 
+static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
+{
+	dst_release(dst);
+	return NULL;
+}
+
+
 /* Clean host part of a prefix. Not necessary in radix tree,
    but results in cleaner routing tables.
 
@@ -592,6 +627,8 @@
 		*err = -EINVAL;
 		return NULL;
 	}
+	if (rtmsg->rtmsg_metric == 0)
+		rtmsg->rtmsg_metric = IP6_RT_PRIO_USER;
 
 	*err = 0;
 	
@@ -603,6 +640,9 @@
 		goto out;
 	}
 
+	rt->u.dst.obsolete = -1;
+	rt->rt6i_expires = rtmsg->rtmsg_info;
+
 	addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst);
 	
 	if (addr_type & IPV6_ADDR_MULTICAST) {
@@ -613,7 +653,7 @@
 		rt->u.dst.input = ip6_forward;
 	}
 
-	rt->u.dst.output = dev_queue_xmit;
+	rt->u.dst.output = ip6_output;
 
 	if (rtmsg->rtmsg_ifindex) {
 		dev = dev_get_by_index(rtmsg->rtmsg_ifindex);
@@ -665,9 +705,16 @@
 			*err = -EINVAL;
 			goto out;
 		}
+	}
 
-		rt->rt6i_nexthop = ndisc_get_neigh(dev, gw_addr);
+	if (dev == NULL) {
+		RDBG(("!dev, "));
+		*err = -ENODEV;
+		goto out;
+	}
 
+	if (rtmsg->rtmsg_flags & (RTF_GATEWAY|RTF_NONEXTHOP)) {
+		rt->rt6i_nexthop = ndisc_get_neigh(dev, &rt->rt6i_gateway);
 		if (rt->rt6i_nexthop == NULL) {
 			RDBG(("!nxthop, "));
 			*err = -ENOMEM;
@@ -676,12 +723,6 @@
 		RDBG(("nxthop, "));
 	}
 
-	if (dev == NULL) {
-		RDBG(("!dev, "));
-		*err = -ENODEV;
-		goto out;
-	}
-
 	rt->rt6i_metric = rtmsg->rtmsg_metric;
 
 	rt->rt6i_dev = dev;
@@ -694,6 +735,29 @@
 	rt6_ins(rt);
 	rt6_unlock();
 
+	/* BUGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG!
+
+	   If rt6_ins will fail (and it occurs regularly f.e. if route
+	   already existed), the route will be freed -> Finita.
+	   Crash. No recovery. NO FIX. Unfortunately, it is not the only
+	   place will it is fatal. It is sad, I believed this
+	   code is a bit more accurate :-(
+
+	   Really, the problem can be solved in two ways:
+
+	   * As I did in old 2.0 IPv4: to increase use count and force
+	     user to destroy stray route. It requires some care,
+	     well, much more care.
+	   * Second and the best: to get rid of this damn backlogging
+	     system. I wonder why Pedro so liked it. It was the most
+	     unhappy day when I invented it (well, by a strange reason
+	     I believed that it is very clever :-)),
+	     and when I managed to clean IPv4 of this crap,
+	     it was really great win.
+	     BTW I forgot how 2.0 route/arp works :-) :-)
+	                                                               --ANK
+	 */
+
 out:
 	if (*err) {
 		RDBG(("dfree(%p) ", rt));
@@ -701,7 +765,17 @@
 		rt = NULL;
 	}
 	RDBG(("ret(%p)\n", rt));
+#if 0
 	return rt;
+#else
+	/* BUGGG! For now always return NULL. (see above)
+
+	   Really, it was used only in two places, and one of them
+	   (rt6_add_dflt_router) is repaired, ip6_fw is not essential
+	   at all. --ANK
+	 */
+	return NULL;
+#endif
 }
 
 int ip6_del_rt(struct rt6_info *rt)
@@ -710,6 +784,12 @@
 
 	start_bh_atomic();
 
+	/* I'd add here couple of cli()
+	   cli(); cli(); cli();
+
+	   Now it is really LOCKED. :-) :-) --ANK
+	 */
+
 	rt6_dflt_pointer = NULL;
 
 	if (atomic_read(&rt6_tbl_lock) == 1)
@@ -737,15 +817,34 @@
 	/*
 	 *	Find route
 	 */
-	rt=rt6_lookup(&rtmsg->rtmsg_dst, &rtmsg->rtmsg_src, dev, dev ? RTF_LINKRT : 0);
+	rt=rt6_lookup(&rtmsg->rtmsg_dst, &rtmsg->rtmsg_src, dev, RTF_LINKRT);
 
 	/*
 	 *	Blow it away
+	 *
+	 *	BUGGGG It will not help with Pedro's subtrees.
+	 *	We urgently need fib6_locate_node function, and
+	 *	it is not the only place where rt6_lookup is used
+	 *	for wrong purpose.
+	 *							--ANK
 	 */
-	if(rt && rt->rt6i_dst.plen == rtmsg->rtmsg_dst_len &&
-	   rt->rt6i_src.plen == rtmsg->rtmsg_src_len) {
-		ip6_del_rt(rt);
-		return 0;
+restart:
+	if (rt && rt->rt6i_src.plen == rtmsg->rtmsg_src_len) {
+		if (rt->rt6i_dst.plen > rtmsg->rtmsg_dst_len) {
+			struct fib6_node *fn = rt->rt6i_node;
+			while ((fn = fn->parent) != NULL) {
+				if (fn->fn_flags & RTN_ROOT)
+					break;
+				if (fn->fn_flags & RTN_RTINFO) {
+					rt = rt6_device_match(rt, dev, RTF_LINKRT);
+					goto restart;
+				}
+			}
+		}
+		if (rt->rt6i_dst.plen == rtmsg->rtmsg_dst_len) {
+			ip6_del_rt(rt);
+			return 0;
+		}
 	}
 
 	return -ESRCH;
@@ -773,7 +872,7 @@
 	rt6_bh_mask = 0;
 }
 
-#ifdef CONFIG_NETLINK
+#ifdef CONFIG_IPV6_NETLINK
 /*
  *	NETLINK interface
  *	routing socket moral equivalent
@@ -785,6 +884,7 @@
 	struct in6_rtmsg *rtmsg;
 	int err;
 
+	rtnl_lock();
 	while (skb->len) {
 		if (skb->len < sizeof(struct in6_rtmsg)) {
 			count = -EINVAL;
@@ -809,10 +909,10 @@
 	}
 
 out:
+	rtnl_unlock();
 	kfree_skb(skb, FREE_READ);	
 	return count;
 }
-#endif /* CONFIG_NETLINK */
 
 static void rt6_sndrtmsg(struct in6_rtmsg *rtmsg)
 {
@@ -825,9 +925,7 @@
 	memcpy(skb_put(skb, sizeof(struct in6_rtmsg)), &rtmsg,
 	       sizeof(struct in6_rtmsg));
 	
-#ifdef CONFIG_NETLINK
 	if (netlink_post(NETLINK_ROUTE6, skb))
-#endif
 		kfree_skb(skb, FREE_WRITE);
 }
 
@@ -867,11 +965,10 @@
 
 	msg->rtmsg_flags = flags;
 
-#ifdef CONFIG_NETLINK
 	if (netlink_post(NETLINK_ROUTE6, skb))
-#endif
 		kfree_skb(skb, FREE_WRITE);
 }
+#endif /* CONFIG_IPV6_NETLINK */
 
 /*
  *	Handle redirects
@@ -888,6 +985,12 @@
 	if (rt == NULL || rt->u.dst.error)
 		return NULL;
 
+	/* Redirect received -> path was valid.
+	   Look, redirects are sent only in response to data packets,
+	   so that this nexthop apparently is reachable. --ANK
+	 */
+	dst_confirm(&rt->u.dst);
+
 	/* Duplicate redirect: silently ignore. */
 	if (ipv6_addr_cmp(target, &rt->rt6i_gateway) == 0)
 		return NULL;
@@ -931,21 +1034,32 @@
 	 *	We have finally decided to accept it.
 	 */
 	if (rt->rt6i_dst.plen == 128) {
+		/* BUGGGG! Very bad bug. Fast path code does not protect
+		 * itself of changing nexthop on the fly, it was supposed
+		 * that crucial parameters (dev, nexthop, hh) ARE VOLATILE.
+		 *                                                   --ANK
+		 * Not fixed!! I plugged it to avoid random crashes
+		 * (they are very unlikely, but I do not want to shrug
+		 *  every time when redirect arrives)
+		 * but the plug must be removed. --ANK
+		 */
+
+#if 0
 		/*
 		 *	Already a host route.
 		 *
 		 */
 		if (rt->rt6i_nexthop)
 			neigh_release(rt->rt6i_nexthop);
-		/*
-		 *	purge hh_cache
-		 */
 		rt->rt6i_flags |= RTF_MODIFIED | RTF_CACHE;
 		if (on_link)
 			rt->rt6i_flags &= ~RTF_GATEWAY;
 		ipv6_addr_copy(&rt->rt6i_gateway, target);
 		rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, target);
 		return rt;
+#else
+		return NULL;
+#endif
 	}
 
 	nrt = ip6_rt_copy(rt);
@@ -965,6 +1079,7 @@
 	rt6_ins(nrt);
 	rt6_unlock();
 
+	/* BUGGGGGGG! nrt can point to nowhere. */
 	return nrt;
 }
 
@@ -975,7 +1090,7 @@
 
 void rt6_pmtu_discovery(struct in6_addr *addr, struct device *dev, int pmtu)
 {
-	struct rt6_info *rt;
+	struct rt6_info *rt, *nrt;
 
 	if (pmtu < 576 || pmtu > 65536) {
 #if RT6_DEBUG >= 1
@@ -994,13 +1109,21 @@
 		return;
 	}
 
+	if (pmtu >= rt->u.dst.pmtu)
+		return;
+
+	/* New mtu received -> path was valid.
+	   They are sent only in response to data packets,
+	   so that this nexthop apparently is reachable. --ANK
+	 */
+	dst_confirm(&rt->u.dst);
+
 	/* It is wrong, but I plugged the hole here.
 	   On-link routes are cloned differently,
 	   look at rt6_redirect --ANK
 	 */
-	if (!(rt->rt6i_flags&RTF_GATEWAY)) {
+	if (!(rt->rt6i_flags&RTF_GATEWAY))
 		return;
-	}
 
 	if (rt->rt6i_dst.plen == 128) {
 		/*
@@ -1012,11 +1135,18 @@
 		return;
 	}
 
-	rt = ip6_rt_copy(rt);
-	ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
-	rt->rt6i_dst.plen = 128;
+	nrt = ip6_rt_copy(rt);
+	ipv6_addr_copy(&nrt->rt6i_dst.addr, addr);
+	nrt->rt6i_dst.plen = 128;
+
+	nrt->rt6i_flags |= (RTF_DYNAMIC | RTF_CACHE);
 
-	rt->rt6i_flags |= (RTF_DYNAMIC | RTF_CACHE);
+	/* It was missing. :-) :-)
+	   I wonder, kernel was deemed to crash after pkt_too_big
+	   and nobody noticed it. Hey, guys, do someone really
+	   use it? --ANK
+	 */
+	nrt->rt6i_nexthop = neigh_clone(rt->rt6i_nexthop);
 
 	rt6_lock();
 	rt6_ins(rt);
@@ -1027,7 +1157,7 @@
  *	Misc support functions
  */
 
-struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
+static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
 {
 	struct rt6_info *rt;
 
@@ -1076,7 +1206,7 @@
 
 	for (rt = fn->leaf; rt; rt=rt->u.next) {
 		if (dev == rt->rt6i_dev &&
-		    ipv6_addr_cmp(&rt->rt6i_dst.addr, addr) == 0)
+		    ipv6_addr_cmp(&rt->rt6i_gateway, addr) == 0)
 			break;
 	}
 
@@ -1117,6 +1247,10 @@
 
 	rt = ip6_route_add(&rtmsg, &err);
 
+	/* BUGGGGGGGGGGGGGGGGGGGG!
+	   rt can be not NULL, but point to heavens.
+	 */
+
 	if (err) {
 		printk(KERN_DEBUG "rt6_add_dflt: ip6_route_add error %d\n",
 		       err);
@@ -1172,6 +1306,7 @@
 		if (err)
 			return -EFAULT;
 			
+		rtnl_lock();
 		switch (cmd) {
 		case SIOCADDRT:
 			ip6_route_add(&rtmsg, &err);
@@ -1182,9 +1317,12 @@
 		default:
 			err = -EINVAL;
 		};
+		rtnl_unlock();
 
+#ifdef CONFIG_IPV6_NETLINK
 		if (err == 0)
 				rt6_sndrtmsg(&rtmsg);
+#endif
 		return err;
 	};
 
@@ -1229,15 +1367,19 @@
 	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->u.dst.output = ip6_output;
 	rt->rt6i_dev = dev_get("lo");
 	rt->u.dst.pmtu = rt->rt6i_dev->mtu;
+	rt->u.dst.obsolete = -1;
 
 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
-	
+	rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+	if (rt->rt6i_nexthop == NULL) {
+		dst_free((struct dst_entry *) rt);
+		return -ENOMEM;
+	}
+
 	ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
 	rt->rt6i_dst.plen = 128;
 
@@ -1248,6 +1390,21 @@
 	return 0;
 }
 
+/* Delete address. Warning: you should check that this address
+   disappeared before calling this function.
+ */
+
+int ip6_rt_addr_del(struct in6_addr *addr, struct device *dev)
+{
+	struct rt6_info *rt;
+
+	rt = rt6_lookup(addr, NULL, dev_get("lo"), RTF_LINKRT);
+	if (rt && rt->rt6i_dst.plen == 128)
+		return ip6_del_rt(rt);
+
+	return 0;
+}
+
 #ifdef CONFIG_RT6_POLICY
 
 static int rt6_flow_match_in(struct rt6_info *rt, struct sk_buff *skb)
@@ -1355,18 +1512,260 @@
 		goto error;
 
 	nrt->rt6i_flags |= RTF_CACHE;
+	/* BUGGGG! nrt can point to nowhere! */
 	rt6_ins(nrt);
 
 	return nrt;
 }
 #endif
 
+/* 
+ * Nope, I am not idiot. I see that it is the ugliest of ugly routines.
+ * Anyone is advertised to write better one. --ANK
+ */
+
+struct rt6_ifdown_arg {
+	struct device *dev;
+	struct rt6_info *rt;
+};
+
+
+static void rt6_ifdown_node(struct fib6_node *fn, void *p_arg)
+{
+	struct rt6_info *rt;
+	struct rt6_ifdown_arg *arg = (struct rt6_ifdown_arg *) p_arg;
+
+	if (arg->rt != NULL)
+		return;
+
+	for (rt = fn->leaf; rt; rt = rt->u.next) {
+		if (rt->rt6i_dev == arg->dev || arg->dev == NULL) {
+			arg->rt = rt;
+			return;
+		}
+	}
+}
+
+void rt6_ifdown(struct device *dev)
+{
+	int count = 0;
+	struct rt6_ifdown_arg arg;
+	struct rt6_info *rt;
+
+	do {
+		arg.dev = dev;
+		arg.rt = NULL;
+		fib6_walk_tree(&ip6_routing_table, rt6_ifdown_node, &arg,
+			       RT6_FILTER_RTNODES);
+		if (arg.rt != NULL)
+			ip6_del_rt(arg.rt);
+		count++;
+	} while (arg.rt != NULL);
+
+	/* And default routes ... */
+
+	for (rt = ip6_routing_table.leaf; rt; ) {
+		if (rt != &ip6_null_entry && (rt->rt6i_dev == dev || dev == NULL)) {
+			struct rt6_info *deleting = rt;
+			rt = rt->u.next;
+			ip6_del_rt(deleting);
+			continue;
+		}
+		rt = rt->u.next;
+	}
+}
+
+#ifdef CONFIG_RTNETLINK
+
+static void inet6_rtm_to_rtmsg(struct rtmsg *r, struct kern_rta *rta,
+			       struct in6_rtmsg *rtmsg)
+{
+	memset(rtmsg, 0, sizeof(*rtmsg));
+	rtmsg->rtmsg_dst_len = r->rtm_dst_len;
+	rtmsg->rtmsg_src_len = r->rtm_src_len;
+	rtmsg->rtmsg_flags = RTF_UP;
+	rtmsg->rtmsg_metric = IP6_RT_PRIO_USER;
+	if (rta->rta_gw) {
+		memcpy(&rtmsg->rtmsg_gateway, rta->rta_gw, 16);
+		rtmsg->rtmsg_flags |= RTF_GATEWAY;
+	}
+	if (rta->rta_dst)
+		memcpy(&rtmsg->rtmsg_dst, rta->rta_dst, 16);
+	if (rta->rta_src)
+		memcpy(&rtmsg->rtmsg_src, rta->rta_src, 16);
+	if (rta->rta_oif)
+		memcpy(&rtmsg->rtmsg_ifindex, rta->rta_oif, 4);
+	if (rta->rta_priority)
+		memcpy(&rtmsg->rtmsg_metric, rta->rta_priority, 4);
+}
+
+int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+{
+	struct kern_rta *rta = arg;
+	struct rtmsg *r = NLMSG_DATA(nlh);
+	struct in6_rtmsg rtmsg;
+
+	inet6_rtm_to_rtmsg(r, rta, &rtmsg);
+	return ip6_route_del(&rtmsg);
+}
+
+int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+{
+	struct kern_rta *rta = arg;
+	struct rtmsg *r = NLMSG_DATA(nlh);
+	struct in6_rtmsg rtmsg;
+	int err = 0;
+
+	inet6_rtm_to_rtmsg(r, rta, &rtmsg);
+	ip6_route_add(&rtmsg, &err);
+	return err;
+}
+
+
+struct rt6_rtnl_dump_arg
+{
+	struct sk_buff *skb;
+	struct netlink_callback *cb;
+	int skip;
+	int count;
+	int stop;
+};
+
+static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
+			 int type, pid_t pid, u32 seq)
+{
+	struct rtmsg *rtm;
+	struct nlmsghdr  *nlh;
+	unsigned char	 *b = skb->tail;
+	unsigned char 	 *o;
+	struct rta_cacheinfo ci;
+
+	nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*rtm));
+	rtm = NLMSG_DATA(nlh);
+	rtm->rtm_family = AF_INET6;
+	rtm->rtm_dst_len = rt->rt6i_dst.plen;
+	rtm->rtm_src_len = rt->rt6i_src.plen;
+	rtm->rtm_tos = 0;
+	rtm->rtm_table = RT_TABLE_MAIN;
+	rtm->rtm_type = RTN_UNICAST;
+	rtm->rtm_flags = 0;
+	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
+	rtm->rtm_nhs = 0;
+	rtm->rtm_protocol = RTPROT_BOOT;
+	if (rt->rt6i_flags&RTF_DYNAMIC)
+		rtm->rtm_protocol = RTPROT_REDIRECT;
+	else if (rt->rt6i_flags&(RTF_ADDRCONF|RTF_ALLONLINK))
+		rtm->rtm_protocol = RTPROT_KERNEL;
+	else if (rt->rt6i_flags&RTF_DEFAULT)
+		rtm->rtm_protocol = RTPROT_RA;
+
+	if (rt->rt6i_flags&RTF_CACHE)
+		rtm->rtm_flags |= RTM_F_CLONED;
+
+	o = skb->tail;
+	if (rtm->rtm_dst_len)
+		RTA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
+	if (rtm->rtm_src_len)
+		RTA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
+	if (rt->u.dst.pmtu)
+		RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
+	if (rt->u.dst.window)
+		RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &rt->u.dst.window);
+	if (rt->u.dst.rtt)
+		RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &rt->u.dst.rtt);
+	if (rt->u.dst.neighbour)
+		RTA_PUT(skb, RTA_GATEWAY, 16, &rt->u.dst.neighbour->primary_key);
+	if (rt->u.dst.dev)
+		RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->rt6i_dev->ifindex);
+	RTA_PUT(skb, RTA_PRIORITY, 4, &rt->rt6i_metric);
+	ci.rta_lastuse = jiffies - rt->u.dst.lastuse;
+	if (rt->rt6i_expires)
+		ci.rta_expires = rt->rt6i_expires - jiffies;
+	else
+		ci.rta_expires = 0;
+	ci.rta_used = 0;
+	ci.rta_clntref = atomic_read(&rt->u.dst.use);
+	ci.rta_error = rt->u.dst.error;
+	RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci);
+	rtm->rtm_optlen = skb->tail - o;
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+	skb_put(skb, b - skb->tail);
+	return -1;
+}
+
+static void rt6_dump_node(struct fib6_node *fn, void *p_arg)
+{
+	struct rt6_info *rt;
+	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
+
+	if (arg->stop)
+		return;
+
+	for (rt = fn->leaf; rt; rt = rt->u.next) {
+		if (arg->count < arg->skip) {
+			arg->count++;
+			continue;
+		}
+		if (rt6_fill_node(arg->skb, rt, RTM_NEWROUTE,
+				  NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq) <= 0) {
+			arg->stop = 1;
+			break;
+		}
+		arg->count++;
+	}
+}
+
+
+int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct rt6_rtnl_dump_arg arg;
+
+	arg.skb = skb;
+	arg.cb = cb;
+	arg.skip = cb->args[0];
+	arg.count = 0;
+	arg.stop = 0;
+	start_bh_atomic();
+	fib6_walk_tree(&ip6_routing_table, rt6_dump_node, &arg, RT6_FILTER_RTNODES);
+	if (arg.stop == 0)
+		rt6_dump_node(&ip6_routing_table, &arg);
+	end_bh_atomic();
+	cb->args[0] = arg.count;
+	return skb->len;
+}
+
+void inet6_rt_notify(int event, struct rt6_info *rt)
+{
+	struct sk_buff *skb;
+	int size = NLMSG_SPACE(sizeof(struct rtmsg)+256);
+
+	skb = alloc_skb(size, GFP_KERNEL);
+	if (!skb) {
+		netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, ENOBUFS);
+		return;
+	}
+	if (rt6_fill_node(skb, rt, event, 0, 0) < 0) {
+		kfree_skb(skb, 0);
+		netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, EINVAL);
+		return;
+	}
+	NETLINK_CB(skb).dst_groups = RTMGRP_IPV6_ROUTE;
+	netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_ROUTE, GFP_ATOMIC);
+}
+
+#endif
+
 /*
  *	/proc
  */
 
 #ifdef CONFIG_PROC_FS
 
+
 #define RT6_INFO_LEN (32 + 4 + 32 + 4 + 32 + 40 + 5 + 1)
 
 struct rt6_proc_arg {
@@ -1411,11 +1810,8 @@
 		
 		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]);
+					rt->rt6i_nexthop->primary_key[i]);
 				arg->len += 2;
 			}
 		} else {
@@ -1424,7 +1820,7 @@
 			arg->len += 32;
 		}
 		arg->len += sprintf(arg->buffer + arg->len,
-				    " %08lx %08x %08x %08lx %8s\n",
+				    " %08x %08x %08x %08x %8s\n",
 				    rt->rt6i_metric, atomic_read(&rt->rt6i_use),
 				    atomic_read(&rt->rt6i_ref), rt->rt6i_flags, 
 				    rt->rt6i_dev ? rt->rt6i_dev->name : "");
@@ -1528,6 +1924,7 @@
 	return arg.len;
 }
 
+
 extern struct rt6_statistics rt6_stats;
 
 static int rt6_proc_stats(char *buffer, char **start, off_t offset, int length,
@@ -1558,28 +1955,28 @@
 	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
 };
+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
+};
 #endif	/* CONFIG_PROC_FS */
 
 __initfunc(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);
+	proc_net_register(&proc_rt6_stats);
 #endif
-#ifdef CONFIG_NETLINK
+#ifdef CONFIG_IPV6_NETLINK
 	netlink_attach(NETLINK_ROUTE6, rt6_msgrcv);
 #endif
 }
@@ -1592,11 +1989,9 @@
 	proc_net_unregister(PROC_NET_RT6_TREE);
 	proc_net_unregister(PROC_NET_RT6_STATS);
 #endif
-#ifdef CONFIG_NETLINK
+#ifdef CONFIG_IPV6_NETLINK
 	netlink_detach(NETLINK_ROUTE6);
 #endif
-#if 0
-	fib6_flush();
-#endif
+	rt6_ifdown(NULL);
 }
 #endif	/* MODULE */
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/sit.c linux/net/ipv6/sit.c
--- v2.1.78/linux/net/ipv6/sit.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/sit.c	Mon Jan 12 15:28:28 1998
@@ -6,7 +6,7 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
  *
- *	$Id: sit.c,v 1.23 1997/11/08 18:15:49 kuznet Exp $
+ *	$Id: sit.c,v 1.24 1997/12/13 21:53:17 kuznet Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -393,18 +393,18 @@
 		goto tx_error;
 
 	if (!dst) {
-		struct nd_neigh *neigh = NULL;
+		struct neighbour *neigh = NULL;
 
 		if (skb->dst)
-			neigh = (struct nd_neigh *) skb->dst->neighbour;
+			neigh = skb->dst->neighbour;
 
 		if (neigh == NULL) {
 			printk(KERN_DEBUG "sit: nexthop == NULL\n");
 			goto tx_error;
 		}
 
-		addr6 = &neigh->ndn_addr;
-		addr_type = ipv6_addr_type(addr6);
+		addr6 = (struct in6_addr*)&neigh->primary_key;
+		addr_type = neigh->type;
 
 		if (addr_type == IPV6_ADDR_ANY) {
 			addr6 = &skb->nh.ipv6h->daddr;
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/sysctl_net_ipv6.c linux/net/ipv6/sysctl_net_ipv6.c
--- v2.1.78/linux/net/ipv6/sysctl_net_ipv6.c	Mon Apr 14 16:28:28 1997
+++ linux/net/ipv6/sysctl_net_ipv6.c	Mon Jan 12 15:28:28 1998
@@ -18,12 +18,6 @@
 	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	*/
@@ -72,26 +66,6 @@
 
 	{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",
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.1.78/linux/net/ipv6/tcp_ipv6.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/tcp_ipv6.c	Mon Jan 12 15:28:28 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.43 1997/10/30 23:52:34 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.44 1997/12/13 21:53:18 kuznet Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -577,9 +577,10 @@
 	if (type == ICMPV6_PKT_TOOBIG && sk->state != TCP_LISTEN) {
 		/* icmp should have updated the destination cache entry */
 
-		dst_check(&np->dst, np->dst_cookie);
+		if (sk->dst_cache)
+			dst_check(&sk->dst_cache, np->dst_cookie);
 
-		if (np->dst == NULL) {
+		if (sk->dst_cache == NULL) {
 			struct flowi fl;
 			struct dst_entry *dst;
 			
@@ -595,10 +596,10 @@
 			ip6_dst_store(sk, dst);
 		}
 
-		if (np->dst->error)
-			sk->err_soft = np->dst->error;
+		if (sk->dst_cache->error)
+			sk->err_soft = sk->dst_cache->error;
 		else
-			sk->mtu = np->dst->pmtu;
+			sk->mtu = sk->dst_cache->pmtu;
 
 		if (sk->sock_readers) { /* remove later */
 			printk(KERN_DEBUG "tcp_v6_err: pmtu disc: socket locked.\n");
@@ -1062,8 +1063,8 @@
 
 	buff->csum = csum_partial((char *)t1, sizeof(*t1), 0);
 
-	fl.nl_u.ip6_u.daddr = &skb->nh.ipv6h->daddr;
-	fl.nl_u.ip6_u.saddr = &skb->nh.ipv6h->saddr;
+	fl.nl_u.ip6_u.daddr = &skb->nh.ipv6h->saddr;
+	fl.nl_u.ip6_u.saddr = &skb->nh.ipv6h->daddr;
 
 	t1->check = csum_ipv6_magic(fl.nl_u.ip6_u.saddr,
 				    fl.nl_u.ip6_u.daddr, 
@@ -1072,8 +1073,8 @@
 
 	fl.proto = IPPROTO_TCP;
 	fl.dev = skb->dev;
-	fl.uli_u.ports.dport = th->dest;
-	fl.uli_u.ports.sport = th->source;
+	fl.uli_u.ports.dport = t1->dest;
+	fl.uli_u.ports.sport = t1->source;
 
 	ip6_xmit(NULL, buff, &fl, NULL);
 	tcp_statistics.TcpOutSegs++;
@@ -1197,22 +1198,6 @@
 		return(0);
 	}
 
-	/*
-	 *	Signal NDISC that the connection is making
-	 *	"forward progress"
-	 *	This is in the fast path and should be _really_ speed up! -Ak
-	 */
-	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->dst)
-				ndisc_validate(np->dst->neighbour);
-		}
-	}
-
 	skb_set_owner_r(skb, sk);
 
 	if (sk->state == TCP_ESTABLISHED) {
@@ -1270,10 +1255,10 @@
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 
-	if (np->dst)
-		dst_check(&np->dst, np->dst_cookie);
+	if (sk->dst_cache)
+		dst_check(&sk->dst_cache, np->dst_cookie);
 
-	if (np->dst == NULL) {
+	if (sk->dst_cache == NULL) {
 		struct flowi fl;
 		struct dst_entry *dst;
 
@@ -1288,7 +1273,7 @@
 		ip6_dst_store(sk, dst);
 	}
 
-	if (np->dst->error) {
+	if (sk->dst_cache->error) {
 		/*
 		 *	lost route to destination
 		 */
@@ -1457,7 +1442,6 @@
 
 static int tcp_v6_destroy_sock(struct sock *sk)
 {
-	struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6;
 	struct sk_buff *skb;
 
 	tcp_clear_xmit_timers(sk);
@@ -1483,8 +1467,8 @@
 	 *	Release destination entry
 	 */
 
-	if (np->dst)
-		dst_release(np->dst);
+	dst_release(sk->dst_cache);
+	sk->dst_cache = NULL;
 
 	return 0;
 }
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.1.78/linux/net/ipv6/udp.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipv6/udp.c	Mon Jan 12 15:28:28 1998
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.18 1997/09/14 08:32:24 davem Exp $
+ *	$Id: udp.c,v 1.21 1997/12/29 19:52:52 kuznet Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -27,6 +27,7 @@
 #include <linux/ipv6.h>
 #include <linux/icmpv6.h>
 #include <linux/init.h>
+#include <asm/uaccess.h>
 
 #include <net/sock.h>
 #include <net/snmp.h>
@@ -282,16 +283,11 @@
 
 static void udpv6_close(struct sock *sk, unsigned long timeout)
 {
-	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-
 	lock_sock(sk);
 	sk->state = TCP_CLOSE;
-
-	if (np->dst)
-		dst_release(np->dst);
-
 	ipv6_sock_mc_close(sk);
 	udp_v6_unhash(sk);
+	sk->dead = 1;
 	release_sock(sk);
 	destroy_sock(sk);
 }
@@ -649,6 +645,16 @@
 
 	int err;
 	
+	/* Rough check on arithmetic overflow,
+	   better check is made in ip6_build_xmit
+
+	   When jumbo header will be implemeted we will change it
+	   to something sort of (len will be size_t)
+	   ulen > SIZE_T_MAX - sizeof(struct udphdr)
+	 */
+	if (ulen < 0 || ulen > 0xFFFF - sizeof(struct udphdr))
+		return -EMSGSIZE;
+
 	if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
 		return(-EINVAL);
 
@@ -665,9 +671,12 @@
 		udh.uh.dest = sin6->sin6_port;
 		daddr = &sin6->sin6_addr;
 
-		if (np->dst && ipv6_addr_cmp(daddr, &np->daddr)) {
-			dst_release(np->dst);
-			np->dst = NULL;
+		/* BUGGGG! If route is not cloned, this check always
+		   fails, hence dst_cache only slows down transmission --ANK
+		 */
+		if (sk->dst_cache && ipv6_addr_cmp(daddr, &np->daddr)) {
+			dst_release(sk->dst_cache);
+			sk->dst_cache = NULL;
 		}
 	} else {
 		if (sk->state != TCP_ESTABLISHED)
diff -u --recursive --new-file v2.1.78/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.1.78/linux/net/ipx/af_ipx.c	Mon Dec  1 12:04:17 1997
+++ linux/net/ipx/af_ipx.c	Mon Jan 12 15:28:28 1998
@@ -415,14 +415,8 @@
 			if (copy != 0)
 			{
 				skb1 = skb_clone(skb, GFP_ATOMIC);
-				if (skb1 != NULL)
-				{
-					skb1->arp = 1;
-				}
-				else
-				{
+				if (skb1 == NULL)
 					return -ENOMEM;
-				}
 			}
 			else
 			{
@@ -515,8 +509,6 @@
 	if (copy)
 	{
 		skb1 = skb_clone(skb, GFP_ATOMIC);
-		if (skb1)
-			skb1->arp=1;
 	}
 	else
 	{
@@ -533,8 +525,6 @@
 	if (sock1 && sock2)
 	{
 		skb2 = skb_clone(skb1, GFP_ATOMIC);
-		if (skb2 != NULL)
-			skb2->arp = 1;
 	}
 	else
 		skb2 = skb1;
@@ -561,7 +551,6 @@
 
 	/* Hopefully, most cases */
 	if (in_offset >= out_offset) {
-		skb->arp = 1;
 		return skb;
 	}
 
@@ -572,11 +561,10 @@
 		skb_reserve(skb2,out_offset);
 		skb2->nh.raw=
 		skb2->h.raw=skb_put(skb2,skb->len);
-		skb2->arp=1;
 		memcpy(skb2->h.raw, skb->h.raw, skb->len);
 	}
 	kfree_skb(skb, FREE_WRITE);
-	return skb2;
+	return NULL;
 }
 
 static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
@@ -1360,7 +1348,6 @@
 		return err;
 
 	skb_reserve(skb,ipx_offset);
-	skb->arp=1;
 	skb->sk=sk;
 
 	/* Fill in IPX header */
diff -u --recursive --new-file v2.1.78/linux/net/netbeui/netbeui_name.c linux/net/netbeui/netbeui_name.c
--- v2.1.78/linux/net/netbeui/netbeui_name.c	Thu Dec 12 06:54:26 1996
+++ linux/net/netbeui/netbeui_name.c	Mon Jan 12 15:28:28 1998
@@ -58,7 +58,9 @@
 	if(nskb==NULL)
 		return;
 	/* Build a name defence packet */
-	dev_queue_xmit(nskb,dev,SOPRI_INTERACTIVE);
+	nskb->dev = dev;
+	nskb->priority = TC_PRIO_CONTROL;
+	dev_queue_xmit(nskb);
 }
 
 void netbeui_heard_name(struct device *dev, struct sk_buff *skb)
@@ -151,7 +153,9 @@
 		if(nskb!=NULL)
 		{
 			/* Build a name reply packet */
-			dev_queue_xmit(nskb,dev,SOPRI_INTERACTIVE);
+			nskb->dev = dev;
+			nskb->priority = TC_PRIO_CONTROL;
+			dev_queue_xmit(nskb);
 		}
 	}
 	kfree_skb(skb, FREE_READ);
diff -u --recursive --new-file v2.1.78/linux/net/netlink/af_netlink.c linux/net/netlink/af_netlink.c
--- v2.1.78/linux/net/netlink/af_netlink.c	Sun Dec 21 22:36:17 1997
+++ linux/net/netlink/af_netlink.c	Mon Jan 12 15:28:28 1998
@@ -315,7 +315,7 @@
 		return -EINVAL;
 
 	/* Only superuser is allowed to send multicasts */
-	if (nladdr->nl_groups && !suser())
+	if (!suser() && nladdr->nl_groups)
 		return -EPERM;
 
 	sk->protinfo.af_netlink.dst_pid = nladdr->nl_pid;
diff -u --recursive --new-file v2.1.78/linux/net/netrom/nr_dev.c linux/net/netrom/nr_dev.c
--- v2.1.78/linux/net/netrom/nr_dev.c	Mon Dec  1 12:04:17 1997
+++ linux/net/netrom/nr_dev.c	Mon Jan 12 15:28:28 1998
@@ -121,7 +121,10 @@
 	unsigned char *bp = skb->data;
 
 	if (arp_find(bp + 7, skb)) {
+#if 0
+		/* BUGGGG! If arp_find returned 1, skb does not exist. --ANK*/
 		kfree_skb(skb, FREE_WRITE);
+#endif
 		return 1;
 	}
 
diff -u --recursive --new-file v2.1.78/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.78/linux/net/netsyms.c	Tue Jan  6 09:37:38 1998
+++ linux/net/netsyms.c	Mon Jan 12 19:31:26 1998
@@ -32,6 +32,7 @@
 #include <net/pkt_sched.h>
 #include <linux/inet.h>
 #include <linux/mroute.h>
+#include <linux/igmp.h>
 
 extern struct net_proto_family inet_family_ops;
 
@@ -44,8 +45,6 @@
 
 #endif
 
-#include <linux/filter.h>
-
 #include <linux/rtnetlink.h>
 
 #include <net/scm.h>
@@ -80,13 +79,6 @@
 EXPORT_SYMBOL(sock_unregister);
 
 /* Socket layer support routines */
-EXPORT_SYMBOL(sk_alloc);
-EXPORT_SYMBOL(sk_free);
-
-#ifdef CONFIG_FILTER
-EXPORT_SYMBOL(sk_run_filter);
-#endif
-
 EXPORT_SYMBOL(memcpy_fromiovec);
 EXPORT_SYMBOL(sock_create);
 EXPORT_SYMBOL(sock_alloc);
@@ -95,6 +87,8 @@
 EXPORT_SYMBOL(sock_getsockopt);
 EXPORT_SYMBOL(sock_sendmsg);
 EXPORT_SYMBOL(sock_recvmsg);
+EXPORT_SYMBOL(sk_alloc);
+EXPORT_SYMBOL(sk_free);
 EXPORT_SYMBOL(sock_wake_async);
 EXPORT_SYMBOL(sock_alloc_send_skb);
 EXPORT_SYMBOL(sock_init_data);
@@ -124,24 +118,41 @@
 EXPORT_SYMBOL(datagram_poll);
 EXPORT_SYMBOL(put_cmsg);
 EXPORT_SYMBOL(net_families);
+EXPORT_SYMBOL(sock_kmalloc);
+EXPORT_SYMBOL(sock_kfree_s);
 
 EXPORT_SYMBOL(neigh_table_init);
-/* Declared in <net/neighbour.h> but not defined?
-   EXPORT_SYMBOL(neigh_table_destroy);
-   EXPORT_SYMBOL(neigh_table_run_bh);
-*/
-EXPORT_SYMBOL(neigh_alloc);
-EXPORT_SYMBOL(neigh_table_ins);
-EXPORT_SYMBOL(neigh_queue_ins);
-EXPORT_SYMBOL(neigh_unlink);
-EXPORT_SYMBOL(neigh_lookup);
-EXPORT_SYMBOL(ntbl_walk_table);
-EXPORT_SYMBOL(neigh_tbl_run_bh);
+EXPORT_SYMBOL(neigh_table_clear);
+EXPORT_SYMBOL(__neigh_lookup);
+EXPORT_SYMBOL(neigh_resolve_output);
+EXPORT_SYMBOL(neigh_connected_output);
+EXPORT_SYMBOL(neigh_update);
+EXPORT_SYMBOL(__neigh_event_send);
+EXPORT_SYMBOL(neigh_event_ns);
+EXPORT_SYMBOL(neigh_ifdown);
+#ifdef CONFIG_ARPD
+EXPORT_SYMBOL(neigh_app_ns);
+#endif
+#ifdef CONFIG_SYSCTL
+EXPORT_SYMBOL(neigh_sysctl_register);
+#endif
+EXPORT_SYMBOL(pneigh_lookup);
+EXPORT_SYMBOL(pneigh_enqueue);
+EXPORT_SYMBOL(neigh_destroy);
+EXPORT_SYMBOL(neigh_parms_alloc);
+EXPORT_SYMBOL(neigh_parms_release);
+EXPORT_SYMBOL(neigh_rand_reach_time);
 
 /*	dst_entry	*/
 EXPORT_SYMBOL(dst_alloc);
 EXPORT_SYMBOL(__dst_free);
 EXPORT_SYMBOL(dst_total);
+EXPORT_SYMBOL(dst_destroy);
+
+/*	misc. support routines */
+EXPORT_SYMBOL(net_ratelimit);
+EXPORT_SYMBOL(net_random);
+EXPORT_SYMBOL(net_srandom);
 
 /* Needed by smbfs.o */
 EXPORT_SYMBOL(__scm_destroy);
@@ -188,8 +199,9 @@
 EXPORT_SYMBOL(ip_fragment);
 EXPORT_SYMBOL(inet_family_ops);
 EXPORT_SYMBOL(in_aton);
-EXPORT_SYMBOL(in_ntoa);
-EXPORT_SYMBOL(net_ratelimit);
+EXPORT_SYMBOL(ip_mc_inc_group);
+EXPORT_SYMBOL(ip_mc_dec_group);
+EXPORT_SYMBOL(__ip_finish_output);
 
 /* needed for ip_gre -cw */
 EXPORT_SYMBOL(ip_statistics);
@@ -271,6 +283,7 @@
 EXPORT_SYMBOL(tcp_simple_retransmit);
 
 EXPORT_SYMBOL(xrlim_allow);
+EXPORT_SYMBOL(dev_mc_delete);
 #endif
 
 #ifdef CONFIG_PACKET_MODULE
@@ -343,7 +356,6 @@
 EXPORT_SYMBOL(__kfree_skb);
 EXPORT_SYMBOL(skb_clone);
 EXPORT_SYMBOL(skb_copy);
-EXPORT_SYMBOL(dev_alloc_skb);
 EXPORT_SYMBOL(netif_rx);
 EXPORT_SYMBOL(dev_add_pack);
 EXPORT_SYMBOL(dev_remove_pack);
@@ -352,6 +364,9 @@
 EXPORT_SYMBOL(dev_alloc_name);
 EXPORT_SYMBOL(dev_ioctl);
 EXPORT_SYMBOL(dev_queue_xmit);
+#ifdef CONFIG_NET_FASTROUTE
+EXPORT_SYMBOL(dev_fastroute_stat);
+#endif
 #ifdef CONFIG_IP_ACCT
 EXPORT_SYMBOL(ip_acct_output);
 #endif
@@ -359,7 +374,6 @@
 EXPORT_SYMBOL(dev_close);
 EXPORT_SYMBOL(dev_mc_add);
 EXPORT_SYMBOL(arp_find);
-EXPORT_SYMBOL(arp_find_1);
 EXPORT_SYMBOL(n_tty_ioctl);
 EXPORT_SYMBOL(tty_register_ldisc);
 EXPORT_SYMBOL(kill_fasync);
diff -u --recursive --new-file v2.1.78/linux/net/packet/af_packet.c linux/net/packet/af_packet.c
--- v2.1.78/linux/net/packet/af_packet.c	Mon Dec  1 12:04:17 1997
+++ linux/net/packet/af_packet.c	Mon Jan 12 15:28:28 1998
@@ -211,6 +211,11 @@
 	 *	so that this procedure is noop.
 	 */
 
+	if (skb->pkt_type == PACKET_LOOPBACK) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
 	skb_push(skb, skb->data-skb->mac.raw);
 
 	/*
@@ -318,16 +323,14 @@
 	 * notable one here. This should really be fixed at the driver level.
 	 */
 	skb_reserve(skb,(dev->hard_header_len+15)&~15);
-	skb->mac.raw = skb->nh.raw = skb->data;
+	skb->nh.raw = skb->data;
 
 	/* Try to align data part correctly */
 	if (dev->hard_header) {
 		skb->data -= dev->hard_header_len;
 		skb->tail -= dev->hard_header_len;
-		skb->mac.raw = skb->data;
 	}
 	err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
-	skb->arp = 1;	/* No ARP needs doing on this (complete) frame */
 	skb->protocol = proto;
 	skb->dev = dev;
 	skb->priority = sk->priority;
@@ -372,9 +375,10 @@
 
 	sk = (struct sock *) pt->data;
 
-	/*
-	 *	The SOCK_PACKET socket receives _all_ frames.
-	 */
+	if (skb->pkt_type == PACKET_LOOPBACK) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
 
 	skb->dev = dev;
 
@@ -469,7 +473,7 @@
 	}
 
 	skb_reserve(skb, (dev->hard_header_len+15)&~15);
-	skb->mac.raw = skb->nh.raw = skb->data;
+	skb->nh.raw = skb->data;
 
 	if (dev->hard_header) {
 		if (dev->hard_header(skb, dev, ntohs(proto),
@@ -480,7 +484,6 @@
 			dev_unlock_list();
 			return -EINVAL;
 		}
-		skb->mac.raw = skb->data;
 		if (sock->type != SOCK_DGRAM) {
 			skb->tail = skb->data;
 			skb->len = 0;
@@ -488,7 +491,6 @@
 	}
 
 	err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
-	skb->arp = 1;	/* No ARP needs doing on this (complete) frame */
 	skb->protocol = proto;
 	skb->dev = dev;
 	skb->priority = sk->priority;
diff -u --recursive --new-file v2.1.78/linux/net/rose/rose_dev.c linux/net/rose/rose_dev.c
--- v2.1.78/linux/net/rose/rose_dev.c	Mon Dec  1 12:04:17 1997
+++ linux/net/rose/rose_dev.c	Mon Jan 12 15:28:28 1998
@@ -102,7 +102,10 @@
 	struct sk_buff *skbn;
 
 	if (arp_find(bp + 7, skb)) {
+#if 0
+		/* BUGGGG! If arp_find returned 1, skb does not exist. --ANK*/
 		kfree_skb(skb, FREE_WRITE);
+#endif
 		return 1;
 	}
 
diff -u --recursive --new-file v2.1.78/linux/net/sched/Config.in linux/net/sched/Config.in
--- v2.1.78/linux/net/sched/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/net/sched/Config.in	Mon Jan 12 15:28:28 1998
@@ -0,0 +1,11 @@
+#
+# Traffic control configuration.
+# 
+tristate 'CBQ packet scheduler' CONFIG_NET_SCH_CBQ
+tristate 'CSZ packet scheduler' CONFIG_NET_SCH_CSZ
+#tristate 'HFQ packet scheduler' CONFIG_NET_SCH_HFQ
+tristate 'RED queueing discipline' CONFIG_NET_SCH_RED
+tristate 'SFQ queueing discipline' CONFIG_NET_SCH_SFQ
+tristate 'auxiliary TBF queue' CONFIG_NET_SCH_TBF
+tristate 'auxiliary FIFO queue' CONFIG_NET_SCH_PFIFO
+tristate 'auxiliary PRIO queue' CONFIG_NET_SCH_PRIO
diff -u --recursive --new-file v2.1.78/linux/net/sched/sch_generic.c linux/net/sched/sch_generic.c
--- v2.1.78/linux/net/sched/sch_generic.c	Mon Dec  1 12:04:17 1997
+++ linux/net/sched/sch_generic.c	Mon Jan 12 15:28:28 1998
@@ -34,6 +34,9 @@
 
 static struct Qdisc_ops *qdisc_base = NULL;
 
+static int default_requeue(struct sk_buff *skb, struct Qdisc* qdisc);
+
+
 /* NOTES.
 
    Every discipline has two major routines: enqueue and dequeue.
@@ -75,6 +78,8 @@
 			break;
 	if (!q)
 		return -ENOENT;
+	if (q->requeue == NULL)
+		q->requeue = default_requeue;
 	*qp = q->next;
 	return 0;
 }
@@ -118,6 +123,7 @@
 };
 
 
+
 /* 3-band FIFO queue: old style, but should be a bit faster (several CPU insns) */
 
 static int
@@ -129,7 +135,7 @@
 	list = ((struct sk_buff_head*)qdisc->data) + prio2band[skb->priority&7];
 
 	if (list->qlen <= skb->dev->tx_queue_len) {
-		skb_queue_tail(list, skb);
+		__skb_queue_tail(list, skb);
 		return 1;
 	}
 	qdisc->dropped++;
@@ -145,13 +151,25 @@
 	struct sk_buff *skb;
 
 	for (prio = 0; prio < 3; prio++, list++) {
-		skb = skb_dequeue(list);
+		skb = __skb_dequeue(list);
 		if (skb)
 			return skb;
 	}
 	return NULL;
 }
 
+static int
+pfifo_fast_requeue(struct sk_buff *skb, struct Qdisc* qdisc)
+{
+	const static u8 prio2band[8] = { 1, 2, 2, 2, 1, 2, 0, 0 };
+	struct sk_buff_head *list;
+
+	list = ((struct sk_buff_head*)qdisc->data) + prio2band[skb->priority&7];
+
+	__skb_queue_head(list, skb);
+	return 1;
+}
+
 static void
 pfifo_fast_reset(struct Qdisc* qdisc)
 {
@@ -185,9 +203,20 @@
 	pfifo_fast_dequeue,
 	pfifo_fast_reset,
 	NULL,
-	pfifo_fast_init
+	pfifo_fast_init,
+	NULL,
+	pfifo_fast_requeue
 };
 
+static int
+default_requeue(struct sk_buff *skb, struct Qdisc* qdisc)
+{
+	if (net_ratelimit())
+		printk(KERN_DEBUG "%s deferred output. It is buggy.\n", skb->dev->name);
+	kfree_skb(skb, FREE_WRITE);
+	return 0;
+}
+
 static struct Qdisc *
 qdisc_alloc(struct device *dev, struct Qdisc_ops *ops, void *arg)
 {
@@ -200,7 +229,6 @@
 	memset(sch, 0, size);
 
 	skb_queue_head_init(&sch->q);
-	skb_queue_head_init(&sch->failure_q);
 	sch->ops = ops;
 	sch->enqueue = ops->enqueue;
 	sch->dequeue = ops->dequeue;
@@ -218,7 +246,6 @@
 		start_bh_atomic();
 		if (ops->reset)
 			ops->reset(qdisc);
-		skb_queue_purge(&qdisc->failure_q);
 		end_bh_atomic();
 	}
 }
@@ -232,7 +259,6 @@
 			ops->reset(qdisc);
 		if (ops->destroy)
 			ops->destroy(qdisc);
-		skb_queue_purge(&qdisc->failure_q);
 		ops->refcnt--;
 		end_bh_atomic();
 		kfree(qdisc);
@@ -373,23 +399,22 @@
 	struct Qdisc *q = dev->qdisc;
 	struct sk_buff *skb;
 
-	skb = skb_dequeue(&q->failure_q);
-	if (!skb) {
-		skb = q->dequeue(q);
-		if (netdev_nit && skb)
-			dev_queue_xmit_nit(skb,dev);
-	}
-	if (skb) {
+	if ((skb = q->dequeue(q)) != NULL) {
+		if (netdev_nit)
+			dev_queue_xmit_nit(skb, dev);
+
 		if (dev->hard_start_xmit(skb, dev) == 0) {
 			q->tx_last = jiffies;
 			return -1;
 		}
-#if 0
-		if (net_ratelimit())
-			printk(KERN_DEBUG "netdevice %s defers output.\n", dev->name);
-#endif
-		skb_queue_head(&q->failure_q, skb);
-		return -1;
+
+		if (q->ops) {
+			q->ops->requeue(skb, q);
+			return -1;
+		}
+
+		printk(KERN_DEBUG "%s: it is impossible!!!\n", dev->name);
+		kfree_skb(skb, FREE_WRITE);
 	}
 	return q->q.qlen;
 }
@@ -510,9 +535,6 @@
           extern struct Qdisc_ops name##_ops; \
           register_qdisc(&##name##_ops); \
 	}
-
-	skb_queue_head_init(&noop_qdisc.failure_q);
-	skb_queue_head_init(&noqueue_qdisc.failure_q);
 
 	register_qdisc(&pfifo_fast_ops);
 #ifdef CONFIG_NET_SCH_CBQ
diff -u --recursive --new-file v2.1.78/linux/net/socket.c linux/net/socket.c
--- v2.1.78/linux/net/socket.c	Mon Dec  1 12:04:18 1997
+++ linux/net/socket.c	Mon Jan 12 15:28:28 1998
@@ -1147,23 +1147,27 @@
 		goto out;
 	/* This will also move the address data into kernel space */
 	err = verify_iovec(&msg_sys, iov, address, VERIFY_READ);
-	if (err < 0)
+	if (err < 0) 
 		goto out;
+	
 	total_len=err;
 
+	sock = sockfd_lookup(fd, &err);
+	if (!sock) 
+		goto out; 
+
 	if (msg_sys.msg_controllen) 
 	{
-		/* XXX We just limit the buffer and assume that the 
-		 * skbuff accounting stops it from going too far.
-		 * I hope this is correct.
- 		 */
-		if (msg_sys.msg_controllen > 256) {
-			err = -EINVAL;
-			goto failed2;
-		}
 		if (msg_sys.msg_controllen > sizeof(ctl))
 		{
-			ctl_buf = kmalloc(msg_sys.msg_controllen, GFP_KERNEL);
+			/* Suggested by the Advanced Sockets API for IPv6 draft:
+			 * Limit the msg_controllen size by the SO_SNDBUF size.
+			 */
+			/* Note - when this code becomes multithreaded on
+			 * SMP machines you have a race to fix here.
+			 */
+			ctl_buf = sock_kmalloc(sock->sk, msg_sys.msg_controllen, 
+					       GFP_KERNEL);
 			if (ctl_buf == NULL) 
 			{
 				err = -ENOBUFS;
@@ -1179,21 +1183,17 @@
 	}
 	msg_sys.msg_flags = flags;
 
-	if ((sock = sockfd_lookup(fd,&err))!=NULL)
-	{
-		if (current->files->fd[fd]->f_flags & O_NONBLOCK)
-			msg_sys.msg_flags |= MSG_DONTWAIT;
-		err = sock_sendmsg(sock, &msg_sys, total_len);
-		sockfd_put(sock);
-	}
-
+	if (current->files->fd[fd]->f_flags & O_NONBLOCK)
+		msg_sys.msg_flags |= MSG_DONTWAIT;
+	err = sock_sendmsg(sock, &msg_sys, total_len);
 failed:
-	if (ctl_buf != ctl)
-		kfree_s(ctl_buf, msg_sys.msg_controllen);
+	if (ctl_buf != ctl)    
+		sock_kfree_s(sock->sk, ctl_buf, msg_sys.msg_controllen);
 failed2:
 	if (msg_sys.msg_iov != iov)
 		kfree(msg_sys.msg_iov);
-out:
+	sockfd_put(sock);
+out:       
 	unlock_kernel();
 	return err;
 }
@@ -1295,8 +1295,8 @@
 /* Argument list sizes for sys_socketcall */
 #define AL(x) ((x) * sizeof(unsigned long))
 static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
-								AL(3),AL(3),AL(4),AL(4),AL(4),AL(6),
-								AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)};
+				AL(3),AL(3),AL(4),AL(4),AL(4),AL(6),
+				AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)};
 #undef AL
 
 /*
@@ -1451,7 +1451,14 @@
 	 */
 	 
 	sk_init();
-	
+
+#ifdef SLAB_SKB
+	/*
+	 *	Initialize skbuff SLAB cache 
+	 */
+	skb_init();
+#endif
+
 
 	/*
 	 *	Wan router layer. 
diff -u --recursive --new-file v2.1.78/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c
--- v2.1.78/linux/net/sunrpc/clnt.c	Sun Dec 21 16:17:45 1997
+++ linux/net/sunrpc/clnt.c	Mon Jan 12 14:39:07 1998
@@ -22,7 +22,6 @@
  */
 
 #include <linux/config.h>
-#include <linux/module.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -72,20 +71,19 @@
 		  struct rpc_program *program, u32 vers, int flavor)
 {
 	struct rpc_version	*version;
-	struct rpc_clnt		*clnt;
+	struct rpc_clnt		*clnt = NULL;
 
 	dprintk("RPC: creating %s client for %s (xprt %p)\n",
-			program->name, servname, xprt);
+		program->name, servname, xprt);
 
 	if (!xprt)
-		return NULL;
-	if (vers>= program->nrvers || !(version = program->version[vers]))
-		return NULL;
+		goto out;
+	if (vers >= program->nrvers || !(version = program->version[vers]))
+		goto out;
 
-	if (!(clnt = (struct rpc_clnt *) rpc_allocate(0, sizeof(*clnt)))) {
-		printk("RPC: out of memory in rpc_create_client\n");
-		return NULL;
-	}
+	clnt = (struct rpc_clnt *) rpc_allocate(0, sizeof(*clnt));
+	if (!clnt)
+		goto out_no_clnt;
 	memset(clnt, 0, sizeof(*clnt));
 
 	clnt->cl_xprt     = xprt;
@@ -103,13 +101,20 @@
 	if (!clnt->cl_port)
 		clnt->cl_autobind = 1;
 
-	if (!rpcauth_create(flavor, clnt)) {
-		printk("RPC: Couldn't create auth handle (flavor %d)\n",
-					flavor);
-		rpc_free(clnt);
-		return NULL;
-	}
+	if (!rpcauth_create(flavor, clnt))
+		goto out_no_auth;
+out:
 	return clnt;
+
+out_no_clnt:
+	printk("RPC: out of memory in rpc_create_client\n");
+	goto out;
+out_no_auth:
+	printk("RPC: Couldn't create auth handle (flavor %d)\n",
+		flavor);
+	rpc_free(clnt);
+	clnt = NULL;
+	goto out;
 }
 
 /*
@@ -779,24 +784,3 @@
 	rpc_exit(task, -EIO);
 	return NULL;
 }
-
-#ifdef MODULE
-int
-init_module(void)
-{
-#ifdef RPC_DEBUG
-	rpc_register_sysctl();
-#endif
-	rpc_proc_init();
-	return 0;
-}
-
-void
-cleanup_module(void)
-{
-#ifdef RPC_DEBUG
-	rpc_unregister_sysctl();
-#endif
-	rpc_proc_exit();
-}
-#endif
diff -u --recursive --new-file v2.1.78/linux/net/sunrpc/sched.c linux/net/sunrpc/sched.c
--- v2.1.78/linux/net/sunrpc/sched.c	Tue Dec 23 16:31:00 1997
+++ linux/net/sunrpc/sched.c	Mon Jan 12 14:39:07 1998
@@ -16,6 +16,7 @@
 #include <linux/unistd.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+
 #include <linux/sunrpc/clnt.h>
 
 #ifdef RPC_DEBUG
@@ -906,3 +907,35 @@
 	up(&rpciod_sema);
 	MOD_DEC_USE_COUNT;
 }
+
+#ifdef RPC_DEBUG
+#include <linux/nfs_fs.h>
+void rpc_show_tasks(void)
+{
+	struct rpc_task *t = all_tasks, *next;
+	struct nfs_wreq *wreq;
+
+	if (!t)
+		return;
+	printk("-pid- proc flgs status -client- --rqstp- -timeout "
+		"-rpcwait -action- --exit--\n");
+	for (; t; t = next) {
+		next = t->tk_next_task;
+		printk("%05d %04d %04x %06d %8p %8p %08ld %8p %8p %8p\n",
+			t->tk_pid, t->tk_proc, t->tk_flags, t->tk_status,
+			t->tk_client, t->tk_rqstp, t->tk_timeout,
+			t->tk_rpcwait, t->tk_action, t->tk_exit);
+
+		if (!(t->tk_flags & RPC_TASK_NFSWRITE))
+			continue;
+		/* NFS write requests */
+		wreq = (struct nfs_wreq *) t->tk_calldata;
+		printk("     NFS: flgs=%08x, pid=%d, pg=%p, off=(%d, %d)\n",
+			wreq->wb_flags, wreq->wb_pid, wreq->wb_page,
+			wreq->wb_offset, wreq->wb_bytes);
+		printk("          name=%s/%s\n",
+			wreq->wb_dentry->d_parent->d_name.name,
+			wreq->wb_dentry->d_name.name);
+	}
+}
+#endif
diff -u --recursive --new-file v2.1.78/linux/net/sunrpc/stats.c linux/net/sunrpc/stats.c
--- v2.1.78/linux/net/sunrpc/stats.c	Sun Apr 13 10:18:23 1997
+++ linux/net/sunrpc/stats.c	Mon Jan 12 14:39:07 1998
@@ -12,6 +12,8 @@
  * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
  */
 
+#include <linux/module.h>
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/proc_fs.h>
@@ -20,7 +22,7 @@
 
 #define RPCDBG_FACILITY	RPCDBG_MISC
 
-static struct proc_dir_entry	*proc_net_rpc = 0;
+static struct proc_dir_entry	*proc_net_rpc = NULL;
 
 /*
  * Get RPC client stats
@@ -161,15 +163,61 @@
 rpc_proc_init(void)
 {
 	dprintk("RPC: registering /proc/net/rpc\n");
-	if (!proc_net_rpc)
-		proc_net_rpc = create_proc_entry("net/rpc", S_IFDIR, 0);
+	if (!proc_net_rpc) {
+		struct proc_dir_entry *ent;
+		ent = create_proc_entry("net/rpc", S_IFDIR, 0);
+		if (ent) {
+#ifdef MODULE
+			ent->fill_inode = rpc_modcount;
+#endif
+			proc_net_rpc = ent;
+		}
+	}
 }
 
 void
 rpc_proc_exit(void)
 {
 	dprintk("RPC: unregistering /proc/net/rpc\n");
-	if (proc_net_rpc)
+	if (proc_net_rpc) {
+		proc_net_rpc = NULL;
 		remove_proc_entry("net/rpc", 0);
-	proc_net_rpc = 0;
+	}
+}
+
+#ifdef MODULE
+/*
+ * This is called as the proc_dir_entry fill_inode function
+ * when an inode is going into or out of service (fill == 1
+ * or 0 respectively).
+ *
+ * We use it here to keep the module from being unloaded
+ * while /proc inodes are in use.
+ */
+void rpc_modcount(struct inode *inode, int fill)
+{
+	if (fill)
+		MOD_INC_USE_COUNT;
+	else
+		MOD_DEC_USE_COUNT;
+}
+
+int
+init_module(void)
+{
+#ifdef RPC_DEBUG
+	rpc_register_sysctl();
+#endif
+	rpc_proc_init();
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+#ifdef RPC_DEBUG
+	rpc_unregister_sysctl();
+#endif
+	rpc_proc_exit();
 }
+#endif
diff -u --recursive --new-file v2.1.78/linux/net/sunrpc/sysctl.c linux/net/sunrpc/sysctl.c
--- v2.1.78/linux/net/sunrpc/sysctl.c	Mon Apr  7 11:35:33 1997
+++ linux/net/sunrpc/sysctl.c	Mon Jan 12 19:43:21 1998
@@ -13,15 +13,11 @@
 #include <linux/ctype.h>
 #include <linux/fs.h>
 #include <linux/sysctl.h>
-#if LINUX_VERSION_CODE >= 0x020100
+
 #include <asm/uaccess.h>
-#else
-# include <linux/mm.h>
-# define copy_from_user		memcpy_fromfs
-# define copy_to_user		memcpy_tofs
-# define access_ok		!verify_area
-#endif
 #include <linux/sunrpc/types.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/stats.h>
 
 /*
  * Declare the debug flags here
@@ -39,17 +35,23 @@
 void
 rpc_register_sysctl(void)
 {
-	if (sunrpc_table_header)
-		return;
-	sunrpc_table_header = register_sysctl_table(sunrpc_table, 1);
+	if (!sunrpc_table_header) {
+		sunrpc_table_header = register_sysctl_table(sunrpc_table, 1);
+#ifdef MODULE
+		if (sunrpc_table[0].de)
+			sunrpc_table[0].de->fill_inode = rpc_modcount;
+#endif
+	}
+			
 }
 
 void
 rpc_unregister_sysctl(void)
 {
-	if (!sunrpc_table_header)
-		return;
-	unregister_sysctl_table(sunrpc_table_header);
+	if (sunrpc_table_header) {
+		unregister_sysctl_table(sunrpc_table_header);
+		sunrpc_table_header = NULL;
+	}
 }
 
 int
@@ -93,6 +95,10 @@
 		while (left && isspace(*p))
 			left--, p++;
 		*(unsigned int *) table->data = value;
+		/* Display the RPC tasks on writing to rpc_debug */
+		if (table->ctl_name == CTL_RPCDEBUG) {
+			rpc_show_tasks();
+		}
 	} else {
 		if (!access_ok(VERIFY_WRITE, buffer, left))
 			return -EFAULT;
diff -u --recursive --new-file v2.1.78/linux/net/wanrouter/wanmain.c linux/net/wanrouter/wanmain.c
--- v2.1.78/linux/net/wanrouter/wanmain.c	Thu Jul 17 10:06:10 1997
+++ linux/net/wanrouter/wanmain.c	Mon Jan 12 14:46:27 1998
@@ -18,6 +18,7 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
+* Oct 15, 1997  Farhan Thawar   changed wan_encapsulate to add a pad byte of 0
 * Jun 27, 1997  Alan Cox	realigned with vendor code
 * Jan 16, 1997	Gene Kozin	router_devlist made public
 * Jan 31, 1997  Alan Cox	Hacked it about a bit for 2.1
@@ -279,9 +280,10 @@
 
 	case ETH_P_IPX:		/* SNAP encapsulation */
 	case ETH_P_ARP:
-		hdr_len += 6;
+		hdr_len += 7;
 		skb_push(skb, 6);
-		skb->data[0] = NLPID_SNAP;
+		skb->data[0] = 0;
+		skb->data[1] = NLPID_SNAP;
 		memcpy(&skb->data[1], oui_ether, sizeof(oui_ether));
 		*((unsigned short*)&skb->data[4]) = htons(skb->protocol);
 		break;
diff -u --recursive --new-file v2.1.78/linux/net/wanrouter/wanproc.c linux/net/wanrouter/wanproc.c
--- v2.1.78/linux/net/wanrouter/wanproc.c	Thu Dec  4 14:53:57 1997
+++ linux/net/wanrouter/wanproc.c	Mon Jan 12 14:46:27 1998
@@ -56,7 +56,7 @@
 /****** Function Prototypes *************************************************/
 
 /* Proc filesystem interface */
-static int router_proc_perms (struct inode*, int);
+static int router_proc_perms(struct inode *, int);
 static ssize_t router_proc_read(struct file* file, char* buf, size_t count, 					loff_t *ppos);
 
 /* Methods for preparing data for reading proc entries */
@@ -118,6 +118,7 @@
 	NULL,			/* rmdir */
 	NULL,			/* mknod */
 	NULL,			/* rename */
+	NULL,			/* follow link */
 	NULL,			/* readlink */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
@@ -339,7 +340,7 @@
 static ssize_t router_proc_read(struct file* file, char* buf, size_t count,
 				loff_t *ppos)
 {
-	struct inode *inode;
+	struct inode *inode = file->f_dentry->d_inode;
 	struct proc_dir_entry* dent;
 	char* page;
 	int pos, offs, len;
diff -u --recursive --new-file v2.1.78/linux/net/x25/x25_dev.c linux/net/x25/x25_dev.c
--- v2.1.78/linux/net/x25/x25_dev.c	Mon Dec  1 12:04:18 1997
+++ linux/net/x25/x25_dev.c	Mon Jan 12 15:28:28 1998
@@ -178,7 +178,6 @@
 
 	skb->protocol = htons(ETH_P_X25);
 	skb->dev      = neigh->dev;
-	skb->arp      = 1;
 
 	dev_queue_xmit(skb);
 }
@@ -208,7 +207,6 @@
 
 	skb->protocol = htons(ETH_P_X25);
 	skb->dev      = neigh->dev;
-	skb->arp      = 1;
 
 	dev_queue_xmit(skb);
 }
@@ -235,7 +233,6 @@
 
 	skb->protocol = htons(ETH_P_X25);
 	skb->dev      = neigh->dev;
-	skb->arp      = 1;
 
 	dev_queue_xmit(skb);
 }
